крупная обнова

This commit is contained in:
Arsen Mirzaev Tatyano-Muradovich 2022-06-01 14:24:48 +10:00
parent e1fa1c1f5a
commit 2c2e830f0a
24 changed files with 1161 additions and 1238 deletions

View File

@ -6,6 +6,9 @@ use yii\console\Controller;
use yii\console\ExitCode;
use app\models\Invoice;
use app\models\Product;
use app\models\ProductGroup;
use app\models\ImportEdgeSupply;
class TestController extends Controller
{
@ -75,4 +78,59 @@ class TestController extends Controller
return ExitCode::OK;
}
public function actionAnalogs($_id = 'product/51987159')
{
return ExitCode::OK;
}
public function actionWriteAnalog($_id = 'product/51987159', $analog = 'product/12051485')
{
// Инициализация товара
$product = Product::searchById($_id);
// Инициализация аналога
$analog = Product::searchById($analog);
if (!$group = ProductGroup::searchByProduct($product)) {
// Не найдена группа товаров
// Запись новой группы
$group = ProductGroup::writeEmpty(active: true);
// Запись товара в группу
$group->writeProduct($product);
}
if ($_group = ProductGroup::searchByProduct($analog)) {
// Найдена друга группа у товара который надо добавить в группу
// Перенос всех участников (включая целевой товар)
return $group->transfer($_group);
} else {
// Не найдена группа у товара который надо добавить в группу
// Запись целевого товара в группу
return $group->writeProduct($analog);
}
return ExitCode::OK;
}
public function actionReadAnalog($_id = 'product/51987159')
{
var_dump((ProductGroup::searchByProduct(Product::searchById($_id))->searchProducts()));
return ExitCode::OK;
}
public function actionEdgeMax()
{
var_dump(ImportEdgeSupply::generateVersion());
return ExitCode::OK;
}
}

View File

@ -86,26 +86,26 @@ class CartController extends Controller
$account = Account::initAccount();
// Поиск корзины (текущего заказа)
$model = Order::searchByType();
$data = Order::searchByType()[0] ?? null;
if (empty($model)) {
if (empty($data['order'])) {
// Корзина не инициализирована
// Инициализация
$model = new Order();
$data['order'] = new Order();
if ($model->save()) {
if ($data['order']->save()) {
// Удалось инициализировать заказ
// Подключение заказа к аккаунту
$model->connect($account);
$data['order']->connect($account);
} else {
throw new Exception('Не удалось инициализировать заказ');
}
}
// Инициализация содержимого корзины
$connections = $model->content(10, $page);
$data['supplies'] = $data['order']->supplies(10, $page, test: true);
// Инициализация данных списка для выбора терминала
$delivery_to_terminal_list = $account->genListTerminalsTo();
@ -138,14 +138,14 @@ class CartController extends Controller
yii::$app->response->format = Response::FORMAT_JSON;
return [
'main' => $this->renderPartial('index', compact('account', 'model', 'connections', 'delivery_to_terminal_list')),
'main' => $this->renderPartial('index', compact('account', 'data', 'delivery_to_terminal_list')),
'title' => 'Корзина',
'redirect' => '/cart',
'_csrf' => yii::$app->request->getCsrfToken()
];
}
return $this->render('index', compact('account', 'model', 'connections', 'delivery_to_terminal_list'));
return $this->render('index', compact('account', 'data', 'delivery_to_terminal_list'));
}
public function actionEditComm(string $catn, string $prod): array|string|null

View File

