Commit fd40a9e1a50870b86e374e87e579162b8d4d7097

Authored by Yarik
1 parent 2a050410

Products to order

assets/OrderAsset.php
... ... @@ -25,4 +25,8 @@
25 25 public $js = [
26 26 'js/order.js',
27 27 ];
  28 +
  29 + public $depends = [
  30 + 'yii\widgets\PjaxAsset',
  31 + ];
28 32 }
... ...
controllers/OrderController.php
1 1 <?php
2 2  
3 3 namespace artbox\order\controllers;
4   -
  4 +
  5 + use artbox\catalog\models\Product;
  6 + use artbox\catalog\models\Variant;
5 7 use artbox\order\models\Delivery;
6 8 use artbox\order\models\Label;
  9 + use artbox\order\models\OrderProduct;
7 10 use artbox\order\models\Payment;
8 11 use Yii;
9 12 use artbox\order\models\Order;
10 13 use artbox\order\models\OrderSearch;
  14 + use yii\base\InvalidParamException;
11 15 use yii\base\Model;
12 16 use yii\web\Controller;
13 17 use yii\web\NotFoundHttpException;
14 18 use yii\filters\VerbFilter;
15   -
  19 +
16 20 /**
17 21 * OrderController implements the CRUD actions for Order model.
18 22 */
... ... @@ -32,7 +36,7 @@
32 36 ],
33 37 ];
34 38 }
35   -
  39 +
