diff --git a/application/backend/controllers/WarehouseController.php b/application/backend/controllers/WarehouseController.php
new file mode 100644
index 000000000..6b3cd85fc
--- /dev/null
+++ b/application/backend/controllers/WarehouseController.php
@@ -0,0 +1,131 @@
+ [
+ 'class' => AccessControl::className(),
+ 'rules' => [
+ [
+ 'allow' => true,
+ 'roles' => ['product manage'],
+ ],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function actions()
+ {
+ return [
+ 'remove-all' => [
+ 'class' => MultipleDelete::className(),
+ 'modelName' => Warehouse::className(),
+ ],
+ 'delete' => [
+ 'class' => DeleteOne::className(),
+ 'modelName' => Warehouse::className(),
+ ],
+ 'update-editable' => [
+ 'class' => UpdateEditable::className(),
+ 'modelName' => Warehouse::className(),
+ 'allowedAttributes' => [
+ ],
+ ],
+ ];
+ }
+
+ public function actionIndex()
+ {
+ $searchModel = new Warehouse();
+ $dataProvider = $searchModel->search($_GET);
+
+ return $this->render(
+ 'index',
+ [
+ 'dataProvider' => $dataProvider,
+ 'searchModel' => $searchModel,
+ ]
+ );
+ }
+
+ public function actionEdit($id = null)
+ {
+ $model = new Warehouse;
+ $model->loadDefaultValues();
+
+ if ($id !== null) {
+ $model = Warehouse::findOne($id);
+ }
+
+
+
+ $post = \Yii::$app->request->post();
+
+ if ($model->load($post) && $model->validate()) {
+
+ $save_result = $model->save();
+ if ($save_result) {
+ Yii::$app->session->setFlash('info', Yii::t('app', 'Object saved'));
+ return $this->redirect(['/backend/warehouse/edit', 'id' => $model->id]);
+ } else {
+ \Yii::$app->session->setFlash('error', Yii::t('app', 'Cannot update data'));
+ }
+
+
+ }
+
+ return $this->render(
+ 'form',
+ [
+ 'model' => $model,
+ ]
+ );
+ }
+
+ public function actionUpdateRemains()
+ {
+ $post = Yii::$app->request->post('remain', null);
+ if (isset($post)) {
+
+ Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
+ $remainId = current(array_keys($post));
+ $model = WarehouseProduct::findOne($remainId);
+ if ($model === null) {
+ throw new NotFoundHttpException;
+ }
+
+ $model->setAttributes(current($post));
+ TagDependency::invalidate(Yii::$app->cache, ActiveRecordHelper::getObjectTag(\app\models\Product::className(), $model->product_id));
+ return $model->save();
+
+
+ } else {
+ throw new HttpException(400);
+ }
+ }
+
+}
diff --git a/application/backend/views/product/product-form.php b/application/backend/views/product/product-form.php
index 2b52ce6b6..0f8cab105 100644
--- a/application/backend/views/product/product-form.php
+++ b/application/backend/views/product/product-form.php
@@ -245,6 +245,22 @@
BackendWidget::end();
?>
+ Yii::t('app', 'Warehouse'),
+ 'icon'=>'archive',
+ 'footer'=>$this->blocks['submit']
+ ]
+ ); ?>
+
+ = $form->field($model, 'sku') ?>
+ = $form->field($model, 'unlimited_count')->widget(\kartik\switchinput\SwitchInput::className())?>
+ = \app\backend\widgets\WarehousesRemains::widget([
+ 'model' => $model,
+ ]) ?>
+
+
- Yii::t('app', 'Warehouse'),
- 'icon'=>'archive',
- 'footer'=>$this->blocks['submit']
- ]
- ); ?>
-
- = $form->field($model, 'sku') ?>
- = $form->field($model, 'in_warehouse')?>
- = $form->field($model, 'unlimited_count')->widget(\kartik\switchinput\SwitchInput::className())?>
- = $form->field($model, 'reserved_count')?>
-
parent_id == 0) : ?>
diff --git a/application/backend/widgets/WarehousesRemains.php b/application/backend/widgets/WarehousesRemains.php
new file mode 100644
index 000000000..ed025b263
--- /dev/null
+++ b/application/backend/widgets/WarehousesRemains.php
@@ -0,0 +1,67 @@
+model === null) {
+ throw new InvalidConfigException("Model should be set for WarehousesRemains widget");
+ }
+
+ $state = $this->model->getWarehousesState();
+
+ $activeWarehousesIds = app\models\Warehouse::activeWarehousesIds();
+ $remains = [];
+ foreach ($state as $remain) {
+ $remains[$remain->warehouse_id] = $remain;
+ if(($key = array_search($remain->warehouse_id, $activeWarehousesIds)) !== false) {
+ unset($activeWarehousesIds[$key]);
+ }
+ }
+
+ // if we have new warehouses that not represented in warehouses state
+ if (count($activeWarehousesIds) > 0) {
+ foreach ($activeWarehousesIds as $id) {
+ // create new record with default values
+ $remain = new app\models\WarehouseProduct;
+ $remain->warehouse_id = $id;
+ $remain->product_id = $this->model->id;
+ $remain->save();
+
+ // add to remains
+ $remains[$remain->warehouse_id] = $remain;
+ }
+ TagDependency::invalidate(Yii::$app->cache, ActiveRecordHelper::getObjectTag($this->model->className(), $this->model->id));
+ }
+
+ return $this->render(
+ 'warehouses-remains',
+ [
+ 'model' => $this->model,
+ 'remains' => $remains,
+ ]
+ );
+ }
+}
\ No newline at end of file
diff --git a/application/backend/widgets/views/warehouses-remains.php b/application/backend/widgets/views/warehouses-remains.php
new file mode 100644
index 000000000..c4d1f9931
--- /dev/null
+++ b/application/backend/widgets/views/warehouses-remains.php
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+ = Yii::t('app', 'Warehouse') ?>
+ |
+
+ = Yii::t('app', 'In warehouse') ?>
+ |
+
+ = Yii::t('app', 'Reserved count') ?>
+ |
+
+ = Yii::t('app', 'SKU') ?>
+ |
+
+
+
+ $remain): ?>
+
+
+ = $remain->warehouse->name ?>
+ |
+
+ =
+ Html::textInput(
+ 'remain[' . $remain->id . '][in_warehouse]',
+ $remain->in_warehouse,
+ [
+ 'class' => 'warehouse-remain-input form-control',
+ 'placeholder' => Yii::t('app', 'In warehouse'),
+ ]
+ ) ?>
+ |
+
+ =
+ Html::textInput(
+ 'remain[' . $remain->id . '][reserved_count]',
+ $remain->reserved_count,
+ [
+ 'class' => 'warehouse-remain-input form-control',
+ 'placeholder' => Yii::t('app', 'Reserved count'),
+ ]
+ ) ?>
+ |
+
+ =
+ Html::textInput(
+ 'remain[' . $remain->id . '][sku]',
+ $remain->sku,
+ [
+ 'class' => 'warehouse-remain-input form-control',
+ 'placeholder' => Yii::t('app', 'SKU'),
+ ]
+ ) ?>
+ |
+
+
+
+
+
+
\ No newline at end of file
diff --git a/application/messages/ru/app.php b/application/messages/ru/app.php
index 1719cbdc9..75c8eaf8d 100755
--- a/application/messages/ru/app.php
+++ b/application/messages/ru/app.php
@@ -221,6 +221,8 @@
'Is active' => 'Активен',
'Item {value} is not assigned:' => '',
'Items in warehouse' => 'На складе',
+ 'In warehouse' => 'На складе',
+ 'Reserved count' => 'Зарезерировано',
'Items inside item: ' => 'Элементы внутри элемента: ',
'Items reserved' => 'Зарезервировано',
'ISO-4217 code' => 'ISO-4217 код',
diff --git a/application/migrations/m150228_090542_multiple_warehouses.php b/application/migrations/m150228_090542_multiple_warehouses.php
new file mode 100644
index 000000000..73b594a3b
--- /dev/null
+++ b/application/migrations/m150228_090542_multiple_warehouses.php
@@ -0,0 +1,306 @@
+createTable(
+ '{{%warehouse}}',
+ [
+ 'id' => Schema::TYPE_PK,
+ 'name' => Schema::TYPE_STRING,
+ 'is_active' => Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 1',
+ 'country_id' => Schema::TYPE_INTEGER . ' NOT NULL',
+ 'city_id' => Schema::TYPE_INTEGER . ' NOT NULL',
+ 'address' => Schema::TYPE_TEXT,
+ 'description' => Schema::TYPE_TEXT,
+ 'sort_order' => Schema::TYPE_INTEGER . ' NOT NULL DEFAULT 0',
+ 'map_latitude' => Schema::TYPE_STRING . ' NOT NULL DEFAULT \'\'',
+ 'map_longitude' => Schema::TYPE_STRING . ' NOT NULL DEFAULT \'\'',
+ 'shipping_center' => Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 1',
+ 'issuing_center' => Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 1',
+ 'xml_id' => Schema::TYPE_STRING . ' NOT NULL DEFAULT \'\'',
+ ],
+ $tableOptions
+ );
+
+ $this->createTable(
+ '{{%warehouse_phone}}',
+ [
+ 'id' => Schema::TYPE_PK,
+ 'warehouse_id' => Schema::TYPE_INTEGER . ' NOT NULL',
+ 'sort_order' => Schema::TYPE_INTEGER . ' NOT NULL DEFAULT 0',
+ 'phone' => Schema::TYPE_STRING . ' NOT NULL',
+ 'name' => Schema::TYPE_STRING,
+ ],
+ $tableOptions
+ );
+
+ $this->createTable(
+ '{{%warehouse_email}}',
+ [
+ 'id' => Schema::TYPE_PK,
+ 'warehouse_id' => Schema::TYPE_INTEGER . ' NOT NULL',
+ 'sort_order' => Schema::TYPE_INTEGER . ' NOT NULL DEFAULT 0',
+ 'email' => Schema::TYPE_STRING . ' NOT NULL',
+ 'name' => Schema::TYPE_STRING,
+ ],
+ $tableOptions
+ );
+
+ $this->createTable(
+ '{{%warehouse_openinghours}}',
+ [
+ 'id' => Schema::TYPE_PK,
+ 'warehouse_id' => Schema::TYPE_INTEGER . ' NOT NULL',
+ 'sort_order' => Schema::TYPE_INTEGER . ' NOT NULL DEFAULT 0',
+ 'monday' => Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 0',
+ 'tuesday' => Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 0',
+ 'wednesday' => Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 0',
+ 'thursday' => Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 0',
+ 'friday' => Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 0',
+ 'saturday' => Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 0',
+ 'sunday' => Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 0',
+ 'all_day' => Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 0', // 24h
+ 'opens' => Schema::TYPE_STRING,
+ 'closes' => Schema::TYPE_STRING,
+ 'break_from' => Schema::TYPE_STRING,
+ 'break_to' => Schema::TYPE_STRING,
+
+ ],
+ $tableOptions
+ );
+
+ $this->createTable(
+ '{{%country}}',
+ [
+ 'id' => Schema::TYPE_PK,
+ 'name' => Schema::TYPE_STRING,
+ 'iso_code' => Schema::TYPE_STRING, // ISO 3166-1 alpha-3
+ 'sort_order' => Schema::TYPE_INTEGER . ' NOT NULL DEFAULT 0',
+ 'slug' => Schema::TYPE_STRING,
+ ],
+ $tableOptions
+ );
+
+ $this->insert(
+ '{{%country}}',
+ [
+ 'name' => 'Россия',
+ 'iso_code' => 'RUS',
+ 'sort_order' => 0,
+ 'slug' => 'rossiya',
+ ]
+ );
+
+ $this->insert(
+ '{{%country}}',
+ [
+ 'name' => 'USA',
+ 'iso_code' => 'USA',
+ 'sort_order' => 1,
+ 'slug' => 'usa',
+ ]
+ );
+
+ $this->createTable(
+ '{{%city}}',
+ [
+ 'id' => Schema::TYPE_PK,
+ 'name' => Schema::TYPE_STRING,
+ 'sort_order' => Schema::TYPE_INTEGER . ' NOT NULL DEFAULT 0',
+ 'slug' => Schema::TYPE_STRING,
+ 'country_id' => Schema::TYPE_INTEGER . ' NOT NULL',
+ ],
+ $tableOptions
+ );
+
+ $this->insert(
+ '{{%city}}',
+ [
+ 'name' => 'Москва',
+ 'slug' => 'moscow',
+ 'country_id' => 1,
+ ]
+ );
+ $this->insert(
+ '{{%city}}',
+ [
+ 'name' => 'Санкт-Петербург',
+ 'slug' => 'spb',
+ 'country_id' => 1,
+ ]
+ );
+ $this->insert(
+ '{{%city}}',
+ [
+ 'name' => 'New York',
+ 'slug' => 'ny',
+ 'country_id' => 2,
+ ]
+ );
+
+ $this->createIndex('city_country', '{{%city}}', 'country_id');
+ $this->createIndex('wh_country', '{{%warehouse}}', 'country_id');
+ $this->createIndex('wh_city', '{{%warehouse}}', 'city_id');
+ $this->createIndex('wh_phone', '{{%warehouse_phone}}', 'warehouse_id');
+ $this->createIndex('wh_email', '{{%warehouse_email}}', 'warehouse_id');
+ $this->createIndex('wh_hours', '{{%warehouse_openinghours}}', 'warehouse_id');
+
+ $this->insert(
+ '{{%warehouse}}',
+ [
+ 'name' => 'Main warehouse',
+ 'country_id' => 1,
+ 'city_id' => 1,
+ 'address' => 'Kremlin',
+ ]
+ );
+
+ $this->insert(
+ '{{%warehouse_phone}}',
+ [
+ 'name' => 'Sales',
+ 'warehouse_id' => 1,
+ 'phone' => '+7 (495) 123-45-67',
+ ]
+ );
+
+ $this->insert(
+ '{{%warehouse_email}}',
+ [
+ 'name' => 'Sales',
+ 'warehouse_id' => 1,
+ 'email' => 'moscow@example.com',
+ ]
+ );
+
+ $this->insert(
+ '{{%warehouse_openinghours}}',
+ [
+ 'warehouse_id' => 1,
+ 'monday' => 1,
+ 'tuesday' => 1,
+ 'wednesday' => 1,
+ 'thursday' => 1,
+ 'friday' => 1,
+ 'saturday' => 1,
+ 'sunday' => 1,
+ 'all_day' => 1,
+ 'opens' => '',
+ 'closes' => '',
+ 'break_from' => '12:00',
+ 'break_to' => '13:00',
+ ]
+ );
+
+ $this->insert(
+ '{{%warehouse}}',
+ [
+ 'name' => 'Second warehouse',
+ 'country_id' => 2,
+ 'city_id' => 3,
+ 'address' => 'The WallStreet hidden warehouse',
+ ]
+ );
+
+ $this->insert(
+ '{{%warehouse_phone}}',
+ [
+ 'name' => 'Sales',
+ 'warehouse_id' => 2,
+ 'phone' => '+1 800 1-WAREHOUSE-1',
+ ]
+ );
+
+ $this->insert(
+ '{{%warehouse_email}}',
+ [
+ 'name' => 'Sales',
+ 'warehouse_id' => 2,
+ 'email' => 'nyc@example.com',
+ ]
+ );
+
+ $this->insert(
+ '{{%warehouse_openinghours}}',
+ [
+ 'warehouse_id' => 2,
+ 'monday' => 1,
+ 'tuesday' => 1,
+ 'wednesday' => 1,
+ 'thursday' => 1,
+ 'friday' => 0,
+ 'saturday' => 0,
+ 'sunday' => 1,
+ 'all_day' => 0,
+ 'opens' => '9:00',
+ 'closes' => '22:00',
+ 'break_from' => '',
+ 'break_to' => '',
+ ]
+ );
+
+ // product bindings
+
+ $this->createTable(
+ '{{%warehouse_product}}',
+ [
+ 'id' => Schema::TYPE_PK,
+ 'warehouse_id' => Schema::TYPE_INTEGER . ' NOT NULL',
+ 'product_id' => Schema::TYPE_INTEGER . ' NOT NULL',
+ 'in_warehouse' => Schema::TYPE_FLOAT . ' NOT NULL DEFAULT 0',
+ 'reserved_count' => Schema::TYPE_FLOAT . ' NOT NULL DEFAULT 0',
+ 'sku' => Schema::TYPE_STRING . ' NOT NULL DEFAULT \'\'',
+ ],
+ $tableOptions
+ );
+
+
+ // migrate old data to new data
+ $this->execute(
+ 'INSERT INTO {{%warehouse_product}} (warehouse_id, product_id, in_warehouse, reserved_count)
+ SELECT 1, p.id, p.in_warehouse, p.reserved_count FROM {{%product}} p'
+ );
+
+ $this->createIndex('wh_pr', '{{%warehouse_product}}', ['warehouse_id', 'product_id'], true);
+
+ $this->dropColumn(
+ '{{%product}}',
+ 'in_warehouse'
+ );
+
+ $this->dropColumn(
+ '{{%product}}',
+ 'reserved_count'
+ );
+ }
+
+ public function down()
+ {
+ $this->dropTable('{{%warehouse}}');
+ $this->dropTable('{{%warehouse_openinghours}}');
+ $this->dropTable('{{%warehouse_email}}');
+ $this->dropTable('{{%warehouse_phone}}');
+ $this->dropTable('{{%country}}');
+ $this->dropTable('{{%city}}');
+
+ $this->addColumn('{{%product}}', 'in_warehouse', Schema::TYPE_FLOAT . ' NOT NULL DEFAULT 0');
+ $this->addColumn('{{%product}}', 'reserved_count', Schema::TYPE_FLOAT . ' NOT NULL DEFAULT 0');
+
+ $this->execute(
+ 'UPDATE {{%product}} p SET p.in_warehouse = (SELECT wp.in_warehouse FROM {{%warehouse_product}} wp WHERE wp.product_id = p.id)'
+ );
+ $this->execute(
+ 'UPDATE {{%product}} p SET p.in_warehouse = (SELECT wp.reserved_count FROM {{%warehouse_product}} wp WHERE wp.product_id = p.id)'
+ );
+ $this->dropTable('{{%warehouse_product}}');
+
+
+ }
+}
diff --git a/application/models/City.php b/application/models/City.php
new file mode 100644
index 000000000..34df28d40
--- /dev/null
+++ b/application/models/City.php
@@ -0,0 +1,51 @@
+ 255]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'id' => Yii::t('app', 'ID'),
+ 'name' => Yii::t('app', 'Name'),
+ 'sort_order' => Yii::t('app', 'Sort Order'),
+ 'slug' => Yii::t('app', 'Slug'),
+ 'country_id' => Yii::t('app', 'Country ID'),
+ ];
+ }
+}
diff --git a/application/models/Country.php b/application/models/Country.php
new file mode 100644
index 000000000..7a2bde069
--- /dev/null
+++ b/application/models/Country.php
@@ -0,0 +1,50 @@
+ 255]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'id' => Yii::t('app', 'ID'),
+ 'name' => Yii::t('app', 'Name'),
+ 'iso_code' => Yii::t('app', 'Iso Code'),
+ 'sort_order' => Yii::t('app', 'Sort Order'),
+ 'slug' => Yii::t('app', 'Slug'),
+ ];
+ }
+}
diff --git a/application/models/Product.php b/application/models/Product.php
index e9eece1ad..4412708fb 100644
--- a/application/models/Product.php
+++ b/application/models/Product.php
@@ -41,6 +41,8 @@
* @property integer $parent_id
* @property integer $currency_id
* @property Product[] $relatedProducts
+ * @property string $sku
+ * @property boolean unlimited_count
*/
class Product extends ActiveRecord implements ImportableInterface, ExportableInterface
{
@@ -50,6 +52,11 @@ class Product extends ActiveRecord implements ImportableInterface, ExportableInt
public $relatedProductsArray = [];
+ /**
+ * @var null|WarehouseProduct[] Stores warehouses state of product. Use Product::getWarehousesState() to retrieve
+ */
+ private $activeWarehousesState = null;
+
/**
* @inheritdoc
*/
@@ -70,12 +77,8 @@ public function rules()
'main_category_id',
'slug_absolute',
'sort_order',
- 'active',
'parent_id',
'is_deleted',
- 'in_warehouse',
- 'unlimited_count',
- 'reserved_count',
'currency_id',
],
'integer'
@@ -94,11 +97,20 @@ public function rules()
],
'string'
],
+ [
+ [
+ 'unlimited_count',
+ 'active',
+ 'slug_absolute',
+ ],
+ 'boolean',
+ ],
[['price', 'old_price'], 'number'],
[['slug'], 'string', 'max' => 80],
[['slug_compiled'], 'string', 'max' => 180],
- [['old_price', 'price', 'in_warehouse', 'reserved_count'], 'default', 'value' => 0,],
- [['active', 'unlimited_count'], 'default', 'value' => 1],
+ [['old_price', 'price',], 'default', 'value' => 0,],
+ [['active','unlimited_count'], 'default', 'value' => true],
+ [['slug_absolute'], 'default', 'value' => false],
[['parent_id', 'slug_absolute', 'sort_order', 'is_deleted'], 'default', 'value' => 0],
[['relatedProductsArray'], 'safe'],
@@ -294,6 +306,34 @@ public function getRelatedProducts()
->viaTable(RelatedProduct::tableName(), ['product_id' => 'id']);
}
+ /**
+ * Returns remains of this product in all active warehouses.
+ * Note that if warehouse was added after product edit - it will not be shown here.
+ * @return WarehouseProduct[]
+ */
+ public function getWarehousesState()
+ {
+ if ($this->activeWarehousesState === null) {
+ $this->activeWarehousesState = WarehouseProduct::getDb()->cache(
+ function($db) {
+ return WarehouseProduct::find()
+ ->where(['in', 'warehouse_id', Warehouse::activeWarehousesIds()])
+ ->andWhere('product_id=:product_id', [':product_id'=>$this->id])
+ ->with('warehouse')
+ ->all();
+ },
+ 86400,
+ new TagDependency([
+ 'tags' => [
+ ActiveRecordHelper::getObjectTag($this->className(), $this->id),
+ ]
+ ])
+ );
+ }
+
+ return $this->activeWarehousesState;
+ }
+
public function beforeSave($insert)
{
if (1 === $this->is_deleted) {
diff --git a/application/models/Warehouse.php b/application/models/Warehouse.php
new file mode 100644
index 000000000..f7f86e223
--- /dev/null
+++ b/application/models/Warehouse.php
@@ -0,0 +1,112 @@
+ 255]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'id' => Yii::t('app', 'ID'),
+ 'name' => Yii::t('app', 'Name'),
+ 'is_active' => Yii::t('app', 'Is Active'),
+ 'country_id' => Yii::t('app', 'Country ID'),
+ 'city_id' => Yii::t('app', 'City ID'),
+ 'address' => Yii::t('app', 'Address'),
+ 'description' => Yii::t('app', 'Description'),
+ 'sort_order' => Yii::t('app', 'Sort Order'),
+ 'map_latitude' => Yii::t('app', 'Map Latitude'),
+ 'map_longitude' => Yii::t('app', 'Map Longitude'),
+ 'shipping_center' => Yii::t('app', 'Shipping Center'),
+ 'issuing_center' => Yii::t('app', 'Issuing Center'),
+ 'xml_id' => Yii::t('app', 'Xml ID'),
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function behaviors()
+ {
+ return [
+ [
+ 'class' => \devgroup\TagDependencyHelper\ActiveRecordHelper::className(),
+ ],
+ ];
+ }
+
+
+ /**
+ * Returns array of ID of all active warehouses
+ * @return integer[]
+ * @throws \Exception
+ */
+ public static function activeWarehousesIds()
+ {
+ if (static::$activeWarehousesIds === null) {
+ static::$activeWarehousesIds = Warehouse::getDb()->cache(
+ function($db) {
+ return Warehouse::find()
+ ->where('is_active=1')
+ ->select('id')
+ ->orderBy('sort_order ASC')
+ ->asArray()
+ ->column($db);
+ },
+ 86400,
+ new TagDependency([
+ 'tags' => [
+ ActiveRecordHelper::getCommonTag(Warehouse::className())
+ ]
+ ])
+ );
+ }
+ return static::$activeWarehousesIds;
+ }
+}
diff --git a/application/models/WarehouseEmail.php b/application/models/WarehouseEmail.php
new file mode 100644
index 000000000..7840e598a
--- /dev/null
+++ b/application/models/WarehouseEmail.php
@@ -0,0 +1,51 @@
+ 255]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'id' => Yii::t('app', 'ID'),
+ 'warehouse_id' => Yii::t('app', 'Warehouse ID'),
+ 'sort_order' => Yii::t('app', 'Sort Order'),
+ 'email' => Yii::t('app', 'Email'),
+ 'name' => Yii::t('app', 'Name'),
+ ];
+ }
+}
diff --git a/application/models/WarehouseOpeninghours.php b/application/models/WarehouseOpeninghours.php
new file mode 100644
index 000000000..5ffccd587
--- /dev/null
+++ b/application/models/WarehouseOpeninghours.php
@@ -0,0 +1,71 @@
+ 255]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'id' => Yii::t('app', 'ID'),
+ 'warehouse_id' => Yii::t('app', 'Warehouse ID'),
+ 'sort_order' => Yii::t('app', 'Sort Order'),
+ 'monday' => Yii::t('app', 'Monday'),
+ 'tuesday' => Yii::t('app', 'Tuesday'),
+ 'wednesday' => Yii::t('app', 'Wednesday'),
+ 'thursday' => Yii::t('app', 'Thursday'),
+ 'friday' => Yii::t('app', 'Friday'),
+ 'saturday' => Yii::t('app', 'Saturday'),
+ 'sunday' => Yii::t('app', 'Sunday'),
+ 'all_day' => Yii::t('app', 'All Day'),
+ 'opens' => Yii::t('app', 'Opens'),
+ 'closes' => Yii::t('app', 'Closes'),
+ 'break_from' => Yii::t('app', 'Break From'),
+ 'break_to' => Yii::t('app', 'Break To'),
+ ];
+ }
+}
diff --git a/application/models/WarehousePhone.php b/application/models/WarehousePhone.php
new file mode 100644
index 000000000..5246014b6
--- /dev/null
+++ b/application/models/WarehousePhone.php
@@ -0,0 +1,51 @@
+ 255]
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'id' => Yii::t('app', 'ID'),
+ 'warehouse_id' => Yii::t('app', 'Warehouse ID'),
+ 'sort_order' => Yii::t('app', 'Sort Order'),
+ 'phone' => Yii::t('app', 'Phone'),
+ 'name' => Yii::t('app', 'Name'),
+ ];
+ }
+}
diff --git a/application/models/WarehouseProduct.php b/application/models/WarehouseProduct.php
new file mode 100644
index 000000000..785a6115d
--- /dev/null
+++ b/application/models/WarehouseProduct.php
@@ -0,0 +1,78 @@
+ 255],
+ [['in_warehouse', 'reserved_count'], 'default', 'value' => 0],
+ [['warehouse_id', 'product_id'], 'unique', 'targetAttribute' => ['warehouse_id', 'product_id'], 'message' => 'The combination of Warehouse ID and Product ID has already been taken.']
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function attributeLabels()
+ {
+ return [
+ 'id' => Yii::t('app', 'ID'),
+ 'warehouse_id' => Yii::t('app', 'Warehouse ID'),
+ 'product_id' => Yii::t('app', 'Product ID'),
+ 'in_warehouse' => Yii::t('app', 'In Warehouse'),
+ 'reserved_count' => Yii::t('app', 'Reserved Count'),
+ 'sku' => Yii::t('app', 'Sku'),
+ ];
+ }
+
+ /**
+ * Relation to Warehouse
+ * @return \yii\db\ActiveQuery
+ */
+ public function getWarehouse()
+ {
+ return $this->hasOne(Warehouse::className(), ['id' => 'warehouse_id']);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function behaviors()
+ {
+ return [
+ [
+ 'class' => \devgroup\TagDependencyHelper\ActiveRecordHelper::className(),
+ ],
+ ];
+ }
+}