diff --git a/mirzaev/skillparts/system/config/console.php.example b/mirzaev/skillparts/system/config/console.php.example new file mode 100644 index 0000000..b17a3f6 --- /dev/null +++ b/mirzaev/skillparts/system/config/console.php.example @@ -0,0 +1,37 @@ + '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', + ], + ] +]; diff --git a/mirzaev/skillparts/system/controllers/ProfileController.php b/mirzaev/skillparts/system/controllers/ProfileController.php index ca18dbf..7377d67 100644 --- a/mirzaev/skillparts/system/controllers/ProfileController.php +++ b/mirzaev/skillparts/system/controllers/ProfileController.php @@ -408,7 +408,7 @@ class ProfileController extends Controller 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'); $sidebar = $this->renderPartial('sidebar'); $groups = self::readGroups(); @@ -421,7 +421,7 @@ class ProfileController extends Controller return [ 'main' => $this->renderPartial('supplies', compact( - 'model', + 'supply', 'groups', 'sidebar', 'panel' @@ -432,7 +432,7 @@ class ProfileController extends Controller } return $this->render('supplies', compact( - 'model', + 'supply', 'groups', 'sidebar', 'panel' @@ -447,8 +447,8 @@ class ProfileController extends Controller public function actionImport() { // Инициализация - $model = new Supply(yii::$app->request->post('Supply') ?? yii::$app->request->get('Supply')); - $model->scenario = $model::SCENARIO_IMPORT_EXCEL; + $supply = new Supply(yii::$app->request->post('Supply') ?? yii::$app->request->get('Supply')); + $supply->scenario = $supply::SCENARIO_IMPORT_EXCEL; $panel = yii::$app->request->post('panel') ?? yii::$app->request->get('panel'); $sidebar = $this->renderPartial('sidebar'); $groups = self::readGroups(); @@ -456,14 +456,18 @@ class ProfileController extends Controller if (yii::$app->request->isPost) { // AJAX-POST-запрос + // Настройка ответа 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 [ 'main' => $this->renderPartial('supplies', compact( - 'model', + 'supply', 'groups', 'sidebar', 'panel' @@ -476,7 +480,7 @@ class ProfileController extends Controller } return $this->render('supplies', compact( - 'model', + 'supply', 'groups', 'sidebar', 'panel' diff --git a/mirzaev/skillparts/system/migrations/arangodb/m211123_114511_create_import_collection.php b/mirzaev/skillparts/system/migrations/arangodb/m211123_114511_create_import_collection.php new file mode 100644 index 0000000..47c10cc --- /dev/null +++ b/mirzaev/skillparts/system/migrations/arangodb/m211123_114511_create_import_collection.php @@ -0,0 +1,20 @@ +createCollection('import', ['type' => 2]); + } + + public function down() + { + $this->dropCollection('import'); + } +} diff --git a/mirzaev/skillparts/system/migrations/arangodb/m211123_120136_create_import_edge_supply_collection.php b/mirzaev/skillparts/system/migrations/arangodb/m211123_120136_create_import_edge_supply_collection.php new file mode 100644 index 0000000..e0ef60b --- /dev/null +++ b/mirzaev/skillparts/system/migrations/arangodb/m211123_120136_create_import_edge_supply_collection.php @@ -0,0 +1,20 @@ +createCollection('import_edge_supply', ['type' => 3]); + } + + public function down() + { + $this->dropCollection('import_edge_supply'); + } +} diff --git a/mirzaev/skillparts/system/migrations/arangodb/m211123_173801_create_import_edge_account_collection.php b/mirzaev/skillparts/system/migrations/arangodb/m211123_173801_create_import_edge_account_collection.php new file mode 100644 index 0000000..c9eda7c --- /dev/null +++ b/mirzaev/skillparts/system/migrations/arangodb/m211123_173801_create_import_edge_account_collection.php @@ -0,0 +1,20 @@ +createCollection('import_edge_account', ['type' => 3]); + } + + public function down() + { + $this->dropCollection('import_edge_account'); + } +} diff --git a/mirzaev/skillparts/system/models/Account.php b/mirzaev/skillparts/system/models/Account.php index a4c7435..ac322fb 100644 --- a/mirzaev/skillparts/system/models/Account.php +++ b/mirzaev/skillparts/system/models/Account.php @@ -977,4 +977,18 @@ class Account extends Document implements IdentityInterface, PartnerInterface { return self::isModer($account) || self::isAdmin($account); } + + /** + * Сгенерировать тип аккаунта на русском языке + */ + public function type(): string + { + return match ($this->type) { + 'administrator' => 'Администратор', + 'moderator' => 'Модератор', + 'user' => 'Пользователь', + 'requested' => 'Запрошен', + default => 'Неизвестно' + }; + } } diff --git a/mirzaev/skillparts/system/models/Document.php b/mirzaev/skillparts/system/models/Document.php index ec56f60..32c324c 100644 --- a/mirzaev/skillparts/system/models/Document.php +++ b/mirzaev/skillparts/system/models/Document.php @@ -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, 'Не пройдена проверка: "arrayValidator"'); } /** @@ -215,6 +215,28 @@ abstract class Document extends ActiveRecord $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) + }; } } diff --git a/mirzaev/skillparts/system/models/Import.php b/mirzaev/skillparts/system/models/Import.php new file mode 100644 index 0000000..6b87caa --- /dev/null +++ b/mirzaev/skillparts/system/models/Import.php @@ -0,0 +1,98 @@ + 'Позиция', + '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 + ); + } +} diff --git a/mirzaev/skillparts/system/models/ImportEdgeAccount.php b/mirzaev/skillparts/system/models/ImportEdgeAccount.php new file mode 100644 index 0000000..3578852 --- /dev/null +++ b/mirzaev/skillparts/system/models/ImportEdgeAccount.php @@ -0,0 +1,70 @@ +where(['_from' => $account->collectionName() . "$account->_key"])->limit($limit)->all(); + } +} diff --git a/mirzaev/skillparts/system/models/ImportEdgeSupply.php b/mirzaev/skillparts/system/models/ImportEdgeSupply.php new file mode 100644 index 0000000..9cdb9e0 --- /dev/null +++ b/mirzaev/skillparts/system/models/ImportEdgeSupply.php @@ -0,0 +1,55 @@ + 'Изображения (imgs)', 'time' => 'Срок доставки (time)', 'bffr' => 'Буфер', - 'file_excel' => 'Документ (file_excel)', - 'file_excel_1' => 'Документ (file_excel)', - 'file_excel_2' => 'Документ (file_excel)', + 'file_excel_1' => 'Документ (file_excel_1)', + 'file_excel_2' => 'Документ (file_excel_2)', + 'file_excel_3' => 'Документ (file_excel_3)', 'file_image' => 'Изображение (file_image)', 'group' => 'Группа (group)', 'account' => 'Аккаунт' @@ -178,18 +179,15 @@ class Product extends Document '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', - 'skipOnEmpty' => false, + 'skipOnEmpty' => true, 'extensions' => 'xlsx', 'checkExtensionByMimeType' => false, - 'maxFiles' => 5, 'maxSize' => 1024 * 1024 * 30, 'wrongExtension' => 'Разрешены только документы в формате: ".xlsx"', 'message' => 'Проблема при чтении документа', @@ -323,131 +321,6 @@ class Product extends Document 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; } - /** - * Вызывается после загрузки поставок из 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; } + + /** + * Инициализация продукта + * + * @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; + } } diff --git a/mirzaev/skillparts/system/models/Supply.php b/mirzaev/skillparts/system/models/Supply.php index 5814401..7312085 100644 --- a/mirzaev/skillparts/system/models/Supply.php +++ b/mirzaev/skillparts/system/models/Supply.php @@ -6,17 +6,25 @@ namespace app\models; use yii; +use app\models\traits\Xml2Array; use app\models\Account; use app\models\Product; 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\ProductInterface; use carono\exchange1c\controllers\ApiController; +use moonland\phpexcel\Excel; + +use DateTime; +use DateTimeZone; + 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['ЗначенияСвойств']['ЗначенияСвойства']" и переписать * Разобраться и создать возможность загрузки от лица другого аккаунта */ - 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; // Настройка - $model->ocid = $id ?? null; - $model->catn = (string) $product->Артикул; - $model->dscr = (string) $product->Описание; - $model->onec = self::xml2array($product->xml); + $_supply->ocid = $id ?? null; + $_supply->catn = (string) $supply->Артикул; + $_supply->dscr = (string) $supply->Описание; + $_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)) { @@ -397,22 +612,37 @@ class Supply extends Product implements ProductInterface, OfferInterface // Если идентификатор свойства совпадает с указанным в настройках свойства хранящего 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; } + /** + * + */ + public function write($context = null) + { + return $this->onec; + } + /** * @param mixed|null $context * @return array @@ -422,52 +652,6 @@ class Supply extends Product implements ProductInterface, OfferInterface 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 номеров * @@ -564,34 +748,34 @@ class Supply extends Product implements ProductInterface, OfferInterface return []; } - /** - * Прочитать стоимость - * - * @param Product|null $product Товар для поиска по вершинам - * - * @return array|null Данные о ценах - */ - public function readCost(Product $product = null): ?array - { - return static::readCostById($this->readId(), $product); - } + // /** + // * Прочитать стоимость + // * + // * @param Product|null $product Товар для поиска по вершинам + // * + // * @return array|null Данные о ценах + // */ + // public function readCost(Product $product = null): ?array + // { + // return static::readCostById($this->readId(), $product); + // } - /** - * Прочитать стоимость по идентификатору поставки - * - * @param string $_id Идентификатор поставки - * @param Product|null $product Товар для поиска по вершинам - * - * @return array|null Данные о ценах - */ - public static function readCostById(string $_id, Product $product = null): ?array - { - if (isset($product)) { - return SupplyEdgeProduct::searchByVertex($_id, $product->readId(), type: 'connect', limit: 1)['onec']['Цены']['Цена']; - } + // /** + // * Прочитать стоимость по идентификатору поставки + // * + // * @param string $_id Идентификатор поставки + // * @param Product|null $product Товар для поиска по вершинам + // * + // * @return array|null Данные о ценах + // */ + // public static function readCostById(string $_id, Product $product = null): ?array + // { + // if (isset($product)) { + // 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(); + } } diff --git a/mirzaev/skillparts/system/models/traits/SearchByEdge.php b/mirzaev/skillparts/system/models/traits/SearchByEdge.php index 44d1bb0..1909c55 100644 --- a/mirzaev/skillparts/system/models/traits/SearchByEdge.php +++ b/mirzaev/skillparts/system/models/traits/SearchByEdge.php @@ -4,12 +4,8 @@ declare(strict_types=1); namespace app\models\traits; -use yii; - use ArangoDBClient\Document; -use Exception; - trait SearchByEdge { /** diff --git a/mirzaev/skillparts/system/views/profile/panel.php b/mirzaev/skillparts/system/views/profile/panel.php index 7f9d79b..9ed9e75 100644 --- a/mirzaev/skillparts/system/views/profile/panel.php +++ b/mirzaev/skillparts/system/views/profile/panel.php @@ -8,10 +8,20 @@ use yii\helpers\Html; use app\models\Notification; 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]; ?> @@ -26,9 +36,11 @@ $panel ?? $panel = 'profile_panel_input_notifications';