36 40 /**
37 41 * Lists all Order models.
38 42 *
... ... @@ -43,21 +47,21 @@
43 47 $searchModel = new OrderSearch();
44 48 $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
45 49 $labels = Label::find()
46   - ->select(
47   - [
48   - 'title',
49   - 'id',
50   - ]
51   - )
52   - ->joinWith('lang')
53   - ->andWhere(
54   - [
55   - 'status' => true,
56   - ]
57   - )
58   - ->indexBy('id')
59   - ->column();
60   -
  50 + ->select(
  51 + [
  52 + 'title',
  53 + 'id',
  54 + ]
  55 + )
  56 + ->joinWith('lang')
  57 + ->andWhere(
  58 + [
  59 + 'status' => true,
  60 + ]
  61 + )
  62 + ->indexBy('id')
  63 + ->column();
  64 +
61 65 return $this->render(
62 66 '@artbox/order/views/order/index',
63 67 [
... ... @@ -67,7 +71,7 @@
67 71 ]
68 72 );
69 73 }
70   -
  74 +
71 75 /**
72 76 * Displays a single Order model.
73 77 *
... ... @@ -84,7 +88,7 @@
84 88 ]
85 89 );
86 90 }
87   -
  91 +
88 92 /**
89 93 * Creates a new Order model.
90 94 * If creation is successful, the browser will be redirected to the 'view' page.
... ... @@ -94,7 +98,7 @@
94 98 public function actionCreate()
95 99 {
96 100 $model = new Order();
97   -
  101 +
98 102 if ($model->load(Yii::$app->request->post()) && $model->save()) {
99 103 return $this->redirect(
100 104 [
... ... @@ -104,50 +108,50 @@
104 108 );
105 109 } else {
106 110 $labels = Label::find()
107   - ->joinWith('lang')
108   - ->select(
109   - [
110   - 'title',
111   - 'id',
112   - ]
113   - )
114   - ->where(
115   - [
116   - 'status' => true,
117   - ]
118   - )
119   - ->indexBy('id')
120   - ->column();
  111 + ->joinWith('lang')
  112 + ->select(
  113 + [
  114 + 'title',
  115 + 'id',
  116 + ]
  117 + )
  118 + ->where(
  119 + [
  120 + 'status' => true,
  121 + ]
  122 + )
  123 + ->indexBy('id')
  124 + ->column();
121 125 $deliveries = Delivery::find()
122   - ->joinWith('lang')
123   - ->select(
124   - [
125   - 'title',
126   - 'id',
127   - ]
128   - )
129   - ->where(
130   - [
131   - 'status' => true,
132   - ]
133   - )
134   - ->indexBy('id')
135   - ->column();
  126 + ->joinWith('lang')
  127 + ->select(
  128 + [
  129 + 'title',
  130 + 'id',
  131 + ]
  132 + )
  133 + ->where(
  134 + [
  135 + 'status' => true,
  136 + ]
  137 + )
  138 + ->indexBy('id')
  139 + ->column();
136 140 $payments = Payment::find()
137   - ->joinWith('lang')
138   - ->select(
139   - [
140   - 'title',
141   - 'id',
142   - ]
143   - )
144   - ->where(
145   - [
146   - 'status' => true,
147   - ]
148   - )
149   - ->indexBy('id')
150   - ->column();
  141 + ->joinWith('lang')
  142 + ->select(
  143 + [
  144 + 'title',
  145 + 'id',
  146 + ]
  147 + )
  148 + ->where(
  149 + [
  150 + 'status' => true,
  151 + ]
  152 + )
  153 + ->indexBy('id')
  154 + ->column();
151 155 return $this->render(
152 156 '@artbox/order/views/order/create',
153 157 [
... ... @@ -159,7 +163,7 @@
159 163 );
160 164 }
161 165 }
162   -
  166 +
163 167 /**
164 168 * Updates an existing Order model.
165 169 * If update is successful, the browser will be redirected to the 'view' page.
... ... @@ -171,16 +175,9 @@
171 175 public function actionUpdate($id)
172 176 {
173 177 $model = $this->findModel($id);
174   -
  178 +
175 179 if ($model->load(Yii::$app->request->post()) && $model->save()) {
176   - if (Model::loadMultiple($model->orderProducts, \Yii::$app->request->post()) && Model::validateMultiple(
177   - $model->orderProducts
178   - )
179   - ) {
180   - foreach ($model->orderProducts as $orderProduct) {
181   - $orderProduct->save(false);
182   - }
183   - }
  180 + OrderProduct::saveItems(\Yii::$app->request->post('OrderProduct'), $id);
184 181 return $this->redirect(
185 182 [
186 183 'view',
... ... @@ -204,35 +201,35 @@
204 201 ->indexBy('id')
205 202 ->column();
206 203 $deliveries = Delivery::find()
207   - ->joinWith('lang')
208   - ->select(
209   - [
210   - 'title',
211   - 'id',
212   - ]
213   - )
214   - ->where(
215   - [
216   - 'status' => true,
217   - ]
218   - )
219   - ->indexBy('id')
220   - ->column();
  204 + ->joinWith('lang')
  205 + ->select(
  206 + [
  207 + 'title',
  208 + 'id',
  209 + ]
  210 + )
  211 + ->where(
  212 + [
  213 + 'status' => true,
  214 + ]
  215 + )
  216 + ->indexBy('id')
  217 + ->column();
221 218 $payments = Payment::find()
222   - ->joinWith('lang')
223   - ->select(
224   - [
225   - 'title',
226   - 'id',
227   - ]
228   - )
229   - ->where(
230   - [
231   - 'status' => true,
232   - ]
233   - )
234   - ->indexBy('id')
235   - ->column();
  219 + ->joinWith('lang')
  220 + ->select(
  221 + [
  222 + 'title',
  223 + 'id',
  224 + ]
  225 + )
  226 + ->where(
  227 + [
  228 + 'status' => true,
  229 + ]
  230 + )
  231 + ->indexBy('id')
  232 + ->column();
236 233 return $this->render(
237 234 '@artbox/order/views/order/update',
238 235 [
... ... @@ -244,7 +241,7 @@
244 241 );
245 242 }
246 243 }
247   -
  244 +
248 245 /**
249 246 * Deletes an existing Order model.
250 247 * If deletion is successful, the browser will be redirected to the 'index' page.
... ... @@ -257,10 +254,116 @@
257 254 {
258 255 $this->findModel($id)
259 256 ->delete();
260   -
  257 +
261 258 return $this->redirect([ 'index' ]);
262 259 }
263   -
  260 +
  261 + public function actionProductList($q = null, $id = null)
  262 + {
  263 + $response = \Yii::$app->response;
  264 + $response->format = $response::FORMAT_JSON;
  265 + $out = [
  266 + 'results' => [
  267 + 'id' => '',
  268 + 'text' => '',
  269 + ],
  270 + ];
  271 + if (!is_null($q)) {
  272 + $out[ 'results' ] = [];
  273 + /**
  274 + * @var Variant[] $variants
  275 + */
  276 + $variants = Variant::find()
  277 + ->joinWith('lang', false)
  278 + ->joinWith('product.lang', false)
  279 + ->andWhere(
  280 + [
  281 + 'like',
  282 + 'product_lang.title',
  283 + $q,
  284 + ]
  285 + )
  286 + ->orWhere(
  287 + [
  288 + 'like',
  289 + 'variant_lang.title',
  290 + $q,
  291 + ]
  292 + )
  293 + ->orWhere([ 'variant.sku' => $q ])
  294 + ->all();
  295 + foreach ($variants as $variant) {
  296 + $out[ 'results' ][] = [
  297 + 'id' => $variant->id,
  298 + 'text' => $variant->product->lang->title,
  299 + ];
  300 + }
  301 + } elseif ($id > 0) {
  302 + /**
  303 + * @var Variant $variant
  304 + */
  305 + $variant = Variant::find()
  306 + ->with('lang', 'product.lang')
  307 + ->where([ 'id' => $id ])
  308 + ->one();
  309 + $out[ 'results' ] = [
  310 + 'id' => $id,
  311 + 'text' => $variant->product->lang->title,
  312 + ];
  313 + }
  314 + return $out;
  315 + }
  316 +
  317 + public function actionAddToOrder()
  318 + {
  319 + $id = \Yii::$app->request->post('id');
  320 + $count = \Yii::$app->request->post('count');
  321 + $orderId = \Yii::$app->request->post('order');
  322 + if (empty($id) || empty($count)) {
  323 + throw new InvalidParamException(\Yii::t('order', 'Set id and count'));
  324 + }
  325 + $order = Order::find()
  326 + ->where([ 'id' => $orderId ])
  327 + ->one();
  328 + if (empty($order)) {
  329 + throw new NotFoundHttpException(\Yii::t('order', 'Order not found'));
  330 + }
  331 + /**
  332 + * @var Variant $variant
  333 + */
  334 + $variant = Variant::find()
  335 + ->where([ 'id' => $id ])
  336 + ->one();
  337 + if (empty($variant)) {
  338 + throw new NotFoundHttpException(\Yii::t('order', 'Variant not found'));
  339 + }
  340 + /**
  341 + * @var OrderProduct $model
  342 + */
  343 + $model = OrderProduct::find()
  344 + ->where(
  345 + [
  346 + 'order_id' => $orderId,
  347 + 'variant_id' => $id,
  348 + ]
  349 + )
  350 + ->one();
  351 + if ($model) {
  352 + $model->count += $count;
  353 + } else {
  354 + $model = new OrderProduct(
  355 + [
  356 + 'order_id' => $orderId,
  357 + 'variant_id' => $id,
  358 + 'sku' => $variant->sku,
  359 + 'price' => $variant->price,
  360 + 'count' => $count,
  361 + ]
  362 + );
  363 + }
  364 + $model->save();
  365 + }
  366 +
