[
'class' => VerbFilter::className(),
'actions' => [
'delete' => [ 'POST' ],
],
],
];
}
/**
* Добавление предмета/оборудование если его нет
* @param array $options
* string 'invent'
* string 'model'
* string 'comment'
* integer|NULL 'type_id'
* string|NULL 'typeName'
* string|NULL 'name'
* string|NULL 'os'
* string|NULL 'mac'
* string|NULL 'serial'
* string|NULL 'product'
* string|NULL 'modelnum'
* @return integer|FALSE
*/
public static function addIfNeed($options)
{
// Если указан инвентарный номер
if (is_array($options) && isset($options[ 'invent' ]))
{
$item = Items::find()
->where([ 'like', 'invent', $options[ 'invent' ]]); // Ищем оборудование с инвентарным номером.
// Если указан серийный номер
if (isset($options[ 'serial' ])) {
$item = $item->andWhere([ 'like', 'serial', $options[ 'serial' ]]); // Ищем дополнительно с серийным номером
}
$item = $item->all(); // Получаем все записи
if (count($item) > 0) // Записи найдены, выводим первую совпавшую
{
return $item[0]->id;
}
// Внесённого оборудования не найдено. Добавим новую запись
if (isset($options[ 'model' ]))
{
// Если указан тип предмета/оборудования
if (isset($options[ 'typeName' ]))
{
$type_id = TypesController::addIfNeed($options[ 'typeName' ]); // Найдём или добавим тип
// Если тип не добавили
if($type_id === FALSE)
{
$type_id = NULL; // сделаем его пустым
}
}
else
{
// Если указан идентификатор типа, укажем его
$type_id = isset($options[ 'type_id' ]) ? $options[ 'type_id' ] : NULL;
}
// Создаём новую запись предмета/оборудования
$item = new Items();
$item->name = isset($options[ 'name' ]) ? $options[ 'name' ] : NULL; // Сетевое имя
$item->model = isset($options[ 'model' ]) ? $options[ 'model' ] : NULL; // Наименование
$item->invent = isset($options[ 'invent' ]) ? $options[ 'invent' ] : NULL; // Инвентарный номер
$item->comment = isset($options[ 'comment' ]) ? $options[ 'comment' ] : NULL; // Коментарий
$item->type_id = $type_id; // Идентификатор типа
$item->os = isset($options[ 'os' ]) ? $options[ 'os' ] : NULL; // Операционная система
$item->mac = isset($options[ 'mac' ]) ? $options[ 'mac' ] : NULL; // MAC-адрес
$item->serial = isset($options[ 'serial' ]) ? $options[ 'serial' ] : NULL; // Серийный номер
$item->product = isset($options[ 'product' ]) ? $options[ 'product' ] : NULL; // Код оборудования
$item->modelnumber = isset($options[ 'modelnumber' ]) ? $options[ 'modelnumber' ] : NULL; // Номер модели
$item->checked = false; // Не инвентризирован (требует внимания после импорта)
// Сохраняем запись
if ($item->validate() && $item->save())
{
return $item->id; // Возвращаем идентификатор записанного оборудования
}
}
}
return FALSE;
}
/**
* Формирование PDF файла для печати QR-кодов для наклеек
* @param integer|array|null id
* @return mixed
*/
public function actionPrint()
{
if (! User::canPermission('takingInventory') ) {
return $this->redirect(['site/index']);
}
// Список предметов/оборудования, если есть
$id = Yii::$app->request->get('id');
$models = Items::find();
if (isset($id))
if (is_array($id))
{
$models = $models->where([ 'in', 'id', $id ]); // Несколько предметов/оборудования
} else
{
$models = $models->where([ 'id' => $id ]); // Один предмет/оборудование
}
$models = $models->all(); // Формирование списка
$pdf = Yii::$app->pdf; // Pабота с PDF
$pdf->methods[ 'SetHeader' ] = ''; // Yii::t('items', 'Items');
$pdf->methods[ 'SetFooter' ] = ''; // ['{PAGENO}'];
// Границы листа
$pdf->marginLeft = 5;
$pdf->marginRight = 5;
$pdf->marginTop = 9;
$pdf->marginBottom = 15;
// Имя файла для выгрузки, по умолчанию document.pdf
$pdf->filename = Yii::t('app', Yii::$app->name) . ' (' . Yii::t('items', 'Items') . ').pdf';
// Заполнение страницы данными
$pdf->content = $this->renderPartial('print', [ 'models' => $models ]);
// Выгрузка PDF
return $pdf->render();
}
/**
* Процедура начала инвентаризации.
* @return mixed
*/
public function actionStart_checking()
{
// Проверка доступа для проведения инвентаризации
if (! User::canPermission('takingInventory') ) {
// Переход к списку предметов/оборудования, если доступ не разрешён.
return $this->redirect(['index']);
}
// Запрос на получение списка идентификаторов предметов/оборудования, которые списаны
$modelS = Moving::find()
->select('item_id')
->joinWith('status')
->Where([ 'ilike', Status::tableName() . '.name', 'Списано' ]);
// Получаем список всех предметов/оборудования, кроме списанного
$model = Items::find()
->select('id')
->innerJoin([ 'm' => $modelS ], 'not m.item_id = id')
->all();
// Устанавливаем флаг непроинвентаризированных для всех предметов/оборудования из полученного списка.
Items::updateAll([ 'checked' => false ], [ 'in', 'id', $model ]);
// Переход к списку предметов/оборудования.
return $this->redirect([ 'index' ]);
}
/**
* Инвентаризация
* @param string|null $qrcheck считанный QR-код
* @return mixed
*/
public function actionCheck()
{
// Проверка доступа для проведения инвентаризации
if (! User::canPermission('takingInventory') ) {
// Показ стартовой страницы, если доступ не разрешён.
return $this->redirect(['site/index']);
}
$model = new Check();
$message = '';
if ($model->load(Yii::$app->request->post()))
{
if ((! empty($model->qrcheck)) && strpos($model->qrcheck, ',') !== false)
{
$keys = explode(',', $model->qrcheck);
Items::updateAll([ 'checked' => true ], [ 'invent' => trim($keys[ 0 ]), 'serial' => trim($keys[ 1 ]) ]);
$items = Items::find()->where([ 'invent' => trim($keys[ 0 ]), 'serial' => trim($keys[ 1 ]) ])->all();
//$message = '[0] = "' . $keys[0] . '", [1] = "' . $keys[1] . '"
';
foreach ($items as $row)
{
$message .= $row->model . ' (' . $row->id . ')';
}
if ($message != '')
$message = Yii::t('items', 'Checked item(s): ') . $message;
$model->qrcheck = '';
}
}
$searchModel = new ItemsSearch();
$dataProvider = $searchModel->noinvent($model);
return $this->render('check', [
'message' => $message,
'model' => $model,
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Список всех предметов/оборудования.
* @return mixed
*/
public function actionIndex()
{
if (! User::canPermission('createRecord') ) {
return $this->redirect(['site/index']);
}
$searchModel = new ItemsSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Импорт данных из файла csv
* Структура файла данных при выгрузке из 1С:
* | № п/п | | Предмет/оборудование | | | | | | | Инвентарный номер | Материально отвественное лицо | | | Место размещения | Регион/подразделение | Количество |
* | 0 | 1| 2 | 3| 4| 5| 6| 7| 8| 9 |10 |11|12|13 |14 |15 |
* | A | B| C | D| E| F| G| H| I| J | K | L| M| N | O | P |
* Так как 1С из коробки не умеет выгружать форму в .csv, то приходится сначала выгрузить в .xls(x), и уже из MS Excel/Lible office Calc сохранять в .csv
*/
public function actionImport()
{
if (! User::canPermission('updateRecord') ) {
return $this->redirect(['site/index']);
}
$model = new Import();
$count = 0;
$counti = 0;
$skip = 0;
$existi = 0;
$errors = '';
$message = '';
$searchModel = new ItemsSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
if (Yii::$app->request->isPost)
{
$model->filecsv = UploadedFile::getInstance($model, 'filecsv');
if ($model->upload())
{
$handle = fopen('upload/' . $model->filecsv->baseName . '.' . $model->filecsv->extension, 'r');
if ($handle !== FALSE)
{
if (strcasecmp($model->filecsv->extension, 'csv') === 0 )
{
while (($row = fgetcsv($handle, 1024, ';')) !== false )
{
if (intval($row[ 0 ]) . '' == $row[ 0 ])
{
$location = $row[ 13 ];
$region = $row[ 14 ];
$count++;
$location_id = LocationsController::addIfNeed([ 'name' => $location, 'region' => $region ]);
if ($location_id !== FALSE)
{
$invent = $row[ 9 ];
if (count(Items::find()->where([ 'like', 'invent', $invent ])->all()) == 0)
{
$model_ = $row[ 2 ];
$comment = Yii::t('moving', 'Imported. {comment}', [ 'comment' => $row[ 10 ] ]);
$item_id = $this::addIfNeed([ 'invent' => $invent, 'model' => $model_, 'comment' => $comment ]);
if ( $item_id !== FALSE)
{
$date = date('d.m.Y');
$state_id = StatusController::addIfNeed([ 'name' => 'Склад' ]);
if ($state_id === FALSE)
{
$state_id = NULL;
} // Состояние предмета/оборудование
$moving = new Moving();
$moving->date = $date;
$moving->item_id = $item_id;
$moving->state_id = $state_id;
$moving->location_id = $location_id;
$moving->comment = $comment;
if ($moving->validate() && $moving->save())
{
$counti++;
} // Добавление перемещение
else
{
Items::find([ 'id' => $item_id ])->delete();
$skip++;
$errors .= '
Движение: ' . implode(';', $row);
} // Не удалось добавить перемещение
unset($moving);
}
} // Предмет/оборудование добавлено
else
{
$existi++;
} // Предмет/оборудование уже есть
}
else
{
$skip++;
$errors .= '
Место расположения: ' . implode(';', $row);
} // не удалось найти или добавить место размещения
} // Строка с данными
} // Перебор строк файла
} else // xls файлы
{
$fileName = 'upload/' . $model->filecsv->baseName . '.' . $model->filecsv->extension;
$inputFileType = \PHPExcel_IOFactory::identify($fileName); // Получение типа данных в файле
$excelReader = \PHPExcel_IOFactory::createReader($inputFileType); // Создание потока чтения из файла
$excelObj = $excelReader->load($fileName); // Открытие файла
$worksheet = $excelObj->getSheet(0); // Работаем только с первым листом (обычно туда выгружает 1С)
// Индексы ячеек
$modelInd = NULL;
$nameInd = NULL;
$inventInd = NULL;
$commentInd = NULL;
$osInd = NULL;
$macInd = NULL;
$serialInd = NULL;
$productInd = NULL;
$modelnumberInd = NULL;
$dateInd = NULL;
$locationInd = NULL;
$regionInd = NULL;
$typeInd = NULL;
// Цикл по всем строкам
$rowNum = 0;
$lastColumn = $worksheet->getHighestColumn();
foreach ($worksheet->getRowIterator() as $row)
{
$rowNum++;
$cellIterator = $row->getCellIterator(); // Получаем итератор ячеек в строке
$cellIterator->setIterateOnlyExistingCells(FALSE); // Указываем проверять даже не установленные ячейки
//$myRow = []; // Массив ячеек исключительно для тестирования
$flag = FALSE; // Признак строки шапки
if ($inventInd === NULL) // Пока не найдена шапка, проверяем строку
foreach($cellIterator as $key => $cell)
{
if (($key == 'A') && (stripos($cell->getValue(), '№') !== FALSE) ) $flag = TRUE; // Если строка шапка, установим флаг
if ($flag) // Работаем с шапкой
{
$counti = $rowNum;
$val = $cell->getValue(); // Получаем значение ячейки
if (stripos($val, 'Основное средство') !== FALSE) $modelInd = $key; // Фиксируем колонку, в которой предмет/оборудование
if (stripos($val, 'Сетевое имя') !== FALSE) $nameInd = $key; // Фиксируем колонку, в которой сетевое имя
if (stripos($val, 'Инвентарный номер') !== FALSE) $inventInd = $key; // Фиксируем колонку, в которой инвентарный номер
if (stripos($val, 'МОЛ') !== FALSE) $commentInd = $key; // Фиксируем колонку, в которой Комментарии
if (stripos($val, 'Операционная система') !== FALSE) $osInd = $key; // Фиксируем колонку, в которой операционная система
if (stripos($val, 'Сетевой адрес') !== FALSE) $macInd = $key; // Фиксируем колонку, в которой сетевой адрес
if (stripos($val, 'Серийный номер') !== FALSE) $serialInd = $key; // Фиксируем колонку, в которой серийный номер
if (stripos($val, 'Код продукта') !== FALSE) $productInd = $key; // Фиксируем колонку, в которой код продукта
if (stripos($val, 'Номер модели') !== FALSE) $modelnumberInd = $key; // Фиксируем колонку, в которой номер модели
if (stripos($val, 'Дата') !== FALSE) $dateInd = $key; // Фиксируем колонку, в которой дата постановки на учёт
if (stripos($val, 'ИФО') !== FALSE) $locationInd = $key; // Фиксируем колонку, в которой место хранения
if (stripos($val, 'Место хранения') !== FALSE) $regionInd = $key; // Фиксируем колонку, в которой регион/подразделение
if (stripos($val, 'Тип') !== FALSE) $typeInd = $key; // Фиксируем колонку, в которой тип оборудования
}
//array_push($myRow, '['.$key.']:' . $cell->getValue()); // Наполнение массива ячеек
}
else
{
$npp = str_replace(' ', '', $worksheet->getCell('A'.$rowNum)->getValue());
if (ctype_digit($npp))
{
if (($modelInd === NULL) || ($inventInd === NULL) || ($locationInd === NULL) || ($regionInd === NULL))
{
$errors .= '
одно из важных полей отсутствует';
break;
}
$location = $worksheet->getCell($locationInd . $rowNum)->getValue();
$region = $worksheet->getCell($regionInd . $rowNum)->getValue();
$location_id = LocationsController::addIfNeed([ 'name' => $location, 'region' => $region ]); // Получение идентификатора расположения
if ($location_id !== FALSE)
{
$count++; // Посчитаем строку оборудования
$invent = $worksheet->getCell($inventInd . $rowNum)->getValue(); // Инвентарный номер
if (count(Items::find()->where([ 'like', 'invent', $invent ])->all()) == 0)
{
$model_ = $worksheet->getCell($modelInd . $rowNum)->getValue();
$comment = $commentInd !== NULL ? Yii::t('moving', 'Imported. {comment}', [ 'comment' => $worksheet->getCell($commentInd . $rowNum)->getValue() ]) : NULL; // Комментарии
$item_id = ($typeInd !== NULL ?
$this::addIfNeed([ 'invent' => $invent, 'model' => $model_, 'comment' => $comment, 'typeName' => $worksheet->getCell($typeInd . $rowNum)->getValue() ]) :
$this::addIfNeed([ 'invent' => $invent, 'model' => $model_, 'comment' => $comment ])); // Получение идентификатора оборудования
if ($item_id !== FALSE)
{
$date = $dateInd !== NULL ? $worksheet->getCell($dateInd . $rowNum)->getValue() : date('d.m.Y');
if ($date == '#NULL!') $date = date('d.m.Y');
$state_id = StatusController::addIfNeed([ 'name' => 'Склад' ]);
if ($state_id === FALSE)
{
$state_id = NULL;
} // Состояние предмета/оборудование
$moving = new Moving();
$moving->date = $date;
$moving->item_id = $item_id;
$moving->state_id = $state_id;
$moving->location_id = $location_id;
$moving->comment = $comment;
if ($moving->validate() && $moving->save())
{
$counti++;
} // Добавление перемещение
else
{
Items::find([ 'id' => $item_id ])->one()->delete();
$skip++;
$errors .= '
Движение: ('. implode('===',$moving->errors['date']) . '::' . $moving->date .')' . implode(';', $worksheet->rangeToArray('A' . $rowNum . ':' . $lastColumn . $rowNum, NULL, NULL, FALSE)[0]);
} // Не удалось добавить перемещение
unset($moving);
}
} // Предмет/оборудование добавлено
else
{
$existi++;
} // Предмет/оборудование уже есть
}
else
{
$skip++;
$errors .= '
Место расположения: ' . implode(';', $worksheet->rangeToArray('A' . $rowNum . ':' . $lastColumn . $rowNum, NULL, NULL, FALSE)[0]);
} // не удалось найти или добавить место размещения
}
else
{
$skip++;
}
// Формирование строки для тестирования
//$skip++;
//$errors .= '
Проверка:' . implode(';', $myRow);
}
}
if (false)
{
$lastRow = $worksheet->getHighestRow();
$lastColumn = $worksheet->getHighestColumn();
for ($row = 1; $row <= $lastRow; $row++)
{
$npp = $worksheet->getCell('A' . $row)->getValue(); // Номер по порядку
if (ctype_digit($npp)) // Исключаем все строки кроме нумерованых
{
$location = $worksheet->getCell('N' . $row)->getValue(); // Расположение
$region = $worksheet->getCell('O' . $row)->getValue(); // Регион
$location_id = LocationsController::addIfNeed([ 'name' => $location, 'region' => $region ]); // Получение идентификатора расположения
if ($location_id !== FALSE)
{
$count++;
$invent = $worksheet->getCell('J' . $row)->getValue(); // Инвентарный номер
if (count(Items::find()->where([ 'like', 'invent', $invent ])->all()) == 0)
{
$model_ = $worksheet->getCell('C' . $row)->getValue(); // Название модели
$comment = Yii::t('moving', 'Imported. {comment}', [ 'comment' => $worksheet->getCell('K' . $row)->getValue() ]); // Коментарий
$item_id = $this::addIfNeed([ 'invent' => $invent, 'model' => $model_, 'comment' => $comment ]); // Получение идентификатора оборудования
if ( $item_id !== FALSE)
{
$date = date('d.m.Y');
$state_id = StatusController::addIfNeed([ 'name' => 'Склад' ]);
if ($state_id === FALSE)
{
$state_id = NULL;
} // Состояние предмета/оборудование
$moving = new Moving();
$moving->date = $date;
$moving->item_id = $item_id;
$moving->state_id = $state_id;
$moving->location_id = $location_id;
$moving->comment = $comment;
if ($moving->validate() && $moving->save())
{
$counti++;
} // Добавление перемещение
else
{
Items::find([ 'id' => $item_id ])->one()->delete();
$skip++;
$errors .= '
Движение: ('. implode('===',$moving->errors['date']) . '::' . $moving->date .')' . implode(';', $worksheet->rangeToArray('A' . $row . ':' . $lastColumn . $row, NULL, NULL, FALSE)[0]);
} // Не удалось добавить перемещение
}
} // Предмет/оборудование добавлено
else
{
$existi++;
} // Предмет/оборудование уже есть
}
else
{
$skip++;
$errors .= '
Место расположения: ' . implode(';', $worksheet->rangeToArray('A' . $row . ':' . $lastColumn . $row, NULL, NULL, FALSE)[0]);
} // не удалось найти или добавить место размещения
}
else
{
$skip++;
}
}
}
}
fclose($handle);
}
$message .= Yii::t('items', 'Read {count} records.
Imported {counti} Items.
Exists {exist} Items.
Error read {skip} records.
{errors}',
[ 'counti' => $counti, 'count' => $count, 'exist' => $existi, 'skip' => $skip, 'errors' => $errors ]);
}
}
return $this->render('import',[
'message' => $message,
'model' => $model,
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Показ одного предмета/оборудования. (не используется)
* @param integer $id
* @return mixed
* @throws NotFoundHttpException если предмет/оборудование отсутствует
*/
public function actionView($id)
{
if (! User::canPermission('updateRecord') ) {
return $this->redirect(['index']);
}
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Создание нового предмета/оборудования.
* @return mixed
*/
public function actionCreate()
{
if (! User::canPermission('createRecord') ) {
return $this->redirect(['site/index']);
}
$model = new Items(); // Новый предмет/оборудование
$model->checked = true;
$modelm = new Moving();
if ($model->load(Yii::$app->request->post()) && $model->save())
{
// Удалось сохранить, создаём первую запись движения
if ($modelm->load(Yii::$app->request->post()))
{
$modelm->item_id = $model->id;
$modelm->comment = 'Поступление';
if ( $modelm->save() ) // Пробуем сохранить движение
{
return $this->redirect([ 'index', 'id' => $model->id ]); // Если удалось, показываем список оборудования
} else
{
$this->findModel($model->id)->delete(); // Иначе удаляем созданную запись предмета/оборудования
unset($model->id); // Очищаем идентификатор предмета/оборудования
$model->isNewRecord = true;
return $this->render('create', [ // Показываем форму создания нового предмета/оборудования
'model' => $model,
'modelm' => $modelm,
]);
}
} else
{
$this->findModel($model->id)->delete(); // Иначе удаляем созданную запись предмета/оборудования
unset($model->id); // Очищаем идентификатор предмета/оборудования
$model->isNewRecord = true;
return $this->render('create', [ // Показываем форму создания нового предмета/оборудования
'model' => $model,
'modelm' => $modelm,
]);
}
} else // не удалось сохранить - отображаем форму создания нового предмета/оборудования
{
return $this->render('create', [
'model' => $model,
'modelm' => $modelm,
]);
}
}
/**
* Изменение существующего предмета/оборудвания.
* Если премет/обрудование сохранён, то возвращаемся на страницу списка всех предметов/оборудования.
* @param integer $id
* @return mixed
* @throws NotFoundHttpException если предмет/оборудование отсутствует
*/
public function actionUpdate($id)
{
if (! User::canPermission('updateRecord') ) {
return $this->redirect(['index']);
}
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) && $model->save())
{
return $this->redirect([ 'index', 'id' => $model->id ]);
}
$searchModelM = new MovingSearch([ 'item_id' => $model->id ]);
$dataProviderM = $searchModelM->search(Yii::$app->request->queryParams);
return $this->render('update', [
'searchModelM' => $searchModelM,
'dataProviderM' => $dataProviderM,
'model' => $model,
]);
}
/**
* Удаляет сушествующий предмет/оборудование.
* Если премет/обрудование удалён, то возвращаемся на страницу списка всех предметов/оборудования.
* @param integer $id
* @return mixed
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionDelete($id)
{
if (! User::canPermission('updateRecord') ) {
return $this->redirect(['site/index']);
}
$this->findModel($id)->delete();
return $this->redirect([ 'index' ]);
}
/**
* Finds the Items model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* @param integer $id
* @return Items the loaded model
* @throws NotFoundHttpException если предмет/оборудование отсутствует
*/
protected function findModel($id)
{
if (($model = Items::findOne($id)) !== null)
{
return $model;
}
throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.'));
}
}