Commit a0da38ca8f71a10592d7b4624367d8127d869369

Authored by Alexey Boroda
2 parents 7cfb6c63 65f5712c

Merge branch 'master' of gitlab.artweb.com.ua:yarik.nechyporuk/artbox-ecommerce

controllers/OrderController.php
... ... @@ -3,19 +3,21 @@
3 3 namespace artweb\artbox\ecommerce\controllers;
4 4  
5 5 use artweb\artbox\components\SmsSender;
  6 + use artweb\artbox\ecommerce\models\OrderLabelHistory;
  7 + use artweb\artbox\ecommerce\models\OrderLog;
6 8 use artweb\artbox\ecommerce\models\OrderSearch;
  9 + use common\components\CreditHelper;
7 10 use common\models\User;
8   - use phpDocumentor\Reflection\Types\Null_;
9 11 use Yii;
10 12 use yii\data\ArrayDataProvider;
11   - use yii\helpers\ArrayHelper;
  13 + use yii\db\ActiveQuery;
12 14 use yii\helpers\Json;
13 15 use yii\helpers\VarDumper;
  16 + use yii\validators\NumberValidator;
14 17 use yii\web\Controller;
15 18 use yii\filters\VerbFilter;
16 19 use yii\data\ActiveDataProvider;
17 20 use yii\web\ForbiddenHttpException;
18   - use yii\web\HttpException;
19 21 use artweb\artbox\ecommerce\models\Order;
20 22 use artweb\artbox\ecommerce\models\OrderProduct;
21 23 use artweb\artbox\ecommerce\models\ProductVariant;
... ... @@ -107,6 +109,14 @@
107 109 public function actionView($id)
108 110 {
109 111 $model = $this->findModel($id);
  112 +
  113 + $historyData = new ActiveDataProvider(
  114 + [
  115 + 'query' => $model->getLabelsHistory()
  116 + ->with('order', 'label', 'user'),
  117 + ]
  118 + );
  119 +
110 120 $dataProvider = new ActiveDataProvider(
111 121 [
112 122 'query' => $model->getProducts(),
... ... @@ -115,8 +125,9 @@
115 125 return $this->render(
116 126 'view',
117 127 [
118   - 'model' => $model,
119   - 'products' => $dataProvider,
  128 + 'model' => $model,
  129 + 'products' => $dataProvider,
  130 + 'historyData' => $historyData,
120 131 ]
121 132 );
122 133 }
... ... @@ -128,6 +139,25 @@
128 139 $model->save();
129 140 }
130 141  
  142 + public function actionLog($id)
  143 + {
  144 + $model = Order::findOne($id);
  145 +
  146 + $logData = new ActiveDataProvider(
  147 + [
  148 + 'query' => $model->getLogs(),
  149 + ]
  150 + );
  151 +
  152 + return $this->render(
  153 + 'log',
  154 + [
  155 + 'model' => $model,
  156 + 'logData' => $logData,
  157 + ]
  158 + );
  159 + }
  160 +
131 161 public function actionDelete($id)
132 162 {
133 163 if (\Yii::$app->user->identity->isAdmin()) {
... ... @@ -151,10 +181,10 @@
151 181  
152 182 public function actionAdd()
153 183 {
154   - if (!empty( \Yii::$app->request->post() )) {
  184 + if (!empty(\Yii::$app->request->post())) {
155 185 $id = \Yii::$app->request->post('OrderProduct')[ 'id' ];
156 186 $order_id = \Yii::$app->request->post('OrderProduct')[ 'order_id' ];
157   - if (!empty( \Yii::$app->request->post('OrderProduct')[ 'count' ] )) {
  187 + if (!empty(\Yii::$app->request->post('OrderProduct')[ 'count' ])) {
158 188 $count = \Yii::$app->request->post('OrderProduct')[ 'count' ];
159 189 } else {
160 190 $count = 1;
... ... @@ -174,7 +204,7 @@
174 204 )
175 205 ->one();
176 206  
177   - if (!empty( $model )) {
  207 + if (!empty($model)) {
178 208 $model->count += $count;
179 209 $model->removed = false;
180 210 } else {
... ... @@ -221,7 +251,7 @@
221 251 if ($orderProduct->load($post)) {
222 252 $orderProduct->save();
223 253 $output = '';
224   - if (isset( $posted[ 'count' ] )) {
  254 + if (isset($posted[ 'count' ])) {
225 255 $output = Yii::$app->formatter->asDecimal($orderProduct->count, 0);
226 256 }
227 257 $out = Json::encode(
... ... @@ -283,7 +313,7 @@
283 313 return $this->renderPartial(
284 314 'print',
285 315 [
286   - 'order' => $order,
  316 + 'order' => $order,
287 317 'dataProvider' => $dataProvider,
288 318 ]
289 319 );
... ... @@ -309,7 +339,7 @@
309 339 $orderProduct->save();
310 340 $orderProduct->order->totalRecount();
311 341 $output = '';
312   - if (isset( $posted[ 'count' ] )) {
  342 + if (isset($posted[ 'count' ])) {
313 343 $output = Yii::$app->formatter->asDecimal($orderProduct->count, 0);
314 344 }
315 345 $out = Json::encode(
... ... @@ -322,9 +352,21 @@
322 352  
323 353 return $out;
324 354 }
325   -
  355 +
326 356 $model = $this->findModel($id);
327 357  
  358 + if ($model->payment == 10) {
  359 + $model->validators->append(
  360 + new NumberValidator(
  361 + [
  362 + 'attributes' => 'credit_sum',
  363 + 'max' => $model->total - CreditHelper::MIN_CREDIT_SUM,
  364 + 'min' => $model->total - CreditHelper::MAX_CREDIT_SUM,
  365 + ]
  366 + )
  367 + );
  368 + }
  369 +
328 370 /**
329 371 * @var User $user
330 372 */
... ... @@ -334,7 +376,7 @@
334 376 throw new ForbiddenHttpException();
335 377 }
336 378 }
337   -
  379 +
338 380 $dataProvider = new ActiveDataProvider(
339 381 [
340 382 'query' => $model->getProducts()
... ... @@ -344,10 +386,10 @@
344 386 ]
345 387 );
346 388  
347   - if (empty( $model->manager_id )) {
  389 + if (empty($model->manager_id)) {
348 390 $model->manager_id = \Yii::$app->user->id;
349 391 }
350   -
  392 +
351 393 $headers = \Yii::$app->response->headers;
352 394 $headers->set('Access-Control-Allow-Origin', '*');
353 395  
... ... @@ -392,7 +434,7 @@
392 434 }
393 435 }
394 436  
395   - public function actionFindProduct($q = null, $id = null)
  437 + public function actionFindProduct($q = NULL, $id = NULL)
396 438 {
397 439 \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
398 440 $out = [
... ... @@ -452,7 +494,7 @@
452 494  
453 495 protected function findModel($id)
454 496 {
455   - if (( $model = Order::findOne($id) ) !== null) {
  497 + if (( $model = Order::findOne($id) ) !== NULL) {
456 498 return $model;
457 499 } else {
458 500 throw new NotFoundHttpException('The requested page does not exist.');
... ... @@ -469,7 +511,7 @@
469 511 if ($model->edit_id == \Yii::$app->user->id) {
470 512 $this->unblockOrder(\Yii::$app->request->post('id'));
471 513 }
472   -
  514 +
473 515 if (!$model->published) {
474 516 $model->deleteUnpublished();
475 517 }
... ... @@ -495,7 +537,7 @@
495 537  
496 538 public function actionBlockOrder()
497 539 {
498   - if (!empty( \Yii::$app->request->post() )) {
  540 + if (!empty(\Yii::$app->request->post())) {
499 541 \Yii::$app->response->format = Response::FORMAT_JSON;
500 542  
501 543 $model = $this->findModel(\Yii::$app->request->post('id'));
... ... @@ -512,7 +554,7 @@
512 554 if ($model->save()) {
513 555 return [
514 556 'time' => $date,
515   - 'user' => !empty( $user ) ? $user->username : '',
  557 + 'user' => !empty($user) ? $user->username : '',
516 558 ];
517 559 } else {
518 560 return [
... ... @@ -540,6 +582,18 @@
540 582 }
541 583 $model->published = true;
542 584 $model->save();
  585 +
  586 + /**
  587 + * Add order to history
  588 + */
  589 + $history = new OrderLabelHistory();
  590 +
  591 + $history->label_id = (integer) $model->label;
  592 + $history->order_id = (integer) $model->id;
  593 + $history->user_id = (integer) \Yii::$app->user->identity->id;
  594 +
  595 + $history->save();
  596 +
543 597 /**
544 598 * @var SmsSender $sender
545 599 */
... ...
controllers/StatisticsController.php
... ... @@ -4,6 +4,7 @@
4 4  
5 5 use artweb\artbox\ecommerce\models\Label;
6 6 use artweb\artbox\ecommerce\models\Order;
  7 + use common\models\User;
7 8 use yii\data\ActiveDataProvider;
8 9 use yii\helpers\ArrayHelper;
9 10 use yii\helpers\VarDumper;
... ... @@ -11,7 +12,7 @@
11 12  
12 13 class StatisticsController extends Controller
13 14 {
14   - public function actionIndex($date_range = NULL, $label = NULL)
  15 + public function actionIndex($date_range = NULL, $label = NULL, $manager = NULL)
15 16 {
16 17 /**
17 18 * Get a dates range
... ... @@ -34,6 +35,12 @@
34 35 } else {
35 36 $labelFilter = [];
36 37 }
  38 +
  39 + if (!empty($manager)) {
  40 + $managerFilter = [ 'manager_id' => $manager ];
  41 + } else {
  42 + $managerFilter = [];
  43 + }
37 44  
38 45 /**
39 46 * Get labels
... ... @@ -41,61 +48,137 @@
41 48 $labels = Label::find()
42 49 ->with('lang')
43 50 ->all();
  51 +
  52 + /**
  53 + * Get user
  54 + */
  55 + $managers = User::find()
  56 + ->all();
44 57  
45 58 /**
46 59 * Generate statistics
47 60 */
48 61 $labelStatistics = ArrayHelper::map(
49 62 $labels,
50   - function($model) {
51   - /**
52   - * @var Label $model
53   - */
  63 + function(Label $model) {
54 64 return $model->lang->title;
55 65 },
56   - function($model) use ($dateFilter) {
57   - /**
58   - * @var Label $model
59   - */
60   - return $model->getStatistics($dateFilter);
  66 + function(Label $model) use ($dateFilter, $managerFilter) {
  67 + return $model->getStatistics($dateFilter, $managerFilter);
61 68 }
62 69 );
63   -
  70 +
  71 + /**
  72 + * Data provider for table
  73 + */
64 74 $dataProvider = new ActiveDataProvider(
65 75 [
66 76 'query' => Order::find()
67 77 ->filterWhere($dateFilter)
  78 + ->andFilterWhere($managerFilter)
68 79 ->andFilterWhere($labelFilter),
69 80 ]
70 81 );
  82 +
  83 + /**
  84 + * Creating charts data
  85 + */
  86 + $labelChartData1 = [
  87 + 'labels' => array_keys($labelStatistics),
  88 + 'datasets' => [
  89 + [
  90 + 'label' => 'Заказов, шт.',
  91 + 'data' => ArrayHelper::getColumn($labelStatistics, 'count', false),
  92 + 'backgroundColor' => 'rgba(54, 162, 235, 0.2)',
  93 + 'borderColor' => 'rgba(54, 162, 235, 1)',
  94 + 'borderWidth' => 1,
  95 + ],
  96 + ],
  97 + ];
71 98  
72   - $chartData = [];
73   - foreach ($labelStatistics as $statistic) {
74   - $chartData[] = $statistic[ 'sum' ];
75   - }
76   - $chartLabels = [];
77   - foreach ($labelStatistics as $name => $statistic) {
78   - $chartLabels[] = $name;
79   - }
80   - $labelChartData = [
81   - 'labels' => $chartLabels,
  99 + $labelChartData2 = [
  100 + 'labels' => array_keys($labelStatistics),
  101 + 'datasets' => [
  102 + [
  103 + 'label' => 'На сумму, грн.',
  104 + 'data' => ArrayHelper::getColumn($labelStatistics, 'sum', false),
  105 + 'backgroundColor' => 'rgba(255, 99, 132, 0.2)',
  106 + 'borderColor' => 'rgba(255,99,132,1)',
  107 + 'borderWidth' => 1,
  108 + ],
  109 + ],
  110 + ];
  111 +
  112 + $labelChartData3 = [
  113 + 'labels' => array_keys($labelStatistics),
82 114 'datasets' => [
83 115 [
84   - 'label' => 'На сумму, грн.',
85   - 'data' => $chartData,
  116 + 'label' => 'Заказано товаров, шт.',
  117 + 'data' => ArrayHelper::getColumn($labelStatistics, 'products', false),
  118 + 'backgroundColor' => 'rgba(255, 206, 86, 0.2)',
  119 + 'borderColor' => 'rgba(255, 206, 86, 1)',
  120 + 'borderWidth' => 1,
  121 + ],
  122 + [
  123 + 'label' => 'Уникальных товаров, шт.',
  124 + 'data' => ArrayHelper::getColumn($labelStatistics, 'unique', false),
  125 + 'backgroundColor' => 'rgba(75, 192, 192, 0.2)',
  126 + 'borderColor' => 'rgba(75, 192, 192, 1)',
  127 + 'borderWidth' => 1,
86 128 ],
87 129 ],
88 130 ];
  131 +
  132 + /**
  133 + * Getting rejection statistics
  134 + */
  135 + $rejectStatistics = Order::getRejectionStatistics($dateFilter, $managerFilter);
  136 +
  137 + /**
  138 + * Charts data for rejects
  139 + */
  140 + $rejectChartData1 = [
  141 + 'labels' => array_keys($rejectStatistics),
  142 + 'datasets' => [
  143 + [
  144 + 'label' => 'Заказов, шт.',
  145 + 'data' => ArrayHelper::getColumn($rejectStatistics, 'count', false),
  146 + 'backgroundColor' => 'rgba(153, 102, 255, 0.2)',
  147 + 'borderColor' => 'rgba(153, 102, 255, 1)',
  148 + 'borderWidth' => 1,
  149 + ],
  150 + ],
  151 + ];
  152 +
  153 + $rejectChartData2 = [
  154 + 'labels' => array_keys($rejectStatistics),
  155 + 'datasets' => [
  156 + [
  157 + 'label' => 'На сумму, грн.',
  158 + 'data' => ArrayHelper::getColumn($rejectStatistics, 'sum', false),
  159 + 'backgroundColor' => 'rgba(255, 159, 64, 0.2)',
  160 + 'borderColor' => 'rgba(255, 159, 64, 1)',
  161 + 'borderWidth' => 1,
  162 + ],
  163 + ],
  164 + ];
  165 +
89 166 return $this->render(
90 167 'index',
91 168 [
92 169 'labels' => $labels,
  170 + 'managers' => $managers,
93 171 'labelStatistics' => $labelStatistics,
94   - 'rejectionStatistics' => Order::getRejectionStatistics($dateFilter),
  172 + 'rejectionStatistics' => $rejectStatistics,
95 173 'dataProvider' => $dataProvider,
96   - 'labelChartData' => $labelChartData,
  174 + 'labelChartData1' => $labelChartData1,
  175 + 'labelChartData2' => $labelChartData2,
  176 + 'labelChartData3' => $labelChartData3,
  177 + 'rejectChartData1' => $rejectChartData1,
  178 + 'rejectChartData2' => $rejectChartData2,
97 179 'dateValue' => empty($date_range) ? '' : $date_range,
98 180 'dataLabel' => empty($label) ? false : $label,
  181 + 'dataManager' => empty($manager) ? false : $manager,
99 182 ]
100 183 );
101 184 }
... ...
models/Basket.php
... ... @@ -10,6 +10,7 @@
10 10 /**
11 11 * Class Basket to work with basket
12 12 *
  13 + * @property bool isCredit
13 14 * @package artweb\artbox\ecommerce\models
14 15 */
15 16 class Basket extends Component
... ... @@ -32,7 +33,7 @@
32 33 $this->session = \Yii::$app->session;
33 34 if (!$this->session->has('basket')) {
34 35 $this->session->set('basket', []);
35   - } elseif(!empty($this->session->get('basket'))) {
  36 + } elseif (!empty( $this->session->get('basket') )) {
36 37 $cookies = \Yii::$app->response->cookies;
37 38 $cookies->add(
38 39 new Cookie(
... ... @@ -144,8 +145,9 @@
144 145 {
145 146 $this->session->set('basket', $data);
146 147 $cookies = \Yii::$app->response->cookies;
147   - if(empty($data)) {
  148 + if (empty( $data )) {
148 149 $cookies->remove('basket');
  150 + $cookies->remove('isCredit');
149 151 } else {
150 152 $cookies->add(
151 153 new Cookie(
... ... @@ -247,6 +249,23 @@
247 249 public function clear()
248 250 {
249 251 $this->setData([]);
  252 + \Yii::$app->response->cookies->remove('isCredit');
  253 + }
  254 +
  255 + /**
  256 + * Check if is credit cookie flag set
  257 + *
  258 + * @return bool
  259 + */
  260 + public static function getIsCredit(): bool
  261 + {
  262 + // Get cookies from global in order to skip yii2 cookie validation
  263 + $cookies = $_COOKIE;
  264 + if (isset( $cookies[ 'isCredit' ] )) {
  265 + return true;
  266 + } else {
  267 + return false;
  268 + }
250 269 }
251 270  
252 271 }
... ...
models/Label.php
... ... @@ -65,40 +65,43 @@
65 65 ];
66 66 }
67 67  
68   - public function getStatistics(array $where = [])
  68 + public function getStatistics(array $date = [], array $manager = [])
69 69 {
70   - $query = ( new Query() )->select(
71   - [
72   - 'sum' => 'SUM(total)',
73   - 'count' => 'COUNT(*)',
74   - 'unique' => ( new Query() )->select('COUNT(*)')
75   - ->from('order')
76   - ->leftJoin(
77   - 'order_product',
78   - '"order"."id"="order_product"."order_id"'
79   - )
80   - ->where([ 'order.label' => $this->id ])
81   - ->andFilterWhere(
82   - $where
83   - ),
84   - 'products' => ( new Query() )->select('SUM(count)')
85   - ->from('order')
86   - ->leftJoin(
87   - 'order_product',
88   - '"order"."id"="order_product"."order_id"'
89   - )
90   - ->where([ 'order.label' => $this->id ])
91   - ->andFilterWhere(
92   - $where
93   - ),
94   - ]
95   - )
96   - ->from('order')
97   - ->where([ 'label' => $this->id ])
98   - ->andFilterWhere(
99   - $where
100   - );
101   -
  70 + $query = ( new Query() )->select(
  71 + [
  72 + 'sum' => 'SUM(total)',
  73 + 'count' => 'COUNT(*)',
  74 + 'unique' => ( new Query() )->select('COUNT(*)')
  75 + ->from('order')
  76 + ->leftJoin(
  77 + 'order_product',
  78 + '"order"."id"="order_product"."order_id"'
  79 + )
  80 + ->where([ 'order.label' => $this->id ])
  81 + ->andFilterWhere(
  82 + $date
  83 + )
  84 + ->andFilterWhere($manager),
  85 + 'products' => ( new Query() )->select('SUM(count)')
  86 + ->from('order')
  87 + ->leftJoin(
  88 + 'order_product',
  89 + '"order"."id"="order_product"."order_id"'
  90 + )
  91 + ->where([ 'order.label' => $this->id ])
  92 + ->andFilterWhere(
  93 + $date
  94 + )
  95 + ->andFilterWhere($manager),
  96 + ]
  97 + )
  98 + ->from('order')
  99 + ->where([ 'label' => $this->id ])
  100 + ->andFilterWhere(
  101 + $date
  102 + )
  103 + ->andFilterWhere($manager);
  104 +
102 105 return $query->one();
103 106 }
104 107 }
... ...
models/Order.php
... ... @@ -8,6 +8,8 @@
8 8 use yii\behaviors\TimestampBehavior;
9 9 use yii\db\ActiveRecord;
10 10 use yii\db\Query;
  11 + use yii\helpers\Json;
  12 + use yii\helpers\VarDumper;
11 13  
12 14 /**
13 15 * Class Order
... ... @@ -55,6 +57,12 @@
55 57 * @property string $city
56 58 * @property string $deliveryString
57 59 * @property boolean $published
  60 + * @property Label $orderLabel
  61 + * @property Delivery $orderDelivery
  62 + * @property OrderPayment $orderPayment
  63 + * @property OrderLog[] $logs
  64 + * @property float $credit_sum
  65 + * @property int $credit_month
58 66 */
59 67 class Order extends ActiveRecord
60 68 {
... ... @@ -92,13 +100,14 @@
92 100 {
93 101 return 'order';
94 102 }
95   -
  103 +
96 104 /**
97   - * @param array $where
  105 + * @param array $date
  106 + * @param array $manager
98 107 *
99 108 * @return array
100 109 */
101   - public static function getRejectionStatistics(array $where = [])
  110 + public static function getRejectionStatistics(array $date = [], array $manager = [])
102 111 {
103 112 $result = [];
104 113 foreach (self::REASONS as $id => $reason) {
... ... @@ -113,12 +122,87 @@
113 122 [
114 123 'reason' => $id,
115 124 ]
116   - )->andFilterWhere($where)->one();
  125 + )
  126 + ->andFilterWhere($date)
  127 + ->andFilterWhere($manager)
  128 + ->one();
117 129 }
118 130  
119 131 return $result;
120 132 }
121 133  
  134 + /**
  135 + * @param string $attr
  136 + * @param array $values
  137 + *
  138 + * @return array
  139 + * Return array in form ['old'=>'old value ...', 'new' => 'new value ...']
  140 + */
  141 + public function getLogAttributes(string $attr, array $values)
  142 + {
  143 + if ($attr == 'deadline') {
  144 + return [
  145 + 'old' => empty($values[ 'old' ]) ? '' : date('d.m.Y', $values[ 'old' ]),
  146 + 'new' => empty($values[ 'new' ]) ? '' : date('d.m.Y', $values[ 'new' ]),
  147 + ];
  148 + } elseif ($attr == 'reason') {
  149 + return [
  150 + 'old' => empty($values[ 'old' ]) ? '' : self::REASONS[ $values[ 'old' ] ],
  151 + 'new' => empty($values[ 'new' ]) ? '' : self::REASONS[ $values[ 'new' ] ],
  152 + ];
  153 + } elseif ($attr == 'label') {
  154 + $labels = Label::find()
  155 + ->with('lang')
  156 + ->indexBy('id')
  157 + ->all();
  158 + return [
  159 + 'old' => empty($values[ 'old' ]) ? '' : $labels[ $values[ 'old' ] ]->lang->title,
  160 + 'new' => empty($values[ 'new' ]) ? '' : $labels[ $values[ 'new' ] ]->lang->title,
  161 + ];
  162 + } elseif ($attr == 'delivery') {
  163 + $deliveries = Delivery::find()
  164 + ->with('lang')
  165 + ->indexBy('id')
  166 + ->all();
  167 + return [
  168 + 'old' => empty($values[ 'old' ]) ? '' : $deliveries[ $values[ 'old' ] ]->lang->title,
  169 + 'new' => empty($values[ 'new' ]) ? '' : $deliveries[ $values[ 'new' ] ]->lang->title,
  170 + ];
  171 + } elseif ($attr == 'manager_id') {
  172 + $users = User::find()
  173 + ->indexBy('id')
  174 + ->all();
  175 + return [
  176 + 'old' => empty($values[ 'old' ]) ? '' : $users[ $values[ 'old' ] ]->username,
  177 + 'new' => empty($values[ 'new' ]) ? '' : $users[ $values[ 'new' ] ]->username,
  178 + ];
  179 + } elseif ($attr == 'payment') {
  180 + $payment = OrderPayment::find()
  181 + ->with('lang')
  182 + ->indexBy('id')
  183 + ->all();
  184 + return [
  185 + 'old' => empty($values[ 'old' ]) ? '' : $payment[ $values[ 'old' ] ]->lang->title,
  186 + 'new' => empty($values[ 'new' ]) ? '' : $payment[ $values[ 'new' ] ]->lang->title,
  187 + ];
  188 + } elseif ($attr == 'shipping_by') {
  189 + return [
  190 + 'old' => empty($values[ 'old' ]) ? '' : self::SHIPPING_BY[ $values[ 'old' ] ][ 'label' ],
  191 + 'new' => empty($values[ 'new' ]) ? '' : self::SHIPPING_BY[ $values[ 'new' ] ][ 'label' ],
  192 + ];
  193 + } elseif ($attr == 'pay') {
  194 + return [
  195 + 'old' => ( $values[ 'old' ] == true ) ? 'Оплачен' : 'Не оплачен',
  196 + 'new' => ( $values[ 'new' ] == true ) ? 'Оплачен' : 'Не оплачен',
  197 + ];
  198 + } else {
  199 + return $values;
  200 + }
  201 + }
  202 +
  203 + /**
  204 + * @inheritdoc
  205 + */
122 206 public function behaviors()
123 207 {
124 208 return [
... ... @@ -131,6 +215,9 @@
131 215 ];
132 216 }
133 217  
  218 + /**
  219 + * @inheritdoc
  220 + */
134 221 public function rules()
135 222 {
136 223 return [
... ... @@ -188,6 +275,21 @@
188 275 ],
189 276 [
190 277 [
  278 + 'credit_month',
  279 + ],
  280 + 'integer',
  281 + 'min' => 3,
  282 + 'max' => 36,
  283 + ],
  284 + [
  285 + [
  286 + 'credit_sum',
  287 + ],
  288 + 'number',
  289 + 'min' => 0,
  290 + ],
  291 + [
  292 + [
191 293 'deadline',
192 294 'name',
193 295 'numbercard',
... ... @@ -209,6 +311,49 @@
209 311 ];
210 312 }
211 313  
  314 + public function afterSave($insert, $changedAttributes)
  315 + {
  316 + $data = [];
  317 + foreach ($changedAttributes as $key => $attribute) {
  318 + if ($this->oldAttributes[ $key ] != $attribute && $key != 'updated_at') {
  319 + $data[ $key ] = $this->getLogAttributes(
  320 + $key,
  321 + [
  322 + 'old' => $attribute,
  323 + 'new' => $this->oldAttributes[ $key ],
  324 + ]
  325 + );
  326 + }
  327 + }
  328 +
  329 + if (!empty($data) && empty($data[ 'edit_time' ])) {
  330 + $log = new OrderLog();
  331 + $log->order_id = (integer) $this->id;
  332 + $log->user_id = (integer) \Yii::$app->user->identity->id;
  333 + $log->data = Json::encode($data);
  334 +
  335 + $log->save();
  336 + }
  337 +
  338 + if (!empty($changedAttributes[ 'label' ])) {
  339 + if ($this->label != (string) $changedAttributes[ 'label' ]) {
  340 + $history = new OrderLabelHistory();
  341 +
  342 + $history->label_id = (integer) $this->label;
  343 + $history->order_id = (integer) $this->id;
  344 + $history->user_id = (integer) \Yii::$app->user->identity->id;
  345 +
  346 + if ($history->save()) {
  347 + \Yii::$app->session->setFlash('label_update', 'Статус заказа обновлен');
  348 + }
  349 + }
  350 + }
  351 + parent::afterSave($insert, $changedAttributes);
  352 + }
  353 +
  354 + /**
  355 + * @inheritdoc
  356 + */
212 357 public function afterFind()
213 358 {
214 359 parent::afterFind();
... ... @@ -216,6 +361,9 @@
216 361  
217 362 }
218 363  
  364 + /**
  365 + * @inheritdoc
  366 + */
219 367 public function beforeSave($insert)
220 368 {
221 369 if (parent::beforeSave($insert)) {
... ... @@ -227,6 +375,9 @@
227 375  
228 376 }
229 377  
  378 + /**
  379 + * Convert some date
  380 + */
230 381 protected function convertDate()
231 382 {
232 383 if (!empty($this->deadline)) {
... ... @@ -239,6 +390,9 @@
239 390  
240 391 }
241 392  
  393 + /**
  394 + * @inheritdoc
  395 + */
242 396 public function attributeLabels()
243 397 {
244 398 return [
... ... @@ -273,9 +427,14 @@
273 427 'shipping_by' => Yii::t('app', 'Отправка за счет'),
274 428 'city' => Yii::t('app', 'Город'),
275 429 'numbercard' => Yii::t('app', '№ карточки'),
  430 + 'credit_month' => Yii::t('app', 'Количество месяцев'),
  431 + 'credit_sum' => Yii::t('app', 'Первоначальный взнос'),
276 432 ];
277 433 }
278 434  
  435 + /**
  436 + * @return \yii\db\ActiveQuery
  437 + */
279 438 public function getUser()
280 439 {
281 440 return $this->hasOne(Customer::className(), [ 'id' => 'user_id' ]);
... ... @@ -314,6 +473,14 @@
314 473 }
315 474  
316 475 /**
  476 + * @return \yii\db\ActiveQuery
  477 + */
  478 + public function getLabelsHistory()
  479 + {
  480 + return $this->hasMany(OrderLabelHistory::className(), [ 'order_id' => 'id' ]);
  481 + }
  482 +
  483 + /**
317 484 * @return string
318 485 */
319 486 public function getDeliveryString()
... ... @@ -330,6 +497,14 @@
330 497 }
331 498  
332 499 /**
  500 + * @return \yii\db\ActiveQuery
  501 + */
  502 + public function getLogs()
  503 + {
  504 + return $this->hasMany(OrderLog::className(), [ 'order_id' => 'id' ]);
  505 + }
  506 +
  507 + /**
333 508 * If deadline is fucked up returns true,
334 509 * if deadline is not setted return false, like everything is ok
335 510 *
... ... @@ -345,13 +520,18 @@
345 520 }
346 521  
347 522 /**
348   - *
  523 + * @return \yii\db\ActiveQuery
349 524 */
350 525 public function getManager()
351 526 {
352 527 return $this->hasOne(User::className(), [ 'id' => 'manager_id' ]);
353 528 }
354 529  
  530 + /**
  531 + * Check if order is blocked for updating
  532 + *
  533 + * @return bool
  534 + */
355 535 public function isBlocked()
356 536 {
357 537 if ($this->edit_id === 0) {
... ... @@ -365,6 +545,9 @@
365 545 }
366 546 }
367 547  
  548 + /**
  549 + * If order products changed recount te total value
  550 + */
368 551 public function totalRecount()
369 552 {
370 553 $products = $this->products;
... ... @@ -379,6 +562,9 @@
379 562 $this->save();
380 563 }
381 564  
  565 + /**
  566 + * If exit unpublished order - delete it
  567 + */
382 568 public function deleteUnpublished()
383 569 {
384 570 /**
... ...
models/OrderLabelHistory.php 0 → 100644
  1 +<?php
  2 +
  3 + namespace artweb\artbox\ecommerce\models;
  4 +
  5 + use yii\behaviors\TimestampBehavior;
  6 + use yii\db\ActiveRecord;
  7 + use common\models\User;
  8 + use Yii;
  9 +
  10 + /**
  11 + * This is the model class for table "order_label_history".
  12 + *
  13 + * @property integer $id
  14 + * @property integer $label_id
  15 + * @property integer $order_id
  16 + * @property integer $user_id
  17 + * @property integer $created_at
  18 + * @property Order $order
  19 + * @property Label $label
  20 + * @property User $user
  21 + */
  22 + class OrderLabelHistory extends ActiveRecord
  23 + {
  24 + /**
  25 + * @inheritdoc
  26 + */
  27 + public static function tableName()
  28 + {
  29 + return 'order_label_history';
  30 + }
  31 +
  32 + /**
  33 + * @inheritdoc
  34 + */
  35 + public function behaviors()
  36 + {
  37 + return [
  38 + [
  39 + 'class' => TimestampBehavior::className(),
  40 + 'updatedAtAttribute' => false,
  41 + ],
  42 + ];
  43 + }
  44 +
  45 + /**
  46 + * @inheritdoc
  47 + */
  48 + public function rules()
  49 + {
  50 + return [
  51 + [
  52 + [
  53 + 'label_id',
  54 + 'order_id',
  55 + 'user_id',
  56 + 'created_at',
  57 + ],
  58 + 'integer',
  59 + ],
  60 + [
  61 + [ 'order_id' ],
  62 + 'exist',
  63 + 'skipOnError' => true,
  64 + 'targetClass' => Order::className(),
  65 + 'targetAttribute' => [ 'order_id' => 'id' ],
  66 + ],
  67 + [
  68 + [ 'label_id' ],
  69 + 'exist',
  70 + 'skipOnError' => true,
  71 + 'targetClass' => Label::className(),
  72 + 'targetAttribute' => [ 'label_id' => 'id' ],
  73 + ],
  74 +// [
  75 +// [ 'user_id' ],
  76 +// 'exist',
  77 +// 'skipOnError' => true,
  78 +// 'targetClass' => User::className(),
  79 +// 'targetAttribute' => [ 'user_id' => 'id' ],
  80 +// ],
  81 + ];
  82 + }
  83 +
  84 + /**
  85 + * @inheritdoc
  86 + */
  87 + public function attributeLabels()
  88 + {
  89 + return [
  90 + 'id' => Yii::t('app', 'ID'),
  91 + 'label_id' => Yii::t('app', 'Label ID'),
  92 + 'order_id' => Yii::t('app', 'Order ID'),
  93 + 'user_id' => Yii::t('app', 'User ID'),
  94 + 'created_at' => Yii::t('app', 'Created At'),
  95 + ];
  96 + }
  97 +
  98 + /**
  99 + * @return \yii\db\ActiveQuery
  100 + */
  101 + public function getOrder()
  102 + {
  103 + return $this->hasOne(Order::className(), [ 'id' => 'order_id' ]);
  104 + }
  105 +
  106 + /**
  107 + * @return \yii\db\ActiveQuery
  108 + */
  109 + public function getLabel()
  110 + {
  111 + return $this->hasOne(Label::className(), [ 'id' => 'label_id' ]);
  112 + }
  113 +
  114 + /**
  115 + * @return \yii\db\ActiveQuery
  116 + */
  117 + public function getUser()
  118 + {
  119 + return $this->hasOne(User::className(), [ 'id' => 'user_id' ]);
  120 + }
  121 + }
... ...
models/OrderLog.php 0 → 100644
  1 +<?php
  2 +
  3 + namespace artweb\artbox\ecommerce\models;
  4 +
  5 + use common\models\User;
  6 + use Yii;
  7 + use yii\behaviors\TimestampBehavior;
  8 + use yii\db\ActiveRecord;
  9 +
  10 + /**
  11 + * This is the model class for table "order_log".
  12 + *
  13 + * @property integer $id
  14 + * @property integer $order_id
  15 + * @property integer $created_at
  16 + * @property integer $user_id
  17 + * @property string $data
  18 + * @property Order $order
  19 + * @property User $user
  20 + */
  21 + class OrderLog extends ActiveRecord
  22 + {
  23 + /**
  24 + * @inheritdoc
  25 + */
  26 + public static function tableName()
  27 + {
  28 + return 'order_log';
  29 + }
  30 +
  31 + /**
  32 + * @inheritdoc
  33 + */
  34 + public function behaviors()
  35 + {
  36 + return [
  37 + [
  38 + 'class' => TimestampBehavior::className(),
  39 + 'updatedAtAttribute' => false,
  40 + ],
  41 + ];
  42 + }
  43 +
  44 + /**
  45 + * @inheritdoc
  46 + */
  47 + public function rules()
  48 + {
  49 + return [
  50 + [
  51 + [
  52 + 'order_id',
  53 + 'created_at',
  54 + 'user_id',
  55 + ],
  56 + 'integer',
  57 + ],
  58 + [
  59 + [ 'data' ],
  60 + 'string',
  61 + ],
  62 + [
  63 + [ 'order_id' ],
  64 + 'exist',
  65 + 'skipOnError' => true,
  66 + 'targetClass' => Order::className(),
  67 + 'targetAttribute' => [ 'order_id' => 'id' ],
  68 + ],
  69 + // [
  70 + // [ 'user_id' ],
  71 + // 'exist',
  72 + // 'skipOnError' => true,
  73 + // 'targetClass' => User::className(),
  74 + // 'targetAttribute' => [ 'user_id' => 'id' ],
  75 + // ],
  76 + ];
  77 + }
  78 +
  79 + /**
  80 + * @inheritdoc
  81 + */
  82 + public function attributeLabels()
  83 + {
  84 + return [
  85 + 'id' => Yii::t('app', 'ID'),
  86 + 'order_id' => Yii::t('app', 'Order ID'),
  87 + 'created_at' => Yii::t('app', 'Created At'),
  88 + 'user_id' => Yii::t('app', 'User ID'),
  89 + 'data' => Yii::t('app', 'Data'),
  90 + ];
  91 + }
  92 +
  93 + /**
  94 + * @return \yii\db\ActiveQuery
  95 + */
  96 + public function getOrder()
  97 + {
  98 + return $this->hasOne(Order::className(), [ 'id' => 'order_id' ]);
  99 + }
  100 +
  101 + /**
  102 + * @return \yii\db\ActiveQuery
  103 + */
  104 + public function getUser()
  105 + {
  106 + return $this->hasOne(User::className(), [ 'id' => 'user_id' ]);
  107 + }
  108 + }
... ...
models/Product.php
... ... @@ -571,7 +571,40 @@
571 571 }
572 572 return $groups;
573 573 }
574   -
  574 +
  575 +
  576 + /**
  577 + * Get characteristic from TaxGroups for current Product filled with $customOptions that satisfy current Product
  578 + *
  579 + * @return TaxGroup[]
  580 + */
  581 + public function getCharacteristic(): array
  582 + {
  583 + $groups = $options = [];
  584 + foreach ($this->getOptions()
  585 + ->with('lang')
  586 + ->all() as $option) {
  587 + /**
  588 + * @var TaxOption $option
  589 + */
  590 + $options[ $option[ 'tax_group_id' ] ][] = $option;
  591 + }
  592 + foreach (TaxGroup::find()
  593 + ->where(['is_menu'=>true])
  594 + ->andWhere([ 'id' => array_keys($options) ])
  595 + ->with('lang')
  596 + ->all() as $group) {
  597 + /**
  598 + * @var TaxGroup $group
  599 + */
  600 + if (!empty($options[ $group->id ])) {
  601 + $group->customOptions = $options[ $group->id ];
  602 + $groups[] = $group;
  603 + }
  604 + }
  605 + return $groups;
  606 + }
  607 +
575 608 public function getVideos()
576 609 {
577 610 return $this->hasMany(ProductVideo::className(), [ 'product_id' => 'id' ]);
... ...
models/ProductVariant.php
... ... @@ -398,6 +398,38 @@
398 398 }
399 399 return $groups;
400 400 }
  401 +
  402 + /**
  403 + * Get characteristic from TaxGroups for current ProductVariant filled with $customOptions that satisfy current Product
  404 + *
  405 + * @return TaxGroup[]
  406 + */
  407 + public function getCharacteristic(): array
  408 + {
  409 + $groups = $options = [];
  410 + foreach ($this->getOptions()
  411 + ->with('lang')
  412 + ->all() as $option) {
  413 + /**
  414 + * @var TaxOption $option
  415 + */
  416 + $options[ $option[ 'tax_group_id' ] ][] = $option;
  417 + }
  418 + foreach (TaxGroup::find()
  419 + ->where(['is_menu'=>true])
  420 + ->andWhere([ 'id' => array_keys($options) ])
  421 + ->with('lang')
  422 + ->all() as $group) {
  423 + /**
  424 + * @var TaxGroup $group
  425 + */
  426 + if (!empty($options[ $group->id ])) {
  427 + $group->customOptions = $options[ $group->id ];
  428 + $groups[] = $group;
  429 + }
  430 + }
  431 + return $groups;
  432 + }
401 433  
402 434 /**
403 435 * Set stocks to override existing in product_stock table
... ...
views/order/_form.php
... ... @@ -146,8 +146,7 @@ JS;
146 146  
147 147 <?php $form = ActiveForm::begin(
148 148 [
149   - 'id' => 'main-form',
150   -// 'options' => [ 'class' => 'form-inline' ],
  149 + 'id' => 'main-form',
151 150 ]
152 151 ); ?>
153 152 <div class="container">
... ... @@ -333,7 +332,10 @@ JS;
333 332 'id',
334 333 'short'
335 334 ),
336   - [ 'prompt' => 'Способ оплаты ...' ]
  335 + [
  336 + 'prompt' => 'Способ оплаты ...',
  337 + 'disabled' => $model->payment == 10 ? 'disabled' : false,
  338 + ]
337 339 ); ?>
338 340  
339 341 <?= $form->field($model, 'insurance') ?>
... ... @@ -368,348 +370,348 @@ JS;
368 370 <br>
369 371 <br>
370 372 <div class="container">
371   - <div class="row">
372   - <?php
373   - echo GridView::widget(
374   - [
375   - 'dataProvider' => $dataProvider,
376   - 'rowOptions' => function($model) {
377   - if ($model->removed) {
378   - return [ 'class' => 'danger' ];
379   - } else {
380   - return [];
381   - }
382   - },
383   - 'layout' => '{items}{pager}',
384   - 'columns' => [
385   - [
386   - 'class' => SerialColumn::className(),
387   - ],
388   - 'sku',
389   - [
390   - 'attribute' => 'product_name',
391   - 'content' => function($model) {
392   - if (!empty($model->product_name)) {
  373 + <div class="row">
  374 + <?php
  375 + echo GridView::widget(
  376 + [
  377 + 'dataProvider' => $dataProvider,
  378 + 'rowOptions' => function ($model) {
  379 + if ($model->removed) {
  380 + return [ 'class' => 'danger' ];
  381 + } else {
  382 + return [];
  383 + }
  384 + },
  385 + 'layout' => '{items}{pager}',
  386 + 'columns' => [
  387 + [
  388 + 'class' => SerialColumn::className(),
  389 + ],
  390 + 'sku',
  391 + [
  392 + 'attribute' => 'product_name',
  393 + 'content' => function ($model) {
  394 + if (!empty( $model->product_name )) {
  395 +
  396 + if (empty( $model->productVariant )) {
  397 + return '';
  398 + }
  399 +
  400 + return Html::a(
  401 + StringHelper::truncate($model->product_name, 10, '...'),
  402 + '#',
  403 + [
  404 + 'onclick' => 'event.preventDefault();',
  405 + 'data-toggle' => 'popover',
  406 + 'data-placement' => 'right',
  407 + 'data-html' => 'true',
  408 + 'data-content' => Html::img(
  409 + $model->productVariant->imageUrl,
  410 + [
  411 + 'class' => 'img-rounded',
  412 + ]
  413 + ) . Html::tag('p', $model->product_name),
  414 + ]
  415 + );
  416 + } else {
  417 + return '';
  418 + }
  419 + },
  420 + ],
  421 + [
  422 + 'attribute' => 'productVariant.product.brand.lang.title',
  423 + 'label' => 'Брэнд',
  424 + ],
  425 + [
  426 + 'attribute' => 'productVariant.lang.title',
  427 + 'label' => \Yii::t('app', 'Цвет'),
  428 + 'content' => function ($model) {
393 429  
394   - if (empty($model->productVariant)) {
  430 + if (empty( $model->productVariant )) {
395 431 return '';
396   - }
397   -
398   - return Html::a(
399   - StringHelper::truncate($model->product_name, 10, '...'),
400   - '#',
401   - [
402   - 'onclick' => 'event.preventDefault();',
403   - 'data-toggle' => 'popover',
404   - 'data-placement' => 'right',
405   - 'data-html' => 'true',
406   - 'data-content' => Html::img(
407   - $model->productVariant->imageUrl,
408   - [
409   - 'class' => 'img-rounded',
410   - ]
411   - ) . Html::tag('p', $model->product_name),
412   - ]
413   - );
414   - } else {
415   - return '';
416   - }
417   - },
418   - ],
419   - [
420   - 'attribute' => 'productVariant.product.brand.lang.title',
421   - 'label' => 'Брэнд',
422   - ],
423   - [
424   - 'attribute' => 'productVariant.lang.title',
425   - 'label' => \Yii::t('app', 'Цвет'),
426   - 'content' => function($model) {
427   -
428   - if (empty($model->productVariant)) {
429   - return '';
430   - }
431   -
432   - if (preg_match('@.*\.(png|jpg|gif)@i', $model->productVariant->lang->title)) {
433   - return '';
434   - } else {
435   - return $model->productVariant->lang->title;
436   - }
437   - },
438   - ],
439   - [
440   - 'attribute' => 'productVariant.size',
441   - 'label' => 'Размер',
442   - ],
443   - 'price',
444   - [
445   - 'class' => 'kartik\grid\EditableColumn',
446   - 'attribute' => 'count',
447   - 'editableOptions' => [
448   - 'header' => \Yii::t('app', 'Количество'),
449   - 'inputType' => kartik\editable\Editable::INPUT_SPIN,
450   - 'options' => [
451   - 'pluginOptions' => [
452   - 'min' => 0,
453   - 'max' => 5000,
454   - ],
455   - ],
456   - 'pluginEvents' => [
457   - 'editableSuccess' => 'function(event) { $.pjax.reload({container:"#order-products-grid"}); }',
458   - ],
459   - ],
460   - 'format' => [
461   - 'decimal',
462   - 0,
463   - ],
464   - 'pageSummary' => false,
465   - ],
466   - 'sum_cost',
467   - [
468   - 'class' => 'kartik\grid\EditableColumn',
469   - 'attribute' => 'booking',
470   - 'editableOptions' => [
471   - 'header' => \Yii::t('app', 'Бронь'),
472   - 'inputType' => kartik\editable\Editable::INPUT_TEXT,
473   - 'options' => [
474   - 'class' => 'booking-typeahead',
475   - 'pluginOptions' => [
476   - 'min' => 0,
477   - 'max' => 20,
478   - ],
479   - ],
480   - 'pluginEvents' => [
481   - 'editableSuccess' => 'function(event) { $.pjax.reload({container:"#order-products-grid"}); }',
482   - ],
483   - ],
484   - 'format' => [
485   - 'text',
486   - ],
487   - 'pageSummary' => false,
488   - ],
489   - [
490   - 'class' => 'kartik\grid\EditableColumn',
491   - 'attribute' => 'status',
492   - 'editableOptions' => [
493   - 'header' => \Yii::t('app', 'Статус'),
494   - 'inputType' => kartik\editable\Editable::INPUT_TEXT,
495   - 'options' => [
496   - 'class' => 'status-typeahead',
497   - 'pluginOptions' => [
498   - 'min' => 0,
499   - 'max' => 20,
500   - ],
501   - ],
502   - 'pluginEvents' => [
503   - 'editableSuccess' => 'function(event) { $.pjax.reload({container:"#order-products-grid"}); }',
504   - ],
505   - ],
506   - 'format' => [
507   - 'text',
508   - ],
509   - 'pageSummary' => false,
510   - ],
511   - [
512   - 'class' => 'kartik\grid\EditableColumn',
513   - 'attribute' => 'return',
514   - 'editableOptions' => [
515   - 'header' => \Yii::t('app', 'Возврат'),
516   - 'inputType' => kartik\editable\Editable::INPUT_CHECKBOX,
517   - 'options' => [],
518   - 'pluginEvents' => [
519   - 'editableSuccess' => 'function(event) { $.pjax.reload({container:"#order-products-grid"}); }',
520   - ],
521   - ],
522   - 'format' => [
523   - 'boolean',
524   - ],
525   - 'pageSummary' => false,
526   - ],
527   - [
528   - 'content' => function($model) {
529   -
530   - if (empty($model->productVariant)) {
531   - return '<i class="glyphicon glyphicon-remove"></i>';
532   - }
533   -
534   - $content = '<table class="table"><tbody><tr><th>Склад</th><th>кол.</th></tr>';
535   - foreach ($model->productVariant->variantStocks as $stock) {
536   - $content .= '<tr><td>' . $stock->stock->title . '</td><td>' . $stock->quantity . '</td></tr>';
537   - }
538   - return Html::a(
539   - '<i class="glyphicon glyphicon-home"></i>',
540   - '#',
541   - [
542   - 'onclick' => 'event.preventDefault();',
543   - 'data-toggle' => 'popover',
544   - 'data-placement' => 'left',
545   - 'data-html' => 'true',
546   - 'data-content' => $content . '</tbody></table>',
547   - ]
548   - );
549   - },
550   - ],
551   - [
552   - 'class' => 'yii\grid\ActionColumn',
553   - 'template' => '{delete}',
554   - 'buttons' => [
555   - 'delete' => function($url, $product) {
556   - if ($product->removed) {
557   - return '';
558   - } else {
559   - return Html::a(
560   - Html::tag('span', '', [ 'class' => 'glyphicon glyphicon-trash' ]),
561   - [
562   - 'delete-product',
563   - 'id' => $product->id,
564   - ],
565   - [
566   - 'class' => 'delete-button',
567   - // 'data' => [
568   - // 'confirm' => 'Вы уверены, что хотите удалить этот элемент?',
569   - // 'method' => 'GET',
570   - // ],
571   - ]
572   - );
573   - }
574   - },
575   - ],
576   - ],
577   - ],
578   - 'responsive' => true,
579   - 'hover' => true,
580   - 'pjax' => true,
581   - 'pjaxSettings' => [
582   - 'options' => [
583   - 'scrollTo' => 'false',
584   - 'id' => 'order-products-grid',
585   - ],
586   - ],
587   - ]
588   - );
589   - ?>
590   - </div>
  432 + }
  433 +
  434 + if (preg_match('@.*\.(png|jpg|gif)@i', $model->productVariant->lang->title)) {
  435 + return '';
  436 + } else {
  437 + return $model->productVariant->lang->title;
  438 + }
  439 + },
  440 + ],
  441 + [
  442 + 'attribute' => 'productVariant.size',
  443 + 'label' => 'Размер',
  444 + ],
  445 + 'price',
  446 + [
  447 + 'class' => 'kartik\grid\EditableColumn',
  448 + 'attribute' => 'count',
  449 + 'editableOptions' => [
  450 + 'header' => \Yii::t('app', 'Количество'),
  451 + 'inputType' => kartik\editable\Editable::INPUT_SPIN,
  452 + 'options' => [
  453 + 'pluginOptions' => [
  454 + 'min' => 0,
  455 + 'max' => 5000,
  456 + ],
  457 + ],
  458 + 'pluginEvents' => [
  459 + 'editableSuccess' => 'function(event) { $.pjax.reload({container:"#order-products-grid"}); }',
  460 + ],
  461 + ],
  462 + 'format' => [
  463 + 'decimal',
  464 + 0,
  465 + ],
  466 + 'pageSummary' => false,
  467 + ],
  468 + 'sum_cost',
  469 + [
  470 + 'class' => 'kartik\grid\EditableColumn',
  471 + 'attribute' => 'booking',
  472 + 'editableOptions' => [
  473 + 'header' => \Yii::t('app', 'Бронь'),
  474 + 'inputType' => kartik\editable\Editable::INPUT_TEXT,
  475 + 'options' => [
  476 + 'class' => 'booking-typeahead',
  477 + 'pluginOptions' => [
  478 + 'min' => 0,
  479 + 'max' => 20,
  480 + ],
  481 + ],
  482 + 'pluginEvents' => [
  483 + 'editableSuccess' => 'function(event) { $.pjax.reload({container:"#order-products-grid"}); }',
  484 + ],
  485 + ],
  486 + 'format' => [
  487 + 'text',
  488 + ],
  489 + 'pageSummary' => false,
  490 + ],
  491 + [
  492 + 'class' => 'kartik\grid\EditableColumn',
  493 + 'attribute' => 'status',
  494 + 'editableOptions' => [
  495 + 'header' => \Yii::t('app', 'Статус'),
  496 + 'inputType' => kartik\editable\Editable::INPUT_TEXT,
  497 + 'options' => [
  498 + 'class' => 'status-typeahead',
  499 + 'pluginOptions' => [
  500 + 'min' => 0,
  501 + 'max' => 20,
  502 + ],
  503 + ],
  504 + 'pluginEvents' => [
  505 + 'editableSuccess' => 'function(event) { $.pjax.reload({container:"#order-products-grid"}); }',
  506 + ],
  507 + ],
  508 + 'format' => [
  509 + 'text',
  510 + ],
  511 + 'pageSummary' => false,
  512 + ],
  513 + [
  514 + 'class' => 'kartik\grid\EditableColumn',
  515 + 'attribute' => 'return',
  516 + 'editableOptions' => [
  517 + 'header' => \Yii::t('app', 'Возврат'),
  518 + 'inputType' => kartik\editable\Editable::INPUT_CHECKBOX,
  519 + 'options' => [],
  520 + 'pluginEvents' => [
  521 + 'editableSuccess' => 'function(event) { $.pjax.reload({container:"#order-products-grid"}); }',
  522 + ],
  523 + ],
  524 + 'format' => [
  525 + 'boolean',
  526 + ],
  527 + 'pageSummary' => false,
  528 + ],
  529 + [
  530 + 'content' => function ($model) {
  531 +
  532 + if (empty( $model->productVariant )) {
  533 + return '<i class="glyphicon glyphicon-remove"></i>';
  534 + }
  535 +
  536 + $content = '<table class="table"><tbody><tr><th>Склад</th><th>кол.</th></tr>';
  537 + foreach ($model->productVariant->variantStocks as $stock) {
  538 + $content .= '<tr><td>' . $stock->stock->title . '</td><td>' . $stock->quantity . '</td></tr>';
  539 + }
  540 + return Html::a(
  541 + '<i class="glyphicon glyphicon-home"></i>',
  542 + '#',
  543 + [
  544 + 'onclick' => 'event.preventDefault();',
  545 + 'data-toggle' => 'popover',
  546 + 'data-placement' => 'left',
  547 + 'data-html' => 'true',
  548 + 'data-content' => $content . '</tbody></table>',
  549 + ]
  550 + );
  551 + },
  552 + ],
  553 + [
  554 + 'class' => 'yii\grid\ActionColumn',
  555 + 'template' => '{delete}',
  556 + 'buttons' => [
  557 + 'delete' => function ($url, $product) {
  558 + if ($product->removed) {
  559 + return '';
  560 + } else {
  561 + return Html::a(
  562 + Html::tag('span', '', [ 'class' => 'glyphicon glyphicon-trash' ]),
  563 + [
  564 + 'delete-product',
  565 + 'id' => $product->id,
  566 + ],
  567 + [
  568 + 'class' => 'delete-button',
  569 + // 'data' => [
  570 + // 'confirm' => 'Вы уверены, что хотите удалить этот элемент?',
  571 + // 'method' => 'GET',
  572 + // ],
  573 + ]
  574 + );
  575 + }
  576 + },
  577 + ],
  578 + ],
  579 + ],
  580 + 'responsive' => true,
  581 + 'hover' => true,
  582 + 'pjax' => true,
  583 + 'pjaxSettings' => [
  584 + 'options' => [
  585 + 'scrollTo' => 'false',
  586 + 'id' => 'order-products-grid',
  587 + ],
  588 + ],
  589 + ]
  590 + );
  591 + ?>
  592 + </div>
591 593 </div>
592 594 <div class="container">
593 595 <?php Pjax::begin([ 'id' => 'total-cost' ]); ?>
594   - <h2>Сумма заказа : <span class="label label-success"><?php echo $model->total; ?><?php echo \Yii::t(
595   - 'app',
596   - 'грн'
597   - ) ?></span></h2>
  596 + <h2>Сумма заказа : <span class="label label-success"><?php echo $model->total; ?><?php echo \Yii::t(
  597 + 'app',
  598 + 'грн'
  599 + ) ?></span></h2>
598 600 <?php Pjax::end(); ?>
599 601 </div>
600 602 <div class="container">
601   - <div class="row">
602   - <?php $newProductForm = ActiveForm::begin(
603   - [
604   - 'action' => yii\helpers\Url::to([ 'add' ]),
605   - 'id' => 'add-product-form',
606   - ]
607   - );
608   - $newOrderProduct = new OrderProduct();
609   - ?>
610   - <div class="col-md-8">
611   - <?php echo $newProductForm->field($newOrderProduct, 'id')
612   - ->widget(
613   - Select2::className(),
614   - [
615   - 'options' => [ 'placeholder' => 'Search for a product ...' ],
616   - 'pluginOptions' => [
617   - 'allowClear' => true,
618   - 'disabled' => $model->isNewRecord ? true : false,
619   - 'minimumInputLength' => 3,
620   - 'language' => [
621   - 'errorLoading' => new JsExpression(
622   - "function () { return 'Waiting for results...'; }"
  603 + <div class="row">
  604 + <?php $newProductForm = ActiveForm::begin(
  605 + [
  606 + 'action' => yii\helpers\Url::to([ 'add' ]),
  607 + 'id' => 'add-product-form',
  608 + ]
  609 + );
  610 + $newOrderProduct = new OrderProduct();
  611 + ?>
  612 + <div class="col-md-8">
  613 + <?php echo $newProductForm->field($newOrderProduct, 'id')
  614 + ->widget(
  615 + Select2::className(),
  616 + [
  617 + 'options' => [ 'placeholder' => 'Search for a product ...' ],
  618 + 'pluginOptions' => [
  619 + 'allowClear' => true,
  620 + 'disabled' => $model->isNewRecord ? true : false,
  621 + 'minimumInputLength' => 3,
  622 + 'language' => [
  623 + 'errorLoading' => new JsExpression(
  624 + "function () { return 'Waiting for results...'; }"
  625 + ),
  626 + ],
  627 + 'ajax' => [
  628 + 'url' => \yii\helpers\Url::to([ 'find-product' ]),
  629 + 'dataType' => 'json',
  630 + 'data' => new JsExpression(
  631 + 'function(params) { return {q:params.term}; }'
  632 + ),
  633 + ],
  634 + 'escapeMarkup' => new JsExpression(
  635 + 'function (markup) { return markup; }'
623 636 ),
624   - ],
625   - 'ajax' => [
626   - 'url' => \yii\helpers\Url::to([ 'find-product' ]),
627   - 'dataType' => 'json',
628   - 'data' => new JsExpression(
629   - 'function(params) { return {q:params.term}; }'
  637 + 'templateResult' => new JsExpression(
  638 + 'function(data) { return data.sku; }'
  639 + ),
  640 + 'templateSelection' => new JsExpression(
  641 + 'function (data) { return data.sku; }'
630 642 ),
631 643 ],
632   - 'escapeMarkup' => new JsExpression(
633   - 'function (markup) { return markup; }'
634   - ),
635   - 'templateResult' => new JsExpression(
636   - 'function(data) { return data.sku; }'
637   - ),
638   - 'templateSelection' => new JsExpression(
639   - 'function (data) { return data.sku; }'
640   - ),
641   - ],
642   - ]
643   - )
644   - ->label('Артикул');
645   -
646   - ?>
647   - </div>
648   - <div class="col-md-2">
649   - <?php echo $newProductForm->field(
650   - $newOrderProduct,
651   - 'count'
652   - )
653   - ->input(
654   - 'number',
  644 + ]
  645 + )
  646 + ->label('Артикул');
  647 +
  648 + ?>
  649 + </div>
  650 + <div class="col-md-2">
  651 + <?php echo $newProductForm->field(
  652 + $newOrderProduct,
  653 + 'count'
  654 + )
  655 + ->input(
  656 + 'number',
  657 + [
  658 + 'disabled' => $model->isNewRecord ? true : false,
  659 + ]
  660 + ); ?>
  661 + </div>
  662 + <div class="col-md-2" style="margin-top: 23px">
  663 + <?php echo Html::submitButton(
  664 + \Yii::t('app', 'Добавить'),
  665 + [
  666 + 'class' => $model->isNewRecord ? 'btn btn-primary disabled' : 'btn btn-primary',
  667 + ]
  668 + ) ?>
  669 + </div>
  670 + <?php echo $newProductForm->field($newOrderProduct, 'order_id')
  671 + ->hiddenInput(
655 672 [
656   - 'disabled' => $model->isNewRecord ? true : false,
  673 + 'value' => $model->id,
657 674 ]
658   - ); ?>
  675 + )
  676 + ->label(false) ?>
  677 + <?php ActiveForm::end(); ?>
659 678 </div>
660   - <div class="col-md-2" style="margin-top: 23px">
661   - <?php echo Html::submitButton(
662   - \Yii::t('app', 'Добавить'),
  679 +
  680 + <br>
  681 + <div class="row">
  682 + <?= Html::button(
  683 + $model->isNewRecord ? \Yii::t('app', 'Создать') : \Yii::t('app', 'Сохранить'),
  684 + [
  685 + 'class' => $model->isNewRecord ? 'btn btn-success btn-lg' : 'btn btn-primary btn-lg',
  686 + 'id' => 'page-submit',
  687 + ]
  688 + ) ?>
  689 + <?= Html::a(
  690 + \Yii::t('app', 'Печать'),
  691 + yii\helpers\Url::to(
  692 + [
  693 + 'order/print',
  694 + 'order_id' => $model->id,
  695 + ]
  696 + ),
  697 + [
  698 + 'class' => $model->isNewRecord ? 'btn btn-info disabled btn-lg' : 'btn btn-info btn-lg',
  699 + 'target' => '_blank',
  700 + ]
  701 + ) ?>
  702 + <?= Html::a(
  703 + \Yii::t('app', 'Выйти'),
  704 + yii\helpers\Url::to(
  705 + [
  706 + 'close-order',
  707 + 'id' => $model->id,
  708 + ]
  709 + ),
663 710 [
664   - 'class' => $model->isNewRecord ? 'btn btn-primary disabled' : 'btn btn-primary',
  711 + 'class' => $model->isNewRecord ? 'btn btn-info disabled btn-lg' : 'btn btn-info btn-lg',
665 712 ]
666 713 ) ?>
667 714 </div>
668   - <?php echo $newProductForm->field($newOrderProduct, 'order_id')
669   - ->hiddenInput(
670   - [
671   - 'value' => $model->id,
672   - ]
673   - )
674   - ->label(false) ?>
675   - <?php ActiveForm::end(); ?>
676   - </div>
677   -
678   - <br>
679   - <div class="row">
680   - <?= Html::button(
681   - $model->isNewRecord ? \Yii::t('app', 'Создать') : \Yii::t('app', 'Сохранить'),
682   - [
683   - 'class' => $model->isNewRecord ? 'btn btn-success btn-lg' : 'btn btn-primary btn-lg',
684   - 'id' => 'page-submit',
685   - ]
686   - ) ?>
687   - <?= Html::a(
688   - \Yii::t('app', 'Печать'),
689   - yii\helpers\Url::to(
690   - [
691   - 'order/print',
692   - 'order_id' => $model->id,
693   - ]
694   - ),
695   - [
696   - 'class' => $model->isNewRecord ? 'btn btn-info disabled btn-lg' : 'btn btn-info btn-lg',
697   - 'target' => '_blank',
698   - ]
699   - ) ?>
700   - <?= Html::a(
701   - \Yii::t('app', 'Выйти'),
702   - yii\helpers\Url::to(
703   - [
704   - 'close-order',
705   - 'id' => $model->id,
706   - ]
707   - ),
708   - [
709   - 'class' => $model->isNewRecord ? 'btn btn-info disabled btn-lg' : 'btn btn-info btn-lg',
710   - ]
711   - ) ?>
712   - </div>
713 715  
714 716 </div>
715 717 <br>
... ...
views/order/_log_item.php 0 → 100644
  1 +<?php
  2 + /**
  3 + * @var OrderLog $model
  4 + * @var Order $order
  5 + */
  6 + use artweb\artbox\ecommerce\models\Order;
  7 + use artweb\artbox\ecommerce\models\OrderLog;
  8 + use yii\helpers\Html;
  9 + use yii\helpers\Json;
  10 +
  11 +?>
  12 +
  13 +<!-- timeline item -->
  14 +<li>
  15 + <!-- timeline icon -->
  16 + <i class="fa fa-user bg-orange"></i>
  17 + <div class="timeline-item">
  18 + <span class="time"><i class="fa fa-calendar"></i> <?= Yii::$app->formatter->asDatetime($model->created_at) ?></span>
  19 +
  20 + <h3 class="timeline-header">Пользователь: <span class="text-orange"><?= $model->user->username ?></span></h3>
  21 +
  22 + <div class="timeline-body">
  23 + <table class="table table-bordered table-striped">
  24 + <tr>
  25 + <th>Поле</th>
  26 + <th>Старое значение</th>
  27 + <th>Новое значение</th>
  28 + </tr>
  29 + <?php
  30 + foreach (Json::decode($model->data) as $key => $item) {
  31 + echo Html::tag(
  32 + 'tr',
  33 + Html::tag('td', $order->attributeLabels()[ $key ]) . Html::tag('td', $item[ 'old' ]) . Html::tag(
  34 + 'td',
  35 + $item[ 'new' ]
  36 + )
  37 + );
  38 + }
  39 + ?>
  40 + </table>
  41 + </div>
  42 +
  43 + </div>
  44 +</li>
... ...
views/order/_timeline_item.php 0 → 100644
  1 +<?php
  2 + /**
  3 + * @var OrderLabelHistory $model
  4 + */
  5 + use artweb\artbox\ecommerce\models\OrderLabelHistory;
  6 + use yii\helpers\Html;
  7 +
  8 +?>
  9 +
  10 +<!-- timeline item -->
  11 +<li>
  12 + <!-- timeline icon -->
  13 + <i class="fa fa-tag bg-blue"></i>
  14 + <div class="timeline-item">
  15 + <span class="time"><i class="fa fa-calendar"></i> <?=Yii::$app->formatter->asDatetime($model->created_at)?></span>
  16 +
  17 + <h3 class="timeline-header"><?=$model->label->lang->title?></h3>
  18 +
  19 + <div class="timeline-body">
  20 + <?php
  21 + if (empty($model->user)) {
  22 + echo Html::tag('p', 'Поступил с сайта', ['class' => 'text-green']);
  23 + } else {
  24 + echo 'Статус присвоил: ' . Html::tag('p', $model->user->username, [
  25 + 'class' => 'text-blue'
  26 + ]);
  27 + }
  28 + ?>
  29 + </div>
  30 +
  31 + </div>
  32 +</li>
... ...
views/order/index.php
... ... @@ -347,7 +347,7 @@ JS;
347 347 [
348 348 'class' => 'yii\grid\ActionColumn',
349 349 'template' => \Yii::$app->user->identity->isAdmin(
350   - ) ? '{view} {update} {delete}' : '{view} {update}',
  350 + ) ? '{history} {view} {update} {delete}' : '{view} {update}',
351 351 'buttons' => [
352 352 'update' => function($url, $model) {
353 353 return Html::a(
... ... @@ -359,6 +359,16 @@ JS;
359 359 ]
360 360 );
361 361 },
  362 + 'history' => function($url, $model) {
  363 + return Html::a(
  364 + Html::tag('span', '', [ 'class' => 'glyphicon glyphicon-time' ]),
  365 + ['log', 'id' => $model->id],
  366 + [
  367 + 'target' => '_blank',
  368 + 'data-pjax' => '0',
  369 + ]
  370 + );
  371 + },
362 372 ],
363 373 ],
364 374 ],
... ...
views/order/log.php 0 → 100755
  1 +<?php
  2 + use artweb\artbox\ecommerce\models\Order;
  3 + use yii\data\ActiveDataProvider;
  4 + use yii\web\View;
  5 + use yii\widgets\ListView;
  6 +
  7 + /**
  8 + * @var View $this
  9 + * @var ActiveDataProvider $logData
  10 + * @var Order $model
  11 + */
  12 +
  13 + $this->title = 'История заказа #' . $model->id;
  14 +
  15 + $this->params[ 'breadcrumbs' ][] = [
  16 + 'url' => yii\helpers\Url::to([ '/ecommerce/order/index' ]),
  17 + 'label' => \Yii::t('app', 'Заказы'),
  18 + ];
  19 + $this->params[ 'breadcrumbs' ][] = [
  20 + 'url' => yii\helpers\Url::to([ '/ecommerce/order/view', 'id' => $model->id ]),
  21 + 'label' => \Yii::t('app', 'Заказ #') . $model->id,
  22 + ];
  23 + $this->params[ 'breadcrumbs' ][] = $this->title;
  24 +
  25 +?>
  26 +
  27 +<div class="order-log">
  28 + <div class="box box-default">
  29 + <div class="box-header with-border">
  30 + <h3 class="box-title">История</h3>
  31 + <div class="box-body">
  32 +
  33 + <?php
  34 + echo ListView::widget(
  35 + [
  36 + 'dataProvider' => $logData,
  37 + 'layout' => '{items}',
  38 + 'itemView' => '_log_item',
  39 + 'itemOptions' => [
  40 + 'tag' => false,
  41 + ],
  42 + 'options' => [
  43 + 'tag' => $logData->totalCount == 0 ? 'div' : 'ul',
  44 + 'class' => $logData->totalCount == 0 ? 'list-view' : 'list-view timeline',
  45 + ],
  46 + 'viewParams' => [
  47 + 'order' => $model,
  48 + ],
  49 + 'emptyText' => 'У этого заказа пока нет истории',
  50 + 'emptyTextOptions' => [
  51 + 'class' => 'callout callout-info'
  52 + ],
  53 + ]
  54 + );
  55 + ?>
  56 +
  57 + </div><!-- /.box-body -->
  58 + </div><!-- /.box -->
  59 + </div>
... ...
views/order/update.php
... ... @@ -10,11 +10,15 @@
10 10 use yii\helpers\Html;
11 11 use yii\web\View;
12 12  
13   - $this->title = 'Заказ #' . $model->id;
  13 + $this->title = 'Обновить заказ #' . $model->id;
14 14 $this->params[ 'breadcrumbs' ][] = [
15 15 'url' => yii\helpers\Url::to([ '/ecommerce/order/index' ]),
16 16 'label' => \Yii::t('app', 'Заказы'),
17 17 ];
  18 + $this->params[ 'breadcrumbs' ][] = [
  19 + 'url' => yii\helpers\Url::to([ '/ecommerce/order/view', 'id' => $model->id, ]),
  20 + 'label' => \Yii::t('app', 'Заказ #') . $model->id,
  21 + ];
18 22 $this->params[ 'breadcrumbs' ][] = $this->title;
19 23  
20 24 $js = '
... ... @@ -54,7 +58,18 @@ $.ajax({
54 58 ';
55 59  
56 60 $this->registerJs($js, View::POS_READY);
57   -
  61 +
  62 + if (!empty(\Yii::$app->session->getFlash('label_update'))) {
  63 + $js = '
  64 +$.notify({
  65 + message: "Статус заказа обновлен"
  66 +}, {
  67 + type : "warning"
  68 +})
  69 +';
  70 + $this->registerJs($js, View::POS_READY);
  71 + }
  72 +
58 73 ?>
59 74 <div class="order-update">
60 75 <div class="container">
... ...
views/order/view.php
1 1 <?php
2 2  
3 3 use artweb\artbox\ecommerce\models\Order;
  4 + use common\components\CreditHelper;
4 5 use kartik\grid\GridView;
  6 + use yii\data\ActiveDataProvider;
  7 + use yii\grid\SerialColumn;
5 8 use yii\helpers\Html;
6 9 use yii\web\View;
7 10 use yii\widgets\DetailView;
  11 + use yii\widgets\ListView;
8 12  
9 13 /**
10   - * @var View $this
11   - * @var Order $model
  14 + * @var View $this
  15 + * @var Order $model
  16 + * @var ActiveDataProvider $products
  17 + * @var ActiveDataProvider $historyData
12 18 */
13 19  
14 20 $this->title = 'Заказ #' . $model->id;
... ... @@ -17,65 +23,184 @@
17 23 'url' => [ 'index' ],
18 24 ];
19 25 $this->params[ 'breadcrumbs' ][] = $this->title;
  26 +
  27 + if (empty($model->payment)) {
  28 + $payment = '';
  29 + } elseif ($model->payment == 10) {
  30 + $payment = Html::tag('h4', $model->orderPayment->lang->title, [ 'class' => 'text-navy' ]) . Html::beginTag(
  31 + 'table',
  32 + [ 'class' => 'table table-bordered' ]
  33 + ) . Html::tag(
  34 + 'tr',
  35 + Html::tag('td', $model->getAttributeLabel('credit_sum')) . Html::tag(
  36 + 'td',
  37 + CreditHelper::checkSum(
  38 + $model->credit_sum
  39 + )
  40 + )
  41 + ) . Html::tag(
  42 + 'tr',
  43 + Html::tag('td', $model->getAttributeLabel('credit_month')) . Html::tag(
  44 + 'td',
  45 + CreditHelper::checkMonth(
  46 + $model->credit_month
  47 + )
  48 + )
  49 + ) . Html::tag(
  50 + 'tr',
  51 + Html::tag('td', 'Кредит') . Html::tag('td', $model->total - $model->credit_sum)
  52 + ) . Html::tag(
  53 + 'tr',
  54 + Html::tag('td', 'Оплата в месяц') . Html::tag(
  55 + 'td',
  56 + CreditHelper::getCredit(
  57 + $model->total - $model->credit_sum,
  58 + $model->credit_month
  59 + ) . ' грн/мес'
  60 + )
  61 + ) . Html::endTag('table');
  62 + } else {
  63 + $payment = $model->orderPayment->lang->title;
  64 + }
  65 +
20 66 ?>
21 67 <div class="order-view">
22   -
23   - <h1><?= Html::encode($this->title) ?></h1>
24   -
25   - <p>
26   - <?= Html::a(
27   - 'Update',
28   - [
29   - 'update',
30   - 'id' => $model->id,
31   - ],
32   - [ 'class' => 'btn btn-primary' ]
33   - ) ?>
34   -
35   - </p>
36   -
37   - <?= DetailView::widget(
38   - [
39   - 'model' => $model,
40   - 'attributes' => [
41   - 'id',
42   - 'deadline',
43   - 'pay',
44   - 'reason',
45   - 'label',
46   - 'name',
47   - 'phone',
48   - 'email',
49   - 'comment',
50   - 'delivery',
51   - 'declaration',
52   - 'stock',
53   - 'payment',
54   - 'insurance',
55   - 'city',
56   - 'adress',
57   - 'body',
58   - 'check',
59   - 'sms',
60   - ],
61   - ]
62   - ) ?>
63   -
64   - <?php
65   - echo GridView::widget(
  68 +
  69 + <h1><?= Html::encode($this->title) ?></h1>
  70 +
  71 + <p>
  72 + <?= Html::a(
  73 + 'Обновить',
  74 + [
  75 + 'update',
  76 + 'id' => $model->id,
  77 + ],
  78 + [ 'class' => 'btn btn-primary' ]
  79 + ) ?>
  80 +
  81 + <?= Html::a(
  82 + 'История',
  83 + [
  84 + 'log',
  85 + 'id' => $model->id,
  86 + ],
  87 + [ 'class' => 'btn bg-orange' ]
  88 + ) ?>
  89 + </p>
  90 +
  91 + <div class="box box-default">
  92 + <div class="box-header with-border">
  93 + <h3 class="box-title">Данные заказа</h3>
  94 + <div class="box-tools pull-right">
  95 + <button class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
  96 + </div><!-- /.box-tools -->
  97 + </div><!-- /.box-header -->
  98 + <div class="box-body">
  99 + <?= DetailView::widget(
66 100 [
67   - 'dataProvider' => $products,
68   - 'columns' => [
  101 + 'model' => $model,
  102 + 'attributes' => [
69 103 'id',
70   - 'product_name',
  104 + 'deadline',
  105 + 'pay',
  106 + [
  107 + 'label' => 'Причина',
  108 + 'value' => empty($model->reason) ? '' : Order::REASONS[ $model->reason ],
  109 + ],
  110 + [
  111 + 'label' => 'Статус',
  112 + 'value' => empty($model->label) ? '' : $model->orderLabel->lang->title,
  113 + ],
71 114 'name',
72   - 'sku',
73   - 'price',
74   - 'count',
75   - 'sum_cost',
  115 + 'phone',
  116 + 'email',
  117 + 'comment',
  118 + [
  119 + 'label' => 'Способ доставки',
  120 + 'value' => empty($model->delivery) ? '' : $model->orderDelivery->lang->title,
  121 + ],
  122 + 'declaration',
  123 + 'stock',
  124 + [
  125 + 'label' => 'Способ оплаты',
  126 + 'value' => $payment,
  127 + 'format' => 'html',
  128 + ],
  129 + 'insurance',
  130 + 'city',
  131 + 'adress',
  132 + 'body',
  133 + 'check',
  134 + 'sms',
76 135 ],
77 136 ]
78   - );
79   - ?>
  137 + ) ?>
  138 + </div><!-- /.box-body -->
  139 + </div><!-- /.box -->
  140 +
  141 +
  142 + <div class="box box-default">
  143 + <div class="box-header with-border">
  144 + <h3 class="box-title">Товары</h3>
  145 + <div class="box-tools pull-right">
  146 + <button class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
  147 + </div><!-- /.box-tools -->
  148 + </div><!-- /.box-header -->
  149 + <div class="box-body">
  150 + <?php
  151 + echo GridView::widget(
  152 + [
  153 + 'dataProvider' => $products,
  154 + 'columns' => [
  155 + [
  156 + 'class' => SerialColumn::className(),
  157 + ],
  158 + 'product_name',
  159 + 'name',
  160 + 'sku',
  161 + 'price',
  162 + 'count',
  163 + 'sum_cost',
  164 + ],
  165 + ]
  166 + );
  167 + ?>
  168 + </div><!-- /.box-body -->
  169 + </div><!-- /.box -->
  170 +
  171 + <div class="box box-default">
  172 + <div class="box-header with-border">
  173 + <h3 class="box-title">История</h3>
  174 + <div class="box-tools pull-right">
  175 + <button class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
  176 + </div><!-- /.box-tools -->
  177 + </div><!-- /.box-header -->
  178 + <div class="box-body">
  179 +
  180 +
  181 + <?php
  182 + echo ListView::widget(
  183 + [
  184 + 'dataProvider' => $historyData,
  185 + 'layout' => '{items}',
  186 + 'itemView' => '_timeline_item',
  187 + 'itemOptions' => [
  188 + 'tag' => false,
  189 + ],
  190 + 'options' => [
  191 + 'tag' => $historyData->totalCount == 0 ? 'div' : 'ul',
  192 + 'class' => $historyData->totalCount == 0 ? 'list-view' : 'list-view timeline',
  193 + ],
  194 + 'emptyText' => 'У этого заказа пока нет истории',
  195 + 'emptyTextOptions' => [
  196 + 'class' => 'callout callout-info',
  197 + ],
  198 + ]
  199 + );
  200 + ?>
  201 +
  202 +
  203 + </div><!-- /.box-body -->
  204 + </div><!-- /.box -->
80 205  
81 206 </div>
... ...
views/statistics/index.php
1 1 <?php
2 2 use artweb\artbox\ecommerce\models\Label;
3 3 use artweb\artbox\ecommerce\models\Order;
  4 + use artweb\artbox\ecommerce\models\ProductVariant;
  5 + use common\models\User;
4 6 use kartik\daterange\DateRangePicker;
5 7 use kartik\grid\GridView;
6 8 use kartik\select2\Select2;
... ... @@ -14,19 +16,30 @@
14 16 /**
15 17 * @var View $this
16 18 * @var Label[] $labels
  19 + * @var User[] $managers
17 20 * @var array $labelStatistics
18 21 * @var array $rejectionStatistics
19 22 * @var ActiveDataProvider $dataProvider
20   - * @var array $labelChartData
  23 + * @var array $labelChartData1
  24 + * @var array $labelChartData2
  25 + * @var array $labelChartData3
  26 + * @var array $rejectChartData1
  27 + * @var array $rejectChartData2
21 28 * @var string $dateValue
22 29 * @var int | boolean $dataLabel
  30 + * @var int | boolean $dataManager
23 31 */
  32 +
  33 + $js = <<< JS
  34 +$('[data-toggle="popover"]').popover();
  35 +JS;
  36 + $this->registerJs($js, View::POS_READY);
24 37  
25 38 ?>
26 39  
27 40 <div class="box box-default">
28 41 <div class="box-header with-border">
29   - <h3 class="box-title">Collapsable</h3>
  42 + <h3 class="box-title">Поиск заказов</h3>
30 43 <div class="box-tools pull-right">
31 44 <button class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
32 45 </div><!-- /.box-tools -->
... ... @@ -37,7 +50,7 @@
37 50 'get'
38 51 ) ?>
39 52 <div class="row">
40   - <div class="col-md-4">
  53 + <div class="col-md-3">
41 54 <?= DateRangePicker::widget(
42 55 [
43 56 'name' => 'date_range',
... ... @@ -51,12 +64,12 @@
51 64 ]
52 65 ) ?>
53 66 </div>
54   - <div class="col-md-6">
  67 + <div class="col-md-4">
55 68 <?= Select2::widget(
56 69 [
57   - 'name' => 'label',
58   - 'value' => $dataLabel,
59   - 'data' => ArrayHelper::map(
  70 + 'name' => 'label',
  71 + 'value' => $dataLabel,
  72 + 'data' => ArrayHelper::map(
60 73 $labels,
61 74 function($model) {
62 75 return $model->id;
... ... @@ -65,18 +78,41 @@
65 78 return $model->lang->title;
66 79 }
67 80 ),
68   - 'options' => [
69   - 'placeholder' => 'Все',
  81 + 'options' => [
  82 + 'placeholder' => 'Все метки',
70 83 ],
71 84 'pluginOptions' => [
72   - 'allowClear' => true,
  85 + 'allowClear' => true,
  86 + ],
  87 + ]
  88 + ) ?>
  89 + </div>
  90 + <div class="col-md-3">
  91 + <?= Select2::widget(
  92 + [
  93 + 'name' => 'manager',
  94 + 'value' => $dataManager,
  95 + 'data' => ArrayHelper::map(
  96 + $managers,
  97 + function(User $model) {
  98 + return $model->id;
  99 + },
  100 + function(User $model) {
  101 + return $model->username;
  102 + }
  103 + ),
  104 + 'options' => [
  105 + 'placeholder' => 'Все менеджеры',
  106 + ],
  107 + 'pluginOptions' => [
  108 + 'allowClear' => true,
73 109 ],
74 110 ]
75 111 ) ?>
76 112 </div>
77 113 <div class="col-md-2">
78 114 <?= Html::submitButton(
79   - 'Go',
  115 + 'Поиск',
80 116 [
81 117 'class' => 'btn btn-success',
82 118 ]
... ... @@ -89,7 +125,7 @@
89 125  
90 126 <div class="box box-default">
91 127 <div class="box-header with-border">
92   - <h3 class="box-title">Метки, статистика за </h3>
  128 + <h3 class="box-title">Метки, статистика за <?= empty($dateValue) ? 'всё время' : $dateValue ?></h3>
93 129 <div class="box-tools pull-right">
94 130 <button class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
95 131 </div><!-- /.box-tools -->
... ... @@ -104,7 +140,15 @@
104 140 <td><b>Уникальных товаров, шт.</b></td>
105 141 </tr>
106 142 <?php
  143 + $total_count = 0;
  144 + $total_sum = 0;
  145 + $total_products = 0;
  146 + $total_unique = 0;
107 147 foreach ($labelStatistics as $name => $statistic) {
  148 + $total_count += $statistic[ 'count' ];
  149 + $total_sum += $statistic[ 'sum' ];
  150 + $total_products += $statistic[ 'products' ];
  151 + $total_unique += $statistic[ 'unique' ];
108 152 echo Html::tag(
109 153 'tr',
110 154 Html::tag('td', $name) . Html::tag('td', $statistic[ 'count' ]) . Html::tag(
... ... @@ -112,39 +156,116 @@
112 156 $statistic[ 'sum' ]
113 157 ) . Html::tag('td', $statistic[ 'products' ]) . Html::tag('td', $statistic[ 'unique' ])
114 158 );
115   - } ?>
  159 + }
  160 + ?>
  161 + <tr>
  162 + <td><b>Всего</b></td>
  163 + <td><b><?=$total_count?></b></td>
  164 + <td><b><?=$total_sum?></b></td>
  165 + <td><b><?=$total_products?></b></td>
  166 + <td><b><?=$total_unique?></b></td>
  167 + </tr>
116 168 </table>
117   -
118   - <?= ChartJs::widget(
119   - [
120   - 'type' => 'bar',
121   - 'options' => [
122   - 'height' => 200,
123   - 'width' => 600,
124   - ],
125   - 'data' => $labelChartData,
126   - 'clientOptions' => [
127   - 'title' => [
128   - 'display' => true,
129   - 'text' => 'Custom Chart Title',
130   - ],
131   - 'scales' => [
132   - 'xAxes' => [
133   - [
134   - 'display' => false,
135   - ],
136   - ],
137   - ],
138   - ],
139   - ]
140   - ); ?>
141   -
142 169 </div><!-- /.box-body -->
  170 +
  171 + <div class="box-footer">
  172 + <div class="nav-tabs-custom">
  173 +
  174 + <!-- Nav tabs -->
  175 + <ul class="nav nav-tabs" role="tablist">
  176 + <li role="presentation" class="active">
  177 + <a href="#home" aria-controls="home" role="tab" data-toggle="tab">Заказы</a></li>
  178 + <li role="presentation"><a href="#profile" aria-controls="profile" role="tab" data-toggle="tab">Сумма</a></li>
  179 + <li role="presentation"><a href="#messages" aria-controls="messages" role="tab" data-toggle="tab">Товары</a>
  180 + </li>
  181 + </ul>
  182 +
  183 + <!-- Tab panes -->
  184 + <div class="tab-content">
  185 + <div role="tabpanel" class="tab-pane active" id="home">
  186 + <?= ChartJs::widget(
  187 + [
  188 + 'type' => 'bar',
  189 + 'options' => [
  190 + 'height' => 200,
  191 + 'width' => 600,
  192 + ],
  193 + 'data' => $labelChartData1,
  194 + 'clientOptions' => [
  195 + 'title' => [
  196 + 'display' => true,
  197 + 'text' => 'Статистика меток',
  198 + ],
  199 + 'scales' => [
  200 + 'xAxes' => [
  201 + [
  202 + 'display' => false,
  203 + ],
  204 + ],
  205 + ],
  206 + ],
  207 + ]
  208 + ); ?>
  209 + </div>
  210 + <div role="tabpanel" class="tab-pane" id="profile">
  211 + <?= ChartJs::widget(
  212 + [
  213 + 'type' => 'bar',
  214 + 'options' => [
  215 + 'height' => 200,
  216 + 'width' => 600,
  217 + ],
  218 + 'data' => $labelChartData2,
  219 + 'clientOptions' => [
  220 + 'title' => [
  221 + 'display' => true,
  222 + 'text' => 'Статистика меток',
  223 + ],
  224 + 'scales' => [
  225 + 'xAxes' => [
  226 + [
  227 + 'display' => false,
  228 + ],
  229 + ],
  230 + ],
  231 + ],
  232 + ]
  233 + ); ?>
  234 + </div>
  235 + <div role="tabpanel" class="tab-pane" id="messages">
  236 + <?= ChartJs::widget(
  237 + [
  238 + 'type' => 'bar',
  239 + 'options' => [
  240 + 'height' => 200,
  241 + 'width' => 600,
  242 + ],
  243 + 'data' => $labelChartData3,
  244 + 'clientOptions' => [
  245 + 'title' => [
  246 + 'display' => true,
  247 + 'text' => 'Статистика меток',
  248 + ],
  249 + 'scales' => [
  250 + 'xAxes' => [
  251 + [
  252 + 'display' => false,
  253 + ],
  254 + ],
  255 + ],
  256 + ],
  257 + ]
  258 + ); ?>
  259 + </div>
  260 + </div>
  261 +
  262 + </div>
  263 + </div>
143 264 </div><!-- /.box -->
144 265  
145 266 <div class="box box-default">
146 267 <div class="box-header with-border">
147   - <h3 class="box-title">Метки, статистика за </h3>
  268 + <h3 class="box-title">Причины отказа, статистика за <?= empty($dateValue) ? 'всё время' : $dateValue ?></h3>
148 269 <div class="box-tools pull-right">
149 270 <button class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
150 271 </div><!-- /.box-tools -->
... ... @@ -169,77 +290,205 @@
169 290 ?>
170 291 </table>
171 292 </div><!-- /.box-body -->
172   -</div><!-- /.box -->
173 293  
174   -<?= GridView::widget(
175   - [
176   - 'dataProvider' => $dataProvider,
177   - 'columns' => [
178   - 'id',
179   - 'created_at:datetime',
180   - 'name',
181   - 'city',
182   - [
183   - 'attribute' => 'orderLabel.label',
184   - 'label' => 'Метка',
185   - ],
186   - 'total',
187   - [
188   - 'attribute' => 'reason',
189   - 'content' => function($model) {
190   - /**
191   - * @var Order $model
192   - */
193   - if (empty($model->reason)) {
194   - return '';
195   - } else {
196   - return Order::REASONS[ $model->reason ];
197   - }
198   - },
199   - ],
200   - [
201   - 'attribute' => 'manager.username',
202   - 'label' => 'Менеджер',
203   - ],
204   - [
205   - 'attribute' => 'body',
206   - 'content' => function($model) {
207   - /**
208   - * @var Order $model
209   - */
210   - if (empty($model->body)) {
211   - return '';
212   - } else {
213   - return Html::a(
214   - StringHelper::truncate($model->body, 10, '...'),
215   - '#',
216   - [
217   - 'data-toggle' => 'tooltip',
218   - 'title' => $model->body,
219   - 'onclick' => 'event.preventDefault();',
220   - ]
221   - );
222   - }
223   - },
224   - ],
225   - [
226   - 'content' => function($model) {
227   - /**
228   - * @var Order $model
229   - */
230   - return Html::a(
231   - Html::tag('i', '', [ 'class' => 'glyphicon glyphicon-eye-open' ]),
232   - [
233   - '/ecommerce/order/view',
234   - 'id' => $model->id,
  294 + <div class="box-footer">
  295 + <div class="nav-tabs-custom">
  296 +
  297 + <!-- Nav tabs -->
  298 + <ul class="nav nav-tabs" role="tablist">
  299 + <li role="presentation" class="active">
  300 + <a href="#count" aria-controls="count" role="tab" data-toggle="tab">Заказы</a></li>
  301 + <li role="presentation"><a href="#sum" aria-controls="sum" role="tab" data-toggle="tab">Сумма</a></li>
  302 + </ul>
  303 +
  304 + <!-- Tab panes -->
  305 + <div class="tab-content">
  306 + <div role="tabpanel" class="tab-pane active" id="count">
  307 + <?= ChartJs::widget(
  308 + [
  309 + 'type' => 'bar',
  310 + 'options' => [
  311 + 'height' => 200,
  312 + 'width' => 600,
  313 + ],
  314 + 'data' => $rejectChartData1,
  315 + 'clientOptions' => [
  316 + 'title' => [
  317 + 'display' => true,
  318 + 'text' => 'Статистика отказов',
  319 + ],
  320 + 'scales' => [
  321 + 'xAxes' => [
  322 + [
  323 + 'display' => false,
  324 + ],
  325 + ],
  326 + ],
  327 + ],
  328 + ]
  329 + ); ?>
  330 + </div>
  331 + <div role="tabpanel" class="tab-pane" id="sum">
  332 + <?= ChartJs::widget(
  333 + [
  334 + 'type' => 'bar',
  335 + 'options' => [
  336 + 'height' => 200,
  337 + 'width' => 600,
  338 + ],
  339 + 'data' => $rejectChartData2,
  340 + 'clientOptions' => [
  341 + 'title' => [
  342 + 'display' => true,
  343 + 'text' => 'Статистика отказов',
235 344 ],
236   - [
237   - 'target' => '_blank',
238   - 'data-pjax' => '0',
239   - ]
240   - );
241   - },
242   - ],
243   - ],
244   - ]
245   -) ?>
  345 + 'scales' => [
  346 + 'xAxes' => [
  347 + [
  348 + 'display' => false,
  349 + ],
  350 + ],
  351 + ],
  352 + ],
  353 + ]
  354 + ); ?>
  355 + </div>
  356 + </div>
  357 +
  358 + </div>
  359 + </div>
  360 +
  361 +</div><!-- /.box -->
  362 +
  363 +
  364 +<div class="box box-default">
  365 + <div class="box-header with-border">
  366 + <h3 class="box-title">Заказы</h3>
  367 + <div class="box-tools pull-right">
  368 + <button class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
  369 + </div><!-- /.box-tools -->
  370 + </div><!-- /.box-header -->
  371 + <div class="box-body table-responsive">
  372 + <?= GridView::widget(
  373 + [
  374 + 'dataProvider' => $dataProvider,
  375 + 'columns' => [
  376 + 'id',
  377 + [
  378 + 'label' => 'Дата добавления',
  379 + 'content' => function(Order $model) {
  380 + return \Yii::$app->formatter->asDate($model->created_at) .
  381 + '<br>' . \Yii::$app->formatter->asTime($model->created_at);
  382 + },
  383 + ],
  384 + 'name',
  385 + [
  386 + 'label' => 'Товары',
  387 + 'content' => function(Order $model) {
  388 + if (empty($model->products)) {
  389 + return '';
  390 + } else {
  391 + $content = '';
  392 + $i = 0;
  393 + foreach ($model->products as $product) {
  394 + if(empty($product->productVariant)){
  395 + $image = '';
  396 + } else {
  397 + $image = $product->productVariant->imageUrl;
  398 + }
  399 + $i++;
  400 + $content .= Html::a(
  401 + $product->sku,
  402 + '#',
  403 + [
  404 + 'onclick' => 'event.preventDefault();',
  405 + 'data-toggle' => 'popover',
  406 + 'data-placement' => 'right',
  407 + 'data-html' => 'true',
  408 + 'data-content' => Html::img(
  409 + $image,
  410 + [
  411 + 'class' => 'img-rounded',
  412 + ]
  413 + ) . Html::tag('p', $product->product_name),
  414 + ]
  415 + );
  416 + if ($i != count($model->products)) {
  417 + $content .= ', ';
  418 + }
  419 + if ($i % 2 == 0) {
  420 + $content .= '<br>';
  421 + }
  422 + }
  423 + return $content;
  424 + }
  425 + },
  426 + ],
  427 + 'city',
  428 + [
  429 + 'attribute' => 'orderLabel.label',
  430 + 'label' => 'Метка',
  431 + ],
  432 + 'total',
  433 + [
  434 + 'attribute' => 'reason',
  435 + 'content' => function($model) {
  436 + /**
  437 + * @var Order $model
  438 + */
  439 + if (empty($model->reason)) {
  440 + return '';
  441 + } else {
  442 + return Order::REASONS[ $model->reason ];
  443 + }
  444 + },
  445 + ],
  446 + [
  447 + 'attribute' => 'manager.username',
  448 + 'label' => 'Менеджер',
  449 + ],
  450 + [
  451 + 'attribute' => 'body',
  452 + 'content' => function($model) {
  453 + /**
  454 + * @var Order $model
  455 + */
  456 + if (empty($model->body)) {
  457 + return '';
  458 + } else {
  459 + return Html::a(
  460 + StringHelper::truncate($model->body, 10, '...'),
  461 + '#',
  462 + [
  463 + 'data-toggle' => 'tooltip',
  464 + 'title' => $model->body,
  465 + 'onclick' => 'event.preventDefault();',
  466 + ]
  467 + );
  468 + }
  469 + },
  470 + ],
  471 + [
  472 + 'content' => function($model) {
  473 + /**
  474 + * @var Order $model
  475 + */
  476 + return Html::a(
  477 + Html::tag('i', '', [ 'class' => 'glyphicon glyphicon-eye-open' ]),
  478 + [
  479 + '/ecommerce/order/view',
  480 + 'id' => $model->id,
  481 + ],
  482 + [
  483 + 'target' => '_blank',
  484 + 'data-pjax' => '0',
  485 + ]
  486 + );
  487 + },
  488 + ],
  489 + ],
  490 + ]
  491 + ) ?>
  492 +
  493 + </div><!-- /.box-body -->
  494 +</div><!-- /.box -->
... ...