Панели аккаунтов, товаров, поставок, прайсов

This commit is contained in:
Arsen Mirzaev Tatyano-Muradovich 2021-11-29 11:00:50 +10:00
parent 29ea6a9a79
commit 8f32df518f
19 changed files with 1204 additions and 409 deletions

View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
return [
'id' => 'skillparts-console',
'basePath' => dirname(__DIR__),
'bootstrap' => ['log'],
'controllerNamespace' => 'app\commands',
'aliases' => [
'@vendor' => dirname(__DIR__) . '/../../../vendor',
'@bower' => '@vendor/bower-asset',
'@npm' => '@vendor/npm-asset',
'@tests' => '@app/tests',
],
'components' => [
'cache' => [
'class' => 'yii\caching\FileCache',
],
'log' => [
'targets' => [
[
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning'],
],
],
],
'arangodb' => require __DIR__ . '/db.php'
],
'params' => require __DIR__ . '/params.php',
'controllerMap' => [
'arangodb-migrate' => 'mirzaev\yii2\arangodb\console\controllers\MigrateController',
'fixture' => [
'class' => 'yii\faker\FixtureController',
],
]
];

View File

@ -408,7 +408,7 @@ class ProfileController extends Controller
public function actionSupplies(): string|array public function actionSupplies(): string|array
{ {
// Инициализация // Инициализация
$model = new Supply(yii::$app->request->post('Supply') ?? yii::$app->request->get('Supply')); $supply = new Supply(yii::$app->request->post('Supply') ?? yii::$app->request->get('Supply'));
$panel = yii::$app->request->post('panel') ?? yii::$app->request->get('panel'); $panel = yii::$app->request->post('panel') ?? yii::$app->request->get('panel');
$sidebar = $this->renderPartial('sidebar'); $sidebar = $this->renderPartial('sidebar');
$groups = self::readGroups(); $groups = self::readGroups();
@ -421,7 +421,7 @@ class ProfileController extends Controller
return [ return [
'main' => $this->renderPartial('supplies', compact( 'main' => $this->renderPartial('supplies', compact(
'model', 'supply',
'groups', 'groups',
'sidebar', 'sidebar',
'panel' 'panel'
@ -432,7 +432,7 @@ class ProfileController extends Controller
} }
return $this->render('supplies', compact( return $this->render('supplies', compact(
'model', 'supply',
'groups', 'groups',
'sidebar', 'sidebar',
'panel' 'panel'
@ -447,8 +447,8 @@ class ProfileController extends Controller
public function actionImport() public function actionImport()
{ {
// Инициализация // Инициализация
$model = new Supply(yii::$app->request->post('Supply') ?? yii::$app->request->get('Supply')); $supply = new Supply(yii::$app->request->post('Supply') ?? yii::$app->request->get('Supply'));
$model->scenario = $model::SCENARIO_IMPORT_EXCEL; $supply->scenario = $supply::SCENARIO_IMPORT_EXCEL;
$panel = yii::$app->request->post('panel') ?? yii::$app->request->get('panel'); $panel = yii::$app->request->post('panel') ?? yii::$app->request->get('panel');
$sidebar = $this->renderPartial('sidebar'); $sidebar = $this->renderPartial('sidebar');
$groups = self::readGroups(); $groups = self::readGroups();
@ -456,14 +456,18 @@ class ProfileController extends Controller
if (yii::$app->request->isPost) { if (yii::$app->request->isPost) {
// AJAX-POST-запрос // AJAX-POST-запрос
// Настройка ответа
yii::$app->response->format = Response::FORMAT_JSON; yii::$app->response->format = Response::FORMAT_JSON;
$model->file_excel = UploadedFile::getInstances($model, 'file_excel'); // Инициализация файлов
$supply->file_excel_1 = UploadedFile::getInstance($supply, 'file_excel_1');
$supply->file_excel_2 = UploadedFile::getInstance($supply, 'file_excel_2');
$supply->file_excel_3 = UploadedFile::getInstance($supply, 'file_excel_3');
if ($model->importExcel()) { if ($supply->importExcel()) {
return [ return [
'main' => $this->renderPartial('supplies', compact( 'main' => $this->renderPartial('supplies', compact(
'model', 'supply',
'groups', 'groups',
'sidebar', 'sidebar',
'panel' 'panel'
@ -476,7 +480,7 @@ class ProfileController extends Controller
} }
return $this->render('supplies', compact( return $this->render('supplies', compact(
'model', 'supply',
'groups', 'groups',
'sidebar', 'sidebar',
'panel' 'panel'

View File

@ -0,0 +1,20 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m211123_114511_create_import_collection extends Migration
{
public function up()
{
/**
* @param string Название коллекции
* @param array Тип коллекции (2 - документ, 3 - ребро)
*/
$this->createCollection('import', ['type' => 2]);
}
public function down()
{
$this->dropCollection('import');
}
}

View File

@ -0,0 +1,20 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m211123_120136_create_import_edge_supply_collection extends Migration
{
public function up()
{
/**
* @param string Название коллекции
* @param array Тип коллекции (2 - документ, 3 - ребро)
*/
$this->createCollection('import_edge_supply', ['type' => 3]);
}
public function down()
{
$this->dropCollection('import_edge_supply');
}
}

View File

@ -0,0 +1,20 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m211123_173801_create_import_edge_account_collection extends Migration
{
public function up()
{
/**
* @param string Название коллекции
* @param array Тип коллекции (2 - документ, 3 - ребро)
*/
$this->createCollection('import_edge_account', ['type' => 3]);
}
public function down()
{
$this->dropCollection('import_edge_account');
}
}

View File

@ -977,4 +977,18 @@ class Account extends Document implements IdentityInterface, PartnerInterface
{ {
return self::isModer($account) || self::isAdmin($account); return self::isModer($account) || self::isAdmin($account);
} }
/**
* Сгенерировать тип аккаунта на русском языке
*/
public function type(): string
{
return match ($this->type) {
'administrator' => 'Администратор',
'moderator' => 'Модератор',
'user' => 'Пользователь',
'requested' => 'Запрошен',
default => 'Неизвестно'
};
}
} }

View File

@ -166,9 +166,9 @@ abstract class Document extends ActiveRecord
/** /**
* Чтение записей по максимальному ограничению * Чтение записей по максимальному ограничению
*/ */
public static function read(int $limit = 100): array public static function read(int $limit = 100, array|null $order = null): array
{ {
return static::find()->limit($limit)->all(); return static::find()->limit($limit)->orderby($order)->all();
} }
/** /**
@ -190,7 +190,7 @@ abstract class Document extends ActiveRecord
$this->addError($attribute, 'Передан не массив'); $this->addError($attribute, 'Передан не массив');
} }
$this->addError($attribute, 'Неизвестная ошибка'); $this->addError($attribute, 'Не пройдена проверка: "arrayValidator"');
} }
/** /**
@ -215,6 +215,28 @@ abstract class Document extends ActiveRecord
$this->addError($attribute, $e->getMessage()); $this->addError($attribute, $e->getMessage());
} }
$this->addError($attribute, 'Неизвестная ошибка'); $this->addError($attribute, 'Не пройдена проверка: "arrayWithNumbersValidator"');
}
/**
* Конвертировать _id в _key
*/
private static function keyFromId(string $_id): string
{
$_key = preg_match_all('/\/([0-9]+)$/m', $_id, $mathes);
return $mathes[0][1];
}
/**
* Статический вызов
*
* @param string $name
* @param array $args
*/
public static function __callStatic(string $name, array $args): mixed {
return match ($name) {
'keyFromId' => self::keyFromId(...$args)
};
} }
} }

View File

@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
namespace app\models;
use yii;
use app\models\traits\SearchByEdge;
use app\models\Account;
/**
* Импорт поставок
*
* Хранит себе связи с поставками которые были загружены вместе
*/
class Import extends Document
{
use SearchByEdge;
/**
* Имя коллекции
*/
public static function collectionName(): string
{
return 'import';
}
/**
* Свойства
*/
public function attributes(): array
{
return array_merge(
parent::attributes(),
[
'pstn',
'name',
'file'
]
);
}
/**
* Метки свойств
*/
public function attributeLabels(): array
{
return array_merge(
parent::attributeLabels(),
[
'pstn' => 'Позиция',
'name' => 'Название',
'file' => 'Файл'
]
);
}
/**
* Правила
*/
public function rules(): array
{
return array_merge(
parent::rules(),
[
[
'pstn',
'required'
],
[
'pstn',
'integer'
],
[
[
'file',
'name'
],
'string'
]
]
);
}
public static function searchByPosition(int $position = 1, Account $account = null, int $limit = 1): array
{
return self::searchByEdge(
from: 'account',
to: 'import',
edge: 'import_edge_account',
direction: 'INBOUND',
subquery_where: 'account._key == "' . Account::initAccount($account)->_key . '" && import_edge_account.type == "loaded"',
where: ['import.pstn' => $position],
limit: $limit
);
}
}

View File

@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace app\models;
use app\models\Account;
/**
* Связь инстанций импорта с поставками
*/
class ImportEdgeAccount extends Edge
{
/**
* Имя коллекции
*/
public static function collectionName(): string
{
return 'import_edge_account';
}
/**
* Свойства
*/
public function attributes(): array
{
return array_merge(
parent::attributes(),
[
]
);
}
/**
* Метки свойств
*/
public function attributeLabels(): array
{
return array_merge(
parent::attributeLabels(),
[
]
);
}
/**
* Правила
*/
public function rules(): array
{
return array_merge(
parent::rules(),
[
]
);
}
/**
* Поиск по аккаунту
*
* @param Account $account Аккаунт
* @param int $limit Ограничение по максимальному количеству
*/
public static function searchByAccount(Account $account, int $limit = 1): array
{
$account = Account::initAccount($account);
return static::find()->where(['_from' => $account->collectionName() . "$account->_key"])->limit($limit)->all();
}
}

View File

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace app\models;
/**
* Связь инстанций импорта с поставками
*/
class ImportEdgeSupply extends Edge
{
/**
* Имя коллекции
*/
public static function collectionName(): string
{
return 'import_edge_supply';
}
/**
* Свойства
*/
public function attributes(): array
{
return array_merge(
parent::attributes(),
[
]
);
}
/**
* Метки свойств
*/
public function attributeLabels(): array
{
return array_merge(
parent::attributeLabels(),
[
]
);
}
/**
* Правила
*/
public function rules(): array
{
return array_merge(
parent::rules(),
[
]
);
}
}

View File

@ -4,19 +4,12 @@ declare(strict_types=1);
namespace app\models; namespace app\models;
use yii;
use yii\web\UploadedFile; use yii\web\UploadedFile;
use yii\imagine\Image; use yii\imagine\Image;
use app\models\Settings;
use app\models\traits\SearchByEdge; use app\models\traits\SearchByEdge;
use moonland\phpexcel\Excel; use moonland\phpexcel\Excel;
use DateTime;
use DateTimeZone;
use Exception;
/** /**
* Продукт (в ассортименте магазина) * Продукт (в ассортименте магазина)
* *
@ -57,9 +50,17 @@ class Product extends Document
/** /**
* Файл .excel для импорта товаров * Файл .excel для импорта товаров
*/ */
public Excel|string|array|null $file_excel = null; public Excel|UploadedFile|string|null $file_excel_1 = null;
public Excel|string|array|null $file_excel_1 = null;
public Excel|string|array|null $file_excel_2 = null; /**
* Файл .excel для импорта товаров
*/
public Excel|UploadedFile|string|null $file_excel_2 = null;
/**
* Файл .excel для импорта товаров
*/
public Excel|UploadedFile|string|null $file_excel_3 = null;
/** /**
* Изображение для импорта * Изображение для импорта
@ -116,9 +117,9 @@ class Product extends Document
'imgs' => 'Изображения (imgs)', 'imgs' => 'Изображения (imgs)',
'time' => 'Срок доставки (time)', 'time' => 'Срок доставки (time)',
'bffr' => 'Буфер', 'bffr' => 'Буфер',
'file_excel' => 'Документ (file_excel)', 'file_excel_1' => 'Документ (file_excel_1)',
'file_excel_1' => 'Документ (file_excel)', 'file_excel_2' => 'Документ (file_excel_2)',
'file_excel_2' => 'Документ (file_excel)', 'file_excel_3' => 'Документ (file_excel_3)',
'file_image' => 'Изображение (file_image)', 'file_image' => 'Изображение (file_image)',
'group' => 'Группа (group)', 'group' => 'Группа (group)',
'account' => 'Аккаунт' 'account' => 'Аккаунт'
@ -178,18 +179,15 @@ class Product extends Document
'message' => '{attribute} должен иметь значение от 0 до 30000' 'message' => '{attribute} должен иметь значение от 0 до 30000'
], ],
[ [
'file_excel',
'required',
'message' => 'Заполните поля: {attribute}',
'on' => self::SCENARIO_IMPORT_EXCEL
],
[ [
'file_excel', 'file_excel_1',
'file_excel_2',
'file_excel_3'
],
'file', 'file',
'skipOnEmpty' => false, 'skipOnEmpty' => true,
'extensions' => 'xlsx', 'extensions' => 'xlsx',
'checkExtensionByMimeType' => false, 'checkExtensionByMimeType' => false,
'maxFiles' => 5,
'maxSize' => 1024 * 1024 * 30, 'maxSize' => 1024 * 1024 * 30,
'wrongExtension' => 'Разрешены только документы в формате: ".xlsx"', 'wrongExtension' => 'Разрешены только документы в формате: ".xlsx"',
'message' => 'Проблема при чтении документа', 'message' => 'Проблема при чтении документа',
@ -323,131 +321,6 @@ class Product extends Document
return $amount; return $amount;
} }
/**
* Импорт товаров
*
* На данный момент обрабатывает только импорт из
* файлов с расширением .excel
*/
public function importExcel(Account|int|null $account = null): bool
{
// Инициализация
$data = [];
$created = 0;
$updated = 0;
$account = Account::initAccount($account);
if ($this->validate()) {
foreach ($this->file_excel as $file) {
// Перебор файлов
// Инициализация
preg_match_all('/UTC([\+\-0-9:]*)/', $account->zone ?? Settings::searchActive()['timezone_default'] ?? 'UTC+3', $timezone);
$timezone = $timezone[1][0];
$path = YII_PATH_PUBLIC . "/../assets/accounts/$account->_key/files/" . (new DateTime('now', new DateTimeZone($timezone)))->getTimestamp();
// Сохранение на диск
if (!file_exists($path))
if (!mkdir($path, 0775, true))
throw new Exception('Не удалось создать директорию', 500);
$file->saveAs($path = $path . "/$file->baseName.$file->extension");
$data[] = Excel::import($path, [
'setFirstRecordAsKeys' => true,
'setIndexSheetByName' => true,
]);
}
foreach ($data as $data) {
// Перебор конвертированных файлов
if (count($data) < 1) {
// Не найдены строки с товарами
$this->addError('erros', 'Не удалось найти данные товаров');
} else {
// Перебор найденных товаров
foreach ($data as $doc) {
// Перебор полученных документов
// Инициализация буфера
$_doc = $doc;
// Поиск всех артикулов (каталожных номеров)
$products = explode(',', $doc['catn'], 300);
// Поиск количества товаров
$amount = $doc['amnt'] ?? 1;
foreach ($products as $_product) {
// Перебор продуктов (если catn перечислены через запятую)
$_product = trim($_product);
// Запись артикула (каталожного номера) в буфер
$_doc['catn'] = $_product;
// Инициализация продукта
$product = new static($_doc);
$product->scenario = $product::SCENARIO_WRITE;
if ($product->validate()) {
// Проверка пройдена
if (($_product = $product->validateForUniqueness()) instanceof static) {
// Найден документ с такими параметрами
// Инициализация буфера с параметрами загружаемого товара
$vars = $product->getAttributes();
// Удаление _key, чтобы не перезаписать его при замене параметров документа в буфере
unset($vars['_key']);
// Перенос данных в буфер (существующий в базе данных дубликат)
$_product->setAttributes($vars, false);
// Перезапись существующего документа
$_product->update();
$updated++;
} else {
// Не найден документ с такими параметрами
// Запись нового документа
if ($product->save()) $created++;
}
} else {
// Проверка не пройдена
// Добавление ошибок
foreach ($product->errors as $attribute => $error) $this->addError($attribute, $error);
}
}
}
}
}
// Деинициализация
$this->file_excel = null;
// Макрос действий после импорта
static::afterImportExcel($created, $updated);
return true;
}
// Запись ошибки
$this->addError('erros', 'Неизвестная ошибка');
// Макрос действий после импорта
static::afterImportExcel($created, $updated);
return false;
}
/** /**
* Поиск по каталожному номеру * Поиск по каталожному номеру
* *
@ -509,81 +382,6 @@ class Product extends Document
return $query; return $query;
} }
/**
* Вызывается после загрузки поставок из excel-документа
*
* @param int $created Количество созданных документов
* @param int $updated Количество обновлённых документов
*/
public static function afterImportExcel(int $created = 0, int $updated = 0): bool
{
// Инициализация параметров
$model = new Notification;
$account = yii::$app->user->identity;
// Инициализация часового пояса
preg_match_all('/UTC([\+\-0-9:]*)/', $account->zone ?? Settings::searchActive()['timezone_default'] ?? 'UTC+3', $timezone);
$timezone = $timezone[1][0];
// Инициализация даты
$date = (new DateTime('now', new DateTimeZone($timezone)))->format('H:i d.m.Y');
// Настройка
$model->text = yii::$app->controller->renderPartial('@app/views/notification/system/afterImportExcel', compact('created', 'updated', 'date'));
$model->type = $model::TYPE_NOTICE;
// Отправка
return (bool) $model->write();
}
/**
* Вызывается после загрузки поставок из 1С
*/
public static function afterImport1c(Account|int|null $account = null): bool
{
// Инициализация
$model = new Notification;
if (is_null($account)) {
// Данные аккаунта не переданы
if (yii::$app->user->isGuest) {
// Аккаунт не аутентифицирован
return false;
} else {
// Аккаунт аутентифицирован
// Инициализация
$account = yii::$app->user->identity;
}
} else {
if (is_int($account)) {
// Передан идентификатор (_key) аккаунта (подразумевается)
// Инициализация (поиск в базе данных)
if (!$account = Account::searchById(Account::collectionName() . "/$account")) {
// Не удалось инициализировать аккаунт
return false;
}
}
}
// Инициализация часового пояса
preg_match_all('/UTC([\+\-0-9:]*)/', $account->zone ?? Settings::searchActive()['timezone_default'] ?? 'UTC+3', $timezone);
$timezone = $timezone[1][0];
$date = (new DateTime('now', new DateTimeZone($timezone)))->format('H:i d.m.Y');
// Настройка
$model->text = yii::$app->controller->renderPartial('@app/views/notification/system/afterImport1c', compact('date'));
$model->type = $model::TYPE_NOTICE;
// Отправка
return (bool) $model->write();
}
/** /**
* Найти по идентификатору поставки * Найти по идентификатору поставки
* *
@ -863,4 +661,50 @@ class Product extends Document
// Возврат (подразумевается ошибка) // Возврат (подразумевается ошибка)
return false; return false;
} }
/**
* Инициализация продукта
*
* @param string $catn Артикул, каталожный номер
*/
public static function initEmpty(string $catn): Supply|array
{
$oemn = self::searchOemn($catn);
if (count($oemn) === 1) {
// Передан только один артикул
if ($model = Product::searchByCatn($catn)) {
// Продукт уже существует
return $model;
}
// Запись пустого продукта
return Product::writeEmpty($catn);
}
// Инициализация
$models = [];
foreach ($oemn as $catn) {
// Перебор всех найденных артикулов
if ($model = Product::searchByCatn($catn)) {
// Продукт уже существует
continue;
}
// Запись
if ($model = Product::writeEmpty($catn)) {
// Записано
// Запись в массив сохранённых моделей
$models[] = $model;
}
}
return $models;
}
} }

View File

@ -6,17 +6,25 @@ namespace app\models;
use yii; use yii;
use app\models\traits\Xml2Array;
use app\models\Account; use app\models\Account;
use app\models\Product; use app\models\Product;
use app\models\SupplyEdgeProduct; use app\models\SupplyEdgeProduct;
use app\models\traits\Xml2Array; use app\models\Settings;
use app\models\Import;
use app\models\ImportEdgeSupply;
use app\models\ImportEdgeAccount;
use carono\exchange1c\interfaces\OfferInterface; use carono\exchange1c\interfaces\OfferInterface;
use carono\exchange1c\interfaces\ProductInterface; use carono\exchange1c\interfaces\ProductInterface;
use carono\exchange1c\controllers\ApiController; use carono\exchange1c\controllers\ApiController;
use moonland\phpexcel\Excel;
use DateTime;
use DateTimeZone;
use Exception; use Exception;
use Throwable;
/** /**
* Поставка (выгрузка товаров от поставщиков) * Поставка (выгрузка товаров от поставщиков)
@ -367,29 +375,236 @@ class Supply extends Product implements ProductInterface, OfferInterface
} }
/** /**
* Запись продукта из 1С (поставка) * Запись поставок из excel
* *
* @see Supply * На данный момент обрабатывает только импорт из
* файлов с расширением .excel
*/
public function importExcel(Account|int|null $account = null): bool
{
// Инициализация
$data = [];
$created = 0;
$updated = 0;
$account = Account::initAccount($account);
if ($this->validate()) {
// Пройдена проверка
if (isset($this->file_excel_1)) {
// Найден файл в позиции 1
// Инициализация позиции
$position = 1;
// Запись в буфер
$file = $this->file_excel_1;
} else if (isset($this->file_excel_2)) {
// Найден файл в позиции 2
// Инициализация позиции
$position = 2;
// Запись в буфер
$file = $this->file_excel_2;
} else if (isset($this->file_excel_3)) {
// Найден файл в позиции 3
// Инициализация позиции
$position = 3;
// Запись в буфер
$file = $this->file_excel_3;
} else {
// Не найден ни один файл
// Запись ошибки
$this->addError('erros', 'Файл для импорта не найден');
}
// Инициализация
preg_match_all('/UTC([\+\-0-9:]*)/', $account->zone ?? Settings::searchActive()['timezone_default'] ?? 'UTC+3', $timezone);
$timezone = $timezone[1][0];
// $path = YII_PATH_PUBLIC . "/../assets/accounts/$account->_key/files/" . (new DateTime('now', new DateTimeZone($timezone)))->getTimestamp();
$path = YII_PATH_PUBLIC . "/files/$account->_key/" . (new DateTime('now', new DateTimeZone($timezone)))->getTimestamp();
// Сохранение на диск
if (!file_exists($path))
if (!mkdir($path, 0775, true))
throw new Exception('Не удалось создать директорию', 500);
$file->saveAs($path = "$path/" . $filename = $file->baseName . '.' . $file->extension);
$data[] = Excel::import($path, [
'setFirstRecordAsKeys' => true,
'setIndexSheetByName' => true,
]);
// Инициализация буфера импортированных товаров
$imported = [];
foreach ($data as $data) {
// Перебор конвертированных файлов
if (count($data) < 1) {
// Не найдены строки с товарами
$this->addError('erros', 'Не удалось найти данные товаров');
} else {
// Найдены строки с товарами
foreach ($data as $doc) {
// Перебор полученных документов
// Инициализация буфера документов
$_doc = $doc;
// Поиск всех артикулов (каталожных номеров)
$supplies = explode(',', $doc['catn'], 300);
// Поиск количества товаров
$amount = $doc['amnt'] ?? 1;
foreach ($supplies as $_supply) {
// Перебор продуктов (если catn перечислены через запятую)
$_supply = trim($_supply);
// Запись артикула (каталожного номера) в буфер
$_doc['catn'] = $_supply;
// Инициализация продукта
$supply = new static($_doc);
$supply->scenario = $supply::SCENARIO_WRITE;
if ($supply->validate()) {
// Проверка пройдена
if (($_supply = $supply->validateForUniqueness()) instanceof static) {
// Найден документ с такими параметрами
// Инициализация буфера с параметрами загружаемого товара
$vars = $supply->getAttributes();
// Удаление _key, чтобы не перезаписать его при замене параметров документа в буфере
unset($vars['_key']);
// Перенос данных в буфер (существующий в базе данных дубликат)
$_supply->setAttributes($vars, false);
// Перезапись существующего документа
$_supply->update();
// Обновление счётчика
$updated++;
// Запись поставки в буфер
$imported[] = $_supply;
} else {
// Не найден документ с такими параметрами
if ($supply->save()) {
// Поставка записана в базу данных
// Обновление счётчика
$created++;
// Запись поставки в буфер
$imported[] = $supply;
};
}
} else {
// Проверка не пройдена
// Добавление ошибок
foreach ($supply->errors as $attribute => $error) $this->addError($attribute, $error);
}
}
}
}
}
if ($imported > 0) {
// Успешно записана минимум 1 поставка
// Инициализация инстанции импорта
$import = new Import;
$import->file = $path;
$import->name = $filename;
$import->pstn = $position;
if ($import->save()) {
// Инстанция импорта успешно загружена
if (ImportEdgeAccount::write(yii::$app->user->identity->collectionName() . '/' . yii::$app->user->identity->_key, $import->collectionName() . "/$import->_key", data: ['type' => 'loaded'])) {
// Записано ребро: АККАУНТ -> ИНСТАНЦИЯ ПОСТАВОК
// Запись в журнал инстанции импорта
$import->journal('connect_with_account', [
'target' => yii::$app->user->identity->collectionName() . '/' . yii::$app->user->identity->_key
]);
}
foreach ($imported as $supply) {
// Перебор импортированных поставок
if (ImportEdgeSupply::write($import->collectionName() . "/$import->_key", $supply->collectionName() . "/$supply->_key", data: ['type' => 'imported'])) {
// Записано ребро: ИНСТАНЦИЯ ПОСТАВОК -> ПОСТАВКА
// Запись в журнал инстанции импорта
$import->journal('connect_with_supply', [
'target' => $supply->collectionName() . "/$supply->_key"
]);
}
}
}
}
// Макрос действий после импорта
static::afterImportExcel($created, $updated);
// Удаление (важно именно задать null для формы в представлении)
$this->file_excel_1 = $this->file_excel_2 = $this->file_excel_3 = null;
return true;
}
// Запись ошибки
$this->addError('erros', 'Не пройдена проверка параметров');
// Макрос действий после импорта
static::afterImportExcel($created, $updated);
// Удаление (важно именно задать null для формы в представлении)
$this->file_excel_1 = $this->file_excel_2 = $this->file_excel_3 = null;
return false;
}
/**
* Запись поставок из 1С
* *
* @todo Понять что может храниться внутри "$model->onec['ЗначенияСвойств']['ЗначенияСвойства']" и переписать * @todo Понять что может храниться внутри "$model->onec['ЗначенияСвойств']['ЗначенияСвойства']" и переписать
* Разобраться и создать возможность загрузки от лица другого аккаунта * Разобраться и создать возможность загрузки от лица другого аккаунта
*/ */
public static function createModel1c($product): ?self public static function createModel1c($supply): ?self
{ {
// Инициализация // Инициализация
$model = self::searchByOcid($id = (string) $product->Ид) ?? new self; $_supply = self::searchByOcid($id = (string) $supply->Ид) ?? new self;
$account ?? $account = yii::$app->user->identity; $account ?? $account = yii::$app->user->identity;
// Настройка // Настройка
$model->ocid = $id ?? null; $_supply->ocid = $id ?? null;
$model->catn = (string) $product->Артикул; $_supply->catn = (string) $supply->Артикул;
$model->dscr = (string) $product->Описание; $_supply->dscr = (string) $supply->Описание;
$model->onec = self::xml2array($product->xml); $_supply->onec = self::xml2array($supply->xml);
if (isset($model->onec['ЗначенияСвойств'])) { if (isset($_supply->onec['ЗначенияСвойств'])) {
// Свойства инициализированы // Свойства инициализированы
foreach ($model->onec['ЗначенияСвойств'] as $property) { foreach ($_supply->onec['ЗначенияСвойств'] as $property) {
// Перебор всех свойств // Перебор всех свойств
if (is_array($property)) { if (is_array($property)) {
@ -397,22 +612,37 @@ class Supply extends Product implements ProductInterface, OfferInterface
// Если идентификатор свойства совпадает с указанным в настройках свойства хранящего OEM номера // Если идентификатор свойства совпадает с указанным в настройках свойства хранящего OEM номера
// Настройка // Настройка
$model->oemn = array_merge(self::searchOemn($property['Значение']), self::searchOemn((string) $product->Артикул)); $_supply->oemn = array_merge(self::searchOemn($property['Значение']), self::searchOemn((string) $supply->Артикул));
} }
} }
} }
} }
// Запись // Запись
if ($model->save()) { if ($_supply->write()) {
// Поставка успешно сохранена // Поставка успешно сохранена
return $model; return $supply;
}
// Запись
if ($_supply->save()) {
// Поставка успешно сохранена
return $supply;
} }
return null; return null;
} }
/**
*
*/
public function write($context = null)
{
return $this->onec;
}
/** /**
* @param mixed|null $context * @param mixed|null $context
* @return array * @return array
@ -422,52 +652,6 @@ class Supply extends Product implements ProductInterface, OfferInterface
return $this->onec; return $this->onec;
} }
/**
* Инициализация продукта
*
* @param string $catn Артикул, каталожный номер
*/
public static function initEmpty(string $catn): Product|array
{
$oemn = self::searchOemn($catn);
if (count($oemn) === 1) {
// Передан только один артикул
if ($model = Product::searchByCatn($catn)) {
// Продукт уже существует
return $model;
}
// Запись пустого продукта
return Product::writeEmpty($catn);
}
// Инициализация
$models = [];
foreach ($oemn as $catn) {
// Перебор всех найденных артикулов
if ($model = Product::searchByCatn($catn)) {
// Продукт уже существует
continue;
}
// Запись
if ($model = Product::writeEmpty($catn)) {
// Записано
// Запись в массив сохранённых моделей
$models[] = $model;
}
}
return $models;
}
/** /**
* Поиск OEM номеров * Поиск OEM номеров
* *
@ -564,34 +748,34 @@ class Supply extends Product implements ProductInterface, OfferInterface
return []; return [];
} }
/** // /**
* Прочитать стоимость // * Прочитать стоимость
* // *
* @param Product|null $product Товар для поиска по вершинам // * @param Product|null $product Товар для поиска по вершинам
* // *
* @return array|null Данные о ценах // * @return array|null Данные о ценах
*/ // */
public function readCost(Product $product = null): ?array // public function readCost(Product $product = null): ?array
{ // {
return static::readCostById($this->readId(), $product); // return static::readCostById($this->readId(), $product);
} // }
/** // /**
* Прочитать стоимость по идентификатору поставки // * Прочитать стоимость по идентификатору поставки
* // *
* @param string $_id Идентификатор поставки // * @param string $_id Идентификатор поставки
* @param Product|null $product Товар для поиска по вершинам // * @param Product|null $product Товар для поиска по вершинам
* // *
* @return array|null Данные о ценах // * @return array|null Данные о ценах
*/ // */
public static function readCostById(string $_id, Product $product = null): ?array // public static function readCostById(string $_id, Product $product = null): ?array
{ // {
if (isset($product)) { // if (isset($product)) {
return SupplyEdgeProduct::searchByVertex($_id, $product->readId(), type: 'connect', limit: 1)['onec']['Цены']['Цена']; // return SupplyEdgeProduct::searchByVertex($_id, $product->readId(), type: 'connect', limit: 1)['onec']['Цены']['Цена'];
} // }
return SupplyEdgeProduct::searchByDirection($_id, type: 'connect', limit: 1)['onec']['Цены']['Цена']; // return SupplyEdgeProduct::searchByDirection($_id, type: 'connect', limit: 1)['onec']['Цены']['Цена'];
} // }
/** /**
* Найти аккаунт владельца * Найти аккаунт владельца
@ -617,4 +801,79 @@ class Supply extends Product implements ProductInterface, OfferInterface
}; };
} }
} }
/**
* Вызывается после загрузки поставок из excel-документа
*
* @param int $created Количество созданных документов
* @param int $updated Количество обновлённых документов
*/
public static function afterImportExcel(int $created = 0, int $updated = 0): bool
{
// Инициализация параметров
$model = new Notification;
$account = yii::$app->user->identity;
// Инициализация часового пояса
preg_match_all('/UTC([\+\-0-9:]*)/', $account->zone ?? Settings::searchActive()['timezone_default'] ?? 'UTC+3', $timezone);
$timezone = $timezone[1][0];
// Инициализация даты
$date = (new DateTime('now', new DateTimeZone($timezone)))->format('H:i d.m.Y');
// Настройка
$model->text = yii::$app->controller->renderPartial('@app/views/notification/system/afterImportExcel', compact('created', 'updated', 'date'));
$model->type = $model::TYPE_NOTICE;
// Отправка
return (bool) $model->write();
}
/**
* Вызывается после загрузки поставок из 1С
*/
public static function afterImport1c(Account|int|null $account = null): bool
{
// Инициализация
$model = new Notification;
if (is_null($account)) {
// Данные аккаунта не переданы
if (yii::$app->user->isGuest) {
// Аккаунт не аутентифицирован
return false;
} else {
// Аккаунт аутентифицирован
// Инициализация
$account = yii::$app->user->identity;
}
} else {
if (is_int($account)) {
// Передан идентификатор (_key) аккаунта (подразумевается)
// Инициализация (поиск в базе данных)
if (!$account = Account::searchById(Account::collectionName() . "/$account")) {
// Не удалось инициализировать аккаунт
return false;
}
}
}
// Инициализация часового пояса
preg_match_all('/UTC([\+\-0-9:]*)/', $account->zone ?? Settings::searchActive()['timezone_default'] ?? 'UTC+3', $timezone);
$timezone = $timezone[1][0];
$date = (new DateTime('now', new DateTimeZone($timezone)))->format('H:i d.m.Y');
// Настройка
$model->text = yii::$app->controller->renderPartial('@app/views/notification/system/afterImport1c', compact('date'));
$model->type = $model::TYPE_NOTICE;
// Отправка
return (bool) $model->write();
}
} }

View File

@ -4,12 +4,8 @@ declare(strict_types=1);
namespace app\models\traits; namespace app\models\traits;
use yii;
use ArangoDBClient\Document; use ArangoDBClient\Document;
use Exception;
trait SearchByEdge trait SearchByEdge
{ {
/** /**

View File

@ -8,10 +8,20 @@ use yii\helpers\Html;
use app\models\Notification; use app\models\Notification;
use app\models\Account; use app\models\Account;
use app\models\Product;
use app\models\Settings;
use app\models\Supply;
use DateTime;
use DateTimeZone;
// Инициализация // Инициализация
$panel ?? $panel = 'profile_panel_input_notifications'; $panel ?? $panel = 'profile_panel_input_accounts';
$panel_accounts ?? $panel_accounts = 'profile_panel_input_accounts_control';
// Инициализация часового пояса
preg_match_all('/UTC([\+\-0-9:]*)/', $account->zone ?? Settings::searchActive()['timezone_default'] ?? 'UTC+3', $timezone);
$timezone = $timezone[1][0];
?> ?>
<link href="/css/pages/profile.css" rel="stylesheet"> <link href="/css/pages/profile.css" rel="stylesheet">
@ -26,9 +36,11 @@ $panel ?? $panel = 'profile_panel_input_notifications';
<h4 class="ml-4 mb-4"><i class="fas fa-user-shield my-auto mr-2"></i>Панель управления</h4> <h4 class="ml-4 mb-4"><i class="fas fa-user-shield my-auto mr-2"></i>Панель управления</h4>
<div id="profile_panel_panel" class="profile_panel"> <div id="profile_panel_panel" class="profile_panel">
<div class="profile_panel_menu mb-3"> <div class="profile_panel_menu mb-3">
<label class="btn button_white mb-0 mr-2" for="profile_panel_input_notifications" onclick="return page_profile_panel_suppliers_choose('profile_panel_input_notifications');">Уведомления</label> <label class="btn button_white mb-0 mr-2" for="profile_panel_input_notifications" onclick="return page_profile_panel_choose('profile_panel_input_notifications');">Уведомления</label>
<label class="btn button_white mb-0 mr-2" for="profile_panel_input_suppliers" onclick="return page_profile_panel_input_suppliers_requests_init();">Поставщики</label> <label class="btn button_white mb-0 mr-2" for="profile_panel_input_accounts" onclick="return page_profile_panel_choose('profile_panel_input_accounts');">Аккаунты</label>
<label class="btn button_white mb-0 mr-2" for="profile_panel_input_settings" onclick="return page_profile_panel_suppliers_choose('profile_panel_input_settings');">Настройки</label> <label class="btn button_white mb-0 mr-2" for="profile_panel_input_products" onclick="return page_profile_panel_choose('profile_panel_input_products');">Товары</label>
<label class="btn button_white mb-0 mr-2" for="profile_panel_input_supplies" onclick="return page_profile_panel_choose('profile_panel_input_supplies');">Поставки</label>
<label class="btn button_white mb-0 mr-2" for="profile_panel_input_settings" onclick="return page_profile_panel_choose('profile_panel_input_settings');">Настройки</label>
</div> </div>
<div class="profile_panel_content"> <div class="profile_panel_content">
<input type="radio" id="profile_panel_input_notifications" name="main_panel" <?= $panel === 'profile_panel_input_notifications' ? 'checked' : null ?> /> <input type="radio" id="profile_panel_input_notifications" name="main_panel" <?= $panel === 'profile_panel_input_notifications' ? 'checked' : null ?> />
@ -76,14 +88,175 @@ $panel ?? $panel = 'profile_panel_input_notifications';
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>
</div> </div>
<input type="radio" id="profile_panel_input_accounts" name="main_panel" <?= $panel === 'profile_panel_input_accounts' ? 'checked' : null ?> />
<input type="radio" id="profile_panel_input_suppliers" name="main_panel" <?= $panel === 'profile_panel_input_suppliers' ? 'checked' : null ?> /> <div class="profile_panel_input_accounts_menu mb-4">
<label class="mb-0 mr-2 px-2 py-1 btn button_white_small" for="profile_panel_input_accounts_control" onclick="return page_profile_panel_accounts_choose('profile_panel_input_accounts_control');">Пользователи</label>
<label class="mb-0 mr-2 px-2 py-1 btn button_white_small" for="profile_panel_input_accounts_suppliers" onclick="return page_profile_panel_accounts_choose('profile_panel_input_accounts_suppliers');">Поставщики</label>
</div>
<input type="radio" id="profile_panel_input_accounts_control" name="main_panel_accounts" <?= $panel_accounts === 'profile_panel_input_accounts_control' ? 'checked' : null ?> />
<div class="col"> <div class="col">
<h5>Заявки на регистрацию</h5> <h5>Список пользователей</h5>
<div class="dropdown-divider mb-4"></div>
<div id="profile_panel_input_accounts_list" class="px-3">
<?php
// Инициализация счетчика аккаунтов
$amount = 0;
// Чтение аккаунтов
$accounts = Account::read(limit: 100, order: ['desc']);
?>
<?php foreach ($accounts ?? [] as $account) : ?>
<?php
foreach ($account->jrnl ?? [] as $jrnl) {
// Перебор записей в журнале
if ($jrnl['action'] === 'create') {
// Найдена дата создания
// Инициализация даты
$create = (new DateTime())->setTimestamp($jrnl['date'])->setTimezone(new DateTimeZone($timezone))->format('d.m.Y') ?? 'Неизвестно';
// Выход из цикла
break;
}
}
?>
<div class="mb-3 row">
<div class="pr-0 col-auto"><?= ++$amount ?>.</div>
<div class="pr-0 col overflow-hidden" title="<?= $account->name ?? 'Неизвестно' ?>">
<?= $account->name ?? 'Неизвестно' ?>
</div>
<div class="my-auto pr-0 col-auto">
<?= $account->indx ?? '' ?>
</div>
<div class="my-auto pr-0 col-auto">
<?= $account->agnt ? 'Поставщик' : 'Покупатель' ?>
</div>
<div class="mr-3 my-auto pr-0 col-2">
<?= $account->type() ?>
</div>
<div class="my-auto pr-0 col-auto text-right">
<?= $create ?? 'Неизвестно' ?>
</div>
<a class="my-auto col-auto fas fa-trash-alt text-dark" type="button" onclick="page_profile_supplies_delete()"></a>
</div>
<? if ($amount < count($accounts)) : ?>
<div class="dropdown-divider mb-3"></div> <div class="dropdown-divider mb-3"></div>
<?php endif ?>
<?php endforeach ?>
</div>
</div>
<input type="radio" id="profile_panel_input_accounts_suppliers" name="main_panel_accounts" <?= $panel_accounts === 'profile_panel_input_accounts_suppliers' ? 'checked' : null ?> />
<div class="col">
<h5>Заявки на регистрацию поставщиков</h5>
<div class="dropdown-divider mb-4"></div>
<div id="profile_panel_input_suppliers_requests"></div> <div id="profile_panel_input_suppliers_requests"></div>
</div> </div>
<input type="radio" id="profile_panel_input_products" name="main_panel" <?= $panel === 'profile_panel_input_products' ? 'checked' : null ?> />
<div class="col">
<h5>Список товаров</h5>
<div class="dropdown-divider mb-4"></div>
<?php
// Инициализация счетчика аккаунтов
$amount = 0;
// Чтение аккаунтов
$products = Product::read(limit: 100, order: ['desc']);
?>
<?php foreach ($products ?? [] as $product) : ?>
<?php
foreach ($product->jrnl ?? [] as $jrnl) {
// Перебор записей в журнале
if ($jrnl['action'] === 'create') {
// Найдена дата создания
// Инициализация даты
$create = (new DateTime())->setTimestamp($jrnl['date'])->setTimezone(new DateTimeZone($timezone))->format('H:i d.m.Y') ?? 'Неизвестно';
// Выход из цикла
break;
}
}
?>
<div class="mb-3 row">
<div class="pr-0 col-auto"><?= ++$amount ?>.</div>
<div class="pr-0 col overflow-hidden" title="<?= $product->name ?? 'Неизвестно' ?>">
<?= $product->catn ?? 'Неизвестно' ?>
</div>
<div class="my-auto pr-0 col-auto text-right">
<?= $create ?? 'Неизвестно' ?>
</div>
<a class="my-auto col-auto fas fa-trash-alt text-dark" type="button" onclick="page_profile_supplies_delete()"></a>
</div>
<? if ($amount < count($products)) : ?>
<div class="dropdown-divider mb-3"></div>
<?php endif ?>
<?php endforeach ?>
</div>
<input type="radio" id="profile_panel_input_supplies" name="main_panel" <?= $panel === 'profile_panel_input_supplies' ? 'checked' : null ?> />
<div class="col">
<h5>Список поставок</h5>
<div class="dropdown-divider mb-4"></div>
<?php
// Инициализация счетчика аккаунтов
$amount = 0;
// Чтение аккаунтов
$supplies = Supply::read(limit: 100, order: ['desc']);
?>
<?php foreach ($supplies ?? [] as $supply) : ?>
<?php
foreach ($supply->jrnl ?? [] as $jrnl) {
// Перебор записей в журнале
if ($jrnl['action'] === 'create') {
// Найдена дата создания
// Инициализация даты
$create = (new DateTime())->setTimestamp($jrnl['date'])->setTimezone(new DateTimeZone($timezone))->format('H:i d.m.Y') ?? 'Неизвестно';
// Выход из цикла
break;
}
}
?>
<div class="mb-3 row">
<div class="pr-0 col-auto"><?= ++$amount ?>.</div>
<div class="pr-0 col-2 overflow-hidden" title="<?= $supply->name ?? 'Неизвестно' ?>">
<?= $supply->catn ?? 'Неизвестно' ?>
</div>
<div class="my-auto pr-0 col">
<?= $supply->dscr ?? 'Без описания' ?>
</div>
<div class="my-auto pr-0 col-auto">
<?= $supply->amnt ?? '0' ?> шт
</div>
<div class="my-auto pr-0 col-auto">
<?= $supply->cost ?? '0' ?> р
</div>
<div class="my-auto pr-0 col-auto text-right">
<?= $create ?? 'Неизвестно' ?>
</div>
<a class="my-auto col-auto fas fa-trash-alt text-dark" type="button" onclick="page_profile_supplies_delete()"></a>
</div>
<? if ($amount < count($supplies)) : ?>
<div class="dropdown-divider mb-3"></div>
<?php endif ?>
<?php endforeach ?>
</div>
<input type="radio" id="profile_panel_input_settings" name="main_panel" <?= $panel === 'profile_panel_input_settings' ? 'checked' : null ?> /> <input type="radio" id="profile_panel_input_settings" name="main_panel" <?= $panel === 'profile_panel_input_settings' ? 'checked' : null ?> />
<div class="col"> <div class="col">
<?php <?php
@ -162,6 +335,12 @@ $panel ?? $panel = 'profile_panel_input_notifications';
<script src="/js/textarea.js" defer></script> <script src="/js/textarea.js" defer></script>
<script defer> <script defer>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// Загружен документ
// Инициализация активной вкладки
page_profile_panel_choose('<?= $panel ?? '' ?>');
// Инициализация панели для ввода текста (тест уведомлений для администратора)
initTextarea( initTextarea(
'#notification-text', '#notification-text',
'#notification-text-current', '#notification-text-current',

View File

@ -34,7 +34,7 @@ use app\models\SupplyEdgeProduct;
echo <<<HTML echo <<<HTML
<dt> <dt>
<a class="row text-dark button_white button_white_hover px-3 py-3 py-lg-2 font-weight-normal" title="Панель управления сайтом" href="$targetUrl" role="button" onclick="return false;"><i class="fas fa-user-shield my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Панель управления</span></a> <a class="row text-dark button_white hover px-3 py-3 py-lg-2 font-weight-normal" title="Панель управления сайтом" href="$targetUrl" role="button" onclick="return false;"><i class="fas fa-user-shield my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Панель управления</span></a>
</dt> </dt>
HTML; HTML;
} else { } else {
@ -56,7 +56,7 @@ use app\models\SupplyEdgeProduct;
// Запрошена та же страница от которой послан запрос (текущая) // Запрошена та же страница от которой послан запрос (текущая)
echo <<<HTML echo <<<HTML
<a class="row text-dark button_white button_white_hover px-3 py-3 py-lg-2 font-weight-normal" title="Управление поставками" href="$targetUrl" role="button" onclick="return false;"><i class="fas fa-truck my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Поставки</span></a> <a class="row text-dark button_white hover px-3 py-3 py-lg-2 font-weight-normal" title="Управление поставками" href="$targetUrl" role="button" onclick="return false;"><i class="fas fa-truck my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Поставки</span></a>
HTML; HTML;
} else { } else {
echo <<<HTML echo <<<HTML
@ -75,7 +75,7 @@ use app\models\SupplyEdgeProduct;
// Запрошена та же страница от которой послан запрос (текущая) // Запрошена та же страница от которой послан запрос (текущая)
echo <<<HTML echo <<<HTML
<a class="row text-dark button_white button_white_hover px-3 py-3 py-lg-2 font-weight-normal" title="Мониторинг и журналирование" href="$targetUrl" role="button" onclick="return false;"><i class="fas fa-eye my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Мониторинг</span></a> <a class="row text-dark button_white hover px-3 py-3 py-lg-2 font-weight-normal" title="Мониторинг и журналирование" href="$targetUrl" role="button" onclick="return false;"><i class="fas fa-eye my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Мониторинг</span></a>
HTML; HTML;
} else { } else {
echo <<<HTML echo <<<HTML
@ -93,7 +93,7 @@ use app\models\SupplyEdgeProduct;
// Запрошена та же страница от которой послан запрос (текущая) // Запрошена та же страница от которой послан запрос (текущая)
echo <<<HTML echo <<<HTML
<a class="row text-dark button_white button_white_hover px-3 py-3 py-lg-2 font-weight-normal" title="Настройки аккаунта" href="$targetUrl" role="button" onclick="return false;"><i class="fas fa-sliders-h my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Настройки</span></a> <a class="row text-dark button_white hover px-3 py-3 py-lg-2 font-weight-normal" title="Настройки аккаунта" href="$targetUrl" role="button" onclick="return false;"><i class="fas fa-sliders-h my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Настройки</span></a>
HTML; HTML;
} else { } else {
echo <<<HTML echo <<<HTML

View File

@ -2,10 +2,17 @@
declare(strict_types=1); declare(strict_types=1);
use app\models\Account;
use yii; use yii;
use yii\bootstrap\ActiveForm; use yii\bootstrap\ActiveForm;
use app\models\Account;
use app\models\Document;
use app\models\Import;
use app\models\Settings;
use DateTime;
use DateTimeZone;
// Инициализация // Инициализация
$panel ?? $panel = 'profile_panel_supplies_input_import'; $panel ?? $panel = 'profile_panel_supplies_input_import';
@ -36,7 +43,9 @@ $panel ?? $panel = 'profile_panel_supplies_input_import';
<input type="radio" id="profile_panel_supplies_input_import" name="main_panel" <?= $panel === 'profile_panel_supplies_input_import' ? 'checked' : null ?> /> <input type="radio" id="profile_panel_supplies_input_import" name="main_panel" <?= $panel === 'profile_panel_supplies_input_import' ? 'checked' : null ?> />
<div class="col"> <div class="col">
<h5>Импорт из Excel-документа</h5> <h5>Импорт из Excel-документа</h5>
<div class="dropdown-divider mb-3"></div> <div class="dropdown-divider mb-4"></div>
<?php if (Account::isMinimalAuthorized()) : ?>
<?php <?php
$form = ActiveForm::begin([ $form = ActiveForm::begin([
'id' => 'form_product_import_excel', 'id' => 'form_product_import_excel',
@ -46,42 +55,149 @@ $panel ?? $panel = 'profile_panel_supplies_input_import';
'options' => ['class' => ''] 'options' => ['class' => '']
], ],
'options' => [ 'options' => [
'class' => 'mb-3', 'class' => 'px-3 mb-3',
'onsubmit' => 'return false;' 'onsubmit' => 'return false;'
] ]
]); ]);
?> ?>
<?= $form->field($supply, 'account', ['options' => ['class' => "mb-4 col-4"]])->input('text', ['placeholder' => yii::$app->user->identity->_key]); ?>
<?php ActiveForm::end(); ?>
<?php
// Инициализация счетчика инстанций импорта
$amount = 0;
<?php if (Account::isMinimalAuthorized()) : ?> // Чтение истанций импорта
<?= $form->field($model, 'account', ['options' => ['class' => "mb-4 col-4"]])->input('text', ['placeholder' => yii::$app->user->identity->_key]); ?> $imports = Import::read(limit: 100, order: ['desc']);
// Инициализация часового пояса
preg_match_all('/UTC([\+\-0-9:]*)/', $account->zone ?? Settings::searchActive()['timezone_default'] ?? 'UTC+3', $timezone);
$timezone = $timezone[1][0];
?>
<?php foreach ($imports as $import) : ?>
<?php
foreach ($import->jrnl as $jrnl) {
// Перебор записей в журнале
if ($jrnl['action'] === 'create') {
// Найдена дата создания
// Инициализация даты
$create = (new DateTime())->setTimestamp($jrnl['date'])->setTimezone(new DateTimeZone($timezone))->format('H:i d.m.Y') ?? 'Неизвестно';
break;
}
}
foreach ($import->jrnl as $jrnl) {
// Перебор записей в журнале
if ($jrnl['action'] === 'connect_with_account') {
// Найдена дата создания
// Инициализация даты
$account = $jrnl['data']['target'];
break;
}
}
?>
<div class="mb-3 row">
<div class="col-auto"><?= ++$amount ?>.</div>
<div class="col">
<?= $import->name ?? 'Без названия' ?>
</div>
<div class="col-3 px-0 text-right">
<?= $create ?? 'Неизвестно' ?>
</div>
<a class="pr-0 my-auto col-auto fas fa-user text-dark" href="<?= $date_connect_with_account ?? 'Неизвестно' ?>"></a>
<a class="pr-0 my-auto col-auto fas fa-file-download text-dark" type="button" onclick="page_profile_supplies_download()"></a>
<a class="my-auto col-auto fas fa-trash-alt text-dark" type="button" onclick="page_profile_supplies_delete()"></a>
</div>
<? if ($amount < count($imports)) : ?>
<div class="dropdown-divider mb-3"></div>
<?php endif ?> <?php endif ?>
<?php endforeach ?>
<?php else : ?>
<?php
$form = ActiveForm::begin([
'id' => 'form_product_import_excel',
'action' => false,
'fieldConfig' => [
'template' => '{label}{input}',
'options' => ['class' => '']
],
'options' => [
'class' => 'px-3 mb-3',
'onsubmit' => 'return false;'
]
]);
?>
<?php if ($import_1 = Import::searchByPosition(1)) : ?>
<div class="mb-3 row"> <div class="mb-3 row">
<div class="col-auto">1.</div> <div class="col-auto">1.</div>
<div class="col"> <div class="col">
Тест импорта.xlsx <?= $import_1[0]->name ?? 'Без названия' ?>
</div> </div>
<a class="pr-0 my-auto col-auto fas fa-file-download text-dark" type="button" aria-hidden="true" onclick="page_profile_supplies_download()"></a> <a class="pr-0 my-auto col-auto fas fa-file-download text-dark" type="button" aria-hidden="true" onclick="page_profile_supplies_download()"></a>
<a class="my-auto col-auto fas fa-trash-alt text-dark" type="button" aria-hidden="true" onclick="page_profile_supplies_delete()"></a> <a class="my-auto col-auto fas fa-trash-alt text-dark" type="button" aria-hidden="true" onclick="page_profile_supplies_delete()"></a>
</div> </div>
<?php else : ?>
<div class="mb-3 row">
<div class="col-auto">1.</div>
<div class="col">
<?= $form->field($supply, 'file_excel_1', ['enableLabel' => false])->fileInput(['multiple' => true, 'onChange' => 'page_profile_supplies_import_excel(this.parentElement.parentElement.parentElement.parentElement, \'profile_panel_supplies_input_import\')']) ?>
</div>
</div>
<?php endif ?>
<div class="dropdown-divider mb-3"></div> <div class="dropdown-divider mb-3"></div>
<?php if ($import_2 = Import::searchByPosition(2)) : ?>
<div class="mb-3 row"> <div class="mb-3 row">
<div class="col-auto">2.</div> <div class="col-auto">2.</div>
<div class="col"> <div class="col">
<?= $form->field($model, 'file_excel_1', ['enableLabel' => false])->fileInput(['multiple' => true, 'onChange' => 'page_profile_supplies_import_excel(this.parentElement.parentElement, \'profile_panel_supplies_input_import\')']) ?> <?= $import_2[0]->name ?? 'Без названия' ?>
</div>
<a class="pr-0 my-auto col-auto fas fa-file-download text-dark" type="button" aria-hidden="true" onclick="page_profile_supplies_download()"></a>
<a class="my-auto col-auto fas fa-trash-alt text-dark" type="button" aria-hidden="true" onclick="page_profile_supplies_delete()"></a>
</div>
<?php else : ?>
<div class="mb-3 row">
<div class="col-auto">2.</div>
<div class="col">
<?= $form->field($supply, 'file_excel_2', ['enableLabel' => false])->fileInput(['multiple' => true, 'onChange' => 'page_profile_supplies_import_excel(this.parentElement.parentElement.parentElement.parentElement, \'profile_panel_supplies_input_import\')']) ?>
</div> </div>
</div> </div>
<?php endif ?>
<div class="dropdown-divider mb-3"></div> <div class="dropdown-divider mb-3"></div>
<?php if ($import_3 = Import::searchByPosition(3)) : ?>
<div class="mb-3 row">
<div class="col-auto">3.</div>
<div class="col">
<?= $import_3[0]->name ?? 'Без названия' ?>
</div>
<a class="pr-0 my-auto col-auto fas fa-file-download text-dark" type="button" aria-hidden="true" onclick="page_profile_supplies_download()"></a>
<a class="my-auto col-auto fas fa-trash-alt text-dark" type="button" aria-hidden="true" onclick="page_profile_supplies_delete()"></a>
</div>
<?php else : ?>
<div class="row"> <div class="row">
<div class="col-auto">3.</div> <div class="col-auto">3.</div>
<div class="col"> <div class="col">
<?= $form->field($model, 'file_excel_2', ['enableLabel' => false])->fileInput(['multiple' => true, 'onChange' => 'page_profile_supplies_import_excel(this.parentElement.parentElement, \'profile_panel_supplies_input_import\')']) ?> <?= $form->field($supply, 'file_excel_3', ['enableLabel' => false])->fileInput(['multiple' => true, 'onChange' => 'page_profile_supplies_import_excel(this.parentElement.parentElement.parentElement.parentElement, \'profile_panel_supplies_input_import\')']) ?>
</div> </div>
</div> </div>
<?php endif ?>
<?= $form->errorSummary($model, ['header' => 'В документе были допущены ошибки:' /*, 'footer' => 'Исправьте их и попробуйте снова'*/]); ?> <?= $form->errorSummary($supply, ['header' => 'В документе были допущены ошибки:' /*, 'footer' => 'Исправьте их и попробуйте снова'*/]); ?>
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>
<?php endif ?>
</div> </div>
</div> </div>
</div> </div>

View File

@ -171,25 +171,23 @@ main {
transition: 0s; transition: 0s;
} }
.button_white { .button_white,
.button_white_small {
background-color: #fff; background-color: #fff;
} }
.button_white:hover { :is(.button_white, .button_white_small):is(.hover,:hover) {
background-color: #eaebee; background-color: #eaebee;
transition: 0s; transition: 0s;
} }
.button_white:active, :is(.button_white, .button_white_small):is(.active, :active, :focus) {
.button_white:focus {
background-color: #cfd3dd; background-color: #cfd3dd;
transition: 0s; transition: 0s;
} }
.button_white_hover { .button_white_small {
/* Что я натворил? */ font-size: small;
background-color: #eaebee !important;
transition: 0s;
} }
.button_grey { .button_grey {

View File

@ -52,35 +52,78 @@ function page_profile_panel_write(form) {
return false; return false;
}; };
function page_profile_panel_input_suppliers_requests_init() { function page_profile_panel_choose(button = 'profile_panel_input_accounts') {
// Инициализация скриптов при открытии вкладки "Поставщики" // Деинициализация всех вкладок
document.getElementById('profile_panel_input_supplies').checked =
document.getElementById('profile_panel_input_products').checked =
document.getElementById('profile_panel_input_accounts').checked =
document.getElementById('profile_panel_input_settings').checked =
document.getElementById('profile_panel_input_notifications').checked = false;
// Выбор панели document.getElementById('profile_panel_input_supplies').setAttribute('onclick', 'return page_profile_panel_choose(\'profile_panel_input_supplies\');');
page_profile_panel_suppliers_choose('profile_panel_input_suppliers'); document.getElementById('profile_panel_input_products').setAttribute('onclick', 'return page_profile_panel_choose(\'profile_panel_input_products\');');
document.getElementById('profile_panel_input_notifications').setAttribute('onclick', 'return page_profile_panel_choose(\'profile_panel_input_notifications\');');
document.getElementById('profile_panel_input_accounts').setAttribute('onclick', 'return page_profile_panel_choose(\'profile_panel_input_accounts\');');
document.getElementById('profile_panel_input_settings').setAttribute('onclick', 'return page_profile_panel_choose(\'profile_panel_input_settings\');');
// Инициализация панели document.querySelector('[for="profile_panel_input_supplies"]').classList.remove('active');
page_profile_panel_input_suppliers_requests_init(); document.querySelector('[for="profile_panel_input_products"]').classList.remove('active');
document.querySelector('[for="profile_panel_input_accounts"]').classList.remove('active');
document.querySelector('[for="profile_panel_input_notifications"]').classList.remove('active');
document.querySelector('[for="profile_panel_input_settings"]').classList.remove('active');
return false; if (button === 'disable') {
}; // Инициализация активной подвкладки вкладки "Аккаунты"
page_profile_panel_accounts_choose('disable', true);
function page_profile_panel_suppliers_choose(button) { return;
if (button === 'profile_panel_input_suppliers') { } else if (button === 'profile_panel_input_accounts') {
document.getElementById('profile_panel_input_notifications').addAttribute('onclick', 'return page_profile_panel_input_suppliers_notifications_init();'); // Инициализация активной подвкладки вкладки "Аккаунты"
document.getElementById('profile_panel_input_settings').addAttribute('onclick', 'return page_profile_panel_input_suppliers_settings_init();'); page_profile_panel_accounts_choose(undefined, true);
} else if (button === 'profile_panel_input_notifications') { } else if (button !== 'profile_panel_input_accounts') {
document.getElementById('profile_panel_input_suppliers').addAttribute('onclick', 'return page_profile_panel_input_suppliers_requests_init();'); // Инициализация активной подвкладки вкладки "Аккаунты"
document.getElementById('profile_panel_input_settings').addAttribute('onclick', 'return page_profile_panel_input_suppliers_settings_init();'); page_profile_panel_accounts_choose('disable', true);
} else if (button === 'profile_panel_input_settings') {
document.getElementById('profile_panel_input_notifications').addAttribute('onclick', 'return page_profile_panel_input_suppliers_notifications_init();');
document.getElementById('profile_panel_input_suppliers').addAttribute('onclick', 'return page_profile_panel_input_suppliers_requests_init();');
} }
// Инициализация запрошенной вкладки
document.getElementById(button).checked = true;
document.getElementById(button).removeAttribute('onclick'); document.getElementById(button).removeAttribute('onclick');
document.querySelector('[for="' + button + '"]').classList.add('active');
}
function page_profile_panel_accounts_choose(button = 'profile_panel_input_accounts_control', active = false) {
if (active === false) {
// Вкладка не активна
// Открытие вкладки с этими подвкладками
page_profile_panel_choose('profile_panel_input_accounts');
}
// Деинициализация всех вкладок
document.getElementById('profile_panel_input_accounts_suppliers').checked =
document.getElementById('profile_panel_input_accounts_control').checked = false
document.getElementById('profile_panel_input_accounts_suppliers').setAttribute('onclick', 'return page_profile_panel_accounts_choose(\'profile_panel_input_accounts_suppliers\');');
document.getElementById('profile_panel_input_accounts_control').setAttribute('onclick', 'return page_profile_panel_accounts_choose(\'profile_panel_input_accounts_control\');');
document.querySelector('[for="profile_panel_input_accounts_suppliers"]').classList.remove('active');
document.querySelector('[for="profile_panel_input_accounts_control"]').classList.remove('active');
if (button === 'disable') {
return;
} else if (button === 'profile_panel_input_accounts_suppliers') {
// Инициализация содержимого блока "Заявки на регистрацию поставщиков"
page_profile_panel_input_suppliers_requests_init();
}
// Инициализация запрошенной вкладки
document.getElementById(button).checked = true;
document.getElementById(button).removeAttribute('onclick');
document.querySelector('[for="' + button + '"]').classList.add('active');
} }
function page_profile_panel_input_suppliers_requests_init(wrap = 'profile_panel_input_suppliers_requests') { function page_profile_panel_input_suppliers_requests_init(wrap = 'profile_panel_input_suppliers_requests') {
// Инициализация блока "Заявки на регистрацию" (поставщиков) // Инициализация содержимого блока "Заявки на регистрацию поставщиков"
// Инициализация оболочки // Инициализация оболочки
wrap = document.getElementById(wrap); wrap = document.getElementById(wrap);
@ -612,7 +655,7 @@ function profile_panel_input_suppliers_requests_block_regen(_key, target) {
* @param {*} target * @param {*} target
* @returns * @returns
*/ */
function profile_panel_input_suppliers_requests_block_accept(_key) { function profile_panel_input_suppliers_requests_block_accept(_key) {
$.ajax({ $.ajax({
url: `/${_key}/accept`, url: `/${_key}/accept`,
type: 'post', type: 'post',