Псевдоанонимные идентификаторы аккаунтов и комментарий к заказу

This commit is contained in:
Arsen Mirzaev Tatyano-Muradovich 2021-04-25 20:00:05 +10:00
parent ada91bd0b7
commit 0a8c74a9a3
17 changed files with 764 additions and 186 deletions

View File

@ -0,0 +1,36 @@
<?php
namespace app\commands;
use yii\console\Controller;
use yii\console\ExitCode;
use app\models\Account;
class AccountController extends Controller
{
/**
* Сгенерировать уникальные идентификаторы
*
* @param int|string $_key Ключ аккаунта (оставить пустым или отправить "all", если для всех)
* @param bool $init Параметр обозначающий изменение только для тех у кого ранее идентификатор задан не был (без перезаписи)
*/
public function actionGenerateIndex(int|string $_key = null, bool $init = true)
{
// Инициализация
$accounts = empty($_key) || strcasecmp($_key, 'all') === 0 ? Account::readAll() : [Account::searchById($_key)];
// Генерация
$amount = Account::generateIndexes($accounts, $init);
echo 'Обработано аккаунтов: ' . $amount;
if ($amount > 0) {
// Был успешно обработан минимум 1 аккаунт
return ExitCode::OK;
}
return ExitCode::UNSPECIFIED_ERROR;
}
}

View File

@ -9,7 +9,10 @@ use app\models\connection\Dellin;
class DellinController extends Controller
{
public function actionCitiesImport()
/**
* Импортировать города из ДеловыеЛинии
*/
public function actionImportCities()
{
if (Dellin::importCities()) {
return ExitCode::OK;
@ -17,4 +20,16 @@ class DellinController extends Controller
return ExitCode::UNSPECIFIED_ERROR;
}
/**
* Импортировать терминалы из ДеловыеЛинии
*/
public function actionImportTerminals()
{
if (Dellin::importTerminals()) {
return ExitCode::OK;
}
return ExitCode::UNSPECIFIED_ERROR;
}
}

View File

@ -4,12 +4,14 @@ declare(strict_types=1);
namespace app\controllers;
use app\models\Notification;
use yii;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\web\Response;
use app\models\Order;
use app\models\OrderEdgeSupply;
use Exception;
@ -24,7 +26,7 @@ class CartController extends Controller
{
// Инициализация
$page = yii::$app->request->get('page') ?? yii::$app->request->post('page') ?? 1;
$account = yii::$app->user;
$account = yii::$app->user->identity;
// Поиск корзины (текущего заказа)
$model = Order::search();
@ -41,7 +43,7 @@ class CartController extends Controller
}
// Инициализация содержимого корзины
$supplies = $model->content(10, $page);
$connections = $model->content(10, $page);
if (yii::$app->request->isPost) {
// POST-запрос
@ -49,13 +51,88 @@ class CartController extends Controller
yii::$app->response->format = Response::FORMAT_JSON;
return [
'main' => $this->renderPartial('index', compact('model', 'supplies')),
'main' => $this->renderPartial('index', compact('model', 'connections')),
'title' => 'Корзина',
'redirect' => '/cart',
'_csrf' => yii::$app->request->getCsrfToken()
];
}
return $this->render('index', compact('model', 'supplies'));
return $this->render('index', compact('model', 'connections'));
}
public function actionEditComm(string $catn): array|string|null
{
// Инициализация
$return = [
'_csrf' => yii::$app->request->getCsrfToken()
];
if (is_null($catn)) {
// Не получен артикул
yii::$app->response->statusCode = 500;
goto end;
}
if ($edges = OrderEdgeSupply::searchBySupplyCatn($catn)) {
// Рёбра найдены (связи заказа с поставкой)
// Инициализация
$amount = 0;
foreach ($edges as $edge) {
// Перебор рёбер (связей заказа с поставкой)
// Инициализация
$text = yii::$app->request->post('text') ?? yii::$app->request->get('text') ?? 'Комментарий к заказу';
$comm = $edge->comm ?? null;
$edge->comm = $text;
if ($edge->save()) {
// Ребро обновлено
// Запись в журнал
$edge->journal('update', ['comm' => ['from' => $comm, 'to' => $edge->comm]]);
// Обновление счётчика
++$amount;
// Запись в буфер ответа
$return['comm'] = $text;
}
}
if ($amount > 0) {
// Удалось записать минимум 1 связь с поставкой
Notification::_write("Обновлён комментарий к товару $catn ($amount шт)");
} else {
// Не удалось записать минимум 1 связь с поставкой
Notification::_write("Не удалось обновить комментарий к товару $catn");
}
}
/**
* Конец алгоритма
*/
end:
if (yii::$app->request->isPost) {
// POST-запрос
yii::$app->response->format = Response::FORMAT_JSON;
return $return;
}
if ($model = Product::searchByCatn($catn)) {
return $this->render('index', compact('model'));
} else {
return $this->redirect('/');
}
}
}

View File