@ -266,11 +266,10 @@ class OrderController extends Controller
}
// Инициализация заказов
$orders = Order::searchByType(
$data = Order::searchByType(
type: $type,
limit: 10,
page: 1,
select: '{account_edge_order, order}',
supplies: true,
from: $from,
to: $to,
@ -278,7 +277,6 @@ class OrderController extends Controller
);
// Фильтрация
if (
!yii::$app->user->isGuest
&& yii::$app->user->identity->type === 'administrator'
@ -287,20 +285,20 @@ class OrderController extends Controller
// Пользователь имеет доступ
// Инициализация заказов для модератора
$moderator_orders = self::genOrdersForModeration();
$moderator_data = Order::searchByType(account: '@all', type: 'requested', limit: 10, page: 1, supplies: true);
} else {
// Пользователь не имеет доступ
// Инициализация заглушки
$moderator_orders = null;
$moderator_data = null;
}
if (yii::$app->request->isPost) {
// POST-запрос
// Инициализация аккаунта
$account ?? $account = Account::initAccount();
if (yii::$app->request->isPost) {
// POST-запрос
// Конвертация из UNIXTIME в формат поддерживаемый календарём по спецификации HTML
$from = DateTime::createFromFormat('U', (string) $from)->format('Y-m-d');
$to = DateTime::createFromFormat('U', (string) $to)->format('Y-m-d');
@ -309,15 +307,15 @@ class OrderController extends Controller
yii::$app->response->format = Response::FORMAT_JSON;
return [
'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)]),
'main' => $this->renderPartial('/orders/index', compact('data', 'moderator_data', 'account', 'search', 'from', 'to', 'window')
+ ['panel' => $this->renderPartial('/orders/search/panel', compact('account') + ['data' => $data] ?? null)]),
'title' => 'Заказы',
'redirect' => '/orders',
'_csrf' => yii::$app->request->getCsrfToken()
];
}
return $this->render('/orders/index', compact('orders', 'moderator_orders'));
return $this->render('/orders/index', compact('data', 'moderator_data', 'account'));
}
/**
@ -394,19 +392,19 @@ class OrderController extends Controller
];
// Инициализация корзины
if (!$model = Order::searchByType($account)) {
if (!$data = Order::searchByType($account)[0]) {
// Корзина не найдена (текущий заказ)
// Инициализация
$model = new Order();
$model->save() or throw new Exception('Не удалось инициализировать заказ');
$data['order'] = new Order();
$data['order']->save() or throw new Exception('Не удалось инициализировать заказ');
// Запись ребра: АККАУНТ -> ЗАКАЗ
AccountEdgeOrder::write($account->readId(), $model->readId(), 'current') or $model->addError('errors', 'Не удалось инициализировать ребро: АККАУНТ -> ЗАКАЗ');
AccountEdgeOrder::write($account->readId(), $data['order']->readId(), 'current') or $data['order']->addError('errors', 'Не удалось инициализировать ребро: АККАУНТ -> ЗАКАЗ');
}
// Если запись не удалась, то вернуть код: 500 Internal Server Error
$model->writeSupply($supply_id, $delivery_type, (int) $amount) or yii::$app->response->statusCode = 500;
$data['order']->writeSupply($supply_id, $delivery_type, (int) $amount) or yii::$app->response->statusCode = 500;
return $return;
}
@ -585,12 +583,23 @@ class OrderController extends Controller
*/
public function actionRequest(): string|array|null
{
// Инициализация
$model = Order::searchByType(supplies: true);
// Инициализация аккаунта
$account = Account::initAccount();
if (($account_edge_order = AccountEdgeOrder::searchByVertex(yii::$app->user->id, $model->readId(), 'current')[0]) ?? false) {
if (isset($account)) {
// Найден аккаунт
if ($account->filled() === true) {
// Заполнены все необходимые поля для оформления заказа (подразумевается прохождение второго этапа регистрации)
// Инициализация данных о заказе и поставках
$data = Order::searchByType(supplies: true)[0];
if (($account_edge_order = AccountEdgeOrder::searchByVertex(yii::$app->user->id, $data['order']->readId(), 'current')[0]) ?? false) {
// Найдено ребро: АККАУНТ -> ЗАКАЗ
// Инициализация статуса необходимости реинициализации
$reinitialization = false;
if ($order_edge_supply = OrderEdgeSupply::searchByDirection($account_edge_order->_to, type: 'write', limit: 500)) {
// Найдены рёбра: ЗАКАЗ -> ПОСТАВКА
@ -601,89 +610,71 @@ class OrderController extends Controller
if ($product = Product::searchBySupplyId($edge->_to)) {
// Найден товар
// Проверка на активность товара
if ($product['stts'] === 'active');
else $edge->delete();
if ($product['stts'] !== 'active') {
// Не активен товар
// Удаление из заказа
$edge->delete();
// Статус необходимости реинициализации
$reinitialization = true;
}
}
}
}
// Реиницилазация
$model = Order::searchByType(supplies: true);
// Реинициализация
if ($reinitialization) $data = Order::searchByType(supplies: true);
// Запись
$account_edge_order->type = 'requested';
if ($account_edge_order->update()) {
if ($account_edge_order->update() > 0) {
// Удалось сохранить изменения
// Запись в журнал
$model->journal('requested');
$data['order']->journal('requested');
// Инициализация буфера поставок
$supplies = [];
foreach ($model['supplies'] as $supply) {
// Перебор поставок
$supplies[] = [
'title' => $supply['supply']['catn'],
'delivery' => 0,
'amount' => [
// 'value' => $supply['amount'][$supply['order_edge_supply'][]] ?? 0,
// 'value' => $supply['amount'] ?? 0,
'value' => 0,
'unit' => 'шт'
],
'cost' => [
// 'value' => $supply['cost'] ?? 0,
'value' => 0,
'unit' => 'руб'
],
'type' => 'supply'
];
}
Invoice::generate($model->_key, $this->renderPartial('/invoice/order/pattern', [
Invoice::generate($data['order']->_key, $this->renderPartial('/invoice/order/pattern', [
'buyer' => [
'id' => yii::$app->user->identity->_key,
'info' => 'Неизвестно'
],
'order' => [
'id' => $model->_key,
'date' => $account_edge_order->date ?? time(),
'entries' => $supplies
]
'data' => $data,
'date' => $account_edge_order->date ?? time()
]));
// Отправка уведомлений модераторам
Notification::_write($this->renderPartial('/notification/system/orders/new', ['id' => $account_edge_order->_key]), true, '@auth', Notification::TYPE_MODERATOR_ORDER_NEW);
// Отправка уведомления покупателю
Notification::_write($this->renderPartial('/notification/system/orders/new', ['id' => $account_edge_order->_key]), true, $account->_key, Notification::TYPE_NOTICE);
}
}
return $this->actionIndex();
} else {
// Не заполнены все необходимые поля для оформления заказа (подразумевается прохождение второго этапа регистрации)
var_dump($account->getErrors());
foreach ($account->getErrors() as $parameter => $errors) {
// Перебор параметров с ошибками
foreach ($errors as $error) {
// Перебор ошибок
// Инициализация ярлыка
$label = $account->getAttributeLabel($parameter) ?? $parameter;
// Отправка уведомления покупателю
Notification::_write($error . " ($label)", account: $account->_key, type: Notification::TYPE_ERROR);
}
}
/**
* Генерация данных заказов для модераторов
*
* Включает поиск запрошенных заказов и связанных с ними поставках
*
* @return array ['order' => array, 'order_edge_account' => array, 'supplies' => array]
*
* @todo Уничтожить заменив на Order::searchByType(supplies: true)
*/
protected static function genOrdersForModeration(int $page = 1): array
{
$orders = Order::searchByType(account: '@all', type: 'requested', limit: 10, page: 1, select: '{account_edge_order, order}');
foreach ($orders as &$order) {
// Перебор заказов
$order['supplies'] = Order::searchById($order['order']['_id'])->content(10, $page);
return null;
}
}
return $orders;
}
/**

View File

@ -791,6 +791,8 @@ class ProfileController extends Controller
return self::syncGeolocationWithDellin($account);
}
return false;
}
/**
@ -1227,6 +1229,9 @@ class ProfileController extends Controller
* Удалить склад
*
* @return array|string|null
*
* @todo
* 1. Удаление всех ImportEdgeSupply с устаревшими версиями
*/
public function actionWarehousesDelete(): array|string|null
{
@ -1277,13 +1282,24 @@ class ProfileController extends Controller
foreach (Supply::searchByImport($import->readId(), limit: 9999) as $supply) {
// Перебор найденных поставок
if (ImportEdgeSupply::searchBySupply($supply, limit: 1)?->delete() === 1) {
if ($edge = ImportEdgeSupply::searchBySupply($supply)) {
// Найдено ребро: ИНСТАНЦИЯ ПОСТАВКИ -> ПОСТАВКА
// Инициализация версии
$version = $edge->vrsn;
if ($edge->delete() === 1) {
// Удалено ребро: ИНСТАНЦИЯ ПОСТАВКИ -> ПОСТАВКА
if (ImportEdgeSupply::searchMaxVersion($supply) <= $version) {
// Не найдена более новая версия поставки (обрабатываемая считается актуальной)
// Удаление поставки
if ($supply->delete() === 1) ++$deleted;
}
}
}
}
// Отправка уведомления
Notification::_write("Удалено $deleted поставок из инстанции поставки $_key", account: $account->_key);

View File

@ -989,7 +989,7 @@ class Account extends Document implements IdentityInterface, PartnerInterface
public static function isModer($account = null): bool
{
if ($account = self::initAccount($account)) {
// Аккаунт инициализирован
// Инициализирован аккаунт
if ($account->type === 'moderator') {
return true;
@ -1022,4 +1022,26 @@ class Account extends Document implements IdentityInterface, PartnerInterface
default => 'Неизвестно'
};
}
public static function filled($account = null): bool|self
{
if ($account = self::initAccount($account)) {
// Инициализирован аккаунт
// Проверка на заполненность обязательных полей
if (empty($account->name)) $account->addError('name', 'Не заполнено необходимое поле для заказа');
if (empty($account->boss)) $account->addError('boss', 'Не заполнено необходимое поле для заказа');
if (empty($account->simc)) $account->addError('simc', 'Не заполнено необходимое поле для заказа');
if (empty($account->comp)) $account->addError('comp', 'Не заполнено необходимое поле для заказа');
if (empty($account->mail)) $account->addError('mail', 'Не заполнено необходимое поле для заказа');
if (empty($account->taxn)) $account->addError('taxn', 'Не заполнено необходимое поле для заказа');
if (empty($account->cntg)) $account->addError('cntg', 'Не заполнено необходимое поле для заказа');
if (empty($account->fadd)) $account->addError('fadd', 'Не заполнено необходимое поле для заказа');
if (empty($account->ladd)) $account->addError('ladd', 'Не заполнено необходимое поле для заказа');
return $account->hasErrors() ? $account : true;
}
return false;
}
}

View File

@ -24,10 +24,20 @@ class AccountEdgeOrder extends Edge
return self::find()->where(['_to' => $order_id])->limit($limit)->all();
}
public static function convertStatusToRussian(string $status): string
/**
* Генерация ярлыка на русском языке для статуса заказа
*
* @param string $status Статус заказа
*
* @return string Ярлык статуса на русском языке
*/
public static function statusToRussian(string $status = ''): string
{
return match ($status) {
'accepted' => 'Доставляется',
'requested' => 'Запрошен',
'accepted' => 'Ожидается отправка',
'going' => 'Доставляется',
'completed' => 'Завершен',
default => 'Обрабатывается'
};
}

View File

@ -150,7 +150,7 @@ abstract class Document extends ActiveRecord
return static::findOne(['_id' => $_id]);
}
public static function readLast(): ?static
public static function readLast(): static|null|bool
{
return static::find()->orderBy(['DESC'])->one();
}

View File

@ -167,20 +167,44 @@ abstract class Edge extends Document
/**
* Поиск рёбер по направлению
*/
public static function searchByDirection(string $target, string $direction = 'OUTBOUND', string $type = '', int $limit = 1): static|array|null
public static function searchByDirection(string $_from, string $direction = 'OUTBOUND', string $type = '', array $where = [], int $limit = 1): static|array|null
{
if (str_contains($direction, 'OUTBOUND')) {
// Исходящие рёбра
$query = static::find()->where(['_from' => $target, 'type' => $type]);
if (isset($where)) {
// Получен параметр $where
// Реинициализация
$where = array_merge($where + ['_from' => $_from]);
} else {
// Не получен параметр $where
// Реинициализация
$where = array_merge(['_from' => $_from, 'type' => $type]);
}
$query = static::find()->where($where);
} else if (str_contains($direction, 'INBOUND')) {
// Входящие рёбра
$query = static::find()->where(['_to' => $target, 'type' => $type]);
if (isset($where)) {
// Получен параметр $where
// Реинициализация
$where = array_merge($where + ['_to' => $_from]);
} else {
// Не получен параметр $where
// Реинициализация
$where = array_merge(['_to' => $_from, 'type' => $type]);
}
$query = static::find()->where($where);
} else if (str_contains($direction, 'ANY')) {
// Исходящие и входящие рёбра
return static::searchByDirection(target: $target, direction: 'OUTBOUND', type: $type, limit: $limit) + static::searchByDirection(target: $target, direction: 'INBOUND', type: $type, limit: $limit);
return static::searchByDirection(_from: $_from, direction: 'OUTBOUND', type: $type, where: $where, limit: $limit) + static::searchByDirection(_from: $_from, direction: 'INBOUND', type: $type, where: $where, limit: $limit);
}
if ($limit < 2) {

View File

@ -4,6 +4,8 @@ declare(strict_types=1);
namespace app\models;
use mirzaev\yii2\arangodb\Query;
/**
* Связь инстанций импорта с поставками
*/
@ -25,6 +27,7 @@ class ImportEdgeSupply extends Edge
return array_merge(
parent::attributes(),
[
'vrsn'
]
);
}
@ -37,6 +40,7 @@ class ImportEdgeSupply extends Edge
return array_merge(
parent::attributeLabels(),
[
'vrsn' => 'Версия'
]
);
}
@ -49,12 +53,63 @@ class ImportEdgeSupply extends Edge
return array_merge(
parent::rules(),
[
[
[
'vrsn'
],
'integer',
'message' => '{attribute} должен быть числом.'
]
]
);
}
public static function searchBySupply(Supply $supply): static
/**
* Перед сохранением
*
* @todo Подождать обновление от ебаного Yii2 и добавить
* проверку типов передаваемых параметров
*/
public function beforeSave($data): bool
{
return static::find()->where(['_to' => $supply->readId()])->one();
if (parent::beforeSave($data)) {
if ($this->isNewRecord) {
}
return true;
}
return false;
}
/**
* Поиск максимальной версии
*
* Ищет максимальную версию у поставок
*
* @param Supply $supply Поставка
*
* @return int Версия, если найдена
*/
public static function searchMaxVersion(Supply $supply): ?int
{
return static::find()->execute("RETURN MAX(
FOR import_edge_supply IN import_edge_supply
FILTER (import_edge_supply._to == 'supply/$supply->_key')
LIMIT 0,999
RETURN import_edge_supply.vrsn
)")[0] ?? null;
}
/**
* Поиск по поставке
*
* @param Supply $supply
*
* @return static|null
*/
public static function searchBySupply(Supply $supply): ?static
{
return static::find()->where(['_to' => $supply->readId()])->one()[0] ?? null;
}
}

View File

@ -274,7 +274,7 @@ class Order extends Document implements DocumentInterface
int|null $from = null,
int|null $to = null,
bool $count = false
): self|int|array|null {
): int|array|null {
// Инициализация аккаунта
if (empty($account) && isset(yii::$app->user->identity)) {
// Данные не переданы
@ -335,7 +335,7 @@ class Order extends Document implements DocumentInterface
}
// Поиск заказов в базе данных
$return = self::searchByEdge(
$orders = self::searchByEdge(
from: 'account',
to: 'order',
subquery_where: $subquery_where,
@ -346,73 +346,81 @@ class Order extends Document implements DocumentInterface
sort: ['DESC'],
select: $select,
direction: 'INBOUND',
count: !$supplies && $count ? true : false
count: !$supplies && $count
);
if (!$supplies && $count) {
// Запрошен подсчет заказов
return $return;
return $orders;
}
// Инициализация буфера возврата
$return = [];
// Инициализация архитектуры буфера вывода
foreach ($orders as $key => $order) {
// Перебор заказов
// Запись в буфер возврата
$return[$key]['order'] = $order;
}
if ($supplies) {
// Запрошен поиск поставок
foreach ($return as &$container) {
foreach ($return as $key => &$container) {
// Перебор заказов
if ($container instanceof Order) {
$buffer = $container;
if ($container['order'] instanceof Order) {
// Инстанция заказа
// Инициализация заказа
$order = $container['order'];
} else {
// Массив с заказом (подразумевается)
// Инициализация настроек
$config = $container['order'];
unset($config['_id'], $config['_rev'], $config['_id']);
// Инициализация буфера
$buffer = new Order($config);
// Инициализация заказа
$order = new Order($config);
}
// Чтение полного содержания
$container['supplies'] = $buffer->content($limit, $page, $search, count: $count);
$return[$key]['supplies'] = $order->supplies($limit, $page, $search, count: $count);
if ($count) {
// Запрошен подсчет поставок
// Запрошен подсчет поставок (переделать под подсчёт)
return $container['supplies'];
return $return[$key]['supplies'];
}
}
}
return $limit === 1 ? $return[0] ?? null : $return;
return $return;
}
// /**
// * Объеденить дубликаты поставок
// *
// * @return array Данные заказа
// */
// public function mergeDuplicateSupplies(array $order): array {
// foreach ($orders as $order) {
// }
// }
/**
* Поиск содержимого заказа
* Поиск содержимых поставок заказа
*
* @todo В будущем возможно заказ не только поставок реализовать
* Переписать реестр и проверку на дубликаты, не понимаю как они работают
*/
public function content(int $limit = 1, int $page = 1, string|null $search = null, bool $count = false): Supply|int|array|null
public function supplies(int $limit = 1, int $page = 1, string|null $search = null, bool $count = false, $test = false): Supply|int|array|null
{
// Инициализация аккаунта
$account = Account::initAccount();
// Генерация сдвига по запрашиваемым данным (пагинация)
$offset = $limit * ($page - 1);
if (!empty($search)) {
// Передан поиск по продуктам в заказах
// Добавить ограничитель
// Запись ограничения по максимальному значению
$limit = 9999;
}
@ -426,160 +434,77 @@ class Order extends Document implements DocumentInterface
'order._id' => $this->readId()
]
],
foreach: ['edge' => 'order_edge_supply'],
where: 'edge._to == supply._id',
where: 'order_edge_supply != []',
limit: $limit,
offset: $offset,
filterStart: ['catn' => $search],
direction: 'INBOUND',
select: '{supply, order_edge_supply}',
select: '{order_edge_supply}',
count: $count
);
if ($count) {
// Запрошен подсчет
// Подсчёт запрошен
return $connections;
}
// Рекурсивная фильтрации соединений
recursive_connections_filtration:
// Инициализация статуса об изменении списка поставок
$changed = false;
// Инициализация глобального буфера поставок
$buffer_global = [];
// Разделение поставок по типам доставки
// foreach ($connections as &$connection) {
// // Перебор поставок
// if (count($connection['order_edge_supply'] ?? []) > 1) {
// // Поставок более чем одна
// // Инициализация (выбор значения для фильтрации)
// $target_dlvr_type = reset($connection['order_edge_supply'])['dlvr']['type'] ?? 'auto';
// // Инициализация буфера поставок
// $buffer = $connection;
// unset($buffer['order_edge_supply']);
// foreach ($connection['order_edge_supply'] as $key => &$order_edge_supply) {
// // Перебор рёбер: ЗАКАЗ -> ПОСТАВКА
// // if ((empty($order_edge_supply['dlvr']['type']) && $target_dlvr_type === 'auto')
// // || (isset($order_edge_supply['dlvr']['type']) && $order_edge_supply['dlvr']['type'] !== $target_dlvr_type)
// // ) {
// if ((isset($order_edge_supply['dlvr']['type']) && $order_edge_supply['dlvr']['type'] === $target_dlvr_type)
// || (empty($order_edge_supply['dlvr']['type']) && $target_dlvr_type === 'auto')
// ) {
// // Тип доставки для одной поставки отличается
// // Запись в буфер
// $buffer['order_edge_supply'][$key] = $order_edge_supply;
// // Удаление из списка
// unset($connection['order_edge_supply'][$key]);
// // Перезапись статуса
// $changed = true;
// }
// }
// // Запись из буфера в глобальный буфер
// $buffer_global[] = $buffer;
// }
// }
// $connections = array_merge($connections, $buffer_global);
// if ($changed) goto recursive_connections_filtration;
// Инициализация реестра дубликатов
$registry = [];
$supplies = [];
// Подсчёт и перестройка массива для очистки от дубликатов
foreach ($connections as $key => &$connection) {
// Перебор поставок
// Перебор объектов для заказа
if (in_array([
'catn' => $connection['supply']['catn'],
'type' => $type = reset($connection['order_edge_supply'])['dlvr']['type'] ?? 'auto'
], $registry)) {
// Если данная поставка найдена в реестре
// Удаление
unset($connections[$key]);
// Пропуск итерации
continue;
}
// Повторный перебор для поиска дубликатов
foreach ($connections as &$connection_for_check) {
if ($connection == $connection_for_check) {
// Найден дубликат
// Запись в реестр
$registry[$key] = [
'catn' => $connection_for_check['supply']['catn'],
'type' => $type
];
}
}
// Инициализация счетчиков
$connection['amount'] = [
'auto' => 0,
'avia' => 0
];
// Подсчет количества поставок
foreach ($connection['order_edge_supply'] as $edge) {
// Перебор связанных поставок
if ($edge['dlvr']['type'] === 'auto') ++$connection['amount']['auto'];
if ($edge['dlvr']['type'] === 'avia') ++$connection['amount']['avia'];
}
}
// Инициализация связанной с заказом поставки
$supply = Supply::searchById($edge['_to']);
// Инициализация дополнительных данных
foreach ($connections as $key => &$connection) {
// Перебор поставок
// Инициализация поставщика в буфере
if (empty($supplies[$supply->prod][$supply->catn][$edge['dlvr']['type']])) $supplies[$supply->prod][$supply->catn][$edge['dlvr']['type']] = [
'supply' => $supply,
'account' => Account::searchBySupplyId($supply->readId()),
'product' => Product::searchBySupplyId($supply->readId()),
'currency' => 'руб',
'amount' => 1
];
else ++$supplies[$supply->prod][$supply->catn][$edge['dlvr']['type']]['amount'];
// Чтение стоимости
$cost = Supply::readCostById($connection['supply']['_id']);
// Инициализация буфера с обрабатываемой поставкой
$buffer = &$supplies[$supply->prod][$supply->catn][$edge['dlvr']['type']];
if (empty($cost) || $cost < 1) {
// Запись ребра
$buffer['edge'] = $edge;
if (empty($buffer['supply']->cost) || $buffer['supply']->cost < 1) {
// Если стоимость не найдена или равна нулю (явная ошибка)
// Удаление из базы данных
$this->deleteSupply($connection['supply']['_id']);
$this->deleteSupply($buffer['supply']);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Добавить уведомление об ошибочном товаре
// Инициализация стоимости товара для уведомления (чтобы там не было NULL)
$cost = $buffer['supply']->cost ?? 0;
// Отправка уведомлений покупателю
Notification::_write("Стоимость товара $supply->catn равна $cost", account: $account->_key, type: Notification::TYPE_ERROR);
Notification::_write("Товар $supply->catn удалён", account: $account->_key, type: Notification::TYPE_ERROR);
// Отправка уведомления поставщику
// Отправка уведомления модератору
// Удаление из списка
unset($connections[$key]);
// Пропуск итерации
continue;
// Выход из цикла
break;
}
// Поиск ребра до аккаунта
$connection['account'] = Account::searchBySupplyId($connection['supply']['_id']);
// Поиск привязанного товара
$connection['product'] = Product::searchBySupplyId($connection['supply']['_id']);
// if (empty(reset($connection['order_edge_supply'])['dlvr']['type']) || reset($connection['order_edge_supply'])['dlvr']['type'] === 'auto') {
// // Доставка автоматическая
try {
// Инициализация данных геолокации
try {
$from = (int) $connection['account']['opts']['delivery_from_terminal'] ?? empty(Settings::searchActive()->delivery_from_default) ? 36 : (int) Settings::searchActive()->delivery_from_default;
$from = (int) $buffer['account']['opts']['delivery_from_terminal'] ?? empty(Settings::searchActive()->delivery_from_default) ? 36 : (int) Settings::searchActive()->delivery_from_default;
} catch (Exception $e) {
$from = empty(Settings::searchActive()->delivery_from_default) ? 36 : (int) Settings::searchActive()->delivery_from_default;
}
@ -590,30 +515,26 @@ class Order extends Document implements DocumentInterface
$to = 36;
}
if ($buffer_connection = $connection['product']['bffr']["$from-$to"] ?? false) {
// Найдены данные доставки в буфере
if (time() < $buffer_connection['expires']) {
// Срок хранения не превышен, информация актуальна
if (($buffer_connection = $buffer['product']['bffr']["$from-$to-" . $edge['dlvr']['type']] ?? false) && time() < $buffer_connection['expires']) {
// Найдены данные доставки в буфере и их срок хранения не превышен, информация актуальна
// Запись в буфер вывода
$connection['delivery']['auto'] = $buffer_connection['data'];
}
$buffer['delivery'] = $buffer_connection['data'];
} else {
// Инициализация инстанции продукта в базе данных
$product = Product::searchByCatnAndProd($connection['product']['catn'], $connection['product']['prod']);
// Инициализация инстанции продукта в базе данных (реинициализация под ActiveRecord)
$product = Product::searchByCatnAndProd($buffer['product']['catn'], $buffer['product']['prod']);
// Инициализация доставки Dellin (автоматическая)
$product->bffr = ($product->bffr ?? []) + [
"$from-$to" => [
'data' => $connection['delivery']['auto'] = Dellin::calcDeliveryAdvanced(
"$from-$to-" . $edge['dlvr']['type'] => [
'data' => $buffer['delivery'] = Dellin::calcDeliveryAdvanced(
$from,
$to,
(int) ($connection['product']['wght'] ?? 0),
(int) ($connection['product']['dmns']['x'] ?? 0),
(int) ($connection['product']['dmns']['y'] ?? 0),
(int) ($connection['product']['dmns']['z'] ?? 0),
count($connection['order_edge_supply'])
(int) ($buffer['product']['wght'] ?? 0),
(int) ($buffer['product']['dmns']['x'] ?? 0),
(int) ($buffer['product']['dmns']['y'] ?? 0),
(int) ($buffer['product']['dmns']['z'] ?? 0),
avia: $edge['dlvr']['type'] === 'avia'
),
'expires' => time() + 86400
]
@ -622,90 +543,16 @@ class Order extends Document implements DocumentInterface
// Отправка в базу данных
$product->update();
}
} catch (Exception $e) {
$connection['delivery']['auto']['error'] = true;
// echo '<pre>';
// var_dump($e->getMessage());
// var_dump($e->getTrace());
// var_dump($e->getFile());
// die;
// var_dump(json_decode($e->getMessage(), true)['errors']); die;
}
// Запись цены (цена поставки + цена доставки + наша наценка)
$connection['cost']['auto'] = ($cost ?? $connection['supply']->onec['Цены']['Цена']['ЦенаЗаЕдиницу']) + ($connection['delivery']['auto']['price']['all'] ?? $connection['delivery']['auto']['price']['one'] ?? 0) + ($settings['increase'] ?? 0) ?? 0;
// } else {
// Доставка самолётом
try {
// Инициализация данных геолокации
try {
$from = (int) $connection['account']['opts']['delivery_from_terminal'] ?? empty(Settings::searchActive()->delivery_from_default) ? 36 : (int) Settings::searchActive()->delivery_from_default;
$buffer['cost'] = ($supply->cost ?? $supply->onec['Цены']['Цена']['ЦенаЗаЕдиницу'] ?? throw new exception('Не найдена цена товара')) + ($buffer['delivery']['price']['all'] ?? $buffer['delivery']['price']['one'] ?? 0) + ($settings['increase'] ?? 0) ?? 0;
} catch (Exception $e) {
$from = empty(Settings::searchActive()->delivery_from_default) ? 36 : (int) Settings::searchActive()->delivery_from_default;
$buffer['delivery'] = null;
}
}
}
try {
$to = (int) yii::$app->user->identity->opts['delivery_to_terminal'] ?? 36;
} catch (Exception $e) {
$to = 36;
}
if ($buffer_connection = $connection['product']['bffr']["$from-$to-avia"] ?? false) {
// Найдены данные доставки в буфере
if (time() < $buffer_connection['expires']) {
// Срок хранения не превышен, информация актуальна
// Запись в буфер вывода
$connection['delivery']['avia'] = $buffer_connection['data'];
}
} else {
// Инициализация инстанции продукта в базе данных
$product = Product::searchByCatnAndProd($connection['product']['catn'], $connection['product']['prod']);
// Инициализация доставки Dellin (автоматическая)
$product->bffr = ($product->bffr ?? []) + [
"$from-$to-avia" => [
'data' => $connection['delivery']['avia'] = Dellin::calcDeliveryAdvanced(
$from,
$to,
(int) ($connection['product']['wght'] ?? 0),
(int) ($connection['product']['dmns']['x'] ?? 0),
(int) ($connection['product']['dmns']['y'] ?? 0),
(int) ($connection['product']['dmns']['z'] ?? 0),
count($connection['order_edge_supply']),
avia: true
),
'expires' => time() + 86400
]
];
// Отправка в базу данных
$product->update();
}
} catch (Exception $e) {
$connection['delivery']['avia']['error'] = true;
// var_dump($e->getMessage());
// var_dump($e->getTrace());
// var_dump($e->getFile());
// die;
// var_dump(json_decode($e->getMessage(), true)['errors']); die;
}
// Запись цены (цена поставки + цена доставки + наша наценка)
$connection['cost']['avia'] = ($cost ?? $connection['supply']->onec['Цены']['Цена']['ЦенаЗаЕдиницу']) + ($connection['delivery']['avia']['price']['all'] ?? $connection['delivery']['avia']['price']['one'] ?? 0) + ($settings['increase'] ?? 0) ?? 0;
// }
// Запись валюты
$connection['currency'] = 'руб';
}
return $connections;
return $supplies;
}
/**

View File

@ -143,11 +143,22 @@ class OrderEdgeSupply extends Edge
return [];
}
public static function convertStatusToRussian(string|int $status): string
/**
* Генерация ярлыка на русском языке для статуса заказа поставки
*
* @param string $status Статус заказа поставки
*
* @return string Ярлык статуса на русском языке
*/
public static function statusToRussian(string $status = ''): string
{
return match ($status) {
'accepted', 1 => 'Ожидается отправка',
default => 'Запрошен'
'requested' => 'Запрошен',
'accepted' => 'Ожидается отправка',
'going' => 'Доставляется',
'completed' => 'Завершен',
default => 'Обрабатывается'
};
}
}

View File

@ -59,11 +59,6 @@ class Product extends Document
*/
public UploadedFile|string|array|null $file_image = null;
/**
* Группа в которой состоит товар
*/
public ProductGroup|null $group = null;
/**
* Имя коллекции
*/
@ -113,7 +108,6 @@ class Product extends Document
'stts' => 'Статус',
'file_excel' => 'Документ (file_excel)',
'file_image' => 'Изображение (file_image)',
'group' => 'Группа (group)',
'account' => 'Аккаунт'
]
);
@ -233,7 +227,7 @@ class Product extends Document
}
/**
* Запись пустого продукта
* Запись пустого товара
*/
public static function writeEmpty(string $catn, string $prod = 'Неизвестный', bool $active = false): ?self
{
@ -275,7 +269,6 @@ class Product extends Document
};
}
if (!file_exists(YII_PATH_PUBLIC . $catalog_h150 = '/img/products/' . $this->_key . '/h150')) {
// Директория для обложек изображений продукта не найдена
@ -462,138 +455,35 @@ class Product extends Document
)[0];
}
/**
* Найти все аналоги
*
* @param string $prod Производитель
* @param string $catn Артикул
* @param int $limit Ограничение по количеству
*
* @return array|null Найденные аналоги
*/
public static function searchAnalogs(string $prod, string $catn, int $limit = 30): ?array
{
// Инициализация буфера возврата
$return = [];
// Поиск ключей аналогов
$analogs = ProductEdgeProduct::searchConnections(self::searchByCatnAndProd($catn, $prod)?->_key, $limit);
foreach ($analogs as $analog) {
// Перебор найденных ключей (_key) аналогов
if ($analog = Product::searchById(self::collectionName() . "/$analog")) {
// Найден товар
if (isset($analog->stts) && $analog->stts === 'active') {
// Пройдена проверка по статусу
// Запись в буфер вывода
$return[] = $analog;
}
} else {
// Не найден товар
}
}
return $return;
}
/**
* Синхронизация аналогов
*
* Связывает с товаром и связанными с ним товарами в одну группу
*
* @param self $to Цель
*
* @return array Созданные рёбра
*/
public function synchronization(self $to): array
{
// Инициализация буфера сохранённых рёбер
$edges = [];
// Инициализация списка товаров в группе и буфера для связей "всех со всеми"
$products = $_products = array_unique(array_merge($this->connections(), $to->connections(), [Product::collectionName() . "/$to->_key"]));
foreach ($products as $product) {
// Перебор связей для создания связей во всех товарах
// Удаление обрабатываемого товара из буферного списка товаров
// unset($_products[array_search($product, $_products)]);
foreach ($_products as $_product) {
// Перебор товаров из буфера
// if ($from = self::searchById($product)) {
// // Товар найден
// } else {
// if ($from = Product::writeEmpty($product)) {
// // Удалось записать товар
// } else {
// // Не удалось записать товар
// continue;
// }
// }
if ($to = self::searchById($_product)) {
// Товар найден
} else {
if ($to = Product::writeEmpty($_product)) {
// Удалось записать товар
} else {
// Не удалось записать товар
continue;
}
}
if ($edge = $this->connect($to)) {
// Ребро создано
// Запись в буфер
$edges[] = $edge;
}
}
}
return $edges;
}
/**
* Подключение аналога
*
* @param self $to Цель
* @param Product $target Товар который надо подключить
*
* @return ProductEdgeProduct|null Ребро между товарами, если создалось
* @return ProductEdgeProductGroup|null Ребро между товаром и группой, если создалось
*/
public function connect(self $to): ?ProductEdgeProduct
public function connect(Product $product): ?ProductEdgeProductGroup
{
if (ProductEdgeProduct::searchByVertex(Product::collectionName() . "/$this->_key", Product::collectionName() . "/$to->_key", type: 'analogue')) {
// Найдено ребро
} else if (ProductEdgeProduct::searchByVertex(Product::collectionName() . "/$to->_key", Product::collectionName() . "/$this->_key", type: 'analogue')) {
// Найдено ребро (наоборот)
if (!$group = ProductGroup::searchByProduct($this)) {
// Не найдена группа товаров
// !!! Вероятно эта проверка здесь не нужна, так как мы знаем входные данные
} else {
// Не найдены ребра
// Запись новой группы
$group = ProductGroup::writeEmpty(active: true);
if ($edge = ProductEdgeProduct::write(Product::collectionName() . "/$this->_key", Product::collectionName() . "/$to->_key", data: ['type' => 'analogue'])) {
// Ребро сохранено
// Запись в журнал о соединении
$this->journal('connect analogue', [
'to' => Product::collectionName() . "/$to->_key"
]);
// Запись в журнал о соединении
$to->journal('connect analogue', [
'from' => Product::collectionName() . "/$this->_key"
]);
return $edge;
// Запись товара в группу
$group->writeProduct($this);
}
if ($_group = ProductGroup::searchByProduct($product)) {
// Найдена другая группа у товара который надо добавить в группу
// Перенос всех участников (включая целевой товар)
return $group->transfer($_group);
} else {
// Не найдена группа у товара который надо добавить в группу
// Запись целевого товара в группу
return $group->writeProduct($product);
}
return null;
@ -602,67 +492,21 @@ class Product extends Document
/**
* Отключение аналога
*
* @param self|null $to Цель (если null, то целью являются все подключенные аналоги)
* @param bool $all Удалить соединения со всеми членами группы
* @return bool Статус выполнения
*/
public function disconnect(self|null $to = null, bool $all = true): bool
public function disconnect(): bool
{
if (isset($to)) {
// Передана цель для удаления (из её группы)
if ($group = ProductGroup::searchByProduct($this)) {
// Найдена группа товаров
if ($edge = @ProductEdgeProduct::searchByVertex(Product::collectionName() . "/$this->_key", Product::collectionName() . "/$to->_key", type: 'analogue')[0]) {
// Найдено ребро
} else if ($edge = @ProductEdgeProduct::searchByVertex(Product::collectionName() . "/$to->_key", Product::collectionName() . "/$this->_key", type: 'analogue')[0]) {
// Найдено ребро (наоборот)
} else {
// Не найдены ребра
return false;
}
} else {
// Не передана цель для удаления (из её группы)
foreach ($this->connections() as $edge) {
// Перебор всех рёбер
if (Product::collectionName() . "/$this->_key" !== $edge && $to = Product::searchById($edge)) {
// Найден товар (проверен на то, что не является самим собой)
// Разъединение
$this->disconnect($to, all: false);
}
}
// Удаление из группы
$group->deleteProduct($this);
return true;
}
} else {
// Не найдена группа товаров
if ($edge->delete() > 0) {
// Удалось удалить ребро (связь)
if ($all) {
// Запрошено удаление соединений со всеми членами группы
foreach ($to->connections() as $edge) {
// Перебор рёбер (найденных соединений с группой в которой находилась цель)
if (Product::collectionName() . "/$this->_key" !== $edge && $to = Product::searchById($edge)) {
// Найден товар (проверен на то, что не является самим собой)
// Разъединение
$this->disconnect($to, all: false);
}
}
}
// Запись в журнал о разъединении
$this->journal('disconnect analogue', [
'to' => Product::collectionName() . "/$to->_key"
]);
// Запись в журнал о соединении
$to->journal('disconnect analogue', [
'from' => Product::collectionName() . "/$this->_key"
]);
// Заебись
return true;
}
@ -670,42 +514,16 @@ class Product extends Document
return false;
}
/**
* Найти все связанные товары
*
* @param int $limit Ограничение по максимальному значению
*/
public function connections(int $limit = 100): array
{
// Инициализация буфера связанных товаров
$products = [];
foreach (ProductEdgeProduct::searchByDirection(self::collectionName() . "/$this->_key", direction: 'ANY', type: 'analogue', limit: $limit) as $edge) {
// Перебор связей для создания списка товаров (вершин)
// Добавление товаров (вершин) в буфер (подразумевается, что без дубликатов)
if (!in_array($edge->_from, $products, true)) $products[] = $edge->_from;
if (!in_array($edge->_to, $products, true)) $products[] = $edge->_to;
}
return $products;
}
/**
* Проверка на уникальность
*
* @param static|null $account Аккаунт
*
* @return bool|static true если создать новую запись, static если найден дубликат
* @return bool|static Товар, если найден
*
* @todo
* 1. Обработка дубликатов
*/
public function validateForUniqueness(Account|int|null $account = null): bool|static
public function validateForUniqueness(): bool|static
{
// Инициализация аккаунта
$account = Account::initAccount($account);
if ($supplies = self::search(['catn' => $this->catn, 'prod' => $this->prod], limit: 100)) {
// Найдены поставки с таким же артикулом (catn) и производителем (prod)
@ -717,9 +535,9 @@ class Product extends Document
// Возврат (найден дубликат в базе данных)
return $supply;
} else return true;
}
// Возврат (подразумевается ошибка)
// Возврат (подразумевается отсутствие дубликатов в базе данных)
return false;
}
@ -796,4 +614,30 @@ class Product extends Document
return false;
}
/**
* Найти товары по группе
*
* @param string|null $_id Идентификатор группы
*
* @return array|null Товары (Product)
*/
public static function searchByProductGroup(string $_id): ?array
{
return static::searchByEdge(
from: 'product_group',
to: 'product',
edge: 'product_edge_product_group',
direction: 'INBOUND',
subquery_where: [
[
'product_edge_product_group._from == "' . $_id . '"'
]
],
subquery_select: 'product',
where: 'product_edge_product_group[0]._id != null',
limit: 1,
select: 'product_edge_product_group[0]'
)[0];
}
}

View File

@ -10,4 +10,17 @@ class ProductEdgeProductGroup extends Edge
{
return 'product_edge_product_group';
}
/**
* Поиск по товару
*
* @param Product $product Товар
* @param int $amount Ограничение по максимальному количеству
*
* @return null|self Ребро, если найдено
*/
public static function searchByProduct(Product $product, int $limit = 1): ?self
{
return self::find()->where(['_from' => $product->readId()])->limit($limit)->all()[0] ?? null;
}
}

