[ 'class' => VerbFilter::className(), 'actions' => [ 'delete' => [ 'POST' ], ], ], ]; } /** * Добавление предмета/оборудование если его нет * @param array $options * string 'invent' * string 'model' * string 'comment' * string|NULL 'type' * string|NULL 'netname' * string|NULL 'os' * string|NULL 'mac' * string|NULL 'serial' * string|NULL 'product' * string|NULL 'modelnumber' * @return integer|FALSE */ public static function addIfNeed($options) { $result = [ 'id' => FALSE, 'error' => Yii::t('items', 'Items: Key field missing "invent" :') . print_r($options, TRUE), ]; // Если указан инвентарный номер if (is_array($options) && isset($options[ 'invent' ])) { $item = Items::find() ->where([ 'invent' => $options[ 'invent' ]]); // Ищем оборудование с инвентарным номером. // Если указан серийный номер if (isset($options[ 'serial' ])) { $item = $item->andWhere([ 'like', 'serial', $options[ 'serial' ]]); // Ищем дополнительно с серийным номером } $item = $item->all(); // Получаем все записи if (count($item) > 0) // Записи найдены, выводим первую совпавшую { $result[ 'id' ] = $item[ 0 ]->id; $result[ 'error' ] = ''; } else { // Внесённого оборудования не найдено. Добавим новую запись if (isset($options[ 'model' ])) { $model = ModelsController::addIfNeed($options); if ($model[ 'id' ] === FALSE) { $result[ 'error' ] .= '
' . $model[ 'error' ]; } else { // Создаём новую запись предмета/оборудования $item = new Items(); $item->name = isset($options[ 'netName' ]) ? $options[ 'netName' ] : NULL; // Сетевое имя $item->model_id = $model[ 'id' ]; // идентификатор модели (Подготовлено для преобразования) $item->invent = isset($options[ 'invent' ]) ? $options[ 'invent' ] : NULL; // Инвентарный номер $item->comment = isset($options[ 'comment' ]) ? $options[ 'comment' ] : NULL; // Коментарий $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->checked = 2; // Не инвентризирован (требует внимания после импорта) // Сохраняем запись if ($item->validate() && $item->save()) { $result[ 'id' ] = $item->id; // Возвращаем идентификатор записанного оборудования $result[ 'error' ] = ''; } else { $result[ 'error' ] .= Yii::t('items', 'Items: Failed to add entry: ') . print_r($item->errors, TRUE) . '
'; } } } } } return $result; } /** * Формирование PDF файла для печати QR-кодов для наклеек * @param integer|array|null id * @return mixed */ public function actionPrint() { if (! User::canPermission('takingInventory') ) { return Yii::$app->response->redirect(['site/index']); } // Yii::$app->response->format = \yii\web\Response::FORMAT_RAW; // Yii::$app->response->headers->add('Content-Type', 'application/pdf'); // Список предметов/оборудования, если есть $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', 'Списано' ]) ->orWhere( [ 'checked' => 2 ] ); // Получаем список всех предметов/оборудования, кроме списанного $model = Items::find() ->select('id') ->innerJoin([ 'm' => $modelS ], 'not m.item_id = id') ->all(); // Устанавливаем флаг непроинвентаризированных для всех предметов/оборудования из полученного списка. Items::updateAll([ 'checked' => 0 ], [ '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); $color=''; // Цветовая метка по умолчанию // Получаем все предметы/оборудование с совпадением инвентарного и серийного номеров $items = Items::find()->where([ 'invent' => trim($keys[ 0 ]), 'serial' => trim($keys[ 1 ]) ])->all(); if (count($items) > 0 ) { foreach ($items as $row) { if ( $row->checked == 1 ) { // Этот инвентарный номер был учтён $color = ' color="#FF0000"'; } // Ищем местоположение $moving_id = Moving::find()->select('MAX(id) as id')->groupBy([ 'item_id' ])->where([ 'item_id' => $row->id ])->one()->id; $moving = Moving::find()->where([ 'id' => $moving_id ])->one(); // Показываем название и местоположение $message .= $row->models->name . ' (' . $moving->locations->name . ' [' . $moving->regions->name . '])'; } } else { // Неизвестный инвентарный номер $color = ' color = "#FF8830"'; $message .= Yii::t('items', 'Not found!') . ' ' . Yii::t('items', 'Inventory number') . ': ' . trim($keys[ 0 ]); } // Отмечаем проинвентаризированными все найденные предметы/оборудование Items::updateAll([ 'checked' => 1 ], [ 'invent' => trim($keys[ 0 ]), 'serial' => trim($keys[ 1 ]) ]); if ($message != '') if ( $color == ' color = "#FF8830"') { $message = '' . $message . ''; } else { $message = '' . Yii::t('items', 'Checked item(s): ') . $message . ''; } $model->qrcheck = ''; } } $searchModel = new ItemsSearch(); //$dataProvider = $searchModel->noinvent($model); $dataProvider = $searchModel->noinvent(Yii::$app->request->queryParams); 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']); } if (isset($_GET[ 'pageSize' ])) { Yii::$app->session['pageSize'] = (int) $_GET[ 'pageSize' ]; unset($_GET[ 'pageSize' ]); } $searchModel = new ItemsSearch(); if (isset(Yii::$app->request->queryParams['id'])) { $id = Yii::$app->request->queryParams['id']; $dataProvider = $searchModel->search(Yii::$app->request->queryParams); $pageSize = $dataProvider->pagination->pageSize; $dataProvider->pagination = FALSE; $rows = $dataProvider->getModels(); $page = 0; foreach ($rows as $key => $val) { if ($id == $val->id) { $page = ceil(($key + 1) / $pageSize); break; } } return $this->redirect(['index', 'page' => $page]); } $dataProvider = $searchModel->search(Yii::$app->request->queryParams); return $this->render('index', [ 'searchModel' => $searchModel, 'dataProvider' => $dataProvider, ]); } /** * Импортирование строк товаров из массива */ public function doImport($arrayRows) { // Инициализация счётчиков $arrayReturn = [ 'countRows' => count($arrayRows), 'countImported' => 0, 'countExists' => 0, 'countErrors' => 0, 'errors' => '', ]; // Проверка наличия ключевых полей if ((!isset($arrayRows[ 0 ][ 'model' ])) || (!isset($arrayRows[ 0 ][ 'type' ])) || (!isset($arrayRows[ 0 ][ 'invent' ])) || (!isset($arrayRows[ 0 ][ 'location' ])) || (!isset($arrayRows[ 0 ][ 'region' ])) || (!isset($arrayRows[ 0 ][ 'date' ])) ) { // Сообщение об ошибке $arrayReturn[ 'countErrors' ] = count($arrayRows); $arrayReturn[ 'errors' ] .= '
' . Yii::t('import', 'Skip all. Key column(s) "model", "type", "invent", "location", "region", "date" not found: ') . print_r($arrayRows[0], TRUE); } else { // Просмотрим весь массив foreach($arrayRows as $row) { // ПОлучим местоположения $location = LocationsController::addIfNeed($row); // Получение идентификатора расположения if ( $location[ 'id' ] === FALSE) { // Сообщим об ошибке $arrayReturn[ 'countErrors' ]++; $arrayReturn[ 'errors' ] .= '
' . $location[ 'error' ]; } else { // Попробуем найти или добавить предмет/оборудование $item = $this->addIfNeed($row); if ($item[ 'id' ] === FALSE) { $arrayReturn[ 'countErrors' ]++; $arrayReturn[ 'errors' ] .= '
' . $item[ 'error' ]; } else { // Проверка, что предмет/оборудование уже были в базе $item = Items::find()->where([ 'id' => $item[ 'id' ]])->one(); if ($item->checked < 2) { $arrayReturn[ 'countExists' ]++; } else { $state = isset($row[ 'status' ]) ? StatusController::addIfNeed($row) : StatusController::addIfNeed([ 'status' => 'Склад' ]); if ( $state[ 'id' ] === FALSE ) { // Сообщим об ошибке $arrayReturn[ 'countErrors' ]++; $arrayReturn[ 'errors' ] .= '
' . $state[ 'error' ]; } else { // Новый предмет/оборудование. Пробуем добавить первое перемещение // Проверка что последнее перемещение такое же, как и импортируемое $moving = Moving::find()->where([ 'item_id' => $item[ 'id' ]])->all(); $flag = false; if (count($moving) > 0) { // Признак, что последнее перемещение совпало с импортируемым $flag = $moving[ count($moving) - 1 ]->location_id == $location[ 'id' ]; } // Проверим, что существующая запись перемещения больше добавляемой $moving = Moving::find()->where([ 'item_id' => $item[ 'id' ]])->andWhere([ '>', 'date', $row[ 'date' ]])->all(); if ($flag || count($moving) > 0) { // Добавлять запись до существующих нельзя. $arrayReturn[ 'countExists' ]++; } else { // Проверим, есть ли уже такое перемещение $moving = Moving::find()->where([ 'item_id' => $item[ 'id' ], 'date' => $row[ 'date' ]])->orderBy([ 'id' => SORT_ASC ])->all(); if (count($moving) == 0) { // Такой транзакции нет ещё. $moving = new Moving(); $moving->date = $row[ 'date' ]; $moving->state_id = $state[ 'id' ]; $moving->item_id = $item[ 'id' ]; $moving->location_id = $location[ 'id' ]; $moving->comment = Yii::t('import', 'Import: {comment}', $row); if ($moving->validate() && $moving->save()) { // Записаали первое движение $arrayReturn[ 'countImported' ]++; } else { // Запись не удалась, пробуем удалить предмет/оборудование Items::find()->where([ 'id' => $item[ 'id' ], 'checked' => 2 ])->one()->delete(); // Сообщим об ошибке $arrayReturn[ 'countErrors' ]++; $arrayReturn[ 'errors' ] .= '
' . Yii::t('import', 'Moving: {date} (') . $moving->errors['date'][0]. Yii::t('import', '), Inventory number:{invent}, model: {model}, location: {location} ( {region} )' , $row); } } else { // Такое перемещение уже было $arrayReturn[ 'countExists' ]++; // Удаление дубликатов, если вдруг они образовались if (count($moving) > 1) { // переберём все записи со второй и удалим их. for ($i = 1; $i < count($moving); $i++) { $moving[$i]->delete(); } } } } unset($moving); } } } } } } // Возврат результата импорта return $arrayReturn; } /** * Импорт данных из файла csv * Структура файла данных при выгрузке из 1С: (Колонки могут меняться. * | № п/п | | Предмет/оборудование | | | | | | | Инвентарный номер | Материально отвественное лицо | | | Место размещения | Регион/подразделение | Количество | * Так как 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(); $message = ''; $searchModel = new ItemsSearch(); $dataProvider = $searchModel->search(Yii::$app->request->queryParams); if (Yii::$app->request->isPost) { $rows = []; $columns = []; $columnsNames = [ 'npp' => Yii::t('import', 'No. in order'), 'model' => Yii::t('import', 'Primary means'), 'netname' => Yii::t('import', 'Network name'), 'invent' => Yii::t('import', 'Inventory number'), 'comment' => Yii::t('import', 'Financially responsible person'), 'os' => Yii::t('import', 'Operation system'), 'mac' => Yii::t('import', 'MAC address'), 'serial' => Yii::t('import', 'Serial number'), 'product' => Yii::t('import', 'Product number'), 'modelnumber' => Yii::t('import', 'Model number'), 'date' => Yii::t('import', 'Date of acceptance for registration'), 'location' => Yii::t('import', 'Location'), 'region' => Yii::t('import', 'Region'), 'type' => Yii::t('import', 'Type'), 'status' => Yii::t('import', 'State'), ]; $model->filecsv = UploadedFile::getInstance($model, 'filecsv'); if ($model->upload()) { $fileName = 'upload/' . $model->filecsv->baseName . '.' . $model->filecsv->extension; $handle = fopen($fileName, 'r'); if ($handle !== FALSE) { if (strcasecmp($model->filecsv->extension, 'csv') === 0 ) { // Построчное чтение CSV файла while (($row = fgetcsv($handle, 2048, ';')) !== false ) { // Пока не собраны индексы столбцов из шапки if (count($columns) == 0) { // Ищем строку с заголовком таблицы if ( stripos($row[0], $columnsNames[ 'npp' ]) !== FALSE ) { // Перебираем все колонки foreach ($row as $key => $item) { // Перебираем все названия заголовков колонок foreach($columnsNames as $name => $text) { // Если название совпало, if (stripos($item, $text) !== FALSE) { // Сохраняем индек колонки $columns[ $name ] = $key; } } } } } else { // Перебираем предметы/оборудование (Номер по порядку должен быть целым числом) if (ctype_digit(str_replace(' ', '', $row[ $columns[ 'npp' ]]))) { // Заполняем очередную строку для таблицы $line = []; foreach ($columns as $key => $index) { $line[ $key ] = trim(str_replace("\t", ' ', $row[ $index ])); // Заменяем табуляторы на пробелы и удаляем ведущие и ведомые пробелы. } // Обработка даты if (isset($line[ 'date' ])) { if ($line[ 'date' ] == '#NULL!') $line[ 'date' ] = date('d.m.Y'); } else { $line[ 'date' ] = date('d.m.Y'); } array_push($rows, $line); } } } // Перебор строк файла } else // xls(x) файлы { $inputFileType = \PHPExcel_IOFactory::identify($fileName); // Получение типа данных в файле $excelReader = \PHPExcel_IOFactory::createReader($inputFileType); // Создание потока чтения из файла $excelObj = $excelReader->load($fileName); // Открытие файла $worksheet = $excelObj->getSheet(0); // Работаем только с первым листом (обычно туда выгружает 1С) // Индексы ячеек // Цикл по всем строкам foreach ($worksheet->getRowIterator() as $row) { $cellIterator = $row->getCellIterator(); // Получаем итератор ячеек в строке $cellIterator->setIterateOnlyExistingCells(FALSE); // Указываем проверять даже не установленные ячейки if (count($columns) == 0) // Пока не найдена шапка, проверяем строку { $flag = FALSE; foreach ($cellIterator as $key => $item) { if (($key == 'A') && (stripos($item->getCalculatedValue(), $columnsNames[ 'npp' ]) !== FALSE)) $flag = TRUE; if ($flag) { foreach ($columnsNames as $name => $text) { if (stripos($item->getCalculatedValue(), $text) !== FALSE) { $columns[ $name ] = $key; } } } } } else { $flag = FALSE; $line = []; foreach ($cellIterator as $key => $item) { if ($key == $columns[ 'npp' ]) { $npp = str_replace(' ', '', $item->getCalculatedValue()); if (ctype_digit($npp)) $flag = TRUE; } if ($flag) { foreach($columns as $keym => $index) { if ($index == $key) $line[ $keym ] = trim(str_replace("\t", ' ', $item->getCalculatedValue())); } } } if ($flag) { if (isset($line[ 'date' ])) { if ($line[ 'date' ] == '#NULL!') $line[ 'date' ] = date('d.m.Y'); } else { $line[ 'date' ] = date('d.m.Y'); } array_push($rows, $line); } } } } fclose($handle); $res = $this->doImport($rows); } $message .= Yii::t('items', 'Read {countRows} records.
Imported {countImported} Items.
Exists {countExists} Items.
Error read {countErrors} records.
{errors}', $res); } } 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', 'id' => $id ]); } 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 = 1; $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 */ public function actionAddcopy($is) { if (! User::canPermission('createRecord') ) { return $this->redirect([ 'site/index' ]); } $origin = Items::find()->where([ 'id' => $is ])->one(); $model = new Items(); $model->checked = 1; $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 { $model->isNewRecord = TRUE; $model->model_id = $origin->model_id; $model->invent = $origin->invent; 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.')); } }