264 367 /**
265 368 * Finds the Order model based on its primary key value.
266 369 * If the model is not found, a 404 HTTP exception will be thrown.
... ...
models/OrderProduct.php
... ... @@ -4,6 +4,7 @@
4 4  
5 5 use artbox\catalog\models\Variant;
6 6 use Yii;
  7 + use yii\helpers\ArrayHelper;
7 8  
8 9 /**
9 10 * This is the model class for table "order_product".
... ... @@ -105,4 +106,74 @@
105 106 {
106 107 return $this->hasOne(Variant::className(), [ 'id' => 'variant_id' ]);
107 108 }
  109 +
  110 + /**
  111 + * @param array $items
  112 + * @param int $orderId
  113 + *
  114 + * @return \artbox\order\models\OrderProduct[]
  115 + */
  116 + public static function saveItems(array $items, int $orderId): array
  117 + {
  118 + $variantIds = ArrayHelper::getColumn($items, 'variant_id', false);
  119 + /**
  120 + * @var \artbox\order\models\OrderProduct[] $deletion
  121 + */
  122 + $deletion = self::find()
  123 + ->where(
  124 + [
  125 + 'not',
  126 + [ 'variant_id' => $variantIds ],
  127 + ]
  128 + )
  129 + ->andWhere([ 'order_id' => $orderId ])
  130 + ->all();
  131 + foreach ($deletion as $record) {
  132 + $record->delete();
  133 + }
  134 + /**
  135 + * @var \artbox\order\models\OrderProduct[] $orderProducts
  136 + */
  137 + $orderProducts = self::find()
  138 + ->where(
  139 + [
  140 + 'variant_id' => $variantIds,
  141 + 'order_id' => $orderId,
  142 + ]
  143 + )
  144 + ->all();
  145 + $newItems = [];
  146 + foreach ($items as $item) {
  147 + $id = $item[ 'variant_id' ];
  148 + $count = $item[ 'count' ];
  149 + foreach ($orderProducts as $orderProduct) {
  150 + if ($orderProduct->variant_id == $id) {
  151 + $orderProduct->count = $count;
  152 + break 2;
  153 + }
  154 + }
  155 + /**
  156 + * @var Variant $variant
  157 + */
  158 + $variant = Variant::find()
  159 + ->where([ 'id' => $id ])
  160 + ->one();
  161 + if ($variant) {
  162 + $newItems[] = new OrderProduct(
  163 + [
  164 + 'order_id' => $orderId,
  165 + 'variant_id' => $id,
  166 + 'sku' => $variant->sku,
  167 + 'price' => $variant->price,
  168 + 'count' => $count,
  169 + ]
  170 + );
  171 + }
  172 + }
  173 + $orderProducts = array_merge($orderProducts, $newItems);
  174 + foreach ($orderProducts as $orderProduct) {
  175 + $orderProduct->save();
  176 + }
  177 + return $orderProducts;
  178 + }