View File

@ -4,15 +4,19 @@ declare(strict_types=1);
namespace app\models;
use app\models\traits\SearchByEdge;
use carono\exchange1c\interfaces\GroupInterface;
use Zenwalker\CommerceML\Model\Group;
/**
* Группировка продуктов
* Группировка продуктов для соединения их в аналоги
*/
class ProductGroup extends Document implements GroupInterface
{
use SearchByEdge;
/**
* Имя коллекции
*/
@ -29,7 +33,7 @@ class ProductGroup extends Document implements GroupInterface
return array_merge(
parent::attributes(),
[
'name'
'stts'
]
);
}
@ -42,7 +46,7 @@ class ProductGroup extends Document implements GroupInterface
return array_merge(
parent::attributeLabels(),
[
'name' => 'Название (name)'
'stts' => 'Статус'
]
);
}
@ -55,23 +59,138 @@ class ProductGroup extends Document implements GroupInterface
return array_merge(
parent::rules(),
[
// [
// 'name',
// 'required',
// 'message' => 'Заполните поле: {attribute}'
// ]
[
'stts',
'string',
'length' => [4, 20],
'message' => '{attribute} должен быть строкой от 4 до 20 символов'
]
]
);
}
/**
* Запись пустой группы
*
* @param bool $active Статус активации
*
* @return self Группа товаров, если создана
*/
public static function writeEmpty(bool $active = false): ?self
{
// Инициализация
$model = new self;
// Настройки
$model->stts = $active ? 'active' : 'inactive';
// Запись
return $model->save() ? $model : null;
}
/**
* Запись члена группы
*
* @deprecated
*/
public function writeMember(Product $member): ProductEdgeProductGroup
{
return ProductEdgeProductGroup::write($member->readId(), $this->readId(), 'member');
}
/**
* Запись товара в группу
*
* @param Product $product Товар
*/
public function writeProduct(Product $product): ?ProductEdgeProductGroup
{
// Запись товара в группу
$edge = ProductEdgeProductGroup::write($product->readId(), $this->readId(), data: ['type' => 'member']);
// Запись в журнал
$this->journal('write member', [
'product' => $product->readId()
]);
return $edge;
}
/**
* Удаление товара из группы
*
* @param Product $product Товар
*
* @return void
*/
public function deleteProduct(Product $product): void
{
// Удаление товара из группы (подразумевается, что будет только одно)
foreach (ProductEdgeProductGroup::searchByVertex($product->readId(), $this->readId(), filter: ['type' => 'member']) as $edge) $edge->delete;
// Запись в журнал
$this->journal('delete member', [
'product' => $product->readId()
]);
}
/**
* Найти рёбра до товаров
*
* @param int $limit Ограничение по максимальному количеству
*/
public function searchEdges(int $limit = 999): ?array
{
return ProductEdgeProductGroup::searchByDirection($this->readId(), 'INBOUND', where: ['type' => 'member'], limit: $limit);
}
/**
* Прочитать связанные товары
*
* @param int $limit Ограничение по максимальному количеству
*/
public function searchProducts(int $limit = 999): ?array
{
// Инициализация буфера товаров
$products = [];
foreach ($this->searchEdges($limit) as $edge) {
// Перебор рёбер
$products[] = Product::searchById($edge->_from);
}
return $products;
}
/**
* Перенос членов группы из другой
*
* @param ProductGroup $group Группа из которой нужен перенос
*
* @return null|int Количество перенесённых товаров, если произведён перенос
*/
public function transfer(ProductGroup $group): ?int
{
// Проверка на то, что запрошен перенос "из себя в себя"
if ($this->readId() === $group->readId()) return null;
// Инициализация счётчика записанных товаров
$transfered = 0;
// Перенос
foreach ($group->searchProducts() as $product) if ($this->writeProduct($product)) ++$transfered;
// Деактивация целевой группы (пустой)
$group->deactivate();
// Запись в журнал
$this->journal('transfer', [
'from' => $group->readId()
]);
return $transfered;
}
/**
* Запись рёбер групп
*
@ -171,4 +290,29 @@ class ProductGroup extends Document implements GroupInterface
{
return static::findOne(['onec_id' => $onec_id]);
}
/**
* Найти по идентификатору товара
*
* @param Product $product Товар
*
* @return self|null Группа (ProductGroup)
*/
public static function searchByProduct(Product $product): ?self
{
return static::searchByEdge(
from: 'product',
to: 'product_group',
edge: 'product_edge_product_group',
direction: 'INBOUND',
subquery_where: [
[
'product_edge_product_group._from == "' . $product->readId() . '"'
]
],
subquery_select: 'product_group',
where: 'product_edge_product_group[0]._id != null',
limit: 1
)[0] ?? null;
}
}

