Подключение к ДеловыеЛинии

This commit is contained in:
Arsen Mirzaev Tatyano-Muradovich 2021-04-19 07:11:38 +10:00
parent d4bc3e2263
commit 7865cac5d4
18 changed files with 676 additions and 170 deletions

6
composer.lock generated
View File

@ -32,7 +32,7 @@
"version": "3.3.11",
"source": {
"type": "git",
"url": "https://github.com/RobinHerbots/Inputmask.git",
"url": "git@github.com:RobinHerbots/Inputmask.git",
"reference": "5e670ad62f50c738388d4dcec78d2888505ad77b"
},
"dist": {
@ -1016,7 +1016,7 @@
"source": {
"type": "git",
"url": "https://git.hood.su/mirzaev/yii2/arangodb",
"reference": "bb7b1b8f8e26c409f9c113a6bd459e3274e94599"
"reference": "cbc26916ea54bb767306e4c2750e710b08eecc21"
},
"require": {
"php": "^8.0.0",
@ -1058,7 +1058,7 @@
"ArangoDb",
"yii2"
],
"time": "2021-04-10T16:39:51+00:00"
"time": "2021-04-11T19:27:43+00:00"
},
{
"name": "mirzaev/yii2-arangodb-sessions",

View File

@ -0,0 +1,20 @@
<?php
namespace app\commands;
use yii\console\Controller;
use yii\console\ExitCode;
use app\models\connection\Dellin;
class DellinController extends Controller
{
public function actionCitiesImport()
{
if (Dellin::importCities()) {
return ExitCode::OK;
}
return ExitCode::UNSPECIFIED_ERROR;
}
}

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 {
$search = function (bool $new = false, bool $count = false) use ($model, $account, $type, $let, $limit): array|int|null {
if ($count) {
// Запрошен подсчёт непрочитанных уведомлений

View File

@ -245,6 +245,57 @@ class ProductController extends Controller
}
}
public function actionEditWght(string $catn): array|string|null
{
// Инициализация
$return = [
'_csrf' => yii::$app->request->getCsrfToken()
];
if (is_null($catn)) {
// Не получен артикул
yii::$app->response->statusCode = 500;
goto end;
}
if ($product = Product::searchByCatn($catn)) {
// Товар найден
// Инициализация
$text = yii::$app->request->post('text') ?? yii::$app->request->get('text') ?? '0';
$text or $text = '0';
$product->wght = $text;
if ($product->save()) {
// Товар обновлён
$return['wght'] = $text;
}
}
/**
* Конец алгоритма
*/
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('/');
}
}
public function actionWriteImage(string $catn): array|string|null
{
// Инициализация

View File

@ -91,10 +91,6 @@ class ProfileController extends Controller
*/
public function actionIndex(): string|array
{
$browser = new Dellin();
$browser->syncCities();
// Инициализация
$model = yii::$app->user->identity;
$panel = yii::$app->request->post('panel') ?? yii::$app->request->get('panel');
@ -122,8 +118,14 @@ class ProfileController extends Controller
}
// Инициализация
$account_sections_city_list = null;
$import_sections_oem_list = $model->genListOem(Supply::searchByAccount(select: 'supply.onec["ЗначенияСвойств"]'));
$delivery_from_city_list = $model->genListCitiesFrom();
$delivery_to_city_list = $model->genListCitiesTo();
$import_oem_list = $model->genListOem(Supply::searchByAccount(select: 'supply.onec["ЗначенияСвойств"]'));
// Сортировка по алфавиту
asort($delivery_from_city_list);
asort($delivery_to_city_list);
asort($import_oem_list);
if (yii::$app->request->isPost) {
// POST-запрос
@ -134,8 +136,9 @@ class ProfileController extends Controller
'main' => $this->renderPartial('index', compact(
'model',
'sidebar',
'account_sections_city_list',
'import_sections_oem_list',
'delivery_from_city_list',
'delivery_to_city_list',
'import_oem_list',
'panel'
)),
'redirect' => '/profile',
@ -146,8 +149,9 @@ class ProfileController extends Controller
return $this->render('index', compact(
'model',
'sidebar',
'account_sections_city_list',
'import_sections_oem_list',
'delivery_from_city_list',
'delivery_to_city_list',
'import_oem_list',
'panel'
));
}
@ -363,7 +367,6 @@ class ProfileController extends Controller
*/
public function actionImport()
{
var_dump($_FILES);
// Инициализация
$model = new Supply(yii::$app->request->post('Supply') ?? yii::$app->request->get('Supply'));
$model->scenario = $model::SCENARIO_IMPORT_EXCEL;

View File

@ -166,6 +166,11 @@ class SearchController extends Controller
// Инициализация
$row['overload'] = true;
}
// Поиск аккаунтов владельцев поставок
foreach ($row['supplies'] as &$edge) {
$edge['account'] = Supply::searchAccountById($edge['_from']);
}
}
// Запись ответа

View File

@ -273,44 +273,113 @@ class Account extends Document implements IdentityInterface, PartnerInterface
// Инициализация
$list = [];
// Перебор свойств поставок
foreach ($supplies as $supply) {
// Инициализация
$id = $supply['ЗначенияСвойства']['Ид'];
if (in_array($id, $list, true)) {
// Перебор поставок
if (in_array($supply['ЗначенияСвойства']['Ид'], $list, true)) {
// Если встретился дубликат (исполняется очень часто)
continue;
}
// Генерация
!isset($supply['ЗначенияСвойства']['Наименование']) or $list[$id] = $supply['ЗначенияСвойства']['Наименование'];
empty($supply['ЗначенияСвойства']['Наименование']) or $list[$supply['ЗначенияСвойства']['Ид']] = $supply['ЗначенияСвойства']['Наименование'];
}
// Инициализация текущего значения параметра в начале массива
if (isset($this->opts['import_sections_oem'])) {
// Параметр 'import_sections_oem' найден в настройках аккаунта
return $this->syncListWithSettings($list, 'import_supplies_oem');
}
if (isset($list[$this->opts['import_sections_oem']])) {
/**
* Генерация списка городов из ДеловыеЛинии для отправителя
*
* Актуальное (выбранное, активное) значение записывается первым
*
* @param array Необработанный список городов
*/
public function genListCitiesFrom(): array
{
// Инициализация
$list = [];
$cities = City::readAll();
foreach ($cities as $city) {
// Перебор городов
if (in_array($city->name, $list, true)) {
// Если встретился дубликат (исполняется очень часто)
continue;
}
// Запись
empty($city->name) or $list[$city->code] = $city->name;
}
return $this->syncListWithSettings($list, 'delivery_from_city');
}
/**
* Генерация списка городов из ДеловыеЛинии для получателя
*
* Актуальное (выбранное, активное) значение записывается первым
*
* @param array Необработанный список городов
*/
public function genListCitiesTo(): array
{
// Инициализация
$list = [];
$cities = City::readAll();
foreach ($cities as $city) {
// Перебор городов
if (in_array($city->name, $list, true)) {
// Если встретился дубликат (исполняется очень часто)
continue;
}
// Запись
empty($city->name) or $list[$city->code] = $city->name;
}
return $this->syncListWithSettings($list, 'delivery_to_city');
}
/**
* Синхронизация списка вариантов параметра с текущим значением из настроек
*
* @param array &$list Список
* @param string $var Название параметра
*
* @return array Сортированный список
*/
protected function syncListWithSettings(array &$list, string $var): array {
// Инициализация текущего значения параметра в начале массива
if (isset($this->opts[$var])) {
// Параметр найден в настройках аккаунта
if (isset($list[$this->opts[$var]])) {
// Найдено совпадение сохранённого параметра с полученным списком из поставок
// Буфер для сохранения параметра
$buffer = $list[$this->opts['import_sections_oem']];
$buffer = $list[$this->opts[$var]];
// Удаление параметра
unset($list[$this->opts['import_sections_oem']]);
unset($list[$this->opts[$var]]);
// Сохранение параметра в начале массива
$list = array_merge([$this->opts['import_sections_oem'] => $buffer], $list);
$list = array_merge([$this->opts[$var] => $buffer], $list);
} else {
// Совпадение не найдено
// Сохранение параметра из данных аккаунта в начале массива
$list = array_merge([$this->opts['import_sections_oem'] => $this->opts['import_sections_oem']], $list);
$list = array_merge([$this->opts[$var] => $this->opts[$var]], $list);
}
} else {
// Параметр 'import_sections_oem' не найден в настройках аккаунта
// Параметр $var не найден в настройках аккаунта
// Сохранение параметра из данных аккаунта в начале массива
$list = array_merge(['Выберите'], $list);

View File

@ -16,6 +16,7 @@ class City extends Document
return array_merge(
parent::attributes(),
[
'indx',
'name',
'code',
'term'
@ -28,10 +29,16 @@ class City extends Document
return array_merge(
parent::rules(),
[
[
'indx',
'string'
],
[
[
'indx',
'name',
'code'
'code',
'term'
],
'required',
'message' => 'Заполните поле: {attribute}'
@ -56,10 +63,19 @@ class City extends Document
return array_merge(
parent::attributeLabels(),
[
'indx' => 'Идентификатор в ДеловыеЛинии',
'name' => 'Название',
'code' => 'Код КЛАДР',
'term' => 'Указатель наличия терминала в городе'
]
);
}
/**
* Поиск по идентификатору в ДеловыеЛинии
*/
public static function searchByDellinId(string $indx): ?static
{
return static::findOne(['indx' => $indx]);
}
}

View File

@ -70,7 +70,7 @@ abstract class Document extends ActiveRecord
$this->jrnl = array_merge(
[[
'date' => time(),
'account' => yii::$app->user->id,
'account' => yii::$app->user->id ?? 'system',
'action' => 'create'
]],
$this->jrnl ?? []
@ -106,7 +106,7 @@ abstract class Document extends ActiveRecord
[array_merge(
[
'date' => $time = time(),
'account' => yii::$app->user->id,
'account' => yii::$app->user->id ?? 'system',
'action' => $action
],
...$data

View File

@ -80,6 +80,7 @@ class Product extends Document
'dscr',
'prod',
'dmns',
'wght',
'imgs',
'time'
]
@ -99,6 +100,7 @@ class Product extends Document
'dscr' => 'Описание (dscr)',
'prod' => 'Производитель (prod)',
'dmns' => 'Габариты (dmns)',
'wght' => 'Вес (wght)',
'imgs' => 'Изображения (imgs)',
'time' => 'Срок доставки (time)',
'file_excel' => 'Документ (file_excel)',
@ -301,7 +303,7 @@ class Product extends Document
// Перебор файлов
// Инициализация
$dir = '../assets/import/' . date('Y_m_d#H-i', time()) . '/excel/';
$dir = YII_PATH_PUBLIC . '../assets/import/' . date('Y-m-d', time()) . '/excel/' . (yii::$app->user->identity->_key ?? 'system') . '/' . time() . '/';
// Сохранение на диск
if (!file_exists($dir)) {

View File

@ -15,7 +15,7 @@ use carono\exchange1c\interfaces\OfferInterface;
use carono\exchange1c\interfaces\ProductInterface;
use carono\exchange1c\controllers\ApiController;
use exception;
use Exception;
/**
* Поставка (выгрузка товаров от поставщиков)
@ -416,7 +416,8 @@ class Supply extends Product implements ProductInterface, OfferInterface
* @param mixed|null $context
* @return array
*/
public function getExportFields1c($context = null) {
public function getExportFields1c($context = null)
{
return $this->onec;
}
@ -498,7 +499,8 @@ class Supply extends Product implements ProductInterface, OfferInterface
* @param $types
* @return void
*/
public static function createPriceTypes1c($types) {
public static function createPriceTypes1c($types)
{
}
/**
@ -511,8 +513,8 @@ class Supply extends Product implements ProductInterface, OfferInterface
* @param \Zenwalker\CommerceML\Model\Simple $specification
* @return void
*/
public function setSpecification1c($specification) {
public function setSpecification1c($specification)
{
}
/**
@ -572,4 +574,43 @@ class Supply extends Product implements ProductInterface, OfferInterface
return SupplyEdgeProduct::search($this->readId(), type: 'connect', limit: 1)['onec']['Цены']['Цена'];
}
/**
* Найти аккаунт владельца
*
* @return Account|null Аккаунт владельца
*/
public function searchAccount(): ?array
{
return static::searchAccountById($this->_id);
}
/**
* Найти аккаунт владельца
*
* @param string|null $_id Идентификатор
*
* @return Account|null Аккаунт владельца
*/
public static function searchAccountById(string $_id): ?array
{
return static::searchByEdge(
from: 'account',
to: 'supply',
edge: 'account_edge_supply',
direction: 'INBOUND',
subquery_where: [
[
'account_edge_supply._from == account._id'
],
[
'account_edge_supply._to == "' . $_id . '"'
]
],
subquery_select: 'account',
where: 'account_edge_supply[0]._id != null',
limit: 1,
select: 'account_edge_supply[0]'
)[0];
}
}

View File

@ -12,61 +12,108 @@ use app\models\City;
use GuzzleHttp\Client as Guzzle;
use Exception;
use phpDocumentor\Reflection\Types\Nullable;
class Dellin extends Model
{
/**
* Инстанция браузера
*/
public Guzzle $browser;
public static Guzzle $browser;
/**
* Сессия аккаунта
*/
public string $session;
public static string $session;
public function __construct($config = [])
{
parent::__construct($config);
$this->browser = new Guzzle([
self::$browser = new Guzzle([
'base_uri' => 'https://api.dellin.ru/'
]);
$this->authorization();
self::authorization();
}
/**
* Поиск городов
*
* @return array|null Найденные города
*/
public function searchCities(): ?array
{
// $this->onReady(function () {
// // Запрос городов
// $request = $this->browser->post('/v2/public/kladr.json', [
// 'json' => [
// 'appkey' => yii::$app->params['dellin']['key'],
// /**
// * Поиск городов
// *
// * @return array|null Найденные города
// */
// public function searchCities(): ?array
// {
// $this->onReady(function () {
// // Запрос городов
// $request = $this->browser->post('/v2/public/kladr.json', [
// 'json' => [
// 'appkey' => yii::$app->params['dellin']['key'],
// ]
// ])
// });
// ]
// ])
// });
// }
/**
* Рассчет доставки
*
* @param string $from Номер КЛАДР
* @param string $to Номер КЛАДР
*
* @return string
*/
public static function calcDelivery(string $from, string $to): array
{
return self::handle(function () use ($from, $to) {
// Всё готово к работе
// Запрос
$request = self::$browser->post('/v1/micro_calc.json', [
'json' => [
'appkey' => yii::$app->params['dellin']['key'],
'sessionID' => self::$session,
'derival' => [
'city' => $from
],
'arrival' => [
'city' => $to
]
]
]);
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);
});
}
/**
* Синхронизация городов
*
* @return array|null Найденные города
*
* @todo Написать проверку по хешу
*/
public function syncCities(): ?array
public static function importCities(): ?int
{
return $this->ifReady(function () {
// Запрос ссылки на файл с городам
// Получаем hash и url ()
$request = $this->browser->post('/v1/public/cities.json', [
return self::handle(function () {
// Всё готово к работе
// Запрос ссылки на файл с городами, возвращает ['hash' => string, 'url' => string]
$request = self::$browser->post('/v1/public/cities.json', [
'json' => [
'appkey' => yii::$app->params['dellin']['key'],
]
@ -77,79 +124,187 @@ class Dellin extends Model
// Инициализация
$response = json_decode((string) $request->getBody(), true);
$hash_target = $response['hash'];
$dir = YII_PATH_PUBLIC . '/../assets/import/' . date('Y-m-d', time()) . '/dellin/cities/' . (yii::$app->user->identity->_key ?? 'system') . '/';
if (!file_exists($dir)) {
// Директории не существует
if ($response['metadata']['status'] === 200) {
// Аутентификация и авторизация пройдены успешно
// Запись сессии
return $this;
mkdir($dir, 0775, true);
}
throw new Exception('Не удалось синхронизировать данные городов с ДеловыеЛинии', $response['metadata']['status']);
$request = self::$browser->get($response['url'], [
'sink' => $file = $dir . time() . '.csv'
]);
// Проверка хеша
if ($hash_target !== $hash_received = md5_file($file)) {
// Удалось пройти проверку на хеши файлов
// Инициализация (чтение файла)
$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 = 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('Не удалось синхронизировать данные городов с ДеловыеЛинии', $request->getStatusCode);
throw new Exception('Не удалось синхронизировать данные городов с ДеловыеЛинии', 500);
});
}
/**
* Аутентификация и авторизация
*/
protected function authorization(): ?self
protected static function authorization(): bool
{
return $this->ifReady(function () {
// Аутентификация и авторизация
$request = $this->browser->post('/v3/auth/login.json', [
'json' => [
'appkey' => yii::$app->params['dellin']['key'],
'login' => yii::$app->params['dellin']['nickname'],
'password' => yii::$app->params['dellin']['password']
]
]);
// Аутентификация и авторизация
$request = self::browser()->post('/v3/auth/login.json', [
'json' => [
'appkey' => yii::$app->params['dellin']['key'],
'login' => yii::$app->params['dellin']['nickname'],
'password' => yii::$app->params['dellin']['password']
]
]);
if ($request->getStatusCode() === 200) {
// Запрос прошел успешно
if ($request->getStatusCode() === 200) {
// Запрос прошел успешно
// Инициализация
$response = json_decode((string) $request->getBody(), true);
// Инициализация
$response = json_decode((string) $request->getBody(), true);
if ($response['metadata']['status'] === 200) {
// Аутентификация и авторизация пройдены успешно
if ($response['metadata']['status'] === 200) {
// Аутентификация и авторизация пройдены успешно
// Запись сессии
$this->session = $response['data']['sessionID'];
// Запись сессии
self::$session = $response['data']['sessionID'];
return $this;
}
throw new Exception('Не удалось авторизироваться в ДеловыеЛинии', $response['metadata']['status']);
return true;
}
throw new Exception('Не удалось авторизироваться в ДеловыеЛинии', $response['metadata']['status']);
}
throw new Exception('Не удалось авторизироваться в ДеловыеЛинии', $request->getStatusCode);
});
throw new Exception('Не удалось авторизироваться в ДеловыеЛинии', $request->getStatusCode);
}
/**
* Проверка на то, что браузер готов к работе
* Инициализация и выполнение
*
* @param callable $function Код к выполнению
* @param [type] ...$vars Параметры к нему
* @param callable|null $function Код к выполнению
* @param [mixed] ...$vars Параметры к нему
*
* @return mixed Возврат из функции
*/
protected function ifReady(callable $function, ...$vars): mixed
protected static function handle(callable $function = null, mixed ...$vars): mixed
{
if (isset($this->browser)) {
// Браузер инициализирован
try {
if (self::browser() instanceof Guzzle) {
// Браузер инициализирован
return $function(...$vars);
if (self::authorization() && isset(self::$session)) {
// Аутентифицирован и авторизован
return $function(...$vars);
} else {
throw new Exception('Аккаунт не авторизирован', 401);
}
} else {
throw new Exception('Браузер не инициализирован', 500);
}
} catch (Exception $e) {
throw new Exception($e->getMessage(), 500, $e->getPrevious());
// throw new Exception('Не удалось инициализировать инстанцию для работы с API ДеловыеЛинии', 500, $e->getPrevious());
}
}
throw new Exception('Браузер не инициализирован', 500);
/**
* Чтение или инициализация браузера
*
* @return Guzzle Инстанция
*/
protected static function browser(): Guzzle
{
return self::$browser ?? self::$browser = new Guzzle([
'base_uri' => 'https://api.dellin.ru/'
]);
}
}

View File

@ -6,13 +6,17 @@ namespace app\models\traits;
use yii;
use exception;
use ArangoDBClient\Document;
use Exception;
trait SearchByEdge
{
/**
* Поиск через связи рёбрами с аккаунтом
*
* Аргумент $asArray и его реализацию пришлось добавить чтобы не переписывать кучу кода
*
* @param string $id Идентификатор пользователя
* @param int $limit Количество
* @param int $offset Сдвиг
@ -27,12 +31,15 @@ trait SearchByEdge
int|null $offset = 0,
array $sort = ['ASC'],
string|array $subquery_where = [],
string|array $subquery_select = null,
array $foreach = [],
string|array $where = [],
array|null $let = [],
string|array $select = null,
callable|null $handle = null,
array $params = []
array $params = [],
bool $asArray = true,
bool $debug = false
): mixed {
$subquery = static::find()
->params($params)
@ -42,7 +49,7 @@ trait SearchByEdge
->where($subquery_where);
$subquery = $subquery
->select($edge ?? $from . '_edge_' . $to)
->select($subquery_select ?? $edge ?? $from . '_edge_' . $to)
->createCommand();
$query = static::find()
@ -58,12 +65,20 @@ trait SearchByEdge
$query->let(...$let);
}
// Запрос
// Инициализация
$request = $query
->foreach($foreach)
->where($where)
->select($select ?? $to);
// Режим проверки
if ($debug) {
// Запрошена проверка
return (string) $request->createCommand();
}
// Запрос
if (isset($handle)) {
// Передана функция для постобработки
@ -71,14 +86,27 @@ trait SearchByEdge
} else if (isset($select)) {
$response = $request->createCommand()->execute()->getAll();
foreach ($response as &$attribute) {
// Приведение всех свойств в массив и очистка от лишних данных
if ($asArray) {
// Передан параметр указывающий на необходимость возврата как объекта
$attribute = $attribute->getAll();
// Очистка
foreach ($response as &$attribute) {
// Приведение всех свойств в массив и очистка от лишних данных
if ($attribute instanceof Document) {
// Получена инстанция документа ArangoDB
$attribute = $attribute->getAll();
}
}
}
return $response;
} else {
if ($limit === 1) {
return $request->one();
}
return $request->all();
}
}

View File

@ -14,7 +14,7 @@ use app\models\Product;
<article class="col-12">
<div class="p-3 d-flex flex-column rounded">
<div id="product_slider" class="row px-3 profile_panel">
<div class="col-1 product_slider_preview p-0 pr-3 mb-3">
<div class="col-1 product_slider_preview p-0 pr-3 mb-3 d-flex flex-column">
<?php
// Инициализация
$covr_not_found = true;
@ -157,37 +157,40 @@ use app\models\Product;
<div class="dropdown-divider px-0 mb-2"></div>
<!-- Информация о товаре -->
<?php if (
!yii::$app->user->isGuest
&& (yii::$app->user->identity->type === 'administrator'
|| yii::$app->user->identity->type === 'moderator')
) : ?>
<section id="prod_<?= $model['catn'] ?>_info" class="col mb-1">
<p class="form-control-sm">
<b class="mr-1">Габариты:</b>
<span id="prod_<?= $model['catn'] ?>_dmns_x" class="ml-1 pointer-event" role="button" onclick="return product_panel_dimensions_edit('<?= $model['catn'] ?>', this, 'x');"><?= empty($model['dmns']['x']) ? '0' : $model['dmns']['x'] ?></span>
<span class="mr-1">см</span>
<small>x</small>
<span id="prod_<?= $model['catn'] ?>_dmns_y" class="ml-1 pointer-event" role="button" onclick="return product_panel_dimensions_edit('<?= $model['catn'] ?>', this, 'y');"><?= empty($model['dmns']['y']) ? '0' : $model['dmns']['y'] ?></span>
<span class="mr-1">см</span>
<small>x</small>
<span id="prod_<?= $model['catn'] ?>_dmns_z" class="ml-1 pointer-event" role="button" onclick="return product_panel_dimensions_edit('<?= $model['catn'] ?>', this, 'z');"><?= empty($model['dmns']['z']) ? '0' : $model['dmns']['z'] ?></span>
<span class="mr-1">см</span>
<section id="prod_<?= $model['catn'] ?>_info" class="col">
<!-- Габариты -->
<p class="form-control-sm p-0 h-auto">
<b>Габариты:</b><span id="prod_<?= $model['catn'] ?>_dmns_x" class="ml-1 pointer-event" role="button" onclick="return product_panel_dimensions_edit('<?= $model['catn'] ?>', this, 'x');"><?= empty($model['dmns']['x']) ? '0' : $model['dmns']['x'] ?></span><span class="mr-1">см</span><small>x</small><span id="prod_<?= $model['catn'] ?>_dmns_y" class="ml-1 pointer-event" role="button" onclick="return product_panel_dimensions_edit('<?= $model['catn'] ?>', this, 'y');"><?= empty($model['dmns']['y']) ? '0' : $model['dmns']['y'] ?></span><span class="mr-1">см</span><small>x</small><span id="prod_<?= $model['catn'] ?>_dmns_z" class="ml-1 pointer-event" role="button" onclick="return product_panel_dimensions_edit('<?= $model['catn'] ?>', this, 'z');"><?= empty($model['dmns']['z']) ? '0' : $model['dmns']['z'] ?></span><span class="mr-1">см</span>
</p>
<!-- Вес -->
<p class="form-control-sm p-0 h-auto">
<b>Вес:</b><span id="prod_<?= $model['catn'] ?>_dmns_x" class="ml-1 pointer-event" role="button" onclick="return product_panel_weight_edit('<?= $model['catn'] ?>', this);"><?= empty($model['wght']) ? '0' : $model['wght'] ?></span><span class="mr-1">кг</span>
</p>
</section>
<?php else : ?>
<section id="prod_<?= $model['catn'] ?>_info" class="col mb-1">
<p class="form-control-sm">
<b class="mr-1">Габариты:</b>
<span id="prod_<?= $model['catn'] ?>_dmns_x" class="mx-1"><?= $model['dmns']['x'] ?? '0' ?></span>
<small>x</small>
<span id="prod_<?= $model['catn'] ?>_dmns_y" class="mx-1"><?= $model['dmns']['y'] ?? '0' ?></span>
<small>x</small>
<span id="prod_<?= $model['catn'] ?>_dmns_z" class="mx-1"><?= $model['dmns']['z'] ?? '0' ?></span>
<!-- Габариты -->
<p class="form-control-sm p-0 h-auto">
<b>Габариты:</b><span id="prod_<?= $model['catn'] ?>_dmns_x" class="ml-1"><?= empty($model['dmns']['x']) ? '0' : $model['dmns']['x'] ?></span><span class="mr-1">см</span><small>x</small><span id="prod_<?= $model['catn'] ?>_dmns_y" class="ml-1"><?= empty($model['dmns']['y']) ? '0' : $model['dmns']['y'] ?></span><span class="mr-1">см</span><small>x</small><span id="prod_<?= $model['catn'] ?>_dmns_z" class="ml-1"><?= empty($model['dmns']['z']) ? '0' : $model['dmns']['z'] ?></span><span class="mr-1">см</span>
</p>
<!-- Вес -->
<p class="form-control-sm p-0 h-auto">
<b>Габариты:</b><span id="prod_<?= $model['catn'] ?>_dmns_x" class="ml-1"><?= empty($model['wght']) ? '0' : $model['wght'] ?></span><small>кг</small>
</p>
</section>
<?php endif ?>
<div class="dropdown-divider px-0 mb-2"></div>
<!-- Описание -->
<div class="row mb-3 h-100 product_panel d-flex flex-column">
<?php if (
!yii::$app->user->isGuest

View File

@ -40,7 +40,8 @@ if (
<?php if (!yii::$app->user->isGuest) : ?>
<input type="radio" id="profile_panel_settings_account" name="main_panel" <?= $panel === 'profile_panel_settings_account' ? 'checked' : null ?> />
<div class="col">
<h5>Основная информация</h5>
<h5>Доставка</h5>
<div class="dropdown-divider mb-3"></div>
<?php $form = ActiveForm::begin([
'id' => 'form_profile_settings',
'action' => false,
@ -54,15 +55,15 @@ if (
// Инициализация
$model ?? $model = yii::$app->user->identity;
$account_sections_city_list or $account_sections_city_list = ['Нет данных'];
$delivery_to_city_list or $delivery_to_city_list = ['Нет данных'];
?>
<?= $form->field($model, 'opts[account_sections_city]', ['options' => ['class' => "mb-1"]])
->dropDownList($account_sections_city_list, [
<?= $form->field($model, 'opts[delivery_to_city]', ['options' => ['class' => "mb-1"]])
->dropDownList($delivery_to_city_list, [
'onChange' => 'page_profile_settings(this.parentElement.parentElement, \'profile_panel_settings_account\')'
])->label('Город'); ?>
<small class="d-block mb-1">Выберите город для <b>рассчёта доставки</b></small>
<small class="d-block mb-1">Выберите город <b><u>получателя</u></b> для <b>рассчёта доставки</b></small>
<?php ActiveForm::end(); ?>
</div>
@ -70,7 +71,32 @@ if (
<?php if (yii::$app->user->identity->agnt) : ?>
<input type="radio" id="profile_panel_settings_company" name="main_panel" <?= $panel === 'profile_panel_settings_company' ? 'checked' : null ?> />
<div class="col">
2
<h5>Доставка</h5>
<div class="dropdown-divider mb-3"></div>
<?php $form = ActiveForm::begin([
'id' => 'form_profile_settings',
'action' => false,
'fieldConfig' => [
'template' => '{label}{input}',
],
'options' => [
'onsubmit' => 'return false;'
]
]);
// Инициализация
$model ?? $model = yii::$app->user->identity;
$delivery_from_city_list or $delivery_from_city_list = ['Нет данных'];
?>
<?= $form->field($model, 'opts[delivery_from_city]', ['options' => ['class' => "mb-1"]])
->dropDownList($delivery_from_city_list, [
'onChange' => 'page_profile_settings(this.parentElement.parentElement, \'profile_panel_settings_company\')'
])->label('Город'); ?>
<small class="d-block mb-1">Выберите город <b><u>отправителя</u></b> для <b>рассчёта доставки</b></small>
<?php ActiveForm::end(); ?>
</div>
<input type="radio" id="profile_panel_settings_import" name="main_panel" <?= $panel === 'profile_panel_settings_import' ? 'checked' : null ?> />
@ -90,13 +116,13 @@ if (
// Инициализация
$model ?? $model = yii::$app->user->identity;
$import_sections_oem_list or $import_sections_oem_list = ['Нет данных'];
$import_oem_list or $import_oem_list = ['Нет данных'];
?>
<?= $form->field($model, 'opts[import_sections_oem]', ['options' => ['class' => "mb-1"]])
->dropDownList($import_sections_oem_list, [
<?= $form->field($model, 'opts[import_supplies_oem]', ['options' => ['class' => "mb-1"]])
->dropDownList($import_oem_list, [
'onChange' => 'page_profile_settings(this.parentElement.parentElement, \'profile_panel_settings_import\')',
'disabled' => count($import_sections_oem_list) <= 1
'disabled' => count($import_oem_list) <= 1
])->label('OEM-номера'); ?>
<small class="d-block mb-1">Выберите поле в котором хранятся <b>ОЕМ-номера</b> и повторите импорт</small>

View File

@ -1,3 +1,11 @@
<?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">
@ -72,43 +80,51 @@
// Инициализация количества
$amount_raw = $amount = $supply['amnt'] ?? $supply['onec']['Количество'];
if (empty($amount_raw) || $amount_raw < 1) {
// Уже не используется
$amount = 'Под заказ';
} else {
$amount .= ' шт';
}
// Инициализация доставки
$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'] ?? 'Неизвестно';
if ($amount_raw < 1 || $price_raw < 1) {
// Нет в наличии или цена 0 рублей
$button_cart = <<<HTML
<div class="col-1 h-100 text-dark d-flex" title="Товар недоступен">
$supplies_html .= <<<HTML
<div class="row my-auto m-0 h-100 text-right">
<a class="col-auto ml-auto my-auto text-dark" href="/order/new/custom" role="button" onclick="return false;">
<small>
Заказать поиск у оператора
</small>
</a>
</div>
HTML;
} else {
$button_cart = <<<HTML
<a class="col-1 h-100 text-dark d-flex button_white rounded" title="Добавить $catn в корзину" href="/cart" role="button" onclick="return cart_write('$catn');">
<i class="fas fa-cart-arrow-down pr-1 m-auto"></i>
</a>
// Есть в наличии
$supplies_html .= <<<HTML
<div class="row my-auto m-0 h-100 text-right">
<small class="col-2 my-auto ml-auto">
$amount
</small>
<small class="col-2 my-auto ml-1">
$delivery_max дней<br/>
$delivery_price рублей
</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');">
<i class="fas fa-cart-arrow-down pr-1 m-auto"></i>
</a>
</div>
HTML;
}
$supplies_html .= <<<HTML
<div class="row my-auto m-0 h-100 text-right">
<small class="col-2 my-auto ml-auto">
$amount
</small>
<small class="col-2 my-auto ml-1">
Доставка
</small>
<b class="col-2 my-auto">
$price
</b>
$button_cart
</div>
HTML;
?>
<?php endforeach ?>

View File

@ -171,6 +171,76 @@ function product_panel_dimensions_save(catn, element, dimension) {
return true;
}
function product_panel_weight_edit(catn, element) {
if (catn !== null && catn !== undefined && element !== null && element !== undefined) {
let input = document.createElement('input');
input.setAttribute('id', element.id);
input.setAttribute('class', 'ml-1 text-center product_options_edit_small');
input.setAttribute('type', 'number');
input.setAttribute('value', element.innerText);
input.setAttribute('aria-invalid', 'false');
element.replaceWith(input);
product_panel_handler_save(product_panel_weight_save, catn, input);
return false;
}
return true;
}
function product_panel_weight_save(catn, element) {
if (catn !== null && catn !== undefined && element !== null && element !== undefined) {
// Инициализация
let text = element.value;
let span = document.createElement('span');
span.setAttribute('id', element.id);
span.setAttribute('class', 'ml-1 pointer-event');
span.setAttribute('role', 'button');
span.setAttribute('onclick', 'return product_panel_weight_edit(\'' + catn + '\', this);');
if (text.length === 0) {
text = '0';
}
span.innerText = text;
element.replaceWith(span);
$.ajax({
url: '/product/' + catn + '/edit/wght',
type: 'post',
dataType: 'json',
data: {
'_csrf': yii.getCsrfToken(),
'text': text
},
success: function (data, status) {
// Заголовок
if (data.weight !== undefined && span !== null && span !== undefined) {
// Обновление заголовка
span.innerText = data.weight;
// Запись аттрибута
span.setAttribute('onclick', 'return product_panel_weight_edit(\'' + catn + '\', this);');
};
product_response_success(data, status);
},
error: product_response_error
});
return false;
}
return true;
}
function product_panel_description_edit(catn, element) {
if (catn !== null && catn !== undefined && element !== null && element !== undefined) {
element.innerHTML = '<textarea class="form-control" cols="50" rows="5" onchange = "return product_panel_description_save(\'' + catn + '\', this.parentElement)">' + element.innerText + '</textarea>';

View File

@ -10,6 +10,7 @@
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
defined('YII_PATH_PUBLIC') or define('YII_PATH_PUBLIC', __DIR__ . '/web');
require __DIR__ . '../../../../vendor/autoload.php';
require __DIR__ . '../../../../vendor/yiisoft/yii2/Yii.php';