Доработка блока "Аналогичные товары"
This commit is contained in:
parent
a235ed25ac
commit
53281ee422
|
@ -95,12 +95,7 @@ class ProductController extends Controller
|
||||||
// Не существует товар к которому планируется соединение
|
// Не существует товар к которому планируется соединение
|
||||||
|
|
||||||
// Инициализация товара
|
// Инициализация товара
|
||||||
$to = new Product();
|
if ($to = Product::writeEmpty((string) $target)) {
|
||||||
|
|
||||||
// Запись артикула
|
|
||||||
$to->catn = (string) $target;
|
|
||||||
|
|
||||||
if ($to->save()) {
|
|
||||||
// Удалось записать товар
|
// Удалось записать товар
|
||||||
} else {
|
} else {
|
||||||
// Не удалось записать товар
|
// Не удалось записать товар
|
||||||
|
@ -116,19 +111,8 @@ class ProductController extends Controller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Запись ребра
|
if (($edge = $from->synchronization($to)) instanceof ProductEdgeProduct) {
|
||||||
if ($edge = ProductEdgeProduct::writeSafe(Product::collectionName() . "/$from->_key", Product::collectionName() . "/$to->_key", data: ['type' => 'analogue'])) {
|
// Ребро создано
|
||||||
// Ребро сохранено
|
|
||||||
|
|
||||||
// Запись в журнал о соединении
|
|
||||||
$from->journal('connect', [
|
|
||||||
'to' => Product::collectionName() . "/$to->_key"
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Запись в журнал о соединении
|
|
||||||
$to->journal('connect', [
|
|
||||||
'from' => Product::collectionName() . "/$from->_key"
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Запись в буфер возврата
|
// Запись в буфер возврата
|
||||||
$return['alert'] = "Продукты успешно соединены ребром: $edge->_key";
|
$return['alert'] = "Продукты успешно соединены ребром: $edge->_key";
|
||||||
|
@ -202,37 +186,9 @@ class ProductController extends Controller
|
||||||
if ($to = Product::searchByCatn($target)) {
|
if ($to = Product::searchByCatn($target)) {
|
||||||
// Существует товар который нужно отсоединить
|
// Существует товар который нужно отсоединить
|
||||||
|
|
||||||
// Поиск ребра
|
if ($from->disconnect($to)) {
|
||||||
if ($edge = @ProductEdgeProduct::searchByVertex(Product::collectionName() . "/$from->_key", Product::collectionName() . "/$to->_key", type: 'analogue')[0]) {
|
|
||||||
// Найдено ребро (from: $from, to: $to)
|
|
||||||
} else if ($edge = @ProductEdgeProduct::searchByVertex(Product::collectionName() . "/$to->_key", Product::collectionName() . "/$from->_key", type: 'analogue')[0]) {
|
|
||||||
// Найдено ребро (from: $to, to: $from)
|
|
||||||
} else {
|
|
||||||
// Не найдены ребра
|
|
||||||
|
|
||||||
// Запись кода ответа
|
|
||||||
yii::$app->response->statusCode = 500;
|
|
||||||
|
|
||||||
// Запись в буфер возврата
|
|
||||||
$return['alert'] = "Не удалось найти связь с этим товаром: $target";
|
|
||||||
|
|
||||||
// Переход в конец алгоритма
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($edge->delete() > 0) {
|
|
||||||
// Удалось удалить ребро (связь)
|
// Удалось удалить ребро (связь)
|
||||||
|
|
||||||
// Запись в журнал о разъединении
|
|
||||||
$from->journal('disconnect', [
|
|
||||||
'to' => Product::collectionName() . "/$to->_key"
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Запись в журнал о соединении
|
|
||||||
$to->journal('disconnect', [
|
|
||||||
'from' => Product::collectionName() . "/$from->_key"
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Запись в буфер возврата
|
// Запись в буфер возврата
|
||||||
$return['alert'] = "Продукты успешно отсоединены";
|
$return['alert'] = "Продукты успешно отсоединены";
|
||||||
} else {
|
} else {
|
||||||
|
@ -243,7 +199,7 @@ class ProductController extends Controller
|
||||||
yii::$app->response->statusCode = 500;
|
yii::$app->response->statusCode = 500;
|
||||||
|
|
||||||
// Запись в буфер возврата
|
// Запись в буфер возврата
|
||||||
$return['alert'] = "Не удалось удалить ребро между $catn и $target";
|
$return['alert'] = "Не удалось отсоединить $target от $catn";
|
||||||
|
|
||||||
// Переход в конец алгоритма
|
// Переход в конец алгоритма
|
||||||
goto end;
|
goto end;
|
||||||
|
|
|
@ -8,29 +8,15 @@ use yii;
|
||||||
use yii\web\Controller;
|
use yii\web\Controller;
|
||||||
use yii\web\Response;
|
use yii\web\Response;
|
||||||
|
|
||||||
use app\models\Account;
|
|
||||||
use app\models\Product;
|
use app\models\Product;
|
||||||
use app\models\Supply;
|
|
||||||
use app\models\Search;
|
use app\models\Search;
|
||||||
|
|
||||||
use app\models\connection\Dellin;
|
/**
|
||||||
use app\models\Settings;
|
* @todo
|
||||||
|
* 1. Ограничение доступа
|
||||||
use Exception;
|
*/
|
||||||
|
|
||||||
class SearchController extends Controller
|
class SearchController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo
|
* @todo
|
||||||
* 1. Сессию привязать к аккаунту и проверку по нему делать, иначе её можно просто сбрасывать
|
* 1. Сессию привязать к аккаунту и проверку по нему делать, иначе её можно просто сбрасывать
|
||||||
|
@ -161,222 +147,26 @@ class SearchController extends Controller
|
||||||
])) {
|
])) {
|
||||||
// Данные найдены по поиску в полях Каталожного номера
|
// Данные найдены по поиску в полях Каталожного номера
|
||||||
|
|
||||||
foreach ($response as &$row) {
|
// Генерация данных для представления
|
||||||
// Перебор продуктов
|
$response = Search::content(products: $response);
|
||||||
|
|
||||||
// Поиск поставок привязанных к продуктам
|
if (yii::$app->request->isPost) {
|
||||||
$connections = Supply::searchByEdge(
|
// POST-запрос
|
||||||
from: 'product',
|
|
||||||
to: 'supply',
|
|
||||||
edge: 'supply_edge_product',
|
|
||||||
limit: 11,
|
|
||||||
direction: 'OUTBOUND',
|
|
||||||
subquery_where: [
|
|
||||||
['product._key' => $row['_key']],
|
|
||||||
['supply.catn == product.catn'],
|
|
||||||
['supply_edge_product.type' => 'connect']
|
|
||||||
],
|
|
||||||
where: 'supply._id == supply_edge_product[0]._from',
|
|
||||||
select: '{supply, supply_edge_product}'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Инициализация буфера
|
|
||||||
$buffer_connections = [];
|
|
||||||
|
|
||||||
if (count($connections) === 11) {
|
|
||||||
// Если в базе данных хранится много поставок
|
|
||||||
|
|
||||||
// Инициализация
|
|
||||||
$row['overload'] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($connections as $key => &$connection) {
|
|
||||||
// Перебор поставок
|
|
||||||
|
|
||||||
if (($cost = $cost['ЦенаЗаЕдиницу'] ?? $connection['supply_edge_product'][0]['onec']['Цены']['Цена']['ЦенаЗаЕдиницу']) < 1
|
|
||||||
|| ($amount = $connection['supply']['amnt'] ?? $connection['supply_edge_product'][0]['onec']['Количество']) < 1
|
|
||||||
) {
|
|
||||||
// Цена меньше единицы (подразумевается как ошибка) или количество меньше единицы
|
|
||||||
|
|
||||||
// Скрыть из выдачи
|
|
||||||
unset($connections[$key]);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Инициализация аккаунта
|
|
||||||
$connection['account'] = Account::searchBySupplyId($connection['supply_edge_product'][0]['_from']);
|
|
||||||
|
|
||||||
// Инициализация продукта
|
|
||||||
$connection['product'] = Product::searchBySupplyId($connection['supply_edge_product'][0]['_from']);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Инициализация данных геолокации
|
|
||||||
|
|
||||||
try {
|
|
||||||
$from = (int) $connection['account']['opts']['delivery_from_terminal'] ?? empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default;
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$from = empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default;
|
|
||||||
}
|
|
||||||
|
|
||||||
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"] ?? false)
|
|
||||||
&& time() < $buffer_connection['expires']
|
|
||||||
) {
|
|
||||||
// Найдены данные доставки в буфере
|
|
||||||
// и срок хранения не превышен, информация актуальна
|
|
||||||
|
|
||||||
// Запись в буфер вывода
|
|
||||||
$connection['delivery'] = $buffer_connection['data'];
|
|
||||||
$connection['delivery']['type'] = 'auto';
|
|
||||||
} else {
|
|
||||||
// Инициализация инстанции продукта в базе данных
|
|
||||||
$product = Product::searchByCatn($connection['product']['catn']);
|
|
||||||
|
|
||||||
// Инициализация доставки Dellin (автоматическая)
|
|
||||||
$product->bffr = [
|
|
||||||
"$from-$to" => [
|
|
||||||
'data' => $connection['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)
|
|
||||||
),
|
|
||||||
'expires' => time() + 86400
|
|
||||||
]
|
|
||||||
] + ($product->bffr ?? []);
|
|
||||||
$connection['delivery']['type'] = 'auto';
|
|
||||||
|
|
||||||
// Отправка в базу данных
|
|
||||||
$product->update();
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$connection['delivery']['error'] = true;
|
|
||||||
|
|
||||||
// var_dump($e->getMessage());
|
|
||||||
// var_dump($e->getTrace());
|
|
||||||
// var_dump($e->getFile());
|
|
||||||
|
|
||||||
// var_dump(json_decode($e->getMessage(), true)['errors']);
|
|
||||||
// die;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Инициализация цены (цена поставки + цена доставки + наша наценка)
|
|
||||||
$connection['cost'] = $cost + ($connection['delivery']['price']['all'] ?? $connection['delivery']['price']['one'] ?? 0) + ($settings['increase'] ?? 0);
|
|
||||||
|
|
||||||
// Инициализация версии для рассчета доставки по воздуху
|
|
||||||
$buffer_delivery_avia = $connection;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Инициализация данных геолокации
|
|
||||||
|
|
||||||
if (($cost = $cost['ЦенаЗаЕдиницу'] ?? $buffer_delivery_avia['supply_edge_product'][0]['onec']['Цены']['Цена']['ЦенаЗаЕдиницу']) < 1
|
|
||||||
|| ($amount = $connection['supply']['amnt'] ?? $connection['supply_edge_product'][0]['onec']['Количество']) < 1
|
|
||||||
) {
|
|
||||||
// Цена меньше единицы (подразумевается как ошибка) или количество меньше единицы
|
|
||||||
// Этот код не будет выполняться, так как цена одна на обе позиции и аналогичная проверка выше уже есть
|
|
||||||
// Однако я это оставлю для возможных доработок в будущем
|
|
||||||
|
|
||||||
// Скрыть из выдачи
|
|
||||||
unset($connections[$key]);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$from = (int) $buffer_delivery_avia['account']['opts']['delivery_from_terminal'] ?? empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default;
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$from = empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$to = (int) yii::$app->user->identity->opts['delivery_to_terminal'] ?? 36;
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$to = 36;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
($buffer_connection = $buffer_delivery_avia['product']['bffr']["$from-$to-avia"] ?? false)
|
|
||||||
&& time() < $buffer_connection['expires']
|
|
||||||
) {
|
|
||||||
// Найдены данные доставки в буфере
|
|
||||||
// и срок хранения не превышен, информация актуальна
|
|
||||||
|
|
||||||
// Запись в буфер вывода
|
|
||||||
$buffer_delivery_avia['delivery'] = $buffer_connection['data'];
|
|
||||||
$buffer_delivery_avia['delivery']['type'] = 'avia';
|
|
||||||
} else {
|
|
||||||
// Инициализация инстанции продукта в базе данных
|
|
||||||
$product = Product::searchByCatn($buffer_delivery_avia['product']['catn']);
|
|
||||||
|
|
||||||
// Инициализация доставки Dellin (автоматическая)
|
|
||||||
$product->bffr = [
|
|
||||||
"$from-$to-avia" => [
|
|
||||||
'data' => $buffer_delivery_avia['delivery'] = Dellin::calcDeliveryAdvanced(
|
|
||||||
$from,
|
|
||||||
$to,
|
|
||||||
(int) ($buffer_delivery_avia['product']['wght'] ?? 0),
|
|
||||||
(int) ($buffer_delivery_avia['product']['dmns']['x'] ?? 0),
|
|
||||||
(int) ($buffer_delivery_avia['product']['dmns']['y'] ?? 0),
|
|
||||||
(int) ($buffer_delivery_avia['product']['dmns']['z'] ?? 0),
|
|
||||||
avia: true
|
|
||||||
),
|
|
||||||
'expires' => time() + 86400
|
|
||||||
]
|
|
||||||
] + ($product->bffr ?? []);
|
|
||||||
$buffer_delivery_avia['delivery']['type'] = 'avia';
|
|
||||||
|
|
||||||
// Отправка в базу данных
|
|
||||||
$product->update();
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$buffer_delivery_avia['delivery']['error'] = true;
|
|
||||||
|
|
||||||
// echo '<pre>';
|
|
||||||
// var_dump($e->getMessage());
|
|
||||||
// var_dump($e->getTrace());
|
|
||||||
// var_dump($e->getFile());
|
|
||||||
|
|
||||||
// var_dump(json_decode($e->getMessage(), true)['errors']);
|
|
||||||
// die;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($buffer_delivery_avia['delivery']['error']) || $buffer_delivery_avia['delivery']['error'] !== true) {
|
|
||||||
// Если рассчиталась доставка самолётом
|
|
||||||
|
|
||||||
// Инициализация цены (цена поставки + цена доставки + наша наценка)
|
|
||||||
$buffer_delivery_avia['cost'] = $cost + ($buffer_delivery_avia['delivery']['price']['all'] ?? $buffer_delivery_avia['delivery']['price']['one'] ?? 0) + ($settings['increase'] ?? 0);
|
|
||||||
|
|
||||||
// Запись в буфер
|
|
||||||
$buffer_connections[] = $buffer_delivery_avia;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Запись обработанных данных
|
|
||||||
$row['supplies'] = array_merge($connections, $buffer_connections);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Запись ответа
|
|
||||||
$return = [
|
|
||||||
'panel' => $this->renderPartial('/search/panel', compact('response')),
|
|
||||||
'_csrf' => yii::$app->request->getCsrfToken()
|
|
||||||
];
|
|
||||||
|
|
||||||
if ((int) yii::$app->request->post('advanced')) {
|
|
||||||
// Полноценный поиск
|
|
||||||
|
|
||||||
// Запись ответа
|
// Запись ответа
|
||||||
$return['main'] = $this->renderPartial('/search/index', compact('response'));
|
$return = [
|
||||||
$return['hide'] = 1;
|
'panel' => $this->renderPartial('/search/panel', compact('response', 'query')),
|
||||||
$return['redirect'] = '/search?type=product&q=' . $query;
|
'_csrf' => yii::$app->request->getCsrfToken()
|
||||||
|
];
|
||||||
|
|
||||||
|
if ((int) yii::$app->request->post('advanced')) {
|
||||||
|
// Полноценный поиск
|
||||||
|
|
||||||
|
// Запись ответа
|
||||||
|
$return['main'] = $this->renderPartial('/search/index', compact('response', 'query'));
|
||||||
|
$return['hide'] = 1;
|
||||||
|
$return['redirect'] = '/search?type=product&q=' . $query;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Данные не найдены
|
// Данные не найдены
|
||||||
|
@ -413,7 +203,7 @@ class SearchController extends Controller
|
||||||
|
|
||||||
$advanced = true;
|
$advanced = true;
|
||||||
|
|
||||||
return $this->render('/search/index', compact('response', 'timer', 'advanced'));
|
return $this->render('/search/index', compact('response', 'timer', 'advanced', 'query'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ abstract class Edge extends Document
|
||||||
* @todo
|
* @todo
|
||||||
* 1. Удалить $type и оставить только $data
|
* 1. Удалить $type и оставить только $data
|
||||||
*/
|
*/
|
||||||
public static function write(string $_from, string $_to, string $type, array $data = []): ?static
|
public static function write(string $_from, string $_to, string $type = '', array $data = []): ?static
|
||||||
{
|
{
|
||||||
// Инициализация
|
// Инициализация
|
||||||
$edge = new static;
|
$edge = new static;
|
||||||
|
@ -159,19 +159,31 @@ abstract class Edge extends Document
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Поиск рёбер
|
* Поиск рёбер по направлению
|
||||||
*/
|
*/
|
||||||
public static function search(string $target, string $direction = 'OUTBOUND', string $type = '', int $limit = 1): static|array|null
|
public static function search(string $target, string $direction = 'OUTBOUND', string $type = '', int $limit = 1): static|array|null
|
||||||
{
|
{
|
||||||
if ($direction === 'OUTBOUND') {
|
if (str_contains($direction, 'OUTBOUND')) {
|
||||||
$query = self::find()->where(['_from' => $target, 'type' => $type]);
|
// Исходящие рёбра
|
||||||
} else if ($direction === 'INBOUND') {
|
|
||||||
$query = self::find()->where(['_to' => $target, 'type' => $type]);
|
$query = static::find()->where(['_from' => $target, 'type' => $type]);
|
||||||
|
} else if (str_contains($direction, 'INBOUND')) {
|
||||||
|
// Входящие рёбра
|
||||||
|
|
||||||
|
$query = static::find()->where(['_to' => $target, 'type' => $type]);
|
||||||
|
} else if (str_contains($direction, 'ANY')) {
|
||||||
|
// Исходящие и входящие рёбра
|
||||||
|
|
||||||
|
return static::search(target: $target, direction: 'OUTBOUND', type: $type, limit: $limit) + static::search(target: $target, direction: 'INBOUND', type: $type, limit: $limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($limit < 2) {
|
if ($limit < 2) {
|
||||||
|
// Одно ребро или меньше
|
||||||
|
|
||||||
return $query->one();
|
return $query->one();
|
||||||
} else {
|
} else {
|
||||||
|
// Несколько рёбер
|
||||||
|
|
||||||
return $query->limit($limit)->all();
|
return $query->limit($limit)->all();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ use DateTime;
|
||||||
use DateTimeZone;
|
use DateTimeZone;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use phpDocumentor\Reflection\DocBlock\Tags\Var_;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Продукт (в ассортименте магазина)
|
* Продукт (в ассортименте магазина)
|
||||||
|
@ -596,10 +598,183 @@ class Product extends Document
|
||||||
*/
|
*/
|
||||||
public static function searchAnalogs(string $catn, int $limit = 30): ?array
|
public static function searchAnalogs(string $catn, int $limit = 30): ?array
|
||||||
{
|
{
|
||||||
|
// Инициализация буфера возврата
|
||||||
|
$return = [];
|
||||||
|
|
||||||
// Поиск ключей аналогов
|
// Поиск ключей аналогов
|
||||||
$analogs = ProductEdgeProduct::searchConnections(self::searchByCatn($catn)->_key, $limit);
|
$analogs = ProductEdgeProduct::searchConnections(self::searchByCatn($catn)->_key, $limit);
|
||||||
|
|
||||||
|
foreach ($analogs as $analog) {
|
||||||
|
// Перебор найденных ключей (_key) аналогов
|
||||||
|
|
||||||
return [];
|
if ($analog = Product::searchById(self::collectionName() . "/$analog")) {
|
||||||
|
// Найден товар
|
||||||
|
|
||||||
|
// Запись в буфер вывода
|
||||||
|
$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 = $from->connect($to)) {
|
||||||
|
// Ребро создано
|
||||||
|
|
||||||
|
// Запись в буфер
|
||||||
|
$edges[] = $edge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $edges;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Подключение аналога
|
||||||
|
*
|
||||||
|
* СВЯЗЫВАЕТ ТОЛЬКО ДВА ТОВАРА, ГРУППА ОСТАНЕТСЯ НЕИЗМЕННОЙ
|
||||||
|
*
|
||||||
|
* @param self $to Цель
|
||||||
|
*/
|
||||||
|
public function connect(self $to): ?ProductEdgeProduct
|
||||||
|
{
|
||||||
|
if (ProductEdgeProduct::searchByVertex(Product::collectionName() . "/$this->_key", Product::collectionName() . "/$to->_key", limit: 1)) {
|
||||||
|
// Дубликат найден
|
||||||
|
} else if (ProductEdgeProduct::searchByVertex(Product::collectionName() . "/$to->_key", Product::collectionName() . "/$this->_key", limit: 1)) {
|
||||||
|
// Дубликат найден (наоборот)
|
||||||
|
|
||||||
|
// Вероятно эта проверка здесь не нужна, так как мы знаем входные данные
|
||||||
|
} else {
|
||||||
|
// Дубликаты не найдены
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Отключение аналога
|
||||||
|
*
|
||||||
|
* @param self $to Цель
|
||||||
|
*/
|
||||||
|
public function disconnect(self $to): bool
|
||||||
|
{
|
||||||
|
// Поиск ребра
|
||||||
|
if ($edge = @ProductEdgeProduct::searchByVertex(Product::collectionName() . "/$this->_key", Product::collectionName() . "/$to->_key", type: 'analogue')[0]) {
|
||||||
|
// Найдено ребро (from: $this, to: $to)
|
||||||
|
} else if ($edge = @ProductEdgeProduct::searchByVertex(Product::collectionName() . "/$to->_key", Product::collectionName() . "/$this->_key", type: 'analogue')[0]) {
|
||||||
|
// Найдено ребро (from: $to, to: $this)
|
||||||
|
} else {
|
||||||
|
// Не найдены ребра
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($edge->delete() > 0) {
|
||||||
|
// Удалось удалить ребро (связь)
|
||||||
|
|
||||||
|
// Запись в журнал о разъединении
|
||||||
|
$this->journal('disconnect analogue', [
|
||||||
|
'to' => Product::collectionName() . "/$to->_key"
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Запись в журнал о соединении
|
||||||
|
$to->journal('disconnect analogue', [
|
||||||
|
'from' => Product::collectionName() . "/$this->_key"
|
||||||
|
]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Найти все связанные товары
|
||||||
|
*
|
||||||
|
* @param int $limit Ограничение по максимальному значению
|
||||||
|
*/
|
||||||
|
public function connections(int $limit = 100): array
|
||||||
|
{
|
||||||
|
// Инициализация буфера связанных товаров
|
||||||
|
$products = [];
|
||||||
|
|
||||||
|
foreach (ProductEdgeProduct::search(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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ class ProductEdgeProduct extends Edge
|
||||||
preg_match_all('/\w+\/([0-9]+)/', $edge->_to, $matches);
|
preg_match_all('/\w+\/([0-9]+)/', $edge->_to, $matches);
|
||||||
|
|
||||||
// Запись артикула в буфер вывода
|
// Запись артикула в буфер вывода
|
||||||
$return[] = $matches[1];
|
$return[] = $matches[1][0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Перерасчет ограничения по количеству
|
// Перерасчет ограничения по количеству
|
||||||
|
@ -47,10 +47,10 @@ class ProductEdgeProduct extends Edge
|
||||||
// Перебор найденных рёбер
|
// Перебор найденных рёбер
|
||||||
|
|
||||||
// Извлечение ключа
|
// Извлечение ключа
|
||||||
preg_match_all('/\w+\/([0-9]+)/', $edge->_to, $matches);
|
preg_match_all('/\w+\/([0-9]+)/', $edge->_from, $matches);
|
||||||
|
|
||||||
// Запись артикула в буфер вывода
|
// Запись артикула в буфер вывода
|
||||||
$return[] = $matches[1];
|
$return[] = $matches[1][0];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
|
|
|
@ -5,9 +5,14 @@ declare(strict_types=1);
|
||||||
namespace app\models;
|
namespace app\models;
|
||||||
|
|
||||||
use yii;
|
use yii;
|
||||||
use yii\web\IdentityInterface;
|
|
||||||
|
|
||||||
use app\models\traits\SearchByEdge;
|
use app\models\traits\SearchByEdge;
|
||||||
|
use app\models\connection\Dellin;
|
||||||
|
use app\models\Settings;
|
||||||
|
|
||||||
|
use datetime;
|
||||||
|
use exception;
|
||||||
|
use throwable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Поиск
|
* Поиск
|
||||||
|
@ -110,4 +115,435 @@ class Search extends Document
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Поиск содержимого поиска
|
||||||
|
*
|
||||||
|
* @todo В будущем возможно заказ не только поставок реализовать
|
||||||
|
* Переписать реестр и проверку на дубликаты, не понимаю как они работают
|
||||||
|
*/
|
||||||
|
public static function content(array $products, int $limit = 50, int $page = 1): Supply|int|array|null
|
||||||
|
{
|
||||||
|
// Инициализация буфера вывода
|
||||||
|
$response = $products;
|
||||||
|
|
||||||
|
// Генерация сдвига по запрашиваемым данным (пагинация)
|
||||||
|
$offset = $limit * ($page - 1);
|
||||||
|
|
||||||
|
foreach ($response as &$row) {
|
||||||
|
// Перебор продуктов
|
||||||
|
|
||||||
|
if ($row instanceof Product) {
|
||||||
|
// В массиве объект - инстанция товара
|
||||||
|
|
||||||
|
// Преобразование к массиву в буфер (унификация данных)
|
||||||
|
$_row = $row->attributes;
|
||||||
|
} else {
|
||||||
|
// В массиве не товар (подразумевается, что это массив с параметрами)
|
||||||
|
|
||||||
|
// Запись в буфер (унификация данных)
|
||||||
|
$_row = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Поиск поставок привязанных к продуктам
|
||||||
|
$connections = Supply::searchByEdge(
|
||||||
|
from: 'product',
|
||||||
|
to: 'supply',
|
||||||
|
edge: 'supply_edge_product',
|
||||||
|
limit: $limit,
|
||||||
|
offset: $offset,
|
||||||
|
direction: 'OUTBOUND',
|
||||||
|
subquery_where: [
|
||||||
|
['product._key' => $_row['_key']],
|
||||||
|
['supply.catn == product.catn'],
|
||||||
|
['supply_edge_product.type' => 'connect']
|
||||||
|
],
|
||||||
|
where: 'supply._id == supply_edge_product[0]._from',
|
||||||
|
select: '{supply, supply_edge_product}'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Инициализация буфера
|
||||||
|
$buffer_connections = [];
|
||||||
|
|
||||||
|
if (count($connections) === 11) {
|
||||||
|
// Если в базе данных хранится много поставок
|
||||||
|
|
||||||
|
// Инициализация
|
||||||
|
$_row['overload'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($connections as $key => &$connection) {
|
||||||
|
// Перебор поставок
|
||||||
|
|
||||||
|
if (($cost = $cost['ЦенаЗаЕдиницу'] ?? $connection['supply_edge_product'][0]['onec']['Цены']['Цена']['ЦенаЗаЕдиницу']) < 1
|
||||||
|
|| ($amount = $connection['supply']['amnt'] ?? $connection['supply_edge_product'][0]['onec']['Количество']) < 1
|
||||||
|
) {
|
||||||
|
// Цена меньше единицы (подразумевается как ошибка) или количество меньше единицы
|
||||||
|
|
||||||
|
// Скрыть из выдачи
|
||||||
|
unset($connections[$key]);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация аккаунта
|
||||||
|
$connection['account'] = Account::searchBySupplyId($connection['supply_edge_product'][0]['_from']);
|
||||||
|
|
||||||
|
// Инициализация продукта
|
||||||
|
$connection['product'] = Product::searchBySupplyId($connection['supply_edge_product'][0]['_from']);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Инициализация данных геолокации
|
||||||
|
|
||||||
|
try {
|
||||||
|
$from = (int) $connection['account']['opts']['delivery_from_terminal'] ?? empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default;
|
||||||
|
} catch (exception $e) {
|
||||||
|
$from = empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default;
|
||||||
|
}
|
||||||
|
|
||||||
|
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"] ?? false)
|
||||||
|
&& time() < $buffer_connection['expires']
|
||||||
|
) {
|
||||||
|
// Найдены данные доставки в буфере
|
||||||
|
// и срок хранения не превышен, информация актуальна
|
||||||
|
|
||||||
|
// Запись в буфер вывода
|
||||||
|
$connection['delivery'] = $buffer_connection['data'];
|
||||||
|
$connection['delivery']['type'] = 'auto';
|
||||||
|
} else {
|
||||||
|
// Инициализация инстанции продукта в базе данных
|
||||||
|
$product = Product::searchByCatn($connection['product']['catn']);
|
||||||
|
|
||||||
|
// Инициализация доставки Dellin (автоматическая)
|
||||||
|
$product->bffr = [
|
||||||
|
"$from-$to" => [
|
||||||
|
'data' => $connection['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)
|
||||||
|
),
|
||||||
|
'expires' => time() + 86400
|
||||||
|
]
|
||||||
|
] + ($product->bffr ?? []);
|
||||||
|
$connection['delivery']['type'] = 'auto';
|
||||||
|
|
||||||
|
// Отправка в базу данных
|
||||||
|
$product->update();
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$connection['delivery']['error'] = true;
|
||||||
|
|
||||||
|
// var_dump($e->getMessage());
|
||||||
|
// var_dump($e->getTrace());
|
||||||
|
// var_dump($e->getFile());
|
||||||
|
|
||||||
|
// var_dump(json_decode($e->getMessage(), true)['errors']);
|
||||||
|
// die;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация цены (цена поставки + цена доставки + наша наценка)
|
||||||
|
$connection['cost'] = $cost + ($connection['delivery']['price']['all'] ?? $connection['delivery']['price']['one'] ?? 0) + ($settings['increase'] ?? 0);
|
||||||
|
|
||||||
|
// Инициализация версии для рассчета доставки по воздуху
|
||||||
|
$buffer_delivery_avia = $connection;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Инициализация данных геолокации
|
||||||
|
|
||||||
|
if (($cost = $cost['ЦенаЗаЕдиницу'] ?? $buffer_delivery_avia['supply_edge_product'][0]['onec']['Цены']['Цена']['ЦенаЗаЕдиницу']) < 1
|
||||||
|
|| ($amount = $connection['supply']['amnt'] ?? $connection['supply_edge_product'][0]['onec']['Количество']) < 1
|
||||||
|
) {
|
||||||
|
// Цена меньше единицы (подразумевается как ошибка) или количество меньше единицы
|
||||||
|
// Этот код не будет выполняться, так как цена одна на обе позиции и аналогичная проверка выше уже есть
|
||||||
|
// Однако я это оставлю для возможных доработок в будущем
|
||||||
|
|
||||||
|
// Скрыть из выдачи
|
||||||
|
unset($connections[$key]);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$from = (int) $buffer_delivery_avia['account']['opts']['delivery_from_terminal'] ?? empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$from = empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$to = (int) yii::$app->user->identity->opts['delivery_to_terminal'] ?? 36;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$to = 36;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
($buffer_connection = $buffer_delivery_avia['product']['bffr']["$from-$to-avia"] ?? false)
|
||||||
|
&& time() < $buffer_connection['expires']
|
||||||
|
) {
|
||||||
|
// Найдены данные доставки в буфере
|
||||||
|
// и срок хранения не превышен, информация актуальна
|
||||||
|
|
||||||
|
// Запись в буфер вывода
|
||||||
|
$buffer_delivery_avia['delivery'] = $buffer_connection['data'];
|
||||||
|
$buffer_delivery_avia['delivery']['type'] = 'avia';
|
||||||
|
} else {
|
||||||
|
// Инициализация инстанции продукта в базе данных
|
||||||
|
$product = Product::searchByCatn($buffer_delivery_avia['product']['catn']);
|
||||||
|
|
||||||
|
// Инициализация доставки Dellin (автоматическая)
|
||||||
|
$product->bffr = [
|
||||||
|
"$from-$to-avia" => [
|
||||||
|
'data' => $buffer_delivery_avia['delivery'] = Dellin::calcDeliveryAdvanced(
|
||||||
|
$from,
|
||||||
|
$to,
|
||||||
|
(int) ($buffer_delivery_avia['product']['wght'] ?? 0),
|
||||||
|
(int) ($buffer_delivery_avia['product']['dmns']['x'] ?? 0),
|
||||||
|
(int) ($buffer_delivery_avia['product']['dmns']['y'] ?? 0),
|
||||||
|
(int) ($buffer_delivery_avia['product']['dmns']['z'] ?? 0),
|
||||||
|
avia: true
|
||||||
|
),
|
||||||
|
'expires' => time() + 86400
|
||||||
|
]
|
||||||
|
] + ($product->bffr ?? []);
|
||||||
|
$buffer_delivery_avia['delivery']['type'] = 'avia';
|
||||||
|
|
||||||
|
// Отправка в базу данных
|
||||||
|
$product->update();
|
||||||
|
}
|
||||||
|
} catch (exception $e) {
|
||||||
|
$buffer_delivery_avia['delivery']['error'] = true;
|
||||||
|
|
||||||
|
// echo '<pre>';
|
||||||
|
// var_dump($e->getMessage());
|
||||||
|
// var_dump($e->getTrace());
|
||||||
|
// var_dump($e->getFile());
|
||||||
|
|
||||||
|
// var_dump(json_decode($e->getMessage(), true)['errors']);
|
||||||
|
// die;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($buffer_delivery_avia['delivery']['error']) || $buffer_delivery_avia['delivery']['error'] !== true) {
|
||||||
|
// Если рассчиталась доставка самолётом
|
||||||
|
|
||||||
|
// Инициализация цены (цена поставки + цена доставки + наша наценка)
|
||||||
|
$buffer_delivery_avia['cost'] = $cost + ($buffer_delivery_avia['delivery']['price']['all'] ?? $buffer_delivery_avia['delivery']['price']['one'] ?? 0) + ($settings['increase'] ?? 0);
|
||||||
|
|
||||||
|
// Запись в буфер
|
||||||
|
$buffer_connections[] = $buffer_delivery_avia;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Запись обработанных данных
|
||||||
|
$_row['supplies'] = array_merge($connections, $buffer_connections);
|
||||||
|
|
||||||
|
// Запись из буфера
|
||||||
|
$row = $_row;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Генерация HTML-кода с найденным товаром
|
||||||
|
*
|
||||||
|
* @param array $row Товар сгенерированный через Search::content()
|
||||||
|
* @param string|null $cpny Компания (сейчас пока что это $name - Название)
|
||||||
|
* @param string|null $covr Обложка
|
||||||
|
* @param string|null $catg Категория
|
||||||
|
* @param string|null $catn Артикул
|
||||||
|
* @param array $empties Реестр не найденных товаров
|
||||||
|
*
|
||||||
|
* @return string HTML-элемент с товаром
|
||||||
|
*/
|
||||||
|
public static function generate(array &$row, string|null &$cpny = null, string|null &$covr = null, string|null &$catg = null, string|null &$catn = null, array &$empties = []): string
|
||||||
|
{
|
||||||
|
// Инициализация
|
||||||
|
extract($row);
|
||||||
|
$cpny ?? $cpny = 'Без названия';
|
||||||
|
// $dscr ?? $dscr = 'Описание';
|
||||||
|
$catg ?? $catg = 'Категория';
|
||||||
|
$catn ?? $catn = 'Неизвестно';
|
||||||
|
foreach ($imgs ?? [] as $img) {
|
||||||
|
// Перебор изображений для обложки
|
||||||
|
|
||||||
|
if ($img['covr'] ?? false) {
|
||||||
|
// Обложка найдена
|
||||||
|
|
||||||
|
$covr = $img['h150'];
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($covr)) {
|
||||||
|
// Обложка не инициализирована
|
||||||
|
|
||||||
|
if (!$covr = $imgs[0]['h150'] ?? false) {
|
||||||
|
// Не удалось использовать первое изображение как обложку
|
||||||
|
|
||||||
|
// Запись обложки по умолчанию
|
||||||
|
$covr = '/img/covers/h150/product.png';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация буфера с HTML поставок
|
||||||
|
$supplies_html = '';
|
||||||
|
|
||||||
|
// Инициализация блокировщика для пустого блока (на случай, если нет поставок, чтобы не было дубликатов вывода)
|
||||||
|
$empty_block = false;
|
||||||
|
|
||||||
|
// Инициализация счётчика поставок
|
||||||
|
$supplies_amount = count($row['supplies']);
|
||||||
|
|
||||||
|
// Инициализация указателя номера цикла
|
||||||
|
$supply_iterator = 1;
|
||||||
|
|
||||||
|
foreach (empty($row['supplies']) || count($row['supplies']) === 0 ? [null] : $row['supplies'] as $supply) {
|
||||||
|
// Инициализация модификатора класса
|
||||||
|
if ($supplies_amount > $supply_iterator) {
|
||||||
|
// Это не последняя строка с товаром и его поставками
|
||||||
|
|
||||||
|
$supply_class_modifier = 'mb-1';
|
||||||
|
} else {
|
||||||
|
// Это последняя строка с товаром и его поставками
|
||||||
|
|
||||||
|
$supply_class_modifier = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($supply)) {
|
||||||
|
// Поставки отсутствуют
|
||||||
|
|
||||||
|
// Генерация данных об отсутствии заказов
|
||||||
|
goto no_supplies;
|
||||||
|
} else {
|
||||||
|
// Обычная обработка поставки
|
||||||
|
|
||||||
|
// Инициализация переменных
|
||||||
|
extract($supply);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация цены
|
||||||
|
$price_raw = $cost;
|
||||||
|
$price = $price_raw . ' ' . $supply_edge_product[0]['onec']['Цены']['Цена']['Валюта'] ?? 'руб';
|
||||||
|
|
||||||
|
// Инициализация количества
|
||||||
|
$amount_raw = $amount = $supply['amnt'] ?? $supply_edge_product[0]['onec']['Количество'];
|
||||||
|
if (empty($amount_raw) || $amount_raw < 1) {
|
||||||
|
// Уже не используется
|
||||||
|
$amount = 'Под заказ';
|
||||||
|
} else {
|
||||||
|
$amount .= ' шт';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($amount_raw < 1 || $price_raw < 1) {
|
||||||
|
// Нет в наличии или цена 0 рублей
|
||||||
|
|
||||||
|
// Поставки отстутвуют
|
||||||
|
no_supplies:
|
||||||
|
|
||||||
|
// Проверка на блокировку
|
||||||
|
if ($empty_block) continue;
|
||||||
|
|
||||||
|
$supplies_html .= <<<HTML
|
||||||
|
<div class="row $supply_class_modifier m-0 h-100 text-right">
|
||||||
|
<a class="col-auto ml-auto my-auto text-dark" href="/order/new/custom" role="button" onclick="return false;">
|
||||||
|
<small>
|
||||||
|
Заказать поиск у оператора
|
||||||
|
</small>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
HTML;
|
||||||
|
|
||||||
|
// Запись в список ненайденных
|
||||||
|
$empties[] = $catn;
|
||||||
|
|
||||||
|
// Запись блокировщика
|
||||||
|
$empty_block = true;
|
||||||
|
|
||||||
|
// Обновление счётчика
|
||||||
|
++$supply_iterator;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация доставки
|
||||||
|
if (isset($delivery['error']) || $delivery === '?') {
|
||||||
|
// Не удалось рассчитать доставку
|
||||||
|
|
||||||
|
// Инициализация типа доставки
|
||||||
|
$delivery_type = $delivery['type'] ?? 'auto';
|
||||||
|
|
||||||
|
// Инициализация индикатора
|
||||||
|
$delivery_icon = '<i class="mr-1 fas fa-truck"></i>';
|
||||||
|
|
||||||
|
// Инициализация времени
|
||||||
|
$delivery = '?';
|
||||||
|
} else {
|
||||||
|
// Удалось рассчитать доставку
|
||||||
|
|
||||||
|
// Инициализация типа доставки
|
||||||
|
$delivery_type = $delivery['type'] ?? 'auto';
|
||||||
|
|
||||||
|
// Инициализация индикатора
|
||||||
|
$delivery_icon = match ($delivery_type) {
|
||||||
|
'avia' => '<i class="mr-1 fas fa-plane"></i>',
|
||||||
|
default => '<i class="mr-1 fas fa-truck"></i>'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Инициализация даты отправки
|
||||||
|
try {
|
||||||
|
// Взять данные из "arrivalToOspSender" (Дата прибытия на терминал-отправитель)
|
||||||
|
|
||||||
|
$delivery_send_date = datetime::createFromFormat('Y-m-d', $delivery['orderDates']['arrivalToOspSender'])->getTimestamp();
|
||||||
|
} catch (throwable $e) {
|
||||||
|
// Взять данные из "pickup" (Дата передачи груза на адресе отправителя)
|
||||||
|
|
||||||
|
$delivery_send_date = datetime::createFromFormat('Y-m-d', $delivery['orderDates']['pickup'])->getTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация времени доставки
|
||||||
|
try {
|
||||||
|
// Доставка по воздуху (подразумевается), данные из "giveoutFromOspReceiver" (Дата и время, с которого груз готов к выдаче на терминале)
|
||||||
|
|
||||||
|
$delivery_converted = datetime::createFromFormat('Y-m-d H:i:s', $delivery['orderDates']['giveoutFromOspReceiver'])->getTimestamp();
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
// Автоматическая доставка (подразумевается), данные из "arrivalToOspReceiver" (Дата прибытия натерминал-получатель)
|
||||||
|
|
||||||
|
$delivery_converted = datetime::createFromFormat('Y-m-d', $delivery['orderDates']['arrivalToOspReceiver'])->getTimestamp();
|
||||||
|
}
|
||||||
|
$delivery = ceil(($delivery_converted - ($delivery_send_date ?? 0)) / 60 / 60 / 24) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация индекса аккаунта
|
||||||
|
$index = $account['indx'] ?? 'Неизвестен';
|
||||||
|
|
||||||
|
// Генерация
|
||||||
|
$supplies_html .= <<<HTML
|
||||||
|
<div class="row $supply_class_modifier m-0 text-right">
|
||||||
|
<small class="ml-auto col-1 ml-2 my-auto pl-2 pr-0">$index</small>
|
||||||
|
<small class="col-1 my-auto pl-2 pr-0 text-center">$amount</small>
|
||||||
|
<small class="col-auto mr-2 my-auto pl-2 pr-0 text-left" title="Ориентировочно">$delivery_icon $delivery дн</small>
|
||||||
|
<b class="col-auto my-auto my-auto text-center">$price</b>
|
||||||
|
<a class="col-1 ml-0 py-2 text-dark d-flex button_white rounded" title="Добавить $catn в корзину" role="button" onclick="return cart_write('{$supply['_id']}', '$delivery_type');">
|
||||||
|
<i class="fas fa-cart-arrow-down pr-1 m-auto"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
HTML;
|
||||||
|
|
||||||
|
// Обновление счётчика
|
||||||
|
++$supply_iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $supplies_html;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use app\models\Product;
|
use app\models\Product;
|
||||||
|
use app\models\Search;
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
@ -27,218 +28,29 @@ use app\models\Product;
|
||||||
<?php else : ?>
|
<?php else : ?>
|
||||||
<?php if (isset($response) && is_array($response) && $response) : ?>
|
<?php if (isset($response) && is_array($response) && $response) : ?>
|
||||||
|
|
||||||
<div class="row py-3 w-100">
|
<div class="row py-3 w-100 flex-column">
|
||||||
<section class="col">
|
<section class="col-auto mb-4">
|
||||||
|
<h4 class="ml-4 mb-3">Товары по запросу: "<?= $query ?? 'ошибка чтения запроса' ?>"</h4>
|
||||||
<?php
|
|
||||||
// Инициализация
|
|
||||||
$covr_not_found = true;
|
|
||||||
$i = 0;
|
|
||||||
$amount = count($response);
|
|
||||||
?>
|
|
||||||
|
|
||||||
<?php foreach ($response as $row) : ?>
|
<?php foreach ($response as $row) : ?>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
// Инициализация
|
// Инициализация данных товара
|
||||||
extract($row);
|
$cpny = $covr = $catg = $catn = null;
|
||||||
$name ?? $name = 'Без названия';
|
|
||||||
// $dscr ?? $dscr = 'Описание';
|
|
||||||
$catg ?? $catg = 'Категория';
|
|
||||||
$catn ?? $catn = 'Неизвестно';
|
|
||||||
|
|
||||||
// Инициализация обложки
|
|
||||||
$covr = null;
|
|
||||||
foreach ($imgs ?? [] as $img) {
|
|
||||||
// Перебор изображений для обложки
|
|
||||||
|
|
||||||
if ($img['covr'] ?? false) {
|
|
||||||
// Обложка найдена
|
|
||||||
|
|
||||||
$covr = $img['h150'];
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_null($covr)) {
|
|
||||||
// Обложка не инициализирована
|
|
||||||
|
|
||||||
if (!$covr = $imgs[0]['h150'] ?? false) {
|
|
||||||
// Не удалось использовать первое изображение как обложку
|
|
||||||
|
|
||||||
// Запись обложки по умолчанию
|
|
||||||
$covr = '/img/covers/h150/product.png';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Инициализация буфера с HTML поставок
|
|
||||||
$supplies_html = '';
|
|
||||||
|
|
||||||
// Инициализация блокировщика для пустого блока (на случай, если нет поставок, чтобы не было дубликатов вывода)
|
|
||||||
$empty_block = false;
|
|
||||||
|
|
||||||
// Инициализация счётчика поставок
|
|
||||||
$supplies_amount = count($row['supplies']);
|
|
||||||
|
|
||||||
// Инициализация указателя номера цикла
|
|
||||||
$supply_iterator = 1;
|
|
||||||
|
|
||||||
// Инициализация реестра пустышек (товаров без поставок или с ошибками)
|
// Инициализация реестра пустышек (товаров без поставок или с ошибками)
|
||||||
$empties = [];
|
$empties = [];
|
||||||
|
|
||||||
|
// Генерация списка товаров
|
||||||
|
$supplies_html = Search::generate($row, $cpny, $covr, $catg, $catn, $empties);
|
||||||
|
|
||||||
|
extract($row);
|
||||||
?>
|
?>
|
||||||
<?php foreach (empty($row['supplies']) || count($row['supplies']) === 0 ? [null] : $row['supplies'] as $supply) : ?>
|
<div class="col mb-2">
|
||||||
<?php
|
|
||||||
|
|
||||||
// Инициализация модификатора класса
|
|
||||||
if ($supplies_amount > $supply_iterator) {
|
|
||||||
// Это не последняя строка с товаром и его поставками
|
|
||||||
|
|
||||||
$supply_class_modifier = 'mb-1';
|
|
||||||
} else {
|
|
||||||
// Это последняя строка с товаром и его поставками
|
|
||||||
|
|
||||||
$supply_class_modifier = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_null($supply)) {
|
|
||||||
// Поставки отсутствуют
|
|
||||||
|
|
||||||
// Генерация данных об отсутствии заказов
|
|
||||||
goto no_supplies;
|
|
||||||
} else {
|
|
||||||
// Обычная обработка поставки
|
|
||||||
|
|
||||||
// Инициализация переменных
|
|
||||||
extract($supply);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Инициализация цены
|
|
||||||
$price_raw = $cost;
|
|
||||||
$price = $price_raw . ' ' . $supply_edge_product[0]['onec']['Цены']['Цена']['Валюта'] ?? 'руб';
|
|
||||||
|
|
||||||
// Инициализация количества
|
|
||||||
$amount_raw = $amount = $supply['amnt'] ?? $supply_edge_product[0]['onec']['Количество'];
|
|
||||||
if (empty($amount_raw) || $amount_raw < 1) {
|
|
||||||
// Уже не используется
|
|
||||||
$amount = 'Под заказ';
|
|
||||||
} else {
|
|
||||||
$amount .= ' шт';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($amount_raw < 1 || $price_raw < 1) {
|
|
||||||
// Нет в наличии или цена 0 рублей
|
|
||||||
|
|
||||||
// Поставки отстутвуют
|
|
||||||
no_supplies:
|
|
||||||
|
|
||||||
// Проверка на блокировку
|
|
||||||
if ($empty_block) continue;
|
|
||||||
|
|
||||||
$supplies_html .= <<<HTML
|
|
||||||
<div class="row $supply_class_modifier m-0 h-100 text-right">
|
|
||||||
<a class="col-auto ml-auto my-auto text-dark" href="/order/new/custom" role="button" onclick="return false;">
|
|
||||||
<small>
|
|
||||||
Заказать поиск у оператора
|
|
||||||
</small>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
HTML;
|
|
||||||
|
|
||||||
// Запись в список ненайденных
|
|
||||||
$empties[] = $catn;
|
|
||||||
|
|
||||||
// Запись блокировщика
|
|
||||||
$empty_block = true;
|
|
||||||
|
|
||||||
// Обновление счётчика
|
|
||||||
++$supply_iterator;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Инициализация доставки
|
|
||||||
if (isset($delivery['error']) || $delivery === '?') {
|
|
||||||
// Не удалось рассчитать доставку
|
|
||||||
|
|
||||||
// Инициализация типа доставки
|
|
||||||
$delivery_type = $delivery['type'] ?? 'auto';
|
|
||||||
|
|
||||||
// Инициализация индикатора
|
|
||||||
$delivery_icon = '<i class="mr-1 fas fa-truck"></i>';
|
|
||||||
|
|
||||||
// Инициализация времени
|
|
||||||
$delivery = '?';
|
|
||||||
} else {
|
|
||||||
// Удалось рассчитать доставку
|
|
||||||
|
|
||||||
// Инициализация типа доставки
|
|
||||||
$delivery_type = $delivery['type'] ?? 'auto';
|
|
||||||
|
|
||||||
// Инициализация индикатора
|
|
||||||
$delivery_icon = match ($delivery_type) {
|
|
||||||
'avia' => '<i class="mr-1 fas fa-plane"></i>',
|
|
||||||
default => '<i class="mr-1 fas fa-truck"></i>'
|
|
||||||
};
|
|
||||||
|
|
||||||
// Инициализация даты отправки
|
|
||||||
try {
|
|
||||||
// Взять данные из "arrivalToOspSender" (Дата прибытия на терминал-отправитель)
|
|
||||||
|
|
||||||
$delivery_send_date = DateTime::createFromFormat('Y-m-d', $delivery['orderDates']['arrivalToOspSender'])->getTimestamp();
|
|
||||||
} catch (Throwable $e) {
|
|
||||||
// Взять данные из "pickup" (Дата передачи груза на адресе отправителя)
|
|
||||||
|
|
||||||
$delivery_send_date = DateTime::createFromFormat('Y-m-d', $delivery['orderDates']['pickup'])->getTimestamp();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Инициализация времени доставки
|
|
||||||
try {
|
|
||||||
// Доставка по воздуху (подразумевается), данные из "giveoutFromOspReceiver" (Дата и время, с которого груз готов к выдаче на терминале)
|
|
||||||
|
|
||||||
$delivery_converted = DateTime::createFromFormat('Y-m-d H:i:s', $delivery['orderDates']['giveoutFromOspReceiver'])->getTimestamp();
|
|
||||||
} catch (Throwable $e) {
|
|
||||||
// Автоматическая доставка (подразумевается), данные из "arrivalToOspReceiver" (Дата прибытия натерминал-получатель)
|
|
||||||
|
|
||||||
$delivery_converted = DateTime::createFromFormat('Y-m-d', $delivery['orderDates']['arrivalToOspReceiver'])->getTimestamp();
|
|
||||||
}
|
|
||||||
$delivery = ceil(($delivery_converted - ($delivery_send_date ?? 0)) / 60 / 60 / 24) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Инициализация индекса аккаунта
|
|
||||||
$index = $account['indx'] ?? 'Неизвестен';
|
|
||||||
|
|
||||||
// Генерация
|
|
||||||
$supplies_html .= <<<HTML
|
|
||||||
<div class="row $supply_class_modifier m-0 text-right">
|
|
||||||
<small class="ml-auto col-1 ml-2 my-auto pl-2 pr-0">$index</small>
|
|
||||||
<small class="col-1 my-auto pl-2 pr-0 text-center">$amount</small>
|
|
||||||
<small class="col-auto mr-2 my-auto pl-2 pr-0 text-left" title="Ориентировочно">$delivery_icon $delivery дн</small>
|
|
||||||
<b class="col-auto my-auto my-auto text-center">$price</b>
|
|
||||||
<a class="col-1 ml-0 py-2 text-dark d-flex button_white rounded" title="Добавить $catn в корзину" role="button" onclick="return cart_write('{$supply['_id']}', '$delivery_type');">
|
|
||||||
<i class="fas fa-cart-arrow-down pr-1 m-auto"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
HTML;
|
|
||||||
|
|
||||||
// Обновление счётчика
|
|
||||||
++$supply_iterator;
|
|
||||||
?>
|
|
||||||
|
|
||||||
<?php endforeach ?>
|
|
||||||
<?php foreach ($empties as $catn) : ?>
|
|
||||||
<?php foreach (Product::searchAnalogs($catn) as $product) : ?>
|
|
||||||
<?php
|
|
||||||
var_dump($product);
|
|
||||||
?>
|
|
||||||
<?php endforeach ?>
|
|
||||||
<?php endforeach ?>
|
|
||||||
<div class="col pb-2">
|
|
||||||
<div class="row p-2 rounded">
|
<div class="row p-2 rounded">
|
||||||
<img class="ml-0 h-100 img-fluid rounded" src="<?= $covr ?>" />
|
<img class="ml-0 h-100 img-fluid rounded" src="<?= $covr ?>" />
|
||||||
<div class="col-3 ml-3 p-0 d-flex flex-column row_fixed_height">
|
<div class="col-3 ml-3 p-0 d-flex flex-column row_fixed_height">
|
||||||
<a class="my-auto text-dark" href="/product/<?= $catn ?>">
|
<a class="my-auto text-dark" href="/product/<?= $catn ?>">
|
||||||
<h5 class="m-0"><?= $name ?></h5>
|
<h5 class="m-0"><?= $cpny ?></h5>
|
||||||
<h6 class="m-0"><small><?= $catn ?></small></h6>
|
<h6 class="m-0"><small><?= $catn ?></small></h6>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -249,14 +61,56 @@ use app\models\Product;
|
||||||
</div> -->
|
</div> -->
|
||||||
<div class="col ml-3 p-0 d-flex flex-column">
|
<div class="col ml-3 p-0 d-flex flex-column">
|
||||||
<?= $supplies_html ?>
|
<?= $supplies_html ?>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<?php if (!empty($empties)) : ?>
|
||||||
|
<section class="col">
|
||||||
|
<?php foreach ($empties as $catn) : ?>
|
||||||
|
|
||||||
|
<?php if ($products = Search::content(products: Product::searchAnalogs($catn))) : ?>
|
||||||
|
|
||||||
|
<h4 class="ml-4 mb-3">Аналогичные товары</h4>
|
||||||
|
|
||||||
|
<?php foreach ($products as $product) : ?>
|
||||||
|
<?php
|
||||||
|
// Инициализация данных товара
|
||||||
|
$cpny = $covr = $catg = $catn = null;
|
||||||
|
|
||||||
|
// Генерация списка товаров
|
||||||
|
$supplies_html = Search::generate($product, $cpny, $covr, $catg, $catn);
|
||||||
|
|
||||||
|
extract($product);
|
||||||
|
?>
|
||||||
|
<div class="col mb-2">
|
||||||
|
<div class="row p-2 rounded">
|
||||||
|
<img class="ml-0 h-100 img-fluid rounded" src="<?= $covr ?>" />
|
||||||
|
<div class="col-3 ml-3 p-0 d-flex flex-column row_fixed_height">
|
||||||
|
<a class="my-auto text-dark" href="/product/<?= $catn ?>">
|
||||||
|
<h5 class="m-0"><?= $cpny ?></h5>
|
||||||
|
<h6 class="m-0"><small><?= $catn ?></small></h6>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="col-1 ml-2 p-0 d-flex flex-column row_fixed_height">
|
||||||
|
<a class="my-auto text-dark" href="/product/<?= $catn ?>">
|
||||||
|
<small><?= $catg ?></small>
|
||||||
|
</a>
|
||||||
|
</div> -->
|
||||||
|
<div class="col ml-3 p-0 d-flex flex-column">
|
||||||
|
<?= $supplies_html ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach ?>
|
||||||
|
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php endforeach ?>
|
||||||
|
</section>
|
||||||
|
<?php endif ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php else : ?>
|
<?php else : ?>
|
||||||
|
|
Reference in New Issue