108 179 }
... ...
views/order/_form.php
1 1 <?php
2 2  
  3 + use kartik\select2\Select2;
3 4 use yii\bootstrap\Html;
  5 + use yii\helpers\Url;
  6 + use yii\web\JsExpression;
4 7 use yii\widgets\ActiveForm;
5 8  
6 9 /**
... ... @@ -54,72 +57,129 @@
54 57 if (!$model->isNewRecord) {
55 58 ?>
56 59 <div class="order-product-container">
57   - <div class="row strong">
58   - <div class="col-md-4">
  60 + <div id="order-product-pjax" style="position: relative;">
  61 + <div class="row strong">
  62 + <div class="col-md-4">
  63 + <?php
  64 + echo Html::tag('strong', \Yii::t('order', 'Product'));
  65 + ?>
  66 + </div>
  67 + <div class="col-md-4">
  68 + <?php
  69 + echo Html::tag('strong', \Yii::t('order', 'Price'));
  70 + ?>
  71 + </div>
  72 + <div class="col-md-4">
  73 + <?php
  74 + echo Html::tag('strong', \Yii::t('order', 'Count'));
  75 + ?>
  76 + </div>
  77 + </div>
  78 +
  79 + <?php
  80 + foreach ($model->orderProducts as $index => $orderProduct) {
  81 + ?>
  82 + <div class="row row-order-product">
  83 + <div class="col-md-4">
  84 + <?php
  85 + echo $form->field($orderProduct, "[$index]variant_id")
  86 + ->hiddenInput()
  87 + ->label(false);
  88 + echo $orderProduct->variant->product->lang->title . '(' . $orderProduct->variant->sku . ')';
  89 + ?>
  90 + </div>
  91 + <div class="col-md-4">
  92 + <?php echo $orderProduct->price; ?>
  93 + </div>
  94 + <div class="col-md-3">
  95 + <?php
  96 + echo $form->field($orderProduct, "[$index]count")
  97 + ->textInput()
  98 + ->label(false);
  99 + ?>
  100 + </div>
  101 + <div class="col-md-1">
  102 + <?php
  103 + echo Html::a(
  104 + Html::icon(
  105 + 'trash-o',
  106 + [
  107 + 'prefix' => 'fa fa-',
  108 + ]
  109 + ),
  110 + '#',
  111 + [
  112 + 'class' => 'remove-order-product',
  113 + ]
  114 + )
  115 + ?>
  116 + </div>
  117 + </div>
  118 + <?php
  119 + }
  120 + ?>
  121 + </div>
  122 + <div class="row">
  123 + <div class="col-md-8">
59 124 <?php
60   - echo Html::tag('strong', \Yii::t('order', 'Product'));
  125 + echo Select2::widget(
  126 + [
  127 + 'name' => 'add-to-order',
  128 + 'options' => [
  129 + 'placeholder' => \Yii::t('order', 'Select product'),
  130 + ],
  131 + 'pluginOptions' => [
  132 + 'allowClear' => true,
  133 + 'minimumInputLength' => 3,
  134 + 'language' => [
  135 + 'errorLoading' => new JsExpression(
  136 + "function() {return '" . \Yii::t('order', 'Waiting for results') . "'; }"
  137 + ),
  138 + ],
  139 + 'ajax' => [
  140 + 'url' => Url::to([ 'product-list' ]),
  141 + 'dataType' => 'json',
  142 + 'data' => new JsExpression('function(params) { return {q:params.term}; }'),
  143 + ],
  144 + 'escapeMarkup' => new JsExpression('function (markup) { return markup; }'),
  145 + 'templateResult' => new JsExpression('function(city) { return city.text; }'),
  146 + 'templateSelection' => new JsExpression('function (city) { return city.text; }'),
  147 + ],
  148 + 'id' => 'add-to-order',
  149 + ]
  150 + );
61 151 ?>
62 152 </div>
63   - <div class="col-md-4">
  153 + <div class="col-md-3">
64 154 <?php
65   - echo Html::tag('strong', \Yii::t('order', 'Price'));
  155 + echo Html::textInput(
  156 + 'count-to-order',
  157 + null,
  158 + [
  159 + 'class' => 'form-control',
  160 + 'id' => 'count-to-order',
  161 + ]
  162 + );
66 163 ?>
67 164 </div>
68   - <div class="col-md-4">
  165 + <div class="col-md-1">
69 166 <?php
70   - echo Html::tag('strong', \Yii::t('order', 'Count'));
  167 + echo Html::a(
  168 + Html::icon(
  169 + 'plus-circle',
  170 + [
  171 + 'prefix' => 'fa fa-',
  172 + ]
  173 + ),
  174 + '#',
  175 + [
  176 + 'class' => 'variant-to-order',
  177 + 'data-id' => $model->id,
  178 + ]
  179 + );
71 180 ?>
72 181 </div>
73 182 </div>
74   -
75   - <?php
76   - foreach ($model->orderProducts as $index => $orderProduct) {
77   - ?>
78   - <div class="row row-order-product">
79   - <div class="col-md-4">
80   - <?php
81   - echo $form->field($orderProduct, "[$index]variant_id")
82   - ->hiddenInput()
83   - ->label(false);
84   - echo $orderProduct->variant->product->lang->title . '(' . $orderProduct->variant->sku . ')';
85   - ?>
86   - </div>
87   - <div class="col-md-4">
88   - <?php echo $orderProduct->price; ?>
89   - </div>
90   - <div class="col-md-3">
91   - <?php
92   - echo $form->field($orderProduct, "[$index]count")
93   - ->textInput()
94   - ->label(false);
95   - ?>
96   - </div>
97   - <div class="col-md-1">
98   - <?php
99   - echo Html::a(
100   - Html::icon(
101   - 'trash-o',
102   - [
103   - 'prefix' => 'fa fa-',
104   - ]
105   - ),
106   - '#',
107   - [
108   - 'class' => 'remove-order-product',
109   - ]
110   - )
111   - ?>
112   - </div>
113   - </div>
114   - <?php
115   - }
116   - ?>
117   - <div class="row">
118   - <div class="col-md-8">
119   - </div>
120   - <div class="col-md-4">
121   - </div>
122   - </div>
123 183 </div>
124 184 <?php
125 185 }
... ...
web/js/order.js
... ... @@ -6,4 +6,34 @@ $(function() {
6 6 .parents('.row-order-product')
7 7 .remove();
8 8 });
9   -});
10 9 \ No newline at end of file
  10 + $(document)
  11 + .on('click', '.variant-to-order', function(e) {
  12 + e.preventDefault();
  13 + var id = $('#add-to-order');
  14 + var count = $('#count-to-order');
  15 + var order = $(this)
  16 + .data('id');
  17 + if (id.val() && count.val()) {
  18 + var selector = '#order-product-pjax';
  19 + showLoader(selector);
  20 + $.post('/admin/order/add-to-order', {
  21 + id: id.val(),
  22 + count: count.val(),
  23 + order: order
  24 + }, function() {
  25 + $.pjax.reload(selector, {
  26 + timeout: 5000,
  27 + fragment: selector
  28 + });
  29 + });
  30 + id.val(null)
  31 + .trigger('change');
  32 + count.val(null)
  33 + .trigger('change');
  34 + }
  35 + });
  36 +});
  37 +function showLoader(container) {
  38 + $(container)
  39 + .prepend('<div class="loader-wrapper"></div>');
  40 +}
11 41 \ No newline at end of file
... ...