+ children )) {
+ foreach($model->children as $index => $child) {
+ ?>
+
+
+
+
diff --git a/common/modules/comment/views/artbox_comment_list.php b/common/modules/comment/views/artbox_comment_list.php
new file mode 100755
index 0000000..20dc4a9
--- /dev/null
+++ b/common/modules/comment/views/artbox_comment_list.php
@@ -0,0 +1,30 @@
+session->getFlash('artbox_comment_success')) != null) {
+ echo Html::tag('p', $success);
+ }
+ echo ListView::widget([
+ 'dataProvider' => $comments,
+ 'itemOptions' => $item_options,
+ 'itemView' => $item_view,
+ 'summary' => '',
+ ]);
+ Pjax::end();
+
\ No newline at end of file
diff --git a/common/modules/comment/views/artbox_comment_reply.php b/common/modules/comment/views/artbox_comment_reply.php
new file mode 100755
index 0000000..2aed197
--- /dev/null
+++ b/common/modules/comment/views/artbox_comment_reply.php
@@ -0,0 +1,61 @@
+ '.field-' . $text_input_id,
+ 'input' => '#' . $text_input_id,
+ ];
+ $artbox_comment_pid_input_selectors = [
+ 'container' => '.field-' . $artbox_comment_pid_input_id,
+ 'input' => '#' . $artbox_comment_pid_input_id,
+ ];
+ $form = ActiveForm::begin([
+ 'id' => $formId . '-reply',
+ 'action' => Url::to([
+ 'artbox-comment/default/create',
+ 'entity' => $comment_model->encryptedEntity,
+ ]),
+ ]);
+?>
+
+ field($comment_model, 'artbox_comment_pid', [
+ 'selectors' => $artbox_comment_pid_input_selectors,
+ 'inputOptions' => [
+ 'id' => $artbox_comment_pid_input_id,
+ 'class' => 'form-control',
+ ],
+ ])
+ ->hiddenInput()
+ ->label(false);
+ echo $form->field($comment_model, 'text', [
+ 'selectors' => $text_input_selectors,
+ 'inputOptions' => [
+ 'id' => $text_input_id,
+ 'class' => 'form-control',
+ 'cols' => 30,
+ 'rows' => 10,
+ ],
+ ])
+ ->textarea();
+ echo Html::submitButton(Yii::t('artbox-comment', 'Submit'));
+ echo Html::button(Yii::t('artbox-comment', 'Cancel'), [ 'data-action' => 'reply-cancel' ]);
+ ?>
+
+
\ No newline at end of file
diff --git a/common/modules/comment/views/manage/index.php b/common/modules/comment/views/manage/index.php
new file mode 100755
index 0000000..dcd3f50
--- /dev/null
+++ b/common/modules/comment/views/manage/index.php
@@ -0,0 +1,78 @@
+ 'Активный',
+ $searchModel::STATUS_HIDDEN => 'Скрытый',
+ $searchModel::STATUS_DELETED => 'Удаленный',
+ ];
+ Pjax::begin();
+ if(($success = \Yii::$app->session->getFlash('artbox_comment_success')) != null) {
+ echo Html::tag('p', $success);
+ }
+ echo GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'filterModel' => $searchModel,
+ 'columns' => [
+ [
+ 'class' => 'yii\grid\ActionColumn',
+ 'template' => '{update} {delete}',
+ ],
+ [
+ 'attribute' => 'artbox_comment_id',
+ 'label' => 'Идентификатор',
+ ],
+ [
+ 'attribute' => 'date_add',
+ 'format' => ['date', 'php:d.m.Y'],
+ 'filter' => false,
+ ],
+ 'text:text',
+ [
+ 'attribute' => 'user_id',
+ 'value' => function($model) {
+ if(!empty($model->user_id)) {
+ return $model->user->username . ' (id:' . $model->user->id . ')';
+ } else {
+ return $model->username.' '.$model->email.' (Гость)';
+ }
+ }
+ ],
+ [
+ 'attribute' => 'status',
+ 'filter' => $statuses,
+ 'value' => function($model) use($statuses) {
+ return $statuses[$model->status];
+ }
+ ],
+ [
+ 'attribute' => 'rating_value',
+ 'label' => $searchModel->getAttributeLabel('rating_value'),
+ 'value' => function($model) {
+ if(!empty($model->rating)) {
+ return $model->rating->value;
+ }
+ return NULL;
+ }
+ ],
+ 'entity',
+ 'entity_id',
+ [
+ 'attribute' => 'children_count',
+ 'label' => $searchModel->getAttributeLabel('children_count'),
+ 'value' => function($model) {
+ return count($model->children);
+ }
+ ],
+ ],
+ ]);
+ Pjax::end();
\ No newline at end of file
diff --git a/common/modules/comment/views/manage/update.php b/common/modules/comment/views/manage/update.php
new file mode 100755
index 0000000..fa9f3de
--- /dev/null
+++ b/common/modules/comment/views/manage/update.php
@@ -0,0 +1,18 @@
+ 'Активный',
+ $model::STATUS_HIDDEN => 'Скрытый',
+ $model::STATUS_DELETED => 'Удаленный',
+ ];
+ $form = ActiveForm::begin();
+ echo $form->field($model, 'text')->textarea();
+ echo $form->field($model, 'status')->dropDownList($statuses);
+ echo Html::submitButton('Обновить');
+ $form->end();
\ No newline at end of file
diff --git a/common/modules/comment/widgets/CommentWidget.php b/common/modules/comment/widgets/CommentWidget.php
new file mode 100755
index 0000000..fb721cd
--- /dev/null
+++ b/common/modules/comment/widgets/CommentWidget.php
@@ -0,0 +1,367 @@
+ 'artbox_comment_container comments-start', 'id' => 'artbox-comment' ];
+
+ /**
+ * @var string the view file that will render comment form.
+ */
+ public $formView = '@artbox-comment/views/artbox_comment_form';
+
+ /**
+ * Form options
+ * @var array
+ */
+ public $formOptions = [
+ 'class' => 'artbox_form_container',
+ ];
+
+ /**
+ * Params to be passed to form
+ * @var array
+ */
+ public $formParams = [ ];
+
+ /**
+ * @var string the view file that will render comments list.
+ */
+ public $listView = '@artbox-comment/views/artbox_comment_list';
+
+ /**
+ * List options
+ * @var array
+ */
+ public $listOptions = [
+ 'class' => 'artbox_list_container',
+ ];
+
+ /**
+ * List params
+ * @var array
+ */
+ public $listParams = [ ];
+
+ /**
+ * Reply options
+ * @var array
+ */
+ public $replyOptions = [
+ 'style' => 'display: none;',
+ 'class' => 'artbox_comment_reply_container',
+ ];
+
+ /**
+ * Reply view
+ * @var string
+ */
+ public $replyView = '@artbox-comment/views/artbox_comment_reply';
+
+ /**
+ * Comment form ID. If you have multiple forms on the same page, please use unique IDs.
+ * @var string Form ID
+ */
+ public $formId = 'artbox-comment-form';
+
+ /**
+ * Comment list ID. If you have multiple forms on the same page, please use unique IDs.
+ * @var string List ID
+ */
+ public $listId = 'artbox-comment-list';
+
+ /**
+ * Item view
+ * @var string
+ */
+ public $itemView = '@artbox-comment/views/artbox_comment_item';
+
+ /**
+ * Item options
+ * @var array
+ */
+ public $itemOptions = [
+ 'class' => 'artbox_item_container',
+ 'itemprop' => 'review',
+ 'itemscope' => 'itemscope',
+ 'itemtype' => 'http://schema.org/Review',
+ ];
+
+ /**
+ * Entity ID attribute, default to primaryKey() if ActiveRecord and throws exception if not
+ * set
+ * @var string entity id attribute
+ */
+ public $entityIdAttribute;
+
+ /**
+ * Info to be passed to Comment Model
+ * @var string $info Additional info
+ */
+ public $info = NULL;
+
+ /**
+ * Client options to be passed to JS
+ * @var array comment widget client options
+ */
+ public $clientOptions = [ ];
+
+ /**
+ * @todo Check if needed
+ * @var string pjax container id
+ */
+ public $pjaxContainerId;
+
+ public $layout = "{form} {reply_form} {list}";
+
+ /**
+ * Model fully namespaced classname
+ * @var string Model namespace
+ */
+ protected $entity;
+
+ /**
+ * Entity ID for attached model
+ * @var integer Entity ID
+ */
+ protected $entityId;
+
+ /**
+ * Encrypted data to be passed to Controller. Consist of:
+ * * Model::className()
+ * * entityId
+ * * info (optional)
+ * @var string encrypted entity key
+ */
+ protected $encryptedEntityKey;
+
+ /**
+ * Parts for widget
+ * @var array $parts
+ */
+ protected $parts;
+
+ /**
+ * Initializes the widget params.
+ */
+ public function init()
+ {
+ // Module init
+ Yii::$app->getModule(Module::$name);
+ // Model init
+ $model = $this->getModel();
+
+ /**
+ * @todo Check if needed
+ */
+ if(empty( $this->pjaxContainerId )) {
+ $this->pjaxContainerId = 'comment-pjax-container-' . $this->getId();
+ }
+
+ $this->entity = $model::className();
+ // Entity ID init
+ if(!empty( $this->entityIdAttribute ) && $this->model->hasProperty($this->entityIdAttribute)) {
+ $this->entityId = $this->model->{$this->entityIdAttribute};
+ } else {
+ if($this->model instanceof \yii\db\ActiveRecord && !empty( $this->model->getPrimaryKey() )) {
+ $this->entityId = (int) $this->model->getPrimaryKey();
+ } else {
+ throw new InvalidConfigException(/*Yii::t('artbox-comment', 'The "entityIdAttribute" value for widget model cannot be empty.')*/);
+ }
+ }
+
+ // Generate encryptedEntityKey
+ $this->encryptedEntityKey = $this->generateEntityKey();
+
+ $this->registerAssets();
+ }
+
+ /**
+ * Executes the widget.
+ * @return string the result of widget execution to be outputted.
+ */
+ public function run()
+ {
+ /* @var Module $module */
+ $module = Yii::$app->getModule(Module::$name);
+ $commentModelClass = $module->commentModelClass;
+ $commentModel = $this->createModel($commentModelClass, [
+ 'entity' => $this->entity,
+ 'entityId' => $this->entityId,
+ 'encryptedEntity' => $this->encryptedEntityKey,
+ 'scenario' => \Yii::$app->user->getIsGuest()?$commentModelClass::SCENARIO_GUEST:$commentModelClass::SCENARIO_USER,
+ ]);
+ if($module::$enableRating) {
+ $ratingModelClass = $module->ratingModelClass;
+ $ratingModel = $this->createRating($ratingModelClass);
+ } else {
+ $ratingModel = NULL;
+ }
+
+ $comments = $commentModelClass::getTree($this->entity, $this->entityId);
+
+ $this->buildParts($commentModel, $comments, $ratingModel);
+
+ return $this->renderWidget();
+ }
+
+ /**
+ * Register assets.
+ */
+ protected function registerAssets()
+ {
+ $this->clientOptions[ 'formSelector' ] = '#' . $this->formId;
+ $this->clientOptions[ 'listSelector' ] = '#' . $this->listId;
+ $options = Json::encode($this->clientOptions);
+ $view = $this->getView();
+ CommentAsset::register($view);
+ $view->registerJs("jQuery('#{$this->formId}').artbox_comment({$options});");
+ }
+
+ /**
+ * Get encrypted entity key
+ * @return string
+ */
+ protected function generateEntityKey()
+ {
+ return Yii::$app->getSecurity()
+ ->encryptByKey(Json::encode([
+ 'entity' => $this->entity,
+ 'entity_id' => $this->entityId,
+ 'info' => $this->info,
+ ]), Module::$encryptionKey);
+ }
+
+ /**
+ * Create comment model
+ *
+ * @param string $className Full namespaced model
+ * @param array $config Init config
+ *
+ * @return CommentInterface Comment model
+ * @throws InvalidConfigException If object not instance of \yii\base\Model
+ */
+ protected function createModel(string $className, array $config = [ ]): CommentInterface
+ {
+ $options = array_merge($config, [ 'class' => $className ]);
+ $object = Yii::createObject($options);
+ if($object instanceof CommentInterface) {
+ return $object;
+ }
+ throw new InvalidConfigException(/*Yii::t(\'artbox-comment\', \'Comment model must be instance of CommentInterface.\')*/);
+ }
+
+ /**
+ * Create rating model
+ *
+ * @param string $className Full namespaced model
+ * @param array $config Init config
+ *
+ * @return CommentInterface Comment model
+ * @throws InvalidConfigException If object not instance of \yii\base\Model
+ */
+ protected function createRating(string $className, array $config = [ ]): RatingModel
+ {
+ $options = array_merge($config, [ 'class' => $className ]);
+ $object = Yii::createObject($options);
+ if($object instanceof RatingModel) {
+ return $object;
+ }
+ throw new InvalidConfigException(/*Yii::t(\'artbox-comment\', \'Comment model must be instance of RatingModel.\')*/);
+ }
+
+ /**
+ * Build parts for rendering widget
+ *
+ * @param CommentInterface $commentModel
+ * @param ActiveDataProvider $comments
+ */
+ protected function buildParts(CommentInterface $commentModel, ActiveDataProvider $comments, $ratingModel = NULL)
+ {
+ $form_options = $this->formOptions;
+ $this->parts[ 'form' ] = Html::tag(ArrayHelper::remove($form_options, 'tag', 'div'), $this->render($this->formView, [
+ 'comment_model' => $commentModel,
+ 'form_params' => $this->formParams,
+ 'model' => $this->getModel(),
+ 'formId' => $this->formId,
+ 'rating_model' => $ratingModel,
+ ]), $form_options);
+
+ if(!\Yii::$app->user->isGuest) {
+ $reply_options = $this->replyOptions;
+ $this->parts[ 'reply_form' ] = Html::tag(ArrayHelper::remove($reply_options, 'tag', 'div'), $this->render($this->replyView, [
+ 'comment_model' => $commentModel,
+ 'form_params' => $this->formParams,
+ 'model' => $this->getModel(),
+ 'formId' => $this->formId,
+ ]), $reply_options);
+ }
+
+ $list_options = array_merge($this->listOptions, [ 'id' => $this->listId ]);
+ $this->parts[ 'list' ] = Html::tag(ArrayHelper::remove($list_options, 'tag', 'div'), $this->render($this->listView, [
+ 'comment_model' => $commentModel,
+ 'list_params' => $this->listParams,
+ 'model' => $this->getModel(),
+ 'comments' => $comments,
+ 'item_options' => $this->itemOptions,
+ 'item_view' => $this->itemView,
+ ]), $list_options);
+ }
+
+ /**
+ * @return string
+ */
+ protected function renderWidget(): string
+ {
+ $layout = $this->layout;
+ $parts = $this->parts;
+ $options = $this->options;
+ $layout = preg_replace('/{list}/', ArrayHelper::getValue($parts, 'list', ''), $layout);
+ $layout = preg_replace('/{form}/', ArrayHelper::getValue($parts, 'form', ''), $layout);
+ $layout = preg_replace('/{reply_form}/', ArrayHelper::getValue($parts, 'reply_form', ''), $layout);
+ $tag = ArrayHelper::remove($options, 'tag', 'div');
+ return Html::tag($tag, $layout, $options);
+ }
+
+ public function setModel(\yii\base\Model $model)
+ {
+ $this->model = $model;
+ }
+
+ public function getModel()
+ {
+ if(!empty( $this->model )) {
+ return $this->model;
+ }
+ throw new InvalidConfigException(/*Yii::t(\'artbox-comment\', \'The "model" property must be set.\')*/);
+ }
+ }
\ No newline at end of file
diff --git a/common/modules/file/FileUploadAsset.php b/common/modules/file/FileUploadAsset.php
new file mode 100755
index 0000000..1f15894
--- /dev/null
+++ b/common/modules/file/FileUploadAsset.php
@@ -0,0 +1,43 @@
+
+ * @since 2.0
+ */
+class FileUploadAsset extends AssetBundle
+{
+
+
+ /**
+ * @inheritdoc
+ */
+ public function init()
+ {
+ parent::init();
+ $this->sourcePath = __DIR__.'/assets';
+ }
+
+ public $css = [
+ 'css/jquery.fileupload.css',
+ 'css/fileupload/style.css'
+ ];
+
+ public $js = [
+ 'js/vendor/jquery.ui.widget.js',
+ 'js/jquery.iframe-transport.js',
+ 'js/jquery.fileupload.js'
+ ];
+}
diff --git a/common/modules/file/Module.php b/common/modules/file/Module.php
new file mode 100755
index 0000000..374e5f4
--- /dev/null
+++ b/common/modules/file/Module.php
@@ -0,0 +1,15 @@
+').prop('href', options.postMessage)[0],
+ target = loc.protocol + '//' + loc.host,
+ xhrUpload = options.xhr().upload;
+ return {
+ send: function (_, completeCallback) {
+ counter += 1;
+ var message = {
+ id: 'postmessage-transport-' + counter
+ },
+ eventName = 'message.' + message.id;
+ iframe = $(
+ '",
+ options: {
+ disabled: false,
+
+ // callbacks
+ create: null
+ },
+ _createWidget: function( options, element ) {
+ element = $( element || this.defaultElement || this )[ 0 ];
+ this.element = $( element );
+ this.uuid = widget_uuid++;
+ this.eventNamespace = "." + this.widgetName + this.uuid;
+
+ this.bindings = $();
+ this.hoverable = $();
+ this.focusable = $();
+
+ if ( element !== this ) {
+ $.data( element, this.widgetFullName, this );
+ this._on( true, this.element, {
+ remove: function( event ) {
+ if ( event.target === element ) {
+ this.destroy();
+ }
+ }
+ });
+ this.document = $( element.style ?
+ // element within the document
+ element.ownerDocument :
+ // element is window or document
+ element.document || element );
+ this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
+ }
+
+ this.options = $.widget.extend( {},
+ this.options,
+ this._getCreateOptions(),
+ options );
+
+ this._create();
+ this._trigger( "create", null, this._getCreateEventData() );
+ this._init();
+ },
+ _getCreateOptions: $.noop,
+ _getCreateEventData: $.noop,
+ _create: $.noop,
+ _init: $.noop,
+
+ destroy: function() {
+ this._destroy();
+ // we can probably remove the unbind calls in 2.0
+ // all event bindings should go through this._on()
+ this.element
+ .unbind( this.eventNamespace )
+ .removeData( this.widgetFullName )
+ // support: jquery <1.6.3
+ // http://bugs.jquery.com/ticket/9413
+ .removeData( $.camelCase( this.widgetFullName ) );
+ this.widget()
+ .unbind( this.eventNamespace )
+ .removeAttr( "aria-disabled" )
+ .removeClass(
+ this.widgetFullName + "-disabled " +
+ "ui-state-disabled" );
+
+ // clean up events and states
+ this.bindings.unbind( this.eventNamespace );
+ this.hoverable.removeClass( "ui-state-hover" );
+ this.focusable.removeClass( "ui-state-focus" );
+ },
+ _destroy: $.noop,
+
+ widget: function() {
+ return this.element;
+ },
+
+ option: function( key, value ) {
+ var options = key,
+ parts,
+ curOption,
+ i;
+
+ if ( arguments.length === 0 ) {
+ // don't return a reference to the internal hash
+ return $.widget.extend( {}, this.options );
+ }
+
+ if ( typeof key === "string" ) {
+ // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
+ options = {};
+ parts = key.split( "." );
+ key = parts.shift();
+ if ( parts.length ) {
+ curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
+ for ( i = 0; i < parts.length - 1; i++ ) {
+ curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
+ curOption = curOption[ parts[ i ] ];
+ }
+ key = parts.pop();
+ if ( arguments.length === 1 ) {
+ return curOption[ key ] === undefined ? null : curOption[ key ];
+ }
+ curOption[ key ] = value;
+ } else {
+ if ( arguments.length === 1 ) {
+ return this.options[ key ] === undefined ? null : this.options[ key ];
+ }
+ options[ key ] = value;
+ }
+ }
+
+ this._setOptions( options );
+
+ return this;
+ },
+ _setOptions: function( options ) {
+ var key;
+
+ for ( key in options ) {
+ this._setOption( key, options[ key ] );
+ }
+
+ return this;
+ },
+ _setOption: function( key, value ) {
+ this.options[ key ] = value;
+
+ if ( key === "disabled" ) {
+ this.widget()
+ .toggleClass( this.widgetFullName + "-disabled", !!value );
+
+ // If the widget is becoming disabled, then nothing is interactive
+ if ( value ) {
+ this.hoverable.removeClass( "ui-state-hover" );
+ this.focusable.removeClass( "ui-state-focus" );
+ }
+ }
+
+ return this;
+ },
+
+ enable: function() {
+ return this._setOptions({ disabled: false });
+ },
+ disable: function() {
+ return this._setOptions({ disabled: true });
+ },
+
+ _on: function( suppressDisabledCheck, element, handlers ) {
+ var delegateElement,
+ instance = this;
+
+ // no suppressDisabledCheck flag, shuffle arguments
+ if ( typeof suppressDisabledCheck !== "boolean" ) {
+ handlers = element;
+ element = suppressDisabledCheck;
+ suppressDisabledCheck = false;
+ }
+
+ // no element argument, shuffle and use this.element
+ if ( !handlers ) {
+ handlers = element;
+ element = this.element;
+ delegateElement = this.widget();
+ } else {
+ element = delegateElement = $( element );
+ this.bindings = this.bindings.add( element );
+ }
+
+ $.each( handlers, function( event, handler ) {
+ function handlerProxy() {
+ // allow widgets to customize the disabled handling
+ // - disabled as an array instead of boolean
+ // - disabled class as method for disabling individual parts
+ if ( !suppressDisabledCheck &&
+ ( instance.options.disabled === true ||
+ $( this ).hasClass( "ui-state-disabled" ) ) ) {
+ return;
+ }
+ return ( typeof handler === "string" ? instance[ handler ] : handler )
+ .apply( instance, arguments );
+ }
+
+ // copy the guid so direct unbinding works
+ if ( typeof handler !== "string" ) {
+ handlerProxy.guid = handler.guid =
+ handler.guid || handlerProxy.guid || $.guid++;
+ }
+
+ var match = event.match( /^([\w:-]*)\s*(.*)$/ ),
+ eventName = match[1] + instance.eventNamespace,
+ selector = match[2];
+ if ( selector ) {
+ delegateElement.delegate( selector, eventName, handlerProxy );
+ } else {
+ element.bind( eventName, handlerProxy );
+ }
+ });
+ },
+
+ _off: function( element, eventName ) {
+ eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) +
+ this.eventNamespace;
+ element.unbind( eventName ).undelegate( eventName );
+
+ // Clear the stack to avoid memory leaks (#10056)
+ this.bindings = $( this.bindings.not( element ).get() );
+ this.focusable = $( this.focusable.not( element ).get() );
+ this.hoverable = $( this.hoverable.not( element ).get() );
+ },
+
+ _delay: function( handler, delay ) {
+ function handlerProxy() {
+ return ( typeof handler === "string" ? instance[ handler ] : handler )
+ .apply( instance, arguments );
+ }
+ var instance = this;
+ return setTimeout( handlerProxy, delay || 0 );
+ },
+
+ _hoverable: function( element ) {
+ this.hoverable = this.hoverable.add( element );
+ this._on( element, {
+ mouseenter: function( event ) {
+ $( event.currentTarget ).addClass( "ui-state-hover" );
+ },
+ mouseleave: function( event ) {
+ $( event.currentTarget ).removeClass( "ui-state-hover" );
+ }
+ });
+ },
+
+ _focusable: function( element ) {
+ this.focusable = this.focusable.add( element );
+ this._on( element, {
+ focusin: function( event ) {
+ $( event.currentTarget ).addClass( "ui-state-focus" );
+ },
+ focusout: function( event ) {
+ $( event.currentTarget ).removeClass( "ui-state-focus" );
+ }
+ });
+ },
+
+ _trigger: function( type, event, data ) {
+ var prop, orig,
+ callback = this.options[ type ];
+
+ data = data || {};
+ event = $.Event( event );
+ event.type = ( type === this.widgetEventPrefix ?
+ type :
+ this.widgetEventPrefix + type ).toLowerCase();
+ // the original event may come from any element
+ // so we need to reset the target on the new event
+ event.target = this.element[ 0 ];
+
+ // copy original event properties over to the new event
+ orig = event.originalEvent;
+ if ( orig ) {
+ for ( prop in orig ) {
+ if ( !( prop in event ) ) {
+ event[ prop ] = orig[ prop ];
+ }
+ }
+ }
+
+ this.element.trigger( event, data );
+ return !( $.isFunction( callback ) &&
+ callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
+ event.isDefaultPrevented() );
+ }
+};
+
+$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
+ $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
+ if ( typeof options === "string" ) {
+ options = { effect: options };
+ }
+ var hasOptions,
+ effectName = !options ?
+ method :
+ options === true || typeof options === "number" ?
+ defaultEffect :
+ options.effect || defaultEffect;
+ options = options || {};
+ if ( typeof options === "number" ) {
+ options = { duration: options };
+ }
+ hasOptions = !$.isEmptyObject( options );
+ options.complete = callback;
+ if ( options.delay ) {
+ element.delay( options.delay );
+ }
+ if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
+ element[ method ]( options );
+ } else if ( effectName !== method && element[ effectName ] ) {
+ element[ effectName ]( options.duration, options.easing, callback );
+ } else {
+ element.queue(function( next ) {
+ $( this )[ method ]();
+ if ( callback ) {
+ callback.call( element[ 0 ] );
+ }
+ next();
+ });
+ }
+ };
+});
+
+var widget = $.widget;
+
+
+
+}));
diff --git a/common/modules/file/config.php b/common/modules/file/config.php
new file mode 100755
index 0000000..c1d2298
--- /dev/null
+++ b/common/modules/file/config.php
@@ -0,0 +1,3 @@
+$w){
+ return true;
+ }else if($height >$h) {
+ return true;
+ }
+ return false;
+ }
+
+
+ private function getUserPath(){
+ if(isset(Yii::$app->user->id)){
+ return 'user_'.Yii::$app->user->id;
+ }else {
+ return 'guest';
+ }
+ }
+
+ private function resizeImg($w, $h, $imageAlias,$imageAliasSave){
+ $img = Image::getImagine()->open(Yii::getAlias($imageAlias));
+
+ $size = $img->getSize();
+
+ $width = $size->getWidth();
+ $height = $size->getHeight();
+
+ $e_width = $w/$h;
+ $e_height = $h/$w;
+
+ $e1_width = $width/$height;
+ $e1_height = $height/$width;
+
+
+
+ if($e_width<$e1_width){
+
+ $new_width = $width*($e_width/$e1_width);
+
+ $y = 0;
+ $x = $width/ 2-($new_width/2);
+ $width = $new_width;
+
+ }else {
+
+ $new_height = $height*($e_height/$e1_height);
+ $x = 0;
+ $y = $height/2-($new_height/2);
+ $height = $new_height;
+ }
+
+
+
+
+ Image::crop($imageAlias, $width, $height,[$x,$y])
+ ->save(Yii::getAlias($imageAliasSave), ['quality' =>
+ 100]);
+
+
+ $imagine = new Imagine();
+ $imagine->open($imageAliasSave)
+ ->resize(new Box($w, $h))
+ ->save($imageAliasSave, array('flatten' => false));
+
+
+
+ }
+
+ private function deleteImages($old_img){
+
+ if(!empty($old_img) && file_exists($_SERVER['DOCUMENT_ROOT'].$old_img)){
+
+ $rootDir = explode("/", $old_img);
+
+ $row = $_SERVER['DOCUMENT_ROOT'].'/'.$rootDir[1].'/'.$rootDir[2].'/'.$rootDir[3].'/';
+
+ $allFiles = scandir($row);
+
+ $allFiles = array_slice($allFiles, 2);
+
+ foreach($allFiles as $oldFile){
+
+ unlink($row.$oldFile);
+
+ }
+
+ }
+ }
+
+ public function actionDeleteImage(){
+
+ $this->enableCsrfValidation = false;
+
+ $request = Yii::$app->request->post();
+
+ if($request){
+ if ($request['old_img']) {
+ $this->deleteImages($request['old_img']);
+ }
+ if(isset($request['action']) && $request['action']=='save'){
+ $object = str_replace('-', '\\',$request['model']);
+ $model = new $object;
+ $model = $model->findOne($request['id']);
+ $model->$request['field'] = $request['new_url'];
+ $model->save();
+ }
+ }
+
+ }
+
+
+ public function actionDownloadPhoto()
+ {
+
+ $model = new ImageSizerForm();
+
+ $request = Yii::$app->request->post();
+
+ if ($request) {
+
+ $model->multi = isset($request['multi'])? 1 : 0;
+
+ $model->file = UploadedFile::getInstance($model, 'file');
+
+ $md5_file = md5_file($model->file->tempName).rand(1, 1000);
+
+ $imgDir = Yii::getAlias('@storage/'.$this->getUserPath().'/'.$md5_file.'/');
+
+ $imageOrigAlias = Yii::getAlias($imgDir.'original'.'.'.$model->file->extension);
+
+ if(!is_dir($imgDir)) {
+ mkdir($imgDir, 0755, true);
+ }
+
+ $model->file->saveAs($imageOrigAlias);
+
+
+ if(isset($request['size'] )){
+
+ $request['size'] = ArrayHelper::toArray(json_decode($request['size']));
+
+ foreach($request['size'] as $size){
+ if($size['width'] && $size['height']){
+
+ $imageAlias = Yii::getAlias($imgDir.$size['width'].'x'.$size['height'].'.'.$model->file->extension);
+
+ $imageLink = '/storage/'.$this->getUserPath().'/'.$md5_file.'/'.$size['width'].'x'.$size['height'].'.'.$model->file->extension;
+
+ $this->resizeImg($size['width'], $size['height'], $imageOrigAlias,$imageAlias);
+
+ }
+ }
+
+ } else {
+
+ $imageLink = '/storage/'.$this->getUserPath().'/'.$md5_file.'/'.'original'.'.'.$model->file->extension;
+
+ }
+
+
+ if($model->multi){
+ $view = $this->renderPartial('/_gallery_item', [
+ 'item' => ['image'=>$imageLink],
+ 'field'=>$request['field']
+ ]);
+ return json_encode(['link'=>$imageLink,
+ 'view' =>$view,
+
+ ]);
+
+
+ } else {
+ $view = $this->renderPartial('/_one_item', [
+ 'item' => ['image'=>$imageLink],
+ 'field'=>$request['field']
+ ]);
+ return json_encode(['link'=>$imageLink,
+ 'view' =>$view,
+ ]);
+ }
+
+
+ }
+ }
+
+
+ public function getex($filename) {
+ return end(explode(".", $filename));
+ }
+
+
+ public function actionImagesUpload(){
+
+ if($_FILES['upload'])
+ {
+ if (($_FILES['upload'] == "none") OR (empty($_FILES['upload']['name'])) )
+ {
+ $message = "Вы не выбрали файл";
+ }
+ else if ($_FILES['upload']["size"] == 0 OR $_FILES['upload']["size"] > 2050000)
+ {
+ $message = "Размер файла не соответствует нормам";
+ }
+ else if (($_FILES['upload']["type"] != "image/jpeg") AND ($_FILES['upload']["type"] != "image/jpeg") AND ($_FILES['upload']["type"] != "image/png") AND ($_FILES['upload']['type'] != 'image/gif'))
+ {
+ $message = "Допускается загрузка только картинок JPG и PNG.";
+ }
+ else if (!is_uploaded_file($_FILES['upload']["tmp_name"]))
+ {
+ $message = "Что-то пошло не так. Попытайтесь загрузить файл ещё раз.";
+ }
+ else{
+ $name =$_FILES['upload']['name'].'.'.$this->getex($_FILES['upload']['name']);
+
+ $path = "../../storage/".$this->getUserPath()."/images/";
+ if(!is_dir($path)) {
+ mkdir($path, 0755, true);
+ }
+
+
+
+ move_uploaded_file($_FILES['upload']['tmp_name'], $path.$name);
+
+ $full_path = '/storage/'.$this->getUserPath().'/images/'.$name;
+
+ $message = "Файл ".$_FILES['upload']['name']." загружен";
+
+
+ }
+ $callback = $_REQUEST['CKEditorFuncNum'];
+ echo '';
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/common/modules/file/models/ImageSizerForm.php b/common/modules/file/models/ImageSizerForm.php
new file mode 100755
index 0000000..b891ab0
--- /dev/null
+++ b/common/modules/file/models/ImageSizerForm.php
@@ -0,0 +1,38 @@
+ 255],
+ [['model', 'form',], 'string'],
+ [['file','img','price_list'], 'file'],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/common/modules/file/views/_gallery_item.php b/common/modules/file/views/_gallery_item.php
new file mode 100755
index 0000000..9aea75e
--- /dev/null
+++ b/common/modules/file/views/_gallery_item.php
@@ -0,0 +1,9 @@
+
+
+
+ = Html::img($item['image'])?>
+
+
\ No newline at end of file
diff --git a/common/modules/file/views/_one_item.php b/common/modules/file/views/_one_item.php
new file mode 100755
index 0000000..fed9ba9
--- /dev/null
+++ b/common/modules/file/views/_one_item.php
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+ = Html::a( Html::img($item['image']),'#',['class'=>'thumbnail']) ?>
+
+
+
diff --git a/common/modules/file/widgets/ImageUploader.php b/common/modules/file/widgets/ImageUploader.php
new file mode 100755
index 0000000..ec7545b
--- /dev/null
+++ b/common/modules/file/widgets/ImageUploader.php
@@ -0,0 +1,63 @@
+render('image_sizer',
+ [
+ 'model'=>$this->model,
+ 'size' => $this->size,
+ 'field' => $this->field,
+ 'height' => $this->height,
+ 'width' => $this->width,
+ 'multi' => $this->multi,
+ 'name' => $this->name,
+ 'remover' => $this->remover
+ ]);
+
+ }
+
+ public function getGallery(){
+ if($this->gallery){
+ $array = explode(",", $this->gallery);
+ if(count($array) > 1){
+ array_pop($array);
+ }
+ return $array;
+ } else {
+ return array();
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/common/modules/file/widgets/views/image_sizer.php b/common/modules/file/widgets/views/image_sizer.php
new file mode 100755
index 0000000..eb63a87
--- /dev/null
+++ b/common/modules/file/widgets/views/image_sizer.php
@@ -0,0 +1,203 @@
+tableSchema->primaryKey[0];
+
+?>
+
+
+
+
+ = Html::activeHiddenInput( $model,$field,['id' => "{$field}_picture_link"]) ?>
+
+
+
+
+
+
+
+
+
+ $field): ?>
+
+
+
+ $field):?>
+ = Html::a(Html::img($model->$field),'#',['class'=>'thumbnail']) ?>
+
+
+
+
+
+
+
+
+ =$name?>
+
+ = Html::activeFileInput( new ImageSizerForm(),'file',['id'=>$field, 'data-url'=>Yii::$app->getUrlManager()->createUrl('file/uploader/download-photo')]);?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ =$name?>
+
+ = Html::activeFileInput( new ImageSizerForm(),'file',['id'=>$field, 'data-url'=>Yii::$app->getUrlManager()->createUrl('file/uploader/download-photo'), 'multiple'=> 'multiple' ]);?>
+
+
+ = Html::activeHiddenInput( $model,$field,['id' => "{$field}_picture_link"]) ?>
+
+
+
+
+
+ context->getGallery() as $image){
+ echo $this->render('@common/modules/file/views/_gallery_item', [ 'item' => ['image'=>$image]]);
+ }
+ ?>
+
+
+
+
diff --git a/common/modules/product/CatalogUrlManager.php b/common/modules/product/CatalogUrlManager.php
new file mode 100755
index 0000000..2332d8c
--- /dev/null
+++ b/common/modules/product/CatalogUrlManager.php
@@ -0,0 +1,307 @@
+getPathInfo();
+ $paths = explode('/', $pathInfo);
+ /**
+ * linija-svitla.ua only
+ */
+ $pattern = '/^(.+)\.htm$/';
+ if(isset($paths[0])){
+ preg_match($pattern,$paths[0], $match );
+ if(!empty($match)){
+ $old_item_name = explode('-',$paths[0] );
+ array_pop($old_item_name);
+ $count = count($old_item_name)-1;
+
+ if(isset($old_item_name[$count]) && $old_item_name[$count] == 'dis' ){
+
+ $sku = $old_item_name[$count-3].'/'.$old_item_name[$count-2].'/'.$old_item_name[$count-1].'_'.$old_item_name[$count];
+ $product_variant = ProductVariant::find()->where(['sku'=>$sku])->one();
+ if($product_variant instanceof ProductVariant){
+
+ $link = 'product'.'/'. $product_variant->product->alias;
+ header("HTTP/1.1 301 Moved Permanently");
+ header("Location: http://www.linija-svitla.ua/".$link);
+ die();
+ }
+ }
+
+
+ if(isset($old_item_name[$count-3]) && isset($old_item_name[$count-2]) && isset($old_item_name[$count-1]) && isset($old_item_name[$count])){
+
+ $sku = $old_item_name[$count-3].'/'.$old_item_name[$count-2].'/'.$old_item_name[$count-1].'/'.$old_item_name[$count];
+ $product_variant = ProductVariant::find()->where(['sku'=>$sku])->one();
+ if($product_variant instanceof ProductVariant){
+
+ $link = 'product'.'/'. $product_variant->product->alias;
+ header("HTTP/1.1 301 Moved Permanently");
+ header("Location: http://www.linija-svitla.ua/".$link);
+ die();
+ }
+ }
+
+ if(isset($old_item_name[$count-2]) && isset($old_item_name[$count-1]) && isset($old_item_name[$count])){
+ $sku = $old_item_name[$count-2].'/'.$old_item_name[$count-1].'/'.$old_item_name[$count];
+ $product_variant = ProductVariant::find()->where(['sku'=>$sku])->one();
+
+ if($product_variant instanceof ProductVariant){
+
+ $link = 'product'.'/'. $product_variant->product->alias;
+ header("HTTP/1.1 301 Moved Permanently");
+ header("Location: http://www.linija-svitla.ua/".$link);
+ die();
+ }
+ $sku = $old_item_name[$count-2].'/'.$old_item_name[$count-1].' '.$old_item_name[$count];
+ $product_variant = ProductVariant::find()->where(['sku'=>$sku])->one();
+
+ if($product_variant instanceof ProductVariant){
+ $link = 'product'.'/'. $product_variant->product->alias;
+ header("HTTP/1.1 301 Moved Permanently");
+ header("Location: http://www.linija-svitla.ua/".$link);
+ die();
+ }
+
+ $sku = $old_item_name[$count-2].'/'.$old_item_name[$count-1].'-'.$old_item_name[$count];
+ $product_variant = ProductVariant::find()->where(['sku'=>$sku])->one();
+
+ if($product_variant instanceof ProductVariant){
+ $link = 'product'.'/'. $product_variant->product->alias;
+ header("HTTP/1.1 301 Moved Permanently");
+ header("Location: http://www.linija-svitla.ua/".$link);
+ die();
+ }
+
+ $sku = $old_item_name[$count-2].'/'.$old_item_name[$count-1].'_'.$old_item_name[$count];
+ $product_variant = ProductVariant::find()->where(['sku'=>$sku])->one();
+
+ if($product_variant instanceof ProductVariant){
+ $link = 'product'.'/'. $product_variant->product->alias;
+ header("HTTP/1.1 301 Moved Permanently");
+ header("Location: http://www.linija-svitla.ua/".$link);
+ die();
+ }
+
+ }
+
+ if(isset($old_item_name[$count-1]) && isset($old_item_name[$count])){
+ $sku = $old_item_name[$count-1].'/'.$old_item_name[$count];
+ $product_variant = ProductVariant::find()->where(['sku'=>$sku])->one();
+ if($product_variant instanceof ProductVariant){
+
+ $link = 'product'.'/'. $product_variant->product->alias;
+ header("HTTP/1.1 301 Moved Permanently");
+ header("Location: http://www.linija-svitla.ua/".$link);
+ die();
+ }
+ }
+
+ if(isset($old_item_name[$count])){
+ $sku = $old_item_name[$count];
+ $product_variant = ProductVariant::find()->where(['sku'=>$sku])->one();
+ if($product_variant instanceof ProductVariant){
+
+ $link = 'product'.'/'. $product_variant->product->alias;
+ header("HTTP/1.1 301 Moved Permanently");
+ header("Location: http://www.linija-svitla.ua/".$link);
+ die();
+ }
+ }
+ }
+ }
+
+ /**
+ * end linija-svitla.ua only
+ */
+
+
+
+
+
+ if (!array_key_exists($paths[0], $this->route_map)) {
+ return false;
+ }
+
+ $params = [];
+ if ($paths[0] == 'catalog') {
+ $route = 'catalog/category';
+
+ // Category
+ if (!empty($paths[1])) {
+ $category = CategorySearch::findByAlias($paths[1]);
+ if (empty($category)) {
+ http_redirect(Url::to(['/']));
+ }
+ $params['category'] = $category;
+ }
+ if (!empty($paths[2])) {
+ // Filter
+
+ if (strpos($paths[2], 'filters:') === 0) {
+ if(!isset($paths[3]) ){
+ $this->parseFilter($paths[2], $params);
+ } else {
+ throw new HttpException(404 ,'Page not found');
+ }
+
+
+ }
+ else {
+ throw new HttpException(404 ,'Page not found');
+ }
+ }
+ } elseif ($paths[0] == 'product') {
+
+ if (!empty($paths[2])) {
+ throw new HttpException(404 ,'Page not found');
+ }
+ $product = ProductSearch::findByAlias($paths[1]);
+ if (empty($product->product_id)) {
+ throw new HttpException(404 ,'Page not found');
+ }
+ $route = 'catalog/product';
+ $params = [
+ 'product' => $product,
+ ];
+ }
+
+ return [$route, $params];
+ }
+ /**
+ * Creates a URL according to the given route and parameters.
+ * @param \yii\web\UrlManager $manager the URL manager
+ * @param string $route the route. It should not have slashes at the beginning or the end.
+ * @param array $params the parameters
+ * @return string|boolean the created URL, or false if this rule cannot be used for creating this URL.
+ */
+ public function createUrl($manager, $route, $params)
+ {
+
+ if (!in_array($route, $this->route_map)) {
+ return false;
+ }
+
+ switch($route) {
+ case 'catalog/category':
+ if (!empty($params['category'])) {
+ $category_alias = is_object($params['category']) ? $params['category']->alias : strtolower($params['category']);
+ $url = 'catalog/'. $category_alias .'/';
+ unset($params['category']);
+ } else {
+ $url = 'catalog/';
+ }
+
+ $this->setFilterUrl($params, $url);
+
+ foreach ($params as $key => $param) {
+ if (empty($params[$key])) {
+ unset($params[$key]);
+ }
+ }
+
+ if (!empty($params) && ($query = http_build_query($params)) !== '') {
+ $url .= '?' . $query;
+ }
+
+ return $url;
+ break;
+
+ case 'catalog/product':
+ if (!empty($params['product'])) {
+ $product_alias = is_object($params['product']) ? $params['product']->alias : strtolower($params['product']);
+ unset($params['product']);
+ }
+ $url = 'product/'. $product_alias;
+
+ if (!empty($params) && ($query = http_build_query($params)) !== '') {
+ $url .= '?' . $query;
+ }
+
+
+
+ return $url;
+ break;
+
+ }
+ }
+
+ private function option_value_encode($value) {
+ return str_replace(array(',', '/'), array('~', '&s;'), $value);
+ }
+
+ private function setFilterUrl(&$params, &$url) {
+ $filter = [];
+ if (!empty($params['filters'])) {
+ foreach ($params['filters'] as $key => $values) {
+ switch($key) {
+ case 'prices':
+ $filter[] = $key .'='. implode(':', $values);
+ break;
+
+ default:
+ foreach($values as &$value) {
+ $value = $this->option_value_encode($value);
+ if (empty($value)) {
+ unset($value);
+ }
+ }
+ $filter[] = $key .'='. implode(',', $values);
+ break;
+ }
+ }
+ if (!empty($filter)) {
+ $url .= 'filters:'. implode(';', $filter);
+ }
+ unset($params['filters']);
+ }
+ }
+
+ private function parseFilter($paths, &$params) {
+ $params['filters'] = [];
+ $filter_str = substr($paths, 8);
+ $filter_options = explode(';', $filter_str);
+ foreach ($filter_options as $filter_option) {
+ $filter_exp = explode('=', $filter_option);
+ if(!empty($filter_exp[1])){
+ list($filter_key, $filter_option) = explode('=', $filter_option);
+ if($filter_key == 'prices') { // price-interval section
+ $prices = explode(':', $filter_option);
+ $params['filters'][$filter_key] = [
+ 'min' => floatval($prices[0]),
+ 'max' => floatval($prices[1]),
+ ];
+ }
+ else { // brands and other sections
+ $params['filters'][$filter_key] = explode(',', $filter_option);
+ }
+ } else {
+ throw new HttpException(404 ,'Page not found');
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/common/modules/product/Module.php b/common/modules/product/Module.php
new file mode 100755
index 0000000..f48c984
--- /dev/null
+++ b/common/modules/product/Module.php
@@ -0,0 +1,24 @@
+owner->hasMany(TaxOption::className(), ['tax_option_id' => 'option_id'])
+ ->viaTable(ProductOption::tableName(),[ 'product_id'=> $this->owner->tableSchema->primaryKey[0]])
+ ->joinWith('taxGroup')->all();
+ }
+
+
+}
\ No newline at end of file
diff --git a/common/modules/product/config.php b/common/modules/product/config.php
new file mode 100755
index 0000000..a7f3866
--- /dev/null
+++ b/common/modules/product/config.php
@@ -0,0 +1,8 @@
+ [
+ 'category_group' => 1,
+ 'brand_group' => 2,
+ ],
+];
\ No newline at end of file
diff --git a/common/modules/product/controllers/DefaultController.php b/common/modules/product/controllers/DefaultController.php
new file mode 100755
index 0000000..b43058a
--- /dev/null
+++ b/common/modules/product/controllers/DefaultController.php
@@ -0,0 +1,20 @@
+render('index');
+ }
+}
diff --git a/common/modules/product/controllers/ManageController.php b/common/modules/product/controllers/ManageController.php
new file mode 100755
index 0000000..ae4b0fa
--- /dev/null
+++ b/common/modules/product/controllers/ManageController.php
@@ -0,0 +1,278 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['POST'],
+ ],
+ ],
+ ];
+ }
+
+ public function actionDev() {
+ foreach (ProductVariant::find()->where(['>', 'price_old', 0])->all() as $item) {
+ if ($item->price >= $item->price_old || $item->price == 0)
+ print $item->price .' | '. $item->price_old ."
\n";
+ }
+ print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~';
+ }
+
+ /**
+ * Lists all Product models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new ProductSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Displays a single Product model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new Product model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $model = new Product();
+
+ if ($model->load(Yii::$app->request->post())) {
+ if ($model->save()) {
+
+ return $this->redirect(['view', 'id' => $model->product_id]);
+ }
+ } else {
+ return $this->render('create', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing Product model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionUpdate($id)
+ {
+ $model = $this->findModel($id);
+ if ($model->load(Yii::$app->request->post())) {
+ $model->unlinkAll('options',true);
+ if ($model->save()) {
+ return $this->redirect(['view', 'id' => $model->product_id]);
+ }
+ } else {
+ $groups = $model->getTaxGroupsByLevel(0);
+
+ return $this->render('update', [
+ 'model' => $model,
+ 'groups' => $groups,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing Product model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($id)
+ {
+ $this->findModel($id)->delete();
+
+ return $this->redirect(['index']);
+ }
+
+ public function actionDelimg($id)
+ {
+ $image = ProductImage::findOne($id);
+
+ if ($image) {
+ $image->delete();
+ }
+
+ print '1';
+ exit;
+ }
+
+ public function actionIs_top($id) {
+ $model = $this->findModel($id);
+
+ $model->is_top = intval(empty($model->is_top));
+
+ $model->save(false, ['is_top']);
+
+ return $this->redirect(['index']);
+ }
+
+ public function actionIs_new($id) {
+ $model = $this->findModel($id);
+
+ $model->is_new = intval(empty($model->is_new));
+
+ $model->save(false, ['is_new']);
+
+ return $this->redirect(['index']);
+ }
+
+ public function actionAkciya($id) {
+ $model = $this->findModel($id);
+
+ $model->akciya = intval(empty($model->akciya));
+
+ $model->save(false, ['akciya']);
+
+ return $this->redirect(['index']);
+ }
+
+ public function actionImport() {
+ $model = new Import();
+
+ if ($model->load(Yii::$app->request->post())) {
+ $file = UploadedFile::getInstances($model, 'file');
+
+ $method = 'go'. ucfirst($model->type);
+ $target = Yii::getAlias('@uploadDir') .'/'. Yii::getAlias('@uploadFile'. ucfirst($model->type));
+
+ if (empty($file)) {
+ $model->errors[] = 'File not upload';
+ } elseif ($method == 'goPrices' && $file[0]->name != 'file_1.csv') {
+ $model->errors[] = 'File need "file_1.csv"';
+ } elseif ($method == 'goProducts' && $file[0]->name == 'file_1.csv') {
+ $model->errors[] = 'File can not "file_1.csv"';
+ } elseif ($model->validate() && $file[0]->saveAs($target)) {
+ // PROCESS PAGE
+ return $this->render('import-process', [
+ 'model' => $model,
+ 'method' => $model->type,
+ 'target' => $target,
+ ]);
+// $model->$method();
+ } else {
+ $model->errors[] = 'File can not be upload or other error';
+ }
+ }
+
+ return $this->render('import', [
+ 'model' => $model,
+ ]);
+ }
+
+ public function actionProducts() {
+ $from = Yii::$app->request->get('from', 0);
+
+ $model = new Import();
+
+ if (Yii::$app->request->isAjax) {
+ Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
+ return $model->goProducts($from, 10);
+ }
+ }
+
+ public function actionPrices() {
+ $from = Yii::$app->request->get('from', 0);
+
+ $model = new Import();
+
+ if (Yii::$app->request->isAjax) {
+ Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
+ return $model->goPrices($from, 10);
+ }
+ }
+
+ public function actionExportProcess($from, $filename)
+ {
+
+ $model = new Export();
+ if(Yii::$app->request->isAjax) {
+ Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
+ return $model->process($filename,$from);
+ }
+ }
+
+ public function actionExport()
+ {
+ $model = new Export();
+
+ if($model->load(Yii::$app->request->post())) {
+ // PROCESS PAGE
+ return $this->render('export-process', [
+ 'model' => $model,
+ 'method' => 'export',
+ ]);
+ }
+
+ return $this->render('export', [
+ 'model' => $model,
+ ]);
+ // $model = new Export();
+ // if(( $file = $model->process(Yii::getAlias('@uploadDir')) )) {
+ // return Yii::$app->response->sendFile($file)
+ // ->send();
+ // }
+ // throw new NotFoundHttpException('Error');
+ }
+
+ /**
+ * Finds the Product model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return Product the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = Product::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/common/modules/product/controllers/ProductUnitController.php b/common/modules/product/controllers/ProductUnitController.php
new file mode 100755
index 0000000..7ee6139
--- /dev/null
+++ b/common/modules/product/controllers/ProductUnitController.php
@@ -0,0 +1,124 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['POST'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all ProductUnit models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new ProductUnitSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Displays a single ProductUnit model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new ProductUnit model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $model = new ProductUnit();
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->product_unit_id]);
+ } else {
+ return $this->render('create', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing ProductUnit model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionUpdate($id)
+ {
+ $model = $this->findModel($id);
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->product_unit_id]);
+ } else {
+ return $this->render('update', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing ProductUnit model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($id)
+ {
+ $this->findModel($id)->delete();
+
+ return $this->redirect(['index']);
+ }
+
+ /**
+ * Finds the ProductUnit model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return ProductUnit the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = ProductUnit::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/common/modules/product/controllers/ProductVariantTypeController.php b/common/modules/product/controllers/ProductVariantTypeController.php
new file mode 100755
index 0000000..840f498
--- /dev/null
+++ b/common/modules/product/controllers/ProductVariantTypeController.php
@@ -0,0 +1,124 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['POST'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all ProductVariantType models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new ProductVariantTypeSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ ]);
+ }
+
+ /**
+ * Displays a single ProductVariantType model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new ProductVariantType model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $model = new ProductVariantType();
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->product_variant_type_id]);
+ } else {
+ return $this->render('create', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing ProductVariantType model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionUpdate($id)
+ {
+ $model = $this->findModel($id);
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['view', 'id' => $model->product_variant_type_id]);
+ } else {
+ return $this->render('update', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing ProductVariantType model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($id)
+ {
+ $this->findModel($id)->delete();
+
+ return $this->redirect(['index']);
+ }
+
+ /**
+ * Finds the ProductVariantType model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return ProductVariantType the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = ProductVariantType::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/common/modules/product/controllers/VariantController.php b/common/modules/product/controllers/VariantController.php
new file mode 100755
index 0000000..aebb745
--- /dev/null
+++ b/common/modules/product/controllers/VariantController.php
@@ -0,0 +1,247 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['POST'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all ProductVariant models.
+ * @return mixed
+ */
+ public function actionIndex($product_id)
+ {
+ $searchModel = new ProductVariantListSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams,$product_id);
+
+ if ( ($product = Yii::$app->request->get('product_id')) !== null) {
+ $product = Product::findOne($product);
+ }
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ 'product' => $product,
+ 'product_id' => $product_id,
+ ]);
+ }
+
+ /**
+ * Displays a single ProductVariant model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new ProductVariant model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate($product_id)
+ {
+ $model = new ProductVariant();
+ $model->product_id = $product_id;
+ if ($model->load(Yii::$app->request->post())) {
+
+ $model->imagesUpload = UploadedFile::getInstances($model, 'imagesUpload');
+ $model->validate();
+
+ if ($model->save()) {
+
+ if ( ($images = $model->imagesUpload()) !== FALSE) {
+ foreach ($images as $image) {
+ $imageModel = new ProductImage();
+ $imageModel->product_id = $model->product_id;
+ $imageModel->product_variant_id = $model->product_variant_id;
+ $imageModel->image = $image;
+ $imageModel->save();
+ }
+ }
+
+ return $this->redirect(['index', 'product_id' => $product_id]);
+ }
+ } else {
+ $groups = $model->getTaxGroupsByLevel(1);
+
+ return $this->render('create', [
+ 'model' => $model,
+ 'groups' => $groups,
+ 'stocks' => [new ProductStock()],
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing ProductVariant model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param integer $product_id
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionUpdate($product_id, $id)
+ {
+ $model = $this->findModel($id);
+ if ($model->load(Yii::$app->request->post())) {
+ $model->unlinkAll('options',true);
+ if ($model->save()) {
+
+
+ if ( ($image = UploadedFile::getInstance($model, 'image')) ) {
+ $imageModel = ProductImage::find()->where(['product_variant_id' => $model->product_variant_id])->one();
+
+ if($imageModel instanceof ProductImage) {
+ $imageModel->product_id = $model->product_id;
+ $imageModel->product_variant_id = $model->product_variant_id;
+ $imageModel->image = $image->name;
+ $imageModel->save();
+ } else {
+ $imageModel = new ProductImage();
+ $imageModel->product_id = $model->product_id;
+ $imageModel->product_variant_id = $model->product_variant_id;
+ $imageModel->image = $image->name;
+ $imageModel->save();
+ }
+
+
+ $imgDir = Yii::getAlias('@storage/products/');
+
+ if(!is_dir($imgDir)) {
+ mkdir($imgDir, 0755, true);
+ }
+
+ $image->saveAs(Yii::getAlias('@storage/products/' . $image->name));
+ }
+
+
+ $ProductStocks = Yii::$app->request->post('ProductStock');
+
+ $quantity = 0;
+
+ if(is_array($ProductStocks)){
+
+ foreach($ProductStocks as $index => $ProductStock){
+ $stock = Stock::find()->where(['name'=>$ProductStock['name']])->one();
+ if(!$stock instanceof Stock){
+ $stock = new Stock();
+ $stock->name = $ProductStock['name'];
+ $stock->save();
+ }
+
+ $psModel = ProductStock::find()->where(['product_variant_id'=>$model->product_variant_id, 'stock_id' =>$stock->stock_id ])->one();
+ if(!$psModel instanceof ProductStock){
+ $psModel = new ProductStock;
+ $psModel->load(['ProductStock'=>$ProductStock]);
+ $psModel->stock_id = $stock->stock_id;
+ $psModel->product_variant_id = $model->product_variant_id;
+ $psModel->product_id = $model->product->product_id;
+ $quantity = $quantity + $psModel->quantity;
+ $psModel->validate();
+ $psModel->save();
+ } else {
+ $psModel->load(['ProductStock'=>$ProductStock]);
+ $quantity = $quantity + $psModel->quantity;
+ $psModel->save();
+ }
+
+ }
+ } else {
+ $model->unlinkAll('stocks',true);
+ }
+
+
+ $model->stock = $quantity;
+ $model->save();
+ }
+ return $this->redirect(['index', 'product_id'=>$product_id]);
+
+
+
+
+ } else {
+ $groups = $model->getTaxGroupsByLevel(1);
+
+ return $this->render('update', [
+ 'model' => $model,
+ 'groups' => $groups,
+ 'stocks' => (!empty($model->variantStocks)) ? $model->variantStocks : [new ProductStock],
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing ProductVariant model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($product_id, $id)
+ {
+
+ $this->findModel($id)->delete();
+
+ return $this->redirect(['index', 'product_id'=>$product_id]);
+ }
+
+ public function actionDelimg($id)
+ {
+ $image = ProductImage::findOne($id);
+
+ if ($image) {
+ $image->delete();
+ }
+
+ print '1';
+ exit;
+ }
+
+ /**
+ * Finds the ProductVariant model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return ProductVariant the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = ProductVariant::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/common/modules/product/helpers/FilterHelper.php b/common/modules/product/helpers/FilterHelper.php
new file mode 100755
index 0000000..ff8e734
--- /dev/null
+++ b/common/modules/product/helpers/FilterHelper.php
@@ -0,0 +1,246 @@
+where(['is_filter' => 'TRUE'])->all(),'alias');
+ }
+ else
+ {
+ return static::$optionsList;
+ }
+
+ }
+
+ /**
+ * Return custom filter-option link
+ * @var array $filter
+ * @var array $options
+ * @return array
+ */
+ public static function getFilterForOption($filter, $key, $value, $remove = false) {
+
+ $optionsTemplate = self::optionsTemplate();
+ array_unshift($optionsTemplate, "special", "brands");
+
+ $result = $filter;
+
+ if (is_array($value)) {
+ foreach($value as $value_key => $value_items) {
+ if (!is_array($value_items)) {
+ $value_items = [$value_items];
+ }
+ foreach($value_items as $value_item) {
+ if ($remove && isset($result[$key]) && ($i = array_search($value_item, $result[$key][$value_key])) !== FALSE) {
+ unset($result[$key][$value_key][$i]);
+ if (empty($result[$key][$value_key])) {
+ unset($result[$key][$value_key]);
+ }
+ } else {
+ if (!isset($result[$key][$value_key]) || array_search($value_item, $result[$key][$value_key]) === FALSE) {
+ $result[$key][$value_key][] = $value_item;
+ }
+ }
+ }
+ }
+ } else {
+ if ($remove && isset($result[$key]) && ($i = array_search($value, $result[$key])) !== FALSE) {
+ unset($result[$key][$i]);
+ if (empty($result[$key])) {
+ unset($result[$key]);
+ }
+ } else {
+ if (!isset($result[$key]) || array_search($value, $result[$key]) === FALSE) {
+ $result[$key][] = $value;
+ }
+ }
+ }
+
+
+ $filterView = [];
+
+ foreach($optionsTemplate as $optionKey){
+ if(isset($result[$optionKey])){
+ $filterView[$optionKey] = $result[$optionKey];
+ }
+
+
+ }
+
+ return $filterView;
+ }
+
+
+
+ /**
+ * @param ActiveQuery $query
+ * @param array $params
+ */
+ public static function setNewQueryParams($query, $params)
+ {
+ $last_query = null;
+ foreach ($params as $key => $param) {
+ switch ($key) {
+ case 'special':
+ self::filterSpecial($param, $query);
+ break;
+ case 'brands':
+ self::filterBrands($param, $query);
+ break;
+ case 'keywords':
+ self::filterKeywords($param, $query);
+ break;
+ case 'prices':
+ self::filterPrices($param, $query);
+ break;
+ default:
+ $last_query = self::filterOptions($param, $last_query);
+ break;
+ }
+ }
+ if(!empty($last_query)) {
+ $query->andWhere(['product.product_id' => $last_query]);
+ }
+ }
+
+ private static function filterOptions(array $params, Query $last_query = null)
+ {
+ $variant_query = ( new Query() )->distinct()
+ ->select('product_variant.product_id as products')
+ ->from('product_variant_option')
+ ->innerJoin(
+ 'product_variant',
+ 'product_variant_option.product_variant_id = product_variant.product_variant_id'
+ )
+ ->innerJoin('tax_option', 'tax_option.tax_option_id = product_variant_option.option_id')
+ ->where([ 'tax_option.alias' => $params ]);
+ $product_query = ( new Query() )->distinct()
+ ->select('product_option.product_id as products')
+ ->from('product_option')
+ ->innerJoin('tax_option', 'product_option.option_id = tax_option.tax_option_id')
+ ->where(
+ [ 'tax_option.alias' => $params ]
+ )->union($variant_query);
+ $query = (new Query())->select('products')->from(['result_table' => $product_query]);
+ if (!empty( $last_query )) {
+ $query->andWhere([ 'product.product_id' => $last_query ]);
+ }
+ return $query;
+ }
+
+ private static function filterSpecial(array $params, ActiveQuery $query)
+ {
+ $conditions = [];
+ /**
+ * @var string $key
+ */
+ foreach ($params as $key => $param) {
+ $conditions[] = [
+ '=',
+ Product::tableName() . '.' . $key,
+ $param,
+ ];
+ }
+ /* If 2 or more special conditions get all that satisfy at least one of them. */
+ if (count($conditions) > 1) {
+ array_unshift($conditions, 'or');
+ } else {
+ $conditions = $conditions[ 0 ];
+ }
+ $query->andFilterWhere($conditions);
+ }
+
+ private static function filterBrands(array $param, ActiveQuery $query)
+ {
+ $query->andFilterWhere([ Product::tableName() . '.brand_id' => $param ]);
+ }
+
+ private static function filterKeywords(array $params, ActiveQuery $query)
+ {
+ $conditions = [];
+ if (!empty( $params )) {
+ if (!is_array($params)) {
+ $params = [ $params ];
+ }
+ /**
+ * @var string $param Inputed keyword
+ */
+ foreach ($params as $param) {
+ $conditions[] = [
+ 'or',
+ [
+ 'ilike',
+ Product::tableName() . '.name',
+ $param,
+ ],
+ [
+ 'ilike',
+ Brand::tableName() . '.name',
+ $param,
+ ],
+ [
+ 'ilike',
+ Category::tableName() . '.name',
+ $param,
+ ],
+ [
+ 'ilike',
+ ProductVariant::tableName() . '.sku',
+ $param,
+ ],
+ ];
+ }
+ }
+ if (count($conditions) > 1) {
+ array_unshift($conditions, 'or');
+ } else {
+ $conditions = $conditions[ 0 ];
+ }
+ $query->andFilterWhere($conditions);
+ }
+
+ private static function filterPrices(array $params, ActiveQuery $query)
+ {
+ $conditions = [];
+ if (!empty( $params[ 'min' ] ) && $params[ 'min' ] > 0) {
+ $conditions[] = [
+ '>=',
+ ProductVariant::tableName() . '.price',
+ $params[ 'min' ],
+ ];
+ }
+ if (!empty( $params[ 'max' ] ) && $params[ 'max' ] > 0) {
+ $conditions[] = [
+ '<=',
+ ProductVariant::tableName() . '.price',
+ $params[ 'max' ],
+ ];
+ }
+ if (count($conditions) > 1) {
+ array_unshift($conditions, 'and');
+ } else {
+ $conditions = $conditions[ 0 ];
+ }
+ $query->andFilterWhere($conditions);
+ }
+
+
+}
\ No newline at end of file
diff --git a/common/modules/product/helpers/ProductHelper.php b/common/modules/product/helpers/ProductHelper.php
new file mode 100755
index 0000000..1935100
--- /dev/null
+++ b/common/modules/product/helpers/ProductHelper.php
@@ -0,0 +1,342 @@
+getTree(); // with('categoryName')->
+ }
+
+ public static function getBrands()
+ {
+ return Brand::find(); // ->with('brandName')
+ }
+
+ /**
+ * Return custom filter-option link
+ * @var array $filter
+ * @var array $options
+ * @return array
+ */
+ public static function getFilterForOption($filter, $key, $value, $remove = false)
+ {
+ $result = $filter;
+ if(is_array($value)) {
+ foreach($value as $value_key => $value_items) {
+ if(!is_array($value_items)) {
+ $value_items = [ $value_items ];
+ }
+ foreach($value_items as $value_item) {
+ if($remove && isset( $result[ $key ] ) && ( $i = array_search($value_item, $result[ $key ][ $value_key ]) ) !== false) {
+ unset( $result[ $key ][ $value_key ][ $i ] );
+ if(empty( $result[ $key ][ $value_key ] )) {
+ unset( $result[ $key ][ $value_key ] );
+ }
+ } else {
+ if(!isset( $result[ $key ][ $value_key ] ) || array_search($value_item, $result[ $key ][ $value_key ]) === false) {
+ $result[ $key ][ $value_key ][] = $value_item;
+ }
+ }
+ }
+ }
+ } else {
+ if($remove && isset( $result[ $key ] ) && ( $i = array_search($value, $result[ $key ]) ) !== false) {
+ unset( $result[ $key ][ $i ] );
+ if(empty( $result[ $key ] )) {
+ unset( $result[ $key ] );
+ }
+ } else {
+ if(!isset( $result[ $key ] ) || array_search($value, $result[ $key ]) === false) {
+ $result[ $key ][] = $value;
+ }
+ }
+ }
+ return $result;
+ }
+
+ public static function addLastProsucts($product_id)
+ {
+ $last_products = self::getLastProducts();
+ if(!in_array($product_id, $last_products)) {
+ $last_products[] = intval($product_id);
+ if(count($last_products) > 16) {
+ array_shift($last_products);
+ }
+ Yii::$app->session->set('last_products', $last_products);
+ }
+ }
+
+ public static function getLastProducts($as_object = false)
+ {
+ $last_products = Yii::$app->session->get('last_products', [ ]);
+ if($as_object) {
+ $last_products = Product::find()
+ ->joinWith([ 'variant' ])
+ ->where([ Product::tableName() . '.product_id' => $last_products ])
+ ->andWhere([
+ '!=',
+ ProductVariant::tableName() . '.status',
+ 1,
+ ])
+ ->all();
+ }
+ return array_reverse($last_products);
+ }
+
+ public static function getSpecialProducts($type, $count, $sort = NULL)
+ {
+ switch($type) {
+ case 'top':
+ $data = [ 'is_top' => true ];
+ break;
+ case 'new':
+ $data = [ 'is_new' => true ];
+ break;
+ case 'promo':
+ $data = [ 'akciya' => true ];
+ break;
+ }
+ return Product::find()
+ ->joinWith('variants')
+ ->where($data)
+ ->andWhere([
+ '!=',
+ ProductVariant::tableName() . '.status',
+ 1,
+ ])
+ ->limit($count)/*->orderBy($sort)*/
+ ->all();
+ }
+
+ public static function getSimilarProducts($product, $count = 10)
+ {
+ if(!is_object($product)) {
+ $product = Product::find()
+ ->where([ 'product_id' => $product ])
+ ->with('enabledVariants')
+ ->one();
+ }
+
+ if(!$product->properties) {
+ return [ ];
+ }
+ $product_categories = [ ];
+ foreach($product->categories as $category) {
+ $product_categories[] = $category->category_id;
+ }
+ $query = Product::find()
+
+ ->innerJoinWith('variant')
+ ->joinWith('category')
+ ->where([
+ '!=',
+ 'product_variant.status',
+ 1,
+ ])
+ ->andWhere([ 'product_category.category_id' => $product_categories ]);
+ // $query->andWhere(['>=', 'product_variant.price', $product->enabledVariant->price * 0.7]);
+ // $query->andWhere(['<=', 'product_variant.price', $product->enabledVariant->price * 1.3]);
+ foreach($product->properties as $group) {
+ $where = [ ];
+ foreach($group->_options as $option) {
+ $where[] = $option->tax_option_id;
+ }
+ if(!$where) {
+ continue;
+ }
+ $query->innerJoin('product_option to' . $group->tax_group_id, 'to' . $group->tax_group_id . '.product_id = product.product_id');
+ $query->andWhere([ 'to' . $group->tax_group_id . '.option_id' => $where ]);
+ }
+ $query->andWhere([
+ '!=',
+ 'product.product_id',
+ $product->product_id,
+ ]);
+ $query->groupBy('product.product_id');
+ $query->limit($count);
+ $products = $query
+ ->all();
+
+ return $products;
+ }
+
+ /**
+ * @param ActiveQuery $query
+ * @param $params
+ * @param bool $setPriceLimits
+ */
+ public static function _setQueryParams(&$query, $params)
+ {
+ if(!empty( $params[ 'keywords' ] )) {
+ if(!is_array($params[ 'keywords' ])) {
+ $params[ 'keywords' ] = [ $params[ 'keywords' ] ];
+ }
+ foreach($params[ 'keywords' ] as $keyword) {
+ $query->orFilterWhere([
+ 'ilike',
+ Product::tableName() . '.name',
+ $keyword,
+ ]);
+ $query->orFilterWhere([
+ 'ilike',
+ Brand::tableName() . '.name',
+ $keyword,
+ ]);
+ $query->orFilterWhere([
+ 'ilike',
+ Category::tableName() . '.name',
+ $keyword,
+ ]);
+ $query->orFilterWhere([
+ 'ilike',
+ ProductVariant::tableName() . '.sku',
+ $keyword,
+ ]);
+ }
+ }
+
+ foreach($params as $key => $param) {
+
+ switch($key) {
+ case 'special':
+ foreach($param as $key => $value) {
+ $query->orFilterWhere([ Product::tableName() . '.' . $key => $value ]);
+ }
+ break;
+ case 'brands':
+ $query->andFilterWhere([ Product::tableName() . '.brand_id' => $param ]);
+ break;
+ case 'keywords':
+ break;
+ case 'prices':
+ if($param[ 'min' ] > 0) {
+ $query->andWhere([
+ '>=',
+ ProductVariant::tableName() . '.price',
+ $param[ 'min' ],
+ ]);
+ }
+ if($param[ 'max' ] > 0) {
+ $query->andWhere([
+ '<=',
+ ProductVariant::tableName() . '.price',
+ $param[ 'max' ],
+ ]);
+ }
+ break;
+ default:
+ $query->andWhere(
+ Product::tableName() . '.product_id IN (
+ SELECT DISTINCT products
+ FROM (
+ SELECT product_id AS products FROM product WHERE product_id IN(
+ SELECT product_id FROM product_option
+ INNER JOIN tax_option ON tax_option.tax_option_id = product_option.option_id
+ INNER JOIN tax_group ON tax_group.tax_group_id = tax_option.tax_group_id
+ WHERE tax_group.alias = \''. $key .'\' AND tax_option.alias IN (\'' . implode('\',\'', $param) . '\'))
+ OR product_id IN (
+ (SELECT product_id AS products
+ FROM product_variant_option
+ INNER JOIN product_variant ON product_variant_option.product_variant_id = product_variant.product_variant_id
+ INNER JOIN tax_option ON tax_option.tax_option_id = product_variant_option.option_id
+ INNER JOIN tax_group ON tax_group.tax_group_id = tax_option.tax_group_id
+ WHERE tax_group.alias = \''. $key .'\' AND tax_option.alias IN (\'' . implode('\',\'', $param) . '\'))
+ )
+ ) AS table_name
+ )'
+ );
+ }
+
+ }
+
+ }
+
+ /**
+ * @param ActiveQuery $query
+ * @param $params
+ * @param bool $setPriceLimits
+ */
+ public static function setCacheQueryParams(&$query, $params)
+ {
+
+
+ foreach($params as $key => $param) {
+
+
+ $query->andWhere(
+ Product::tableName() . '.product_id IN (
+ SELECT DISTINCT products
+ FROM (
+ SELECT product_id AS products FROM product WHERE product_id IN(
+ SELECT product_id FROM product_option
+ INNER JOIN tax_option ON tax_option.tax_option_id = product_option.option_id
+ INNER JOIN tax_group ON tax_group.tax_group_id = tax_option.tax_group_id
+ WHERE tax_group.alias = \''. $key .'\' AND tax_option.alias IN (\'' . implode('\',\'', $param) . '\'))
+ OR product_id IN (
+ (SELECT product_id AS products
+ FROM product_variant_option
+ INNER JOIN product_variant ON product_variant_option.product_variant_id = product_variant.product_variant_id
+ INNER JOIN tax_option ON tax_option.tax_option_id = product_variant_option.option_id
+ INNER JOIN tax_group ON tax_group.tax_group_id = tax_option.tax_group_id
+ WHERE tax_group.alias = \''. $key .'\' AND tax_option.alias IN (\'' . implode('\',\'', $param) . '\'))
+ )
+ ) AS table_name
+ )'
+ );
+ }
+
+
+ }
+
+
+
+
+
+
+ public static function productCountQuery($category = NULL, $params, $excludeKeys = [ ])
+ {
+ $p = [ ];
+ foreach($params as $key => $param) {
+ if(in_array($key, $excludeKeys)) {
+ $p[ $key ] = $param;
+ }
+ }
+ /** @var ActiveQuery $query */
+ if(!empty( $category )) {
+ $query = $category->getProducts();
+ } else {
+ $query = Product::find();
+ }
+ ProductHelper::_setQueryParams($query, $params);
+ $query->select([ 'COUNT(product.product_id)' ]);
+
+ return $query;
+ }
+
+ public static function addLastCategory($category_id) {
+ \Yii::$app->session->set('last_category_id', $category_id);
+ }
+
+ public static function getLastCategory() {
+ return \Yii::$app->session->get('last_category_id');
+ }
+ }
\ No newline at end of file
diff --git a/common/modules/product/models/Brand.php b/common/modules/product/models/Brand.php
new file mode 100755
index 0000000..efb9e49
--- /dev/null
+++ b/common/modules/product/models/Brand.php
@@ -0,0 +1,122 @@
+ SaveImgBehavior::className(),
+ 'fields' => [
+ ['name'=>'image','directory' => 'brands' ]
+ ]
+ ],
+ 'slug' => [
+ 'class' => 'common\behaviors\Slug',
+ 'in_attribute' => 'name',
+ 'out_attribute' => 'alias',
+ 'translit' => true
+ ],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return [
+ [['name'], 'string'],
+ [['brand_name_id'], 'integer'],
+ [['in_menu'], 'boolean'],
+ [['meta_desc', 'seo_text'], 'string'],
+ [['alias', 'name'], 'string', 'max' => 250],
+ [['meta_title', 'image'], 'string', 'max' => 255],
+ [['meta_robots'], 'string', 'max' => 50],
+ [['imageUpload'], 'safe'],
+ [['imageUpload'], 'file', 'extensions' => 'jpg, gif, png'],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'name' => Yii::t('product', 'Name of the brand'),
+ 'brand_id' => Yii::t('product', 'Brand ID'),
+ 'brand_name_id' => Yii::t('product', 'Brand Name ID'),
+ 'alias' => Yii::t('product', 'Alias'),
+ 'image' => Yii::t('product', 'Image'),
+ 'imageUrl' => Yii::t('product', 'Image'),
+ 'meta_title' => Yii::t('product', 'Meta Title'),
+ 'meta_desc' => Yii::t('product', 'Meta Desc'),
+ 'meta_robots' => Yii::t('product', 'Meta Robots'),
+ 'seo_text' => Yii::t('product', 'Seo Text'),
+ 'in_menu' => Yii::t('product', 'Выводить в меню'),
+ ];
+ }
+
+
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getProducts()
+ {
+ return $this->hasMany(Product::className(), ['brand_id' => 'brand_id']);
+ }
+
+ public function getImageFile() {
+ return empty($this->image) ? null : '/storage/brands/'. $this->image;
+ }
+
+ public function getImageUrl() {
+ return empty($this->image) ? null : '/storage/brands/'. $this->image;
+ }
+
+}
diff --git a/common/modules/product/models/BrandQuery.php b/common/modules/product/models/BrandQuery.php
new file mode 100755
index 0000000..5351a07
--- /dev/null
+++ b/common/modules/product/models/BrandQuery.php
@@ -0,0 +1,47 @@
+andWhere('[[status]]=1');
+ }*/
+
+ /**
+ * @inheritdoc
+ * @return Brand[]|array
+ */
+ public function all($db = null)
+ {
+// $this->with('brandName');
+ return parent::all($db);
+ }
+
+ /**
+ * @inheritdoc
+ * @return Brand|array|null
+ */
+ public function one($db = null)
+ {
+// $this->with('brandName');
+ return parent::one($db);
+ }
+
+ /**
+ * Select brand by alias
+ * @param $slug
+ * @return $this
+ */
+ public function byAlias($alias)
+ {
+ $this->andFilterWhere(['alias' => $alias]);
+ return $this;
+ }
+}
diff --git a/common/modules/product/models/BrandSearch.php b/common/modules/product/models/BrandSearch.php
new file mode 100755
index 0000000..2e800e0
--- /dev/null
+++ b/common/modules/product/models/BrandSearch.php
@@ -0,0 +1,125 @@
+with('brandName')
+
+ // add conditions that should always apply here
+
+ $dataProvider = new ActiveDataProvider([
+ 'query' => $query,
+ ]);
+
+ $this->load($params);
+
+ /*if (!$this->validate()) {
+ // uncomment the following line if you do not want to return any records when validation fails
+ // $query->where('0=1');
+ return $dataProvider;
+ }*/
+
+ $dataProvider->setSort([
+ 'attributes' => [
+ 'brand_name',
+ 'alias'
+ ]
+ ]);
+
+ // grid filtering conditions
+ $query->andFilterWhere([
+ 'brand_id' => $this->brand_id,
+ 'brand_name_id' => $this->brand_name_id,
+ ]);
+
+
+ $query->andFilterWhere(['ilike', 'alias', $this->alias])
+ ->andFilterWhere(['ilike', 'image', $this->image])
+ ->andFilterWhere(['ilike', 'meta_title', $this->meta_title])
+ ->andFilterWhere(['ilike', 'meta_desc', $this->meta_desc])
+ ->andFilterWhere(['ilike', 'meta_robots', $this->meta_robots])
+ ->andFilterWhere(['ilike', 'seo_text', $this->seo_text]);
+ if (!empty($this->brand_name)) {
+ $query->andFilterWhere(['ilike', 'name', $this->brand_name]);
+ }
+
+ $query->orderBy('brand_id', 'asc');
+
+ return $dataProvider;
+ }
+
+ public function getBrands($category = null) {
+
+ $query = Brand::find()
+ ->select([
+ Brand::tableName() .'.*'
+ ])
+ ->innerJoin(Product::tableName(), Product::tableName() .'.brand_id='. Brand::tableName() .'.brand_id')
+ ->innerJoin(ProductCategory::tableName(), ProductCategory::tableName() .'.product_id='. Product::tableName() .'.product_id');
+
+
+ $query->innerJoin('product_variant', 'product_variant.product_id = '. Product::tableName() .'.product_id');
+ $query->where(['!=', 'product_variant.status', 1]);
+ $query->groupBy(Product::tableName() .'.product_id');
+ if (!empty($category)) {
+ $query->andWhere([
+ ProductCategory::tableName() .'.category_id' => $category->category_id
+ ]);
+ }
+ $query->groupBy(Brand::tableName() .'.brand_id');
+
+ return $query;
+ }
+
+ public static function findByAlias($alias) {
+ /** @var CategoryQuery $query */
+ $query = Brand::find()
+ ->andFilterWhere(['alias' => $alias]);
+ if (($model = $query->one()) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/common/modules/product/models/Category.php b/common/modules/product/models/Category.php
new file mode 100755
index 0000000..0c9870a
--- /dev/null
+++ b/common/modules/product/models/Category.php
@@ -0,0 +1,331 @@
+ [
+ 'class' => ArtboxTreeBehavior::className(),
+ 'keyNameGroup' => null,
+ 'keyNamePath' => 'path',
+ ],
+ 'slug' => [
+ 'class' => 'common\behaviors\Slug',
+ 'in_attribute' => 'name',
+ 'out_attribute' => 'alias',
+ 'translit' => true
+ ],
+
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public static function tableName()
+ {
+ return 'category';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return [
+ [['name'], 'string'],
+ [['parent_id', 'depth', 'category_name_id', 'product_unit_id'], 'integer'],
+ [['path', 'meta_desc', 'h1', 'seo_text'], 'string'],
+ [['meta_title', 'image'], 'string', 'max' => 255],
+ [['meta_robots'], 'string', 'max' => 50],
+ [['alias', 'name'], 'string', 'max' => 250],
+ [['populary'], 'boolean'],
+ [['imageUpload','taxGroup'], 'safe'],
+ [['imageUpload'], 'file', 'extensions' => 'jpg, gif, png'],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'category_id' => Yii::t('product', 'Category ID'),
+ 'parent_id' => Yii::t('product', 'Parent ID'),
+ 'path' => Yii::t('product', 'Path'),
+ 'depth' => Yii::t('product', 'Depth'),
+ 'image' => Yii::t('product', 'Image'),
+ 'imageUrl' => Yii::t('product', 'Image'),
+ 'meta_title' => Yii::t('product', 'Meta Title'),
+ 'meta_desc' => Yii::t('product', 'Meta Desc'),
+ 'meta_robots' => Yii::t('product', 'Meta Robots'),
+ 'h1' => Yii::t('product', 'h1'),
+ 'seo_text' => Yii::t('product', 'Seo Text'),
+ 'product_unit_id' => Yii::t('product', 'Product Unit ID'),
+ 'alias' => Yii::t('product', 'Alias'),
+ 'populary' => Yii::t('product', 'Populary'),
+ 'name' => Yii::t('product', 'Name'),
+ 'remote_id' => Yii::t('product', 'Remote ID'),
+ ];
+ }
+
+ public static function find()
+ {
+ return new CategoryQuery(get_called_class());
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getProductUnit()
+ {
+ return $this->hasOne(ProductUnit::className(), ['product_unit_id' => 'product_unit_id']);
+ }
+
+ public function getProducts() {
+ return $this->hasMany(Product::className(), ['product_id' => 'product_id'])
+ ->viaTable('product_category', ['category_id' => 'category_id']);
+ }
+
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getProductCategories()
+ {
+ return $this->hasMany(ProductCategory::className(), ['category_id' => 'category_id']);
+ }
+
+ public function getBrands(){
+ return $this->getProducts()->select('brand.*')->joinWith('brand')->groupBy('brand.brand_id');
+
+ }
+
+ public function getTaxGroupsByLevel($level)
+ {
+ return $this->hasMany(TaxGroup::className(), ['tax_group_id' => 'tax_group_id'])
+ ->viaTable('tax_group_to_category', ['category_id' => 'category_id'])
+ ->andWhere(['level' => $level]);
+ }
+
+ public function getRemote_category()
+ {
+ return ArtboxTreeHelper::getArrayField($this->remote_id);
+ }
+
+ public function setRemote_category($value)
+ {
+ if (!empty($value) && is_array($value)) {
+ $this->remote_id = ArtboxTreeHelper::setArrayField($value, false);
+ }
+ }
+
+
+
+
+ public function getImageFile() {
+ return empty($this->image) ? '/storage/no_photo.png' : Yii::getAlias('@imagesDir/categories/'. $this->image);
+ }
+
+ public function getImageUrl()
+ {
+ return empty($this->image) ? '/storage/no_photo.png' : Yii::getAlias('@imagesUrl/categories/' . $this->image);
+ }
+
+ public function beforeSave($insert)
+ {
+ if (parent::beforeSave($insert)) {
+
+ if (empty($this->parent_id))
+ $this->parent_id = 0;
+
+ return true;
+ }
+ return false;
+ }
+
+ public function beforeDelete()
+ {
+
+ if(!empty($this->products)){
+ foreach($this->products as $product){
+ $product->delete();
+ }
+ }
+ ProductCategory::deleteAll(['category_id' => $this->category_id]);
+ return true;
+ }
+
+ /**
+ * @param array $product_id
+ * @param array $product_variant_id
+ * @return ActiveQuery
+ */
+
+ public function getFilterQuery( $product_id = [], $product_variant_id = []){
+ $query1 = (new Query())
+ ->distinct()
+ ->select([
+ 'option_id'
+ ])
+ ->from('tax_option')
+ ->innerJoin('product_variant_option', 'tax_option.tax_option_id = product_variant_option.option_id')
+ ->innerJoin('tax_group', 'tax_group.tax_group_id = tax_option.tax_group_id')
+ ->innerJoin('product_variant', 'product_variant.product_variant_id = product_variant_option.product_variant_id')
+ ->innerJoin('product', 'product.product_id = product_variant.product_id')
+ ->innerJoin('product_category', 'product_category.product_id = product.product_id')
+ ->innerJoin('tax_group_to_category', 'tax_group.tax_group_id = tax_group_to_category.tax_group_id')
+ ->where(['product_category.category_id' => $this->category_id,
+ 'tax_group.is_filter' => TRUE,
+ 'tax_group_to_category.category_id'=>$this->category_id,
+
+ ])
+ ->filterWhere([
+ 'product_variant_option.product_variant_id' => $product_variant_id
+ ])
+ ->andWhere(['!=', 'product_variant.status', 1]);
+
+ $query2 = (new Query())
+ ->distinct()
+ ->select([
+ 'option_id'
+ ])
+ ->from('tax_option')
+ ->innerJoin('product_option', 'tax_option.tax_option_id = product_option.option_id')
+ ->innerJoin('tax_group', 'tax_group.tax_group_id = tax_option.tax_group_id')
+ ->innerJoin('product', 'product.product_id = product_option.product_id')
+ ->innerJoin('product_category', 'product_category.product_id = product.product_id')
+ ->innerJoin('product_variant', 'product_variant.product_id = product.product_id')
+ ->innerJoin('tax_group_to_category', 'tax_group.tax_group_id = tax_group_to_category.tax_group_id')
+ ->where(['product_category.category_id' => $this->category_id,
+ 'tax_group.is_filter' => TRUE,
+ 'tax_group_to_category.category_id'=>$this->category_id,
+ ])
+ ->filterWhere([
+ 'product_option.product_id' => $product_id
+ ])
+ ->andWhere(['!=', 'product_variant.status', 1]);
+ $query3 = (new Query())
+ ->select([
+ 'tax_option.*',
+ 'tax_group.*',
+ 'tax_option.alias as option_alias',
+ 'tax_group.alias as group_alias',
+ 'tax_option.name as value',
+ 'tax_option.sort AS tax_option_sort',
+ 'tax_group.sort AS tax_group_sort',
+ ])
+ ->from(['tax_option' ])
+ ->where(['tax_option.tax_option_id'=>$query1->union($query2)])
+
+ ->innerJoin('tax_group','tax_group.tax_group_id = tax_option.tax_group_id')
+ ->orderBy('tax_option.sort, tax_group.sort');
+ return $query3;
+ }
+
+ /**
+ * @return mixed
+ * @throws \Exception
+ */
+
+
+ public function getActiveFilters() {
+ return Category::getDb()->cache(function(){
+ return $this->getFilterQuery()->all();
+ }, 3600);
+
+ }
+
+ /**
+ * @param array $product_id
+ * @param array $product_variant_id
+ * @return mixed
+ */
+ public function getSelectFilters($product_id = [], $product_variant_id = []) {
+
+ return $this->getFilterQuery($product_id, $product_variant_id)->select('tax_option_id')->column();
+
+ }
+
+
+ public function getTaxGroupsForMenu()
+ {
+
+ $connection = Yii::$app->getDb();
+ $command = $connection->createCommand('
+ SELECT ton.alias as option_alias,ton.name as value, *
+ FROM tax_option as ton
+ RIGHT JOIN tax_group ON ton.tax_group_id = tax_group.tax_group_id
+ RIGHT JOIN tax_group_to_category ON tax_group.tax_group_id = tax_group_to_category.tax_group_id
+ WHERE ton.tax_option_id IN (
+ SELECT po.option_id FROM product_option as po
+ WHERE po.product_id IN (
+ SELECT product_id FROM product_category WHERE category_id = :category_id
+ )
+ )
+ AND tax_group.is_menu = true AND tax_group_to_category.category_id = :category_id',
+ [
+ ':category_id' => $this->category_id
+
+ ]);
+ return $command->queryAll();
+
+
+
+ }
+
+
+ public function setTaxGroup($value)
+ {
+ return $this->taxgroup = $value;
+ }
+
+ public function getTaxGroup()
+ {
+ return $this->hasMany(TaxGroup::className(), ['tax_group_id' => 'tax_group_id'])
+ ->viaTable('tax_group_to_category', ['category_id' => 'category_id']);
+ }
+
+}
diff --git a/common/modules/product/models/CategoryQuery.php b/common/modules/product/models/CategoryQuery.php
new file mode 100755
index 0000000..b7abeaa
--- /dev/null
+++ b/common/modules/product/models/CategoryQuery.php
@@ -0,0 +1,38 @@
+andWhere('[[status]]=1');
+ }*/
+
+ /**
+ * @inheritdoc
+ * @return Category[]|array
+ */
+ public function all($db = null)
+ {
+ return parent::all($db);
+ }
+
+ /**
+ * @inheritdoc
+ * @return Category|array|null
+ */
+ public function one($db = null)
+ {
+// $this->joinWith('categoryName');
+ return parent::one($db);
+ }
+}
diff --git a/common/modules/product/models/CategorySearch.php b/common/modules/product/models/CategorySearch.php
new file mode 100755
index 0000000..e2ccfd1
--- /dev/null
+++ b/common/modules/product/models/CategorySearch.php
@@ -0,0 +1,114 @@
+ $query,
+ ]);
+
+ $this->load($params);
+
+ /*if (!$this->validate()) {
+ // uncomment the following line if you do not want to return any records when validation fails
+ // $query->where('0=1');
+ return $dataProvider;
+ }*/
+
+ // grid filtering conditions
+ $query->andFilterWhere([
+ 'category.category_id' => $this->category_id,
+ 'category.parent_id' => $this->parent_id,
+ 'category.category_name_id' => $this->category_name_id,
+ 'category.product_unit_id' => $this->product_unit_id,
+ ]);
+
+ $query->andFilterWhere(['like', 'category.alias', $this->alias]);
+
+ $query->orderBy(['category.path' => SORT_ASC, 'category.depth' => SORT_ASC, 'category.category_id' => SORT_ASC]);
+
+ return $dataProvider;
+ }
+
+ public static function findByAlias($alias) {
+ /** @var CategoryQuery $query */
+ $query = Category::find()
+ ->andFilterWhere(['alias' => $alias]);
+ if (($model = $query->one()) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+
+ public static function findByRemoteID($id) {
+ /** @var CategoryQuery $query */
+ $query = Category::find()
+ ->andFilterWhere(['@>', 'remote_id', ArtboxTreeHelper::setArrayField($id)]);
+ if (($model = $query->one()) !== null) {
+ return $model;
+ }
+ return null;
+ }
+
+// public static function findByProductsKeywords($keywords = [], $category = null) {
+//
+// }
+}
diff --git a/common/modules/product/models/Export.php b/common/modules/product/models/Export.php
new file mode 100755
index 0000000..cc0736a
--- /dev/null
+++ b/common/modules/product/models/Export.php
@@ -0,0 +1,125 @@
+with([
+ 'variantsWithFilters',
+ 'brand',
+ 'categories',
+ 'filters'])
+ ->limit(100)
+ ->offset($from)
+ ->all();
+ $filesize = Product::find()
+ ->count();
+ foreach($products as $product) {
+
+ $mods = [];
+ $filterString = $this->convertFilterToString($product->filters);
+
+ foreach($product->variantsWithFilters as $variant) {
+ $color = $variant->name;
+ $mods[] = $variant->sku . '=' . $this->convertFilterToString($variant->filters) . '=' . $color . '=' . ( ( !empty( $variant->image ) ) ? $variant->image->image : '' ) . '=' . $variant->stock;
+ }
+
+ $fotos = [];
+
+ $categories = [];
+ foreach($product->categories as $value) {
+ $categories[] = $value->name;
+ }
+
+ $categories = implode(',', $categories);
+
+ $list = [
+ $categories,
+ !empty($product->brand) ? $product->brand ->name :'',
+ $product->name,
+ '',
+ ( ( !empty( $product->description ) ) ? $product->description : '' ),
+ $filterString,
+ ( !empty( $product->variant ) ) ? $product->variant->price_old : '',
+ ( !empty( $product->variant ) ) ? $product->variant->price : '',
+ intval($product->akciya),
+ '',
+ intval($product->is_new),
+ intval($product->is_top),
+ $product->video,
+ implode(',', $fotos),
+ ];
+ $to_write = array_merge($list, $mods);
+ fputcsv($handle, $to_write, ';');
+ unset( $product );
+ }
+
+ fclose($handle);
+
+ $from += 100;
+ $end = false;
+ if($from > $filesize) {
+ $end = true;
+ }
+
+ $result = [
+ 'end' => $end,
+ 'from' => $from,
+ 'totalsize' => $filesize,
+ 'filename' => $filename,
+ ];
+
+ if($end) {
+ $result = array_merge($result, [
+ 'link' => '/storage/sync/'.$filename,
+ ]);
+ }
+
+ return $result;
+
+ }
+
+ public function convertFilterToString($filters)
+ {
+ if(!empty($filters)){
+ $fittersArray = [];
+ foreach($filters as $filter) {
+ if($filter->taxGroup instanceof TaxGroup){
+ $fittersArray[ $filter->taxGroup->alias ][] = $filter->value;
+ }
+
+ }
+ $filterString = [];
+
+ foreach($fittersArray as $filterName => $filterRows) {
+ $row = implode(',', $filterRows);
+ $filterString[] = "[{$filterName}:{$row}]";
+ }
+ return implode('*', $filterString);
+ }
+
+ }
+ }
\ No newline at end of file
diff --git a/common/modules/product/models/Import.php b/common/modules/product/models/Import.php
new file mode 100755
index 0000000..d62f900
--- /dev/null
+++ b/common/modules/product/models/Import.php
@@ -0,0 +1,614 @@
+ 'csv'],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'file' => Yii::t('product', 'File'),
+ ];
+ }
+
+ public function getType() {
+ if (!$this->type) {
+ $this->type = 'products';
+ }
+ return $this->type;
+ }
+
+ public function goPrices($from = 0, $limit = null) {
+ set_time_limit(0);
+ $new_products = $linked_products = 0;
+
+ if ( !($handle = $this->getProductsFile('uploadFilePrices')) ) {
+ $this->errors[] = 'File not found';
+ return FALSE;
+ }
+
+ $filesize = filesize(Yii::getAlias('@uploadDir') .'/'. Yii::getAlias('@uploadFilePrices'));
+ if ($from) {
+ fseek($handle, $from);
+ }
+
+ $j = 0;
+
+ $is_utf = (preg_match('//u', file_get_contents(Yii::getAlias('@uploadDir') .'/'. Yii::getAlias('@uploadFilePrices'), null, null, null, 1000000)));
+
+
+ if($from == 0){
+ ProductStock::updateAll(['quantity' => 0 ]);
+ ProductVariant::updateAll(['status' => 1 ]);
+ }
+
+
+
+ while (empty($limit) || $j++ < $limit)
+ {
+
+
+ if(!(($data = fgetcsv ($handle, 10000, ";")) !== FALSE)){
+ break;
+ }
+
+ foreach ($data as &$value)
+ {
+ if (!$is_utf) {
+ $value = iconv ('windows-1251', "UTF-8//TRANSLIT//IGNORE", $value);
+ }
+ $value = trim ($value);
+ }
+
+ // данные строк
+ $modification_code = @$data[0];
+ $price = floatval(@$data[1]);
+ $price_promo = floatval(@$data[2]);
+ $count = intval(@$data[3]);
+ $city_name = @$data[4];
+ $product_title = @$data[5];
+
+ if (empty ($modification_code)) {
+ continue;
+ }
+ // товары в пути
+ if (empty ($city_name))
+ {
+// $this->saveNotFoundRecord (
+// [$modification_code, $product_title],
+// Yii::getAlias('@uploadFilePricesAway')
+// );
+
+ $this->output[] = 'Товар '. $product_title . ' в пути';
+
+ continue;
+ }
+
+ if ( ($productVariant = ProductVariant::find()->filterWhere(['sku' => $modification_code])->one()) === null ) {
+ // 'Нет даной модификации в базе';
+// $this->saveNotFoundRecord (
+// [$modification_code, $product_title],
+// Yii::getAlias('@uploadFilePricesNoVariant')
+// );
+
+ $this->output[] = 'Для товара '. $product_title . ' не найдено соотвествие';
+
+ continue;
+ }
+
+
+
+
+ // ===== Set stock ====
+ if ( $city_name ) {
+ if ( ($stock = Stock::find()->filterWhere(['name' => trim($city_name)])->one()) === null ) {
+
+ // Create stock
+ $stock = new Stock();
+ $stock->name = trim($city_name);
+ $stock->save();
+ }
+
+ $productStock = ProductStock::find()->where(['product_variant_id' => $productVariant->product_variant_id, 'stock_id' => $stock->stock_id])->one();
+ if(!$productStock instanceof ProductStock) {
+ $productStock = new ProductStock;
+ $productStock->product_variant_id = $productVariant->product_variant_id;
+ $productStock->stock_id = $stock->stock_id;
+ $productStock->product_id = $productVariant->product_id;
+ }
+ $productStock->quantity = $count;
+
+ $productStock->save();
+ $productStocks = ProductStock::find()->where(['product_variant_id' => $productVariant->product_variant_id])->andWhere(['<>', 'stock_id', $stock->stock_id])->all();
+
+ $quantity = array_sum(ArrayHelper::getColumn($productStocks, 'quantity')) + $count;
+ } else {
+
+ $productStocks = ProductStock::find()->where(['product_variant_id' => $productVariant->product_variant_id])->all();
+
+ if($productStocks instanceof ProductStock){
+ $quantity = array_sum(ArrayHelper::getColumn($productStocks, 'quantity')) + $count;
+ } else {
+ $quantity = 0;
+ }
+
+ }
+
+ if ($price_promo) {
+ $productVariant->price_old = $price;
+ $productVariant->price = $price_promo;
+ } else {
+ $productVariant->price = $price;
+ $productVariant->price_old = $price_promo;
+ }
+
+ $productVariant->stock = $quantity;
+ $productVariant->status = 0;
+ $productVariant->save();
+
+ $this->output[] = '
Товар '. $product_title .' успешно сохранен';
+ }
+
+ $result = [
+ 'end' => feof($handle),
+ 'from' => ftell($handle),
+ 'totalsize' => $filesize,
+ 'items' => $this->output,
+
+ ];
+
+ fclose ($handle);
+
+ if ($result['end']) {
+ unlink(Yii::getAlias('@uploadDir') .'/'. Yii::getAlias('@uploadFilePrices'));
+ }
+
+ return $result;
+ }
+
+ public function goProducts($from = 0, $limit = null) {
+
+ set_time_limit(0);
+ $new_products = $linked_products = 0;
+
+ if ( !($handle = $this->getProductsFile('uploadFileProducts')) ) {
+ $this->errors[] = 'File not found';
+ return FALSE;
+ }
+
+ $filesize = filesize(Yii::getAlias('@uploadDir') .'/'. Yii::getAlias('@uploadFileProducts'));
+
+
+ if ($from) {
+ fseek($handle, $from);
+ }
+
+ $j = 0;
+
+ $is_utf = (preg_match('//u', file_get_contents(Yii::getAlias('@uploadDir') .'/'. Yii::getAlias('@uploadFileProducts'), null, null, null, 1000000)));
+
+ $result_items = [];
+
+
+
+ while ( empty($limit) || $j++ < $limit)
+ {
+ if(!(($data = fgetcsv ($handle, 10000, ";")) !== FALSE)){
+ break;
+ }
+
+ foreach ($data as &$value)
+ {
+ if (!$is_utf) {
+ $value = iconv ('windows-1251', "UTF-8//TRANSLIT//IGNORE", $value);
+ }
+ $value = trim ($value);
+ }
+
+ // будет всегда 19 элементов
+ for ($i = 0; $i <= 18; $i++)
+ {
+ if (! isset ($data[$i]))
+ {
+ $data[$i] = null;
+ }
+ }
+
+
+ // 1 Группа (категория)
+ $catalog_names = explode(',',$data[0]);
+
+ if (empty ($catalog_names))
+ {
+ $result_items[] = "Не указана категория (строка $j) ";
+ continue;
+ }
+
+ // 2 Бренд
+ $brand_name = $data[1];
+ if (empty ($brand_name))
+ {
+ $result_items[] = "Не указан бренд (строка $j)";
+ continue;
+ }
+
+ // 3 Название товара
+ $product_name = $data[2];
+ if (empty ($product_name))
+ {
+ $result_items[] = "Не указано наименование товара (строка $j)";
+ continue;
+ }
+
+ // 4 Описание Укр
+ $product_body_uk = $data[3];
+
+ // 5 Описание Рус
+ $product_body_ru = $data[4];
+
+ // 6 Фильтр [god:2013-2014]*[pol:мужской]*[naznacenie-germo-beg:Для вещей]*[material-germo-bag:нет]*[value-germo-bag:нет]*[weight-germo-bag:нет]*[ipx-germo-bag:нет]*[in-pacage-bag:нет]*[size-germo-bag:нет]*[rekomend-germo-bag:нет]
+ $filters = explode ('*', $data[5]);
+
+ // 11 Цена акция
+ $product_cost_old = floatval($data[7]);
+ $product_cost = '';
+ // 10 Цена
+ if ($product_cost_old) {
+ $product_cost_old = floatval($data[6]);
+ $product_cost = floatval($data[7]);
+ }
+
+ // 12 Акция
+ $product_akciya = (bool)$data[8];
+
+ // 13 Сопуд. Тов.
+ $similar = explode (',', $data[9]);
+
+ // 14 Новинки
+ $product_new = (bool)$data[10];
+
+ // 15 Топ продаж
+ $product_top = (bool)$data[11];
+
+
+ // 17 ВИДЕО КОД
+ $product_video = $data[12];
+
+ // 18 Галлерея фото
+ if (trim($data[13])) {
+ $fotos = explode (',', trim($data[13]));
+ }
+
+ // 19 Штрих код товара.
+ // расшифровал - это модификации товара!
+
+ $product_image = explode ('=', $data[14]);
+ $product_image = @$product_image[3];
+
+ if ( ($_product = Product::find()->filterWhere(['name' => trim($product_name)])->one()) === null ) {
+ $_product = new Product();
+ }
+
+ $is_new_product = empty($_product->product_id);
+ $category_id = [];
+ foreach($catalog_names as $catalog_name){
+
+ // ==== Set category ====
+ if ( ($category = Category::find()->filterWhere([ 'name' => trim($catalog_name)])->one()) === null ) {
+ // Create category
+ $category = new Category();
+ $category->name = trim($catalog_name);
+ $category->save();
+ }
+
+ $category_id[] = $category->category_id;
+ }
+
+
+ $_product->categories = $category_id;
+
+ // ===== Set brand ====
+ if ( $brand_name ) {
+ if ( ($brand = Brand::find()->filterWhere(['name' => trim($brand_name)])->one()) !== null ) {
+ $_product->brand_id = $brand->brand_id;
+ } else {
+ // Create brand
+ $brand = new Brand();
+ $brand->name = trim($brand_name);
+ $brand->save();
+ $_product->brand_id = $brand->brand_id;
+ }
+ }
+
+ $_product->name = $product_name;
+ $_product->video = $product_video;
+ $_product->description = $product_body_ru;
+ $_product->is_top = $product_top;
+ $_product->akciya = $product_akciya;
+ $_product->is_new = $product_new;
+
+ if (!$_product->save()) {
+ $result_items[] = 'Product #'. $_product->name .' not saved' . " (строка $j)";
+ continue;
+ }
+
+
+
+ if (!empty($fotos)) {
+ foreach($fotos as $foto) {
+ $source_image = Yii::getAlias('@uploadDir') . '/product_images/'. urlencode($foto);
+
+ if (file_exists($source_image)) {
+ if (($productImage = ProductImage::find()->andFilterWhere(['image'=> $foto])->andFilterWhere(['product_id' => $_product->product_id])->one()) === null) {
+ copy($source_image, Yii::getAlias('@productsDir') . "/" . $foto);
+ $productImage = new ProductImage();
+ $productImage->product_id = $_product->product_id;
+ $productImage->image = $foto;
+ $productImage->save();
+ }
+ }
+ }
+ }
+
+
+ // нужно для проставления характеристик относящихся к модификациям
+ $MOD_ARRAY = [];
+
+ for ($i = 14; $i < count ($data); $i ++)
+ {
+ if (! empty ($data[$i]))
+ {
+ $mod_arr = explode ('=', $data[$i]);
+ $mod_art = $mod_arr[0];
+ $variant_filters = explode ('*', $mod_arr[1]);
+ $mod_color = $mod_arr[2];
+ $mod_image = $mod_arr[3];
+ $mod_stock = isset($mod_arr[4]) ?$mod_arr[4]:1;
+ $mod_cost = isset($product_cost) ? floatval($product_cost) : 0;
+ $mod_old_cost = floatval($product_cost_old);
+
+ // Check product variant
+ if ( ($_productVariant = ProductVariant::find()->andFilterWhere([ 'sku'=> $mod_art])->andFilterWhere(['product_id' => $_product->product_id])->one()) === null ) {
+ $_productVariant = new ProductVariant();
+ $_productVariant->product_id = $_product->product_id;
+ }
+ $_productVariant->product_unit_id = 1;
+
+ $_productVariant->sku = $mod_art;
+ $_productVariant->price = $mod_cost;
+ $_productVariant->price_old = $mod_old_cost;
+ $_productVariant->stock = $mod_stock;
+
+ $product_variant_type_name = '';
+ if (! empty ($mod_color)) {
+ $product_variant_type_name = 'Цвет';
+ $_productVariant->name = $mod_color;
+ }
+
+ if (! empty ($variant_filters)) {
+
+ $variants_options = $this->saveFilters($variant_filters,1,$category_id);
+
+ }
+
+
+ if (isset($variants_options) && !empty($variants_options)) {
+ $_productVariant->options = $variants_options;
+ }
+
+
+ // ===== Set variant type ====
+ if ( $product_variant_type_name ) {
+ if ( ($product_variant_type = ProductVariantType::find()->filterWhere([ 'name'=> $product_variant_type_name])->one()) !== null ) {
+ $_productVariant->product_variant_type_id = $product_variant_type->product_variant_type_id;
+ } else {
+ $product_variant_type = new ProductVariantType();
+ $product_variant_type->name = $product_variant_type_name;
+ $product_variant_type->save();
+ $_productVariant->product_variant_type_id = $product_variant_type->product_variant_type_id;
+ }
+ }
+
+ $_productVariant->save();
+
+ $MOD_ARRAY[] = $_productVariant->product_variant_id;
+
+ if ($mod_image) {
+ $source_image = Yii::getAlias('@productsDir') . '/'. urlencode($mod_image);
+ if (file_exists($source_image)) {
+ if (($variantImage = ProductImage::find()->andFilterWhere([ 'image' => $mod_image])->andFilterWhere(['product_variant_id' => $_productVariant->product_variant_id])->one()) === null) {
+// copy($source_image, Yii::getAlias('@productsDir') . "/" . $mod_image);
+ $variantImage = new ProductImage();
+ $variantImage->product_id = $_product->product_id;
+ $variantImage->product_variant_id = $_productVariant->product_variant_id;
+ $variantImage->image = $mod_image;
+ $variantImage->save();
+ }
+ }
+ }
+ }
+ }
+
+
+
+ if (! empty ($filters)) {
+
+ $options = $this->saveFilters($filters,0,$category_id);
+
+ }
+
+
+ if (isset($options) && !empty($options)) {
+ $_product->options = $options;
+ }
+
+ $_product->save();
+
+ $result_items[] = "Product {$_product->name} #{$_product->product_id} saved (". ($is_new_product ? 'new product' : 'exists product') .")" . " (строка $j)";
+ }
+
+ $result = [
+ 'end' => feof($handle),
+ 'from' => ftell($handle),
+ 'totalsize' => $filesize,
+ 'items' => $result_items,
+
+ ];
+
+ fclose ($handle);
+
+ if ($result['end']) {
+ unlink(Yii::getAlias('@uploadDir') .'/'. Yii::getAlias('@uploadFileProducts'));
+ }
+
+ return $result;
+ }
+
+
+ public function goEvent($file) {
+
+ set_time_limit(0);
+
+
+ $handle = fopen($file, 'r');
+
+
+ while (($data = fgetcsv($handle, 1000, ";")) !== FALSE) {
+ if(isset($data[0]) && isset($data[1])){
+ $product = ProductVariant::find()->where(['sku' => $data[1]])->joinWith('product')->one();
+ if($product instanceof ProductVariant){
+ $model= EventsToProducts::find()->where(['event_id' =>$data[0], 'product_id' => $product->product->product_id ])->one();
+ if(!$model instanceof EventsToProducts){
+ $model = new EventsToProducts;
+ $model->event_id = $data[0];
+ $model->product_id = $product->product->product_id;
+ $model->save();
+ }
+ }
+ }
+
+ }
+ fclose($handle);
+ unlink($file);
+
+ }
+
+ private function getProductsFile($file_type) {
+ $filename = Yii::getAlias('@uploadDir') .'/'. Yii::getAlias('@'. $file_type);
+ if (!is_file($filename)) {
+ $this->errors[] = "File $filename not found";
+ return FALSE;
+ }
+ return fopen ($filename, 'r');
+ }
+
+ private function saveNotFoundRecord (array $line, $filename)
+ {
+ $str = implode (';', $line)."\n";
+ $str = iconv ("UTF-8//TRANSLIT//IGNORE", "windows-1251", $str);
+
+ $fg = fopen (Yii::getAlias('@uploadDir') .'/'. $filename, 'a+');
+ fputs ($fg, $str);
+ fclose ($fg);
+ }
+
+
+ /**
+ * @param $filters array of filters like [['pol'='мужской'],['god' = '2013'],['volume'='25 л']*['size'='49 x 30 x 20см'],['composition'='600D полиэстер']]
+ * @param $level 0 for products and 1 for product variant
+ * @param $catalog_names array catalogs id
+ * @return array
+ */
+ private function saveFilters($filters, $level,$catalog_names){
+ $options = [];
+ foreach($filters as $filter) {
+
+ preg_match_all('/\[(.*):(.*)\]/',$filter,$filter);
+
+ if (empty($filter[1][0])) {
+ continue;
+ }
+ $filter_name = trim($filter[1][0]);
+
+ $taxGroup = TaxGroup::find()->where(['alias'=>$filter_name])->one();
+ if(!$taxGroup instanceof TaxGroup){
+ $taxGroup = new TaxGroup();
+ $taxGroup->alias = $filter_name;
+ $taxGroup->level = $level;
+ $taxGroup->name = $filter_name;
+ $taxGroup->module = 'string';
+ $taxGroup->hierarchical = FALSE;
+ $taxGroup->categories = $catalog_names;
+ $taxGroup->is_filter = FALSE;
+ $taxGroup->save();
+ }
+
+ $filters_options = explode(',',$filter[2][0]);
+
+ foreach($filters_options as $filter_options){
+ $value = TaxValueString::find()->innerJoinWith('taxOption')->andWhere(['value'=>$filter_options])->andWhere(['tax_option.tax_group_id' => $taxGroup->tax_group_id])->one();
+
+ if (!$value instanceof TaxValueString) {
+ // Create option
+ $option = new TaxOption();
+ $option->tax_group_id = $taxGroup->tax_group_id;
+ $option->name = $filter_options;
+ $option->save();
+
+ $value = new TaxValueString();
+ $value->tax_option_id = $option->tax_option_id;
+ $value->value = $filter_options;
+ $value->save();
+
+ $option->default_value = $value->tax_value_id;
+ $option->save();
+ }
+ $options[] = $value->tax_option_id;
+
+ }
+
+
+ }
+
+ return $options;
+ }
+}
\ No newline at end of file
diff --git a/common/modules/product/models/Product.php b/common/modules/product/models/Product.php
new file mode 100755
index 0000000..57b69c9
--- /dev/null
+++ b/common/modules/product/models/Product.php
@@ -0,0 +1,508 @@
+ SaveMultipleImgBehavior::className(),
+ 'fields' => [
+ ['name'=>'imagesUpload','directory' => 'products' ]
+ ]
+ ],
+ [
+ 'class' => FilterBehavior::className(),
+ ],
+ [
+ 'class' => Slug::className(),
+ 'in_attribute' => 'name',
+ 'out_attribute' => 'alias',
+ 'translit' => true
+ ]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public static function tableName()
+ {
+ return '{{%product}}';
+ }
+
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getEvents(){
+ return $this->hasMany(Event::className(), ['event_id' => 'event_id'])->viaTable('events_to_products', ['product_id' => 'product_id']);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return [
+// [['categories'], 'required'],
+ [['brand_id'], 'integer'],
+ [['name'], 'string', 'max' => 150],
+ [['alias'], 'string', 'max' => 250],
+ [['categories', 'variants', 'options', 'imagesUpload'], 'safe'],
+// [['imagesUpload'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg, gif', 'maxFiles' => 50],
+ [['description', 'video'], 'safe'],
+ [['is_top', 'is_new', 'akciya'], 'boolean'],
+// [['product_id'], 'exist', 'skipOnError' => true, 'targetClass' => Product::className(), 'targetAttribute' => ['product_id' => 'product_id']],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'product_id' => Yii::t('product', 'ID'),
+ 'name' => Yii::t('product', 'Name'),
+ 'brand_id' => Yii::t('product', 'Brand'),
+ 'categories' => Yii::t('product', 'Categories'), // relation behavior field
+ 'category' => Yii::t('product', 'Category'), // relation behavior field
+ 'image' => Yii::t('product', 'Image'),
+ 'images' => Yii::t('product', 'Images'),
+ 'description' => Yii::t('product', 'Description'),
+ 'video' => Yii::t('product', 'Video embeded'),
+ 'variants' => Yii::t('product', 'Variants'),
+ 'is_top' => Yii::t('product', 'Is top'),
+ 'is_new' => Yii::t('product', 'Is new'),
+ 'akciya' => Yii::t('product', 'Is promo'),
+ ];
+ }
+
+ public function getUrl() {
+ return '/product/'. $this->alias;
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getBrand()
+ {
+ return $this->hasOne(Brand::className(), ['brand_id' => 'brand_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getImage()
+ {
+ return $this->hasOne(ProductImage::className(), ['product_id' => 'product_id']);
+ }
+
+ /**
+ * fetch stored image url
+ * @return string
+ */
+ public function getImageUrl()
+ {
+
+ $image = !empty($this->image) ? $this->image : $this->variant->image;
+ return !empty($image) ? $image->imageUrl : '/storage/no_photo.png';
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getImages()
+ {
+ return $this->hasMany(ProductImage::className(), ['product_id' => 'product_id'])->where(['product_variant_id' => null]);
+ }
+
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getVariant()
+ {
+ return $this->hasOne(ProductVariant::className(), ['product_id' => 'product_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getVariants()
+ {
+ return $this->hasMany(ProductVariant::className(), ['product_id' => 'product_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getEnabledVariant()
+ {
+ return $this->hasOne(ProductVariant::className(), ['product_id' => 'product_id'])
+ ->andOnCondition(['!=', ProductVariant::tableName() .'.status', 1]);
+ }
+
+ public function getVariantPrice() {
+ return $this->variant->price;
+ }
+
+
+ public function getPrice() {
+ return $this->variant->price;
+ }
+
+ public function getEnabledVariantPrice() {
+ return $this->enabledVariants[0]->price;
+ }
+
+
+
+ public function getEnabledVariants()
+ {
+ return $this->hasMany(ProductVariant::className(), ['product_id' => 'product_id'])->andOnCondition(['!=', ProductVariant::tableName() .'.status', 1])->joinWith('image');
+ }
+
+ /*
+ * Get variants grouped by type
+ */
+ public function getEnabledVariantsGrouped()
+ {
+ $variants = [];
+ foreach ($this->enabledVariants as $variant) {
+ $variants[$variant->product_variant_type_id ? $variant->product_variant_type_id : 1][] = $variant;
+ }
+ if (empty($variants)) {
+ return [];
+ }
+ $ids = array_keys($variants);
+ $variants_type = [];
+ foreach(ProductVariantType::find()->select(['product_variant_type_id', 'name2'])->where(['product_variant_type_id' => $ids])->all() as $variant_type) {
+ $variant_type->_variants = $variants[$variant_type->product_variant_type_id];
+ $variants_type[] = $variant_type;
+ }
+ return $variants_type;
+ }
+
+ public function setVariants($variants) {
+ $this->_variants = $variants;
+ }
+
+ public function getFullName()
+ {
+ return empty($this->brand) ? $this->name : $this->brand->name .' '. $this->name;
+ }
+
+ public function getCategories() {
+ return $this->hasMany(Category::className(), ['category_id' => 'category_id'])->viaTable('product_category', ['product_id' => 'product_id']);
+// return $this->getRelations('product_categories');
+ }
+ public function getCategoriesWithName() {
+ return $this->hasMany(Category::className(), ['category_id' => 'category_id'])->viaTable('product_category', ['product_id' => 'product_id']);
+// return $this->getRelations('product_categories');
+ }
+
+ public function getCategoriesNames() {
+ $result = [];
+ foreach($this->categories as $category) {
+ $result[] = $category->name;
+ }
+ return $result;
+ }
+
+ public function getVariantsWithFilters(){
+ return $this->hasMany(ProductVariant::className(), ['product_id' => 'product_id'])->with(['filters','image']);
+ }
+
+ /**
+ * @return ActiveQuery
+ */
+ public function getCategory() {
+ return $this->hasOne(Category::className(), ['category_id' => 'category_id'])->viaTable('product_category', ['product_id' => 'product_id']);
+ }
+
+ public function getOptions() {
+ return $this->hasMany(TaxOption::className(), ['tax_option_id' => 'option_id'])->viaTable('product_option', ['product_id' => 'product_id']);
+ }
+
+ public function getProperties() {
+ $groups = $options = [];
+ foreach ($this->options as $option) {
+ $options[$option->tax_group_id][] = $option;
+ }
+ foreach (TaxGroup::find()->where(['tax_group_id' => array_keys($options)])->all() as $group) {
+ if (!empty($options[$group->tax_group_id])) {
+ $group->_options = $options[$group->tax_group_id];
+ $groups[] = $group;
+ }
+ }
+ return $groups;
+ }
+
+ public function getActiveProperties($category_id) {
+ $cacheKey = ['ActiveProperties','id' => $category_id, 'options' =>$this->options, 'product_id' => $this->product_id ];
+
+ if(!$groups = Yii::$app->cache->get($cacheKey)){
+
+ $groups = $options = [];
+ foreach ($this->options as $option) {
+ $options[$option->tax_group_id][] = $option;
+ }
+
+ $taxGroups = TaxGroup::find()->joinWith('categories')->where(['tax_group.tax_group_id' => array_keys($options), 'tax_group.display' => TRUE, 'category.category_id' => $category_id])->all();
+
+ foreach ($taxGroups as $group) {
+ if (!empty($options[$group->tax_group_id])) {
+ $group->_options = $options[$group->tax_group_id];
+ $groups[] = $group;
+ }
+ }
+ Yii::$app->cache->set($cacheKey,$groups,3600*24);
+ }
+
+ return $groups;
+ }
+
+ public function getStocks() {
+ return $this->hasMany(Stock::className(), ['stock_id' => 'stock_id'])->viaTable(ProductStock::tableName(), ['product_id' => 'product_id']);
+ }
+
+
+ public function getQuantity() {
+ return ProductStock::find()
+ ->where(['product_id' => $this->product_id])
+ ->sum('quantity');
+ }
+
+
+ public function beforeSave($insert)
+ {
+
+ if(parent::beforeSave($insert)){
+
+ return true;
+ }
+ return false;
+
+
+
+ }
+
+ public function afterSave($insert, $changedAttributes)
+ {
+ parent::afterSave($insert, $changedAttributes);
+
+
+ if(!empty($this->categories)){
+ $categories = Category::findAll($this->categories);
+ $this->unlinkAll('categories', true);
+ foreach($categories as $category){
+ $this->link('categories', $category);
+ }
+ }
+
+ if(!empty($this->options)){
+ $options = TaxOption::findAll($this->options);
+ $this->unlinkAll('options',true);
+ foreach($options as $option){
+ $this->link('options', $option);
+ }
+ }
+
+
+ if (!empty($this->_variants)) {
+ $todel = [];
+ foreach ($this->variants ?: [] as $_variant) {
+ $todel[$_variant->product_variant_id] = $_variant->product_variant_id;
+ }
+ foreach ($this->_variants as $_variant) {
+ if (!is_array($_variant)) {
+ return;
+ }
+ if (!empty($_variant['product_variant_id'])) {
+ unset($todel[$_variant['product_variant_id']]);
+ $model = ProductVariant::findOne($_variant['product_variant_id']);
+ } else {
+ $model = new ProductVariant();
+ }
+ $_variant['product_id'] = $this->product_id;
+ $model->load(['ProductVariant' => $_variant]);
+ $model->product_id = $this->product_id;
+ $model->save();
+ }
+ if (!empty($todel)) {
+ ProductVariant::deleteAll(['product_variant_id' => $todel]);
+ }
+ }
+ }
+
+ public function beforeDelete() {
+ if(parent::beforeDelete()){
+ $this->unlinkAll('categories', true);
+ $this->unlinkAll('options', true);
+ ProductImage::deleteAll(['product_id' => $this->product_id]);
+
+ ProductVariant::deleteAll(['product_id' => $this->product_id]);
+ ProductStock::deleteAll(['product_id' => $this->product_id]);
+ Share::deleteAll(['product_id' => $this->product_id]);
+ return true;
+ }
+ return false;
+
+ }
+
+ public function imagesUpload()
+ {
+ if ($this->validate()) {
+ $images = [];
+ foreach ($this->imagesUpload as $image) {
+ $imageName = $image->baseName .'.'. $image->extension;
+ $i = 0;
+ while(file_exists(Yii::getAlias('@imagesDir/products/' . $imageName))) {
+ $i++;
+ $imageName = $image->baseName .'_'. $i .'.'. $image->extension;
+ }
+ $imgDir =Yii::getAlias('@imagesDir/products/');
+ if(!is_dir($imgDir)) {
+ mkdir($imgDir, 0755, true);
+ }
+
+
+ $image->saveAs($imgDir .$imageName);
+ $images[] = $imageName;
+ }
+ return $images;
+ } else {
+ return false;
+ }
+ }
+
+ public function getImagesHTML() {
+ $op = [];
+ if ($this->images) {
+ foreach ($this->images as $image) {
+ $op[] = \common\components\artboximage\ArtboxImageHelper::getImage($image->imageUrl, 'admin_thumb');
+ }
+ }
+ return $op;
+ }
+
+ public function getImagesConfig() {
+ $op = [];
+ if ($this->images) {
+ foreach ($this->images as $image) {
+ $op[] = [
+ 'caption' => $image->image,
+ 'url' => \yii\helpers\Url::to(['/product/manage/delimg', 'id' => $image->product_image_id]),
+ 'key' => $image->product_image_id,
+ 'extra' => [
+ 'id' => $image->product_image_id,
+ ],
+ ];
+ }
+ }
+ return $op;
+ }
+
+ public function recalculateRating() {
+ /**
+ * @var ProductToRating $averageRating
+ */
+ $average = $this->getComments()->joinWith('rating')->select(['average' => 'avg(artbox_comment_rating.value)::float'])->scalar();
+ if(!$average) {
+ $average = 0;
+ }
+ $averageRating = $this->averageRating;
+ if(!empty($averageRating)) {
+ $averageRating->value = $average;
+ } else {
+ $averageRating = new ProductToRating(['product_id' => $this->product_id, 'value' => $average]);
+ }
+ if($averageRating->save()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public function getComments() {
+ return $this->hasMany(CommentModel::className(), ['entity_id' => 'product_id'])->where(['artbox_comment.entity' => self::className(), 'artbox_comment.status' => CommentModel::STATUS_ACTIVE, 'artbox_comment.artbox_comment_pid' => NULL]);
+ }
+
+ public function getAverageRating() {
+ return $this->hasOne(ProductToRating::className(), ['product_id' => 'product_id']);
+ }
+
+ public function getTaxGroupsByLevel($level)
+ {
+ $categories = ArrayHelper::getColumn($this->categories, 'category_id');
+ return TaxGroup::find()->distinct()->innerJoin('tax_group_to_category', 'tax_group_to_category.tax_group_id = tax_group.tax_group_id')->andWhere(['tax_group_to_category.category_id' => $categories])->andWhere(['level' => $level]);
+ }
+
+ public function setCategories($values)
+ {
+ $this->categories = $values;
+ }
+
+ public function setOptions($values)
+ {
+ $this->options = $values;
+ }
+
+ public function getFilters(){
+
+ return $this->hasMany(TaxOption::className(), ['tax_option_id' => 'option_id'])
+ ->viaTable('product_option',[ 'product_id'=> 'product_id'])
+ ->joinWith('taxGroup');
+ }
+
+}
diff --git a/common/modules/product/models/ProductCategory.php b/common/modules/product/models/ProductCategory.php
new file mode 100755
index 0000000..36d2ed8
--- /dev/null
+++ b/common/modules/product/models/ProductCategory.php
@@ -0,0 +1,58 @@
+ true, 'targetClass' => Category::className(), 'targetAttribute' => ['category_id' => 'category_id']],
+ [['product_id'], 'exist', 'skipOnError' => true, 'targetClass' => Product::className(), 'targetAttribute' => ['product_id' => 'product_id']],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'product_id' => Yii::t('product', 'Product'),
+ 'category_id' => Yii::t('product', 'Category'),
+ ];
+ }
+
+ public function getProduct() {
+ return $this->getEntity1();
+ }
+
+ public function getCategory() {
+ return $this->getEntity2();
+ }
+}
diff --git a/common/modules/product/models/ProductImage.php b/common/modules/product/models/ProductImage.php
new file mode 100755
index 0000000..a69a9f2
--- /dev/null
+++ b/common/modules/product/models/ProductImage.php
@@ -0,0 +1,163 @@
+ 255],
+ [['product_id'], 'exist', 'skipOnError' => true, 'targetClass' => Product::className(), 'targetAttribute' => ['product_id' => 'product_id']],
+ [['product_variant_id'], 'exist', 'skipOnError' => true, 'targetClass' => ProductVariant::className(), 'targetAttribute' => ['product_variant_id' => 'product_variant_id']],
+ [['imageUpload'], 'safe'],
+ [['imageUpload'], 'file', 'extensions' => 'jpg, gif, png'],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'product_image_id' => Yii::t('product', 'Product Image ID'),
+ 'product_id' => Yii::t('product', 'Product ID'),
+ 'product_variant_id' => Yii::t('product', 'Product Variant ID'),
+ 'product' => Yii::t('product', 'Product'),
+ 'product_variant' => Yii::t('product', 'Product Variant'),
+ 'image' => Yii::t('product', 'Image'),
+ 'alt' => Yii::t('product', 'Alt'),
+ 'title' => Yii::t('product', 'Title'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getProduct()
+ {
+ $return = $this->hasOne(Product::className(), ['product_id' => 'product_id']);
+ if (empty($return)) {
+ $return = $this->productVariant->product_id;
+ }
+ return $return;
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getProductVariant()
+ {
+ return $this->hasOne(Product::className(), ['product_variant_id' => 'product_variant_id']);
+ }
+
+// /**
+// * @inheritdoc
+// * @return ProductImageQuery the active query used by this AR class.
+// */
+// public static function find()
+// {
+// return new ProductImageQuery(get_called_class());
+// }
+
+ /**
+ * fetch stored image file name with complete path
+ * @return string
+ */
+ public function getImageFile()
+ {
+ return isset($this->image) ? '/storage/products/' . $this->image : null;
+ }
+
+ /**
+ * fetch stored image url
+ * @return string
+ */
+ public function getImageUrl()
+ {
+ // return a default image placeholder if your source image is not found
+ return isset($this->image) ? '/storage/products/'. $this->image : '/storage/no_photo.png';
+ }
+
+ /**
+ * Process upload of image
+ *
+ * @return mixed the uploaded image instance
+ */
+ public function uploadImage() {
+ // get the uploaded file instance. for multiple file uploads
+ // the following data will return an array (you may need to use
+ // getInstances method)
+ $image = UploadedFile::getInstance($this, 'imageUpload');
+
+ // if no image was uploaded abort the upload
+ if (empty($image)) {
+ return false;
+ }
+
+ // store the source file name
+ $this->filename = $image->name;
+ $ext = end((explode(".", $image->name)));
+
+ // generate a unique file name
+ $this->image = Yii::$app->security->generateRandomString().".{$ext}";
+
+ // the uploaded image instance
+ return $image;
+ }
+
+ /**
+ * Process deletion of image
+ *
+ * @return boolean the status of deletion
+ */
+ public function deleteImage() {
+ $file = $this->getImageFile();
+
+ // check if file exists on server
+ if (empty($file) || !file_exists($file)) {
+ return false;
+ }
+
+ // check if uploaded file can be deleted on server
+ if (!unlink($file)) {
+ return false;
+ }
+
+ // if deletion successful, reset your file attributes
+ $this->image = null;
+ $this->filename = null;
+
+ return true;
+ }
+}
diff --git a/common/modules/product/models/ProductImageQuery.php b/common/modules/product/models/ProductImageQuery.php
new file mode 100755
index 0000000..d10b794
--- /dev/null
+++ b/common/modules/product/models/ProductImageQuery.php
@@ -0,0 +1,35 @@
+andWhere('[[status]]=1');
+ }*/
+
+ /**
+ * @inheritdoc
+ * @return ProductImage[]|array
+ */
+ public function all($db = null)
+ {
+ $this->where(['product_variant_id' => NULL]);
+ return parent::all($db);
+ }
+
+ /**
+ * @inheritdoc
+ * @return ProductImage|array|null
+ */
+ public function one($db = null)
+ {
+ return parent::one($db);
+ }
+}
diff --git a/common/modules/product/models/ProductOption.php b/common/modules/product/models/ProductOption.php
new file mode 100755
index 0000000..de5d49f
--- /dev/null
+++ b/common/modules/product/models/ProductOption.php
@@ -0,0 +1,65 @@
+ true, 'targetClass' => Product::className(), 'targetAttribute' => ['product_id' => 'product_id']],
+ [['option_id'], 'exist', 'skipOnError' => true, 'targetClass' => TaxOption::className(), 'targetAttribute' => ['option_id' => 'tax_option_id']],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'product_id' => Yii::t('product', 'Product ID'),
+ 'option_id' => Yii::t('product', 'Option ID'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getProduct()
+ {
+ return $this->hasOne(Product::className(), ['product_id' => 'product_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getOption()
+ {
+ return $this->hasOne(TaxOption::className(), ['tax_option_id' => 'option_id']);
+ }
+}
diff --git a/common/modules/product/models/ProductSearch.php b/common/modules/product/models/ProductSearch.php
new file mode 100755
index 0000000..8da95c0
--- /dev/null
+++ b/common/modules/product/models/ProductSearch.php
@@ -0,0 +1,129 @@
+joinWith(['brand', 'categories', 'variant']);
+
+ $query->groupBy(['product.product_id']);
+
+ $dataProvider = new ActiveDataProvider([
+ 'query' => $query,
+ ]);
+
+ if ( !($this->load($params) && $this->validate()) ) {
+ return $dataProvider;
+ }
+
+ $dataProvider->setSort([
+ 'attributes' => [
+ 'name',
+ 'brand_name' => [
+ 'asc' => ['brand.name' => SORT_ASC],
+ 'desc' => ['brand.name' => SORT_DESC],
+ 'default' => SORT_DESC,
+ 'label' => 'Brand name',
+ ],
+ 'category_name',
+ 'variant_sku',
+ ]
+ ]);
+
+ if (isset($this->is_top)) {
+ $query->andFilterWhere([
+ 'is_top' => (bool)$this->is_top,
+ ]);
+ }
+ if (isset($this->is_new)) {
+ $query->andFilterWhere([
+ 'is_new' => (bool)$this->is_new,
+ ]);
+ }
+ if (isset($this->akciya)) {
+ $query->andFilterWhere([
+ 'akciya' => (bool)$this->akciya,
+ ]);
+ }
+ $query->andFilterWhere([
+ 'product.brand_id' => $this->brand_id,
+ 'product.product_id' => $this->product_id,
+ 'product_category.category_id' => $this->category_id
+ ]);
+
+ $query->andFilterWhere(['ilike', 'product.name', $this->name]);
+ $query->andFilterWhere(['ilike', 'brand.name', $this->brand_name]);
+ $query->andFilterWhere(['ilike', 'category.name', $this->category_name]);
+ $query->andFilterWhere(['ilike', 'product_variant.sku', $this->variant_sku]);
+
+ return $dataProvider;
+ }
+
+ public static function findByAlias($alias) {
+
+ $query = Product::find()->where(["alias"=>$alias]);
+ if (($model = $query->one()) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested product does not exist.');
+ }
+ }
+
+ public static function findByRemoteID($id) {
+ /** @var CategoryQuery $query */
+ $query = Product::find()
+ ->andFilterWhere(['remote_id' => $id]);
+ if (($model = $query->one()) !== null) {
+ return $model;
+ }
+ return null;
+ }
+}
diff --git a/common/modules/product/models/ProductStock.php b/common/modules/product/models/ProductStock.php
new file mode 100755
index 0000000..ead58bc
--- /dev/null
+++ b/common/modules/product/models/ProductStock.php
@@ -0,0 +1,95 @@
+ true, 'targetClass' => Product::className(), 'targetAttribute' => ['product_id' => 'product_id']],
+ [['product_variant_id'], 'exist', 'skipOnError' => true, 'targetClass' => ProductVariant::className(), 'targetAttribute' => ['product_variant_id' => 'product_variant_id']],
+ [['stock_id'], 'exist', 'skipOnError' => true, 'targetClass' => Stock::className(), 'targetAttribute' => ['stock_id' => 'stock_id']],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'product_id' => 'Product ID',
+ 'stock_id' => 'Stock ID',
+ 'quantity' => 'Количество',
+ 'product_variant_id' => 'Product Variant ID',
+ 'name' => "Название"
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getProduct()
+ {
+ return $this->hasOne(Product::className(), ['product_id' => 'product_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getProductVariant()
+ {
+ return $this->hasOne(ProductVariant::className(), ['product_variant_id' => 'product_variant_id']);
+ }
+
+
+ public function getName(){
+ return (!empty($this->stock))? $this->stock->name : '';
+ }
+
+ public function setName($value){
+ $this->name = $value;
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getStock()
+ {
+ return $this->hasOne(Stock::className(), ['stock_id' => 'stock_id']);
+ }
+
+
+ public static function primaryKey()
+ {
+ return ["stock_id","product_variant_id"];
+ }
+}
diff --git a/common/modules/product/models/ProductUnit.php b/common/modules/product/models/ProductUnit.php
new file mode 100755
index 0000000..f6dbe42
--- /dev/null
+++ b/common/modules/product/models/ProductUnit.php
@@ -0,0 +1,69 @@
+ 255],
+ [['code'], 'string', 'max' => 50],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'product_unit_id' => Yii::t('product', 'Product Unit ID'),
+ 'name' => Yii::t('product', 'Name'),
+ 'code' => Yii::t('product', 'Code'),
+ 'is_default' => Yii::t('product', 'Is Default'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getCategories()
+ {
+ return $this->hasMany(Category::className(), ['product_unit_id' => 'product_unit_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getProductVariants()
+ {
+ return $this->hasMany(ProductVariant::className(), ['product_unit_id' => 'product_unit_id']);
+ }
+}
diff --git a/common/modules/product/models/ProductUnitSearch.php b/common/modules/product/models/ProductUnitSearch.php
new file mode 100755
index 0000000..8b0be90
--- /dev/null
+++ b/common/modules/product/models/ProductUnitSearch.php
@@ -0,0 +1,72 @@
+ $query,
+ ]);
+
+ $this->load($params);
+
+ if (!$this->validate()) {
+ // uncomment the following line if you do not want to return any records when validation fails
+ // $query->where('0=1');
+ return $dataProvider;
+ }
+
+ // grid filtering conditions
+ $query->andFilterWhere([
+ 'product_unit_id' => $this->product_unit_id,
+ 'is_default' => $this->is_default,
+ ]);
+
+ $query->andFilterWhere(['like', 'name', $this->name])
+ ->andFilterWhere(['like', 'code', $this->code]);
+
+ return $dataProvider;
+ }
+}
diff --git a/common/modules/product/models/ProductVariant.php b/common/modules/product/models/ProductVariant.php
new file mode 100755
index 0000000..1bc052d
--- /dev/null
+++ b/common/modules/product/models/ProductVariant.php
@@ -0,0 +1,313 @@
+ 0],
+ [['price', 'price_old', 'stock'], 'number'],
+ [['name', 'sku'], 'string', 'max' => 255],
+ [['remote_id'], 'string', 'max' => 20],
+ [['options', 'imagesUpload'], 'safe'],
+// [['imagesUpload'], 'safe'],
+// [['imagesUpload'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg, gif', 'maxFiles' => 50],
+ [['product_unit_id'], 'exist', 'skipOnError' => true, 'targetClass' => ProductUnit::className(), 'targetAttribute' => ['product_unit_id' => 'product_unit_id']],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'product_variant_id' => Yii::t('product', 'Product Variant ID'),
+ 'product_id' => Yii::t('product', 'Product ID'),
+ 'name' => Yii::t('product', 'Name'),
+ 'sku' => Yii::t('product', 'Sku'),
+ 'price' => Yii::t('product', 'Price'),
+ 'price_old' => Yii::t('product', 'Price Old'),
+ 'stock' => Yii::t('product', 'Stock'),
+ 'product_unit_id' => Yii::t('product', 'Product Unit ID'),
+ 'product_variant_type_id' => Yii::t('product', 'Product Variant Type ID'),
+ 'stock_caption' => Yii::t('product', 'Stock'),
+ 'image' => Yii::t('product', 'Image'),
+ 'images' => Yii::t('product', 'Images'),
+ 'status' => Yii::t('product', 'Снят с производства'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getProductUnit()
+ {
+ return $this->hasOne(ProductUnit::className(), ['product_unit_id' => 'product_unit_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getProductVariantType()
+ {
+ return $this->hasOne(ProductVariantType::className(), ['product_variant_type_id' => 'product_variant_type_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getProduct()
+ {
+ return $this->hasOne(Product::className(), ['product_id' => 'product_id']);
+ }
+
+ public function getProductStock() {
+ return $this->hasMany(ProductStock::className(), ['product_variant_id' => 'product_variant_id']);
+ }
+
+ public function getQuantity() {
+ return ProductStock::find()
+ ->where(['product_variant_id' => $this->product_variant_id])
+ ->sum('quantity');
+ }
+
+ public function getStock_caption() {
+ return is_null($this->stock) ? '∞' : ($this->stock > 0 ? Yii::t('product', 'Enable') : Yii::t('product', 'Disable')); // intval($this->stock);
+ }
+
+ public function getVariantStocks(){
+
+ return $this->hasMany(ProductStock::className(),['product_variant_id'=> 'product_variant_id'])->joinWith('stock');
+ }
+
+
+ public function getStocks(){
+
+ return $this->hasMany(Stock::className(),['stock_id'=>'stock_id'])
+ ->viaTable(ProductStock::tableName(),['product_variant_id'=> 'product_variant_id']);
+ }
+
+
+ public function getFilters(){
+
+ return $this->hasMany(TaxOption::className(), ['tax_option_id' => 'option_id'])
+ ->viaTable('product_variant_option',[ 'product_variant_id'=> 'product_variant_id'])
+ ->joinWith('taxGroup');
+ }
+
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getImage()
+ {
+ return $this->hasOne(ProductImage::className(), ['product_variant_id' => 'product_variant_id']);
+ }
+
+ /**
+ * fetch stored image url
+ * @return string
+ */
+ public function getImageUrl()
+ {
+ // return a default image placeholder if your source image is not found
+ return (!empty($this->image) && file_exists(Yii::getAlias('@productsDir') . "/" . $this->image->image )) ? $this->image->imageUrl : '/storage/no_photo.png';
+ }
+
+ public function getFullname() {
+ return empty($this->product) ? null : ($this->product->name . (empty($this->name) ? '' : ' '. $this->name));
+ }
+
+ public function getImagesHTML() {
+ $op = [];
+ if ($this->images) {
+ foreach ($this->images as $image) {
+ $op[] = \common\components\artboximage\ArtboxImageHelper::getImage($image->imageUrl, 'admin_thumb');
+ }
+ }
+ return $op;
+ }
+
+ public function getImagesConfig() {
+ $op = [];
+ if ($this->images) {
+ foreach ($this->images as $image) {
+ $op[] = [
+ 'caption' => $image->image,
+ 'width' => '120px',
+ 'url' => \yii\helpers\Url::to(['/product/manage/delimg', 'id' => $image->product_image_id]),
+ 'key' => $image->product_image_id,
+ 'extra' => [
+ 'id' => $image->product_image_id,
+ ],
+ ];
+ }
+ }
+ return $op;
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getImages()
+ {
+ return $this->hasMany(ProductImage::className(), ['product_variant_id' => 'product_variant_id']);
+ }
+
+ public function getOptions() {
+ return $this->hasMany(TaxOption::className(), ['tax_option_id' => 'option_id'])->viaTable('product_variant_option', ['product_variant_id' => 'product_variant_id']);
+ }
+
+ public function setOptions($value){
+ $this->_options = $value;
+ }
+
+ public function getProperties() {
+ $groups = $options = [];
+ foreach ($this->options as $option) {
+ $options[$option->tax_group_id][] = $option;
+ }
+ foreach (TaxGroup::find()->where(['tax_group_id' => array_keys($options)])->all() as $group) {
+ if (!empty($options[$group->tax_group_id])) {
+ $group->_options = $options[$group->tax_group_id];
+ $groups[] = $group;
+ }
+ }
+ return $groups;
+ }
+
+
+ public function getId(){
+ return $this->product_variant_id;
+ }
+
+ public function setStocks($stocks) {
+ $this->stocks = (array) $stocks;
+ }
+
+ public function getCategory() {
+ return $this->hasOne(Category::className(), ['category_id' => 'category_id'])->viaTable('product_category', ['product_id' => 'product_id']);
+ }
+
+ public function getCategories() {
+ return $this->hasMany(Category::className(), ['category_id' => 'category_id'])->viaTable('product_category', ['product_id' => 'product_id']);
+ }
+
+ public function getTaxGroupsByLevel($level)
+ {
+ $categories = ArrayHelper::getColumn($this->categories, 'category_id');
+ return TaxGroup::find()->distinct()->innerJoin('tax_group_to_category', 'tax_group_to_category.tax_group_id = tax_group.tax_group_id')->where(['tax_group_to_category.category_id' => $categories])->where(['level' => $level]);
+ }
+
+ public function afterSave($insert, $changedAttributes)
+ {
+
+ if(!empty($this->_options)){
+ $options = TaxOption::findAll($this->_options);
+ $this->unlinkAll('options',true);
+ foreach($options as $option){
+ $this->link('options', $option);
+ }
+ }
+
+
+// if (!is_null($this->stocks)) {
+// //ProductStock::deleteAll(['product_variant_id' => $this->product_variant_id]);
+// $values = [];
+// foreach ($this->stocks as $id => $quantity) {
+// $productStock = ProductStock::find()->where(['product_variant_id' => $this->product_variant_id, 'stock_id' => $id])->one();
+// $productStock->quantity = $quantity;
+// $productStock->save();
+// }
+// }
+ parent::afterSave($insert, $changedAttributes);
+ }
+
+ public function beforeDelete() {
+ if(parent::beforeDelete()){
+ ProductVariantOption::deleteAll(['product_variant_id' => $this->product_variant_id]);
+ ProductImage::deleteAll(['product_variant_id' => $this->product_variant_id]);
+ ProductStock::deleteAll(['product_variant_id' => $this->product_variant_id]);
+ return true;
+ }
+ return false;
+ }
+
+ public function imagesUpload()
+ {
+ if ($this->validate()) {
+ $images = [];
+ foreach ($this->imagesUpload as $image) {
+ $imageName = $image->baseName .'.'. $image->extension;
+ $i = 0;
+ while(file_exists(Yii::getAlias('@imagesDir/products/' . $imageName))) {
+ $i++;
+ $imageName = $image->baseName .'_'. $i .'.'. $image->extension;
+ }
+
+ $image->saveAs(Yii::getAlias('@imagesDir/products/' .$imageName));
+ $images[] = $imageName;
+ }
+ return $images;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/common/modules/product/models/ProductVariantListSearch.php b/common/modules/product/models/ProductVariantListSearch.php
new file mode 100755
index 0000000..4593951
--- /dev/null
+++ b/common/modules/product/models/ProductVariantListSearch.php
@@ -0,0 +1,82 @@
+ $query,
+ ]);
+
+ $this->load($params);
+
+ if (!$this->validate()) {
+ // uncomment the following line if you do not want to return any records when validation fails
+ // $query->where('0=1');
+ return $dataProvider;
+ }
+
+ if($product_id){
+ $query->andFilterWhere([
+ 'product_id' => $product_id,
+ ]);
+ }
+
+ // grid filtering conditions
+ $query->andFilterWhere([
+ 'product_variant_id' => $this->product_variant_id,
+ 'price' => $this->price,
+ 'price_old' => $this->price_old,
+ 'stock' => $this->stock,
+ 'product_unit_id' => $this->product_unit_id,
+ 'product_variant_type_id' => $this->product_variant_type_id,
+ ]);
+
+ $query->andFilterWhere(['like', 'name', $this->name])
+ ->andFilterWhere(['like', 'sku', $this->sku])
+ ->andFilterWhere(['like', 'remote_id', $this->remote_id]);
+
+ return $dataProvider;
+ }
+}
diff --git a/common/modules/product/models/ProductVariantOption.php b/common/modules/product/models/ProductVariantOption.php
new file mode 100755
index 0000000..c1d30ae
--- /dev/null
+++ b/common/modules/product/models/ProductVariantOption.php
@@ -0,0 +1,65 @@
+ true, 'targetClass' => ProductVariant::className(), 'targetAttribute' => ['product_variant_id' => 'product_variant_id']],
+ [['option_id'], 'exist', 'skipOnError' => true, 'targetClass' => TaxOption::className(), 'targetAttribute' => ['option_id' => 'tax_option_id']],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'product_variant_id' => 'Product Variant ID',
+ 'option_id' => 'Option ID',
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getProductVariant()
+ {
+ return $this->hasOne(ProductVariant::className(), ['product_variant_id' => 'product_variant_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getOption()
+ {
+ return $this->hasOne(TaxOption::className(), ['tax_option_id' => 'option_id']);
+ }
+}
diff --git a/common/modules/product/models/ProductVariantSearch.php b/common/modules/product/models/ProductVariantSearch.php
new file mode 100755
index 0000000..3e4bfae
--- /dev/null
+++ b/common/modules/product/models/ProductVariantSearch.php
@@ -0,0 +1,147 @@
+ $query,
+ ]);
+
+ $this->load($params);
+
+ if (!empty($params['product_id'])) {
+ $this->product_id = $params['product_id'];
+ }
+
+ if (!$this->validate()) {
+ // uncomment the following line if you do not want to return any records when validation fails
+ // $query->where('0=1');
+ return $dataProvider;
+ }
+
+ $dataProvider->setSort([
+ 'attributes' => [
+ 'name' => [
+ 'asc' => ['product_variant.name' => SORT_ASC],
+ 'desc' => ['product_variant.value' => SORT_DESC],
+ 'default' => SORT_DESC,
+ 'label' => 'Variant name',
+ ],
+ 'brand_name' => [
+ 'asc' => ['brand_name.value' => SORT_ASC],
+ 'desc' => ['brand_name.value' => SORT_DESC],
+ 'default' => SORT_DESC,
+ 'label' => 'Brand name',
+ ],
+ 'category_name',
+ 'sku',
+ ]
+ ]);
+
+ $query->joinWith(['product', 'product.brand', 'product.categories']);
+
+ if (isset($this->is_top)) {
+ $query->andFilterWhere([
+ 'product.is_top' => (bool)$this->is_top,
+ ]);
+ }
+ if (isset($this->is_new)) {
+ $query->andFilterWhere([
+ 'product.is_new' => (bool)$this->is_new,
+ ]);
+ }
+ if (isset($this->akciya)) {
+ $query->andFilterWhere([
+ 'product.akciya' => (bool)$this->akciya,
+ ]);
+ }
+
+ // grid filtering conditions
+ $query->andFilterWhere([
+ 'product_variant.product_id' => $this->product_id,
+ 'product_variant_id' => $this->product_variant_id,
+ ]);
+
+ if (!empty($this->fullname)) {
+ $query->orFilterWhere(['like', 'product_variant.name', $this->fullname]);
+ $query->orFilterWhere(['ilike', 'product.name', $this->fullname]);
+ }
+ $query->andFilterWhere(['ilike', 'product.brand_name.value', $this->brand_name]);
+ $query->andFilterWhere(['ilike', 'product.category_name.value', $this->category_name]);
+ $query->andFilterWhere(['ilike', 'sku', $this->sku]);
+
+ $query->groupBy(['product_variant_id']);
+ $query->orderBy('product_variant.product_id', 'ASC');
+
+ return $dataProvider;
+ }
+
+ public static function findBySku($sku) {
+ /** @var ProductQuery $query */
+ $query = ProductVariant::find()
+ ->andFilterWhere(['sku' => $sku]);
+ if (($model = $query->one()) !== null) {
+ return $model;
+ }
+ return;
+ }
+
+ public static function findByRemoteID($id) {
+ /** @var CategoryQuery $query */
+ $query = ProductVariant::find()
+ ->andFilterWhere(['remote_id' => $id]);
+ if (($model = $query->one()) !== null) {
+ return $model;
+ }
+ return;
+ }
+}
diff --git a/common/modules/product/models/ProductVariantType.php b/common/modules/product/models/ProductVariantType.php
new file mode 100755
index 0000000..d8e813a
--- /dev/null
+++ b/common/modules/product/models/ProductVariantType.php
@@ -0,0 +1,49 @@
+ 50],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'product_variant_type_id' => 'Product Variant Type ID',
+ 'name' => 'Name',
+ ];
+ }
+
+ public function getId() {
+ return $this->product_variant_type_id;
+ }
+}
diff --git a/common/modules/product/models/ProductVariantTypeSearch.php b/common/modules/product/models/ProductVariantTypeSearch.php
new file mode 100755
index 0000000..c4804b4
--- /dev/null
+++ b/common/modules/product/models/ProductVariantTypeSearch.php
@@ -0,0 +1,70 @@
+ $query,
+ ]);
+
+ $this->load($params);
+
+ if (!$this->validate()) {
+ // uncomment the following line if you do not want to return any records when validation fails
+ // $query->where('0=1');
+ return $dataProvider;
+ }
+
+ // grid filtering conditions
+ $query->andFilterWhere([
+ 'product_variant_type_id' => $this->product_variant_type_id,
+ ]);
+
+ $query->andFilterWhere(['like', 'name', $this->name])
+ ->andFilterWhere(['like', 'name2', $this->name2]);
+
+ return $dataProvider;
+ }
+}
diff --git a/common/modules/product/models/Stock.php b/common/modules/product/models/Stock.php
new file mode 100755
index 0000000..4986388
--- /dev/null
+++ b/common/modules/product/models/Stock.php
@@ -0,0 +1,79 @@
+ 150],
+ [['name'], 'required'],
+ ];
+ }
+
+
+ public function getSiteName(){
+ switch(mb_strtolower($this->name)){
+ case "харьков свет":
+ return 'МАГАЗИН ХАРЬКОВ';
+ break;
+ case "осокорки":
+ return 'МАГАЗИН "ОСОКОРКИ"';
+ break;
+ case "олимп":
+ return 'ТЦ "ОЛИМПИЙСКИЙ"';
+ break;
+ case "магазин":
+ return "МАГАЗИН ГЛУБОЧЕЦКАЯ";
+ break;
+ default:
+ //return "На складе";
+ break;
+ }
+ }
+
+
+
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'stock_id' => Yii::t('product', 'Stock ID'),
+ 'name' => Yii::t('product', 'Name'),
+ ];
+ }
+ /**
+ * @inheritdoc
+ * @return StockQuery the active query used by this AR class.
+ */
+ public static function find()
+ {
+ return new StockQuery(get_called_class());
+ }
+}
diff --git a/common/modules/product/models/StockQuery.php b/common/modules/product/models/StockQuery.php
new file mode 100755
index 0000000..2650415
--- /dev/null
+++ b/common/modules/product/models/StockQuery.php
@@ -0,0 +1,34 @@
+andWhere('[[status]]=1');
+ }*/
+
+ /**
+ * @inheritdoc
+ * @return Stock[]|array
+ */
+ public function all($db = null)
+ {
+ return parent::all($db);
+ }
+
+ /**
+ * @inheritdoc
+ * @return Stock|array|null
+ */
+ public function one($db = null)
+ {
+ return parent::one($db);
+ }
+}
diff --git a/common/modules/product/views/default/index.php b/common/modules/product/views/default/index.php
new file mode 100755
index 0000000..3913fe0
--- /dev/null
+++ b/common/modules/product/views/default/index.php
@@ -0,0 +1,12 @@
+
+
= $this->context->action->uniqueId ?>
+
+ This is the view content for action "= $this->context->action->id ?>".
+ The action belongs to the controller "= get_class($this->context) ?>"
+ in the "= $this->context->module->id ?>" module.
+
+
+ You may customize this page by editing the following file:
+ = __FILE__ ?>
+
+
diff --git a/common/modules/product/views/manage/_form.php b/common/modules/product/views/manage/_form.php
new file mode 100755
index 0000000..6c7f33f
--- /dev/null
+++ b/common/modules/product/views/manage/_form.php
@@ -0,0 +1,87 @@
+
+
+
+
+ ['enctype' => 'multipart/form-data']
+ ]); ?>
+
+ = $form->field($model, 'name')->textInput(['maxlength' => true]) ?>
+
+ = $form->field($model, 'is_top')->checkbox(['label' => 'ТОП']) ?>
+ = $form->field($model, 'is_new')->checkbox(['label' => 'Новинка']) ?>
+ = $form->field($model, 'akciya')->checkbox(['label' => 'Акционный']) ?>
+
+ = $form->field($model, 'description')->widget(\mihaildev\ckeditor\CKEditor::className(),['editorOptions' => [ 'preset' => 'full', 'inline' => false, ], ]); ?>
+ = $form->field($model, 'video')->textarea(); ?>
+
+ = $form->field($model, 'brand_id')->dropDownList(
+ ArrayHelper::map(ProductHelper::getBrands()->all(), 'brand_id', 'name'),
+ [
+ 'prompt' => Yii::t('product', 'Select brand')
+ ]
+ ) ?>
+
+ = $form->field($model, 'categories')->widget(Select2::className(), [
+ 'data' => ArtboxTreeHelper::treeMap(ProductHelper::getCategories(), 'category_id', 'name'),
+ 'language' => 'ru',
+ 'options' => [
+ 'placeholder' => Yii::t('product', 'Select categories'),
+ 'multiple' => true,
+ ],
+ 'pluginOptions' => [
+ 'allowClear' => true
+ ],
+ ]
+ ) ?>
+
+ = $form->field($model, 'imagesUpload[]')->widget(\kartik\file\FileInput::classname(), [
+ 'language' => 'ru',
+ 'options' => [
+ 'accept' => 'image/*',
+ 'multiple' => true,
+ ],
+ 'pluginOptions' => [
+ 'allowedFileExtensions' => ['jpg', 'gif', 'png'],
+ 'initialPreview' => !empty($model->imagesHTML) ? $model->imagesHTML : [],
+ 'initialPreviewConfig' => $model->imagesConfig,
+ 'overwriteInitial' => false,
+ 'showRemove' => false,
+ 'showUpload' => false,
+// 'uploadUrl' => empty($model->product_id) ? null : \yii\helpers\Url::to(['/product/manage/uploadImage']),
+ 'uploadAsync' => !empty($model->product_id),
+ 'previewFileType' => 'image',
+ ],
+ ]); ?>
+
+
+ all() as $group) :?>
+ = $form->field($model, 'options')->checkboxList(
+ ArrayHelper::map($group->options, 'tax_option_id', 'ValueRenderFlash'),
+ [
+ 'multiple' => true,
+ 'unselect' => null,
+ ]
+ )->label($group->name);?>
+
+
+
+
+ = Html::submitButton($model->isNewRecord ? Yii::t('product', 'Create') : Yii::t('product', 'Update'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
+
+
+
+
+
diff --git a/common/modules/product/views/manage/_search.php b/common/modules/product/views/manage/_search.php
new file mode 100755
index 0000000..f021b65
--- /dev/null
+++ b/common/modules/product/views/manage/_search.php
@@ -0,0 +1,31 @@
+
+
+
+
+ ['index'],
+ 'method' => 'get',
+ ]); ?>
+
+ = $form->field($model, 'name') ?>
+
+ = $form->field($model, 'brand_id') ?>
+
+ = $form->field($model, 'product_id') ?>
+
+
+ = Html::submitButton(Yii::t('product', 'Search'), ['class' => 'btn btn-primary']) ?>
+ = Html::resetButton(Yii::t('product', 'Reset'), ['class' => 'btn btn-default']) ?>
+
+
+
+
+
diff --git a/common/modules/product/views/manage/create.php b/common/modules/product/views/manage/create.php
new file mode 100755
index 0000000..36f73c2
--- /dev/null
+++ b/common/modules/product/views/manage/create.php
@@ -0,0 +1,21 @@
+title = Yii::t('product', 'Create Product');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('product', 'Products'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/common/modules/product/views/manage/export-process.php b/common/modules/product/views/manage/export-process.php
new file mode 100755
index 0000000..48f6372
--- /dev/null
+++ b/common/modules/product/views/manage/export-process.php
@@ -0,0 +1,106 @@
+
+registerJs("var in_process=true;
+ var count=1;
+ var filename = null;
+
+ doExport(0,filename);
+
+ function doExport(from,filename) {
+ from = typeof(from) != 'undefined' ? from : 0;
+
+ $.ajax({
+ method: 'get',
+ url: '".Yii::$app->request->baseUrl .'/product/manage/export-process'."',
+ data: {
+ from:from,
+ filename: filename
+ },
+ dataType: 'json',
+ success: function(data){
+
+ var per = Math.round(100*data.from/data.totalsize)+'%';
+ $('#progressbar div').css({width: per});
+
+ if(data != false && !data.end)
+ {
+ doExport(data.from,data.filename);
+ }
+ else
+ {
+ console.log(data.link);
+ $(progressbar).hide('fast');
+ $('#result_link').attr('href', data.link).removeClass('hidden');
+ in_process = false;
+ }
+ },
+ error: function(xhr, status, errorThrown) {
+ }
+ });
+ }");
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/common/modules/product/views/manage/export.php b/common/modules/product/views/manage/export.php
new file mode 100755
index 0000000..d18b220
--- /dev/null
+++ b/common/modules/product/views/manage/export.php
@@ -0,0 +1,55 @@
+
+
+
\ No newline at end of file
diff --git a/common/modules/product/views/manage/import-process.php b/common/modules/product/views/manage/import-process.php
new file mode 100755
index 0000000..af24fce
--- /dev/null
+++ b/common/modules/product/views/manage/import-process.php
@@ -0,0 +1,70 @@
+registerJs("
+
+");
+?>
+
+
+
+
diff --git a/common/modules/product/views/manage/import.php b/common/modules/product/views/manage/import.php
new file mode 100755
index 0000000..81f9a31
--- /dev/null
+++ b/common/modules/product/views/manage/import.php
@@ -0,0 +1,51 @@
+
+
+
diff --git a/common/modules/product/views/manage/index.php b/common/modules/product/views/manage/index.php
new file mode 100755
index 0000000..3c79fc9
--- /dev/null
+++ b/common/modules/product/views/manage/index.php
@@ -0,0 +1,141 @@
+title = Yii::t('product', 'Products');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+ render('_search', ['model' => $searchModel]); ?>
+
+
+ = Html::a(Yii::t('product', 'Create Product'), ['create'], ['class' => 'btn btn-success']) ?>
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'filterModel' => $searchModel,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+ 'name',
+ [
+ 'label' => Yii::t('product', 'Brand'),
+ 'attribute' => 'brand_name',
+ 'value' => 'brand.name',
+ 'format' => 'raw',
+ 'filter' => Select2::widget([
+ 'model' => $searchModel,
+ 'attribute' => 'brand_id',
+ 'data' => ArrayHelper::map(ProductHelper::getBrands()->all(), 'brand_id', 'name'),
+ 'language' => 'ru',
+ 'options' => [
+ 'placeholder' => Yii::t('product', 'Select brand'),
+ 'multiple' => false,
+ ],
+ 'pluginOptions' => [
+ 'allowClear' => true
+ ],
+ ])
+ ],
+ [
+ 'label' => Yii::t('product', 'Category'),
+ 'attribute' => 'category_name',
+ 'value' => function($model) {
+ $categories = [];
+ foreach ($model->categories as $category) {
+ $categories[] = $category->name;
+ }
+ return implode(", ", $categories);
+ },
+ 'format' => 'raw',
+ 'filter' => Select2::widget([
+ 'model' => $searchModel,
+ 'attribute' => 'category_id',
+ 'data' => ArtboxTreeHelper::treeMap(ProductHelper::getCategories(), 'category_id', 'name'),
+ 'language' => 'ru',
+ 'options' => [
+ 'placeholder' => Yii::t('product', 'Select category'),
+ 'multiple' => false,
+ ],
+ 'pluginOptions' => [
+ 'allowClear' => true
+ ],
+ ])
+ ],
+ [
+ 'label' => Yii::t('product', 'SKU'),
+ 'attribute' => 'variant_sku',
+ 'value' => 'variant.sku',
+ ],
+ 'variant.price',
+ 'variant.price_old',
+ [
+ 'label' => Yii::t('product', 'Stock'),
+ 'attribute' => 'variant_stock',
+ 'value' => 'variant.stock_caption',
+ ],
+ [
+ 'class' => 'yii\grid\ActionColumn',
+ 'template' => '{items} {view} |{is_top} {is_new} {akciya} | {update} {delete}',
+ 'buttons' => [
+ 'is_top' => function ($url, $model) {
+ return Html::a('
', $url, [
+ 'title' => Yii::t('product', ($model->is_top ? 'Set not is top' : 'Set is top')),
+ ]);
+ },
+ 'is_new' => function ($url, $model) {
+ return Html::a('
', $url, [
+ 'title' => Yii::t('product', ($model->is_new ? 'Set not is new' : 'Set is new')),
+ ]);
+ },
+ 'akciya' => function ($url, $model) {
+ return Html::a('
', $url, [
+ 'title' => Yii::t('product', ($model->akciya ? 'Set not is promotion' : 'Set is promotion')),
+ ]);
+ },
+ 'items' => function ($url, $model) {
+ return Html::a('
', $url, [
+ 'title' => Yii::t('product', 'Variants'),
+ ]);
+ },
+
+ ],
+ 'urlCreator' => function ($action, $model, $key, $index) {
+ switch ($action) {
+ case 'items':
+ return \yii\helpers\Url::to(['/product/variant', 'product_id' => $model->product_id]);
+ break;
+ case 'is_top':
+ return \yii\helpers\Url::to(['manage/is_top', 'id' => $model->product_id]);
+ break;
+ case 'is_new':
+ return \yii\helpers\Url::to(['manage/is_new', 'id' => $model->product_id]);
+ break;
+ case 'akciya':
+ return \yii\helpers\Url::to(['manage/akciya', 'id' => $model->product_id]);
+ break;
+ case 'view':
+ return \yii\helpers\Url::to(['/catalog/product', 'id' => $model->product_id, ['target' => '_blank']]);
+ break;
+ case 'update':
+ return \yii\helpers\Url::to(['manage/update', 'id' => $model->product_id]);
+ break;
+ case 'delete':
+ return \yii\helpers\Url::to(['manage/delete', 'id' => $model->product_id]);
+ break;
+ }
+ }
+ ],
+ ],
+ ]); ?>
+
diff --git a/common/modules/product/views/manage/update.php b/common/modules/product/views/manage/update.php
new file mode 100755
index 0000000..b4b80a5
--- /dev/null
+++ b/common/modules/product/views/manage/update.php
@@ -0,0 +1,24 @@
+title = Yii::t('product', 'Update {modelClass}: ', [
+ 'modelClass' => 'Product',
+]) . ' ' . $model->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('product', 'Products'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->product_id]];
+$this->params['breadcrumbs'][] = Yii::t('product', 'Update');
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ 'groups' => $groups,
+ ]) ?>
+
+
diff --git a/common/modules/product/views/manage/view.php b/common/modules/product/views/manage/view.php
new file mode 100755
index 0000000..96a750f
--- /dev/null
+++ b/common/modules/product/views/manage/view.php
@@ -0,0 +1,40 @@
+title = $model->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('product', 'Products'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('product', 'Update'), ['update', 'id' => $model->product_id], ['class' => 'btn btn-primary']) ?>
+ = Html::a(Yii::t('product', 'Delete'), ['delete', 'id' => $model->product_id], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => Yii::t('product', 'Are you sure you want to delete this item?'),
+ 'method' => 'post',
+ ],
+ ]) ?>
+
+
+ = DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+ 'product_id',
+ 'name',
+ 'fullname',
+ 'brand.name',
+ 'category.name',
+ 'image.imageUrl:image'
+ ],
+ ]) ?>
+
+
diff --git a/common/modules/product/views/product-unit/_form.php b/common/modules/product/views/product-unit/_form.php
new file mode 100755
index 0000000..f1a1206
--- /dev/null
+++ b/common/modules/product/views/product-unit/_form.php
@@ -0,0 +1,27 @@
+
+
+
diff --git a/common/modules/product/views/product-unit/_search.php b/common/modules/product/views/product-unit/_search.php
new file mode 100755
index 0000000..5fbe4cc
--- /dev/null
+++ b/common/modules/product/views/product-unit/_search.php
@@ -0,0 +1,33 @@
+
+
+
+
+ ['index'],
+ 'method' => 'get',
+ ]); ?>
+
+ = $form->field($model, 'product_unit_id') ?>
+
+ = $form->field($model, 'name') ?>
+
+ = $form->field($model, 'code') ?>
+
+ = $form->field($model, 'is_default')->checkbox() ?>
+
+
+ = Html::submitButton(Yii::t('product', 'Search'), ['class' => 'btn btn-primary']) ?>
+ = Html::resetButton(Yii::t('product', 'Reset'), ['class' => 'btn btn-default']) ?>
+
+
+
+
+
diff --git a/common/modules/product/views/product-unit/create.php b/common/modules/product/views/product-unit/create.php
new file mode 100755
index 0000000..5c54fcc
--- /dev/null
+++ b/common/modules/product/views/product-unit/create.php
@@ -0,0 +1,21 @@
+title = Yii::t('product', 'Create Product Unit');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('product', 'Product Units'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/common/modules/product/views/product-unit/index.php b/common/modules/product/views/product-unit/index.php
new file mode 100755
index 0000000..de9aad8
--- /dev/null
+++ b/common/modules/product/views/product-unit/index.php
@@ -0,0 +1,34 @@
+title = Yii::t('product', 'Product Units');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+ render('_search', ['model' => $searchModel]); ?>
+
+
+ = Html::a(Yii::t('product', 'Create Product Unit'), ['create'], ['class' => 'btn btn-success']) ?>
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'filterModel' => $searchModel,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+
+ 'name',
+ 'code:html',
+ 'is_default:boolean',
+
+ ['class' => 'yii\grid\ActionColumn'],
+ ],
+ ]); ?>
+
diff --git a/common/modules/product/views/product-unit/update.php b/common/modules/product/views/product-unit/update.php
new file mode 100755
index 0000000..fcd5ef7
--- /dev/null
+++ b/common/modules/product/views/product-unit/update.php
@@ -0,0 +1,23 @@
+title = Yii::t('product', 'Update {modelClass}: ', [
+ 'modelClass' => 'Product Unit',
+]) . $model->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('product', 'Product Units'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->product_unit_id]];
+$this->params['breadcrumbs'][] = Yii::t('product', 'Update');
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/common/modules/product/views/product-unit/view.php b/common/modules/product/views/product-unit/view.php
new file mode 100755
index 0000000..a5fbf82
--- /dev/null
+++ b/common/modules/product/views/product-unit/view.php
@@ -0,0 +1,38 @@
+title = $model->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('product', 'Product Units'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('product', 'Update'), ['update', 'id' => $model->product_unit_id], ['class' => 'btn btn-primary']) ?>
+ = Html::a(Yii::t('product', 'Delete'), ['delete', 'id' => $model->product_unit_id], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => Yii::t('product', 'Are you sure you want to delete this item?'),
+ 'method' => 'post',
+ ],
+ ]) ?>
+
+
+ = DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+ 'product_unit_id',
+ 'name',
+ 'code',
+ 'is_default:boolean',
+ ],
+ ]) ?>
+
+
diff --git a/common/modules/product/views/product-variant-type/_form.php b/common/modules/product/views/product-variant-type/_form.php
new file mode 100755
index 0000000..f0a7cfc
--- /dev/null
+++ b/common/modules/product/views/product-variant-type/_form.php
@@ -0,0 +1,25 @@
+
+
+
diff --git a/common/modules/product/views/product-variant-type/_search.php b/common/modules/product/views/product-variant-type/_search.php
new file mode 100755
index 0000000..7b3cdec
--- /dev/null
+++ b/common/modules/product/views/product-variant-type/_search.php
@@ -0,0 +1,31 @@
+
+
+
+
+ ['index'],
+ 'method' => 'get',
+ ]); ?>
+
+ = $form->field($model, 'product_variant_type_id') ?>
+
+ = $form->field($model, 'name') ?>
+
+ = $form->field($model, 'name2') ?>
+
+
+ = Html::submitButton(Yii::t('product', 'Search'), ['class' => 'btn btn-primary']) ?>
+ = Html::resetButton(Yii::t('product', 'Reset'), ['class' => 'btn btn-default']) ?>
+
+
+
+
+
diff --git a/common/modules/product/views/product-variant-type/create.php b/common/modules/product/views/product-variant-type/create.php
new file mode 100755
index 0000000..e7d68c8
--- /dev/null
+++ b/common/modules/product/views/product-variant-type/create.php
@@ -0,0 +1,21 @@
+title = Yii::t('product', 'Create Product Variant Type');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('product', 'Product Variant Types'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/common/modules/product/views/product-variant-type/index.php b/common/modules/product/views/product-variant-type/index.php
new file mode 100755
index 0000000..4e44be0
--- /dev/null
+++ b/common/modules/product/views/product-variant-type/index.php
@@ -0,0 +1,32 @@
+title = Yii::t('product', 'Product Variant Types');
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+ render('_search', ['model' => $searchModel]); ?>
+
+
+ = Html::a(Yii::t('product', 'Create Product Variant Type'), ['create'], ['class' => 'btn btn-success']) ?>
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'filterModel' => $searchModel,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+ 'name',
+ 'name2',
+
+ ['class' => 'yii\grid\ActionColumn'],
+ ],
+ ]); ?>
+
diff --git a/common/modules/product/views/product-variant-type/update.php b/common/modules/product/views/product-variant-type/update.php
new file mode 100755
index 0000000..d4eb6de
--- /dev/null
+++ b/common/modules/product/views/product-variant-type/update.php
@@ -0,0 +1,23 @@
+title = Yii::t('product', 'Update {modelClass}: ', [
+ 'modelClass' => 'Product Variant Type',
+]) . $model->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('product', 'Product Variant Types'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->product_variant_type_id]];
+$this->params['breadcrumbs'][] = Yii::t('product', 'Update');
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ ]) ?>
+
+
diff --git a/common/modules/product/views/product-variant-type/view.php b/common/modules/product/views/product-variant-type/view.php
new file mode 100755
index 0000000..1a54e27
--- /dev/null
+++ b/common/modules/product/views/product-variant-type/view.php
@@ -0,0 +1,37 @@
+title = $model->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('product', 'Product Variant Types'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('product', 'Update'), ['update', 'id' => $model->product_variant_type_id], ['class' => 'btn btn-primary']) ?>
+ = Html::a(Yii::t('product', 'Delete'), ['delete', 'id' => $model->product_variant_type_id], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => Yii::t('product', 'Are you sure you want to delete this item?'),
+ 'method' => 'post',
+ ],
+ ]) ?>
+
+
+ = DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+ 'product_variant_type_id',
+ 'name',
+ 'name2',
+ ],
+ ]) ?>
+
+
diff --git a/common/modules/product/views/variant/_form.php b/common/modules/product/views/variant/_form.php
new file mode 100755
index 0000000..1fcde25
--- /dev/null
+++ b/common/modules/product/views/variant/_form.php
@@ -0,0 +1,149 @@
+registerJs($js, View::POS_LOAD);
+?>
+
+
+ 'dynamic-form',
+ 'options' => ['enctype' => 'multipart/form-data']
+ ]); ?>
+
+ = $form->field($model, 'name')->textInput(['maxlength' => true]) ?>
+
+ = $form->field($model, 'product_id')->hiddenInput()->label(false); ?>
+
+ = $form->field($model, 'sku')->textarea(); ?>
+ = $form->field($model, 'price')->textarea(); ?>
+ = $form->field($model, 'price_old')->textarea(); ?>
+ = $form->field($model, 'image')->widget(\kartik\file\FileInput::classname(), [
+ 'model' => $model,
+ 'attribute' => 'image',
+ 'options' => [
+ 'accept' => 'image/*',
+ 'multiple' => true
+ ],
+ 'pluginOptions' => [
+ 'allowedFileExtensions' => ['jpg','gif','png'],
+ 'initialPreview' => $model->imageUrl ? \common\components\artboximage\ArtboxImageHelper::getImage($model->imageUrl, 'products') : '',
+ 'overwriteInitial' => true,
+ 'showRemove' => true,
+ 'showUpload' => false,
+ ],
+ ]); ?>
+
+ 'dynamicform_wrapper', // required: only alphanumeric characters plus "_" [A-Za-z0-9_]
+ 'widgetBody' => '.container-items', // required: css class selector
+ 'widgetItem' => '.item', // required: css class
+ 'limit' => 10, // the maximum times, an element can be added (default 999)
+ 'min' => 0, // 0 or 1 (default 1)
+ 'insertButton' => '.add-item', // css class
+ 'deleteButton' => '.remove-item', // css class
+ 'model' => $stocks[0],
+ 'formId' => 'dynamic-form',
+ 'formFields' => [
+ 'quantity',
+ 'name',
+ ],
+ ]); ?>
+
+
+
+
+ Склады
+
+
+
+
+
+ $stock): ?>
+
+
+ isNewRecord) {
+ echo Html::activeHiddenInput($stock, "[{$i}]stock_id");
+ }
+ ?>
+
+
+ = $form->field($stock, "[{$i}]quantity")->textInput(['maxlength' => true]) ?>
+
+
+ = $form->field($stock, "[{$i}]name")->textInput(['maxlength' => true]) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ = $form->field($model, 'product_unit_id')->dropDownList(
+ ArrayHelper::map(\common\modules\product\models\ProductUnit::find()->all(), 'product_unit_id', 'name'),
+ [
+ 'prompt' => Yii::t('product', 'Unit'),
+ ])->label(Yii::t('product', 'Unit')) ?>
+
+
+ all() as $group) :?>
+ = $form->field($model, 'options')->checkboxList(
+ ArrayHelper::map($group->options, 'tax_option_id', 'ValueRenderFlash'),
+ [
+ 'multiple' => true,
+ 'unselect' => null,
+ ]
+ )->label($group->name);?>
+
+
+
+
+ = Html::submitButton($model->isNewRecord ? Yii::t('product', 'Create') : Yii::t('product', 'Update'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
+
+
+
+
+
diff --git a/common/modules/product/views/variant/_search.php b/common/modules/product/views/variant/_search.php
new file mode 100755
index 0000000..00015dc
--- /dev/null
+++ b/common/modules/product/views/variant/_search.php
@@ -0,0 +1,31 @@
+
+
+
+
+ ['index'],
+ 'method' => 'get',
+ ]); ?>
+
+ = $form->field($model, 'name') ?>
+
+ = $form->field($model, 'brand_id') ?>
+
+ = $form->field($model, 'product_id') ?>
+
+
+ = Html::submitButton(Yii::t('product', 'Search'), ['class' => 'btn btn-primary']) ?>
+ = Html::resetButton(Yii::t('product', 'Reset'), ['class' => 'btn btn-default']) ?>
+
+
+
+
+
diff --git a/common/modules/product/views/variant/create.php b/common/modules/product/views/variant/create.php
new file mode 100755
index 0000000..1c3d2de
--- /dev/null
+++ b/common/modules/product/views/variant/create.php
@@ -0,0 +1,23 @@
+title = Yii::t('product', 'Create Product');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('product', 'Products'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ 'groups' => $groups,
+ 'stocks' => $stocks,
+ ]) ?>
+
+
diff --git a/common/modules/product/views/variant/index.php b/common/modules/product/views/variant/index.php
new file mode 100755
index 0000000..4355909
--- /dev/null
+++ b/common/modules/product/views/variant/index.php
@@ -0,0 +1,96 @@
+title = Yii::t('product', 'Variants');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('product', 'Products'), 'url' => ['/product/manage']];
+if (!empty($product)) {
+ $this->params['breadcrumbs'] = [
+ ['label' => Yii::t('product', 'Variants'), 'url' => ['/product/variant']],
+ $product->fullname
+ ];
+} else {
+ $this->params['breadcrumbs'][] = $this->title;
+}
+?>
+
+
+
= Html::encode($this->title) ?>
+ render('_search', ['model' => $searchModel]); ?>
+
+
+ = Html::a(Yii::t('product', 'Create Variant'), Url::toRoute(['create','product_id'=> $product_id]), ['class' => 'btn btn-success']) ?>
+
+ = GridView::widget([
+ 'dataProvider' => $dataProvider,
+ 'filterModel' => $searchModel,
+ 'columns' => [
+ ['class' => 'yii\grid\SerialColumn'],
+
+ [
+ 'attribute' => 'product_id',
+ 'value' => 'fullname',
+ 'label' => Yii::t('product', 'Name'),
+ 'filter' => \kartik\select2\Select2::widget([
+ 'model' => $searchModel,
+ 'attribute' => 'product_id',
+ 'data' => \yii\helpers\ArrayHelper::map(\common\modules\product\models\Product::find()->orderBy(['name' => 'ASC'])->all(), 'product_id', 'name'),
+ 'language' => 'ru',
+ 'options' => [
+ 'placeholder' => Yii::t('product', 'Select product'),
+ 'multiple' => false,
+ ],
+ 'pluginOptions' => [
+ 'allowClear' => true
+ ],
+ ]),
+ ],
+ 'sku',
+ 'price',
+ 'price_old',
+ 'stock',
+
+ [
+ 'class' => 'yii\grid\ActionColumn',
+ 'buttons' => [
+ 'view' => function ($url, $model)
+ {
+ return Html::a (
+ '
',
+ Url::to(['view','product_id'=> $model->product_id, 'id' => $model->product_variant_id]),
+ [
+ 'title' => "Просмотр",
+ ]
+ );
+ },
+ 'update' => function ($url, $model)
+ {
+ return Html::a (
+ '
',
+ Url::to(['update','product_id'=> $model->product_id, 'id' => $model->product_variant_id]),
+ [
+ 'title' => "Редактировать",
+ ]
+ );
+ },
+ 'delete' => function ($url, $model)
+ {
+
+ return Html::a('
', Url::to(['delete','product_id'=> $model->product_id, 'id' => $model->product_variant_id]), [
+ 'title' => Yii::t('yii', 'Delete'),
+ 'data-confirm' => Yii::t('yii', 'Are you sure to delete this item?'),
+ 'data-method' => 'post',
+ ]);
+
+ },
+ ],
+ ],
+ ],
+ ]); ?>
+
diff --git a/common/modules/product/views/variant/update.php b/common/modules/product/views/variant/update.php
new file mode 100755
index 0000000..a146f7d
--- /dev/null
+++ b/common/modules/product/views/variant/update.php
@@ -0,0 +1,27 @@
+title = Yii::t('product', 'Update {modelClass}: ', [
+ 'modelClass' => 'Product',
+]) . ' ' . $model->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('product', 'Products'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->product->name, 'url' => ['view', 'id' => $model->product->product_id]];
+$this->params['breadcrumbs'][] = ['label' => Yii::t('product', 'Variants'), 'url' => Url::to(['/product/variant', 'product_id' => $model->product->product_id])];
+$this->params['breadcrumbs'][] = Yii::t('product', 'Update');
+?>
+
+
+
= Html::encode($this->title) ?>
+
+ = $this->render('_form', [
+ 'model' => $model,
+ 'groups' => $groups,
+ 'stocks' => $stocks,
+ ]) ?>
+
+
diff --git a/common/modules/product/views/variant/view.php b/common/modules/product/views/variant/view.php
new file mode 100755
index 0000000..5eaecd8
--- /dev/null
+++ b/common/modules/product/views/variant/view.php
@@ -0,0 +1,42 @@
+title = $model->name;
+$this->params['breadcrumbs'][] = ['label' => Yii::t('product', 'Products'), 'url' => ['index']];
+$this->params['breadcrumbs'][] = ['label' => $model->product->name, 'url' => ['view', 'id' => $model->product->product_id]];
+$this->params['breadcrumbs'][] = ['label' => Yii::t('product', 'Variants'), 'url' => ['/product/variant?product_id='. $model->product->product_id]];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = Html::a(Yii::t('product', 'Update'), ['update', 'id' => $model->product_variant_id], ['class' => 'btn btn-primary']) ?>
+ = Html::a(Yii::t('product', 'Delete'), ['delete', 'id' => $model->product_variant_id], [
+ 'class' => 'btn btn-danger',
+ 'data' => [
+ 'confirm' => Yii::t('product', 'Are you sure you want to delete this item?'),
+ 'method' => 'post',
+ ],
+ ]) ?>
+
+
+ = DetailView::widget([
+ 'model' => $model,
+ 'attributes' => [
+ 'product_id',
+ 'name',
+ 'fullname',
+ 'brand.name',
+ 'category.name',
+ 'image.imageUrl:image'
+ ],
+ ]) ?>
+
+
diff --git a/common/modules/product/widgets/brandsCarouselWidget.php b/common/modules/product/widgets/brandsCarouselWidget.php
new file mode 100755
index 0000000..1263a09
--- /dev/null
+++ b/common/modules/product/widgets/brandsCarouselWidget.php
@@ -0,0 +1,21 @@
+all();
+ return $this->render('brandsCarousel', [
+ 'brands' => $brands,
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/common/modules/product/widgets/catalogSubmenuWidget.php b/common/modules/product/widgets/catalogSubmenuWidget.php
new file mode 100755
index 0000000..ef9388f
--- /dev/null
+++ b/common/modules/product/widgets/catalogSubmenuWidget.php
@@ -0,0 +1,36 @@
+root_id);
+
+ $categories = $rootCategory->getAllChildren(2, [], 'categoryName')->all();
+ $populary = [];
+ foreach($categories as $category) {
+ if ($category->populary) {
+ $populary[] = $category;
+ }
+ }
+
+ return $this->render('submenu', [
+ 'rootCategory' => $rootCategory,
+ 'rootClass' => $this->rootClass,
+ 'populary' => $populary,
+ 'items' => $rootCategory->buildTree($categories, $rootCategory->category_id)
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/common/modules/product/widgets/lastProducts.php b/common/modules/product/widgets/lastProducts.php
new file mode 100755
index 0000000..ab00838
--- /dev/null
+++ b/common/modules/product/widgets/lastProducts.php
@@ -0,0 +1,22 @@
+render('products_block', [
+ 'title' => \Yii::t('product', 'Вы недавно просматривали'),
+ 'class' => 'last-products',
+ 'products' => ProductHelper::getLastProducts(true),
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/common/modules/product/widgets/similarProducts.php b/common/modules/product/widgets/similarProducts.php
new file mode 100755
index 0000000..aebd754
--- /dev/null
+++ b/common/modules/product/widgets/similarProducts.php
@@ -0,0 +1,35 @@
+product, $this->count);
+
+ if (!$this->title) {
+ $this->title = Yii::t('product', 'Similar products');
+ }
+
+ return $this->render('products_block', [
+ 'title' => $this->title,
+ 'class' => 'similar-products',
+ 'products' => $products,
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/common/modules/product/widgets/specialProducts.php b/common/modules/product/widgets/specialProducts.php
new file mode 100755
index 0000000..2a9bb15
--- /dev/null
+++ b/common/modules/product/widgets/specialProducts.php
@@ -0,0 +1,47 @@
+type, $this->count, $this->sort);
+
+ if (!$this->title) {
+ switch($this->type) {
+ case 'top':
+ $this->title = Yii::t('product', 'Top products');
+ break;
+ case 'promo':
+ $this->title = Yii::t('product', 'Promo products');
+ break;
+ case 'new':
+ $this->title = Yii::t('product', 'New products');
+ break;
+ }
+ }
+
+ return $this->render('products_block', [
+ 'title' => $this->title,
+ 'class' => $this->type,
+ 'products' => $products,
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/common/modules/product/widgets/views/brandsCarousel.php b/common/modules/product/widgets/views/brandsCarousel.php
new file mode 100755
index 0000000..0d76762
--- /dev/null
+++ b/common/modules/product/widgets/views/brandsCarousel.php
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/common/modules/product/widgets/views/product_smart.php b/common/modules/product/widgets/views/product_smart.php
new file mode 100755
index 0000000..471cdaf
--- /dev/null
+++ b/common/modules/product/widgets/views/product_smart.php
@@ -0,0 +1,70 @@
+
+
+
+
+
';
+ } else if($class == 'new') {
+ print '
';
+ } else if($class == 'promo'){
+ print '
Акция
';
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+ есть на складе
+
+
+
+ Код: =$product->variant->sku?>
+
+
+
+
+ = $product->variant->price ?> грн
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/modules/product/widgets/views/products_block.php b/common/modules/product/widgets/views/products_block.php
new file mode 100755
index 0000000..0652a2e
--- /dev/null
+++ b/common/modules/product/widgets/views/products_block.php
@@ -0,0 +1,28 @@
+
+
+
+
+
+//= $title?>
+
+
+
+ = $this->render('product_smart', [
+ 'product' => $product,
+ 'class' => $class
+ ]);?>
+
+
+
+
+
+
+
+
+
diff --git a/common/modules/product/widgets/views/submenu.php b/common/modules/product/widgets/views/submenu.php
new file mode 100755
index 0000000..a20f588
--- /dev/null
+++ b/common/modules/product/widgets/views/submenu.php
@@ -0,0 +1,50 @@
+
diff --git a/common/modules/rubrication/Module.php b/common/modules/rubrication/Module.php
new file mode 100755
index 0000000..dd3159a
--- /dev/null
+++ b/common/modules/rubrication/Module.php
@@ -0,0 +1,26 @@
+keyNameId === null) {
+ $primaryKey = $owner->primaryKey();
+ if (!isset($primaryKey[0])) {
+ throw new Exception('"' . $owner->className() . '" must have a primary key.');
+ }
+ $this->keyNameId = $primaryKey[0];
+ }
+ }
+
+ /*
+ * Inicialize behavior (read and prepare params)
+ */
+ public function init() {
+ if (empty($this->valueModel)) {
+ $this->valueModel = TaxValueString::className();
+ }
+ if (empty($this->valuePKey)) {
+ $vm = $this->valueModel;
+ $primaryKey = $vm::primaryKey();
+ if (!isset($primaryKey[0])) {
+ throw new Exception('"' . $this->valueModel . '" must have a primary key.');
+ }
+ if (count($primaryKey) > 1) {
+ throw new Exception('"' . $this->valueModel . '" is contains multiple keys.');
+ }
+ $this->valuePKey = $primaryKey[0];
+ }
+ }
+
+ /*
+ * Events for auto-drive default value
+ */
+ public function events() {
+ return [
+ ActiveRecord::EVENT_AFTER_INSERT => 'afterInsert',
+ ActiveRecord::EVENT_AFTER_UPDATE => 'beforeUpdate',
+ ActiveRecord::EVENT_BEFORE_DELETE => 'beforeDelete',
+ ];
+ }
+
+ public function getAutoValue() {
+ return $this->owner->hasOne($this->valueModel, [$this->valuePKey => $this->keyNameValue]);
+ }
+
+ public function afterInsert() {
+ /** @var ActiveRecord $valueModel */
+ $valueModel = new $this->valueModel;
+ foreach ($this->valueFields as $key => $field) {
+ $valueModel->setAttribute($field, $this->$key);
+ }
+ $valueModel->setAttribute($this->valueOptionId, $this->owner->getAttribute($this->keyNameId));
+ $valueModel->save();
+ $this->owner->setAttribute($this->keyNameValue, $valueModel->getAttribute($this->valuePKey));
+ $this->owner->{$this->slug['slugKeyName']} = $this->slugify($valueModel->{$this->slug['valueKeyName']});
+ $this->owner->save(false);
+ }
+
+ public function beforeUpdate() {
+ if ($this->owner->getIsNewRecord()) {
+ throw new NotSupportedException('Method "' . $this->owner->className() . '::insert" is not supported for inserting new entitys.');
+ }
+
+ if (!$this->owner->getAttribute($this->keyNameValue)) {
+ return;
+ }
+
+ /** @var ActiveRecord $valueModel */
+ $valueModelName = $this->valueModel;
+ $valueModel = $valueModelName::findOne($this->owner->getAttribute($this->keyNameValue));
+
+ $isave = false;
+ foreach ($this->valueFields as $key => $field) {
+ if ($valueModel->getAttribute($field) == $this->$key) {
+ continue;
+ }
+ $isave = true;
+ $valueModel->setAttribute($field, $this->$key);
+ }
+ if ($isave) {
+ $valueModel->setAttribute($this->valueOptionId, $this->owner->getAttribute($this->keyNameId));
+ $valueModel->save();
+ if (!empty($this->slug) && empty($this->owner->{$this->slug['slugKeyName']})) {
+ $this->owner->{$this->slug['slugKeyName']} = $this->slugify($valueModel->{$this->slug['valueKeyName']});
+ }
+ }
+ }
+
+ public function beforeDelete() {
+ /** @var ActiveRecord $valueModel */
+ $valueModelName = $this->valueModel;
+ $valueModelName::deleteAll([
+ $this->valueOptionId => $this->owner->getAttribute($this->keyNameId)
+ ]);
+ }
+
+ private function slugify( $slug )
+ {
+ return Inflector::slug( TransliteratorHelper::process( $slug ), '-', true );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function canSetProperty($key, $checkVars = true)
+ {
+ return array_key_exists($key, $this->valueFields) ?
+ true : parent::canSetProperty($key, $checkVars = true);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function __set($key, $value) {
+ if (isset($this->valueFields[$key])) {
+ $this->values[$key] = $value;
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function __get($key) {
+ return $this->_getValue($key);
+ }
+
+ public function _getValue($key) {
+ if (isset($this->values[$key])) {
+ return $this->values[$key];
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/modules/rubrication/controllers/DefaultController.php b/common/modules/rubrication/controllers/DefaultController.php
new file mode 100755
index 0000000..cbc303c
--- /dev/null
+++ b/common/modules/rubrication/controllers/DefaultController.php
@@ -0,0 +1,20 @@
+render('index');
+ }
+}
diff --git a/common/modules/rubrication/controllers/TaxGroupController.php b/common/modules/rubrication/controllers/TaxGroupController.php
new file mode 100755
index 0000000..30c37b9
--- /dev/null
+++ b/common/modules/rubrication/controllers/TaxGroupController.php
@@ -0,0 +1,158 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['POST'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all TaxGroup models.
+ * @param $level integer
+ * @return mixed
+ */
+ public function actionIndex($level)
+ {
+ $dataProvider = new ActiveDataProvider([
+ 'query' => TaxGroup::find()->where(['level' => $level]),
+ ]);
+
+ return $this->render('index', [
+ 'dataProvider' => $dataProvider,
+ 'level' => $level
+ ]);
+ }
+
+ /**
+ * Displays a single TaxGroup model.
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionView($level,$id)
+ {
+ return $this->render('view', [
+ 'model' => $this->findModel($id),
+ ]);
+ }
+
+ /**
+ * Creates a new TaxGroup model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @param $level integer
+ * @return mixed
+ */
+ public function actionCreate($level)
+ {
+ $model = new TaxGroup();
+
+ if ($model->load(Yii::$app->request->post()) && $model->validate()) {
+ $model->level = $level;
+ $model->save();
+ return $this->redirect(['index', 'level' => $level]);
+ } else {
+ return $this->render('create', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing TaxGroup model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param $level integer
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionUpdate($level,$id)
+ {
+ $model = $this->findModel($id);
+
+ if ($model->load(Yii::$app->request->post()) && $model->save()) {
+ return $this->redirect(['index', 'level' => $level]);
+ } else {
+ return $this->render('update', [
+ 'model' => $model,
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing TaxGroup model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param $level integer
+ * @param integer $id
+ * @return mixed
+ */
+ public function actionDelete($level,$id)
+ {
+ $this->findModel($id)->delete();
+ return $this->redirect(['index', 'level' => $level]);
+ }
+
+ /*
+ * Group-relations
+ */
+ public function actionRelation($id, $relations = ['tax_option_to_group', 'tax_option_to_option']) {
+ $group = $this->findModel($id);
+ $items = [];
+
+ foreach($relations as $relation) {
+ $rows = $group->getRelations($relation)->all();
+ $items = array_merge($items, $rows);
+ }
+
+ return $this->render('relations', [
+ 'items' => $items,
+ 'group' => $group,
+ ]);
+ }
+
+ /*
+ * Rebuilp MP-params for group options
+ */
+ public function actionRebuild($id) {
+ TaxOption::find()->rebuildMP($id);
+
+ return $this->redirect(['index']);
+ }
+
+ /**
+ * Finds the TaxGroup model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param integer $id
+ * @return TaxGroup the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = TaxGroup::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+}
diff --git a/common/modules/rubrication/controllers/TaxOptionController.php b/common/modules/rubrication/controllers/TaxOptionController.php
new file mode 100755
index 0000000..12bf38a
--- /dev/null
+++ b/common/modules/rubrication/controllers/TaxOptionController.php
@@ -0,0 +1,193 @@
+ [
+ 'class' => VerbFilter::className(),
+ 'actions' => [
+ 'delete' => ['POST'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Lists all TaxOption models.
+ * @return mixed
+ */
+ public function actionIndex()
+ {
+ $searchModel = new TaxOptionSearch();
+ $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
+
+ $group = TaxGroup::findOne(Yii::$app->request->queryParams['group']);
+
+ return $this->render('index', [
+ 'searchModel' => $searchModel,
+ 'dataProvider' => $dataProvider,
+ 'group' => $group,
+ ]);
+ }
+
+ /**
+ * Displays a single TaxOption model.
+ * @param string $id
+ * @return mixed
+ */
+ public function actionView($id)
+ {
+ $model = $this->findModel($id);
+ $group = TaxGroup::findOne($model->tax_group_id);
+ return $this->render('view', [
+ 'model' => $model,
+ 'group' => $group,
+ ]);
+ }
+
+ /**
+ * Creates a new TaxOption model.
+ * If creation is successful, the browser will be redirected to the 'view' page.
+ * @return mixed
+ */
+ public function actionCreate()
+ {
+ $model = new TaxOption();
+ $group = TaxGroup::findOne(Yii::$app->request->queryParams['group']);
+ $valueModelName = $this->getValueModelName($group);
+ $valueModel = new $valueModelName;
+
+ if ($model->load(Yii::$app->request->post())) {
+ if ( ($image = UploadedFile::getInstance($model, 'image')) ) {
+ $model->image = $image->name;
+ }
+ if ($model->save() && $image) {
+
+ $imgDir = Yii::getAlias('@storage/tax_option/');
+
+ if(!is_dir($imgDir)) {
+ mkdir($imgDir, 0755, true);
+ }
+
+ $image->saveAs(Yii::getAlias('@storage/tax_option/' . $image->name));
+ }
+
+ $valueModel->tax_option_id = $model->tax_option_id;
+ $valueModel->value = $model->name;
+ $valueModel->save();
+
+ $model->default_value = $valueModel->tax_value_id;
+ $model->save();
+
+ return is_null(Yii::$app->request->post('create_and_new')) ? $this->redirect(['view', 'id' => $model->tax_option_id]) : $this->redirect(array_merge(['create'], Yii::$app->request->queryParams));
+ } else {
+ $model->tax_group_id = $group->tax_group_id;
+ if (!empty(Yii::$app->request->queryParams['parent'])) {
+ $model->parent_id = Yii::$app->request->queryParams['parent'];
+ }
+ return $this->render('create', [
+ 'model' => $model,
+ 'group' => $group,
+ 'valueModel' => $valueModel,
+ ]);
+ }
+ }
+
+ /**
+ * Updates an existing TaxOption model.
+ * If update is successful, the browser will be redirected to the 'view' page.
+ * @param string $id
+ * @return mixed
+ */
+ public function actionUpdate($id)
+ {
+ $model = $this->findModel($id);
+ $group = TaxGroup::findOne($model->tax_group_id);
+
+
+ if ($model->load(Yii::$app->request->post())) {
+
+
+ if ( ($image = UploadedFile::getInstance($model, 'image')) ) {
+ $model->image = $image->name;
+ }
+ if ($model->save() && $image) {
+
+ $imgDir = Yii::getAlias('@storage/tax_option/');
+
+ if(!is_dir($imgDir)) {
+ mkdir($imgDir, 0755, true);
+ }
+
+ $image->saveAs(Yii::getAlias('@storage/tax_option/' . $image->name));
+ }
+
+ TaxOption::find()->rebuildMP($model->tax_group_id);
+
+ return $this->redirect(['view', 'id' => $model->tax_option_id]);
+ } else {
+ return $this->render('update', [
+ 'model' => $model,
+ 'group' => $group
+ ]);
+ }
+ }
+
+ /**
+ * Deletes an existing TaxOption model.
+ * If deletion is successful, the browser will be redirected to the 'index' page.
+ * @param string $id
+ * @return mixed
+ */
+ public function actionDelete($id)
+ {
+ $model = $this->findModel($id);
+ $group_id = $model->tax_group_id;
+
+ $model->delete();
+
+ return $this->redirect(['index', 'group' => $group_id]);
+ }
+
+ /**
+ * Finds the TaxOption model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param string $id
+ * @return TaxOption the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($id)
+ {
+ if (($model = TaxOption::findOne($id)) !== null) {
+ return $model;
+ } else {
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+ }
+
+ protected function getValueModelName($group) {
+ $valueClass = '\common\modules\rubrication\models\TaxValue'. ucfirst($group->module);
+ return class_exists($valueClass) ? $valueClass : FALSE;
+ }
+}
diff --git a/common/modules/rubrication/helpers/RubricationHelper.php b/common/modules/rubrication/helpers/RubricationHelper.php
new file mode 100755
index 0000000..4044373
--- /dev/null
+++ b/common/modules/rubrication/helpers/RubricationHelper.php
@@ -0,0 +1,51 @@
+ Yii::t('order', 'Invisible'),
+ 1 => Yii::t('order', 'Active'),
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ * Returns sort-interval of appropriate options and others
+ * @return array.
+ */
+ static public function SortArray($low = 0, $high = 100) {
+ return range($low, $high);
+ }
+
+ static public function OptionTypes() {
+ if (!is_null(self::$types)) {
+ return self::$types;
+ }
+
+ $module = \Yii::$app->getModule('rubrication');
+
+ if (!is_array($module->types))
+ return [];
+
+ return $module->types;
+
+ }
+
+ public function checkboxList($items, $options = [])
+ {
+ $this->adjustLabelFor($options);
+ $this->parts['{input}'] = Html::activeCheckboxList($this->model, $this->attribute, $items, $options);
+
+ return $this;
+ }
+}
\ No newline at end of file
diff --git a/common/modules/rubrication/models/TaxGroup.php b/common/modules/rubrication/models/TaxGroup.php
new file mode 100755
index 0000000..096eaa7
--- /dev/null
+++ b/common/modules/rubrication/models/TaxGroup.php
@@ -0,0 +1,145 @@
+ [
+ 'class' => 'common\behaviors\Slug',
+ 'in_attribute' => 'name',
+ 'out_attribute' => 'alias',
+ 'translit' => true
+ ],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public static function tableName()
+ {
+ return 'tax_group';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return [
+ [['name', 'module'], 'required'],
+ [['description', 'settings'], 'string'],
+ [['hierarchical', 'is_filter', 'display','is_menu'], 'boolean'],
+ [['level', 'sort'], 'integer'],
+ [['alias', 'module'], 'string', 'max' => 50],
+ [['name'], 'string', 'max' => 255],
+ [['categories'], 'safe']
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'tax_group_id' => 'Tax Group ID',
+ 'alias' => 'Alias',
+ 'name' => 'Name',
+ 'description' => 'Description',
+ 'module' => 'Module',
+ 'hierarchical' => 'Hierarchical',
+// 'settings' => 'Settings',
+ 'is_filter' => 'Use in filter',
+ 'sort' => 'Sort',
+ 'display' => 'Display',
+ 'is_menu' => 'Отображать в меню',
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxGroupToGroups()
+ {
+ return $this->hasMany(TaxGroupToGroup::className(), ['tax_group1_id' => 'tax_group_id'])->inverseOf('taxGroup1');
+ }
+
+ public function getCategories()
+ {
+ return $this->hasMany(Category::className(), ['category_id' => 'category_id'])
+ ->viaTable('tax_group_to_category', ['tax_group_id' => 'tax_group_id']);
+ }
+
+ public function setCategories($values)
+ {
+ $this->categories = $values;
+ }
+
+ public function afterSave($insert, $changedAttributes)
+ {
+
+ $this->unlinkAll('categories',true);
+ $categories = Category::findAll($this->categories);
+ foreach($categories as $category){
+ $this->link('categories', $category);
+ }
+
+
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getOptions() {
+ return $this->getTaxOptions();
+ }
+ public function getTaxOptions()
+ {
+ return $this->hasMany(TaxOption::className(), ['tax_group_id' => 'tax_group_id'])->inverseOf('taxGroup');
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxOptionToGroups()
+ {
+ return $this->hasMany(TaxOptionToGroup::className(), ['tax_group_id' => 'tax_group_id'])->inverseOf('taxGroup');
+ }
+
+ public function getValueModelName($full_path = true) {
+ $valueClass = 'TaxValue'. ucfirst($this->module);
+ $fullClass = '\common\modules\rubrication\models\\'. $valueClass;
+ return class_exists($fullClass) ? $full_path ? $fullClass : $valueClass : FALSE;
+ }
+}
diff --git a/common/modules/rubrication/models/TaxGroupToCategory.php b/common/modules/rubrication/models/TaxGroupToCategory.php
new file mode 100755
index 0000000..481b582
--- /dev/null
+++ b/common/modules/rubrication/models/TaxGroupToCategory.php
@@ -0,0 +1,68 @@
+ true, 'targetClass' => Category::className(), 'targetAttribute' => ['category_id' => 'category_id']],
+ [['tax_group_id'], 'exist', 'skipOnError' => true, 'targetClass' => TaxGroup::className(), 'targetAttribute' => ['tax_group_id' => 'tax_group_id']],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'tax_group_to_category_id' => 'Tax Group To Category ID',
+ 'tax_group_id' => 'Tax Group ID',
+ 'category_id' => 'Category ID',
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getCategory()
+ {
+ return $this->hasOne(Category::className(), ['category_id' => 'category_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxGroup()
+ {
+ return $this->hasOne(TaxGroup::className(), ['tax_group_id' => 'tax_group_id']);
+ }
+}
diff --git a/common/modules/rubrication/models/TaxGroupToGroup.php b/common/modules/rubrication/models/TaxGroupToGroup.php
new file mode 100755
index 0000000..b621239
--- /dev/null
+++ b/common/modules/rubrication/models/TaxGroupToGroup.php
@@ -0,0 +1,69 @@
+ 50]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'tax_group1_id' => Yii::t('app', 'Tax Group1 ID'),
+ 'tax_group2_id' => Yii::t('app', 'Tax Group2 ID'),
+ 'alias' => Yii::t('app', 'Alias'),
+ 'sort' => Yii::t('app', 'Sort'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxGroup1()
+ {
+ return $this->hasOne(TaxGroup::className(), ['tax_group_id' => 'tax_group1_id']);
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxGroup2()
+ {
+ return $this->hasOne(TaxGroup::className(), ['tax_group_id' => 'tax_group2_id']);
+ }
+}
diff --git a/common/modules/rubrication/models/TaxOption.php b/common/modules/rubrication/models/TaxOption.php
new file mode 100755
index 0000000..d977fd9
--- /dev/null
+++ b/common/modules/rubrication/models/TaxOption.php
@@ -0,0 +1,221 @@
+ [
+ 'class' => ArtboxTreeBehavior::className(),
+ 'keyNameGroup' => 'tax_group_id',
+ ],
+ 'slug' => [
+ 'class' => 'common\behaviors\Slug',
+ 'in_attribute' => 'name',
+ 'out_attribute' => 'alias',
+ 'translit' => true
+ ],
+
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public static function tableName()
+ {
+ return '{{%tax_option}}';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function rules()
+ {
+ return [
+ [['tax_group_id','name'], 'required'],
+ [['tax_group_id', 'parent_id', 'sort', 'default_value'], 'integer'],
+ [['image','alias', 'value'], 'string', 'max' => 255],
+ [['tax_group_id'], 'exist', 'skipOnError' => true, 'targetClass' => TaxGroup::className(), 'targetAttribute' => ['tax_group_id' => 'tax_group_id']],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'tax_option_id' => Yii::t('app', 'Tax Option ID'),
+ 'tax_group_id' => Yii::t('app', 'Tax Group ID'),
+ 'parent_id' => Yii::t('app', 'Parent ID'),
+ 'alias' => Yii::t('app', 'Alias'),
+ 'sort' => Yii::t('app', 'Sort'),
+ 'default_value' => Yii::t('app', 'Default Value'),
+ 'image' => Yii::t('product', 'Image'),
+ ];
+ }
+
+ public static function find() {
+ return new TaxOptionQuery(get_called_class());
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxEntityRelations()
+ {
+ return $this->hasMany(TaxEntityRelation::className(), ['tax_option_id' => 'tax_option_id'])->inverseOf('taxOption');
+ }
+
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getGroup()
+ {
+ return $this->getTaxGroup();
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxGroup()
+ {
+ return $this->hasOne(TaxGroup::className(), ['tax_group_id' => 'tax_group_id'])->inverseOf('taxOptions');
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxOptions()
+ {
+ return $this->hasMany(TaxOption::className(), ['parent_id' => 'tax_option_id'])->inverseOf('parent');
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxOptionToGroups()
+ {
+ return $this->hasMany(TaxOptionToGroup::className(), ['tax_option_id' => 'tax_option_id'])->inverseOf('taxOption');
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxOptionToOptions()
+ {
+ return $this->hasMany(TaxOptionToOption::className(), ['tax_option1_id' => 'tax_option_id'])->inverseOf('taxOption1');
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxOptionToOptions0()
+ {
+ return $this->hasMany(TaxOptionToOption::className(), ['tax_option2_id' => 'tax_option_id'])->inverseOf('taxOption2');
+ }
+
+
+ public function getTaxValueString(){
+ return $this->name;
+ }
+
+
+ /**
+ */
+ public function getValue()
+ {
+ return $this->name;
+ }
+ public function setValue($value){
+ return $this->name = $value;
+ }
+ /**
+ */
+ public function getValueRenderFlash()
+ {
+ return $this->name;
+ }
+
+ /**
+ */
+ public function getValueRenderHTML()
+ {
+ return $this->name;
+ }
+
+
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getValues()
+ {
+ if ($valueClass = $this->getValueModelName())
+ return $this->hasMany($valueClass, ['tax_option_id' => 'tax_option_id'])->inverseOf('taxOption')->cascadeOnDelete();
+ }
+
+ public function beforeSave($insert)
+ {
+ if (parent::beforeSave($insert)) {
+
+ if (empty($this->parent_id))
+ $this->parent_id = 0;
+
+
+
+ return true;
+ }
+ return false;
+ }
+
+ public function getImageFile() {
+ return empty($this->image) ? null : Yii::getAlias('@imagesDir/tax_option/'. $this->image);
+ }
+
+ public function getImageUrl()
+ {
+ return empty($this->image) ? null : Yii::getAlias('@imagesUrl/tax_option/' . $this->image);
+ }
+}
diff --git a/common/modules/rubrication/models/TaxOptionQuery.php b/common/modules/rubrication/models/TaxOptionQuery.php
new file mode 100755
index 0000000..323c24c
--- /dev/null
+++ b/common/modules/rubrication/models/TaxOptionQuery.php
@@ -0,0 +1,39 @@
+andWhere('[[status]]=1');
+ }*/
+
+ /**
+ * @inheritdoc
+ * @return TaxOption[]|array
+ */
+ public function all($db = null)
+ {
+
+ return parent::all($db);
+ }
+
+ /**
+ * @inheritdoc
+ * @return TaxOption|array|null
+ */
+ public function one($db = null)
+ {
+
+ return parent::one($db);
+ }
+}
diff --git a/common/modules/rubrication/models/TaxOptionRelation.php b/common/modules/rubrication/models/TaxOptionRelation.php
new file mode 100755
index 0000000..c5390be
--- /dev/null
+++ b/common/modules/rubrication/models/TaxOptionRelation.php
@@ -0,0 +1,50 @@
+ 50]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'tax_option1_id' => Yii::t('product', 'Tax Option1 ID'),
+ 'tax_option2_id' => Yii::t('product', 'Tax Option2 ID'),
+ 'alias' => Yii::t('product', 'Alias'),
+ 'sort' => Yii::t('product', 'Sort'),
+ ];
+ }
+}
diff --git a/common/modules/rubrication/models/TaxOptionSearch.php b/common/modules/rubrication/models/TaxOptionSearch.php
new file mode 100755
index 0000000..9cdddb4
--- /dev/null
+++ b/common/modules/rubrication/models/TaxOptionSearch.php
@@ -0,0 +1,76 @@
+tax_group_id = intval($params['group']);
+ unset($params['group']);
+ }
+
+ $dataProvider = new ActiveDataProvider([
+ 'query' => $query,
+ ]);
+
+ $this->load($params);
+
+// if (!$this->validate()) {
+// return $dataProvider;
+// }
+
+ // grid filtering conditions
+ $query->andFilterWhere([
+ 'tax_option_id' => $this->tax_option_id,
+ 'tax_group_id' => $this->tax_group_id,
+ 'parent_id' => $this->parent_id,
+ 'sort' => $this->sort,
+ ]);
+
+ $query->andFilterWhere(['like', 'alias', $this->alias]);
+ $query->andFilterWhere(['like', 'tax_value_string.value', $this->default_value]);
+
+ $query->orderBy(['path_int' => SORT_ASC, 'depth' => SORT_ASC, 'sort' => SORT_ASC]);
+
+ return $dataProvider;
+ }
+}
diff --git a/common/modules/rubrication/models/TaxValueFloat.php b/common/modules/rubrication/models/TaxValueFloat.php
new file mode 100755
index 0000000..a8d76e6
--- /dev/null
+++ b/common/modules/rubrication/models/TaxValueFloat.php
@@ -0,0 +1,58 @@
+ true, 'targetClass' => TaxOption::className(), 'targetAttribute' => ['tax_option_id' => 'tax_option_id']],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'tax_value_id' => Yii::t('app', 'Tax Value ID'),
+ 'tax_option_id' => Yii::t('app', 'Tax Option ID'),
+ 'value' => Yii::t('app', 'Value'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxOption()
+ {
+ return $this->hasOne(TaxOption::className(), ['tax_option_id' => 'tax_option_id'])->inverseOf('taxValues');
+ }
+}
diff --git a/common/modules/rubrication/models/TaxValueInt.php b/common/modules/rubrication/models/TaxValueInt.php
new file mode 100755
index 0000000..89406f2
--- /dev/null
+++ b/common/modules/rubrication/models/TaxValueInt.php
@@ -0,0 +1,57 @@
+ true, 'targetClass' => TaxOption::className(), 'targetAttribute' => ['tax_option_id' => 'tax_option_id']],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'tax_value_id' => Yii::t('app', 'Tax Value ID'),
+ 'tax_option_id' => Yii::t('app', 'Tax Option ID'),
+ 'value' => Yii::t('app', 'Value'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxOption()
+ {
+ return $this->hasOne(TaxOption::className(), ['tax_option_id' => 'tax_option_id'])->inverseOf('taxValues');
+ }
+}
diff --git a/common/modules/rubrication/models/TaxValueLink.php b/common/modules/rubrication/models/TaxValueLink.php
new file mode 100755
index 0000000..c6c3d90
--- /dev/null
+++ b/common/modules/rubrication/models/TaxValueLink.php
@@ -0,0 +1,69 @@
+ 150],
+ [['link'], 'string', 'max' => 255],
+ [['tax_option_id'], 'exist', 'skipOnError' => true, 'targetClass' => TaxOption::className(), 'targetAttribute' => ['tax_option_id' => 'tax_option_id']],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'tax_value_id' => Yii::t('app', 'Tax Value ID'),
+ 'tax_option_id' => Yii::t('app', 'Tax Option ID'),
+ 'name' => Yii::t('app', 'Name'),
+ 'link' => Yii::t('app', 'Link'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxOption()
+ {
+ return $this->hasOne(TaxOption::className(), ['tax_option_id' => 'tax_option_id'])->inverseOf('taxValueLinks');
+ }
+
+ public static function getValueRenderFlash($value) {
+ return $value->name;
+ }
+
+ public static function getValueRenderHTML($value) {
+ return Html::a($value->name, $value->link);
+ }
+}
diff --git a/common/modules/rubrication/models/TaxValueString.php b/common/modules/rubrication/models/TaxValueString.php
new file mode 100755
index 0000000..881110b
--- /dev/null
+++ b/common/modules/rubrication/models/TaxValueString.php
@@ -0,0 +1,58 @@
+ 255],
+ [['tax_option_id'], 'exist', 'skipOnError' => true, 'targetClass' => TaxOption::className(), 'targetAttribute' => ['tax_option_id' => 'tax_option_id']],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'tax_value_id' => Yii::t('rubrication', 'Tax Value ID'),
+ 'tax_option_id' => Yii::t('rubrication', 'Tax Option ID'),
+ 'value' => Yii::t('rubrication', 'Value'),
+ ];
+ }
+
+ /**
+ * @return \yii\db\ActiveQuery
+ */
+ public function getTaxOption()
+ {
+ return $this->hasOne(TaxOption::className(), ['tax_option_id' => 'tax_option_id']);
+ }
+}
diff --git a/common/modules/rubrication/views/default/index.php b/common/modules/rubrication/views/default/index.php
new file mode 100755
index 0000000..d9e8d45
--- /dev/null
+++ b/common/modules/rubrication/views/default/index.php
@@ -0,0 +1,12 @@
+