@ -92,7 +92,7 @@ class NotificationController extends Controller
* @param bool $new Активация проверки на то, что уведомление не получено
* @param bool $count Посчитать
*/
$search = function (bool $new = false, bool $count = false) use ($model, $account, $type, $let, $limit): array|int|null {
$search = function (bool $new = false, bool $count = false) use ($model, $account, $type, $let, $limit): array|int|null|Notification {
if ($count) {
// Запрошен подсчёт непрочитанных уведомлений
@ -164,7 +164,7 @@ class NotificationController extends Controller
goto end;
}
foreach ($notifications as $notification) {
foreach (is_array($notifications) ? $notifications : [$notifications] as $notification) {
// Перебор найденных уведомлений
if ($preload) {
@ -174,7 +174,7 @@ class NotificationController extends Controller
}
// Запись ребра: ПОЛЬЗОВАТЕЛЬ -> УВЕДОМЛЕНИЕ (о том, что уведомление прочитано)
AccountEdgeNotification::write(yii::$app->user->id, $notification->readId(), $type);
AccountEdgeNotification::write($account->readId(), $notification->readId(), $type);
}
if (yii::$app->request->post('last')) {

View File

@ -96,7 +96,7 @@ class OrderController extends Controller
// POST-запрос
// Инициализация входных данных
$account = yii::$app->user;
$account = yii::$app->user->identity;
$supplies = yii::$app->request->post('supplies');
yii::$app->response->format = Response::FORMAT_JSON;
@ -122,7 +122,7 @@ class OrderController extends Controller
$model->save() or throw new Exception('Не удалось инициализировать заказ');
// Запись ребра: АККАУНТ -> ЗАКАЗ
AccountEdgeOrder::write($account->id, $model->readId(), 'current') or $model->addError('errors', 'Не удалось инициализировать ребро: АККАУНТ -> ЗАКАЗ');
AccountEdgeOrder::write($account->readId(), $model->readId(), 'current') or $model->addError('errors', 'Не удалось инициализировать ребро: АККАУНТ -> ЗАКАЗ');
}
// Проверка входных данных
@ -155,7 +155,7 @@ class OrderController extends Controller
// Инициализация
$targets = yii::$app->request->post('targets') ?? yii::$app->request->get('targets');
$page = yii::$app->request->get('page') ?? yii::$app->request->post('page') ?? 1;
$account = yii::$app->user;
$account = yii::$app->user->identity;
$order = Order::search();
if ($targets) {
@ -182,7 +182,7 @@ class OrderController extends Controller
$order->journal('reserved');
// Поиск
$edge = AccountEdgeOrder::searchByVertex($account->id, $order->readId(), 'current');
$edge = AccountEdgeOrder::searchByVertex($account->readId(), $order->readId(), 'current');
if (count($edge) > 1) {
// Найден более чем 1 заказ
@ -214,7 +214,7 @@ class OrderController extends Controller
}
// Инициализация содержимого корзины
$supplies = $order->content(10, $page);
$connections = $order->content(10, $page);
if (yii::$app->request->isPost) {
// POST-запрос
@ -222,14 +222,14 @@ class OrderController extends Controller
yii::$app->response->format = Response::FORMAT_JSON;
return [
'main' => $this->renderPartial('/cart/index', compact('order', 'supplies')),
'main' => $this->renderPartial('/cart/index', compact('order', 'connections')),
'title' => 'Корзина',
'redirect' => '/cart',
'_csrf' => yii::$app->request->getCsrfToken()
];
}
return $this->render('/cart/index', compact('order', 'supplies'));
return $this->render('/cart/index', compact('order', 'connections'));
}
/**
@ -242,7 +242,7 @@ class OrderController extends Controller
$page = yii::$app->request->get('page') ?? yii::$app->request->post('page') ?? 1;
$account = yii::$app->user;
$order = Order::search();
$supplies = $order->content(10, $page);
$connections = $order->content(10, $page);
foreach (isset($targets[0]) && is_array($targets[0]) ? $targets : [$targets] as $target) {
// Унификация входных параметров
@ -250,22 +250,22 @@ class OrderController extends Controller
foreach ($target as $catn => $amount) {
// Перебор целей (переданных объектов в корзине)
foreach ($supplies as $supply) {
foreach ($connections as $connection) {
// Перебор объектов в корзине
if ($supply->catn === $catn) {
if ($connection['supply']['catn'] === $catn) {
// Цель найдена
if ($supply->amnt > $amount) {
if ($connection['amount'] > $amount) {
// Запрошено уменьшение количества
// Удаление
$order->deleteSupply([$catn => $supply->amnt - $amount]);
} else if ($supply->amnt < $amount) {
$order->deleteSupply([$catn => $connection['amount'] - $amount]);
} else if ($connection['amount'] < $amount) {
// Запрошено увеличение количества
// Запись
$order->writeSupply([$supply->catn => $amount - $supply->amnt]);
$order->writeSupply([$connection['supply']['catn'] => $amount - $connection['amount']]);
}
}
}
@ -273,7 +273,7 @@ class OrderController extends Controller
}
// Ренициализация
$supplies = $order->content(10, $page);
$connections = $order->content(10, $page);
if (yii::$app->request->isPost) {
// POST-запрос
@ -281,14 +281,14 @@ class OrderController extends Controller
yii::$app->response->format = Response::FORMAT_JSON;
return [
'main' => $this->renderPartial('/cart/index', compact('order', 'supplies')),
'main' => $this->renderPartial('/cart/index', compact('order', 'connections')),
'title' => 'Корзина',
'redirect' => '/cart',
'_csrf' => yii::$app->request->getCsrfToken()
];
}
return $this->render('/cart/index', compact('order', 'supplies'));
return $this->render('/cart/index', compact('order', 'connections'));
}
/**

View File

@ -208,7 +208,6 @@ class ProductController extends Controller
// Инициализация
$text = yii::$app->request->post('text') ?? yii::$app->request->get('text') ?? '0';
$text or $text = '0';
$dimension = yii::$app->request->post('dimension') ?? yii::$app->request->get('dimension') ?? 'x';
$product->dmns = array_merge(
@ -265,8 +264,6 @@ class ProductController extends Controller
// Инициализация
$text = yii::$app->request->post('text') ?? yii::$app->request->get('text') ?? '0';
$text or $text = '0';
$product->wght = $text;
if ($product->save()) {

View File

@ -12,6 +12,8 @@ use app\models\Product;
use app\models\Supply;
use app\models\Search;
use app\models\connection\Dellin;
class SearchController extends Controller
{
/**
@ -167,9 +169,19 @@ class SearchController extends Controller
$row['overload'] = true;
}
// Поиск аккаунтов владельцев поставок
foreach ($row['supplies'] as &$edge) {
// Перебор поставок
// Инициализация аккаунта
$edge['account'] = Supply::searchAccountById($edge['_from']);
// Инициализация доставки
$edge['delivery'] = Dellin::calcDelivery($edge['account']['opts']['delivery_from_city'] ?? $settings['delivery_from_city_default'] ?? '2700000100000000000000000', yii::$app->user->identity->opts['delivery_to_city'] ?? $settings['delivery_to_city_default'] ?? '2700000100000000000000000')['terminals_standard'];
$edge['delivery']['max'] = $edge['delivery']['period_to'];
$edge['delivery']['price'] = $edge['delivery']['price'];
// Инициализация цены (цена поставки + цена доставки + наша наценка)
$edge['prce'] = ($edge['prce'] ?? $edge['onec']['Цены']['Цена']['ЦенаЗаЕдиницу']) + ($edge['delivery']['price'] ?? 0) + ($settings['increase'] ?? 0);
}
}

View File

@ -36,6 +36,7 @@ class Account extends Document implements IdentityInterface, PartnerInterface
[
'auth',
'mail',
'indx',
'pswd',
'name',
'simc',
@ -60,6 +61,7 @@ class Account extends Document implements IdentityInterface, PartnerInterface
[
'auth' => 'Аутентификационный хеш',
'mail' => 'Почта',
'indx' => 'Псевдоанонимный идентификатор',
'pswd' => 'Пароль',
'name' => 'Имя',
'simc' => 'Номер',
@ -82,9 +84,32 @@ class Account extends Document implements IdentityInterface, PartnerInterface
return array_merge(
parent::rules(),
[
[['mail', 'pswd'], 'required', 'message' => 'Заполните поле'],
['mail', 'email'],
[['mail', 'comp', 'simc'], 'unique', 'message' => '2']
[
[
'mail',
'pswd'
],
'required',
'message' => 'Заполните поле'
],
[
'mail',
'email'
],
[
[
'mail',
'indx',
'comp',
'simc'
],
'unique',
'message' => 'Атрибут {attribute} должен иметь уникальное значение'
],
[
'indx',
'string'
]
]
);
}
@ -356,7 +381,8 @@ class Account extends Document implements IdentityInterface, PartnerInterface
*
* @return array Сортированный список
*/
protected function syncListWithSettings(array &$list, string $var): array {
protected function syncListWithSettings(array &$list, string $var): array
{
// Инициализация текущего значения параметра в начале массива
if (isset($this->opts[$var])) {
// Параметр найден в настройках аккаунта
@ -387,4 +413,65 @@ class Account extends Document implements IdentityInterface, PartnerInterface
return $list;
}
/**
* Генерация псевдоанонимных индексов
*
* @param [int] $accounts Массив аккаунтов
* @param bool $init Параметр обозначающий изменение только для тех у кого ранее идентификатор задан не был (без перезаписи)
*
* @return int Количество успешно обработанных аккаунтов
*/
public static function generateIndexes(array $accounts, bool $init = true): int
{
// Инициализация
$amount = 0;
// Функция для конвертации цифр в буквы
$int_to_string = function (int $target): string {
$alphabet = ['А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ё', 'Ж', 'З', 'И', 'Й', 'К', 'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф', 'Х', 'Ц', 'Ч', 'Ш', 'Щ', 'Ъ', 'Ы', 'Ь', 'Э', 'Ю', 'Я'];
return $alphabet[$target];
};
foreach ($accounts as $account) {
// Перебор запрошенных аккаунтов
if (($init && empty($account->indx)) || !$init) {
// Запись только тех аккаунтов у кого не инициализирован индекс или если указано отсутствие проверки
// Повтор генерации
regenerate:
// Генерация
$account->indx = $int_to_string(random_int(0, 32)) . $int_to_string(random_int(0, 32)) . $int_to_string(random_int(0, 32));
// Запись
if ($account->save()) {
// Аккаунт сохранён (в базе данных это поле проверяется на уникальность)
if (yii::$app->getRequest()->isConsoleRequest) {
// Вызов из терминала
echo "Удалось сохранить аккаунт с псевдоанонимным идентификатором $account->indx" . PHP_EOL;
}
// Запись операции в счётчик
$amount++;
} else {
// Аккаунт не сохранен (подразумевается несовпадение идентификаторов)
if (yii::$app->getRequest()->isConsoleRequest) {
// Вызов из терминала
echo "Не удалось сохранить аккаунт с псевдоанонимным идентификатором $account->indx" . PHP_EOL;
}
goto regenerate;
}
}
}
return $amount;
}
}

View File

@ -11,6 +11,8 @@ use app\models\traits\SearchByEdge;
use carono\exchange1c\controllers\ApiController;
use carono\exchange1c\interfaces\DocumentInterface;
use app\models\connection\Dellin;
use Exception;
/**
@ -45,6 +47,7 @@ class Order extends Document implements DocumentInterface
parent::attributes(),
[
'ocid',
'stts',
'sync'
]
);
@ -59,6 +62,7 @@ class Order extends Document implements DocumentInterface
parent::attributeLabels(),
[
'ocid' => 'Идентификатор 1C',
'stts' => 'Статус',
'sync' => 'Статус синхронизации с 1C'
]
);
@ -92,7 +96,7 @@ class Order extends Document implements DocumentInterface
public function connect(Account $account): ?AccountEdgeOrder
{
// Запись ребра: АККАУНТ -> ЗАКАЗ
return AccountEdgeOrder::write($account->id, $this->readId(), 'current') ?? throw new Exception('Не удалось инициализировать ребро: АККАУНТ -> ЗАКАЗ');
return AccountEdgeOrder::write($account->readId(), $this->readId(), 'current') ?? throw new Exception('Не удалось инициализировать ребро: АККАУНТ -> ЗАКАЗ');
}
/**
@ -110,7 +114,7 @@ class Order extends Document implements DocumentInterface
public function writeSupply(Supply|string|array $supply, Account $trgt = null): int
{
// Инициализация
$trgt ?? $trgt = yii::$app->user ?? throw new Exception('Не удалось инициализировать заказчика');
$trgt ?? $trgt = yii::$app->user->identity ?? throw new Exception('Не удалось инициализировать заказчика');
if ($supply instanceof Supply) {
// Передана инстанция класса поставки или второй элемент массива не является числом
@ -235,7 +239,7 @@ class Order extends Document implements DocumentInterface
public static function search(Account $account = null, string $type = 'current', int $limit = 1, int $page = 1, string $select = null): self|array|null
{
// Инициализация
$account or $account = yii::$app->user ?? throw new Exception('Не удалось инициализировать пользователя');
$account or $account = yii::$app->user->identity ?? throw new Exception('Не удалось инициализировать пользователя');
// Генерация сдвига по запрашиваемым данным (пагинация)
$offset = $limit * ($page - 1);
@ -255,7 +259,7 @@ class Order extends Document implements DocumentInterface
to: 'order',
subquery_where: [
[
'account._id' => $account->id
'account._id' => $account->readId()
],
$where_type
],
@ -281,8 +285,8 @@ class Order extends Document implements DocumentInterface
// Генерация сдвига по запрашиваемым данным (пагинация)
$offset = $limit * ($page - 1);
// Поиск рёбер: ЗАКАЗ -> ПОСТАВКА
$supplies = Supply::searchByEdge(
// Поиск по рёбрам: ЗАКАЗ -> ПОСТАВКА
$connections = Supply::searchByEdge(
from: 'order',
to: 'supply',
edge: 'order_edge_supply',
@ -295,21 +299,22 @@ class Order extends Document implements DocumentInterface
where: 'edge._to == supply._id',
limit: $limit,
offset: $offset,
direction: 'INBOUND'
direction: 'INBOUND',
select: '{supply, order_edge_supply}'
);
// Инициализация реестра дубликатов
$registry = [];
// Подсчёт и перестройка массива для очистки от дубликатов
foreach ($supplies as $key => &$supply) {
foreach ($connections as $key => &$connection) {
// Перебор поставок
if (in_array($supply->catn, $registry)) {
if (in_array($connection['supply']['catn'], $registry)) {
// Если данная поставка найдена в реестре
// Удаление
unset($supplies[$key]);
unset($connections[$key]);
// Пропуск итерации
continue;
@ -319,47 +324,56 @@ class Order extends Document implements DocumentInterface
$amount = 0;
// Повторный перебор для поиска дубликатов
foreach ($supplies as &$supply4check) {
if ($supply == $supply4check) {
foreach ($connections as &$connection_for_check) {
if ($connection == $connection_for_check) {
// Найден дубликат
// Постинкрементация счётчика
$amount++;
// Запись в реестр
$registry[] = $supply4check->catn;
$registry[] = $connection_for_check['supply']['catn'];
}
}
// Запись количества для заказа
$supply->amnt = $amount;
$connection['amount'] = $amount;
}
// Поиск стоимости для каждой поставки
foreach ($supplies as $key => &$supply) {
// Инициализация дополнительных данных
foreach ($connections as $key => &$connection) {
// Перебор поставок
// Чтение стоимости
$cost = $supply->readCost();
$cost = Supply::readCostById($connection['supply']['_id']);
if ($cost < 1) {
// Если стоимость равна нулю (явная ошибка)
if (empty($cost) || $cost['ЦенаЗаЕдиницу'] < 1) {
// Если стоимость не найдена или равна нулю (явная ошибка)
// Удаление из базы данных
$this->deleteSupply($supply->readId());
$this->deleteSupply($connection['supply']->readId());
// Удаление из списка
unset($supplies[$key]);
unset($connections[$key]);
// Пропуск итерации
continue;
}
// Запись цены
$supply->cost = $cost['ЦенаЗаЕдиницу'] . ' ' . $cost['Валюта'];
// Поиск ребра до аккаунта
$connection['account'] = Supply::searchAccountById($connection['supply']['_id']);
// Инициализация доставки
$connection['delivery'] = Dellin::calcDelivery($connection['account']['opts']['delivery_from_city'] ?? $settings['delivery_from_city_default'] ?? '2700000100000000000000000', yii::$app->user->identity->opts['delivery_to_city'] ?? $settings['delivery_to_city_default'] ?? '2700000100000000000000000')['terminals_standard'];
// Запись цены (цена поставки + цена доставки + наша наценка)
$connection['cost'] = ($cost['ЦенаЗаЕдиницу'] ?? $connection['supply']->onec['Цены']['Цена']['ЦенаЗаЕдиницу']) + ($connection['delivery']['price'] ?? 0) + ($settings['increase'] ?? 0);
// Запись валюты
$connection['currency'] = $cost['Валюта'];
}
return $supplies;
return $connections;
}
/**

View File

@ -6,8 +6,57 @@ namespace app\models;
class OrderEdgeSupply extends Edge
{
/**
* Имя коллекции
*/
public static function collectionName(): string
{
return 'order_edge_supply';
}
/**
* Свойства
*/
public function attributes(): array
{
return array_merge(
parent::attributes(),
[
'comm'
]
);
}
/**
* Метки свойств
*/
public function attributeLabels(): array
{
return array_merge(
parent::attributeLabels(),
[
'comm' => 'Комментарий к заказу'
]
);
}
/**
* Поиск поставки по артикулу
*
* @param string $catn Артикул
* @param int $limit Максимальное количество
*
* @return array Поставки
*/
public static function searchBySupplyCatn(string $catn, int $limit = 10): array
{
if ($supply = Supply::searchByCatn($catn, 1)) {
// Поставка найдена
return self::find()->where(['_to' => $supply->readId()])->limit($limit)->all();
}
return [];
}
}

View File

@ -565,14 +565,31 @@ class Supply extends Product implements ProductInterface, OfferInterface
/**
* Прочитать стоимость
*
* @param Product|null $product Товар для поиска по вершинам
*
* @return array|null Данные о ценах
*/
public function readCost(Product $product = null): array
public function readCost(Product $product = null): ?array
{
return static::readCostById($this->readId(), $product);
}
/**
* Прочитать стоимость по идентификатору поставки
*
* @param string $_id Идентификатор поставки
* @param Product|null $product Товар для поиска по вершинам
*
* @return array|null Данные о ценах
*/
public static function readCostById(string $_id, Product $product = null): ?array
{
if (isset($product)) {
return SupplyEdgeProduct::searchByVertex($this->readId(), $product->readId(), type: 'connect', limit: 1)['onec']['Цены']['Цена'];
return SupplyEdgeProduct::searchByVertex($_id, $product->readId(), type: 'connect', limit: 1)['onec']['Цены']['Цена'];
}
return SupplyEdgeProduct::search($this->readId(), type: 'connect', limit: 1)['onec']['Цены']['Цена'];
return SupplyEdgeProduct::search($_id, type: 'connect', limit: 1)['onec']['Цены']['Цена'];
}
/**
@ -582,7 +599,7 @@ class Supply extends Product implements ProductInterface, OfferInterface
*/
public function searchAccount(): ?array
{
return static::searchAccountById($this->_id);
return static::searchAccountById($this->readId());
}
/**

View File

@ -55,6 +55,76 @@ class Dellin extends Model
// });
// }
/**
* Рассчет доставки (расширенный)
*
* @param string $from Номер КЛАДР
* @param string $to Номер КЛАДР
* @param int $weight Вес (кг)
* @param int $x Ширина (см)
* @param int $y Высота (см)
* @param int $z Длинна (см)
*
* @return string
*/
public static function calcDeliveryAdvanced(string $from, string $to, int $weight, int $x, int $y, int $z): array
{
return self::handle(function () use ($from, $to, $weight, $x, $y, $z) {
// Всё готово к работе
// Рассчёт типа доставки
if (
$weight <= 10
&& (($x <= 5.4 && $y <= 3.9 && $z <= 3.9)
|| ($x <= 3.9 && $y <= 5.4 && $z <= 3.9)
|| ($x <= 3.9 && $y <= 3.9 && $z <= 5.4))
&& $x * $y * $z <= 1000000
) {
// Доставка категории "small"
$type = 'small';
} else {
// Доставка категории "auto"
$type = 'auto';
}
// Запрос
$request = self::$browser->post('/v1/micro_calc.json', [
'json' => [
'appkey' => yii::$app->params['dellin']['key'],
'sessionID' => self::$session,
'delivery' => [
'deliveryType' => [
'type' => $type
],
'arrival' => [
'variant' => 'terminal',
]
]
]
]);
if ($request->getStatusCode() === 200) {
// Запрос прошел успешно
// Инициализация
$response = json_decode((string) $request->getBody(), true);
if ($response['metadata']['status'] === 200) {
// Со стороны ДеловыеЛинии ошибок нет
return $response['data'];
}
throw new Exception('На стороне сервера ДеловыеЛинии какие-то проблемы, либо отправлен неверный запрос', 500);
}
throw new Exception('Не удалось запросить рассчёт доставки у ДеловыеЛинии', 500);
});
}
/**
* Рассчет доставки
*
@ -103,9 +173,9 @@ class Dellin extends Model
}
/**
* Синхронизация городов
* Импорт городов
*
* @return array|null Найденные города
* @return array|null Сохранённые города
*/
public static function importCities(): ?int
{
@ -137,94 +207,217 @@ class Dellin extends Model
'sink' => $file = $dir . time() . '.csv'
]);
// Проверка хеша
if ($hash_target !== $hash_received = md5_file($file)) {
// Удалось пройти проверку на хеши файлов
// Проверка хеша (оказалось это хеш запроса, бесполезный)
// if ($hash_target === $hash_received = md5_file($file)) {
// Удалось пройти проверку на хеши файлов
// Инициализация (чтение файла)
$file = fopen($file, "r");
$first_raw_block = true;
// Инициализация (чтение файла)
$file = fopen($file, "r");
$first_raw_block = true;
while ($row = fgets($file, 4096)) {
// Перебор строк
while ($row = fgets($file, 4096)) {
// Перебор строк
if ($first_raw_block) {
// Сработала защита от чтения первой строки файла (указываются названия колонок)
if ($first_raw_block) {
// Сработала защита от чтения первой строки файла (указываются названия колонок)
// Отключение
$first_raw_block = false;
// Отключение
$first_raw_block = false;
// Пропуск цикла
continue;
}
// Инициализация
$data = explode(',', $row, 4);
$amount = 0;
// Очистка
array_walk($data, fn (&$value) => $value = trim($value, '"'));
if ($city = City::searchByDellinId($data[0])) {
// Удалось найти город в базе данных
$after_import_log = function () use ($city): void {
// Запись в журнал
$city->journal('update');
if (yii::$app->getRequest()->isConsoleRequest) {
// Вызов из терминала
echo 'Удалось перезаписать город: ' . $city->name . PHP_EOL;
}
};
} else {
// Не удалось найти город в базе данных
$city = new City();
$after_import_log = function () use ($city): void {
if (yii::$app->getRequest()->isConsoleRequest) {
// Вызов из терминала
echo 'Удалось записать город: ' . $city->name . PHP_EOL;
}
};
}
// Запись
$city->indx = $data[0];
$city->name = $data[1];
$city->code = $data[2];
$city->term = (bool) $data[3];
// Отправка в базу данных
if ($city->save()) {
// Удалось сохранить в базе данных
// Запись в журнал
$after_import_log();
// Постинкрементация счётчика
$amount++;
continue;
} else {
// Не удалось сохранить в базе данных
throw new Exception('Не удалось сохранить город ' . $data[1] . ' в базу данных', 500);
}
// Пропуск цикла
continue;
}
// Деинициализация
fclose($file);
// Инициализация
$data = explode(',', $row, 4);
$amount = 0;
return $amount;
} else {
// Не удалось пройти проверку на соответствие хешей файлов
// Очистка
array_walk($data, fn (&$value) => $value = trim($value, '"'));
throw new Exception('Хеши файлов не совпадают. Должен быть: "' . $hash_target . '", получен: "' . $hash_received . '"', 500);
if ($city = City::searchByDellinId($data[0])) {
// Удалось найти город в базе данных
$after_import_log = function () use ($city): void {
// Запись в журнал
$city->journal('update');
if (yii::$app->getRequest()->isConsoleRequest) {
// Вызов из терминала
echo 'Удалось перезаписать город: ' . $city->name . PHP_EOL;
}
};
} else {
// Не удалось найти город в базе данных
$city = new City();
$after_import_log = function () use ($city): void {
if (yii::$app->getRequest()->isConsoleRequest) {
// Вызов из терминала
echo 'Удалось записать город: ' . $city->name . PHP_EOL;
}
};
}
// Запись
$city->indx = $data[0];
$city->name = $data[1];
$city->code = $data[2];
$city->term = (bool) $data[3];
// Отправка в базу данных
if ($city->save()) {
// Удалось сохранить в базе данных
// Запись в журнал
$after_import_log();
// Постинкрементация счётчика
$amount++;
continue;
} else {
// Не удалось сохранить в базе данных
throw new Exception('Не удалось сохранить город ' . $data[1] . ' в базу данных', 500);
}
}
// Деинициализация
fclose($file);
return $amount;
// } else {
// // Не удалось пройти проверку на соответствие хешей файлов
// throw new Exception('Хеши файлов не совпадают. Должен быть: "' . $hash_target . '", получен: "' . $hash_received . '"', 500);
// }
}
throw new Exception('Не удалось синхронизировать данные городов с ДеловыеЛинии', 500);
});
}
/**
* Импорт терминалов
*
* @return array|null Сохранённые терминалы
*/
public static function importTerminals(): ?int
{
return self::handle(function () {
// Всё готово к работе
// Запрос ссылки на файл с городами, возвращает ['hash' => string, 'url' => string]
$request = self::$browser->post('/v3/public/terminals.json', [
'json' => [
'appkey' => yii::$app->params['dellin']['key'],
]
]);
if ($request->getStatusCode() === 200) {
// Запрос прошел успешно
// Инициализация
$response = json_decode((string) $request->getBody(), true);
$dir = YII_PATH_PUBLIC . '/../assets/import/' . date('Y-m-d', time()) . '/dellin/terminals/' . (yii::$app->user->identity->_key ?? 'system') . '/';
if (!file_exists($dir)) {
// Директории не существует
mkdir($dir, 0775, true);
}
$request = self::$browser->get($response['url'], [
'sink' => $file = $dir . time() . '.json'
]);
die;
// Инициализация (чтение файла)
$file = fopen($file, "r");
$first_raw_block = true;
while ($row = fgets($file, 4096)) {
// Перебор строк
if ($first_raw_block) {
// Сработала защита от чтения первой строки файла (указываются названия колонок)
// Отключение
$first_raw_block = false;
// Пропуск цикла
continue;
}
// Инициализация
$data = explode(',', $row, 4);
$amount = 0;
// Очистка
array_walk($data, fn (&$value) => $value = trim($value, '"'));
if ($city = Terminal::searchByDellinId($data[0])) {
// Удалось найти город в базе данных
$after_import_log = function () use ($city): void {
// Запись в журнал
$city->journal('update');
if (yii::$app->getRequest()->isConsoleRequest) {
// Вызов из терминала
echo 'Удалось перезаписать город: ' . $city->name . PHP_EOL;
}
};
} else {
// Не удалось найти город в базе данных
$terminal = new Terminal();
$after_import_log = function () use ($terminal): void {
if (yii::$app->getRequest()->isConsoleRequest) {
// Вызов из терминала
echo 'Удалось записать город: ' . $terminal->name . PHP_EOL;
}
};
}
// Запись
$terminal->indx = $data[0];
$terminal->name = $data[1];
$terminal->code = $data[2];
$terminal->term = (bool) $data[3];
// Отправка в базу данных
if ($terminal->save()) {
// Удалось сохранить в базе данных
// Запись в журнал
$after_import_log();
// Постинкрементация счётчика
$amount++;
continue;
} else {
// Не удалось сохранить в базе данных
throw new Exception('Не удалось сохранить город ' . $data[1] . ' в базу данных', 500);
}
}
// Деинициализация
fclose($file);
return $amount;
}
throw new Exception('Не удалось синхронизировать данные городов с ДеловыеЛинии', 500);

View File

@ -69,6 +69,7 @@ trait SearchByEdge
$request = $query
->foreach($foreach)
->where($where)
->limit($limit)
->select($select ?? $to);
// Режим проверки
@ -84,6 +85,8 @@ trait SearchByEdge
return $handle($request);
} else if (isset($select)) {
// Указан выбор свойств
$response = $request->createCommand()->execute()->getAll();
if ($asArray) {
@ -103,9 +106,7 @@ trait SearchByEdge
return $response;
} else {
if ($limit === 1) {
return $request->one();
}
// Иначе просто запросить для ActiveQuery
return $request->all();
}

View File

@ -1,3 +1,11 @@
<?php
use yii;
use app\models\connection\Dellin;
?>
<link href="/css/pages/cart.css" rel="stylesheet">
<div id="page_cart" class="container mb-auto py-3">
@ -6,7 +14,7 @@
<div class="col mb-4 list rounded overflow-hidden">
<div class="row py-2">
<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 class="col-2">
<span>Артикул</span>
@ -25,27 +33,48 @@
</div>
</div>
<?php
if (isset($supplies) && !empty($supplies)) {
foreach ($supplies as $supply) {
if (isset($connections) && !empty($connections)) {
foreach ($connections as $connection) {
// Перебор поставок
// Инициализация доставки
$delivery = $connection['delivery']['period_to'];
// Инициализация цены
$cost = $connection['cost'] . ' ' . $connection['currency'];
// Инициализация комментария
$comment = $connection['order_edge_supply'][0]['comm'] ?? 'Комментарий к заказу';
echo <<<HTML
<div class="row py-2 cart_list_target">
<div class="pl-3 mr-1">
<input id="cart_list_checkbox_$supply->catn" type="checkbox" onchange="return cart_list_checkbox(this);"/>
</div>
<div class="col-2">
$supply->catn
</div>
<div class="col-4">
$supply->dscr
</div>
<div class="col-1 ml-auto">
<input id="cart_list_amnt_$supply->catn" class="form-control text-center" type="text" value="$supply->amnt" onchange="return cart_list_amount_update('$supply->catn', this)" aria-invalid="false">
</div>
<div class="col-2 text-right">
$supply->time
</div>
<div class="col-2 mr-3 text-right">
$supply->cost
<div class="col">
<div class="row">
<div class="pl-3 my-auto mr-1">
<input id="cart_list_checkbox_{$connection['supply']['catn']}" type="checkbox" onchange="return cart_list_checkbox(this);"/>
</div>
<div class="col-2 my-auto">
{$connection['supply']['catn']}
</div>
<div class="col-4 my-auto">
{$connection['supply']['dscr']}
</div>
<div class="col-1 my-auto ml-auto">
<input id="cart_list_amnt_{$connection['supply']['catn']}" class="form-control text-center" type="text" value="{$connection['amount']}" onchange="return cart_list_amount_update('{$connection['supply']['catn']}', this)" aria-invalid="false">
</div>
<div class="col-2 my-auto text-right">
$delivery дней
</div>
<div class="col-2 my-auto mr-3 text-right">
$cost
</div>
</div>
<div class="dropdown-divider"></div>
<div class="row mb-1">
<div class="col-12">
<p id="cart_list_comment_{$connection['supply']['catn']}" class="mt-0 ml-0 text-break pointer-event" role="button" onclick="return cart_list_comment_edit('{$connection['supply']['catn']}', this);">$comment</p>
</div>
</div>
</div>
</div>
HTML;

View File

@ -1,11 +1,3 @@
<?php
use yii;
use app\models\connection\Dellin;
?>
<link href="/css/pages/search.css" rel="stylesheet">
<div id="page_search" class="container flex-grow-1 d-flex">
@ -75,7 +67,7 @@ use app\models\connection\Dellin;
$catn = $supply['catn'] ?? $supply['onec']['Артикул'];
// Инициализация цены
$price_raw = $supply['prce'] ?? $supply['onec']['Цены']['Цена']['ЦенаЗаЕдиницу'];
$price_raw = $supply['prce'];
$price = $price_raw . ' ' . $supply['onec']['Цены']['Цена']['Валюта'];
// Инициализация количества
@ -88,9 +80,10 @@ use app\models\connection\Dellin;
}
// Инициализация доставки
$delivery = Dellin::calcDelivery($supply['account']['opts']['delivery_from_city'], yii::$app->user->identity->opts['delivery_to_city'])['terminals_standard'];
$delivery_max = $delivery['period_to'] ?? 'Неизвестно';
$delivery_price = $delivery['price'] ?? 'Неизвестно';
$delivery = $supply['delivery']['max'];
// Инициализация индекса аккаунта
$index = $supply['account']['indx'] ?? 'Неизвестен';
if ($amount_raw < 1 || $price_raw < 1) {
// Нет в наличии или цена 0 рублей
@ -109,17 +102,19 @@ use app\models\connection\Dellin;
$supplies_html .= <<<HTML
<div class="row my-auto m-0 h-100 text-right">
<small class="col-2 my-auto ml-auto">
<small class="col-1 my-auto ml-auto pl-2 pr-0">
$index
</small>
<small class="col-1 my-auto pl-2 pr-0">
$amount
</small>
<small class="col-2 my-auto ml-1">
$delivery_max дней<br/>
$delivery_price рублей
<small class="col-1 my-auto pl-2 pr-0 mr-2">
$delivery дн
</small>
<b class="col-2 my-auto">
$price
</b>
<a class="col-1 h-100 text-dark d-flex button_white rounded" title="Добавить $catn в корзину" href="/cart" role="button" onclick="return cart_write('$catn');">
<a class="col-1 h-100 text-dark d-flex button_white rounded" title="Добавить $catn в корзину" role="button" onclick="return cart_write('$catn');">
<i class="fas fa-cart-arrow-down pr-1 m-auto"></i>
</a>
</div>

View File

@ -2,11 +2,12 @@
background-color: #fff;
}
#page_cart article .list .row:nth-child(2n+1) {
#page_cart article .list > .row:nth-child(2n+1) {
background-color: #f7f6f9;
}
#page_cart article .list .row:first-child {
#page_cart article .list > .row:first-child {
/* border-bottom: 2px solid #bcc2d5; */
background-color: #dbdde3;
}

View File

@ -188,6 +188,61 @@ function cart_cost_calculate() {
document.getElementById('cart_cost').innerHTML = costs;
}
/**
* Изменить количество товара в корзине
*/
function cart_list_comment_edit(catn, element) {
if (catn !== null && catn !== undefined && element !== null && element !== undefined) {
element.innerHTML = '<textarea class="form-control" cols="50" rows="5" onchange = "return cart_list_comment_save(\'' + catn + '\', this.parentElement)">' + element.innerText + '</textarea>';
element.removeAttribute('onclick');
return false;
}
return true;
}
function cart_list_comment_save(catn, element) {
if (catn !== null && catn !== undefined && element !== null && element !== undefined) {
// Инициализация
let text = element.children[0].value;
// Обновление заголовка (предзагрузка)
element.innerHTML = text;
// Запись аттрибута (предзагрузка)
element.setAttribute('onclick', 'return cart_list_comment_edit(\'' + catn + '\', this);');
$.ajax({
url: '/cart/' + catn + '/edit/comm',
type: 'post',
dataType: 'json',
data: {
'_csrf': yii.getCsrfToken(),
'text': text
},
success: function (data, status) {
// Заголовок
if (data.comm !== undefined && element !== null && element !== undefined) {
// Обновление заголовка
element.innerHTML = data.comm;
// Запись аттрибута
element.setAttribute('onclick', 'return cart_list_comment_edit(\'' + catn + '\', this);');
};
cart_response_success(data, status);
},
error: cart_response_error
});
return false;
}
return true;
}
cart_cost_calculate();
function cart_response(data, status) {