Склады

This commit is contained in:
Arsen Mirzaev Tatyano-Muradovich 2021-12-27 14:03:15 +10:00
parent 7a4f12aa94
commit b08f7f7d0a
22 changed files with 574 additions and 325 deletions

View File

@ -58,21 +58,9 @@ class AuthenticationController extends Controller
// Настройка кода ответа // Настройка кода ответа
yii::$app->response->format = Response::FORMAT_JSON; yii::$app->response->format = Response::FORMAT_JSON;
// Валидация формы
// if (!empty($errors = ActiveForm::validate($model))) {
// // Настройка кода ответа
// yii::$app->response->statusCode = 401;
// return $errors;
// };
if (!yii::$app->user->isGuest || $model->authentication()) { if (!yii::$app->user->isGuest || $model->authentication()) {
// Аккаунт аутентифицирован // Аккаунт аутентифицирован
// Создание сессии
// yii::$app->session->open();
// Инициализация // Инициализация
$notifications_button = $this->renderPartial('/notification/button'); $notifications_button = $this->renderPartial('/notification/button');
$notifications_panel = $this->renderPartial('/notification/panel', ['notifications_panel_full' => true]); $notifications_panel = $this->renderPartial('/notification/panel', ['notifications_panel_full' => true]);
@ -94,19 +82,6 @@ class AuthenticationController extends Controller
// Запись ответа // Запись ответа
$return['redirect'] = '/' . $cookies['redirect']; $return['redirect'] = '/' . $cookies['redirect'];
// try {
// if (empty($return['main'] = $this->renderPartial($return['redirect']))) {
// throw new Exception('Представление найдено, но вернуло пустой результат');
// }
// } catch (Throwable $t) {
// $return['main'] = $this->renderPartial($return['redirect'] . '/index');
// }
// Генерация и запись
// $controller = 'app\\controllers\\' . ucfirst($cookies['redirect']) . 'Controller';
// $action = 'action' . ucfirst($cookies['redirect_action']);
// $return['main'] = (new $controller())->$action();
// Очистка cookie // Очистка cookie
unset(yii::$app->response->cookies['redirect']); unset(yii::$app->response->cookies['redirect']);

View File

@ -298,13 +298,16 @@ class OrderController extends Controller
if (yii::$app->request->isPost) { if (yii::$app->request->isPost) {
// POST-запрос // POST-запрос
// Настройка // Инициализация аккаунта
yii::$app->response->format = Response::FORMAT_JSON; $account ?? $account = Account::initAccount();
// Конвертация из UNIXTIME в формат поддерживаемый календарём по спецификации HTML // Конвертация из UNIXTIME в формат поддерживаемый календарём по спецификации HTML
$from = DateTime::createFromFormat('U', (string) $from)->format('Y-m-d'); $from = DateTime::createFromFormat('U', (string) $from)->format('Y-m-d');
$to = DateTime::createFromFormat('U', (string) $to)->format('Y-m-d'); $to = DateTime::createFromFormat('U', (string) $to)->format('Y-m-d');
// Запись формата ответа
yii::$app->response->format = Response::FORMAT_JSON;
return [ return [
'main' => $this->renderPartial('/orders/index', compact('orders', 'moderator_orders', 'search', 'from', 'to', 'window') 'main' => $this->renderPartial('/orders/index', compact('orders', 'moderator_orders', 'search', 'from', 'to', 'window')
+ ['panel' => $this->renderPartial('/orders/search/panel', compact('account') + ['response' => @$orders[0]['supplies']] ?? null)]), + ['panel' => $this->renderPartial('/orders/search/panel', compact('account') + ['response' => @$orders[0]['supplies']] ?? null)]),

View File

@ -20,7 +20,7 @@ use app\models\Settings;
use app\models\Dellin; use app\models\Dellin;
use app\models\SettingsEdgeSettings; use app\models\SettingsEdgeSettings;
use app\models\Terminal; use app\models\Terminal;
use app\models\Warehouse;
use Dadata\DadataClient as Dadata; use Dadata\DadataClient as Dadata;
use Throwable; use Throwable;
@ -453,6 +453,7 @@ class ProfileController extends Controller
$help = (bool) (yii::$app->request->post('help') ?? yii::$app->request->get('help')); $help = (bool) (yii::$app->request->post('help') ?? yii::$app->request->get('help'));
$target = yii::$app->request->post('account') ?? yii::$app->request->get('account'); $target = yii::$app->request->post('account') ?? yii::$app->request->get('account');
$number = yii::$app->request->post('number') ?? yii::$app->request->get('number'); $number = yii::$app->request->post('number') ?? yii::$app->request->get('number');
$warehouse = yii::$app->request->post('warehouse') ?? yii::$app->request->get('warehouse');
$sidebar = $this->renderPartial('sidebar'); $sidebar = $this->renderPartial('sidebar');
$groups = self::readGroups(); $groups = self::readGroups();
@ -474,6 +475,9 @@ class ProfileController extends Controller
]; ];
} }
// Инициализация файла
$supply->file_excel = UploadedFile::getInstance($supply, 'file_excel');
if (!empty($target)) { if (!empty($target)) {
// Получен аккаунт для которого необходимо загрузить каталог // Получен аккаунт для которого необходимо загрузить каталог
@ -489,9 +493,9 @@ class ProfileController extends Controller
if (Account::isMinimalAuthorized()) { if (Account::isMinimalAuthorized()) {
// Авторизован доступ к выполнению этого действия // Авторизован доступ к выполнению этого действия
$supply->{"file_excel_$number"} = UploadedFile::getInstance($supply, 'file_excel'); if ($supply->importExcel((int) $warehouse, $target)) {
// Импорт успешно завершён
if ($supply->importExcel($target)) {
return [ return [
'main' => $this->renderPartial('supplies', compact( 'main' => $this->renderPartial('supplies', compact(
'supply', 'supply',
@ -515,12 +519,9 @@ class ProfileController extends Controller
} else { } else {
// Не получен аккаунт для которого необходимо загрузить каталог // Не получен аккаунт для которого необходимо загрузить каталог
// Инициализация файлов
$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 ($supply->importExcel()) { if ($supply->importExcel((int) $warehouse)) {
return [ return [
'main' => $this->renderPartial('supplies', compact( 'main' => $this->renderPartial('supplies', compact(
'supply', 'supply',
@ -536,12 +537,15 @@ class ProfileController extends Controller
} }
} }
return $this->render('supplies', compact( return [
'main' => $this->renderPartial('supplies', compact(
'supply', 'supply',
'groups', 'groups',
'sidebar', 'sidebar',
'panel' 'panel'
)); )),
'_csrf' => yii::$app->request->getCsrfToken()
];
} }
public static function readGroups() public static function readGroups()

View File

@ -78,19 +78,6 @@ class RegistrationController extends Controller
// Запись ответа // Запись ответа
$return['redirect'] = '/' . $cookies['redirect']; $return['redirect'] = '/' . $cookies['redirect'];
// try {
// if (empty($return['main'] = $this->renderPartial($return['redirect']))) {
// throw new Exception('Представление найдено, но вернуло пустой результат');
// }
// } catch (Throwable $t) {
// $return['main'] = $this->renderPartial($return['redirect'] . '/index');
// }
// Генерация и запись
// $controller = 'app\\controllers\\' . ucfirst($cookies['redirect']) . 'Controller';
// $action = 'action' . ucfirst($cookies['redirect_action']);
// $return['main'] = (new $controller())->$action();
// Очистка cookie // Очистка cookie
unset(yii::$app->response->cookies['redirect']); unset(yii::$app->response->cookies['redirect']);

View File

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

View File

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

View File

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

View File

@ -11,6 +11,7 @@ use carono\exchange1c\interfaces\PartnerInterface;
use app\models\Dellin; use app\models\Dellin;
use app\models\traits\SearchByEdge; use app\models\traits\SearchByEdge;
use yii\web\UploadedFile; use yii\web\UploadedFile;
/** /**
@ -455,40 +456,6 @@ class Account extends Document implements IdentityInterface, PartnerInterface
return $this->syncListWithSettings($list, 'import_supplies_oem'); return $this->syncListWithSettings($list, 'import_supplies_oem');
} }
/**
* Генерация списка терминалов из ДеловыеЛинии для отправителя
*
* Актуальное (выбранное, активное) значение записывается первым
*
* @param array Необработанный список терминалов
*/
public function genListTerminalsFrom(): array
{
// Инициализация
$list = [];
$cities = Dellin::readAll();
foreach ($cities as $city) {
// Перебор городов
foreach ($city->data['terminals']['terminal'] as $termial) {
// Перебор терминалов
if (in_array($termial['id'], $list, true)) {
// Если встретился дубликат (исполняется очень часто)
continue;
}
// Запись
$list[$termial['id']] = $city->data['name'] . ' (' . $termial['address'] . ')';
}
}
return $this->syncListWithSettings($list, 'delivery_from_terminal');
}
/** /**
* Генерация списка терминалов из ДеловыеЛинии для получателя * Генерация списка терминалов из ДеловыеЛинии для получателя
* *
@ -901,7 +868,7 @@ class Account extends Document implements IdentityInterface, PartnerInterface
* *
* @param static|null $account Аккаунт * @param static|null $account Аккаунт
*/ */
public static function initAccount($account = null): ?static public static function initAccount(Account|int $account = null): ?static
{ {
if (is_null($account)) { if (is_null($account)) {
// Данные аккаунта не переданы // Данные аккаунта не переданы
@ -917,7 +884,11 @@ class Account extends Document implements IdentityInterface, PartnerInterface
return $account; return $account;
} }
} else { } else {
if (is_int($account)) { if ($account instanceof Account) {
// Передана инстанция аккаунта
return $account;
} else if (is_int($account)) {
// Передан идентификатор (_key) аккаунта (подразумевается) // Передан идентификатор (_key) аккаунта (подразумевается)
// Инициализация (поиск в базе данных) // Инициализация (поиск в базе данных)

View File

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace app\models;
use app\models\Account;
/**
* Связь аккаунтов и складов
*/
class AccountEdgeWarehouse extends Edge
{
/**
* Имя коллекции
*/
public static function collectionName(): string
{
return 'account_edge_warehouse';
}
}

View File

@ -51,32 +51,6 @@ class AccountForm extends Model
'message' => 'Заполните поле', 'message' => 'Заполните поле',
'on' => self::SCENARIO_AUTHENTICATION 'on' => self::SCENARIO_AUTHENTICATION
], ],
// Обязательные поля для регистрации
// [
// [
// 'rept',
// 'pols'
// ],
// 'required',
// 'message' => 'Заполните поле',
// 'on' => self::SCENARIO_REGISTRATION
// ],
// Повтор пароля
// [
// 'rept',
// 'compare',
// 'compareAttribute' => 'pswd',
// 'message' => "Пароли не совпадают",
// 'on' => self::SCENARIO_REGISTRATION,
// ],
// Принятие политики конфидециальности
// [
// 'pols',
// 'compare',
// 'compareValue' => 'on',
// 'message' => "Чтобы продолжить примите нашу политику конфидециальности",
// 'on' => self::SCENARIO_REGISTRATION,
// ],
// Функция "Запомнить меня" // Функция "Запомнить меня"
[ [
'auto', 'auto',
@ -151,12 +125,20 @@ class AccountForm extends Model
// Удалось инициализировать аккаунт // Удалось инициализировать аккаунт
try { try {
$account->validatePasswordWithHash($this->pswd); if (!$account->validatePasswordWithHash($this->pswd)) {
// Не пройдена проверка с хешем
throw new exception;
}
} catch (Exception $e) { } catch (Exception $e) {
// Проверка с хешем не пройдена // Не пройдена проверка с хешем
try { try {
$account->validatePasswordWithoutHash($this->pswd); if (!$account->validatePasswordWithoutHash($this->pswd)) {
// Не пройдена проверка с паролем
throw new exception;
}
} catch (Exception $e) { } catch (Exception $e) {
// Проверка без хеша не пройдена // Проверка без хеша не пройдена

View File

@ -12,7 +12,7 @@ use app\models\Account;
/** /**
* Импорт поставок * Импорт поставок
* *
* Хранит себе связи с поставками которые были загружены вместе * Хранит в себе связи с поставками которые были загружены вместе
*/ */
class Import extends Document class Import extends Document
{ {
@ -34,7 +34,6 @@ class Import extends Document
return array_merge( return array_merge(
parent::attributes(), parent::attributes(),
[ [
'pstn',
'name', 'name',
'file' 'file'
] ]
@ -49,7 +48,6 @@ class Import extends Document
return array_merge( return array_merge(
parent::attributeLabels(), parent::attributeLabels(),
[ [
'pstn' => 'Позиция',
'name' => 'Название', 'name' => 'Название',
'file' => 'Файл' 'file' => 'Файл'
] ]
@ -64,14 +62,6 @@ class Import extends Document
return array_merge( return array_merge(
parent::rules(), parent::rules(),
[ [
[
'pstn',
'required'
],
[
'pstn',
'integer'
],
[ [
[ [
'file', 'file',
@ -83,15 +73,26 @@ class Import extends Document
); );
} }
public static function searchByPosition(int $position = 1, Account $account = null, int $limit = 1): array /**
* Поиск по складу
*
* @param Warehouse $warehouse Инстанция склада
* @param int $limit Ограничение по максимальному количеству
*
* @return array Инстанции испортов
*/
public static function searchByWarehouse(Warehouse $warehouse, int $limit = 10): array
{ {
return self::searchByEdge( return self::searchByEdge(
from: 'account', from: 'warehouse',
to: 'import', to: 'import',
edge: 'import_edge_account', edge: 'warehouse_edge_import',
direction: 'INBOUND', direction: 'INBOUND',
subquery_where: 'account._key == "' . Account::initAccount($account)->_key . '" && import_edge_account.type == "loaded"', subquery_where: [
where: ['import.pstn' => $position], ['warehouse_edge_import._from' => $warehouse->readId()],
['warehouse_edge_import.type' => 'loaded']
],
where: 'warehouse_edge_import[0] != null',
limit: $limit limit: $limit
); );
} }

View File

@ -51,32 +51,9 @@ class Product extends Document
* Файл .excel для импорта товаров * Файл .excel для импорта товаров
* *
* Универсальный, когда неизвестно на какую позицию загружать каталог * Универсальный, когда неизвестно на какую позицию загружать каталог
*
* @todo Избавиться от свойств и сделать бесконечное количество места под новые каталоги
*/ */
public Excel|UploadedFile|string|null $file_excel = null; public Excel|UploadedFile|string|null $file_excel = null;
/**
* Файл .excel для импорта товаров
*
* @todo Избавиться от свойств и сделать бесконечное количество места под новые каталоги
*/
public Excel|UploadedFile|string|null $file_excel_1 = null;
/**
* Файл .excel для импорта товаров
*
* @todo Избавиться от свойств и сделать бесконечное количество места под новые каталоги
*/
public Excel|UploadedFile|string|null $file_excel_2 = null;
/**
* Файл .excel для импорта товаров
*
* @todo Избавиться от свойств и сделать бесконечное количество места под новые каталоги
*/
public Excel|UploadedFile|string|null $file_excel_3 = null;
/** /**
* Изображение для импорта * Изображение для импорта
*/ */
@ -132,9 +109,7 @@ class Product extends Document
'imgs' => 'Изображения (imgs)', 'imgs' => 'Изображения (imgs)',
'time' => 'Срок доставки (time)', 'time' => 'Срок доставки (time)',
'bffr' => 'Буфер', 'bffr' => 'Буфер',
'file_excel_1' => 'Документ (file_excel_1)', 'file_excel' => 'Документ (file_excel)',
'file_excel_2' => 'Документ (file_excel_2)',
'file_excel_3' => 'Документ (file_excel_3)',
'file_image' => 'Изображение (file_image)', 'file_image' => 'Изображение (file_image)',
'group' => 'Группа (group)', 'group' => 'Группа (group)',
'account' => 'Аккаунт' 'account' => 'Аккаунт'
@ -194,11 +169,7 @@ class Product extends Document
'message' => '{attribute} должен иметь значение от 0 до 30000' 'message' => '{attribute} должен иметь значение от 0 до 30000'
], ],
[ [
[ 'file_excel',
'file_excel_1',
'file_excel_2',
'file_excel_3'
],
'file', 'file',
'skipOnEmpty' => true, 'skipOnEmpty' => true,
'extensions' => 'xlsx', 'extensions' => 'xlsx',

View File

@ -13,7 +13,7 @@ use app\models\SupplyEdgeProduct;
use app\models\Settings; use app\models\Settings;
use app\models\Import; use app\models\Import;
use app\models\ImportEdgeSupply; use app\models\ImportEdgeSupply;
use app\models\ImportEdgeAccount; use app\models\WarehouseEdgeImport;
use carono\exchange1c\interfaces\OfferInterface; use carono\exchange1c\interfaces\OfferInterface;
use carono\exchange1c\interfaces\ProductInterface; use carono\exchange1c\interfaces\ProductInterface;
@ -379,8 +379,11 @@ class Supply extends Product implements ProductInterface, OfferInterface
* *
* На данный момент обрабатывает только импорт из * На данный момент обрабатывает только импорт из
* файлов с расширением .excel * файлов с расширением .excel
*
* @param int $warehouse Идентификатор склада (_key)
* @param Account|int|null $account Аккаунт
*/ */
public function importExcel(Account|int|null $account = null): bool public function importExcel(int $warehouse, Account|int|null $account = null): bool
{ {
// Инициализация // Инициализация
$data = []; $data = [];
@ -391,49 +394,26 @@ class Supply extends Product implements ProductInterface, OfferInterface
if ($this->validate()) { if ($this->validate()) {
// Пройдена проверка // Пройдена проверка
if (isset($this->file_excel_1)) { if (!isset($this->file_excel)) {
// Найден файл в позиции 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', 'Файл для импорта не найден'); $this->addError('errors', 'Файл для импорта не найден');
return false;
} }
// Инициализация // Инициализация хранилища файлов
preg_match_all('/UTC([\+\-0-9:]*)/', $account->zone ?? Settings::searchActive()['timezone_default'] ?? 'UTC+3', $timezone); preg_match_all('/UTC([\+\-0-9:]*)/', $account->zone ?? Settings::searchActive()['timezone_default'] ?? 'UTC+3', $timezone);
$timezone = $timezone[1][0]; $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/$warehouse/" . (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 (!file_exists($path))
if (!mkdir($path, 0775, true)) if (!mkdir($path, 0775, true))
throw new Exception('Не удалось создать директорию', 500); throw new Exception('Не удалось создать директорию', 500);
$file->saveAs($path = "$path/" . $filename = $file->baseName . '.' . $file->extension); $this->file_excel->saveAs($path = "$path/" . $filename = $this->file_excel->baseName . '.' . $this->file_excel->extension);
$data[] = Excel::import($path, [ $data[] = Excel::import($path, [
'setFirstRecordAsKeys' => true, 'setFirstRecordAsKeys' => true,
@ -449,7 +429,7 @@ class Supply extends Product implements ProductInterface, OfferInterface
if (count($data) < 1) { if (count($data) < 1) {
// Не найдены строки с товарами // Не найдены строки с товарами
$this->addError('erros', 'Не удалось найти данные товаров'); $this->addError('errors', 'Не удалось найти данные товаров');
} else { } else {
// Найдены строки с товарами // Найдены строки с товарами
@ -542,17 +522,16 @@ class Supply extends Product implements ProductInterface, OfferInterface
$import->file = $path; $import->file = $path;
$import->name = $filename; $import->name = $filename;
$import->pstn = $position;
if ($import->save()) { if ($import->save()) {
// Инстанция импорта успешно загружена // Инстанция импорта успешно загружена
if (ImportEdgeAccount::write(yii::$app->user->identity->collectionName() . '/' . yii::$app->user->identity->_key, $import->collectionName() . "/$import->_key", data: ['type' => 'loaded'])) { if (WarehouseEdgeImport::write(Warehouse::collectionName() . "/$warehouse", $import->readId(), data: ['type' => 'loaded'])) {
// Записано ребро: АККАУНТ -> ИНСТАНЦИЯ ПОСТАВОК // Записано ребро: СКЛАД -> ИНСТАНЦИЯ ПОСТАВОК
// Запись в журнал инстанции импорта // Запись в журнал инстанции импорта
$import->journal('connect_with_account', [ $import->journal('connect_with_warehouse', [
'target' => yii::$app->user->identity->collectionName() . '/' . yii::$app->user->identity->_key 'target' => Warehouse::collectionName() . "/$warehouse"
]); ]);
} }
@ -564,7 +543,7 @@ class Supply extends Product implements ProductInterface, OfferInterface
// Запись в журнал инстанции импорта // Запись в журнал инстанции импорта
$import->journal('connect_with_supply', [ $import->journal('connect_with_supply', [
'target' => $supply->collectionName() . "/$supply->_key" 'target' => $supply->readId()
]); ]);
} }
} }
@ -575,19 +554,19 @@ class Supply extends Product implements ProductInterface, OfferInterface
static::afterImportExcel($created, $updated); static::afterImportExcel($created, $updated);
// Удаление (важно именно задать null для формы в представлении) // Удаление (важно именно задать null для формы в представлении)
$this->file_excel_1 = $this->file_excel_2 = $this->file_excel_3 = null; $this->file_excel = null;
return true; return true;
} }
// Запись ошибки // Запись ошибки
$this->addError('erros', 'Не пройдена проверка параметров'); $this->addError('errors', 'Не пройдена проверка параметров');
// Макрос действий после импорта // Макрос действий после импорта
static::afterImportExcel($created, $updated); static::afterImportExcel($created, $updated);
// Удаление (важно именно задать null для формы в представлении) // Удаление (важно именно задать null для формы в представлении)
$this->file_excel_1 = $this->file_excel_2 = $this->file_excel_3 = null; $this->file_excel = null;
return false; return false;
} }
@ -757,34 +736,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']['Цены']['Цена'];
// } }
/** /**
* Найти аккаунт владельца * Найти аккаунт владельца

View File

@ -0,0 +1,246 @@
<?php
declare(strict_types=1);
namespace app\models;
use app\models\traits\SearchByEdge;
/**
* Склад
*
* Хранит в себе связи с инстанциями поставок, а от них и связи со всеми поставками
*/
class Warehouse extends Document
{
use SearchByEdge;
/**
* Имя коллекции
*/
public static function collectionName(): string
{
return 'warehouse';
}
/**
* Свойства
*/
public function attributes(): array
{
return array_merge(
parent::attributes(),
[
'name',
'addr',
'trmn',
'actv'
]
);
}
/**
* Метки свойств
*/
public function attributeLabels(): array
{
return array_merge(
parent::attributeLabels(),
[
'name' => 'Название',
'addr' => 'Адрес',
'trmn' => 'Терминал',
'actv' => 'Активность'
]
);
}
/**
* Правила
*/
public function rules(): array
{
return array_merge(
parent::rules(),
[
[
[
'name',
'addr'
],
'string'
],
[
'actv',
'boolean'
]
]
);
}
/**
* Запись по аккаунту
*
* @param Account|null $account Аккаунт
*
* @return static|null Записанный склад
*/
public static function writeByAccount(Account|null $account = null): ?static
{
// Инициализация аккаунта
$account = Account::initAccount($account);
// Инициализация склада
$warehouse = new static;
// Запись параметров склада
$warehouse->actv = true;
if ($warehouse->save()) {
// Удалось записать склад в базу данных
// Инициализация ребра: АККАУНТ -> СКЛАД
AccountEdgeWarehouse::writeSafe($account->readId(), $warehouse->readId(), data: ['type' => 'connected']);
return $warehouse;
}
return null;
}
/**
* Найти по аккаунту
*
* @param Account|null $account Аккаунт
* @param int $limit Ограничение по максимальному количеству
*
* @return mixed Склады
*/
public static function searchByAccount(Account|null $account = null, int $limit = 10): mixed
{
if ($account = Account::initAccount($account)) {
// Инициализирован аккаунт
return static::searchByEdge(
from: 'account',
to: 'warehouse',
edge: 'account_edge_warehouse',
direction: 'INBOUND',
subquery_where: [
[
'account_edge_warehouse._from == "' . $account->readId() . '"'
],
[
'account_edge_warehouse.type == "connected"'
]
],
where: 'account_edge_warehouse[0] != null',
limit: $limit
);
}
return null;
}
/**
* Инициализация с записью
*
* Читает все склады привязанные к аккаунту, либо создает новый склад
*
* @param Account|null $account Аккаунт
* @param int $limit Ограничение по максимальному количеству
*
* @return array Склады
*/
public static function initWithWrite(Account|null $account = null, int $limit = 10): array
{
if ($account = Account::initAccount($account)) {
// Инициализирован аккаунт
if ($warehouses = static::searchByAccount($account, $limit)) {
// Найдены склады
return $warehouses;
}
return [static::writeByAccount()];
}
return [];
}
/**
* Генерация списка терминалов из ДеловыеЛинии для отправителя
*
* Актуальное (выбранное, активное) значение записывается первым
*
* @param array Необработанный список терминалов
*/
public function genListTerminalsFrom(): array
{
// Инициализация
$list = [];
$cities = Dellin::read(9999);
foreach ($cities as $city) {
// Перебор городов
foreach ($city->data['terminals']['terminal'] as $termial) {
// Перебор терминалов
if (in_array($termial['id'], $list, true)) {
// Если встретился дубликат (исполняется очень часто)
continue;
}
// Запись
$list[$termial['id']] = $city->data['name'] . ' (' . $termial['address'] . ')';
}
}
return $this->syncListWithSettings($list, 'trmn');
}
/**
* Синхронизация списка вариантов параметра с текущим значением из настроек
*
* @param array &$list Список
* @param string $var Название параметра
*
* @return array Сортированный список
*/
protected function syncListWithSettings(array &$list, string $var): array
{
// Инициализация текущего значения параметра в начале массива
if (isset($this->$var)) {
// Параметр найден в настройках аккаунта
if (isset($list[$this->$var])) {
// Найдено совпадение сохранённого параметра с полученным списком из поставок
// Буфер для сохранения параметра
$buffer = $list[$this->$var];
// Удаление параметра
unset($list[$this->$var]);
// Сохранение параметра в начале массива
$list = [$this->$var => $buffer] + $list;
} else {
// Совпадение не найдено
// Сохранение параметра из данных аккаунта в начале массива
$list = [$this->$var => $this->$var] + $list;
}
} else {
// Параметр $var не найден в настройках аккаунта
// Сохранение параметра из данных аккаунта в начале массива
$list = ['Выберите' => 'Выберите'] + $list;
}
return $list;
}
}

View File

@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace app\models;
use app\models\Account;
/**
* Связь складов с инстанциями импортов
*/
class WarehouseEdgeImport extends Edge
{
/**
* Имя коллекции
*/
public static function collectionName(): string
{
return 'warehouse_edge_import';
}
/**
* Поиск по складу
*
* @param Warehouse $warehouse Склад
* @param int $limit Ограничение по максимальному количеству
*
* @return array Связи склада и инстанций поставок
*
* @deprecated Бесполезно
*/
public static function searchByWarehouse(Warehouse $warehouse, int $limit = 1): array
{
return static::find()->where(['_from' => $warehouse->readId()])->limit($limit)->all();
}
/**
* Поиск по инстанции импорта
*
* @param Import $import Инстанция импорта
* @param int $limit Ограничение по максимальному количеству
*
* @return array Связи склада и инстанций поставок
*
* @deprecated Бесполезно
*/
public static function searchByImport(Import $import, int $limit = 1): array
{
return static::find()->where(['_to' => $import->readId()])->limit($limit)->all();
}
}

View File

@ -25,7 +25,6 @@ if (
</nav> </nav>
<article class="col-9"> <article class="col-9">
<div class="p-4 rounded"> <div class="p-4 rounded">
<h4 class="ml-4 mb-4"><i class="fas fa-sliders-h my-auto mr-2"></i>Настройки</h4>
<div id="profile_panel_settings" class="profile_panel"> <div id="profile_panel_settings" class="profile_panel">
<div class="profile_panel_menu mb-3"> <div class="profile_panel_menu mb-3">
<?php if (!yii::$app->user->isGuest) : ?> <?php if (!yii::$app->user->isGuest) : ?>

View File

@ -15,7 +15,6 @@ $panel ?? $panel = 'profile_panel_monitoring_input_search_history';
</nav> </nav>
<article class="col-9"> <article class="col-9">
<div class="p-4 rounded"> <div class="p-4 rounded">
<h4 class="ml-3 mb-4"><i class="fas fa-eye my-auto mr-2"></i>Мониторинг</h4>
<div id="profile_panel_monitoring" class="profile_panel"> <div id="profile_panel_monitoring" 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_monitoring_input_orders_history">Заказы</label> <label class="btn button_white mb-0 mr-2" for="profile_panel_monitoring_input_orders_history">Заказы</label>

View File

@ -34,7 +34,6 @@ $timezone = $timezone[1][0];
</nav> </nav>
<article class="col-9"> <article class="col-9">
<div class="p-4 rounded"> <div class="p-4 rounded">
<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_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>

View File

@ -6,9 +6,9 @@ use yii;
use yii\bootstrap\ActiveForm; use yii\bootstrap\ActiveForm;
use app\models\Account; use app\models\Account;
use app\models\Document;
use app\models\Import; use app\models\Import;
use app\models\Settings; use app\models\Settings;
use app\models\Warehouse;
use DateTime; use DateTime;
use DateTimeZone; use DateTimeZone;
@ -27,11 +27,10 @@ $panel ?? $panel = 'profile_panel_supplies_input_import';
</nav> </nav>
<article class="col-9"> <article class="col-9">
<div class="h-100 p-4 rounded"> <div class="h-100 p-4 rounded">
<h4 class="ml-4 mb-4"><i class="fas fa-truck my-auto mr-2"></i>Управление поставками</h4>
<div id="profile_panel_supplies" class="profile_panel"> <div id="profile_panel_supplies" 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_supplies_input_settings">Настройки</label> <label class="btn button_white mb-0 mr-2" for="profile_panel_supplies_input_settings">Настройки</label>
<label class="btn button_white mb-0 mr-2" for="profile_panel_supplies_input_import">Импорт</label> <label class="btn button_white mb-0 mr-2" for="profile_panel_supplies_input_import">Склады</label>
</div> </div>
<div class="profile_panel_content"> <div class="profile_panel_content">
@ -42,10 +41,10 @@ $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>Управление складами</h5>
<div class="dropdown-divider mb-4"></div> <div class="dropdown-divider mb-4"></div>
<?php if (Account::isMinimalAuthorized()) : ?> <?php if (false && Account::isMinimalAuthorized()) : ?>
<?php <?php
$form = ActiveForm::begin([ $form = ActiveForm::begin([
'id' => 'form_product_import_excel', 'id' => 'form_product_import_excel',
@ -62,7 +61,7 @@ $panel ?? $panel = 'profile_panel_supplies_input_import';
?> ?>
<?= $form->field($supply, 'account', ['options' => ['class' => "ml-auto mb-4 pr-0 col-4 d-flex"]])->input('text', ['placeholder' => yii::$app->user->identity->_key])->label(options: ['class' => 'mr-2 my-auto font-weight-bold']); ?> <?= $form->field($supply, 'account', ['options' => ['class' => "ml-auto mb-4 pr-0 col-4 d-flex"]])->input('text', ['placeholder' => yii::$app->user->identity->_key])->label(options: ['class' => 'mr-2 my-auto font-weight-bold']); ?>
<?= $form->field($supply, 'file_excel', ['enableLabel' => false, 'options' => ['class' => 'mr-auto mb-4 pr-0 col-5 d-flex flex-column']])->fileInput(['class' => 'my-auto', 'multiple' => true, 'onChange' => 'page_profile_supplies_import_excel_moderator(this.parentElement.parentElement, \'profile_panel_supplies_input_import\', this.parentElement.parentElement.getElementsByTagName(\'input\')[0].value)']) ?> <?= $form->field($supply, 'file_excel', ['enableLabel' => false, 'options' => ['class' => 'mr-auto mb-4 pr-0 col-5 d-flex flex-column']])->fileInput(['class' => 'my-auto', 'multiple' => true, 'onChange' => 'page_profile_supplies_import_excel(this.parentElement.parentElement, this.parentElement.parentElement.getElementsByTagName(\'input\')[0].value, \'profile_panel_supplies_input_import\')']) ?>
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>
<?php <?php
@ -123,9 +122,58 @@ $panel ?? $panel = 'profile_panel_supplies_input_import';
<?php endif ?> <?php endif ?>
<?php endforeach ?> <?php endforeach ?>
<?php else : ?> <?php else : ?>
<?php
// Инициализация складов
$warehouses = Warehouse::initWithWrite();
// Инициализация итератора
$amount_warehouses = 0;
?>
<?php foreach ($warehouses as $warehouse) : ?>
<section class="col<?= ++$amount_warehouses <= count($warehouses) ? ' mb-4' : '' ?>">
<h5 class="d-flex"><?= $warehouse->name ?? 'Без названия' ?><small class="ml-auto mt-auto"><a class="text-dark fas fa-pen" type="button" onclick="edit_zaloopa"></a></small></h5>
<?php $form = ActiveForm::begin([
'id' => 'form_warehouse_settings',
'action' => false,
'fieldConfig' => [
'template' => '{label}{input}',
],
'options' => [
'onsubmit' => 'return false;'
]
]);
$list = $warehouse->genListTerminalsFrom();
?>
<?= $form->field($warehouse, 'trmn', ['options' => ['class' => "mb-3"]])
->dropDownList($list, [
'onChange' => 'page_profile_settings(this.parentElement.parentElement, \'profile_panel_settings_company\')',
'disabled' => count($list) <= 1
])->label(false); ?>
<?php ActiveForm::end(); ?>
<?php
// Инициализация номера инстанции импорта
$amount_imports = 0;
?>
<?php foreach (Import::searchByWarehouse($warehouse) as $import) : ?>
<div class="mx-2 mb-3 row">
<div class="col-auto"><?= ++$amount_imports ?>.</div>
<div class="col">
<?= $import[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 endforeach ?>
<?php <?php
$form = ActiveForm::begin([ $form = ActiveForm::begin([
'id' => 'form_product_import_excel', 'id' => 'form_warehouse_supplies',
'action' => false, 'action' => false,
'fieldConfig' => [ 'fieldConfig' => [
'template' => '{label}{input}', 'template' => '{label}{input}',
@ -137,70 +185,21 @@ $panel ?? $panel = 'profile_panel_supplies_input_import';
] ]
]); ]);
?> ?>
<?php if ($import_1 = Import::searchByPosition(1)) : ?>
<div class="mb-3 row">
<div class="col-auto">1.</div>
<div class="col">
<?= $import_1[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">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>
<?php if ($import_2 = Import::searchByPosition(2)) : ?>
<div class="mb-3 row"> <div class="mb-3 row">
<div class="col-auto">2.</div> <?= $form->field($supply, 'file_excel', ['enableLabel' => false])->fileInput(['multiple' => true, 'onChange' => 'page_profile_supplies_import_excel(this.parentElement.parentElement.parentElement, ' . $warehouse->_key . ', undefined, \'profile_panel_supplies_input_import\')']) ?>
<div class="col">
<?= $import_2[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="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>
<?php endif ?>
<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="col-auto">3.</div>
<div class="col">
<?= $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>
<?php endif ?>
<?= $form->errorSummary($supply, ['header' => 'В документе были допущены ошибки:' /*, 'footer' => 'Исправьте их и попробуйте снова'*/]); ?> <?= $form->errorSummary($supply, ['header' => 'В документе были допущены ошибки:' /*, 'footer' => 'Исправьте их и попробуйте снова'*/]); ?>
<?php ActiveForm::end(); ?> <?php ActiveForm::end(); ?>
</section>
<?php if ($amount_warehouses < count($warehouses)) : ?>
<div class="dropdown-divider mx-3 mb-4"></div>
<?php endif ?>
<?php endforeach ?>
<?php endif ?> <?php endif ?>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,4 +0,0 @@
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php [L]

View File

@ -27,34 +27,40 @@ function page_profile_supplies(form, panel) {
return false; return false;
}; };
function page_profile_supplies_import_excel(form, panel, account) { function page_profile_supplies_import_excel(form, warehouse, account, panel) {
function send(help = false) { function send(help = false) {
if (form === undefined) { if (form === undefined) {
form = { form = {
'_csrf': yii.getCsrfToken() '_csrf': yii.getCsrfToken()
}; };
form.help = +help;
if (panel !== undefined) { if (panel !== undefined) {
form.panel = panel; form.panel = panel;
}; };
if (account !== undefined) { if (warehouse !== undefined) {
form.number = prompt('Номер позиции в которую загружать каталог', 1); form.warehouse = warehouse;
};
if (account !== undefined) {
form.account = account; form.account = account;
}; };
} else { } else {
form = new FormData(form); form = new FormData(form);
form.append('help', help); form.append('help', +help);
if (panel !== undefined) { if (panel !== undefined) {
form.append('panel', panel); form.append('panel', panel);
}; };
if (account !== undefined) { if (warehouse !== undefined) {
form.append('number', prompt('Номер позиции в которую загружать каталог', 1)); form.append('warehouse', warehouse);
};
if (account !== undefined) {
form.append('account', account); form.append('account', account);
}; };
}; };