View File

@ -392,12 +392,12 @@ class Search extends Document
*
* @param array $row Товар сгенерированный через Search::content()
* @param string|null $cover Обложка
* @param array $empties Реестр не найденных товаров
* @param array $list Реестр найденных товаров
* @param bool $analogs Запрошены аналоги (не выведет пустые товары)
*
* @return string HTML-элемент с товаром
*/
public static function generate(array &$row, string|null &$cover = null, array &$empties = [], bool $analogs = false): string
public static function generate(array &$row, string|null &$cover = null, array &$list = [], bool $analogs = false): string
{
foreach ($row['imgs'] ?? [] as &$img) {
// Перебор изображений для обложки
@ -437,6 +437,9 @@ class Search extends Document
foreach (empty($row['supplies']) || $supplies_amount === 0 ? [null] : $row['supplies'] as &$supply) {
// Перебор поставок
// Запись в список найденных
$list[$row['prod']] = [$row['catn']] + (isset($list[$row['prod']]) ? $list[$row['prod']] : []);
// Инициализация модификатора класса
if ($supplies_amount > $supply_iterator) {
// Это не последняя строка с товаром и его поставками
@ -491,9 +494,6 @@ class Search extends Document
</div>
HTML;
// Запись в список ненайденных
$empties[$row['prod']] = [$row['catn']] + (isset($empties[$row['prod']]) ? $empties[$row['prod']] : []);
// Запись блокировщика
$empty_block = true;

View File

@ -92,15 +92,7 @@ class Supply extends Product implements ProductInterface, OfferInterface
{
return array_merge(
parent::rules(),
[
// [
// [
// 'oemn'
// ],
// 'arrayValidator',
// 'message' => '{attribute} должен быть массивом.'
// ]
]
[]
);
}
@ -341,7 +333,7 @@ class Supply extends Product implements ProductInterface, OfferInterface
// Пользователь аутентифицирован и авторизован
// Инициализация п̸̨͇͑͋͠р̷̬̂́̀̊о̸̜̯̹̅͒͘͝д̴̨̨̨̟̈́̆у̴̨̭̮̠́͋̈́к̴̭͊̋̎т̵̛̣͈̔̐͆а̵̨͖͑
$product = self::initEmpty($this->catn);
$product = self::initEmpty($this->catn, $this->prod);
if (!is_array($product)) {
// Создался только один товар и вернулся в виде модели
@ -356,7 +348,7 @@ class Supply extends Product implements ProductInterface, OfferInterface
// Перебор артикулов из массива ОЕМ-номеров
// Инициализация и запись
$product[] = self::initEmpty($oem);
$product[] = self::initEmpty($oem, $this->prod);
}
}
@ -456,23 +448,51 @@ class Supply extends Product implements ProductInterface, OfferInterface
foreach ($data as $doc) {
// Перебор полученных документов
// Проверка на пустую страницу или документ (пустой массив)
if (empty($doc)) continue;
foreach ($doc as $row) {
// Перебор строк
if (!is_array($row)) {
// Строка является не массивом со значением колонок, а самим значением колонки (значит в $data и так хранился $doc - у файла всего одна страница)
// Универсализация
$row = $doc;
}
// Поиск артикула
$article = $row['Артикул'] ?? $row['артикул'] ?? $row['Article'] ?? $row['article'] ?? $row['catn'];
$article = (string) ($row['Артикул'] ?? $row['артикул'] ?? $row['Article'] ?? $row['article'] ?? $row['catn'] ?? null);
// Поиск количества товаров
$amount = $row['Количество'] ?? $row['количество'] ?? $row['Amount'] ?? $row['amount'] ?? $row['amnt'] ?? 1;
$amount = $row['Количество'] ?? $row['количество'] ?? $row['Amount'] ?? $row['amount'] ?? $row['amnt'] ?? null;
// Поиск производителя
$prod = (string) ($row['Производитель'] ?? $row['производитель'] ?? $row['Production'] ?? $row['production'] ?? $row['prod'] ?? null);
// Поиск аналогов
$analogs = explode(',', (string) ($row['Аналоги'] ?? $row['аналоги'] ?? $row['Analogs'] ?? $row['analogs'] ?? $row['ОЕМ'] ?? $row['eom'] ?? ''), 50);
// Поиск производителя
$prod = $row['Производитель'] ?? $row['производитель'] ?? $row['Production'] ?? $row['production'] ?? $row['prod'] ?? 'Неизвестный';
// Инициализация буфера аналогов
$buffer = [];
// Дополнительная фильтрация аналогов
foreach ($analogs as $analog) foreach (explode('/', $analog, 50) as $value) $buffer[] = $value;
// Запись аналогов из буфера
$analogs = $buffer;
// Пропуск пустых строк (подразумевается)
if ($article === null || $amount === null || $prod === null) continue;
// Инициализация товара для инициализации группы
if (!$product = Product::searchByCatnAndProd($article, $prod)) $product = Product::writeEmpty($article, $prod, Account::isMinimalAuthorized($account));
// Инициализация группы товаров
if (!$group = ProductGroup::searchByProduct($product)) $group = ProductGroup::writeEmpty(active: true);
// Инициализация функции создания поставки
$create = function (string $_supply) use ($article, $row, $prod, $analogs, &$created, &$updated, &$imported, $amount, $account): bool {
$create = function (string $_supply, int|null $amount = null) use ($group, $row, $prod, $analogs, &$created, &$updated, &$imported, $account): bool {
// Очистка
$_supply = trim($_supply);
@ -484,7 +504,7 @@ class Supply extends Product implements ProductInterface, OfferInterface
// Запись артикула (каталожного номера) в буфер
$_row['catn'] = $_supply;
$_row['cost'] = (float) preg_replace('/[^\d\.]+/', '', preg_replace('/\,+/', ' ', $row['Стоимость'] ?? $row['стоимость'] ?? $row['Цена'] ?? $row['цена'] ?? $row['Cost'] ?? $row['cost'] ?? $row['Price'] ?? $row['price'])) ?? 0;
$_row['cost'] = (float) preg_replace('/[^\d\.]+/', '', preg_replace('/\,+/', ' ', (string) ($row['Стоимость'] ?? $row['стоимость'] ?? $row['Цена'] ?? $row['цена'] ?? $row['Cost'] ?? $row['cost'] ?? $row['Price'] ?? $row['price']))) ?? 0;
$_row['prod'] = $prod;
$_row['oemn'] = array_walk($analogs, 'trim');
@ -496,14 +516,29 @@ class Supply extends Product implements ProductInterface, OfferInterface
if ($supply->validate()) {
// Проверка пройдена
if (($_supply = $supply->validateForUniqueness($account)) instanceof static) {
if ((($_supply = $supply->validateForUniqueness()) instanceof Supply)) {
// Найдена поставка с такими параметрами (артикул и производитель)
if ($_supply->cost !== $_row['cost']) {
$supply = $_supply;
} else {
// Не найдена поставка с такими параметрами
if ($supply->save()) {
// Поставка записана в базу данных
// Обновление счётчика
$created++;
// Запись поставки в буфер
$imported[] = $supply;
};
}
if ($supply->cost !== $_row['cost']) {
// Стоимость изменилась
if ($product = Product::searchByCatnAndProd($supply->catn, $supply->prod)) {
// Найден товар подходящий для привязки с только что созданной поставкой (подразумевается что уже был привязан в коде выше)
// Найден товар подходящий для привязки
// Приведение типа (для анализатора)
if (is_array($product)) $product = $product[0];
@ -527,31 +562,42 @@ class Supply extends Product implements ProductInterface, OfferInterface
}
// Завершение выполнения при ошибке
if ($error) return !$error;
if ($error) return false;
if ($product = Product::searchByCatnAndProd($_supply->catn, $_supply->prod)) {
if ($product = Product::searchByCatnAndProd($supply->catn, $supply->prod)) {
// Найден товар подходящий для привязки с этой поставкой
for ($i = 0; $i++ < $amount;) {
// Перебор создаваемых рёбер (так работает обозначение количества товаров в наличии)
// Поиск ребёр
$edges = SupplyEdgeProduct::searchByVertex($_supply->readId(), $product->readId(), limit: 999);
$edges = SupplyEdgeProduct::searchByVertex($supply->readId(), $product->readId(), limit: 999);
if (isset($amount)) {
// Передано количество
if (count($edges) === 0) {
// Ребёр нет, но должны быть (если количество загружаемых поставок более нуля)
// Инициализация количества рёбер которые не удалось записать
$failed = 0;
for ($i = 0; $i++ < $amount;) {
// Перебор создаваемых рёбер (так работает обозначение количества товаров в наличии)
// Запись ребра
SupplyEdgeProduct::write($_supply->readId(), $product->readId(), data: ['type' => 'connect']);
if (SupplyEdgeProduct::write($supply->readId(), $product->readId(), data: ['type' => 'connect'])) {
// Записано ребро
} else {
// Не записано ребро
// Обновление количества рёбер которые не удалось записать
++$failed;
}
}
// Отправка уведомления
if ($failed > 0) Notification::_write("Не удалось записать $failed рёбер у поставки $supply->catn");
} else if ($amount === count($edges)) {
// Количество товаров в поставке не изменилось
// Раз изменений нет, то обновлять ничего не нужно
continue;
// continue;
} else if ($amount < count($edges)) {
// Количество товаров в поставке стало МЕНЬШЕ
@ -575,7 +621,7 @@ class Supply extends Product implements ProductInterface, OfferInterface
}
// Отправка уведомления
Notification::_write("Не удалось удалить $failed рёбер у поставки $_supply->catn");
Notification::_write("Не удалось удалить $failed рёбер у поставки $supply->catn");
} else if ($amount > count($edges)) {
// Количество товаров в поставке стало БОЛЬШЕ
@ -588,7 +634,7 @@ class Supply extends Product implements ProductInterface, OfferInterface
for ($i = 0; $i < $write; $i++) {
// Перебор рёбер на запись (синхронизация)
if (SupplyEdgeProduct::write($_supply->readId(), $product->readId(), data: ['type' => 'connect'])) {
if (SupplyEdgeProduct::write($supply->readId(), $product->readId(), data: ['type' => 'connect'])) {
// Записано ребро
} else {
// Не записано ребро
@ -599,7 +645,7 @@ class Supply extends Product implements ProductInterface, OfferInterface
}
// Отправка уведомления
Notification::_write("Не удалось записать $failed рёбер у поставки $_supply->catn");
if ($failed > 0) Notification::_write("Не удалось записать $failed рёбер у поставки $supply->catn");
}
}
}
@ -612,32 +658,16 @@ class Supply extends Product implements ProductInterface, OfferInterface
unset($vars['_key']);
// Перенос данных в буфер (существующий в базе данных дубликат)
$_supply->setAttributes($vars, false);
$supply->setAttributes($vars, false);
// Перезапись существующего документа
$_supply->update();
$supply->update();
// Обновление счётчика
$updated++;
// Запись поставки в буфер
$imported[] = $_supply;
// Запись в буфер (для универсальной обработки)
$supply = $_supply;
} else {
// Не найден документ с такими параметрами
if ($supply->save()) {
// Поставка записана в базу данных
// Обновление счётчика
$created++;
// Запись поставки в буфер
$imported[] = $supply;
};
}
} else {
// Проверка не пройдена
@ -654,26 +684,19 @@ class Supply extends Product implements ProductInterface, OfferInterface
// Активация товара
$product->activate();
// Инициализация списка артикулов группы для добавления аналогов
$group = [$article] + $analogs;
foreach ($group as $catn) {
// Перебор артикулов для добавления аналогов
foreach (Product::searchByCatn((string) $catn) as $target) {
// Перебор товаров для добавления аналогов
// foreach (Product::searchByCatn($product->catn) as $target) {
// // Перебор товаров по артикулу (все производители)
// }
// Добавление в группу аналогов
if ($to = Product::searchByCatn((string) $target['catn'], 1)) $product->synchronization($to);
}
}
$group->writeProduct($product);
}
return !$error;
};
// Запись поставки
$create((string) $article);
$create($article, (int) $amount);
foreach ($analogs as $_supply) {
// Перебор аналогов (если найдены)
@ -710,7 +733,7 @@ class Supply extends Product implements ProductInterface, OfferInterface
foreach ($imported as $supply) {
// Перебор импортированных поставок
if (ImportEdgeSupply::write($import->collectionName() . "/$import->_key", $supply->collectionName() . "/$supply->_key", data: ['type' => 'imported'])) {
if (ImportEdgeSupply::write($import->readId(), $supply->readId(), data: ['type' => 'imported', 'vrsn' => ImportEdgeSupply::searchMaxVersion($supply) + 1])) {
// Записано ребро: ИНСТАНЦИЯ ПОСТАВОК -> ПОСТАВКА
// Запись в журнал инстанции импорта

View File

@ -32,6 +32,7 @@ trait SearchByEdge
array $params = [],
bool $asArray = true,
bool $debug = false,
bool $aql = false,
bool $count = false
): mixed {
$subquery = static::find()
@ -71,11 +72,20 @@ trait SearchByEdge
->limit($limit)
->select($select ?? $to);
// Режим вывода строки запроса
if ($aql) {
// Запрошена проверка
return (string) $request->createCommand();
}
// Режим проверки
if ($debug) {
// Запрошена проверка
return (string) $request->createCommand();
var_dump((string) $request->createCommand());
return null;
}
// Запрос

View File

@ -6,7 +6,7 @@ use yii;
use yii\bootstrap\ActiveForm;
use app\models\connection\Dellin;
use app\models\Supply;
use DateTime;
?>
@ -21,10 +21,14 @@ use DateTime;
<div class="pl-3 mr-1">
<input id="checkbox_cart_all" type="checkbox" onchange="return cart_list_checkbox(this);" />
</div>
<div class="col-2">
<span>Производитель</span>
</div>
<div class="col-2">
<span>Артикул</span>
</div>
<div class="col-4">
<div class="col-2">
<span>Поставщик</span>
</div>
<div class="col-1 ml-auto px-0 text-center">
<span>Количество</span>
@ -37,31 +41,30 @@ use DateTime;
</div>
</div>
<?php
if (isset($connections) && !empty($connections)) {
foreach ($connections as $connection) {
if (!empty($data['supplies'])) {
// Найдены цели для заказа
// Инициализация списка поставок
$targets = [];
foreach ($data['supplies'] as $prod => $list) {
// Перебор поставщиков
foreach ($list as $catn => $deliveries) {
// Перебор поставок
// Инициализация переменных
extract($connection);
// Инициализация цены
$price_raw = $cost;
foreach ($deliveries as $delivery => $supply) {
// Перебор типов доставки
// Инициализация комментария
$comment = $order_edge_supply[0]['comm'] ?? 'Комментарий к заказу';
if ($amount['auto'] > 0) {
// Найдены поставки с автоматической доставкой
// Инициализация цены
$price_auto = $price_raw['auto'] . ' ' . $currency;
$comment = $supply['edge']['comm'] ?? 'Комментарий к заказу';
// Инициализация доставки
if (!isset($delivery) || (isset($delivery['auto'], $delivery['auto']['error']) || $delivery === '?')) {
if (empty($supply['delivery'])) {
// Не удалось рассчитать доставку
// Инициализация времени
$delivery_auto = '?';
$days = '?';
} else {
// Удалось рассчитать доставку
@ -69,11 +72,11 @@ use DateTime;
try {
// Взять данные из "arrivalToOspSender" (Дата прибытия на терминал-отправитель)
$delivery_auto_send_date = DateTime::createFromFormat('Y-m-d', $delivery['auto']['orderDates']['arrivalToOspSender'])->getTimestamp();
$delivery_send_date = DateTime::createFromFormat('Y-m-d', $supply['delivery']['orderDates']['arrivalToOspSender'])->getTimestamp();
} catch (Throwable $e) {
// Взять данные из "pickup" (Дата передачи груза на адресе отправителя)
$delivery_auto_send_date = DateTime::createFromFormat('Y-m-d', $delivery['auto']['orderDates']['pickup'])->getTimestamp();
$delivery_send_date = DateTime::createFromFormat('Y-m-d', $supply['delivery']['orderDates']['pickup'])->getTimestamp();
}
// Инициализация времени доставки
@ -82,118 +85,50 @@ use DateTime;
// Оставлено на всякий случай для дальнейших разбирательств
$delivery_auto_converted = DateTime::createFromFormat('Y-m-d H:i:s', $delivery['auto']['orderDates']['giveoutFromOspReceiver'])->getTimestamp();
$delivery_converted = DateTime::createFromFormat('Y-m-d H:i:s', $supply['delivery']['orderDates']['giveoutFromOspReceiver'])->getTimestamp();
} catch (Throwable $e) {
// Автоматическая доставка (подразумевается), данные из "arrivalToOspReceiver" (Дата прибытия натерминал-получатель)
$delivery_auto_converted = DateTime::createFromFormat('Y-m-d', $delivery['auto']['orderDates']['arrivalToOspReceiver'])->getTimestamp();
}
$delivery_auto = ceil(($delivery_auto_converted - ($delivery_auto_send_date ?? 0)) / 60 / 60 / 24) + 1;
}
// Генерация HTML
echo <<<HTML
<div class="row py-2 cart_list_target">
<div class="col">
<div class="row">
<div class="pl-3 my-auto mr-1">
<input id="cart_list_checkbox_{$supply['catn']}_auto" type="checkbox" onchange="return cart_list_checkbox(this);"/>
</div>
<div class="col-2 my-auto">
{$supply['catn']}
</div>
<div class="col-4 my-auto">
</div>
<div class="col-1 my-auto ml-auto">
<input id="cart_list_amnt_{$supply['catn']}_auto" class="form-control text-center" type="text" value="{$amount['auto']}" onchange="return cart_list_amount_update('{$supply['catn']}', 'auto', this)" aria-invalid="false">
</div>
<div class="col-2 my-auto text-right">
<p title="Ориентировочно"><i class="mr-1 fas fa-truck"></i> <b>~</b>$delivery_auto дн</p>
</div>
<div class="col-2 my-auto mr-3 text-right">
$price_auto
</div>
</div>
<div class="dropdown-divider"></div>
<div class="row mb-1">
<div class="col-12">
<p id="cart_list_comment_{$supply['catn']}_auto" class="mt-0 ml-0 text-break pointer-event" role="button" onclick="return cart_list_comment_edit('{$supply['catn']}', 'auto', this);">$comment</p>
</div>
</div>
</div>
</div>
HTML;
}
if ($amount['avia'] > 0) {
// Найдены поставки с доставкой по воздуху
// Инициализация цены
$price_avia = $price_raw['avia'] . ' ' . $currency;
// Инициализация доставки
if (!isset($delivery) || (isset($delivery, $delivery['auto'], $delivery['avia']['error']) || $delivery === '?')) {
// Не удалось рассчитать доставку
// Инициализация времени
$delivery_avia = '?';
} else {
// Удалось рассчитать доставку
// Инициализация даты отправки
try {
// Взять данные из "arrivalToOspSender" (Дата прибытия на терминал-отправитель)
$delivery_avia_send_date = DateTime::createFromFormat('Y-m-d', $delivery['avia']['orderDates']['arrivalToOspSender'])->getTimestamp();
} catch (Throwable $e) {
// Взять данные из "pickup" (Дата передачи груза на адресе отправителя)
$delivery_avia_send_date = DateTime::createFromFormat('Y-m-d', $delivery['avia']['orderDates']['pickup'])->getTimestamp();
}
// Инициализация времени доставки
try {
// Доставка по воздуху (подразумевается), данные из "giveoutFromOspReceiver" (Дата и время, с которого груз готов к выдаче на терминале)
$delivery_avia_converted = DateTime::createFromFormat('Y-m-d H:i:s', $delivery['avia']['orderDates']['giveoutFromOspReceiver'])->getTimestamp();
} catch (Throwable $e) {
// Автоматическая доставка (подразумевается), данные из "arrivalToOspReceiver" (Дата прибытия натерминал-получатель)
// Оставлено на всякий случай для дальнейших разбирательств
$delivery_avia_converted = DateTime::createFromFormat('Y-m-d', $delivery['avia']['orderDates']['arrivalToOspReceiver'])->getTimestamp();
$delivery_converted = DateTime::createFromFormat('Y-m-d', $supply['delivery']['orderDates']['arrivalToOspReceiver'])->getTimestamp();
}
$delivery_avia = ceil(($delivery_avia_converted - ($delivery_avia_send_date ?? 0)) / 60 / 60 / 24) + 1;
$days = ceil(($delivery_converted - ($delivery_send_date ?? 0)) / 60 / 60 / 24) + 1;
}
// Инициализация иконки
$icon = $delivery === 'avia' ? 'fa-plane' : 'fa-truck';
// Генерация HTML
echo <<<HTML
<div class="row py-2 cart_list_target">
<div class="col">
<div class="row">
<div class="pl-3 my-auto mr-1">
<input id="cart_list_checkbox_{$supply['catn']}_avia" type="checkbox" onchange="return cart_list_checkbox(this);"/>
<input id="cart_list_checkbox_{$prod}_{$catn}_auto" type="checkbox" onchange="return cart_list_checkbox(this);"/>
</div>
<div class="col-2 my-auto">
{$supply['catn']}
$prod
</div>
<div class="col-4 my-auto">
{$supply['dscr']}
<div class="col-2 my-auto">
$catn
</div>
<div class="col-2 my-auto">
{$supply['account']['indx']}
</div>
<div class="col-1 my-auto ml-auto">
<input id="cart_list_amnt_{$supply['catn']}_avia" class="form-control text-center" type="text" value="{$amount['avia']}" onchange="return cart_list_amount_update('{$supply['catn']}', 'avia', this)" aria-invalid="false">
<input id="cart_list_amnt_{$prod}_{$catn}_auto" class="form-control text-center" type="text" value="{$supply['amount']}" onchange="return cart_list_amount_update('$prod', '$catn', 'auto', this)" aria-invalid="false">
</div>
<div class="col-2 my-auto text-right">
<p title="Ориентировочно"><i class="mr-1 fas fa-plane"></i> <b>~</b>$delivery_avia дн</p>
<p title="Ориентировочно"><i class="mr-1 fas $icon"></i> <b>~</b>$days дн</p>
</div>
<div class="col-2 my-auto mr-3 text-right">
$price_avia
{$supply['cost']} {$supply['currency']}
</div>
</div>
<div class="dropdown-divider"></div>
<div class="row mb-1">
<div class="col-12">
<p id="cart_list_comment_{$supply['catn']}_avia" class="mt-0 ml-0 text-break pointer-event" role="button" onclick="return cart_list_comment_edit('{$supply['catn']}', 'avia', this);">$comment</p>
<p id="cart_list_comment_{$prod}_{$catn}_auto" class="mt-0 ml-0 text-break pointer-event" role="button" onclick="return cart_list_comment_edit('$prod', '$catn', 'auto', this);">$comment</p>
</div>
</div>
</div>
@ -201,6 +136,7 @@ use DateTime;
HTML;
}
}
}
} else {
echo <<<HTML
<div class="row py-2">

View File

@ -8,7 +8,7 @@ use app\models\Settings;
<head>
<meta charset="UTF-8">
<title>Счёт <?= $order['id'] ?></title>
<title>Счёт <?= $data['order']->_key ?></title>
</head>
<body>
@ -89,7 +89,7 @@ use app\models\Settings;
preg_match_all('/UTC([\+\-0-9:]*)/', $account->zone ?? Settings::searchActive()['timezone_default'] ?? 'UTC+3', $timezone);
$timezone = $timezone[1][0];
?>
<b>Счет на оплату <?= $order['id'] ?> от <?= (new DateTime())->setTimestamp($order['date'])->setTimezone(new DateTimeZone($timezone))->format('d.m.Y') ?></b>
<b>Счет на оплату <?= $data['order']->_key ?> от <?= (new DateTime())->setTimestamp($date)->setTimezone(new DateTimeZone($timezone))->format('d.m.Y') ?></b>
</td>
</tr>
@ -133,7 +133,9 @@ use app\models\Settings;
<tr>
<td style="text-align: center; border: solid; border-top: thick; border-left: thick;" colspan="1" rowspan="2" valign="center"><b></b></td>
<td style="text-align: center; border: solid; border-top: thick;" colspan="6" rowspan="2" valign="center"><b>Товары (работы, услуги)</b></td>
<td style="text-align: center; border: solid; border-top: thick;" colspan="3" rowspan="2" valign="center"><b>Производитель</b></td>
<td style="text-align: center; border: solid; border-top: thick;" colspan="3" rowspan="2" valign="center"><b>Товар</b></td>
<td style="text-align: center; border: solid; border-top: thick;" colspan="2" rowspan="2" valign="center"><b>Поставщик</b></td>
<td style="text-align: center; border: solid; border-top: thick;" colspan="2" rowspan="2" valign="center"><b>Количество</b></td>
<td style="text-align: center; border: solid; border-top: thick;" colspan="2" rowspan="2" valign="center"><b>Цена</b></td>
<td style="text-align: center; border: solid; border-top: thick; border-right: thick;" colspan="2" rowspan="2" valign="center"><b>Сумма</b></td>
@ -151,17 +153,22 @@ use app\models\Settings;
$cost = 0;
?>
<?php foreach ($order['entries'] as $entry) : ?>
<?php foreach ($data['supplies'] as $prod => $supplies) : ?>
<?php foreach ($supplies as $catn => $deliveries) : ?>
<?php foreach ($deliveries as $delivery => $supply) : ?>
<tr>
<td style="text-align: center; border: solid; border-left: thick;" colspan="1" valign="center"><?= $row++ ?></td>
<td style="text-align: left; border: solid;" colspan="6" valign="center"><?= $entry['title'] ?></td>
<td style="text-align: center; border: solid;" colspan="2" valign="center"><?= $entry['amount']['value'] ?></td>
<td style="text-align: center; border: solid;" valign="center"><?= $entry['cost']['value'] ?></td>
<td style="text-align: center; border: solid;" valign="center"><?= $entry['cost']['unit'] ?></td>
<td style="text-align: center; border: solid; border-right: thick;" colspan="2" valign="center"><?= $cost += $entry['cost']['value'] * $entry['amount']['value'] ?></td>
<td style="text-align: left; border: solid;" colspan="3" valign="center"><?= $prod ?></td>
<td style="text-align: left; border: solid;" colspan="3" valign="center"><?= $catn ?></td>
<td style="text-align: left; border: solid;" colspan="2" valign="center"><?= $supply['account']['indx'] ?></td>
<td style="text-align: center; border: solid;" colspan="2" valign="center"><?= $supply['amount'] ?></td>
<td style="text-align: center; border: solid;" valign="center"><?= $supply['cost'] * $supply['amount'] ?></td>
<td style="text-align: center; border: solid;" valign="center"><?= $supply['currency'] ?></td>
<td style="text-align: center; border: solid; border-right: thick;" colspan="2" valign="center"><?= $cost += $supply['cost'] * $supply['amount'] ?></td>
</tr>
<?php endforeach ?>
<?php endforeach ?>
<?php endforeach ?>
<tr>
<td style="border-top: thick;" colspan="13"></td>

View File

@ -1,3 +1,3 @@
<div class="p-3 h-100">
<p class="text-center my-auto">Новый заказ: <a href="/orders#<?= $id ?>">#<?= $id ?></a></p>
<p class="text-center my-auto">Заказ запрошен: <a href="/orders#<?= $id ?>">#<?= $id ?></a></p>
</div>

View File

@ -47,16 +47,15 @@ if (empty($window)) {
<label class="btn btn-sm button_white mb-0 mr-2" for="orders_panel_moderation_handled">Обрабатываются</label>
<label class="btn btn-sm button_white mb-0" for="orders_panel_moderation_completed">Завершены</label>
</div>
<?php if (!empty($moderator_orders)) : ?>
<?php foreach ($moderator_orders as $order) : ?>
<div id="<?= $order['order']['_key'] ?>_panel" class="page_order_panel mb-3 py-3 px-4 rounded">
<?php if (!empty($moderator_data)) : ?>
<?php foreach ($moderator_data as $moderator_data) : ?>
<div id="<?= $moderator_data['order']->_key ?>_panel" class="page_order_panel mb-3 py-3 px-4 rounded">
<h5 class="row mt-1 mb-3">
<?php
// Инициализация времени отправки заказа
$date = null;
foreach ($order['order']['jrnl'] as $entry) {
foreach ($moderator_data['order']->jrnl as $entry) {
// Перебор записей в журнале
if ($entry['action'] === 'requested') {
@ -81,7 +80,7 @@ if (empty($window)) {
];
?>
<p class="col-auto ml-1 font-weight-bold">#<?= $order['order']['_key'] ?></p>
<p class="col-auto ml-1 font-weight-bold">#<?= $moderator_data['order']->_key ?></p>
<p class="col-auto mr-1 font-weight-bold">
<span class="mr-2"><?= $date['H:i'] ?? 'Неизвестно' ?></span>
<span><?= $date['m.d.Y'] ?? 'Неизвестно' ?></span>
@ -89,25 +88,21 @@ if (empty($window)) {
</h5>
<div class="dropdown-divider mb-3"></div>
<div class="row px-2">
<div id="orders_panel_supplies_<?= $order['order']['_key'] ?>" class="col-3 unselectable">
<?php if (!empty($order['supplies'])) : ?>
<div id="orders_panel_supplies_<?= $moderator_data['order']->_key ?>" class="col-3 unselectable">
<?php if (!empty($moderator_data['supplies'])) : ?>
<?php
// Инициализация счетчика поставок для отрисовки горизонтального разделителя
$count = 1;
?>
<?php foreach ($order['supplies'] as $supply) :
// Перебор поставок
?>
<?php foreach ($moderator_data['supplies'] as $prod => $list) : ?>
<?php foreach ($list as $catn => $deliveries) : ?>
<?php foreach ($deliveries as $delivery => $supply) : ?>
<?php
// Инициализация окружения
extract($supply);
// Инициализация обложки
$covr = null;
foreach ($imgs ?? [] as $img) {
foreach ($supply['product']->imgs ?? [] as $img) {
// Перебор изображений для обложки
if ($img['covr'] ?? false) {
@ -122,7 +117,7 @@ if (empty($window)) {
if (is_null($covr)) {
// Обложка не инициализирована
if (!$covr = $imgs[0]['h150'] ?? false) {
if (!$covr = $supply['product']->imgs[0]['h150'] ?? false) {
// Не удалось использовать первое изображение как обложку
// Запись обложки по умолчанию
@ -130,86 +125,56 @@ if (empty($window)) {
}
}
if ($amount['auto'] > 0) {
// Найдены поставки с автоматической доставкой
if ($supply['amount'] > 0) {
// Пройдена проверка на количество поставок в заказе
if (Order::checkSuppliesStts($order_edge_supply)) {
$status = '<span id="' . $supply['catn'] . '_auto_supply_stts_indicator_icon" class="ml-auto my-auto fas fa-check"></span>';
} else {
// if (Order::checkSuppliesStts($order_edge_supply)) {
// $status = '<span id="' . $supply['catn'] . '_auto_supply_stts_indicator_icon" class="ml-auto my-auto fas fa-check"></span>';
// } else {
$status = '';
}
// }
// Инициализация иконки
$icon = $delivery === 'avia' ? 'fa-plane' : 'fa-truck';
// Генерация HTML
echo <<<HTML
<a id="{$supply['catn']}_auto_supply" class="row mb-2 p-2 px-0 rounded row_supply" type="button" onclick="return orders_supply_edit('{$supply['catn']}_auto', {$order['order']['_key']});">
<a id="{$prod}_{$catn}_{$delivery}_supply" class="row mb-2 p-2 px-0 rounded row_supply" type="button" onclick="return orders_supply_edit('{$prod}_{$catn}_{$delivery}', {$moderator_data['order']->_key});">
<img class="col-auto px-0 h-100 img-fluid rounded" src="$covr" />
<p id="{$supply['catn']}_auto_supply_stts_indicator" class="col d-flex text-dark">
{$product['catn']} x{$amount['auto']}
<small class="ml-2 my-auto fas fa-truck"></small>
<p id="{$prod}_{$catn}_{$delivery}_supply_stts_indicator" class="col d-flex text-dark">
{$catn} x{$supply['amount']}
<small class="ml-2 my-auto fas $icon"></small>
$status
</p>
</a>
HTML;
}
if ($amount['avia'] > 0) {
// Найдены поставки с автоматической доставкой
if (Order::checkSuppliesStts($order_edge_supply)) {
$status = '<span id="' . $supply['catn'] . '_avia_supply_stts_indicator_icon" class="ml-auto my-auto fas fa-check"></span>';
} else {
$status = '';
}
// Генерация HTML
echo <<<HTML
<a id="{$supply['catn']}_avia_supply" class="row mb-2 p-2 px-0 rounded row_supply" type="button" onclick="return orders_supply_edit('{$supply['catn']}_avia', {$order['order']['_key']});">
<img class="col-auto px-0 h-100 img-fluid rounded" src="$covr" />
<p id="{$supply['catn']}_avia_supply_stts_indicator" class="col d-flex text-dark">
{$product['catn']} x{$amount['avia']}
<small class="ml-2 my-auto fas fa-plane"></small>
$status
</p>
</a>
HTML;
}
?>
<?php if ($count++ < count($order['supplies'])) : ?>
<?php if ($count++ < count($moderator_data['supplies'])) : ?>
<div class="dropdown-divider mb-2"></div>
<?php endif ?>
<?php endforeach ?>
<?php endforeach ?>
<?php endforeach ?>
<?php else : ?>
<div class="row">
<p>Поставки не найдены</p>
</div>
<?php endif ?>
</div>
<div id="orders_panel_edit_<?= $order['order']['_key'] ?>" class="px-4 col-6 d-flex flex-column">
<div id="orders_panel_edit_<?= $moderator_data['order']->_key ?>" class="px-4 col-6 d-flex flex-column">
<p class="my-auto">Выберите поставку</p>
</div>
<div id="orders_panel_info_<?= $order['order']['_key'] ?>" class="col-3 d-flex flex-column">
<?php
// Конвертация статуса заказа
$status = match ($order['account_edge_order'][0]['type']) {
'requested' => 'Запрошен',
'handled' => 'Обрабатывается',
'completed' => 'Завершен',
}
?>
<p class="row mt-0 mb-3 px-2"><b>Статус:</b> <span class="ml-auto"><?= $status ?></span></p>
<a id="<?= $order['order']['_key'] ?>_button" class="row mt-auto mb-0 text-center text-white btn button_blue button_clean disabled" type="button" onclick="return order_accept('<?= $order['order']['_key'] ?>');">Подтвердить</a>
<div id="orders_panel_info_<?= $moderator_data['order']->_key ?>" class="col-3 d-flex flex-column">
<p class="row mt-0 mb-3 px-2"><b>Статус:</b> <span class="ml-auto"><?= AccountEdgeOrder::statusToRussian(AccountEdgeOrder::searchByOrder($moderator_data['order']->readId())['stts'] ?? '') ?></span></p>
<a id="<?= $moderator_data['order']->_key ?>_button" class="row mt-auto mb-0 text-center text-white btn button_blue button_clean disabled" type="button" onclick="return order_accept('<?= $moderator_data['order']->_key ?>');">Подтвердить</a>
</div>
</div>
<script defer>
document.addEventListener(
'DOMContentLoaded',
function() {
order_init('<?= $order['order']['_key'] ?>');
order_init('<?= $moderator_data['order']->_key ?>');
},
false
);
@ -273,27 +238,26 @@ if (empty($window)) {
</div>
<?php
if (isset($orders) && !empty($orders)) {
foreach ($orders as $order) {
if (!empty($data)) {
// Найдена информация о заказах
foreach ($data as $data) {
// Перебор заказов
// Инициализация
extract($order);
// Инициализация ребра: АККАУНТ -> ЗАКАЗ
$edge = AccountEdgeOrder::searchByOrder($moderator_data['order']->readId());
if (isset($order['stts']) && $order['stts'] === 'reserved') {
// Заказ был резервирован (отменён)
if (isset($edge['stts']) && ($order['stts'] === 'reserved' || $edge['stts'] === 'current')) {
// Заказ был резервирован (отменён) или это активный заказ (несформированный, в корзине)
continue;
}
// Пропуск активного заказа (несформированного, корзины)
if ($account_edge_order[0]['type'] === 'current') continue;
// Инициализация времени подтверждения заказа
if (isset($order['jrnl'])) {
// Журнал найден
if (isset($data['order']->jrnl)) {
// Найден журнал
foreach ($order['jrnl'] as $entry) {
foreach ($data['order']->jrnl as $entry) {
// Перебор записей в журнале
if ($entry['action'] === 'accepted') {
@ -318,37 +282,41 @@ if (empty($window)) {
// Инициализация общей стоимости поставок
$sum = 0;
if (isset($supplies)) {
if (isset($data['supplies'])) {
// Найдены поставки
// Инициализация максимального срока доставки
$delivery_max = 0;
// Инициализация поставок
foreach ($supplies as $supply) {
foreach ($data['supplies'] as $prod => $list) {
// Перебор поставок
foreach ($list as $catn => $deliveries) {
// Перебор доставок
foreach ($deliveries as $delivery => $supply) {
// Перебор заказов
// Инициализация переменных
extract($supply);
// Инициализация ребра: ЗАКАЗ -> ПОСТАВКА
$edge = OrderEdgeSupply::searchBySupplyCatnAndProd((string) $catn, $prod);
// Инициализация цены
$price_raw = $cost;
$price_raw = $supply['cost'];
// Инициализация комментария
$comment = $order_edge_supply['comm'] ?? 'Комментарий к заказу';
$comment = $edge['comm'] ?? 'Комментарий к заказу';
if ($amount['auto'] > 0) {
// Найдены поставки с автоматической доставкой
if ($supply['amount'] > 0) {
// Пройдена проверка на количество поставок в заказе
// Инициализация цены
$price_auto = $price_raw['auto'] . ' ' . $currency;
$price = $price_raw * $supply['amount'] . ' ' . $supply['currency'];
// Инициализация доставки
if (!isset($delivery) || (isset($delivery['auto'], $delivery['auto']['error']) || $delivery === '?')) {
if (!isset($supply['delivery']) || (isset($supply['delivery'], $supply['delivery']['error']) || $supply['delivery'] === '?')) {
// Не удалось рассчитать доставку
// Инициализация времени
$delivery_auto = '?';
$days = '?';
} else {
// Удалось рассчитать доставку
@ -356,11 +324,11 @@ if (empty($window)) {
try {
// Взять данные из "arrivalToOspSender" (Дата прибытия на терминал-отправитель)
$delivery_auto_send_date = DateTime::createFromFormat('Y-m-d', $delivery['auto']['orderDates']['arrivalToOspSender'])->getTimestamp();
$delivery_send_date = DateTime::createFromFormat('Y-m-d', $supply['delivery']['orderDates']['arrivalToOspSender'])->getTimestamp();
} catch (Throwable $e) {
// Взять данные из "pickup" (Дата передачи груза на адресе отправителя)
$delivery_auto_send_date = DateTime::createFromFormat('Y-m-d', $delivery['auto']['orderDates']['pickup'])->getTimestamp();
$delivery_send_date = DateTime::createFromFormat('Y-m-d', $supply['delivery']['orderDates']['pickup'])->getTimestamp();
}
// Инициализация времени доставки
@ -369,119 +337,57 @@ if (empty($window)) {
// Оставлено на всякий случай для дальнейших разбирательств
$delivery_auto_converted = DateTime::createFromFormat('Y-m-d H:i:s', $delivery['auto']['orderDates']['giveoutFromOspReceiver'])->getTimestamp();
$delivery_converted = DateTime::createFromFormat('Y-m-d H:i:s', $supply['delivery']['orderDates']['giveoutFromOspReceiver'])->getTimestamp();
} catch (Throwable $e) {
// Автоматическая доставка (подразумевается), данные из "arrivalToOspReceiver" (Дата прибытия натерминал-получатель)
$delivery_auto_converted = DateTime::createFromFormat('Y-m-d', $delivery['auto']['orderDates']['arrivalToOspReceiver'])->getTimestamp();
$delivery_converted = DateTime::createFromFormat('Y-m-d', $supply['delivery']['orderDates']['arrivalToOspReceiver'])->getTimestamp();
}
$delivery_auto = ceil(($delivery_auto_converted - ($delivery_auto_send_date ?? 0)) / 60 / 60 / 24) + 1;
$days = ceil(($delivery_converted - ($delivery_send_date ?? 0)) / 60 / 60 / 24) + 1;
}
// Инициализация статуса связи поставки
$status = OrderEdgeSupply::convertStatusToRussian($part['stts'] ?? '');
$status = OrderEdgeSupply::statusToRussian($edge['stts'] ?? '');
// Инициализация класса для поставки (если необходимо)
$css = match ($part['stts'] ?? '') {
$css = match ($edge['stts'] ?? '') {
'accepted' => ' supply_accepted',
default => ''
};
// Реинициализация максимальной даты доставки
if ($delivery_max !== '?' && $delivery_max < $delivery_auto) $delivery_max = $delivery_auto;
else if ($delivery_auto === '?') $delivery_max = '?';
if ($delivery_max !== '?' && $delivery_max < $days) $delivery_max = $days;
else if ($days === '?') $delivery_max = '?';
// Генерация HTML
// Пробела между supply и $css не должно быть
// Инициализация иконки
$icon = $delivery === 'avia' ? 'fa-plane' : 'fa-truck';
// Генерация HTML (пробела между supply и $css не должно быть)
$supplies_html .= <<<HTML
<div class="row py-2 supply$css text-center">
<div class="m-auto col-2">{$supply['catn']}</div>
<div class="m-auto col-2">{$catn}</div>
<small class="m-auto col-3">$status</small>
<div class="m-auto col-3">$delivery_auto <small class="mr-1 fas fa-truck"></small></div>
<div class="m-auto col-2">{$amount['auto']}</div>
<div class="m-auto col-2">$price_auto</div>
<div class="m-auto col-3">$days <small class="mr-1 fas $icon"></small></div>
<div class="m-auto col-2">{$supply['amount']}</div>
<div class="m-auto col-2">$price</div>
</div>
HTML;
}
if ($amount['avia'] > 0) {
// Найдены поставки с доставкой по воздуху
// Инициализация цены
$price_avia = $price_raw['avia'] . ' ' . $currency;
// Инициализация доставки
if (!isset($delivery) || (isset($delivery['avia'], $delivery['avia']['error']) || $delivery === '?')) {
// Не удалось рассчитать доставку
// Инициализация времени
$delivery_avia = '?';
} else {
// Удалось рассчитать доставку
// Инициализация даты отправки
try {
// Взять данные из "arrivalToOspSender" (Дата прибытия на терминал-отправитель)
$delivery_avia_send_date = DateTime::createFromFormat('Y-m-d', $delivery['avia']['orderDates']['arrivalToOspSender'])->getTimestamp();
} catch (Throwable $e) {
// Взять данные из "pickup" (Дата передачи груза на адресе отправителя)
$delivery_avia_send_date = DateTime::createFromFormat('Y-m-d', $delivery['avia']['orderDates']['pickup'])->getTimestamp();
}
// Инициализация времени доставки
try {
// Доставка по воздуху (подразумевается), данные из "giveoutFromOspReceiver" (Дата и время, с которого груз готов к выдаче на терминале)
$delivery_avia_converted = DateTime::createFromFormat('Y-m-d H:i:s', $delivery['avia']['orderDates']['giveoutFromOspReceiver'])->getTimestamp();
} catch (Throwable $e) {
// Автоматическая доставка (подразумевается), данные из "arrivalToOspReceiver" (Дата прибытия натерминал-получатель)
// Оставлено на всякий случай для дальнейших разбирательств
$delivery_avia_converted = DateTime::createFromFormat('Y-m-d', $delivery['avia']['orderDates']['arrivalToOspReceiver'])->getTimestamp();
}
$delivery_avia = ceil(($delivery_avia_converted - ($delivery_avia_send_date ?? 0)) / 60 / 60 / 24) + 1;
}
// Инициализация статуса связи поставки
$status = OrderEdgeSupply::convertStatusToRussian($part['stts'] ?? '');
// Инициализация класса для поставки (если необходимо)
$css = match ($part['stts'] ?? '') {
'accepted' => ' supply_accepted',
default => ''
};
// Реинициализация максимальной даты доставки
if ($delivery_max !== '?' && $delivery_max < $delivery_avia) $delivery_max = $delivery_avia;
else if ($delivery_avia === '?') $delivery_max = '?';
// Генерация HTML
// Пробела между supply и $css не должно быть
$supplies_html .= <<<HTML
<div class="row py-2 supply$css text-center">
<div class="m-auto col-2">{$supply['catn']}</div>
<small class="m-auto col-3">$status</small>
<div class="m-auto col-3">$delivery_avia <small class="mr-1 fas fa-plane"></small></div>
<div class="m-auto col-2">{$amount['avia']}</div>
<div class="m-auto col-2">$price_avia</div>
</div>
HTML;
}
// Инициализация общей цены
$sum = $price_raw['avia'] * $amount['avia'] + $price_raw['auto'] * $amount['auto'];
$sum += $price_raw * $supply['amount'];
}
}
}
}
}
// Инициализация статуса заказа
$status = AccountEdgeOrder::convertStatusToRussian($account_edge_order[0]['type']);
$status = AccountEdgeOrder::statusToRussian(AccountEdgeOrder::searchByOrder($moderator_data['order']->readId())['stts'] ?? '');
// Инициализация счета для скачивания
$invoice = <<<HTML
<a class="text-dark" href="/invoices/{$order['_key']}/download"><i class="fas fa-file-invoice-dollar"></i></a>
<a class="text-dark" href="/invoices/{$data['order']->_key}/download"><i class="fas fa-file-invoice-dollar"></i></a>
HTML;
echo <<<HTML

View File

@ -40,7 +40,7 @@ use app\models\SupplyEdgeProduct;
} else {
echo <<<HTML
<dt>
<a class="row text-dark button_white px-3 py-3 py-lg-2 font-weight-normal" title="Панель управления сайтом" href="$targetUrl" role="button" onclick="return page_profile_panel_settings();"><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 px-3 py-3 py-lg-2 font-weight-normal" title="Панель управления сайтом" href="/profile/panel" role="button" onclick="return page_profile_panel_settings();"><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>
HTML;
}
@ -60,7 +60,7 @@ use app\models\SupplyEdgeProduct;
HTML;
} else {
echo <<<HTML
<a class="row text-dark button_white px-3 py-3 py-lg-2 font-weight-normal" title="Управление поставками" href="$targetUrl" role="button" onclick="return page_profile_supplies();"><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 px-3 py-3 py-lg-2 font-weight-normal" title="Управление поставками" href="/profile/supplies" role="button" onclick="return page_profile_supplies();"><i class="fas fa-truck my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Поставки</span></a>
HTML;
}
}
@ -79,7 +79,7 @@ use app\models\SupplyEdgeProduct;
HTML;
} else {
echo <<<HTML
<a class="row text-dark button_white px-3 py-3 py-lg-2 font-weight-normal" title="Мониторинг и журналирование" href="$targetUrl" role="button" onclick="return page_profile_monitoring();"><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 px-3 py-3 py-lg-2 font-weight-normal" title="Мониторинг и журналирование" href="/profile/monitoring" role="button" onclick="return page_profile_monitoring();"><i class="fas fa-eye my-auto mx-auto mx-lg-0 col-1 pl-0 mr-lg-2"></i><span>Мониторинг</span></a>
HTML;
}
?>
@ -97,7 +97,7 @@ use app\models\SupplyEdgeProduct;
HTML;
} else {
echo <<<HTML
<a class="row text-dark button_white px-3 py-3 py-lg-2 font-weight-normal" title="Настройки аккаунта" href="$targetUrl" role="button" onclick="return page_profile_settings();"><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 px-3 py-3 py-lg-2 font-weight-normal" title="Настройки аккаунта" href="/profile" role="button" onclick="return page_profile_settings();"><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;
}
?>

View File

@ -3,6 +3,7 @@
declare(strict_types=1);
use app\models\Product;
use app\models\ProductGroup;
use app\models\Search;
?>
@ -29,7 +30,7 @@ use app\models\Search;
<?php if (isset($response) && is_array($response) && $response) : ?>
<?php
// Инициализация реестра пустышек (товаров без поставок или с ошибками)
$empties = [];
$list = [];
?>
<section class="mb-4 col-auto">
@ -43,7 +44,7 @@ use app\models\Search;
$catn = $row['catn'] ?? 'Неизвестно';
// Генерация списка товаров
$supplies_html = Search::generate($row, $covr, $empties);
$supplies_html = Search::generate($row, $covr, $list);
?>
<div class="col mb-2">
<div class="row p-2 rounded">
@ -62,7 +63,6 @@ use app\models\Search;
<?php endforeach ?>
</section>
<?php if (!empty($empties)) : ?>
<section class="col">
<?php
// Инициализация буфера аналогов
@ -71,28 +71,34 @@ use app\models\Search;
// Инициализация буфера записанных аналогов
$writed = [];
foreach ($empties as $prod => $products) {
// Перебор производителей не найденных товаров
foreach ($list as $prod => $products) {
// Перебор производителей найденных товаров
// Инициализация производителя
$analogs[$prod] = $analogs[$prod] ?? [];
foreach ($products as $catn) {
// Перебор не найденных товаров товаров производителя
// Перебор найденных товаров товаров производителя
// Чтение и запись аналогов
$analogs[$catn] = Search::content(products: Product::searchAnalogs($prod, $catn));
$analogs[$prod][$catn] = Search::content(products: ProductGroup::searchByProduct(Product::searchByCatnAndProd($catn, $prod))->searchProducts());
// Исключение из вывода в списке аналогов (проверка на дубликат)
$writed[$catn] = true;
$writed[$prod][$catn] = true;
}
}
// Инициализация блокировщика отрисовки заголовка
$block_title = false;
?>
<?php foreach ($analogs as $products) : ?>
<?php foreach ($analogs as $prod => $catns) : ?>
<?php foreach ($catns as $catn => $products) : ?>
<?php foreach ($products as $product) : ?>
<?php
// Проверка на дубликат
if (array_key_exists($product['catn'], $writed)) continue;
if (array_key_exists($product['catn'], $writed[$prod])) continue;
// Инициализация данных товара
$covr = null;
@ -131,8 +137,8 @@ use app\models\Search;
?>
<?php endforeach ?>
<?php endforeach ?>
<?php endforeach ?>
</section>
<?php endif ?>
<?php else : ?>
<?php if ($advanced ?? false) : ?>

View File

@ -526,7 +526,7 @@ function cart_registration_entity_generate(account) {
// Инициализация ярлыка "COMP"
let label_comp = document.createElement('label');
label_comp.setAttribute('class', 'control-label');
label_comp.innerText = 'Организация';
label_comp.innerText = 'Компания';
// Инициализация поля "COMP"
let input_comp = document.createElement('input');