Commit a0405ed668e4f096c83064fe6863f761479ef3dd
Merge remote-tracking branch 'origin/master'
Showing
29 changed files
with
947 additions
and
120 deletions
Show diff stats
backend/controllers/CheckPriceController.php
... | ... | @@ -8,6 +8,7 @@ use yii\filters\AccessControl; |
8 | 8 | use backend\components\base\BaseController; |
9 | 9 | use yii\filters\VerbFilter; |
10 | 10 | use backend\models\Details; |
11 | +use common\models\DetailsCurrency; | |
11 | 12 | use backend\models\ImportersFiles; |
12 | 13 | use backend\models\Importers; |
13 | 14 | use yii\base\ErrorException; |
... | ... | @@ -61,7 +62,7 @@ class CheckPriceController extends BaseController |
61 | 62 | |
62 | 63 | public function actionIndex() |
63 | 64 | { |
64 | - //$query = (new Query())->select('*')->from('{{%importer_files}}')->where(['not', ['time_end' => null]])->orderBy(['upload_time' => SORT_DESC]); | |
65 | + | |
65 | 66 | $query = Importers::find()->where(['active' => true])->orderBy(['price_date_update' => SORT_DESC]); |
66 | 67 | $provider = new ActiveDataProvider([ |
67 | 68 | 'query' => $query, |
... | ... | @@ -79,11 +80,12 @@ class CheckPriceController extends BaseController |
79 | 80 | public function actionView ($id, $date_update) |
80 | 81 | { |
81 | 82 | |
82 | - $query = Details::find()->where(['IMPORT_ID' => $id, 'timestamp' => $date_update])->limit(20); | |
83 | + $query = DetailsCurrency::find()->where(['IMPORT_ID' => $id, 'timestamp' => $date_update])->limit(20); | |
83 | 84 | |
84 | - $importer = Importers::findOne($id)->name; | |
85 | + $importer = Importers::findOne( $id ); | |
85 | 86 | $date = Yii::$app->formatter->asDate( $date_update, 'yyyy-MM-dd' ); |
86 | 87 | |
88 | + | |
87 | 89 | $provider = new ActiveDataProvider([ |
88 | 90 | 'query' => $query, |
89 | 91 | 'pagination' => false, | ... | ... |
1 | +<?php | |
2 | +/** | |
3 | + * Created by PhpStorm. | |
4 | + * User: Tsurkanov | |
5 | + * Date: 15.10.2015 | |
6 | + * Time: 12:27 | |
7 | + */ | |
8 | + | |
9 | +namespace backend\controllers; | |
10 | + | |
11 | +use backend\components\base\BaseController; | |
12 | +use common\components\CustomVarDamp; | |
13 | +use yii\filters\AccessControl; | |
14 | +use backend\models\UploadFileCrossingForm; | |
15 | +use backend\models\DetailsCrosses; | |
16 | +use yii\web\UploadedFile; | |
17 | +use \Yii; | |
18 | + | |
19 | +class CrossingUploadController extends BaseController | |
20 | +{ | |
21 | + public $layout = "/column"; | |
22 | + | |
23 | + /** | |
24 | + * @inheritdoc | |
25 | + */ | |
26 | + public function behaviors() | |
27 | + { | |
28 | + return [ | |
29 | + 'access' => [ | |
30 | + 'class' => AccessControl::className(), | |
31 | + 'rules' => [ | |
32 | + [ | |
33 | + 'actions' => ['index', 'result'], | |
34 | + 'allow' => true, | |
35 | + 'roles' => ['@'], | |
36 | + ], | |
37 | + ], | |
38 | + ], | |
39 | +// 'verbs' => [ | |
40 | +// 'class' => VerbFilter::className(), | |
41 | +// 'actions' => [ | |
42 | +// 'logout' => ['post'], | |
43 | +// ], | |
44 | +// ], | |
45 | + ]; | |
46 | + } | |
47 | + | |
48 | + /** | |
49 | + * @inheritdoc | |
50 | + */ | |
51 | + public function actions() | |
52 | + { | |
53 | + return [ | |
54 | + 'error' => [ | |
55 | + 'class' => 'yii\web\ErrorAction', | |
56 | + ], | |
57 | + ]; | |
58 | + } | |
59 | + | |
60 | + | |
61 | + public function actionIndex() | |
62 | + { | |
63 | + $model = new UploadFileCrossingForm(); | |
64 | + return $this->render('index', ['model' => $model]); | |
65 | + } | |
66 | + | |
67 | + public function actionResult() | |
68 | + { | |
69 | + $model = new UploadFileCrossingForm(); | |
70 | + $data = []; | |
71 | + if ($model->load(Yii::$app->request->post())) { | |
72 | + $model->file = UploadedFile::getInstance($model, 'file'); | |
73 | + | |
74 | + if ($model->validate()) { | |
75 | + $file_name = $model->file->name; | |
76 | + $model->file_path = Yii::getAlias('@temp_upload') . '/' . $file_name; | |
77 | + | |
78 | + $model->file->saveAs($model->file_path); | |
79 | + //запускаем парсинг | |
80 | + // доп. опции для парсера - удаление префикса в артикулах | |
81 | + $options['mode'] = 'crosses'; | |
82 | + $fields = []; | |
83 | + if ($model->delete_prefix1) { | |
84 | + $fields[] = 'ARTICLE'; | |
85 | + } | |
86 | + if ($model->delete_prefix2) { | |
87 | + $fields[] = 'CROSS_ARTICLE'; | |
88 | + } | |
89 | + if ( $fields ) { | |
90 | + $options [ 'converter_conf' ] = [ 'configuration' => [ "article" => $fields , | |
91 | + "string" => ['ARTICLE', 'CROSS_ARTICLE'],] ]; | |
92 | + } else { | |
93 | + $options [ 'converter_conf' ] = [ 'configuration' => [ "string" => ['ARTICLE', 'CROSS_ARTICLE'], ] ]; | |
94 | + } | |
95 | + | |
96 | + $data = $model->readFile( $options ); | |
97 | + $crosses_model = new DetailsCrosses(); | |
98 | + $crosses_model->ManualInsertWithIgnore( $data ); | |
99 | + | |
100 | + Yii::$app->session->setFlash('success', 'Файл кроссов успешно загружен'); | |
101 | + return $this->render('index', ['model' => $model]); | |
102 | + }else{ | |
103 | + // не прошла валидация форма загрузки файлов | |
104 | + $errors_str = ''; | |
105 | + foreach ($model->getErrors() as $error) { | |
106 | + $errors_str .= implode( array_values($error) ); | |
107 | + } | |
108 | + throw new \ErrorException( $errors_str ); | |
109 | + } | |
110 | + | |
111 | + } else { | |
112 | + throw new \ErrorException( 'Ошибка загрузки данных' ); | |
113 | + } | |
114 | + } | |
115 | +} | |
0 | 116 | \ No newline at end of file | ... | ... |
backend/controllers/ParserController.php
... | ... | @@ -10,7 +10,6 @@ use backend\models\UploadFileParsingForm; |
10 | 10 | use yii\web\UploadedFile; |
11 | 11 | use yii\data\ArrayDataProvider; |
12 | 12 | use yii\multiparser\DynamicFormHelper; |
13 | -use backend\components\parsers\CustomParserConfigurator; | |
14 | 13 | use backend\models\ImportersFiles; |
15 | 14 | use backend\models\Importers; |
16 | 15 | use yii\base\ErrorException; |
... | ... | @@ -101,7 +100,6 @@ class ParserController extends BaseController |
101 | 100 | } |
102 | 101 | |
103 | 102 | $model->file->saveAs($model->file_path); |
104 | - | |
105 | 103 | // для авто загрузки, обработка завершена |
106 | 104 | if ($model->mode) { |
107 | 105 | $model->success = true; |
... | ... | @@ -112,7 +110,10 @@ class ParserController extends BaseController |
112 | 110 | // === ручная загрузка =========== |
113 | 111 | //запускаем парсинг |
114 | 112 | // доп. опции для парсера |
115 | - $options = []; | |
113 | + $options = ['converter_conf' => | |
114 | + ['importer_id' => $files_model->importer_id] | |
115 | + ]; | |
116 | + | |
116 | 117 | if( ! $model->action ) // обработка с кастомным разделителем |
117 | 118 | $options['$delimiter'] = $model->delimiter; |
118 | 119 | |
... | ... | @@ -124,12 +125,11 @@ class ParserController extends BaseController |
124 | 125 | |
125 | 126 | } else { |
126 | 127 | // не прошла валидация форма загрузки файлов |
127 | - //@todo - отправка на страницу ошибок | |
128 | - $errors_arr = $model->getErrors(); | |
129 | - foreach ($errors_arr as $error) { | |
130 | - CustomVarDamp::dump(array_values($error)); | |
128 | + $errors_str = ''; | |
129 | + foreach ($model->getErrors() as $error) { | |
130 | + $errors_str .= implode( array_values($error) ); | |
131 | 131 | } |
132 | - die; | |
132 | + throw new ErrorException( $errors_str ); | |
133 | 133 | } |
134 | 134 | // листаем пагинатором, или повторно вызываем - считываем из кеша отпрасенные данные |
135 | 135 | } else if (Yii::$app->getCache()->get('parser_data')) { |
... | ... | @@ -145,8 +145,11 @@ class ParserController extends BaseController |
145 | 145 | ], |
146 | 146 | ]); |
147 | 147 | |
148 | + | |
149 | + $last_index = end( array_flip( $data[0] ) ); | |
150 | + $header_counts = $last_index + 1; | |
148 | 151 | //формируем заголовок для пользователя, где он сможет выбрать соответсвие полей (выпадающий список) |
149 | - $header_model = DynamicFormHelper::CreateDynamicModel(count($data[0])); | |
152 | + $header_model = DynamicFormHelper::CreateDynamicModel( $header_counts ); | |
150 | 153 | |
151 | 154 | return $this->render('results', |
152 | 155 | ['model' => $data, |
... | ... | @@ -197,7 +200,9 @@ class ParserController extends BaseController |
197 | 200 | Yii::$app->getCache()->delete('parser_data'); |
198 | 201 | Yii::$app->getCache()->delete('parser_configuration'); |
199 | 202 | |
200 | - unlink($configuration['file_path']); | |
203 | + if( file_exists($configuration['file_path']) ) | |
204 | + unlink($configuration['file_path']); | |
205 | + | |
201 | 206 | return $this->render('index', ['model' => $configuration]); |
202 | 207 | |
203 | 208 | }; | ... | ... |
backend/models/Currency.php
backend/models/Details.php
... | ... | @@ -2,6 +2,7 @@ |
2 | 2 | |
3 | 3 | namespace backend\models; |
4 | 4 | |
5 | +use common\components\CustomVarDamp; | |
5 | 6 | use Yii; |
6 | 7 | use backend\components\base\BaseActiveRecord; |
7 | 8 | |
... | ... | @@ -22,8 +23,21 @@ use backend\components\base\BaseActiveRecord; |
22 | 23 | */ |
23 | 24 | class Details extends BaseActiveRecord |
24 | 25 | { |
25 | - const KEY_COLUMN = ['IMPORT_ID','BRAND','ARTICLE']; | |
26 | + /** | |
27 | + *обязательные колонки | |
28 | + */ | |
29 | + const KEY_COLUMN = ['IMPORT_ID', 'BRAND', 'ARTICLE']; | |
30 | + | |
31 | + /** | |
32 | + * int - размер пакета запроса | |
33 | + */ | |
26 | 34 | const BATCH = 500; |
35 | + | |
36 | + /** | |
37 | + * @var bool - признак необходимости удалить префикс Артикула перед вставкой | |
38 | + */ | |
39 | + public $delete_price = false; | |
40 | + | |
27 | 41 | /** |
28 | 42 | * @inheritdoc |
29 | 43 | */ |
... | ... | @@ -38,8 +52,8 @@ class Details extends BaseActiveRecord |
38 | 52 | public function rules() |
39 | 53 | { |
40 | 54 | return [ |
41 | - [[ 'BRAND', 'ARTICLE', 'PRICE', 'DESCR', 'BOX'], 'required'], | |
42 | - // [['IMPORT_ID', 'BOX', 'ADD_BOX'], 'integer'], | |
55 | + [['BRAND', 'ARTICLE', 'PRICE', 'DESCR', 'BOX'], 'required'], | |
56 | + // [['IMPORT_ID', 'BOX', 'ADD_BOX'], 'integer'], | |
43 | 57 | [['PRICE'], 'number'], |
44 | 58 | [['BOX'], 'integer'], |
45 | 59 | [['timestamp'], 'safe'], |
... | ... | @@ -69,13 +83,38 @@ class Details extends BaseActiveRecord |
69 | 83 | ]; |
70 | 84 | } |
71 | 85 | |
72 | - public function ManualInsert ($data) | |
86 | + /** | |
87 | + *удаление (если $delete_price установлен)б а затем вставка данных с апдейтом прямымыми запросоми SQL | |
88 | + * @param $data - массив вставляемых данных, вставка будет прозводится пакетами размером указанным в константе BATCH | |
89 | + * @param $importer_id - (int) - идентификатор поставщика у которого будет сперва удалены прайсы а потом вставлены из массива $data | |
90 | + * @throws \yii\db\Exception | |
91 | + */ | |
92 | + public function ManualInsert($data, $importer_id) | |
93 | + { | |
94 | + if ($this->delete_price) { | |
95 | + // запустим пакетное удаление всех прайсов поставщика | |
96 | + do { | |
97 | + $query = Yii::$app->db->createCommand()->delete(self::tableName(), "IMPORT_ID = {$importer_id}")->sql . ' Limit ' . $this::BATCH; | |
98 | + $res = Yii::$app->db->createCommand($query)->execute(); | |
99 | + } while ($res); | |
100 | + | |
101 | + } | |
102 | + | |
103 | + $this->ManualInsertWithUpdate($data); | |
104 | + } | |
105 | + | |
106 | + /** | |
107 | + * вставка данных с апдейтом прямым запросом SQL | |
108 | + * @param $data - массив вставляемых данный, вставка будет прозводится пакетами размером указанным в константе BATCH | |
109 | + * @throws \yii\db\Exception | |
110 | + */ | |
111 | + private function ManualInsertWithUpdate($data) | |
73 | 112 | { |
74 | 113 | // \common\components\CustomVarDamp::dumpAndDie($data); |
75 | 114 | $table_name = self::tableName(); |
76 | - $keys_arr = array_keys( $data[0] ); | |
115 | + $keys_arr = array_keys($data[0]); | |
77 | 116 | // найдем те поля которые не являются ключами. Их нужно будет при дубляже апдейтить |
78 | - $fields_arr_to_update = array_diff( $keys_arr, $this::KEY_COLUMN ); | |
117 | + $fields_arr_to_update = array_diff($keys_arr, $this::KEY_COLUMN); | |
79 | 118 | |
80 | 119 | $query_update = ' on duplicate key update '; |
81 | 120 | foreach ($fields_arr_to_update as $field) { |
... | ... | @@ -87,18 +126,16 @@ class Details extends BaseActiveRecord |
87 | 126 | // запросы будем выполнять пакетами |
88 | 127 | // размер пакета установлен в константе |
89 | 128 | // разобъем массив на пакеты и будем их проходить |
90 | - $data = array_chunk($data, $this::BATCH ); | |
91 | - foreach( $data as $current_batch_array ){ | |
129 | + $data = array_chunk($data, $this::BATCH); | |
130 | + foreach ($data as $current_batch_array) { | |
92 | 131 | |
93 | - //воспользуемся пакетной вставкой от фреймворка, плюс сразу с экранированием и защитой от инъекций | |
132 | + //воспользуемся пакетной вставкой от фреймворка | |
94 | 133 | $query_insert = Yii::$app->db->createCommand()->batchInsert($table_name, $keys_arr, $current_batch_array)->sql; |
95 | 134 | // добавим фрагмент с апдейтом при дубляже |
96 | 135 | $query = "{$query_insert} {$query_update}"; |
97 | 136 | // \common\components\CustomVarDamp::dumpAndDie($query); |
98 | - $res = Yii::$app->db->createCommand($query)->execute(); | |
137 | + Yii::$app->db->createCommand($query)->execute(); | |
99 | 138 | |
100 | 139 | } |
101 | 140 | } |
102 | - | |
103 | - | |
104 | 141 | } | ... | ... |
1 | +<?php | |
2 | + | |
3 | +namespace backend\models; | |
4 | + | |
5 | +use common\components\CustomVarDamp; | |
6 | +use Yii; | |
7 | + | |
8 | +/** | |
9 | + * This is the model class for table "{{%details_crosses}}". | |
10 | + * | |
11 | + * @property string $ID | |
12 | + * @property string $ARTICLE | |
13 | + * @property string $BRAND | |
14 | + * @property string $CROSS_BRAND | |
15 | + * @property string $CROSS_ARTICLE | |
16 | + * @property string $timestamp | |
17 | + */ | |
18 | +class DetailsCrosses extends \backend\components\base\BaseActiveRecord | |
19 | +{ | |
20 | + /** | |
21 | + * int - размер пакета запроса | |
22 | + */ | |
23 | + const BATCH = 1000; | |
24 | + /** | |
25 | + * @inheritdoc | |
26 | + */ | |
27 | + public static function tableName() | |
28 | + { | |
29 | + return '{{%details_crosses}}'; | |
30 | + } | |
31 | + | |
32 | + /** | |
33 | + * @inheritdoc | |
34 | + */ | |
35 | + public function rules() | |
36 | + { | |
37 | + return [ | |
38 | + [['ARTICLE', 'BRAND', 'CROSS_BRAND', 'CROSS_ARTICLE'], 'required'], | |
39 | + [['timestamp'], 'safe'], | |
40 | + [['ARTICLE', 'BRAND', 'CROSS_BRAND', 'CROSS_ARTICLE'], 'string', 'max' => 100] | |
41 | + ]; | |
42 | + } | |
43 | + | |
44 | + /** | |
45 | + * @inheritdoc | |
46 | + */ | |
47 | + public function attributeLabels() | |
48 | + { | |
49 | + return [ | |
50 | + 'ID' => Yii::t('app', 'ID'), | |
51 | + 'ARTICLE' => Yii::t('app', 'Article'), | |
52 | + 'BRAND' => Yii::t('app', 'Brand'), | |
53 | + 'CROSS_BRAND' => Yii::t('app', 'Cross Brand'), | |
54 | + 'CROSS_ARTICLE' => Yii::t('app', 'Cross Article'), | |
55 | + 'timestamp' => Yii::t('app', 'Timestamp'), | |
56 | + ]; | |
57 | + } | |
58 | + | |
59 | + /** | |
60 | + * вставка данных с игнором дублей прямым запросом SQL | |
61 | + * @param $data - массив вставляемых данный, вставка будет прозводится пакетами размером указанным в константе BATCH | |
62 | + * @throws \yii\db\Exception | |
63 | + */ | |
64 | + //@todo - вынести все ручные инсерты в отдельный класс | |
65 | + public function ManualInsertWithIgnore( $data ) | |
66 | + { | |
67 | + // \common\components\CustomVarDamp::dumpAndDie($data); | |
68 | + $table_name = self::tableName(); | |
69 | + $keys_arr = array_keys($data[0]); | |
70 | + | |
71 | + // запросы будем выполнять пакетами | |
72 | + // размер пакета установлен в константе | |
73 | + // разобъем массив на пакеты и будем их проходить | |
74 | + $data = array_chunk($data, $this::BATCH); | |
75 | + foreach ($data as $current_batch_array) { | |
76 | + | |
77 | + //воспользуемся пакетной вставкой от фреймворка | |
78 | + $query = Yii::$app->db->createCommand()->batchInsert($table_name, $keys_arr, $current_batch_array)->sql; | |
79 | + // добавим ключевое слово - ignore | |
80 | + $query = preg_replace('/INSERT/','INSERT IGNORE', $query); | |
81 | + Yii::$app->db->createCommand($query)->execute(); | |
82 | + | |
83 | + } | |
84 | + } | |
85 | +} | ... | ... |
backend/models/Importers.php
... | ... | @@ -5,6 +5,7 @@ namespace backend\models; |
5 | 5 | use common\components\CustomVarDamp; |
6 | 6 | use Yii; |
7 | 7 | use backend\components\base\BaseActiveRecord; |
8 | +use backend\models\Currency; | |
8 | 9 | |
9 | 10 | /** |
10 | 11 | * |
... | ... | @@ -89,6 +90,15 @@ class Importers extends BaseActiveRecord |
89 | 90 | ]; |
90 | 91 | } |
91 | 92 | |
93 | + public function getCurrency () | |
94 | + { | |
95 | + return $this->hasOne(Currency::className(), ['id' => 'currency_id'])->one()->name; | |
96 | + } | |
97 | + | |
98 | + public function getCurrencyRate () | |
99 | + { | |
100 | + return $this->hasOne(Currency::className(), ['id' => 'currency_id'])->one()->rate; | |
101 | + } | |
92 | 102 | |
93 | 103 | public function getKeys () |
94 | 104 | { | ... | ... |
1 | +<?php | |
2 | +namespace backend\models; | |
3 | + | |
4 | +use yii\base\ErrorException; | |
5 | +use yii\base\Model; | |
6 | +use yii\web\UploadedFile; | |
7 | +use Yii; | |
8 | +use common\components\CustomVarDamp; | |
9 | + | |
10 | +/** | |
11 | + * UploadForm is the model behind the upload form. | |
12 | + */ | |
13 | +class UploadFileCrossingForm extends Model | |
14 | +{ | |
15 | + /** | |
16 | + * @var UploadedFile file attribute | |
17 | + */ | |
18 | + // атрибуты формы | |
19 | + public $file; | |
20 | + public $delete_prefix1; | |
21 | + public $delete_prefix2; | |
22 | + public $file_path; | |
23 | + | |
24 | + | |
25 | + | |
26 | + public function rules() | |
27 | + { | |
28 | + return [ | |
29 | + ['file', 'required', 'message' => 'Не выбран файл!' ], | |
30 | + [['file'], 'file', 'extensions' => 'csv', 'checkExtensionByMimeType'=>false ], | |
31 | + [['delete_prefix1', 'delete_prefix2'], 'boolean' ], | |
32 | + | |
33 | + ]; | |
34 | + } | |
35 | + | |
36 | + public function attributeLabels() | |
37 | + { | |
38 | + return [ | |
39 | + 'file' => Yii::t('app', 'Источник'), | |
40 | + 'delete_prefix1' => Yii::t('app', 'Удалять префикс в артикуле товара 1'), | |
41 | + 'delete_prefix2' => Yii::t('app', 'Удалять префикс в артикуле товара 2'), | |
42 | + ]; | |
43 | + } | |
44 | + | |
45 | + public function readFile( $options = [] ){ | |
46 | + | |
47 | + $data = Yii::$app->multiparser->parse( $this->file_path, $options ); | |
48 | + if( !is_array( $data ) ){ | |
49 | + throw new ErrorException("Ошибка чтения из файла кроссов {$this->file_path}"); | |
50 | + } | |
51 | + // файл больше не нужен - данные прочитаны и сохранены в кеш | |
52 | + if( file_exists($this->file_path) ) | |
53 | + unlink($this->file_path); | |
54 | + | |
55 | + return $data; | |
56 | + } | |
57 | + | |
58 | + public function fields() | |
59 | + { | |
60 | + return [ | |
61 | + | |
62 | + 'delete_price1', | |
63 | + 'delete_price2' | |
64 | + | |
65 | + ]; | |
66 | + } | |
67 | + | |
68 | + | |
69 | +} | |
0 | 70 | \ No newline at end of file | ... | ... |
backend/models/UploadFileParsingForm.php
1 | 1 | <?php |
2 | 2 | namespace backend\models; |
3 | 3 | |
4 | +use yii\base\ErrorException; | |
4 | 5 | use yii\base\Model; |
5 | 6 | use yii\web\UploadedFile; |
6 | 7 | use Yii; |
... | ... | @@ -47,9 +48,7 @@ class UploadFileParsingForm extends Model |
47 | 48 | return [ |
48 | 49 | ['importer_id', 'required', 'message' => 'Не указан поставщик!' ], |
49 | 50 | ['file', 'required', 'message' => 'Не выбран файл!' ], |
50 | - //@todo - not working this file validator!!! - fixed | |
51 | - [['file'], 'file'],// 'extensions' => ['csv', 'xml'] ], | |
52 | - // 'wrongMimeType' => 'Указан неподдерживаемый тип файла. Можно выбирать csv, xml файлы.' ], | |
51 | + [['file'], 'file', 'extensions' => ['csv', 'xml'], 'checkExtensionByMimeType'=>false ], | |
53 | 52 | ['importer_id', 'integer','max' => 999999, 'min' => 0 ], |
54 | 53 | [['action','delete_prefix', 'delete_price', 'success'], 'boolean', 'except' => 'auto' ], // только для ручной загрузки |
55 | 54 | ['delimiter', 'string', 'max' => 1], |
... | ... | @@ -73,8 +72,11 @@ class UploadFileParsingForm extends Model |
73 | 72 | |
74 | 73 | $data = Yii::$app->multiparser->parse( $this->file_path, $options ); |
75 | 74 | if( !is_array( $data ) ){ |
76 | - $data = ['No results']; | |
75 | + throw new ErrorException("Ошибка чтения из файла прайса {$this->file_path}"); | |
77 | 76 | } |
77 | + // файл больше не нужен - данные прочитаны и сохранены в кеш | |
78 | + if( file_exists($this->file_path) ) | |
79 | + unlink($this->file_path); | |
78 | 80 | |
79 | 81 | return $data; |
80 | 82 | } | ... | ... |
backend/views/check-price/view.php
... | ... | @@ -8,24 +8,29 @@ use yii\bootstrap\Modal; |
8 | 8 | /* @var $this yii\web\View */ |
9 | 9 | /* @var $searchModel backend\models\CatalogSearch */ |
10 | 10 | /* @var $dataProvider yii\data\ActiveDataProvider */ |
11 | -$this->title = 'Прайс ' . Html::encode( "{$importer} от {$date}" ); | |
11 | +$this->title = 'Прайс ' . Html::encode("{$importer->name} от {$date}"); | |
12 | 12 | $this->params['breadcrumbs'][] = $this->title; |
13 | 13 | |
14 | 14 | ?> |
15 | -<div class="catalog-index"> | |
16 | - | |
17 | - <h1><?= Html::encode($this->title) ?></h1> | |
18 | - | |
19 | - <?= GridView::widget( ['dataProvider' => $dataProvider, | |
20 | - | |
21 | - ] ); | |
22 | - | |
23 | - | |
24 | - ?> | |
25 | - | |
26 | - | |
27 | - | |
28 | -</div> | |
15 | + <div class="catalog-index"> | |
16 | + | |
17 | + <h1><?= Html::encode($this->title) ?></h1> | |
18 | + | |
19 | + <?= GridView::widget( ['dataProvider' => $dataProvider, | |
20 | + 'columns' => [ | |
21 | + ['attribute' => 'FULL_ARTICLE'], | |
22 | + ['attribute' => 'ARTICLE'], | |
23 | + ['attribute' => 'BRAND'], | |
24 | + ['attribute' => 'DESCR'], | |
25 | + ['attribute' => 'BOX'], | |
26 | + ['attribute' => 'ADD_BOX'], | |
27 | + ['attribute' => 'GROUP'], | |
28 | + ['attribute' => 'name'], | |
29 | + ['attribute' => 'PRICE'], | |
30 | + ] | |
31 | + ] ); ?> | |
32 | + | |
33 | + </div> | |
29 | 34 | <?php |
30 | 35 | |
31 | 36 | ?> |
32 | 37 | \ No newline at end of file | ... | ... |
1 | +<?php | |
2 | +use yii\widgets\ActiveForm; | |
3 | +use yii\helpers\Html; | |
4 | +use yii\helpers\ArrayHelper; | |
5 | + | |
6 | +?> | |
7 | +<div class="row"> | |
8 | + <div class="col-lg-5"> | |
9 | + <?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data',],'action'=>['crossing-upload/result']]); | |
10 | + | |
11 | + if ($msg = \Yii::$app->session->getFlash('success')) { // вернулись после успешной загрузки данного файла | |
12 | + echo Html::tag('h3', $msg ,['class'=>'bg-success']); | |
13 | + } | |
14 | + ?> | |
15 | + <h3>Кросс файлы</h3> | |
16 | + | |
17 | + <?= $form->field($model, 'delete_prefix1')->checkbox() ?> | |
18 | + <?= $form->field($model, 'delete_prefix2')->checkbox() ?> | |
19 | + <?= $form->field($model, 'file')->fileInput()->label(false) ?> | |
20 | + | |
21 | + <div class="form-group"> | |
22 | + <?= Html::submitButton(Yii::t( 'app', 'Выполнить' ), ['class' => 'btn btn-primary']) ?> | |
23 | + </div> | |
24 | + | |
25 | + <?php ActiveForm::end() ?> | |
26 | + </div> | |
27 | +</div> | |
28 | + | ... | ... |
backend/views/layouts/column.php
... | ... | @@ -283,6 +283,7 @@ $this->beginContent('@app/views/layouts/main.php'); |
283 | 283 | 'options' => ['class' => 'sidebar-menu'], |
284 | 284 | 'items' => [ |
285 | 285 | ['label' => "Загрузка файлов", 'url' => ['#'], 'items' => [ |
286 | + ['label' => 'Кросс-файлы', 'url' => ['crossing-upload/index']], | |
286 | 287 | ['label' => 'Файлы на сервере', 'url' => ['parser/server-files']], |
287 | 288 | ['label' => 'Загрузить файл на сервер', 'url' => ['parser/index', 'mode' => 1]], |
288 | 289 | ['label' => 'Ручная загрузка', 'url' => ['parser/index']], | ... | ... |
common/components/PriceWriter.php
... | ... | @@ -14,22 +14,45 @@ use backend\models\ImportersFiles; |
14 | 14 | use backend\models\Importers; |
15 | 15 | use backend\models\Details; |
16 | 16 | |
17 | -class PriceWriter { | |
17 | +/** | |
18 | + * Class PriceWriter | |
19 | + * @package common\components | |
20 | + * записывает в БД отпарсенные данные | |
21 | + * запись происходит в несколько таблиц | |
22 | + */ | |
23 | +class PriceWriter | |
24 | +{ | |
25 | + /** | |
26 | + * @var - int - 0 - интерактивный режим, 1 - консольный | |
27 | + */ | |
18 | 28 | public $mode; |
29 | + | |
30 | + /** | |
31 | + * @var - массив с настройками записи | |
32 | + */ | |
19 | 33 | public $configuration; |
34 | + | |
35 | + /** | |
36 | + * @var - массив с данными которые нужно записать | |
37 | + */ | |
20 | 38 | public $data; |
21 | 39 | |
22 | - public function writeDataToDB () | |
40 | + function __construct() | |
41 | + { | |
42 | + set_time_limit(300); | |
43 | + } | |
44 | + | |
45 | + public function writeDataToDB() | |
23 | 46 | { |
24 | 47 | // 1. запишем дату старта в таблицу файлов поставщика (ImportersFiles) |
25 | 48 | // id загруженного файла получим из конфигурации |
26 | - $files_model = ImportersFiles::findOne( $this->configuration['record_id'] ); | |
49 | + $files_model = ImportersFiles::findOne($this->configuration['record_id']); | |
27 | 50 | |
28 | 51 | $update_date = date('Y-m-d H:i:s'); |
29 | 52 | $files_model->time_start = $update_date; |
30 | 53 | // запишем дату начала загрузки |
31 | 54 | if (!$files_model->save()) { |
32 | - throw new \ErrorException(implode( ', ', $files_model->getErrors())); | |
55 | + throw new \ErrorException(implode(', ', $files_model->getErrors())); | |
33 | 56 | } |
34 | 57 | |
35 | 58 | // 2. запишем полученные данные в таблицу товаров (Details) |
... | ... | @@ -42,8 +65,16 @@ class PriceWriter { |
42 | 65 | $row['PRICE'] = \Yii::$app->multiparser->convertToFloat($row['PRICE']); |
43 | 66 | $row['BOX'] = \Yii::$app->multiparser->convertToInteger($row['BOX']); |
44 | 67 | // присвоим полный артикул |
68 | + | |
45 | 69 | $row['FULL_ARTICLE'] = $row['ARTICLE']; |
46 | - if(isset($row['ADD_BOX'])) | |
70 | + if ((int)$this->configuration['delete_prefix']) { | |
71 | + $row = \Yii::$app->multiparser->convertToArticle( $row, $this->configuration['importer_id'] ); | |
72 | + } else { | |
73 | + $row['ARTICLE'] = \Yii::$app->multiparser->convertToArticle( $row['ARTICLE'] ); | |
74 | + } | |
75 | + | |
76 | + | |
77 | + if (isset($row['ADD_BOX'])) | |
47 | 78 | $row['ADD_BOX'] = \Yii::$app->multiparser->convertToInteger($row['ADD_BOX']); |
48 | 79 | |
49 | 80 | // проверим все ли обязательные колонки были указаны пользователем |
... | ... | @@ -55,31 +86,32 @@ class PriceWriter { |
55 | 86 | } |
56 | 87 | } |
57 | 88 | |
58 | - // дополним данные значением импортера и даты обновления цены | |
59 | - $this->data = \Yii::$app->multiparser->addColumns($this->data, ['IMPORT_ID' => $this->configuration['importer_id'], 'timestamp' => $update_date]); | |
60 | - | |
61 | - try { | |
62 | - //@todo add transaction | |
63 | - // попытаемся вставить данные в БД с апдейтом по ключам | |
64 | - $details_model->ManualInsert($this->data); | |
89 | + // дополним данные значением импортера и даты обновления цены | |
90 | + $this->data = \Yii::$app->multiparser->addColumns($this->data, ['IMPORT_ID' => $this->configuration['importer_id'], 'timestamp' => $update_date]); | |
91 | + try { | |
92 | + //@todo add transaction | |
65 | 93 | |
66 | - // 3. зафиксируем дату конца загрузки в файлах поставщика | |
94 | + if ((int)$this->configuration['delete_price']) { | |
95 | + $details_model->delete_price = true; | |
96 | + } | |
97 | + //2. попытаемся вставить данные в БД с апдейтом по ключам | |
98 | + $details_model->ManualInsert($this->data, $this->configuration['importer_id']); | |
67 | 99 | |
68 | - if (!$files_model->save()) { | |
69 | - throw new \ErrorException(implode( ', ', $files_model->getErrors())); | |
70 | - } | |
100 | + // 3. зафиксируем дату конца загрузки в файлах поставщика | |
101 | + if (!$files_model->save()) { | |
102 | + throw new \ErrorException(implode(', ', $files_model->getErrors())); | |
103 | + } | |
71 | 104 | |
72 | - // 4. зафиксируем дату загрузки в таблице поставщиков | |
73 | - $imp_model = Importers::findOne($this->configuration['importer_id']); | |
74 | - $imp_model->price_date_update = $update_date; | |
105 | + // 4. зафиксируем дату загрузки в таблице поставщиков | |
106 | + $imp_model = Importers::findOne($this->configuration['importer_id']); | |
107 | + $imp_model->price_date_update = $update_date; | |
75 | 108 | |
76 | - if (!$imp_model->save()) { | |
77 | - throw new \ErrorException(implode( ', ', $imp_model->getErrors())); | |
78 | - } | |
79 | - } catch (ErrorException $e) { | |
80 | - throw new \ErrorException( $e->getMessage() ); | |
109 | + if (!$imp_model->save()) { | |
110 | + throw new \ErrorException(implode(', ', $imp_model->getErrors())); | |
81 | 111 | } |
82 | - | |
112 | + } catch (ErrorException $e) { | |
113 | + throw new \ErrorException($e->getMessage()); | |
114 | + } | |
83 | 115 | |
84 | 116 | |
85 | 117 | return true; | ... | ... |
common/components/parsers/CustomConverter.php
1 | 1 | <?php |
2 | 2 | namespace common\components\parsers; |
3 | + | |
3 | 4 | use common\components\CustomVarDamp; |
4 | 5 | use yii\multiparser\Converter; |
5 | 6 | use backend\models\Details; |
7 | +use backend\models\DetailsCrosses; | |
8 | +use backend\models\ImportersPrefix; | |
6 | 9 | |
7 | -class CustomConverter extends Converter { | |
10 | +class CustomConverter extends Converter | |
11 | +{ | |
8 | 12 | |
9 | 13 | /** |
10 | 14 | * @param $value_arr - двумерный массив значений, которому нужно присвоить ключи |
... | ... | @@ -13,38 +17,44 @@ class CustomConverter extends Converter { |
13 | 17 | */ |
14 | 18 | public static $sign; |
15 | 19 | public static $multiplier; |
20 | + public static $importer_id; | |
21 | + public static $brand; | |
16 | 22 | |
17 | - public static function convertToAssocArray ( array $value_arr, array $key_array, $key_prefix = '' ) | |
23 | + public static function convertToAssocArray(array $value_arr, array $key_array, $key_prefix = '') | |
18 | 24 | { |
19 | 25 | // очистка служебного префикса в массиве заголовков |
20 | 26 | if ($key_prefix) { |
21 | 27 | // @todo оптимизировать - два переворота массива - избыточно |
22 | - $key_array = array_flip( $key_array ); | |
28 | + $key_array = array_flip($key_array); | |
23 | 29 | |
24 | - array_walk( $key_array, function ( &$value, $key, $key_prefix ){ $value = str_replace( $key_prefix, '',$value ); }, $key_prefix ); | |
30 | + array_walk($key_array, function (&$value, $key, $key_prefix) { | |
31 | + $value = str_replace($key_prefix, '', $value); | |
32 | + }, $key_prefix); | |
25 | 33 | |
26 | - $key_array = array_flip( $key_array ); | |
34 | + $key_array = array_flip($key_array); | |
27 | 35 | //уберем пустые элементы |
28 | - $key_array = array_filter($key_array, function ($value){ return $value !==''; }); | |
36 | + $key_array = array_filter($key_array, function ($value) { | |
37 | + return $value !== ''; | |
38 | + }); | |
29 | 39 | } |
30 | 40 | |
31 | - array_walk( $value_arr, | |
32 | - function ( &$value, $key, $key_array ) { | |
41 | + array_walk($value_arr, | |
42 | + function (&$value, $key, $key_array) { | |
33 | 43 | $res = $value; |
34 | 44 | foreach ($res as $sub_key => $sub_value) { |
35 | 45 | if (isset($key_array[$sub_key])) { |
36 | 46 | // если такой ключ в базовом массиве (массиве ключей) есть, то заменим новым, иначе просто удалим |
37 | 47 | $new_key = $key_array[$sub_key]; |
38 | - if( !array_key_exists( $new_key , $res ) ){ | |
39 | - $res[ $new_key ] = $value[$sub_key]; | |
48 | + if (!array_key_exists($new_key, $res)) { | |
49 | + $res[$new_key] = $value[$sub_key]; | |
40 | 50 | } |
41 | 51 | } |
42 | - unset( $res[$sub_key] ); | |
52 | + unset($res[$sub_key]); | |
43 | 53 | $value = $res; |
44 | 54 | } |
45 | 55 | |
46 | 56 | }, |
47 | - $key_array); | |
57 | + $key_array); | |
48 | 58 | |
49 | 59 | return $value_arr; |
50 | 60 | } |
... | ... | @@ -54,7 +64,7 @@ class CustomConverter extends Converter { |
54 | 64 | * @param $add_array - массив с колонками (ключи) и значениями колонок |
55 | 65 | * @return mixed |
56 | 66 | */ |
57 | - public function addColumns ( array $value_arr , array $add_array ) | |
67 | + public function addColumns(array $value_arr, array $add_array) | |
58 | 68 | { |
59 | 69 | $i = 0; |
60 | 70 | while ($i < count($value_arr)) { |
... | ... | @@ -66,7 +76,7 @@ class CustomConverter extends Converter { |
66 | 76 | return $value_arr; |
67 | 77 | } |
68 | 78 | |
69 | - public static function convertToDetails ( array $row ) | |
79 | + public static function convertToDetails(array $row) | |
70 | 80 | { |
71 | 81 | // присвоим полный артикул |
72 | 82 | $row['FULL_ARTICLE'] = $row['ARTICLE']; |
... | ... | @@ -75,19 +85,35 @@ class CustomConverter extends Converter { |
75 | 85 | // проверим все ли обязательные колонки были указаны пользователем |
76 | 86 | $details_model->load(['Details' => $row]); |
77 | 87 | |
78 | - if (!$details_model->validate()){ | |
88 | + if (!$details_model->validate()) { | |
79 | 89 | $errors = ''; |
80 | - foreach ( $details_model->errors as $key => $arr_errors ) { | |
81 | - $errors .= "Аттрибут $key - " . implode( ' , ', $arr_errors ); | |
90 | + foreach ($details_model->errors as $key => $arr_errors) { | |
91 | + $errors .= "Аттрибут $key - " . implode(' , ', $arr_errors); | |
82 | 92 | } |
83 | - throw new \ErrorException( $errors ); | |
93 | + throw new \ErrorException($errors); | |
84 | 94 | } |
85 | 95 | return $row; |
86 | 96 | } |
87 | 97 | |
88 | - public function ConvertToMultiply ( array $row ) | |
98 | + public static function convertToCrosses( array $row ) | |
99 | + { | |
100 | + | |
101 | + $details_model = new DetailsCrosses(); | |
102 | + // проверим все ли обязательные колонки были указаны пользователем | |
103 | + $details_model->load(['DetailsCrosses' => $row]); | |
104 | + | |
105 | + if (!$details_model->validate()) { | |
106 | + $errors = ''; | |
107 | + foreach ($details_model->errors as $key => $arr_errors) { | |
108 | + $errors .= "Аттрибут $key - " . implode(' , ', $arr_errors); | |
109 | + } | |
110 | + throw new \ErrorException($errors); | |
111 | + } | |
112 | + return $row; | |
113 | + } | |
114 | + public function ConvertToMultiply(array $row) | |
89 | 115 | { |
90 | - $PRICE = $row[ 'PRICE' ]; | |
116 | + $PRICE = $row['PRICE']; | |
91 | 117 | $sign = self::$sign; |
92 | 118 | $multiplier = self::$multiplier; |
93 | 119 | //CustomVarDamp::dumpAndDie(self); |
... | ... | @@ -96,29 +122,87 @@ class CustomConverter extends Converter { |
96 | 122 | if ($multiplier > 0) { |
97 | 123 | $PRICE += $multiplier; |
98 | 124 | } |
99 | - } | |
100 | - else if ($sign == '-') { | |
125 | + } else if ($sign == '-') { | |
101 | 126 | if ($multiplier > 0) { |
102 | 127 | $PRICE -= $multiplier; |
103 | 128 | } |
104 | - } | |
105 | - else if ($sign == '*') { | |
129 | + } else if ($sign == '*') { | |
106 | 130 | if ($multiplier > 0) { |
107 | 131 | $PRICE *= $multiplier; |
108 | 132 | } |
109 | - } | |
110 | - else if ($sign == '/') { | |
133 | + } else if ($sign == '/') { | |
111 | 134 | if ($multiplier > 0) { |
112 | 135 | $PRICE /= $multiplier; |
113 | 136 | } |
114 | 137 | } |
115 | 138 | } |
116 | 139 | |
117 | - $row[ 'PRICE' ] = $PRICE; | |
140 | + $row['PRICE'] = $PRICE; | |
118 | 141 | |
119 | 142 | return $row; |
120 | 143 | |
121 | 144 | } |
122 | 145 | |
146 | + public static function convertToArticle( $value, $importer_id = '' ) | |
147 | + { | |
148 | + if(isset( $importer_id )){ | |
149 | + self::$importer_id = $importer_id; | |
150 | + } | |
151 | + | |
152 | + if (is_array($value)) { | |
153 | + | |
154 | + $row = $value; | |
155 | + // 1. Уберем префикс который разделен пробелом (если он есть) | |
156 | + $words = explode(" ", $row['ARTICLE']); | |
157 | + if (count($words) > 1) { | |
158 | + array_shift($words); | |
159 | + $row['ARTICLE'] = implode(" ", $words); | |
160 | + } | |
161 | + | |
162 | + if( isset( $row['BRAND'] ) && isset( self::$importer_id ) ){ | |
163 | + // 2. Уберем брендовый префикс (если он есть) | |
164 | + self::$brand = $row['BRAND']; | |
165 | + $prefix = ''; | |
166 | + // запрос закешируем | |
167 | + $prefix = ImportersPrefix::getDb()->cache( function ($db) { | |
168 | + return ImportersPrefix::find()->where([ 'importer_id' => self::$importer_id, | |
169 | + 'brand' => self::$brand ])->one(); | |
170 | + }); | |
171 | + | |
172 | + if ($prefix) { | |
173 | + $row['BRAND'] = str_replace($prefix, "", $row['BRAND']); | |
174 | + } | |
175 | + } | |
176 | + | |
177 | + return $row; | |
178 | + | |
179 | + } else { | |
180 | + $words = explode( " ", $value ); | |
181 | + if ( count( $words ) > 1) { | |
182 | + array_shift( $words ); | |
183 | + $value = implode( " ", $words ); | |
184 | + } | |
185 | + | |
186 | + return $value; | |
187 | + } | |
188 | + } | |
189 | + | |
190 | + public static function convertToBrand($value) | |
191 | + { | |
192 | + $res = parent::convertToEncode($value);; | |
193 | + $res = trim(strtoupper($res)); | |
194 | + $res = str_replace("Ä", "A", str_replace("Ö", "O", str_replace("Ü", "U", str_replace("Ë", "E", str_replace("Ò", "O", $res))))); | |
195 | + $res = str_replace(array('@', '#', '~', '"', "'", "?", "!"), '', $res); | |
196 | + | |
197 | + return $res; | |
198 | + } | |
199 | + | |
200 | + public static function convertToString($value) | |
201 | + { | |
202 | + $value = parent::convertToEncode($value); | |
203 | + | |
204 | + return str_replace(array('!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '=', '-', '~', '`', '"', "'", ' ', '№', '%', ';', ':', '[', ']', '{', '}', '*', '?', '/', '\'', '|', '.', ',', '<', '>', '\\'), '', $value); | |
205 | + } | |
206 | + | |
123 | 207 | |
124 | 208 | } |
125 | 209 | \ No newline at end of file | ... | ... |
common/components/parsers/CustomCsvParser.php
... | ... | @@ -16,7 +16,7 @@ use yii\base\ErrorException; |
16 | 16 | |
17 | 17 | class CustomCsvParser extends \yii\multiparser\CsvParser { |
18 | 18 | |
19 | - public $last_line = 10; | |
19 | + public $last_line = 100; | |
20 | 20 | //public $hasHeaderRow = true; |
21 | 21 | // public $keys = ['first','second', 'third', 'forth', 'fifth']; |
22 | 22 | public function setupConverter() | ... | ... |
common/components/parsers/config.php
... | ... | @@ -5,8 +5,8 @@ |
5 | 5 | ['class' => 'common\components\parsers\CustomCsvParser', |
6 | 6 | 'auto_detect_first_line' => true, |
7 | 7 | 'converter_conf' => [ |
8 | - //'class' => ' common\components\parsers\CustomConverter', // @todo переделать на подключаемый behavior | |
9 | - 'configuration' => ["string" => 'DESCR'],] | |
8 | + //'class' => ' common\components\parsers\CustomConverter', // @todo переделать на компонент | |
9 | + 'configuration' => ["encode" => 'DESCR'],] | |
10 | 10 | ], |
11 | 11 | 'console' => |
12 | 12 | ['class' => 'common\components\parsers\CustomCsvParser', |
... | ... | @@ -17,10 +17,11 @@ |
17 | 17 | 'hasKey' => 1, |
18 | 18 | 'configuration' => ["string" => 'DESCR', |
19 | 19 | "float" => 'PRICE', |
20 | + "brand" => 'BRAND', | |
20 | 21 | "integer" => ['BOX','ADD_BOX'], |
21 | 22 | "multiply" => [], |
22 | - "details" => [] // @todo сделать отдельно конфигурирование валидации | |
23 | - | |
23 | + "article" => [], | |
24 | + "details" => [] | |
24 | 25 | ] |
25 | 26 | ],], |
26 | 27 | |
... | ... | @@ -34,6 +35,20 @@ |
34 | 35 | "ADD_BOX"=> 'В пути', |
35 | 36 | "GROUP" => 'Группа RG' |
36 | 37 | ], |
38 | + | |
39 | + 'crosses' => ['class' => 'common\components\parsers\CustomCsvParser', | |
40 | + 'auto_detect_first_line' => true, | |
41 | + 'min_column_quantity' => 4, | |
42 | + 'hasHeaderRow' => true, | |
43 | + 'keys' =>['ARTICLE', 'CROSS_ARTICLE', 'BRAND', 'CROSS_BRAND'], | |
44 | + 'converter_conf' => [ | |
45 | + //'class' => ' common\components\parsers\CustomConverter', | |
46 | + 'hasKey' => 1, | |
47 | + 'configuration' => [ | |
48 | + "brand" => ['BRAND', 'CROSS_BRAND'], | |
49 | + "crosses" => [], | |
50 | + ] | |
51 | + ],], | |
37 | 52 | ], |
38 | 53 | 'xml' => |
39 | 54 | ['console' => | ... | ... |
common/config/main-local.php
... | ... | @@ -2,7 +2,6 @@ |
2 | 2 | return [ |
3 | 3 | 'components' => [ |
4 | 4 | 'db' => [ |
5 | - //'class' => 'yii\db\Connection', | |
6 | 5 | 'class' => 'backend\components\base\CustomDbConnection', |
7 | 6 | 'dsn' => 'mysql:host=195.248.225.150;dbname=italautocomua', |
8 | 7 | 'username' => 'italautocomua', |
... | ... | @@ -10,6 +9,14 @@ return [ |
10 | 9 | 'charset' => 'utf8', |
11 | 10 | 'tablePrefix' => 'w_', |
12 | 11 | ], |
12 | + 'full_privileges_db' => [ | |
13 | + 'class' => 'backend\components\base\CustomDbConnection', | |
14 | + 'dsn' => 'mysql:host=195.248.225.150;dbname=italautocomua', | |
15 | + 'username' => 'percona', | |
16 | + 'password' => 'PpvSuMGEnLr4UKnVGUZPRa5wPCHF98Jf', | |
17 | + 'charset' => 'utf8', | |
18 | + 'tablePrefix' => 'w_', | |
19 | + ], | |
13 | 20 | 'mailer' => [ |
14 | 21 | 'class' => 'yii\swiftmailer\Mailer', |
15 | 22 | 'viewPath' => '@common/mail', | ... | ... |
1 | +<?php | |
2 | + | |
3 | +namespace common\models; | |
4 | + | |
5 | +use Yii; | |
6 | + | |
7 | +/** | |
8 | + * This is the model class for table "{{%details_currency}}". | |
9 | + * | |
10 | + * @property string $ID | |
11 | + * @property string $IMPORT_ID | |
12 | + * @property string $BRAND | |
13 | + * @property string $ARTICLE | |
14 | + * @property string $FULL_ARTICLE | |
15 | + * @property double $PRICE | |
16 | + * @property string $DESCR | |
17 | + * @property string $BOX | |
18 | + * @property string $ADD_BOX | |
19 | + * @property string $GROUP | |
20 | + * @property string $timestamp | |
21 | + * @property string $name | |
22 | + * @property double $rate | |
23 | + */ | |
24 | +class DetailsCurrency extends \backend\components\base\BaseActiveRecord | |
25 | +{ | |
26 | + /** | |
27 | + * @inheritdoc | |
28 | + */ | |
29 | + public static function tableName() | |
30 | + { | |
31 | + return '{{%details_currency}}'; | |
32 | + } | |
33 | + | |
34 | + /** | |
35 | + * @inheritdoc | |
36 | + */ | |
37 | + public function rules() | |
38 | + { | |
39 | + return [ | |
40 | + [['ID', 'IMPORT_ID', 'BOX', 'ADD_BOX'], 'integer'], | |
41 | + [['IMPORT_ID', 'BRAND', 'ARTICLE', 'FULL_ARTICLE', 'PRICE', 'DESCR', 'BOX', 'name', 'rate'], 'required'], | |
42 | + [['PRICE', 'rate'], 'number'], | |
43 | + [['timestamp'], 'safe'], | |
44 | + [['BRAND', 'ARTICLE'], 'string', 'max' => 100], | |
45 | + [['FULL_ARTICLE'], 'string', 'max' => 150], | |
46 | + [['DESCR', 'GROUP'], 'string', 'max' => 200], | |
47 | + [['name'], 'string', 'max' => 50] | |
48 | + ]; | |
49 | + } | |
50 | + | |
51 | + /** | |
52 | + * @inheritdoc | |
53 | + */ | |
54 | + public function attributeLabels() | |
55 | + { | |
56 | + return [ | |
57 | + 'ID' => Yii::t('app', 'ID'), | |
58 | + 'IMPORT_ID' => Yii::t('app', 'Import ID'), | |
59 | + 'BRAND' => Yii::t('app', 'Бренд'), | |
60 | + 'ARTICLE' => Yii::t('app', 'Имя'), | |
61 | + 'FULL_ARTICLE' => Yii::t('app', 'Артикул'), | |
62 | + 'PRICE' => Yii::t('app', 'Цена'), | |
63 | + 'DESCR' => Yii::t('app', 'Описание'), | |
64 | + 'BOX' => Yii::t('app', 'Кол-во'), | |
65 | + 'ADD_BOX' => Yii::t('app', 'В пути'), | |
66 | + 'GROUP' => Yii::t('app', 'Группа RG'), | |
67 | + 'timestamp' => Yii::t('app', 'Дата обновления'), | |
68 | + 'name' => Yii::t('app', 'Валюта'), | |
69 | + 'rate' => Yii::t('app', 'Курс'), | |
70 | + ]; | |
71 | + } | |
72 | +} | ... | ... |
console/controllers/ParserController.php
... | ... | @@ -32,8 +32,7 @@ class ParserController extends Controller |
32 | 32 | 'importer_id' => $importer_id, |
33 | 33 | 'parser_config' => ['keys' => $keys, |
34 | 34 | 'converter_conf' => |
35 | - ['sign' => $sign, | |
36 | - 'multiplier' => $multiplier], | |
35 | + [ 'sign' => $sign, 'multiplier' => $multiplier, 'importer_id' => $importer_id ], | |
37 | 36 | 'mode' => 'console'] |
38 | 37 | ]; |
39 | 38 | if ($this->parseFileConsole($file_path, $config)) { | ... | ... |
console/migrations/m150925_111922_add_foreign_key_ImportFiles.php
... | ... | @@ -22,7 +22,7 @@ class m150925_111922_add_foreign_key_ImportFiles extends Migration |
22 | 22 | $this->addForeignKey('importer_fk', '{{%importers_files}}', 'importer_id', '{{%importers}}', 'id'); |
23 | 23 | } |
24 | 24 | |
25 | - public function down() | |
25 | + public function safeDown() | |
26 | 26 | { |
27 | 27 | $this->dropForeignKey('importer_fk', '{{%importers_files}}'); |
28 | 28 | } | ... | ... |
console/migrations/m151013_062829_deletePrefixFunction.php
0 → 100644
1 | +<?php | |
2 | + | |
3 | +use yii\db\Schema; | |
4 | +use yii\db\Migration; | |
5 | + | |
6 | +/** | |
7 | + * Class m151013_062829_deletePrefixFunction | |
8 | + * добавляем две функции - одна ищет префикс по поставщику и бренду, | |
9 | + * другая удаляет найденный префикс из переданного артикула | |
10 | + */ | |
11 | +class m151013_062829_deletePrefixFunction extends Migration | |
12 | +{ | |
13 | + | |
14 | + public function safeUp() | |
15 | + { | |
16 | + $find_prefix = <<< MySQL | |
17 | + CREATE FUNCTION FindPrefix(p_importer_id int, p_brand VARCHAR(100)) RETURNS VARCHAR(50) | |
18 | + BEGIN | |
19 | + DECLARE _prefix varchar(10); | |
20 | + | |
21 | + select prefix into _prefix From w_importers_prefix where importer_id = p_importer_id and brand = p_brand COLLATE utf8_general_ci; | |
22 | + | |
23 | + RETURN (_prefix); | |
24 | + END | |
25 | +MySQL; | |
26 | + | |
27 | + $delete_prefix = <<< MySQL | |
28 | + CREATE FUNCTION DeletePrefix(p_articul VARCHAR(150), p_importer_id int, p_brand VARCHAR(100)) RETURNS VARCHAR(150) | |
29 | + BEGIN | |
30 | + DECLARE _articul varchar(10); | |
31 | + | |
32 | + select substring(p_articul, LENGTH( FindPrefix( p_importer_id, p_brand ) ) + 1 ) into _articul; | |
33 | + RETURN (_articul); | |
34 | + END | |
35 | +MySQL; | |
36 | + | |
37 | + $this->execute($find_prefix); | |
38 | + $this->execute($delete_prefix); | |
39 | + | |
40 | + } | |
41 | + | |
42 | + public function safedown() | |
43 | + { | |
44 | + | |
45 | + $find_prefix = <<< MySQL | |
46 | + drop FUNCTION FindPrefix; | |
47 | +MySQL; | |
48 | + | |
49 | + $delete_prefix = <<< MySQL | |
50 | + drop FUNCTION DeletePrefix; | |
51 | +MySQL; | |
52 | + | |
53 | + $this->execute($find_prefix); | |
54 | + $this->execute($delete_prefix); | |
55 | + | |
56 | + | |
57 | + } | |
58 | + | |
59 | + | |
60 | +} | ... | ... |
console/migrations/m151016_080952_editDetailsCrossesTrigger.php
0 → 100644
1 | +<?php | |
2 | + | |
3 | +use yii\db\Migration; | |
4 | + | |
5 | +/** | |
6 | + * Class m151016_080952_editDetailsCrossesTrigger | |
7 | + * заменим существующий триггер на новый и переименуем его на более информативное имя | |
8 | + * с w_details_crosses на w_details_crosses_before_insert | |
9 | + * существующая функциональность сохраняется | |
10 | + * добавляется замена брендов и вставка новых брендов в таблицу w_brand | |
11 | + */ | |
12 | +class m151016_080952_editDetailsCrossesTrigger extends Migration | |
13 | +{ | |
14 | + public function init() | |
15 | + { | |
16 | + // используем специальное подключение с супер правами | |
17 | + $this->db = 'full_privileges_db'; | |
18 | + parent::init(); | |
19 | + } | |
20 | + | |
21 | + public function safeUp() | |
22 | + { | |
23 | + $drop_trigger = 'Drop trigger if exists w_details_crosses'; | |
24 | + | |
25 | + $before_insert_trigger = <<< MySQL | |
26 | + CREATE DEFINER=`root`@`localhost` TRIGGER `w_details_crosses_before_insert` BEFORE INSERT ON `w_details_crosses` | |
27 | + FOR EACH ROW BEGIN | |
28 | + DECLARE vBrand varchar(150); | |
29 | + SET vBrand = ''; | |
30 | + | |
31 | + | |
32 | + SELECT to_brand INTO vBrand from w_brands_replace where from_brand = NEW.BRAND; | |
33 | + | |
34 | + IF vBrand = '' or vBrand = NULL then | |
35 | + insert ignore into w_brands (BRAND) values(NEW.BRAND); | |
36 | + else | |
37 | + SET NEW.BRAND = vBrand; | |
38 | + end if; | |
39 | + | |
40 | + SET vBrand = ''; | |
41 | + | |
42 | + | |
43 | + SELECT to_brand INTO vBrand from w_brands_replace where from_brand = NEW.CROSS_BRAND; | |
44 | + | |
45 | + IF vBrand = '' or vBrand = NULL then | |
46 | + insert ignore into w_brands (BRAND) values(NEW.CROSS_BRAND); | |
47 | + else | |
48 | + SET NEW.CROSS_BRAND = vBrand; | |
49 | + end if; | |
50 | + | |
51 | + insert ignore into `w_details_description`(`name`, `brand`) | |
52 | + values (NEW.`ARTICLE`,NEW.`BRAND`),(NEW.`CROSS_ARTICLE`,NEW.`CROSS_BRAND`); | |
53 | + END | |
54 | +MySQL; | |
55 | + | |
56 | + $this->execute($drop_trigger); | |
57 | + $this->execute($before_insert_trigger); | |
58 | + | |
59 | + } | |
60 | + | |
61 | + public function safedown() | |
62 | + { | |
63 | + // вернем все как было | |
64 | + $drop_trigger = 'Drop trigger if exists w_details_crosses_before_insert'; | |
65 | + | |
66 | + $before_insert_trigger = <<< MySQL | |
67 | + CREATE DEFINER=`italautocomua`@`localhost` TRIGGER `w_details_crosses` BEFORE INSERT ON `w_details_crosses` | |
68 | + FOR EACH ROW BEGIN | |
69 | + insert ignore into `w_details_description`(`name`, `brand`) | |
70 | + values (NEW.`ARTICLE`,NEW.`BRAND`),(NEW.`CROSS_ARTICLE`,NEW.`CROSS_BRAND`); | |
71 | + END | |
72 | +MySQL; | |
73 | + | |
74 | + $this->execute($drop_trigger); | |
75 | + $this->execute($before_insert_trigger); | |
76 | + | |
77 | + | |
78 | + } | |
79 | + | |
80 | +} | ... | ... |
console/migrations/m151016_090927_editDetailsTrigger.php
0 → 100644
1 | +<?php | |
2 | + | |
3 | +use yii\db\Migration; | |
4 | + | |
5 | +/** | |
6 | + * Class m151016_090927_editDetailsTrigger | |
7 | + * заменим существующий триггер на новый и переименуем его на более информативное имя | |
8 | + * с w_details на w_details_before_insert | |
9 | + * существующая функциональность сохраняется | |
10 | + * добавляется замена брендов и вставка новых брендов в таблицу w_brand, | |
11 | + * а также поиск и замена налогов товаров по таблице w_details_replace | |
12 | + */ | |
13 | +class m151016_090927_editDetailsTrigger extends Migration | |
14 | +{ | |
15 | + public function init() | |
16 | + { | |
17 | + // используем специальное подключение с супер правами | |
18 | + $this->db = 'full_privileges_db'; | |
19 | + parent::init(); | |
20 | + } | |
21 | + | |
22 | + public function safeUp() | |
23 | + { | |
24 | + $drop_trigger = 'Drop trigger if exists w_details'; | |
25 | + | |
26 | + $before_insert_trigger = <<< MySQL | |
27 | + CREATE DEFINER=`root`@`localhost` TRIGGER `w_details_before_insert` BEFORE INSERT ON `w_details` | |
28 | + FOR EACH ROW BEGIN | |
29 | + DECLARE vBrand varchar(150); | |
30 | + DECLARE vArticle varchar(100); | |
31 | + SET vBrand = ''; | |
32 | + SET vArticle = ''; | |
33 | + | |
34 | + SELECT to_brand INTO vBrand from w_brands_replace where from_brand = NEW.BRAND; | |
35 | + | |
36 | + IF vBrand = '' or vBrand = NULL then | |
37 | + insert ignore into w_brands (BRAND) values(NEW.BRAND); | |
38 | + else | |
39 | + SET NEW.BRAND = vBrand; | |
40 | + end if; | |
41 | + | |
42 | + SET vBrand = ''; | |
43 | + | |
44 | + SELECT to_name, to_brand INTO vArticle, vBrand from w_details_replace where from_name = NEW.ARTICLE AND from_brand = NEW.BRAND LIMIT 1; | |
45 | + | |
46 | + IF vArticle != '' then | |
47 | + SET NEW.BRAND = vBrand; | |
48 | + SET NEW.ARTICLE = vArticle; | |
49 | + end if; | |
50 | + | |
51 | + INSERT INTO `w_details_description`(`name`, `brand`, `supplier_description`, `article`) | |
52 | + values (NEW.`ARTICLE`,NEW.`BRAND`,NEW.`DESCR`,NEW.`FULL_ARTICLE`) | |
53 | + on duplicate key update `supplier_description` = if (`supplier_description` = '',values(`supplier_description`),`supplier_description`); | |
54 | + END | |
55 | +MySQL; | |
56 | + | |
57 | + $this->execute($drop_trigger); | |
58 | + $this->execute($before_insert_trigger); | |
59 | + | |
60 | + } | |
61 | + | |
62 | + public function safedown() | |
63 | + { | |
64 | + // вернем все как было | |
65 | + $drop_trigger = 'Drop trigger if exists w_details_before_insert'; | |
66 | + | |
67 | + $before_insert_trigger = <<< MySQL | |
68 | + CREATE DEFINER=`italautocomua`@`localhost` TRIGGER `w_details` BEFORE INSERT ON `w_details` | |
69 | + FOR EACH ROW BEGIN | |
70 | + INSERT INTO `w_details_description`(`name`, `brand`, `supplier_description`, `article`) | |
71 | + values (NEW.`ARTICLE`,NEW.`BRAND`,NEW.`DESCR`,NEW.`FULL_ARTICLE`) | |
72 | + on duplicate key update `supplier_description` = if (`supplier_description` = '',values(`supplier_description`),`supplier_description`); | |
73 | + END | |
74 | +MySQL; | |
75 | + | |
76 | + $this->execute($drop_trigger); | |
77 | + $this->execute($before_insert_trigger); | |
78 | + | |
79 | + } | |
80 | +} | ... | ... |
console/migrations/m151016_130143_add_fk_currency_to_Importers.php
0 → 100644
1 | +<?php | |
2 | + | |
3 | +use yii\db\Schema; | |
4 | +use yii\db\Migration; | |
5 | + | |
6 | +class m151016_130143_add_fk_currency_to_Importers extends Migration | |
7 | +{ | |
8 | + public function safeUp() | |
9 | + { | |
10 | + $this->addForeignKey('currency_fk', '{{%importers}}', 'currency_id', '{{%currency}}', 'id'); | |
11 | + } | |
12 | + | |
13 | + public function safeDown() | |
14 | + { | |
15 | + $this->dropForeignKey('currency_fk', '{{%importers}}'); | |
16 | + } | |
17 | +} | ... | ... |
console/migrations/m151016_144435_addViewDetailsCurrency.php
0 → 100644
1 | +<?php | |
2 | + | |
3 | +use yii\db\Schema; | |
4 | +use yii\db\Migration; | |
5 | + | |
6 | +class m151016_144435_addViewDetailsCurrency extends Migration | |
7 | +{ | |
8 | + public function up() | |
9 | + { | |
10 | + | |
11 | + $view = <<< MySQL | |
12 | + create view w_details_currency as | |
13 | + select w_details.*, w_currency.name, w_currency.rate from w_details | |
14 | + inner join w_importers on w_importers.id = w_details.import_id | |
15 | + inner join w_currency on w_currency.id = w_importers.currency_id; | |
16 | +MySQL; | |
17 | + | |
18 | + $this->execute($view); | |
19 | + | |
20 | + } | |
21 | + | |
22 | + public function down() | |
23 | + { | |
24 | + // вернем все как было | |
25 | + $drop_view = 'drop view if exists w_details_currency'; | |
26 | + | |
27 | + $this->execute($drop_view); | |
28 | + | |
29 | + } | |
30 | +} | ... | ... |
test1.zip deleted
No preview for this file type
vendor/yiisoft/multiparser/Converter.php
vendor/yiisoft/multiparser/CsvParser.php
... | ... | @@ -59,7 +59,6 @@ class CsvParser implements ParserInterface |
59 | 59 | */ |
60 | 60 | public function setup() |
61 | 61 | { |
62 | - | |
63 | 62 | $this->file->setCsvControl($this->delimiter); |
64 | 63 | $this->file->setFlags(\SplFileObject::READ_CSV); |
65 | 64 | $this->file->setFlags(\SplFileObject::SKIP_EMPTY); |
... | ... | @@ -108,7 +107,6 @@ class CsvParser implements ParserInterface |
108 | 107 | } |
109 | 108 | |
110 | 109 | for ($i = 1; $i <= count($row); $i++) { |
111 | - // CustomVarDamp::dump($row[$i]); | |
112 | 110 | |
113 | 111 | if ($row[$i - 1] <> '') { |
114 | 112 | $j++; |
... | ... | @@ -179,11 +177,12 @@ class CsvParser implements ParserInterface |
179 | 177 | protected function readRow( ) |
180 | 178 | { |
181 | 179 | $row = $this->file->fgetcsv(); |
182 | - // уберем нулевые колонки | |
183 | - $row = array_filter($row, function($val){ | |
184 | - return $val <> ''; | |
185 | - }); | |
180 | + | |
186 | 181 | if (is_array($row)) { |
182 | + // уберем нулевые колонки | |
183 | + $row = array_filter($row, function($val){ | |
184 | + return $val <> ''; | |
185 | + }); | |
187 | 186 | // если есть заголовок, то перед конвертацией его нужно назначить |
188 | 187 | if ($this->hasHeaderRow && $this->keys !== NULL) { |
189 | 188 | ... | ... |
vendor/yiisoft/multiparser/DynamicFormHelper.php
... | ... | @@ -29,7 +29,7 @@ class DynamicFormHelper |
29 | 29 | * ключ - имя, значение - значение аттрибута |
30 | 30 | * если передано число, то создается переданное количество аттрибутов с именами - attr_0, attr_1... |
31 | 31 | */ |
32 | - public static function CreateDynamicModel($source) | |
32 | + public static function CreateDynamicModel( $source ) | |
33 | 33 | { |
34 | 34 | $arr_keys = []; |
35 | 35 | if (is_array($source)) { | ... | ... |