крупная обнова
This commit is contained in:
parent
e1fa1c1f5a
commit
2c2e830f0a
|
@ -6,6 +6,9 @@ use yii\console\Controller;
|
||||||
use yii\console\ExitCode;
|
use yii\console\ExitCode;
|
||||||
|
|
||||||
use app\models\Invoice;
|
use app\models\Invoice;
|
||||||
|
use app\models\Product;
|
||||||
|
use app\models\ProductGroup;
|
||||||
|
use app\models\ImportEdgeSupply;
|
||||||
|
|
||||||
class TestController extends Controller
|
class TestController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -75,4 +78,59 @@ class TestController extends Controller
|
||||||
|
|
||||||
return ExitCode::OK;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,26 +86,26 @@ class CartController extends Controller
|
||||||
$account = Account::initAccount();
|
$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 {
|
} else {
|
||||||
throw new Exception('Не удалось инициализировать заказ');
|
throw new Exception('Не удалось инициализировать заказ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Инициализация содержимого корзины
|
// Инициализация содержимого корзины
|
||||||
$connections = $model->content(10, $page);
|
$data['supplies'] = $data['order']->supplies(10, $page, test: true);
|
||||||
|
|
||||||
// Инициализация данных списка для выбора терминала
|
// Инициализация данных списка для выбора терминала
|
||||||
$delivery_to_terminal_list = $account->genListTerminalsTo();
|
$delivery_to_terminal_list = $account->genListTerminalsTo();
|
||||||
|
@ -138,14 +138,14 @@ class CartController extends Controller
|
||||||
yii::$app->response->format = Response::FORMAT_JSON;
|
yii::$app->response->format = Response::FORMAT_JSON;
|
||||||
|
|
||||||
return [
|
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' => 'Корзина',
|
'title' => 'Корзина',
|
||||||
'redirect' => '/cart',
|
'redirect' => '/cart',
|
||||||
'_csrf' => yii::$app->request->getCsrfToken()
|
'_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
|
public function actionEditComm(string $catn, string $prod): array|string|null
|
||||||
|
|
|
@ -266,11 +266,10 @@ class OrderController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
// Инициализация заказов
|
// Инициализация заказов
|
||||||
$orders = Order::searchByType(
|
$data = Order::searchByType(
|
||||||
type: $type,
|
type: $type,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
page: 1,
|
page: 1,
|
||||||
select: '{account_edge_order, order}',
|
|
||||||
supplies: true,
|
supplies: true,
|
||||||
from: $from,
|
from: $from,
|
||||||
to: $to,
|
to: $to,
|
||||||
|
@ -278,7 +277,6 @@ class OrderController extends Controller
|
||||||
);
|
);
|
||||||
|
|
||||||
// Фильтрация
|
// Фильтрация
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!yii::$app->user->isGuest
|
!yii::$app->user->isGuest
|
||||||
&& yii::$app->user->identity->type === 'administrator'
|
&& 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 {
|
} else {
|
||||||
// Пользователь не имеет доступ
|
// Пользователь не имеет доступ
|
||||||
|
|
||||||
// Инициализация заглушки
|
// Инициализация заглушки
|
||||||
$moderator_orders = null;
|
$moderator_data = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (yii::$app->request->isPost) {
|
|
||||||
// POST-запрос
|
|
||||||
|
|
||||||
// Инициализация аккаунта
|
// Инициализация аккаунта
|
||||||
$account ?? $account = Account::initAccount();
|
$account ?? $account = Account::initAccount();
|
||||||
|
|
||||||
|
if (yii::$app->request->isPost) {
|
||||||
|
// POST-запрос
|
||||||
|
|
||||||
// Конвертация из UNIXTIME в формат поддерживаемый календарём по спецификации HTML
|
// Конвертация из UNIXTIME в формат поддерживаемый календарём по спецификации HTML
|
||||||
$from = DateTime::createFromFormat('U', (string) $from)->format('Y-m-d');
|
$from = DateTime::createFromFormat('U', (string) $from)->format('Y-m-d');
|
||||||
$to = DateTime::createFromFormat('U', (string) $to)->format('Y-m-d');
|
$to = DateTime::createFromFormat('U', (string) $to)->format('Y-m-d');
|
||||||
|
@ -309,15 +307,15 @@ class OrderController extends Controller
|
||||||
yii::$app->response->format = Response::FORMAT_JSON;
|
yii::$app->response->format = Response::FORMAT_JSON;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'main' => $this->renderPartial('/orders/index', compact('orders', 'moderator_orders', 'search', 'from', 'to', 'window')
|
'main' => $this->renderPartial('/orders/index', compact('data', 'moderator_data', 'account', 'search', 'from', 'to', 'window')
|
||||||
+ ['panel' => $this->renderPartial('/orders/search/panel', compact('account') + ['response' => @$orders[0]['supplies']] ?? null)]),
|
+ ['panel' => $this->renderPartial('/orders/search/panel', compact('account') + ['data' => $data] ?? null)]),
|
||||||
'title' => 'Заказы',
|
'title' => 'Заказы',
|
||||||
'redirect' => '/orders',
|
'redirect' => '/orders',
|
||||||
'_csrf' => yii::$app->request->getCsrfToken()
|
'_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();
|
$data['order'] = new Order();
|
||||||
$model->save() or throw new Exception('Не удалось инициализировать заказ');
|
$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
|
// Если запись не удалась, то вернуть код: 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;
|
return $return;
|
||||||
}
|
}
|
||||||
|
@ -585,12 +583,23 @@ class OrderController extends Controller
|
||||||
*/
|
*/
|
||||||
public function actionRequest(): string|array|null
|
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)) {
|
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 = Product::searchBySupplyId($edge->_to)) {
|
||||||
// Найден товар
|
// Найден товар
|
||||||
|
|
||||||
// Проверка на активность товара
|
if ($product['stts'] !== 'active') {
|
||||||
if ($product['stts'] === 'active');
|
// Не активен товар
|
||||||
else $edge->delete();
|
|
||||||
|
// Удаление из заказа
|
||||||
|
$edge->delete();
|
||||||
|
|
||||||
|
// Статус необходимости реинициализации
|
||||||
|
$reinitialization = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Реиницилазация
|
// Реинициализация
|
||||||
$model = Order::searchByType(supplies: true);
|
if ($reinitialization) $data = Order::searchByType(supplies: true);
|
||||||
|
|
||||||
// Запись
|
// Запись
|
||||||
$account_edge_order->type = 'requested';
|
$account_edge_order->type = 'requested';
|
||||||
|
|
||||||
if ($account_edge_order->update()) {
|
if ($account_edge_order->update() > 0) {
|
||||||
// Удалось сохранить изменения
|
// Удалось сохранить изменения
|
||||||
|
|
||||||
// Запись в журнал
|
// Запись в журнал
|
||||||
$model->journal('requested');
|
$data['order']->journal('requested');
|
||||||
|
|
||||||
// Инициализация буфера поставок
|
Invoice::generate($data['order']->_key, $this->renderPartial('/invoice/order/pattern', [
|
||||||
$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', [
|
|
||||||
'buyer' => [
|
'buyer' => [
|
||||||
'id' => yii::$app->user->identity->_key,
|
'id' => yii::$app->user->identity->_key,
|
||||||
'info' => 'Неизвестно'
|
'info' => 'Неизвестно'
|
||||||
],
|
],
|
||||||
'order' => [
|
'data' => $data,
|
||||||
'id' => $model->_key,
|
'date' => $account_edge_order->date ?? time()
|
||||||
'date' => $account_edge_order->date ?? time(),
|
|
||||||
'entries' => $supplies
|
|
||||||
]
|
|
||||||
]));
|
]));
|
||||||
|
|
||||||
// Отправка уведомлений модераторам
|
// Отправка уведомлений модераторам
|
||||||
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, '@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();
|
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 null;
|
||||||
* Генерация данных заказов для модераторов
|
}
|
||||||
*
|
|
||||||
* Включает поиск запрошенных заказов и связанных с ними поставках
|
|
||||||
*
|
|
||||||
* @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 $orders;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -791,6 +791,8 @@ class ProfileController extends Controller
|
||||||
|
|
||||||
return self::syncGeolocationWithDellin($account);
|
return self::syncGeolocationWithDellin($account);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1227,6 +1229,9 @@ class ProfileController extends Controller
|
||||||
* Удалить склад
|
* Удалить склад
|
||||||
*
|
*
|
||||||
* @return array|string|null
|
* @return array|string|null
|
||||||
|
*
|
||||||
|
* @todo
|
||||||
|
* 1. Удаление всех ImportEdgeSupply с устаревшими версиями
|
||||||
*/
|
*/
|
||||||
public function actionWarehousesDelete(): array|string|null
|
public function actionWarehousesDelete(): array|string|null
|
||||||
{
|
{
|
||||||
|
@ -1277,13 +1282,24 @@ class ProfileController extends Controller
|
||||||
foreach (Supply::searchByImport($import->readId(), limit: 9999) as $supply) {
|
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;
|
if ($supply->delete() === 1) ++$deleted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Отправка уведомления
|
// Отправка уведомления
|
||||||
Notification::_write("Удалено $deleted поставок из инстанции поставки $_key", account: $account->_key);
|
Notification::_write("Удалено $deleted поставок из инстанции поставки $_key", account: $account->_key);
|
||||||
|
|
|
@ -989,7 +989,7 @@ class Account extends Document implements IdentityInterface, PartnerInterface
|
||||||
public static function isModer($account = null): bool
|
public static function isModer($account = null): bool
|
||||||
{
|
{
|
||||||
if ($account = self::initAccount($account)) {
|
if ($account = self::initAccount($account)) {
|
||||||
// Аккаунт инициализирован
|
// Инициализирован аккаунт
|
||||||
|
|
||||||
if ($account->type === 'moderator') {
|
if ($account->type === 'moderator') {
|
||||||
return true;
|
return true;
|
||||||
|
@ -1022,4 +1022,26 @@ class Account extends Document implements IdentityInterface, PartnerInterface
|
||||||
default => 'Неизвестно'
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,10 +24,20 @@ class AccountEdgeOrder extends Edge
|
||||||
return self::find()->where(['_to' => $order_id])->limit($limit)->all();
|
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) {
|
return match ($status) {
|
||||||
'accepted' => 'Доставляется',
|
'requested' => 'Запрошен',
|
||||||
|
'accepted' => 'Ожидается отправка',
|
||||||
|
'going' => 'Доставляется',
|
||||||
|
'completed' => 'Завершен',
|
||||||
default => 'Обрабатывается'
|
default => 'Обрабатывается'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,7 +150,7 @@ abstract class Document extends ActiveRecord
|
||||||
return static::findOne(['_id' => $_id]);
|
return static::findOne(['_id' => $_id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function readLast(): ?static
|
public static function readLast(): static|null|bool
|
||||||
{
|
{
|
||||||
return static::find()->orderBy(['DESC'])->one();
|
return static::find()->orderBy(['DESC'])->one();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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')) {
|
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')) {
|
} 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')) {
|
} 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) {
|
if ($limit < 2) {
|
||||||
|
|
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace app\models;
|
namespace app\models;
|
||||||
|
|
||||||
|
use mirzaev\yii2\arangodb\Query;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Связь инстанций импорта с поставками
|
* Связь инстанций импорта с поставками
|
||||||
*/
|
*/
|
||||||
|
@ -25,6 +27,7 @@ class ImportEdgeSupply extends Edge
|
||||||
return array_merge(
|
return array_merge(
|
||||||
parent::attributes(),
|
parent::attributes(),
|
||||||
[
|
[
|
||||||
|
'vrsn'
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -37,6 +40,7 @@ class ImportEdgeSupply extends Edge
|
||||||
return array_merge(
|
return array_merge(
|
||||||
parent::attributeLabels(),
|
parent::attributeLabels(),
|
||||||
[
|
[
|
||||||
|
'vrsn' => 'Версия'
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -49,12 +53,63 @@ class ImportEdgeSupply extends Edge
|
||||||
return array_merge(
|
return array_merge(
|
||||||
parent::rules(),
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -274,7 +274,7 @@ class Order extends Document implements DocumentInterface
|
||||||
int|null $from = null,
|
int|null $from = null,
|
||||||
int|null $to = null,
|
int|null $to = null,
|
||||||
bool $count = false
|
bool $count = false
|
||||||
): self|int|array|null {
|
): int|array|null {
|
||||||
// Инициализация аккаунта
|
// Инициализация аккаунта
|
||||||
if (empty($account) && isset(yii::$app->user->identity)) {
|
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',
|
from: 'account',
|
||||||
to: 'order',
|
to: 'order',
|
||||||
subquery_where: $subquery_where,
|
subquery_where: $subquery_where,
|
||||||
|
@ -346,73 +346,81 @@ class Order extends Document implements DocumentInterface
|
||||||
sort: ['DESC'],
|
sort: ['DESC'],
|
||||||
select: $select,
|
select: $select,
|
||||||
direction: 'INBOUND',
|
direction: 'INBOUND',
|
||||||
count: !$supplies && $count ? true : false
|
count: !$supplies && $count
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!$supplies && $count) {
|
if (!$supplies && $count) {
|
||||||
// Запрошен подсчет заказов
|
// Запрошен подсчет заказов
|
||||||
|
|
||||||
return $return;
|
return $orders;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация буфера возврата
|
||||||
|
$return = [];
|
||||||
|
|
||||||
|
// Инициализация архитектуры буфера вывода
|
||||||
|
foreach ($orders as $key => $order) {
|
||||||
|
// Перебор заказов
|
||||||
|
|
||||||
|
// Запись в буфер возврата
|
||||||
|
$return[$key]['order'] = $order;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($supplies) {
|
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 {
|
} else {
|
||||||
|
// Массив с заказом (подразумевается)
|
||||||
|
|
||||||
// Инициализация настроек
|
// Инициализация настроек
|
||||||
$config = $container['order'];
|
$config = $container['order'];
|
||||||
unset($config['_id'], $config['_rev'], $config['_id']);
|
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) {
|
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 В будущем возможно заказ не только поставок реализовать
|
* @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);
|
$offset = $limit * ($page - 1);
|
||||||
|
|
||||||
if (!empty($search)) {
|
if (!empty($search)) {
|
||||||
// Передан поиск по продуктам в заказах
|
// Передан поиск по продуктам в заказах
|
||||||
|
|
||||||
// Добавить ограничитель
|
// Запись ограничения по максимальному значению
|
||||||
|
|
||||||
$limit = 9999;
|
$limit = 9999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,160 +434,77 @@ class Order extends Document implements DocumentInterface
|
||||||
'order._id' => $this->readId()
|
'order._id' => $this->readId()
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
foreach: ['edge' => 'order_edge_supply'],
|
where: 'order_edge_supply != []',
|
||||||
where: 'edge._to == supply._id',
|
|
||||||
limit: $limit,
|
limit: $limit,
|
||||||
offset: $offset,
|
offset: $offset,
|
||||||
filterStart: ['catn' => $search],
|
filterStart: ['catn' => $search],
|
||||||
direction: 'INBOUND',
|
direction: 'INBOUND',
|
||||||
select: '{supply, order_edge_supply}',
|
select: '{order_edge_supply}',
|
||||||
count: $count
|
count: $count
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($count) {
|
if ($count) {
|
||||||
// Запрошен подсчет
|
// Подсчёт запрошен
|
||||||
|
|
||||||
return $connections;
|
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) {
|
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) {
|
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]);
|
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 {
|
||||||
// Инициализация данных геолокации
|
// Инициализация данных геолокации
|
||||||
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) {
|
} catch (Exception $e) {
|
||||||
$from = empty(Settings::searchActive()->delivery_from_default) ? 36 : (int) Settings::searchActive()->delivery_from_default;
|
$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;
|
$to = 36;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($buffer_connection = $connection['product']['bffr']["$from-$to"] ?? false) {
|
if (($buffer_connection = $buffer['product']['bffr']["$from-$to-" . $edge['dlvr']['type']] ?? false) && time() < $buffer_connection['expires']) {
|
||||||
// Найдены данные доставки в буфере
|
// Найдены данные доставки в буфере и их срок хранения не превышен, информация актуальна
|
||||||
|
|
||||||
if (time() < $buffer_connection['expires']) {
|
|
||||||
// Срок хранения не превышен, информация актуальна
|
|
||||||
|
|
||||||
// Запись в буфер вывода
|
// Запись в буфер вывода
|
||||||
$connection['delivery']['auto'] = $buffer_connection['data'];
|
$buffer['delivery'] = $buffer_connection['data'];
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Инициализация инстанции продукта в базе данных
|
// Инициализация инстанции продукта в базе данных (реинициализация под ActiveRecord)
|
||||||
$product = Product::searchByCatnAndProd($connection['product']['catn'], $connection['product']['prod']);
|
$product = Product::searchByCatnAndProd($buffer['product']['catn'], $buffer['product']['prod']);
|
||||||
|
|
||||||
// Инициализация доставки Dellin (автоматическая)
|
// Инициализация доставки Dellin (автоматическая)
|
||||||
$product->bffr = ($product->bffr ?? []) + [
|
$product->bffr = ($product->bffr ?? []) + [
|
||||||
"$from-$to" => [
|
"$from-$to-" . $edge['dlvr']['type'] => [
|
||||||
'data' => $connection['delivery']['auto'] = Dellin::calcDeliveryAdvanced(
|
'data' => $buffer['delivery'] = Dellin::calcDeliveryAdvanced(
|
||||||
$from,
|
$from,
|
||||||
$to,
|
$to,
|
||||||
(int) ($connection['product']['wght'] ?? 0),
|
(int) ($buffer['product']['wght'] ?? 0),
|
||||||
(int) ($connection['product']['dmns']['x'] ?? 0),
|
(int) ($buffer['product']['dmns']['x'] ?? 0),
|
||||||
(int) ($connection['product']['dmns']['y'] ?? 0),
|
(int) ($buffer['product']['dmns']['y'] ?? 0),
|
||||||
(int) ($connection['product']['dmns']['z'] ?? 0),
|
(int) ($buffer['product']['dmns']['z'] ?? 0),
|
||||||
count($connection['order_edge_supply'])
|
avia: $edge['dlvr']['type'] === 'avia'
|
||||||
),
|
),
|
||||||
'expires' => time() + 86400
|
'expires' => time() + 86400
|
||||||
]
|
]
|
||||||
|
@ -622,90 +543,16 @@ class Order extends Document implements DocumentInterface
|
||||||
// Отправка в базу данных
|
// Отправка в базу данных
|
||||||
$product->update();
|
$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;
|
$buffer['cost'] = ($supply->cost ?? $supply->onec['Цены']['Цена']['ЦенаЗаЕдиницу'] ?? throw new exception('Не найдена цена товара')) + ($buffer['delivery']['price']['all'] ?? $buffer['delivery']['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;
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$from = empty(Settings::searchActive()->delivery_from_default) ? 36 : (int) Settings::searchActive()->delivery_from_default;
|
$buffer['delivery'] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return $supplies;
|
||||||
$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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -143,11 +143,22 @@ class OrderEdgeSupply extends Edge
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function convertStatusToRussian(string|int $status): string
|
|
||||||
|
/**
|
||||||
|
* Генерация ярлыка на русском языке для статуса заказа поставки
|
||||||
|
*
|
||||||
|
* @param string $status Статус заказа поставки
|
||||||
|
*
|
||||||
|
* @return string Ярлык статуса на русском языке
|
||||||
|
*/
|
||||||
|
public static function statusToRussian(string $status = ''): string
|
||||||
{
|
{
|
||||||
return match ($status) {
|
return match ($status) {
|
||||||
'accepted', 1 => 'Ожидается отправка',
|
'requested' => 'Запрошен',
|
||||||
default => 'Запрошен'
|
'accepted' => 'Ожидается отправка',
|
||||||
|
'going' => 'Доставляется',
|
||||||
|
'completed' => 'Завершен',
|
||||||
|
default => 'Обрабатывается'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,11 +59,6 @@ class Product extends Document
|
||||||
*/
|
*/
|
||||||
public UploadedFile|string|array|null $file_image = null;
|
public UploadedFile|string|array|null $file_image = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* Группа в которой состоит товар
|
|
||||||
*/
|
|
||||||
public ProductGroup|null $group = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Имя коллекции
|
* Имя коллекции
|
||||||
*/
|
*/
|
||||||
|
@ -113,7 +108,6 @@ class Product extends Document
|
||||||
'stts' => 'Статус',
|
'stts' => 'Статус',
|
||||||
'file_excel' => 'Документ (file_excel)',
|
'file_excel' => 'Документ (file_excel)',
|
||||||
'file_image' => 'Изображение (file_image)',
|
'file_image' => 'Изображение (file_image)',
|
||||||
'group' => 'Группа (group)',
|
|
||||||
'account' => 'Аккаунт'
|
'account' => 'Аккаунт'
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -233,7 +227,7 @@ class Product extends Document
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Запись пустого продукта
|
* Запись пустого товара
|
||||||
*/
|
*/
|
||||||
public static function writeEmpty(string $catn, string $prod = 'Неизвестный', bool $active = false): ?self
|
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')) {
|
if (!file_exists(YII_PATH_PUBLIC . $catalog_h150 = '/img/products/' . $this->_key . '/h150')) {
|
||||||
// Директория для обложек изображений продукта не найдена
|
// Директория для обложек изображений продукта не найдена
|
||||||
|
|
||||||
|
@ -462,138 +455,35 @@ class Product extends Document
|
||||||
)[0];
|
)[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')) {
|
if (!$group = ProductGroup::searchByProduct($this)) {
|
||||||
// Найдено ребро
|
// Не найдена группа товаров
|
||||||
} else if (ProductEdgeProduct::searchByVertex(Product::collectionName() . "/$to->_key", Product::collectionName() . "/$this->_key", type: 'analogue')) {
|
|
||||||
// Найдено ребро (наоборот)
|
|
||||||
|
|
||||||
// !!! Вероятно эта проверка здесь не нужна, так как мы знаем входные данные
|
// Запись новой группы
|
||||||
} else {
|
$group = ProductGroup::writeEmpty(active: true);
|
||||||
// Не найдены ребра
|
|
||||||
|
|
||||||
if ($edge = ProductEdgeProduct::write(Product::collectionName() . "/$this->_key", Product::collectionName() . "/$to->_key", data: ['type' => 'analogue'])) {
|
// Запись товара в группу
|
||||||
// Ребро сохранено
|
$group->writeProduct($this);
|
||||||
|
|
||||||
// Запись в журнал о соединении
|
|
||||||
$this->journal('connect analogue', [
|
|
||||||
'to' => Product::collectionName() . "/$to->_key"
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Запись в журнал о соединении
|
|
||||||
$to->journal('connect analogue', [
|
|
||||||
'from' => Product::collectionName() . "/$this->_key"
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $edge;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($_group = ProductGroup::searchByProduct($product)) {
|
||||||
|
// Найдена другая группа у товара который надо добавить в группу
|
||||||
|
|
||||||
|
// Перенос всех участников (включая целевой товар)
|
||||||
|
return $group->transfer($_group);
|
||||||
|
} else {
|
||||||
|
// Не найдена группа у товара который надо добавить в группу
|
||||||
|
|
||||||
|
// Запись целевого товара в группу
|
||||||
|
return $group->writeProduct($product);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -602,67 +492,21 @@ class Product extends Document
|
||||||
/**
|
/**
|
||||||
* Отключение аналога
|
* Отключение аналога
|
||||||
*
|
*
|
||||||
* @param self|null $to Цель (если null, то целью являются все подключенные аналоги)
|
* @return bool Статус выполнения
|
||||||
* @param bool $all Удалить соединения со всеми членами группы
|
|
||||||
*/
|
*/
|
||||||
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]) {
|
// Удаление из группы
|
||||||
// Найдено ребро
|
$group->deleteProduct($this);
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -670,42 +514,16 @@ class Product extends Document
|
||||||
return false;
|
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 Товар, если найден
|
||||||
*
|
|
||||||
* @return bool|static true если создать новую запись, static если найден дубликат
|
|
||||||
*
|
*
|
||||||
* @todo
|
* @todo
|
||||||
* 1. Обработка дубликатов
|
* 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)) {
|
if ($supplies = self::search(['catn' => $this->catn, 'prod' => $this->prod], limit: 100)) {
|
||||||
// Найдены поставки с таким же артикулом (catn) и производителем (prod)
|
// Найдены поставки с таким же артикулом (catn) и производителем (prod)
|
||||||
|
|
||||||
|
@ -717,9 +535,9 @@ class Product extends Document
|
||||||
|
|
||||||
// Возврат (найден дубликат в базе данных)
|
// Возврат (найден дубликат в базе данных)
|
||||||
return $supply;
|
return $supply;
|
||||||
} else return true;
|
}
|
||||||
|
|
||||||
// Возврат (подразумевается ошибка)
|
// Возврат (подразумевается отсутствие дубликатов в базе данных)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -796,4 +614,30 @@ class Product extends Document
|
||||||
|
|
||||||
return false;
|
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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,4 +10,17 @@ class ProductEdgeProductGroup extends Edge
|
||||||
{
|
{
|
||||||
return 'product_edge_product_group';
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,15 +4,19 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace app\models;
|
namespace app\models;
|
||||||
|
|
||||||
|
use app\models\traits\SearchByEdge;
|
||||||
|
|
||||||
use carono\exchange1c\interfaces\GroupInterface;
|
use carono\exchange1c\interfaces\GroupInterface;
|
||||||
|
|
||||||
use Zenwalker\CommerceML\Model\Group;
|
use Zenwalker\CommerceML\Model\Group;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Группировка продуктов
|
* Группировка продуктов для соединения их в аналоги
|
||||||
*/
|
*/
|
||||||
class ProductGroup extends Document implements GroupInterface
|
class ProductGroup extends Document implements GroupInterface
|
||||||
{
|
{
|
||||||
|
use SearchByEdge;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Имя коллекции
|
* Имя коллекции
|
||||||
*/
|
*/
|
||||||
|
@ -29,7 +33,7 @@ class ProductGroup extends Document implements GroupInterface
|
||||||
return array_merge(
|
return array_merge(
|
||||||
parent::attributes(),
|
parent::attributes(),
|
||||||
[
|
[
|
||||||
'name'
|
'stts'
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -42,7 +46,7 @@ class ProductGroup extends Document implements GroupInterface
|
||||||
return array_merge(
|
return array_merge(
|
||||||
parent::attributeLabels(),
|
parent::attributeLabels(),
|
||||||
[
|
[
|
||||||
'name' => 'Название (name)'
|
'stts' => 'Статус'
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -55,23 +59,138 @@ class ProductGroup extends Document implements GroupInterface
|
||||||
return array_merge(
|
return array_merge(
|
||||||
parent::rules(),
|
parent::rules(),
|
||||||
[
|
[
|
||||||
// [
|
[
|
||||||
// 'name',
|
'stts',
|
||||||
// 'required',
|
'string',
|
||||||
// 'message' => 'Заполните поле: {attribute}'
|
'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
|
public function writeMember(Product $member): ProductEdgeProductGroup
|
||||||
{
|
{
|
||||||
return ProductEdgeProductGroup::write($member->readId(), $this->readId(), 'member');
|
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]);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -392,12 +392,12 @@ class Search extends Document
|
||||||
*
|
*
|
||||||
* @param array $row Товар сгенерированный через Search::content()
|
* @param array $row Товар сгенерированный через Search::content()
|
||||||
* @param string|null $cover Обложка
|
* @param string|null $cover Обложка
|
||||||
* @param array $empties Реестр не найденных товаров
|
* @param array $list Реестр найденных товаров
|
||||||
* @param bool $analogs Запрошены аналоги (не выведет пустые товары)
|
* @param bool $analogs Запрошены аналоги (не выведет пустые товары)
|
||||||
*
|
*
|
||||||
* @return string HTML-элемент с товаром
|
* @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) {
|
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) {
|
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) {
|
if ($supplies_amount > $supply_iterator) {
|
||||||
// Это не последняя строка с товаром и его поставками
|
// Это не последняя строка с товаром и его поставками
|
||||||
|
@ -491,9 +494,6 @@ class Search extends Document
|
||||||
</div>
|
</div>
|
||||||
HTML;
|
HTML;
|
||||||
|
|
||||||
// Запись в список ненайденных
|
|
||||||
$empties[$row['prod']] = [$row['catn']] + (isset($empties[$row['prod']]) ? $empties[$row['prod']] : []);
|
|
||||||
|
|
||||||
// Запись блокировщика
|
// Запись блокировщика
|
||||||
$empty_block = true;
|
$empty_block = true;
|
||||||
|
|
||||||
|
|
|
@ -92,15 +92,7 @@ class Supply extends Product implements ProductInterface, OfferInterface
|
||||||
{
|
{
|
||||||
return array_merge(
|
return array_merge(
|
||||||
parent::rules(),
|
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)) {
|
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) {
|
foreach ($data as $doc) {
|
||||||
// Перебор полученных документов
|
// Перебор полученных документов
|
||||||
|
|
||||||
|
// Проверка на пустую страницу или документ (пустой массив)
|
||||||
|
if (empty($doc)) continue;
|
||||||
|
|
||||||
foreach ($doc as $row) {
|
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);
|
$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);
|
$_supply = trim($_supply);
|
||||||
|
|
||||||
|
@ -484,7 +504,7 @@ class Supply extends Product implements ProductInterface, OfferInterface
|
||||||
|
|
||||||
// Запись артикула (каталожного номера) в буфер
|
// Запись артикула (каталожного номера) в буфер
|
||||||
$_row['catn'] = $_supply;
|
$_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['prod'] = $prod;
|
||||||
$_row['oemn'] = array_walk($analogs, 'trim');
|
$_row['oemn'] = array_walk($analogs, 'trim');
|
||||||
|
|
||||||
|
@ -496,14 +516,29 @@ class Supply extends Product implements ProductInterface, OfferInterface
|
||||||
if ($supply->validate()) {
|
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 ($product = Product::searchByCatnAndProd($supply->catn, $supply->prod)) {
|
||||||
// Найден товар подходящий для привязки с только что созданной поставкой (подразумевается что уже был привязан в коде выше)
|
// Найден товар подходящий для привязки
|
||||||
|
|
||||||
// Приведение типа (для анализатора)
|
// Приведение типа (для анализатора)
|
||||||
if (is_array($product)) $product = $product[0];
|
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) {
|
if (count($edges) === 0) {
|
||||||
// Ребёр нет, но должны быть (если количество загружаемых поставок более нуля)
|
// Ребёр нет, но должны быть (если количество загружаемых поставок более нуля)
|
||||||
|
|
||||||
|
// Инициализация количества рёбер которые не удалось записать
|
||||||
|
$failed = 0;
|
||||||
|
|
||||||
for ($i = 0; $i++ < $amount;) {
|
for ($i = 0; $i++ < $amount;) {
|
||||||
// Перебор создаваемых рёбер (так работает обозначение количества товаров в наличии)
|
// Перебор создаваемых рёбер (так работает обозначение количества товаров в наличии)
|
||||||
|
|
||||||
// Запись ребра
|
if (SupplyEdgeProduct::write($supply->readId(), $product->readId(), data: ['type' => 'connect'])) {
|
||||||
SupplyEdgeProduct::write($_supply->readId(), $product->readId(), data: ['type' => 'connect']);
|
// Записано ребро
|
||||||
|
} else {
|
||||||
|
// Не записано ребро
|
||||||
|
|
||||||
|
// Обновление количества рёбер которые не удалось записать
|
||||||
|
++$failed;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отправка уведомления
|
||||||
|
if ($failed > 0) Notification::_write("Не удалось записать $failed рёбер у поставки $supply->catn");
|
||||||
} else if ($amount === count($edges)) {
|
} else if ($amount === count($edges)) {
|
||||||
// Количество товаров в поставке не изменилось
|
// Количество товаров в поставке не изменилось
|
||||||
|
|
||||||
// Раз изменений нет, то обновлять ничего не нужно
|
// Раз изменений нет, то обновлять ничего не нужно
|
||||||
continue;
|
// continue;
|
||||||
} else if ($amount < count($edges)) {
|
} 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)) {
|
} else if ($amount > count($edges)) {
|
||||||
// Количество товаров в поставке стало БОЛЬШЕ
|
// Количество товаров в поставке стало БОЛЬШЕ
|
||||||
|
|
||||||
|
@ -588,7 +634,7 @@ class Supply extends Product implements ProductInterface, OfferInterface
|
||||||
for ($i = 0; $i < $write; $i++) {
|
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 {
|
} 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']);
|
unset($vars['_key']);
|
||||||
|
|
||||||
// Перенос данных в буфер (существующий в базе данных дубликат)
|
// Перенос данных в буфер (существующий в базе данных дубликат)
|
||||||
$_supply->setAttributes($vars, false);
|
$supply->setAttributes($vars, false);
|
||||||
|
|
||||||
// Перезапись существующего документа
|
// Перезапись существующего документа
|
||||||
$_supply->update();
|
$supply->update();
|
||||||
|
|
||||||
// Обновление счётчика
|
// Обновление счётчика
|
||||||
$updated++;
|
$updated++;
|
||||||
|
|
||||||
// Запись поставки в буфер
|
|
||||||
$imported[] = $_supply;
|
|
||||||
|
|
||||||
// Запись в буфер (для универсальной обработки)
|
|
||||||
$supply = $_supply;
|
|
||||||
} else {
|
|
||||||
// Не найден документ с такими параметрами
|
|
||||||
|
|
||||||
if ($supply->save()) {
|
|
||||||
// Поставка записана в базу данных
|
|
||||||
|
|
||||||
// Обновление счётчика
|
|
||||||
$created++;
|
|
||||||
|
|
||||||
// Запись поставки в буфер
|
// Запись поставки в буфер
|
||||||
$imported[] = $supply;
|
$imported[] = $supply;
|
||||||
};
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Проверка не пройдена
|
// Проверка не пройдена
|
||||||
|
|
||||||
|
@ -654,26 +684,19 @@ class Supply extends Product implements ProductInterface, OfferInterface
|
||||||
// Активация товара
|
// Активация товара
|
||||||
$product->activate();
|
$product->activate();
|
||||||
|
|
||||||
// Инициализация списка артикулов группы для добавления аналогов
|
// foreach (Product::searchByCatn($product->catn) as $target) {
|
||||||
$group = [$article] + $analogs;
|
// // Перебор товаров по артикулу (все производители)
|
||||||
|
// }
|
||||||
foreach ($group as $catn) {
|
|
||||||
// Перебор артикулов для добавления аналогов
|
|
||||||
|
|
||||||
foreach (Product::searchByCatn((string) $catn) as $target) {
|
|
||||||
// Перебор товаров для добавления аналогов
|
|
||||||
|
|
||||||
// Добавление в группу аналогов
|
// Добавление в группу аналогов
|
||||||
if ($to = Product::searchByCatn((string) $target['catn'], 1)) $product->synchronization($to);
|
$group->writeProduct($product);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return !$error;
|
return !$error;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Запись поставки
|
// Запись поставки
|
||||||
$create((string) $article);
|
$create($article, (int) $amount);
|
||||||
|
|
||||||
foreach ($analogs as $_supply) {
|
foreach ($analogs as $_supply) {
|
||||||
// Перебор аналогов (если найдены)
|
// Перебор аналогов (если найдены)
|
||||||
|
@ -710,7 +733,7 @@ class Supply extends Product implements ProductInterface, OfferInterface
|
||||||
foreach ($imported as $supply) {
|
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])) {
|
||||||
// Записано ребро: ИНСТАНЦИЯ ПОСТАВОК -> ПОСТАВКА
|
// Записано ребро: ИНСТАНЦИЯ ПОСТАВОК -> ПОСТАВКА
|
||||||
|
|
||||||
// Запись в журнал инстанции импорта
|
// Запись в журнал инстанции импорта
|
||||||
|
|
|
@ -32,6 +32,7 @@ trait SearchByEdge
|
||||||
array $params = [],
|
array $params = [],
|
||||||
bool $asArray = true,
|
bool $asArray = true,
|
||||||
bool $debug = false,
|
bool $debug = false,
|
||||||
|
bool $aql = false,
|
||||||
bool $count = false
|
bool $count = false
|
||||||
): mixed {
|
): mixed {
|
||||||
$subquery = static::find()
|
$subquery = static::find()
|
||||||
|
@ -71,11 +72,20 @@ trait SearchByEdge
|
||||||
->limit($limit)
|
->limit($limit)
|
||||||
->select($select ?? $to);
|
->select($select ?? $to);
|
||||||
|
|
||||||
|
// Режим вывода строки запроса
|
||||||
|
if ($aql) {
|
||||||
|
// Запрошена проверка
|
||||||
|
|
||||||
|
return (string) $request->createCommand();
|
||||||
|
}
|
||||||
|
|
||||||
// Режим проверки
|
// Режим проверки
|
||||||
if ($debug) {
|
if ($debug) {
|
||||||
// Запрошена проверка
|
// Запрошена проверка
|
||||||
|
|
||||||
return (string) $request->createCommand();
|
var_dump((string) $request->createCommand());
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Запрос
|
// Запрос
|
||||||
|
|
|
@ -6,7 +6,7 @@ use yii;
|
||||||
use yii\bootstrap\ActiveForm;
|
use yii\bootstrap\ActiveForm;
|
||||||
|
|
||||||
use app\models\connection\Dellin;
|
use app\models\connection\Dellin;
|
||||||
|
use app\models\Supply;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
@ -21,10 +21,14 @@ use DateTime;
|
||||||
<div class="pl-3 mr-1">
|
<div class="pl-3 mr-1">
|
||||||
<input id="checkbox_cart_all" type="checkbox" onchange="return cart_list_checkbox(this);" />
|
<input id="checkbox_cart_all" type="checkbox" onchange="return cart_list_checkbox(this);" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<span>Производитель</span>
|
||||||
|
</div>
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<span>Артикул</span>
|
<span>Артикул</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-2">
|
||||||
|
<span>Поставщик</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-1 ml-auto px-0 text-center">
|
<div class="col-1 ml-auto px-0 text-center">
|
||||||
<span>Количество</span>
|
<span>Количество</span>
|
||||||
|
@ -37,31 +41,30 @@ use DateTime;
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
if (isset($connections) && !empty($connections)) {
|
if (!empty($data['supplies'])) {
|
||||||
foreach ($connections as $connection) {
|
// Найдены цели для заказа
|
||||||
|
|
||||||
|
// Инициализация списка поставок
|
||||||
|
$targets = [];
|
||||||
|
|
||||||
|
foreach ($data['supplies'] as $prod => $list) {
|
||||||
|
// Перебор поставщиков
|
||||||
|
|
||||||
|
foreach ($list as $catn => $deliveries) {
|
||||||
// Перебор поставок
|
// Перебор поставок
|
||||||
|
|
||||||
// Инициализация переменных
|
foreach ($deliveries as $delivery => $supply) {
|
||||||
extract($connection);
|
// Перебор типов доставки
|
||||||
|
|
||||||
// Инициализация цены
|
|
||||||
$price_raw = $cost;
|
|
||||||
|
|
||||||
// Инициализация комментария
|
// Инициализация комментария
|
||||||
$comment = $order_edge_supply[0]['comm'] ?? 'Комментарий к заказу';
|
$comment = $supply['edge']['comm'] ?? 'Комментарий к заказу';
|
||||||
|
|
||||||
if ($amount['auto'] > 0) {
|
|
||||||
// Найдены поставки с автоматической доставкой
|
|
||||||
|
|
||||||
// Инициализация цены
|
|
||||||
$price_auto = $price_raw['auto'] . ' ' . $currency;
|
|
||||||
|
|
||||||
// Инициализация доставки
|
// Инициализация доставки
|
||||||
if (!isset($delivery) || (isset($delivery['auto'], $delivery['auto']['error']) || $delivery === '?')) {
|
if (empty($supply['delivery'])) {
|
||||||
// Не удалось рассчитать доставку
|
// Не удалось рассчитать доставку
|
||||||
|
|
||||||
// Инициализация времени
|
// Инициализация времени
|
||||||
$delivery_auto = '?';
|
$days = '?';
|
||||||
} else {
|
} else {
|
||||||
// Удалось рассчитать доставку
|
// Удалось рассчитать доставку
|
||||||
|
|
||||||
|
@ -69,11 +72,11 @@ use DateTime;
|
||||||
try {
|
try {
|
||||||
// Взять данные из "arrivalToOspSender" (Дата прибытия на терминал-отправитель)
|
// Взять данные из "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) {
|
} catch (Throwable $e) {
|
||||||
// Взять данные из "pickup" (Дата передачи груза на адресе отправителя)
|
// Взять данные из "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) {
|
} 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" (Дата прибытия натерминал-получатель)
|
// Автоматическая доставка (подразумевается), данные из "arrivalToOspReceiver" (Дата прибытия натерминал-получатель)
|
||||||
|
$delivery_converted = DateTime::createFromFormat('Y-m-d', $supply['delivery']['orderDates']['arrivalToOspReceiver'])->getTimestamp();
|
||||||
// Оставлено на всякий случай для дальнейших разбирательств
|
|
||||||
|
|
||||||
$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;
|
$days = ceil(($delivery_converted - ($delivery_send_date ?? 0)) / 60 / 60 / 24) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Инициализация иконки
|
||||||
|
$icon = $delivery === 'avia' ? 'fa-plane' : 'fa-truck';
|
||||||
|
|
||||||
// Генерация HTML
|
// Генерация HTML
|
||||||
echo <<<HTML
|
echo <<<HTML
|
||||||
<div class="row py-2 cart_list_target">
|
<div class="row py-2 cart_list_target">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="pl-3 my-auto mr-1">
|
<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>
|
||||||
<div class="col-2 my-auto">
|
<div class="col-2 my-auto">
|
||||||
{$supply['catn']}
|
$prod
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4 my-auto">
|
<div class="col-2 my-auto">
|
||||||
{$supply['dscr']}
|
$catn
|
||||||
|
</div>
|
||||||
|
<div class="col-2 my-auto">
|
||||||
|
{$supply['account']['indx']}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-1 my-auto ml-auto">
|
<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>
|
||||||
<div class="col-2 my-auto text-right">
|
<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>
|
||||||
<div class="col-2 my-auto mr-3 text-right">
|
<div class="col-2 my-auto mr-3 text-right">
|
||||||
$price_avia
|
{$supply['cost']} {$supply['currency']}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
<div class="row mb-1">
|
<div class="row mb-1">
|
||||||
<div class="col-12">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -201,6 +136,7 @@ use DateTime;
|
||||||
HTML;
|
HTML;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
echo <<<HTML
|
echo <<<HTML
|
||||||
<div class="row py-2">
|
<div class="row py-2">
|
||||||
|
|
|
@ -8,7 +8,7 @@ use app\models\Settings;
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Счёт №<?= $order['id'] ?></title>
|
<title>Счёт №<?= $data['order']->_key ?></title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
@ -89,7 +89,7 @@ use app\models\Settings;
|
||||||
preg_match_all('/UTC([\+\-0-9:]*)/', $account->zone ?? Settings::searchActive()['timezone_default'] ?? 'UTC+3', $timezone);
|
preg_match_all('/UTC([\+\-0-9:]*)/', $account->zone ?? Settings::searchActive()['timezone_default'] ?? 'UTC+3', $timezone);
|
||||||
$timezone = $timezone[1][0];
|
$timezone = $timezone[1][0];
|
||||||
?>
|
?>
|
||||||
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
@ -133,7 +133,9 @@ use app\models\Settings;
|
||||||
|
|
||||||
<tr>
|
<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; 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;" 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>
|
<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;
|
$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>
|
<tr>
|
||||||
<td style="text-align: center; border: solid; border-left: thick;" colspan="1" valign="center"><?= $row++ ?></td>
|
<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: 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: center; border: solid;" colspan="2" valign="center"><?= $entry['amount']['value'] ?></td>
|
<td style="text-align: left; border: solid;" colspan="2" valign="center"><?= $supply['account']['indx'] ?></td>
|
||||||
<td style="text-align: center; border: solid;" valign="center"><?= $entry['cost']['value'] ?></td>
|
<td style="text-align: center; border: solid;" colspan="2" valign="center"><?= $supply['amount'] ?></td>
|
||||||
<td style="text-align: center; border: solid;" valign="center"><?= $entry['cost']['unit'] ?></td>
|
<td style="text-align: center; border: solid;" valign="center"><?= $supply['cost'] * $supply['amount'] ?></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: 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>
|
</tr>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
|
<?php endforeach ?>
|
||||||
|
<?php endforeach ?>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td style="border-top: thick;" colspan="13"></td>
|
<td style="border-top: thick;" colspan="13"></td>
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
<div class="p-3 h-100">
|
<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>
|
</div>
|
||||||
|
|
|
@ -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 mr-2" for="orders_panel_moderation_handled">Обрабатываются</label>
|
||||||
<label class="btn btn-sm button_white mb-0" for="orders_panel_moderation_completed">Завершены</label>
|
<label class="btn btn-sm button_white mb-0" for="orders_panel_moderation_completed">Завершены</label>
|
||||||
</div>
|
</div>
|
||||||
<?php if (!empty($moderator_orders)) : ?>
|
<?php if (!empty($moderator_data)) : ?>
|
||||||
|
<?php foreach ($moderator_data as $moderator_data) : ?>
|
||||||
<?php foreach ($moderator_orders as $order) : ?>
|
<div id="<?= $moderator_data['order']->_key ?>_panel" class="page_order_panel mb-3 py-3 px-4 rounded">
|
||||||
<div id="<?= $order['order']['_key'] ?>_panel" class="page_order_panel mb-3 py-3 px-4 rounded">
|
|
||||||
<h5 class="row mt-1 mb-3">
|
<h5 class="row mt-1 mb-3">
|
||||||
<?php
|
<?php
|
||||||
// Инициализация времени отправки заказа
|
// Инициализация времени отправки заказа
|
||||||
$date = null;
|
$date = null;
|
||||||
|
|
||||||
foreach ($order['order']['jrnl'] as $entry) {
|
foreach ($moderator_data['order']->jrnl as $entry) {
|
||||||
// Перебор записей в журнале
|
// Перебор записей в журнале
|
||||||
|
|
||||||
if ($entry['action'] === 'requested') {
|
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">
|
<p class="col-auto mr-1 font-weight-bold">
|
||||||
<span class="mr-2"><?= $date['H:i'] ?? 'Неизвестно' ?></span>
|
<span class="mr-2"><?= $date['H:i'] ?? 'Неизвестно' ?></span>
|
||||||
<span><?= $date['m.d.Y'] ?? 'Неизвестно' ?></span>
|
<span><?= $date['m.d.Y'] ?? 'Неизвестно' ?></span>
|
||||||
|
@ -89,25 +88,21 @@ if (empty($window)) {
|
||||||
</h5>
|
</h5>
|
||||||
<div class="dropdown-divider mb-3"></div>
|
<div class="dropdown-divider mb-3"></div>
|
||||||
<div class="row px-2">
|
<div class="row px-2">
|
||||||
<div id="orders_panel_supplies_<?= $order['order']['_key'] ?>" class="col-3 unselectable">
|
<div id="orders_panel_supplies_<?= $moderator_data['order']->_key ?>" class="col-3 unselectable">
|
||||||
<?php if (!empty($order['supplies'])) : ?>
|
<?php if (!empty($moderator_data['supplies'])) : ?>
|
||||||
<?php
|
<?php
|
||||||
// Инициализация счетчика поставок для отрисовки горизонтального разделителя
|
// Инициализация счетчика поставок для отрисовки горизонтального разделителя
|
||||||
$count = 1;
|
$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
|
<?php
|
||||||
// Инициализация окружения
|
|
||||||
extract($supply);
|
|
||||||
|
|
||||||
// Инициализация обложки
|
// Инициализация обложки
|
||||||
$covr = null;
|
$covr = null;
|
||||||
|
|
||||||
foreach ($imgs ?? [] as $img) {
|
foreach ($supply['product']->imgs ?? [] as $img) {
|
||||||
// Перебор изображений для обложки
|
// Перебор изображений для обложки
|
||||||
|
|
||||||
if ($img['covr'] ?? false) {
|
if ($img['covr'] ?? false) {
|
||||||
|
@ -122,7 +117,7 @@ if (empty($window)) {
|
||||||
if (is_null($covr)) {
|
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)) {
|
// 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>';
|
// $status = '<span id="' . $supply['catn'] . '_auto_supply_stts_indicator_icon" class="ml-auto my-auto fas fa-check"></span>';
|
||||||
} else {
|
// } else {
|
||||||
$status = '';
|
$status = '';
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// Инициализация иконки
|
||||||
|
$icon = $delivery === 'avia' ? 'fa-plane' : 'fa-truck';
|
||||||
|
|
||||||
// Генерация HTML
|
// Генерация HTML
|
||||||
echo <<<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" />
|
<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">
|
<p id="{$prod}_{$catn}_{$delivery}_supply_stts_indicator" class="col d-flex text-dark">
|
||||||
{$product['catn']} x{$amount['auto']}
|
{$catn} x{$supply['amount']}
|
||||||
<small class="ml-2 my-auto fas fa-truck"></small>
|
<small class="ml-2 my-auto fas $icon"></small>
|
||||||
$status
|
$status
|
||||||
</p>
|
</p>
|
||||||
</a>
|
</a>
|
||||||
HTML;
|
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($moderator_data['supplies'])) : ?>
|
||||||
<?php if ($count++ < count($order['supplies'])) : ?>
|
|
||||||
<div class="dropdown-divider mb-2"></div>
|
<div class="dropdown-divider mb-2"></div>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
|
<?php endforeach ?>
|
||||||
|
<?php endforeach ?>
|
||||||
<?php else : ?>
|
<?php else : ?>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<p>Поставки не найдены</p>
|
<p>Поставки не найдены</p>
|
||||||
</div>
|
</div>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</div>
|
</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>
|
<p class="my-auto">Выберите поставку</p>
|
||||||
</div>
|
</div>
|
||||||
<div id="orders_panel_info_<?= $order['order']['_key'] ?>" class="col-3 d-flex flex-column">
|
<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>
|
||||||
<?php
|
<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>
|
||||||
// Конвертация статуса заказа
|
|
||||||
$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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script defer>
|
<script defer>
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
'DOMContentLoaded',
|
'DOMContentLoaded',
|
||||||
function() {
|
function() {
|
||||||
|
order_init('<?= $moderator_data['order']->_key ?>');
|
||||||
order_init('<?= $order['order']['_key'] ?>');
|
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
@ -273,27 +238,26 @@ if (empty($window)) {
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
if (isset($orders) && !empty($orders)) {
|
if (!empty($data)) {
|
||||||
foreach ($orders as $order) {
|
// Найдена информация о заказах
|
||||||
|
|
||||||
|
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;
|
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') {
|
if ($entry['action'] === 'accepted') {
|
||||||
|
@ -318,37 +282,41 @@ if (empty($window)) {
|
||||||
// Инициализация общей стоимости поставок
|
// Инициализация общей стоимости поставок
|
||||||
$sum = 0;
|
$sum = 0;
|
||||||
|
|
||||||
if (isset($supplies)) {
|
if (isset($data['supplies'])) {
|
||||||
// Найдены поставки
|
// Найдены поставки
|
||||||
|
|
||||||
// Инициализация максимального срока доставки
|
// Инициализация максимального срока доставки
|
||||||
$delivery_max = 0;
|
$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 {
|
} else {
|
||||||
// Удалось рассчитать доставку
|
// Удалось рассчитать доставку
|
||||||
|
|
||||||
|
@ -356,11 +324,11 @@ if (empty($window)) {
|
||||||
try {
|
try {
|
||||||
// Взять данные из "arrivalToOspSender" (Дата прибытия на терминал-отправитель)
|
// Взять данные из "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) {
|
} catch (Throwable $e) {
|
||||||
// Взять данные из "pickup" (Дата передачи груза на адресе отправителя)
|
// Взять данные из "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) {
|
} catch (Throwable $e) {
|
||||||
// Автоматическая доставка (подразумевается), данные из "arrivalToOspReceiver" (Дата прибытия натерминал-получатель)
|
// Автоматическая доставка (подразумевается), данные из "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',
|
'accepted' => ' supply_accepted',
|
||||||
default => ''
|
default => ''
|
||||||
};
|
};
|
||||||
|
|
||||||
// Реинициализация максимальной даты доставки
|
// Реинициализация максимальной даты доставки
|
||||||
if ($delivery_max !== '?' && $delivery_max < $delivery_auto) $delivery_max = $delivery_auto;
|
if ($delivery_max !== '?' && $delivery_max < $days) $delivery_max = $days;
|
||||||
else if ($delivery_auto === '?') $delivery_max = '?';
|
else if ($days === '?') $delivery_max = '?';
|
||||||
|
|
||||||
// Генерация HTML
|
// Инициализация иконки
|
||||||
// Пробела между supply и $css не должно быть
|
$icon = $delivery === 'avia' ? 'fa-plane' : 'fa-truck';
|
||||||
|
|
||||||
|
// Генерация HTML (пробела между supply и $css не должно быть)
|
||||||
$supplies_html .= <<<HTML
|
$supplies_html .= <<<HTML
|
||||||
<div class="row py-2 supply$css text-center">
|
<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>
|
<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-3">$days <small class="mr-1 fas $icon"></small></div>
|
||||||
<div class="m-auto col-2">{$amount['auto']}</div>
|
<div class="m-auto col-2">{$supply['amount']}</div>
|
||||||
<div class="m-auto col-2">$price_auto</div>
|
<div class="m-auto col-2">$price</div>
|
||||||
</div>
|
</div>
|
||||||
HTML;
|
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
|
$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;
|
HTML;
|
||||||
|
|
||||||
echo <<<HTML
|
echo <<<HTML
|
||||||
|
|
|
@ -40,7 +40,7 @@ use app\models\SupplyEdgeProduct;
|
||||||
} else {
|
} else {
|
||||||
echo <<<HTML
|
echo <<<HTML
|
||||||
<dt>
|
<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>
|
</dt>
|
||||||
HTML;
|
HTML;
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ use app\models\SupplyEdgeProduct;
|
||||||
HTML;
|
HTML;
|
||||||
} else {
|
} else {
|
||||||
echo <<<HTML
|
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;
|
HTML;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ use app\models\SupplyEdgeProduct;
|
||||||
HTML;
|
HTML;
|
||||||
} else {
|
} else {
|
||||||
echo <<<HTML
|
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;
|
HTML;
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
@ -97,7 +97,7 @@ use app\models\SupplyEdgeProduct;
|
||||||
HTML;
|
HTML;
|
||||||
} else {
|
} else {
|
||||||
echo <<<HTML
|
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;
|
HTML;
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use app\models\Product;
|
use app\models\Product;
|
||||||
|
use app\models\ProductGroup;
|
||||||
use app\models\Search;
|
use app\models\Search;
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
@ -29,7 +30,7 @@ use app\models\Search;
|
||||||
<?php if (isset($response) && is_array($response) && $response) : ?>
|
<?php if (isset($response) && is_array($response) && $response) : ?>
|
||||||
<?php
|
<?php
|
||||||
// Инициализация реестра пустышек (товаров без поставок или с ошибками)
|
// Инициализация реестра пустышек (товаров без поставок или с ошибками)
|
||||||
$empties = [];
|
$list = [];
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<section class="mb-4 col-auto">
|
<section class="mb-4 col-auto">
|
||||||
|
@ -43,7 +44,7 @@ use app\models\Search;
|
||||||
$catn = $row['catn'] ?? 'Неизвестно';
|
$catn = $row['catn'] ?? 'Неизвестно';
|
||||||
|
|
||||||
// Генерация списка товаров
|
// Генерация списка товаров
|
||||||
$supplies_html = Search::generate($row, $covr, $empties);
|
$supplies_html = Search::generate($row, $covr, $list);
|
||||||
?>
|
?>
|
||||||
<div class="col mb-2">
|
<div class="col mb-2">
|
||||||
<div class="row p-2 rounded">
|
<div class="row p-2 rounded">
|
||||||
|
@ -62,7 +63,6 @@ use app\models\Search;
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<?php if (!empty($empties)) : ?>
|
|
||||||
<section class="col">
|
<section class="col">
|
||||||
<?php
|
<?php
|
||||||
// Инициализация буфера аналогов
|
// Инициализация буфера аналогов
|
||||||
|
@ -71,28 +71,34 @@ use app\models\Search;
|
||||||
// Инициализация буфера записанных аналогов
|
// Инициализация буфера записанных аналогов
|
||||||
$writed = [];
|
$writed = [];
|
||||||
|
|
||||||
foreach ($empties as $prod => $products) {
|
|
||||||
// Перебор производителей не найденных товаров
|
foreach ($list as $prod => $products) {
|
||||||
|
// Перебор производителей найденных товаров
|
||||||
|
|
||||||
|
// Инициализация производителя
|
||||||
|
$analogs[$prod] = $analogs[$prod] ?? [];
|
||||||
|
|
||||||
foreach ($products as $catn) {
|
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;
|
$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 foreach ($products as $product) : ?>
|
||||||
<?php
|
<?php
|
||||||
// Проверка на дубликат
|
// Проверка на дубликат
|
||||||
if (array_key_exists($product['catn'], $writed)) continue;
|
if (array_key_exists($product['catn'], $writed[$prod])) continue;
|
||||||
|
|
||||||
// Инициализация данных товара
|
// Инициализация данных товара
|
||||||
$covr = null;
|
$covr = null;
|
||||||
|
@ -131,8 +137,8 @@ use app\models\Search;
|
||||||
?>
|
?>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
|
<?php endforeach ?>
|
||||||
</section>
|
</section>
|
||||||
<?php endif ?>
|
|
||||||
|
|
||||||
<?php else : ?>
|
<?php else : ?>
|
||||||
<?php if ($advanced ?? false) : ?>
|
<?php if ($advanced ?? false) : ?>
|
||||||
|
|
|
@ -526,7 +526,7 @@ function cart_registration_entity_generate(account) {
|
||||||
// Инициализация ярлыка "COMP"
|
// Инициализация ярлыка "COMP"
|
||||||
let label_comp = document.createElement('label');
|
let label_comp = document.createElement('label');
|
||||||
label_comp.setAttribute('class', 'control-label');
|
label_comp.setAttribute('class', 'control-label');
|
||||||
label_comp.innerText = 'Организация';
|
label_comp.innerText = 'Компания';
|
||||||
|
|
||||||
// Инициализация поля "COMP"
|
// Инициализация поля "COMP"
|
||||||
let input_comp = document.createElement('input');
|
let input_comp = document.createElement('input');
|
||||||
|
|
Reference in New Issue