3 namespace app\controllers;
10 use app\models\Locations;
11 use app\models\ItemsSearch;
12 use app\models\MovingSearch;
13 use app\models\Status;
15 use yii\web\Controller;
16 use yii\web\NotFoundHttpException;
17 use yii\web\UploadedFile;
18 use yii\filters\VerbFilter;
23 #require "/vendor/phpoffice/phpexcel/Classes/PHPExcel.php";
26 * ItemsController implements the CRUD actions for Items model.
28 class ItemsController extends Controller
33 public function behaviors()
37 'class' => VerbFilter::className(),
39 'delete' => [ 'POST' ],
46 * Добавление предмета/оборудование если его нет
47 * @param array $options
51 * integer|NULL 'type_id'
52 * string|NULL 'typeName'
56 * string|NULL 'serial'
57 * string|NULL 'product'
58 * string|NULL 'modelnum'
59 * @return integer|FALSE
61 public static function addIfNeed($options)
63 // Если указан инвентарный номер
64 if (is_array($options) && isset($options[ 'invent' ]))
67 ->where([ 'like', 'invent', $options[ 'invent' ]]); // Ищем оборудование с инвентарным номером.
68 // Если указан серийный номер
69 if (isset($options[ 'serial' ])) {
70 $item = $item->andWhere([ 'like', 'serial', $options[ 'serial' ]]); // Ищем дополнительно с серийным номером
72 $item = $item->all(); // Получаем все записи
74 if (count($item) > 0) // Записи найдены, выводим первую совпавшую
78 // Внесённого оборудования не найдено. Добавим новую запись
79 if (isset($options[ 'model' ]))
81 // Если указан тип предмета/оборудования
82 if (isset($options[ 'typeName' ]))
84 $type_id = TypesController::addIfNeed($options[ 'typeName' ]); // Найдём или добавим тип
85 // Если тип не добавили
86 if($type_id === FALSE)
88 $type_id = NULL; // сделаем его пустым
93 // Если указан идентификатор типа, укажем его
94 $type_id = isset($options[ 'type_id' ]) ? $options[ 'type_id' ] : NULL;
96 // Создаём новую запись предмета/оборудования
98 $item->name = isset($options[ 'name' ]) ? $options[ 'name' ] : NULL; // Сетевое имя
99 $item->model = isset($options[ 'model' ]) ? $options[ 'model' ] : NULL; // Наименование
100 $item->invent = isset($options[ 'invent' ]) ? $options[ 'invent' ] : NULL; // Инвентарный номер
101 $item->comment = isset($options[ 'comment' ]) ? $options[ 'comment' ] : NULL; // Коментарий
102 $item->type_id = $type_id; // Идентификатор типа
103 $item->os = isset($options[ 'os' ]) ? $options[ 'os' ] : NULL; // Операционная система
104 $item->mac = isset($options[ 'mac' ]) ? $options[ 'mac' ] : NULL; // MAC-адрес
105 $item->serial = isset($options[ 'serial' ]) ? $options[ 'serial' ] : NULL; // Серийный номер
106 $item->product = isset($options[ 'product' ]) ? $options[ 'product' ] : NULL; // Код оборудования
107 $item->modelnumber = isset($options[ 'modelnumber' ]) ? $options[ 'modelnumber' ] : NULL; // Номер модели
108 $item->checked = false; // Не инвентризирован (требует внимания после импорта)
110 if ($item->validate() && $item->save())
112 return $item->id; // Возвращаем идентификатор записанного оборудования
120 * Формирование PDF файла для печати QR-кодов для наклеек
121 * @param integer|array|null id
124 public function actionPrint()
126 if (! User::canPermission('takingInventory') ) {
127 return $this->redirect(['site/index']);
129 // Список предметов/оборудования, если есть
130 $id = Yii::$app->request->get('id');
132 $models = Items::find();
136 $models = $models->where([ 'in', 'id', $id ]); // Несколько предметов/оборудования
139 $models = $models->where([ 'id' => $id ]); // Один предмет/оборудование
141 $models = $models->all(); // Формирование списка
143 $pdf = Yii::$app->pdf; // Pабота с PDF
145 $pdf->methods[ 'SetHeader' ] = ''; // Yii::t('items', 'Items');
146 $pdf->methods[ 'SetFooter' ] = ''; // ['{PAGENO}'];
148 $pdf->marginLeft = 5;
149 $pdf->marginRight = 5;
151 $pdf->marginBottom = 15;
152 // Имя файла для выгрузки, по умолчанию document.pdf
153 $pdf->filename = Yii::t('app', Yii::$app->name) . ' (' . Yii::t('items', 'Items') . ').pdf';
155 // Заполнение страницы данными
156 $pdf->content = $this->renderPartial('print', [ 'models' => $models ]);
159 return $pdf->render();
163 * Процедура начала инвентаризации.
166 public function actionStart_checking()
168 // Проверка доступа для проведения инвентаризации
169 if (! User::canPermission('takingInventory') ) {
170 // Переход к списку предметов/оборудования, если доступ не разрешён.
171 return $this->redirect(['index']);
173 // Запрос на получение списка идентификаторов предметов/оборудования, которые списаны
174 $modelS = Moving::find()
177 ->Where([ 'ilike', Status::tableName() . '.name', 'Списано' ]);
179 // Получаем список всех предметов/оборудования, кроме списанного
180 $model = Items::find()
182 ->innerJoin([ 'm' => $modelS ], 'not m.item_id = id')
185 // Устанавливаем флаг непроинвентаризированных для всех предметов/оборудования из полученного списка.
186 Items::updateAll([ 'checked' => false ], [ 'in', 'id', $model ]);
188 // Переход к списку предметов/оборудования.
189 return $this->redirect([ 'index' ]);
194 * @param string|null $qrcheck считанный QR-код
197 public function actionCheck()
199 // Проверка доступа для проведения инвентаризации
200 if (! User::canPermission('takingInventory') ) {
201 // Показ стартовой страницы, если доступ не разрешён.
202 return $this->redirect(['site/index']);
205 $model = new Check();
207 if ($model->load(Yii::$app->request->post()))
209 if ((! empty($model->qrcheck)) && strpos($model->qrcheck, ',') !== false)
211 $keys = explode(',', $model->qrcheck);
212 Items::updateAll([ 'checked' => true ], [ 'invent' => trim($keys[ 0 ]), 'serial' => trim($keys[ 1 ]) ]);
213 $items = Items::find()->where([ 'invent' => trim($keys[ 0 ]), 'serial' => trim($keys[ 1 ]) ])->all();
214 //$message = '[0] = "' . $keys[0] . '", [1] = "' . $keys[1] . '"<br />';
215 foreach ($items as $row)
217 $message .= $row->model . ' (' . $row->id . ')';
220 $message = Yii::t('items', 'Checked item(s): ') . $message;
221 $model->qrcheck = '';
224 $searchModel = new ItemsSearch();
225 $dataProvider = $searchModel->noinvent($model);
227 return $this->render('check', [
228 'message' => $message,
230 'searchModel' => $searchModel,
231 'dataProvider' => $dataProvider,
236 * Список всех предметов/оборудования.
239 public function actionIndex()
241 if (! User::canPermission('createRecord') ) {
242 return $this->redirect(['site/index']);
244 $searchModel = new ItemsSearch();
245 $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
247 return $this->render('index', [
248 'searchModel' => $searchModel,
249 'dataProvider' => $dataProvider,
254 * Импорт данных из файла csv
255 * Структура файла данных при выгрузке из 1С:
256 * | № п/п | | Предмет/оборудование | | | | | | | Инвентарный номер | Материально отвественное лицо | | | Место размещения | Регион/подразделение | Количество |
257 * | 0 | 1| 2 | 3| 4| 5| 6| 7| 8| 9 |10 |11|12|13 |14 |15 |
258 * | A | B| C | D| E| F| G| H| I| J | K | L| M| N | O | P |
259 * Так как 1С из коробки не умеет выгружать форму в .csv, то приходится сначала выгрузить в .xls(x), и уже из MS Excel/Lible office Calc сохранять в .csv
261 public function actionImport()
263 if (! User::canPermission('updateRecord') ) {
264 return $this->redirect(['site/index']);
266 $model = new Import();
273 $searchModel = new ItemsSearch();
274 $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
275 if (Yii::$app->request->isPost)
277 $nppColumnName = Yii::t('import', 'No. in order');
278 $itemColumnName = Yii::t('import', 'Primary means');
279 $netColumnName = Yii::t('import', 'Network name');
280 $inventColumnName = Yii::t('import', 'Inventory number');
281 $molColumnName = Yii::t('import', 'Financially responsible person');
282 $osColumnName = Yii::t('import', 'Operation system');
283 $macColumnName = Yii::t('import', 'MAC address');
284 $serialColumnName = Yii::t('import', 'Serial number');
285 $ProdictColumnName = Yii::t('import', 'Product number');
286 $modelNumberColumnName = Yii::t('import', 'Model number');
287 $dateColumnName = Yii::t('import', 'Date of acceptance for registration');
288 $locationColumnName = Yii::t('import', 'Location');
289 $regionColumnName = Yii::t('import', 'Region');
290 $typeColumnName = Yii::t('import', 'Type');
292 $model->filecsv = UploadedFile::getInstance($model, 'filecsv');
293 if ($model->upload())
295 $fileName = 'upload/' . $model->filecsv->baseName . '.' . $model->filecsv->extension;
296 $handle = fopen($fileName, 'r');
297 if ($handle !== FALSE)
299 if (strcasecmp($model->filecsv->extension, 'csv') === 0 )
301 while (($row = fgetcsv($handle, 1024, ';')) !== false )
303 if (intval($row[ 0 ]) . '' == $row[ 0 ])
305 $location = $row[ 13 ];
306 $region = $row[ 14 ];
308 $location_id = LocationsController::addIfNeed([ 'name' => $location, 'region' => $region ]);
309 if ($location_id !== FALSE)
312 if (count(Items::find()->where([ 'like', 'invent', $invent ])->all()) == 0)
315 $comment = Yii::t('moving', 'Imported. {comment}', [ 'comment' => $row[ 10 ] ]);
316 $item_id = $this::addIfNeed([ 'invent' => $invent, 'model' => $model_, 'comment' => $comment ]);
317 if ( $item_id !== FALSE)
319 $date = date('d.m.Y');
320 $state_id = StatusController::addIfNeed([ 'name' => 'Склад' ]);
321 if ($state_id === FALSE)
324 } // Состояние предмета/оборудование
326 $moving = new Moving();
327 $moving->date = $date;
328 $moving->item_id = $item_id;
329 $moving->state_id = $state_id;
330 $moving->location_id = $location_id;
331 $moving->comment = $comment;
332 if ($moving->validate() && $moving->save())
335 } // Добавление перемещение
338 Items::find([ 'id' => $item_id ])->delete();
340 $errors .= '<br>Движение: ' . implode(';', $row);
341 } // Не удалось добавить перемещение
344 } // Предмет/оборудование добавлено
348 } // Предмет/оборудование уже есть
353 $errors .= '<br>Место расположения: ' . implode(';', $row);
354 } // не удалось найти или добавить место размещения
355 } // Строка с данными
356 } // Перебор строк файла
359 $fileName = 'upload/' . $model->filecsv->baseName . '.' . $model->filecsv->extension;
360 $inputFileType = \PHPExcel_IOFactory::identify($fileName); // Получение типа данных в файле
361 $excelReader = \PHPExcel_IOFactory::createReader($inputFileType); // Создание потока чтения из файла
362 $excelObj = $excelReader->load($fileName); // Открытие файла
363 $worksheet = $excelObj->getSheet(0); // Работаем только с первым листом (обычно туда выгружает 1С)
373 $modelnumberInd = NULL;
379 // Цикл по всем строкам
381 $lastColumn = $worksheet->getHighestColumn();
382 foreach ($worksheet->getRowIterator() as $row)
385 $cellIterator = $row->getCellIterator(); // Получаем итератор ячеек в строке
386 $cellIterator->setIterateOnlyExistingCells(FALSE); // Указываем проверять даже не установленные ячейки
388 //$myRow = []; // Массив ячеек исключительно для тестирования
389 $flag = FALSE; // Признак строки шапки
390 if ($inventInd === NULL) // Пока не найдена шапка, проверяем строку
391 foreach($cellIterator as $key => $cell)
393 if (($key == 'A') && (stripos($cell->getValue(), '№') !== FALSE) ) $flag = TRUE; // Если строка шапка, установим флаг
395 if ($flag) // Работаем с шапкой
398 $val = $cell->getValue(); // Получаем значение ячейки
399 if (stripos($val, 'Основное средство') !== FALSE) $modelInd = $key; // Фиксируем колонку, в которой предмет/оборудование
400 if (stripos($val, 'Сетевое имя') !== FALSE) $nameInd = $key; // Фиксируем колонку, в которой сетевое имя
401 if (stripos($val, 'Инвентарный номер') !== FALSE) $inventInd = $key; // Фиксируем колонку, в которой инвентарный номер
402 if (stripos($val, 'МОЛ') !== FALSE) $commentInd = $key; // Фиксируем колонку, в которой Комментарии
403 if (stripos($val, 'Операционная система') !== FALSE) $osInd = $key; // Фиксируем колонку, в которой операционная система
404 if (stripos($val, 'Сетевой адрес') !== FALSE) $macInd = $key; // Фиксируем колонку, в которой сетевой адрес
405 if (stripos($val, 'Серийный номер') !== FALSE) $serialInd = $key; // Фиксируем колонку, в которой серийный номер
406 if (stripos($val, 'Код продукта') !== FALSE) $productInd = $key; // Фиксируем колонку, в которой код продукта
407 if (stripos($val, 'Номер модели') !== FALSE) $modelnumberInd = $key; // Фиксируем колонку, в которой номер модели
408 if (stripos($val, 'Дата') !== FALSE) $dateInd = $key; // Фиксируем колонку, в которой дата постановки на учёт
409 if (stripos($val, 'ИФО') !== FALSE) $locationInd = $key; // Фиксируем колонку, в которой место хранения
410 if (stripos($val, 'Место хранения') !== FALSE) $regionInd = $key; // Фиксируем колонку, в которой регион/подразделение
411 if (stripos($val, 'Тип') !== FALSE) $typeInd = $key; // Фиксируем колонку, в которой тип оборудования
414 //array_push($myRow, '['.$key.']:' . $cell->getValue()); // Наполнение массива ячеек
418 $npp = str_replace(' ', '', $worksheet->getCell('A'.$rowNum)->getValue());
419 if (ctype_digit($npp))
421 if (($modelInd === NULL) || ($inventInd === NULL) || ($locationInd === NULL) || ($regionInd === NULL))
423 $errors .= '<br>одно из важных полей отсутствует';
426 $location = $worksheet->getCell($locationInd . $rowNum)->getValue();
427 $region = $worksheet->getCell($regionInd . $rowNum)->getValue();
428 $location_id = LocationsController::addIfNeed([ 'name' => $location, 'region' => $region ]); // Получение идентификатора расположения
429 if ($location_id !== FALSE)
431 $count++; // Посчитаем строку оборудования
432 $invent = $worksheet->getCell($inventInd . $rowNum)->getValue(); // Инвентарный номер
433 if (count(Items::find()->where([ 'like', 'invent', $invent ])->all()) == 0)
435 $model_ = $worksheet->getCell($modelInd . $rowNum)->getValue();
436 $comment = $commentInd !== NULL ? Yii::t('moving', 'Imported. {comment}', [ 'comment' => $worksheet->getCell($commentInd . $rowNum)->getValue() ]) : NULL; // Комментарии
437 $item_id = ($typeInd !== NULL ?
438 $this::addIfNeed([ 'invent' => $invent, 'model' => $model_, 'comment' => $comment, 'typeName' => $worksheet->getCell($typeInd . $rowNum)->getValue() ]) :
439 $this::addIfNeed([ 'invent' => $invent, 'model' => $model_, 'comment' => $comment ])); // Получение идентификатора оборудования
440 if ($item_id !== FALSE)
442 $date = $dateInd !== NULL ? $worksheet->getCell($dateInd . $rowNum)->getValue() : date('d.m.Y');
443 if ($date == '#NULL!') $date = date('d.m.Y');
445 $state_id = StatusController::addIfNeed([ 'name' => 'Склад' ]);
446 if ($state_id === FALSE)
449 } // Состояние предмета/оборудование
451 $moving = new Moving();
452 $moving->date = $date;
453 $moving->item_id = $item_id;
454 $moving->state_id = $state_id;
455 $moving->location_id = $location_id;
456 $moving->comment = $comment;
457 if ($moving->validate() && $moving->save())
460 } // Добавление перемещение
463 Items::find([ 'id' => $item_id, 'checked' => FALSE ])->one()->delete();
465 $errors .= '<br>Движение: ('. implode('===',$moving->errors['date']) . '::' . $moving->date .')' . implode(';', $worksheet->rangeToArray('A' . $rowNum . ':' . $lastColumn . $rowNum, NULL, NULL, FALSE)[0]);
466 } // Не удалось добавить перемещение
469 } // Предмет/оборудование добавлено
473 } // Предмет/оборудование уже есть
478 $errors .= '<br>Место расположения: ' . implode(';', $worksheet->rangeToArray('A' . $rowNum . ':' . $lastColumn . $rowNum, NULL, NULL, FALSE)[0]);
479 } // не удалось найти или добавить место размещения
490 $message .= Yii::t('items', 'Read {count} records.<br />Imported {counti} Items.<br />Exists {exist} Items.<br />Error read {skip} records.<br />{errors}',
491 [ 'counti' => $counti, 'count' => $count, 'exist' => $existi, 'skip' => $skip, 'errors' => $errors ]);
494 return $this->render('import',[
495 'message' => $message,
497 'searchModel' => $searchModel,
498 'dataProvider' => $dataProvider,
503 * Показ одного предмета/оборудования. (не используется)
506 * @throws NotFoundHttpException если предмет/оборудование отсутствует
508 public function actionView($id)
510 if (! User::canPermission('updateRecord') ) {
511 return $this->redirect(['index']);
513 return $this->render('view', [
514 'model' => $this->findModel($id),
519 * Создание нового предмета/оборудования.
522 public function actionCreate()
524 if (! User::canPermission('createRecord') ) {
525 return $this->redirect(['site/index']);
527 $model = new Items(); // Новый предмет/оборудование
528 $model->checked = true;
529 $modelm = new Moving();
530 if ($model->load(Yii::$app->request->post()) && $model->save())
532 // Удалось сохранить, создаём первую запись движения
533 if ($modelm->load(Yii::$app->request->post()))
535 $modelm->item_id = $model->id;
536 $modelm->comment = 'Поступление';
538 if ( $modelm->save() ) // Пробуем сохранить движение
540 return $this->redirect([ 'index', 'id' => $model->id ]); // Если удалось, показываем список оборудования
543 $this->findModel($model->id)->delete(); // Иначе удаляем созданную запись предмета/оборудования
544 unset($model->id); // Очищаем идентификатор предмета/оборудования
545 $model->isNewRecord = true;
546 return $this->render('create', [ // Показываем форму создания нового предмета/оборудования
553 $this->findModel($model->id)->delete(); // Иначе удаляем созданную запись предмета/оборудования
554 unset($model->id); // Очищаем идентификатор предмета/оборудования
555 $model->isNewRecord = true;
556 return $this->render('create', [ // Показываем форму создания нового предмета/оборудования
561 } else // не удалось сохранить - отображаем форму создания нового предмета/оборудования
563 return $this->render('create', [
572 * Изменение существующего предмета/оборудвания.
573 * Если премет/обрудование сохранён, то возвращаемся на страницу списка всех предметов/оборудования.
576 * @throws NotFoundHttpException если предмет/оборудование отсутствует
578 public function actionUpdate($id)
580 if (! User::canPermission('updateRecord') ) {
581 return $this->redirect(['index']);
583 $model = $this->findModel($id);
585 if ($model->load(Yii::$app->request->post()) && $model->save())
587 return $this->redirect([ 'index', 'id' => $model->id ]);
590 $searchModelM = new MovingSearch([ 'item_id' => $model->id ]);
591 $dataProviderM = $searchModelM->search(Yii::$app->request->queryParams);
593 return $this->render('update', [
594 'searchModelM' => $searchModelM,
595 'dataProviderM' => $dataProviderM,
601 * Удаляет сушествующий предмет/оборудование.
602 * Если премет/обрудование удалён, то возвращаемся на страницу списка всех предметов/оборудования.
605 * @throws NotFoundHttpException if the model cannot be found
607 public function actionDelete($id)
609 if (! User::canPermission('updateRecord') ) {
610 return $this->redirect(['site/index']);
612 $this->findModel($id)->delete();
614 return $this->redirect([ 'index' ]);
618 * Finds the Items model based on its primary key value.
619 * If the model is not found, a 404 HTTP exception will be thrown.
621 * @return Items the loaded model
622 * @throws NotFoundHttpException если предмет/оборудование отсутствует
624 protected function findModel($id)
626 if (($model = Items::findOne($id)) !== null)
631 throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.'));