diff --git a/mirzaev/skillparts/system/assets/AppAsset.php b/mirzaev/skillparts/system/assets/AppAsset.php index c801b8d..55e1348 100644 --- a/mirzaev/skillparts/system/assets/AppAsset.php +++ b/mirzaev/skillparts/system/assets/AppAsset.php @@ -45,7 +45,11 @@ class AppAsset extends AssetBundle 'js/search.js', 'js/notification.js', 'js/reinitialization.js', - 'js/geolocation.js' + 'https://api-maps.yandex.ru/2.1/?apikey=0c4ba9aa-c448-4bd0-9c8c-6181f21ede90&lang=ru_RU', + 'js/yandex/metrika.js', + 'js/yandex/geolocation.js', + 'https://www.googletagmanager.com/gtag/js?id=G-6XYKBJJWR4', + 'js/google/analytics.js' ]; public $jsOptions = [ // 'position' => View::POS_HEAD diff --git a/mirzaev/skillparts/system/controllers/InvoiceController.php b/mirzaev/skillparts/system/controllers/InvoiceController.php index 876ef89..c17cb17 100644 --- a/mirzaev/skillparts/system/controllers/InvoiceController.php +++ b/mirzaev/skillparts/system/controllers/InvoiceController.php @@ -16,7 +16,7 @@ class InvoiceController extends Controller if ($order = Order::searchById(Order::collectionName() . '/' . $order)) { return $this->renderPartial('/invoice/order/pattern', [ - 'account' => yii::$app->user->identity->_key, + 'account' => yii::$app->user->identity, 'order' => [ 'id' => $order->_key, 'date' => $order->date ?? time() // @todo доделать diff --git a/mirzaev/skillparts/system/controllers/NotificationController.php b/mirzaev/skillparts/system/controllers/NotificationController.php index 55c3d67..7691316 100644 --- a/mirzaev/skillparts/system/controllers/NotificationController.php +++ b/mirzaev/skillparts/system/controllers/NotificationController.php @@ -184,7 +184,7 @@ class NotificationController extends Controller $notification = $notifications[0]; $return['popup'] = [ - 'html' => $this->renderPartial('popup', compact('model', 'notification')), + 'html' => $this->renderPartial('popup', compact('model', 'notification', 'account')), 'id' => 'popup/' . $notification->readId() ]; } else if (yii::$app->request->post('stream')) { diff --git a/mirzaev/skillparts/system/controllers/OrderController.php b/mirzaev/skillparts/system/controllers/OrderController.php index eefce7a..d03a1ed 100644 --- a/mirzaev/skillparts/system/controllers/OrderController.php +++ b/mirzaev/skillparts/system/controllers/OrderController.php @@ -24,6 +24,7 @@ use app\models\Supply; use app\models\SupplyEdgeProduct; use Codeception\PHPUnit\ResultPrinter\HTML; + use DateTime; use Exception; use Throwable; @@ -306,7 +307,7 @@ class OrderController extends Controller return [ 'main' => $this->renderPartial('/orders/index', compact('orders', 'moderator_orders', 'search', 'from', 'to', 'window') - + ['panel' => $this->renderPartial('/orders/search/panel', ['response' => $orders[0]['supplies']])]), + + ['panel' => $this->renderPartial('/orders/search/panel', compact('account') + ['response' => @$orders[0]['supplies']] ?? null)]), 'title' => 'Заказы', 'redirect' => '/orders', '_csrf' => yii::$app->request->getCsrfToken() @@ -489,14 +490,14 @@ class OrderController extends Controller yii::$app->response->format = Response::FORMAT_JSON; return [ - 'main' => $this->renderPartial('/cart/index', compact('order', 'connections')), + 'main' => $this->renderPartial('/cart/index', compact('order', 'connections', 'account')), 'title' => 'Корзина', 'redirect' => '/cart', '_csrf' => yii::$app->request->getCsrfToken() ]; } - return $this->render('/cart/index', compact('order', 'connections')); + return $this->render('/cart/index', compact('order', 'connections', 'account')); } /** @@ -507,7 +508,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(); $connections = $order->content(10, $page); @@ -566,14 +567,14 @@ class OrderController extends Controller yii::$app->response->format = Response::FORMAT_JSON; return [ - 'main' => $this->renderPartial('/cart/index', compact('order', 'connections')), + 'main' => $this->renderPartial('/cart/index', compact('order', 'connections', 'account')), 'title' => 'Корзина', 'redirect' => '/cart', '_csrf' => yii::$app->request->getCsrfToken() ]; } - return $this->render('/cart/index', compact('order', 'connections')); + return $this->render('/cart/index', compact('order', 'connections', 'account')); } /** @@ -681,7 +682,7 @@ class OrderController extends Controller } - if ($order_edge_supply = OrderEdgeSupply::searchById($_id = OrderEdgeSupply::collectionName() . '/' . $_key)) { + if ($order_edge_supply = OrderEdgeSupply::searchById($_id = OrderEdgeSupply::collectionName() . '/' . $catn)) { // Удалось найти инстанцию поставки // Инициализация ребра: ПОСТАВКА -> ТОВАР diff --git a/mirzaev/skillparts/system/controllers/ProfileController.php b/mirzaev/skillparts/system/controllers/ProfileController.php index c9be47b..5ab3680 100644 --- a/mirzaev/skillparts/system/controllers/ProfileController.php +++ b/mirzaev/skillparts/system/controllers/ProfileController.php @@ -286,12 +286,38 @@ class ProfileController extends Controller /** * Страница мониторинга */ - public function actionMonitoring(): string|array + public function actionMonitoring(Account|int|null $account = null): string|array { // Инициализация $panel = yii::$app->request->post('panel') ?? yii::$app->request->get('panel'); $sidebar = $this->renderPartial('sidebar'); + if (is_null($account)) { + // Данные аккаунта не переданы + + if (yii::$app->user->isGuest) { + // Аккаунт не аутентифицирован + + return []; + } else { + // Аккаунт аутентифицирован + + // Инициализация + $account = yii::$app->user->identity; + } + } else { + if (is_int($account)) { + // Передан идентификатор (_key) аккаунта (подразумевается) + + // Инициализация (поиск в базе данных) + if (!$account = Account::searchById(Account::collectionName() . "/$account")) { + // Не удалось инициализировать аккаунт + + return []; + } + } + } + // Инициализация номера страницы $page_search_history = (yii::$app->request->post('search') ?? yii::$app->request->get('search')) - 1; @@ -368,7 +394,8 @@ class ProfileController extends Controller 'sidebar', 'search_history', 'page_search_history', - 'panel' + 'panel', + 'account' )); } @@ -483,32 +510,6 @@ class ProfileController extends Controller // Настройка ответа yii::$app->response->format = Response::FORMAT_JSON; - // if (is_null($account)) { - // // Данные аккаунта не переданы - - // if (yii::$app->user->isGuest) { - // // Аккаунт не аутентифицирован - - // return false; - // } else { - // // Аккаунт аутентифицирован - - // // Инициализация - // $account = yii::$app->user->identity; - // } - // } else { - // if (is_int($account)) { - // // Передан идентификатор (_key) аккаунта (подразумевается) - - // // Инициализация (поиск в базе данных) - // if (!$account = Account::searchById(Account::collectionName() . "/$account")) { - // // Не удалось инициализировать аккаунт - - // return false; - // } - // } - // } - // Инициализация аккаунта if (yii::$app->user->isGuest) { // Аккаунт не аутентифицирован @@ -524,47 +525,32 @@ class ProfileController extends Controller // Настройка ответа yii::$app->response->format = Response::FORMAT_JSON; - // Инициализация IP-адреса - $ip = yii::$app->request->userIp === 'localhost' || yii::$app->request->userIp === '127.0.0.1' ? '46.226.227.20' : yii::$app->request->userIp; + if (false !== $return = self::geolocationCheck($account)) { + // Удалось сгенерировать данные для возврата - // Проверка записи геолокации - if (isset($account->geol)) { - // Удалось найти данные геолокации - } else { - // Не удалось найти данные геолокации - - try { - // Инициализация данных геолокации - $dadata = new Dadata(yii::$app->params['dadata']['key'], yii::$app->params['dadata']['secret']); - - // Запись в буфер данных о геолокации - $account->geol = $dadata->iplocate($ip); - } catch (Throwable $t) { - return false; - } - - // Запись в буфер данных о типе геолокации - $account->geol = ['type' => 'ip'] + ($account->geol ?? []); - - self::syncGeolocationWithDellin($account); + return $return; } - return self::geolocationAccuracyCheck($account); + return [ + 'geolocation' => false, + '_csrf' => yii::$app->request->getCsrfToken() + ]; } return false; } /** - * Генерация ответа по данным геолокации - * - * Проверка точности и наличия данных о геолокации + * Проверка наличия записи о геолокации * * @param Account|string|null $account * * @return array|bool JSON, в случае успеха + * + * @todo + * 1. 👁_👁 */ - public static function geolocationAccuracyCheck(Account|int|null $account = null): array|bool + public static function geolocationCheck(Account|int|null $account = null): array|bool { if (is_null($account)) { // Данные аккаунта не переданы @@ -597,15 +583,15 @@ class ProfileController extends Controller // Генерация ответа return [ - 'requestGps' => match ($account->geol['type'] ?? null) { - 'gps' => false, - default => true - }, + 'geolocation' => true, '_csrf' => yii::$app->request->getCsrfToken() ]; } - return false; + return [ + 'geolocation' => false, + '_csrf' => yii::$app->request->getCsrfToken() + ]; } /** @@ -614,6 +600,9 @@ class ProfileController extends Controller * @param string|null $account Аккаунт * * @return array|bool JSON, в случае успеха + * + * @todo + * 1. Избавиться от второго запроса к DaData ($dadata->clean("address", $account->city);) */ public function actionGeolocationWrite(): array|bool { @@ -623,36 +612,13 @@ class ProfileController extends Controller // Настройка ответа yii::$app->response->format = Response::FORMAT_JSON; - // if (is_null($account)) { - // // Данные аккаунта не переданы - - // if (yii::$app->user->isGuest) { - // // Аккаунт не аутентифицирован - - // return false; - // } else { - // // Аккаунт аутентифицирован - - // // Инициализация - // $account = yii::$app->user->identity; - // } - // } else { - // if (is_int($account)) { - // // Передан идентификатор (_key) аккаунта (подразумевается) - - // // Инициализация (поиск в базе данных) - // if (!$account = Account::searchById(Account::collectionName() . "/$account")) { - // // Не удалось инициализировать аккаунт - - // return false; - // } - // } - // } - // Инициализация аккаунта if (yii::$app->user->isGuest) { // Аккаунт не аутентифицирован + // Запись кода ответа + yii::$app->response->statusCode = 401; + return false; } else { // Аккаунт аутентифицирован @@ -664,15 +630,21 @@ class ProfileController extends Controller // Настройка ответа yii::$app->response->format = Response::FORMAT_JSON; - // Инициализация широты - $latitude = yii::$app->request->post('latitude') ?? yii::$app->request->get('latitude'); + // Инициализация данных яндекса + $yandex = yii::$app->request->post('yandex') ?? yii::$app->request->get('yandex'); - // Инициализация долготы - $longitude = yii::$app->request->post('longitude') ?? yii::$app->request->get('longitude'); + // Инициализация широты (среднее значение из общего периметра) + $latitude = ($yandex['coordinates'][0][0] + $yandex['coordinates'][1][0]) / 2; + + // Инициализация долготы (среднее значение из общего периметра) + $longitude = ($yandex['coordinates'][0][1] + $yandex['coordinates'][1][1]) / 2; if (empty($latitude) || empty($longitude)) { // Широта или долгота не передана + // Запись кода ответа + yii::$app->response->statusCode = 500; + return false; } @@ -682,13 +654,31 @@ class ProfileController extends Controller // Запись в буфер данных о геолокации $account->geol = $dadata->geolocate("address", $latitude, $longitude)[0]; + + // Запись в буфер полученного города + $account->city = $account->geol['data']['city']; + + // Запись в буфер новых данных (непонятно почему, но только вторым запросом можно получать часовую зону) + $account->geol = $account->geol + $dadata->clean("address", $account->city); + + // Запись в буфер часовой зоны + $account->zone = $account->geol['data']['timezone']; + + if($account->update() < 1) { + // Не удалось записать данные + + // Запись кода ответа + yii::$app->response->statusCode = 500; + + return false; + }; } catch (Throwable $t) { + // Запись кода ответа + yii::$app->response->statusCode = 500; + return false; } - // Запись в буфер данных о типе геолокации - $account->geol = ['type' => 'gps'] + $account->geol; - return self::syncGeolocationWithDellin($account); } } diff --git a/mirzaev/skillparts/system/controllers/SearchController.php b/mirzaev/skillparts/system/controllers/SearchController.php index 89edb26..c23dde9 100644 --- a/mirzaev/skillparts/system/controllers/SearchController.php +++ b/mirzaev/skillparts/system/controllers/SearchController.php @@ -26,6 +26,7 @@ class SearchController extends Controller { // Инициализация параметров $auth_only = false; + $account = yii::$app->user->identity; if ($auth_only && yii::$app->user->isGuest) { // Если активирован режим "Поиск только аутентифицированным" и запрос пришел не от аутентифицированного @@ -49,7 +50,7 @@ class SearchController extends Controller yii::$app->response->format = Response::FORMAT_JSON; return [ - 'panel' => $this->renderPartial('/search/panel', ['history' => true]), + 'panel' => $this->renderPartial('/search/panel', compact('account') + ['history' => true]), '_csrf' => yii::$app->request->getCsrfToken() ]; } diff --git a/mirzaev/skillparts/system/controllers/VerifyController.php b/mirzaev/skillparts/system/controllers/VerifyController.php index c42370c..9d2ee99 100644 --- a/mirzaev/skillparts/system/controllers/VerifyController.php +++ b/mirzaev/skillparts/system/controllers/VerifyController.php @@ -6,9 +6,10 @@ namespace app\controllers; use yii; use yii\web\Controller; -use app\models\Account; use yii\web\Response; +use app\models\Account; + class VerifyController extends Controller { public function actionIndex(string $vrfy = null): string|Response @@ -36,6 +37,9 @@ class VerifyController extends Controller if (yii::$app->user->identity->vrfy === true) { // Регистрация аккаунта уже подтверждена + // Генерация хеша пароля + yii::$app->user->identity->pswd = yii::$app->security->generatePasswordHash(yii::$app->user->identity->pswd); + if (yii::$app->request->isPost) { // POST-запрос diff --git a/mirzaev/skillparts/system/models/Account.php b/mirzaev/skillparts/system/models/Account.php index 5b31838..a8e6ad5 100644 --- a/mirzaev/skillparts/system/models/Account.php +++ b/mirzaev/skillparts/system/models/Account.php @@ -44,6 +44,7 @@ class Account extends Document implements IdentityInterface, PartnerInterface 'name', 'simc', 'sity', + 'zone', 'comp', 'taxn', 'onec', @@ -72,6 +73,7 @@ class Account extends Document implements IdentityInterface, PartnerInterface 'name' => 'Имя', 'simc' => 'Номер', 'sity' => 'Город', + 'zone' => 'Часовой пояс', 'comp' => 'Компания', 'taxn' => 'ИНН', 'onec' => 'Данные 1C', @@ -116,7 +118,10 @@ class Account extends Document implements IdentityInterface, PartnerInterface 'message' => 'Атрибут {attribute} должен иметь уникальное значение' ], [ - 'indx', + [ + 'indx', + 'zone' + ], 'string' ], [ @@ -287,11 +292,18 @@ class Account extends Document implements IdentityInterface, PartnerInterface } /** - * Проверка пароля + * Проверка пароля с хешированием */ - public function validatePassword(string $pswd): bool + public function validatePasswordWithHash(string $pswd): bool + { + return yii::$app->security->validatePassword($pswd, $this->pswd); + } + + /** + * Проверка пароля без хеширования + */ + public function validatePasswordWithoutHash(string $pswd): bool { - // return yii::$app->security->validatePassword($pswd, $this->pswd); return $pswd === $this->pswd; } @@ -487,7 +499,7 @@ class Account extends Document implements IdentityInterface, PartnerInterface // Запись empty($terminal->city) && empty($terminal->strt) && empty($terminal->hous) - or $list[$terminal->dell] = (empty($terminal->city) ? '' : "г. $terminal->city"). (empty($terminal->strt) ? '' : ", ул. $terminal->strt") . (empty($terminal->hous) ? '' : ", д. $terminal->hous") . (empty($terminal->offs) ? '' : ", оф. $terminal->offs") . (empty($terminal->comm) ? '' : " ($terminal->comm)"); + or $list[$terminal->dell] = (empty($terminal->city) ? '' : "г. $terminal->city") . (empty($terminal->strt) ? '' : ", ул. $terminal->strt") . (empty($terminal->hous) ? '' : ", д. $terminal->hous") . (empty($terminal->offs) ? '' : ", оф. $terminal->offs") . (empty($terminal->comm) ? '' : " ($terminal->comm)"); } return $this->syncListWithSettings($list, 'delivery_to_terminal'); diff --git a/mirzaev/skillparts/system/models/AccountForm.php b/mirzaev/skillparts/system/models/AccountForm.php index 689997d..c2e0c26 100644 --- a/mirzaev/skillparts/system/models/AccountForm.php +++ b/mirzaev/skillparts/system/models/AccountForm.php @@ -8,6 +8,7 @@ use yii; use yii\base\Model; use app\models\Account; +use Exception; /** * AccountForm is the model behind the login form. @@ -146,10 +147,24 @@ class AccountForm extends Model return; } - if (!$account || !$account->validatePassword($this->pswd)) { - // Проверка не пройдена + if ($account) { + // Удалось инициализировать аккаунт - $this->addError($attribute, 'Проверьте пароль'); + try { + $account->validatePasswordWithHash($this->pswd); + } catch (Exception $e) { + // Проверка с хешем не пройдена + + try { + $account->validatePasswordWithoutHash($this->pswd); + } catch (Exception $e) { + // Проверка без хеша не пройдена + + $this->addError($attribute, 'Проверьте пароль'); + } + } + } else { + $this->addError($attribute, 'Не удалось идентифицировать аккаунт'); } } } diff --git a/mirzaev/skillparts/system/models/Notification.php b/mirzaev/skillparts/system/models/Notification.php index da6b5f5..37fdc05 100644 --- a/mirzaev/skillparts/system/models/Notification.php +++ b/mirzaev/skillparts/system/models/Notification.php @@ -168,7 +168,7 @@ class Notification extends Document $text = htmlspecialchars(strip_tags($text ?? null)); $model->html = <<$text
+$text
HTML; } @@ -281,4 +281,19 @@ class Notification extends Document return $return ? $return : null; } + + /** + * Конвертация типа уведомления в версию для отображения + * + * @param string|null $type Тип уведомления + * + * @return string + */ + public function genTypeToRussian(string $type = null): string { + return match($type ?? $this->type) { + 'notice' => 'Уведомление', + 'warning' => 'Предупреждение', + 'error' => 'Ошибка' + }; + } } diff --git a/mirzaev/skillparts/system/models/Product.php b/mirzaev/skillparts/system/models/Product.php index 55912ba..9b4f335 100644 --- a/mirzaev/skillparts/system/models/Product.php +++ b/mirzaev/skillparts/system/models/Product.php @@ -7,10 +7,14 @@ namespace app\models; use yii; use yii\web\UploadedFile; use yii\imagine\Image; +use app\models\Settings; use app\models\traits\SearchByEdge; use moonland\phpexcel\Excel; +use DateTime; +use DateTimeZone; + use Exception; /** @@ -318,18 +322,45 @@ class Product extends Document * На данный момент обрабатывает только импорт из * файлов с расширением .excel */ - public function importExcel(): bool + public function importExcel(Account|int|null $account = null): bool { // Инициализация $data = []; $amount = 0; + if (is_null($account)) { + // Данные аккаунта не переданы + + if (yii::$app->user->isGuest) { + // Аккаунт не аутентифицирован + + return false; + } else { + // Аккаунт аутентифицирован + + // Инициализация + $account = yii::$app->user->identity; + } + } else { + if (is_int($account)) { + // Передан идентификатор (_key) аккаунта (подразумевается) + + // Инициализация (поиск в базе данных) + if (!$account = Account::searchById(Account::collectionName() . "/$account")) { + // Не удалось инициализировать аккаунт + + return false; + } + } + } + + if ($this->validate()) { foreach ($this->file_excel as $file) { // Перебор файлов // Инициализация - $dir = YII_PATH_PUBLIC . '../assets/import/' . date('Y-m-d', time()) . '/excel/' . (yii::$app->user->identity->_key ?? 'system') . '/' . time() . '/'; + $dir = YII_PATH_PUBLIC . '../assets/import/' . (new DateTime('now', new DateTimeZone($account->zone ?? Settings::search()['timezone_default'] ?? 'UTC+3'))) . '/excel/' . (yii::$app->user->identity->_key ?? 'system') . '/' . time() . '/'; // Сохранение на диск if (!file_exists($dir)) { @@ -467,9 +498,16 @@ class Product extends Document */ public static function afterImportExcel(int $amount = 0): bool { - // Инициализация + // Инициализация параметров $model = new Notification; - $date = date('H:i d.m.Y', time()); + $account = yii::$app->user->identity; + + // Инициализация часового пояса + preg_match_all('/UTC([\+\-0-9:]*)/', $account->zone ?? Settings::search()['timezone_default'] ?? 'UTC+3', $timezone); + $timezone = $timezone[1][0]; + + // Инициализация даты + $date = (new DateTime('now', new DateTimeZone($timezone)))->format('H:i d.m.Y'); // Настройка $model->text = yii::$app->controller->renderPartial('@app/views/notification/system/afterImportExcel', compact('amount', 'date')); @@ -482,11 +520,42 @@ class Product extends Document /** * Вызывается после загрузки поставок из 1С */ - public static function afterImport1c(): bool + public static function afterImport1c(Account|int|null $account = null): bool { // Инициализация $model = new Notification; - $date = date('H:i d.m.Y', time()); + + if (is_null($account)) { + // Данные аккаунта не переданы + + if (yii::$app->user->isGuest) { + // Аккаунт не аутентифицирован + + return false; + } else { + // Аккаунт аутентифицирован + + // Инициализация + $account = yii::$app->user->identity; + } + } else { + if (is_int($account)) { + // Передан идентификатор (_key) аккаунта (подразумевается) + + // Инициализация (поиск в базе данных) + if (!$account = Account::searchById(Account::collectionName() . "/$account")) { + // Не удалось инициализировать аккаунт + + return false; + } + } + } + + // Инициализация часового пояса + preg_match_all('/UTC([\+\-0-9:]*)/', $account->zone ?? Settings::search()['timezone_default'] ?? 'UTC+3', $timezone); + $timezone = $timezone[1][0]; + + $date = (new DateTime('now', new DateTimeZone($timezone)))->format('H:i d.m.Y'); // Настройка $model->text = yii::$app->controller->renderPartial('@app/views/notification/system/afterImport1c', compact('date')); diff --git a/mirzaev/skillparts/system/models/connection/Dellin.php b/mirzaev/skillparts/system/models/connection/Dellin.php index efacab5..56f601f 100644 --- a/mirzaev/skillparts/system/models/connection/Dellin.php +++ b/mirzaev/skillparts/system/models/connection/Dellin.php @@ -9,10 +9,14 @@ use yii\base\Model; use app\models\Dellin as DellinModel; use app\models\Product; +use app\models\Account; use app\models\Settings; + use GuzzleHttp\Client as Guzzle; use GuzzleHttp\Exception\ClientException as GuzzleException; +use DateTime; +use DateTimeZone; use Exception; class Dellin extends Model @@ -68,18 +72,44 @@ class Dellin extends Model * @param int $y Высота (cм) * @param int $z Длинна (cм) * @param int $amount Количество + * @param Account|int|null $account Аккаунт * * @return string * * @todo Загружать помимо терминалов ещё и адреса, чтобы доделать доставку малогабаритных грузов * Разрабраться с параметрами 0,54м * 0,39м * 0,39м (0.082134м) и 0.1 куб метр в чем разница */ - public static function calcDeliveryAdvanced(int $from, int $to, int $weight, int $x, int $y, int $z, int $amount = 1, bool $avia = false): array + public static function calcDeliveryAdvanced(int $from, int $to, int $weight, int $x, int $y, int $z, int $amount = 1, bool $avia = false, Account|int|null $account = null): array { - - return self::handle(function () use ($from, $to, $weight, $x, $y, $z, $amount, $avia) { + return self::handle(function () use ($from, $to, $weight, $x, $y, $z, $amount, $avia, $account) { // Всё готово к работе + if (is_null($account)) { + // Данные аккаунта не переданы + + if (yii::$app->user->isGuest) { + // Аккаунт не аутентифицирован + + return []; + } else { + // Аккаунт аутентифицирован + + // Инициализация + $account = yii::$app->user->identity; + } + } else { + if (is_int($account)) { + // Передан идентификатор (_key) аккаунта (подразумевается) + + // Инициализация (поиск в базе данных) + if (!$account = Account::searchById(Account::collectionName() . "/$account")) { + // Не удалось инициализировать аккаунт + + return []; + } + } + } + // Инициализация $from = DellinModel::searchByTerminalId($from, terminal_data_only: true); $to = DellinModel::searchByTerminalId($to, terminal_data_only: true); @@ -126,7 +156,8 @@ class Dellin extends Model $query = []; // Рассчёт типа доставки - if ( !$avia + if ( + !$avia && $weight <= 30 && ($length <= 0.54 && $width <= 0.39 && $height <= 0.39) && $length * $width * $height <= 0.1 @@ -164,6 +195,10 @@ class Dellin extends Model $query['delivery']['arrival']['terminalID'] = $to->id; } + // Инициализация часового пояса + preg_match_all('/UTC([\+\-0-9:]*)/', $account->zone ?? Settings::search()['timezone_default'] ?? 'UTC+3', $timezone); + $timezone = $timezone[1][0]; + // Инициализация $query = array_merge_recursive( $query, @@ -172,8 +207,7 @@ class Dellin extends Model 'sessionID' => self::$session, 'delivery' => [ 'derival' => [ - // 'produceDate' => date('Y-m-d', time() + ($settings['delivery_handle_time'] ?? 86400)) - 'produceDate' => date('Y-m-d', time() + 86400 * 3) + 'produceDate' => (new DateTime())->setTimestamp(time())->setTimezone(new DateTimeZone($timezone))->format('Y-m-d') ] ], 'members' => [ @@ -273,146 +307,42 @@ class Dellin extends Model }); } - /** - * Импорт городов - * - * @return array|null Сохранённые города - */ - // public static function importCities(): ?int - // { - // return self::handle(function () { - // // Всё готово к работе - - // // Запрос ссылки на файл с городами, возвращает ['hash' => string, 'url' => string] - // $request = self::$browser->post('/v1/public/cities.json', [ - // 'json' => [ - // 'appkey' => yii::$app->params['dellin']['key'], - // ] - // ]); - - // if ($request->getStatusCode() === 200) { - // // Запрос прошел успешно - - // // Инициализация - // $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)) { - // // Директории не существует - - // mkdir($dir, 0775, true); - // } - - // $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('Не удалось синхронизировать данные городов с ДеловыеЛинии', 500); - // }); - // } - - /** * Импорт терминалов * * @return array|null Сохранённые терминалы */ - public static function importTerminals(): ?int + public static function importTerminals(Account|int|null $account = null): ?int { - return self::handle(function () { + return self::handle(function () use ($account) { // Всё готово к работе + if (is_null($account)) { + // Данные аккаунта не переданы + + if (yii::$app->user->isGuest) { + // Аккаунт не аутентифицирован + + return 0; + } else { + // Аккаунт аутентифицирован + + // Инициализация + $account = yii::$app->user->identity; + } + } else { + if (is_int($account)) { + // Передан идентификатор (_key) аккаунта (подразумевается) + + // Инициализация (поиск в базе данных) + if (!$account = Account::searchById(Account::collectionName() . "/$account")) { + // Не удалось инициализировать аккаунт + + return 0; + } + } + } + // Запрос ссылки на файл с городами, возвращает ['hash' => string, 'url' => string] $request = self::$browser->post('/v3/public/terminals.json', [ 'json' => [ @@ -423,9 +353,13 @@ class Dellin extends Model if ($request->getStatusCode() === 200) { // Запрос прошел успешно - // Инициализация + // Инициализация часового пояса + preg_match_all('/UTC([\+\-0-9:]*)/', $account->zone ?? Settings::search()['timezone_default'] ?? 'UTC+3', $timezone); + $timezone = $timezone[1][0]; + + // Инициализация параметров $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') . '/'; + $dir = YII_PATH_PUBLIC . '/../assets/import/' . (new DateTime('now', new DateTimeZone($timezone)))->format('Y-m-d') . '/dellin/terminals/' . (yii::$app->user->identity->_key ?? 'system') . '/'; $amount = 0; if (!file_exists($dir)) { diff --git a/mirzaev/skillparts/system/views/invoice/order/pattern.php b/mirzaev/skillparts/system/views/invoice/order/pattern.php index 9a8dea0..15fce92 100644 --- a/mirzaev/skillparts/system/views/invoice/order/pattern.php +++ b/mirzaev/skillparts/system/views/invoice/order/pattern.php @@ -1,3 +1,8 @@ + @@ -78,7 +83,13 @@Новый заказ: #= $id ?>
+= $product['catn'] . ' x' . $amount['auto'] ?> - - - -
- + if (Order::checkSuppliesStts($order_edge_supply)) { + $status = ''; + } else { + $status = ''; + } + + // Генерация HTML + echo << + ++ {$product['catn']} x{$amount['auto']} + + $status +
+ + HTML; + } + + if ($amount['avia'] > 0) { + // Найдены поставки с автоматической доставкой + + if (Order::checkSuppliesStts($order_edge_supply)) { + $status = ''; + } else { + $status = ''; + } + + // Генерация HTML + echo << + ++ {$product['catn']} x{$amount['avia']} + + $status +
+ + HTML; + } + + ?> @@ -242,13 +289,27 @@ if (empty($window)) { // Пропуск активного заказа (несформированного, корзины) if ($account_edge_order[0]['type'] === 'current') continue; - // Деинициализация мусора - unset($date); - // Инициализация времени подтверждения заказа - if (isset($order['jrnl'])) foreach ($order['jrnl'] as $entry) { - if ($entry['action'] === 'accepted') $date = date('H:i d.m.Y', $entry['date']); + if (isset($order['jrnl'])) { + // Журнал найден + + foreach ($order['jrnl'] as $entry) { + // Перебор записей в журнале + + if ($entry['action'] === 'accepted') { + // Найден принятый заказ + + // Инициализация часового пояса + preg_match_all('/UTC([\+\-0-9:]*)/', $account->zone ?? Settings::search()['timezone_default'] ?? 'UTC+3', $timezone); + $timezone = $timezone[1][0]; + + // Запись данных о дате заказа + $date = (new DateTime())->setTimestamp($entry['date'])->setTimezone(new DateTimeZone($timezone))->format('H:i d.m.Y'); + } + } } + + // Инициализация значения по умолчанию $date ?? $date = 'Ожидается'; // Инициализация буфера поставок @@ -260,99 +321,158 @@ if (empty($window)) { if (isset($supplies)) { // Найдены поставки + // Инициализация максимального срока доставки + $delivery_max = 0; + // Инициализация поставок foreach ($supplies as $supply) { // Перебор поставок - // Инициализация окружения + // Инициализация переменных extract($supply); - // Инициализация связи поставки с заказом (подразумевается, что все одинаковые по основным параметрам) - $part = $order_edge_supply[0]; + // Инициализация цены + $price_raw = $cost; - // Инициализация доставки - if (isset($delivery['error'])) { - // Не удалось рассчитать доставку + // Инициализация комментария + $comment = $order_edge_supply['comm'] ?? 'Комментарий к заказу'; - // Инициализация индикатора - $date_icon = ''; + if ($amount['auto'] > 0) { + // Найдены поставки с автоматической доставкой + + // Инициализация цены + $price_auto = $price_raw['auto'] . ' ' . $currency; // Инициализация доставки - if (isset($part['date'])) { - // Найдены данные в инстанции поставки + if (!isset($delivery) || (isset($delivery['auto'], $delivery['auto']['error']) || $delivery === '?')) { + // Не удалось рассчитать доставку - // Запись в буфер вывода - // $time = $part['time'] . ' дн'; - $date = date('d.m.Y', $part['date']); - $date_html = "$date"; + // Инициализация времени + $delivery_auto = '?'; } else { - $date = 'Неизвестно'; - $date_html = "$date"; + // Удалось рассчитать доставку + + // Инициализация даты отправки + try { + // Взять данные из "arrivalToOspSender" (Дата прибытия на терминал-отправитель) + + $delivery_auto_send_date = DateTime::createFromFormat('Y-m-d', $delivery['auto']['orderDates']['arrivalToOspSender'])->getTimestamp(); + } catch (Throwable $e) { + // Взять данные из "pickup" (Дата передачи груза на адресе отправителя) + + $delivery_auto_send_date = DateTime::createFromFormat('Y-m-d', $delivery['auto']['orderDates']['pickup'])->getTimestamp(); + } + + // Инициализация времени доставки + try { + // Доставка по воздуху (подразумевается), данные из "giveoutFromOspReceiver" (Дата и время, с которого груз готов к выдаче на терминале) + + // Оставлено на всякий случай для дальнейших разбирательств + + $delivery_auto_converted = DateTime::createFromFormat('Y-m-d H:i:s', $delivery['auto']['orderDates']['giveoutFromOspReceiver'])->getTimestamp(); + } catch (Throwable $e) { + // Автоматическая доставка (подразумевается), данные из "arrivalToOspReceiver" (Дата прибытия натерминал-получатель) + + $delivery_auto_converted = DateTime::createFromFormat('Y-m-d', $delivery['auto']['orderDates']['arrivalToOspReceiver'])->getTimestamp(); + } + $delivery_auto = ceil(($delivery_auto_converted - ($delivery_auto_send_date ?? 0)) / 60 / 60 / 24) + 1; } - // Инициализация стоимости - if (isset($part['cost'])) $cost_html = '' . ($cost = $part['cost']) . ' ' . $currency . ''; - else $cost_html = '' . ($cost = 'Неизвестно') . ''; - } else { - // Удалось рассчитать доставку + // Инициализация статуса связи поставки + $status = OrderEdgeSupply::convertStatusToRussian($part['stts'] ?? ''); - // Инициализация типа доставки - $time_type = $part['dlvr']['type'] ?? 'auto'; - - // Инициализация индикатора - $date_icon = match ($time_type) { - 'avia' => '', - default => '' + // Инициализация класса для поставки (если необходимо) + $css = match ($part['stts'] ?? '') { + 'accepted' => ' supply_accepted', + default => '' }; - // Инициализация доставки - if (isset($part['date'])) { - // Найдены данные в инстанции поставки + // Реинициализация максимальной даты доставки + if ($delivery_max !== '?' && $delivery_max < $delivery_auto) $delivery_max = $delivery_auto; + else if ($delivery_auto === '?') $delivery_max = '?'; - // Запись в буфер вывода - // $time = $part['time'] . ' дн'; - $date = date('d.m.Y', $part['date']); - $date_html = "$date"; - } else { - // Рассчет времени из данных поставки - - try { - $time_converted = DateTime::createFromFormat('Y-m-d H:i:s', $delivery['auto']['orderDates']['giveoutFromOspReceiver'])->getTimestamp(); - } catch (Exception $e) { - $time_converted = DateTime::createFromFormat('Y-m-d', $delivery['auto']['orderDates']['arrivalToOspReceiver'])->getTimestamp(); - } - // $time = (ceil(($time_converted - time()) / 60 / 60 / 24) + 1) . ' дн'; - $date = date('d.m.Y', $time_converted); - $date_html = "$date"; - } - - // Инициализация стоимости - if (isset($part['cost'])) $cost_html = '' . ($cost = $part['cost']) . ' ' . $currency . ''; - else $cost_html = '' . ($cost = $cost['auto']) . ' ' . $currency . ''; + // Генерация HTML + // Пробела между supply и $css не должно быть + $supplies_html .= << +