Compare commits

...

155 Commits

Author SHA1 Message Date
root 761db76970 Merge branch 'stable' of https://git.mirzaev.sexy/mirzaev/skillparts into stable 2023-06-20 08:56:37 +00:00
root 9ebb4385cb что-то старое и грустное 2023-06-20 08:55:16 +00:00
Arsen Mirzaev Tatyano-Muradovich 1918db868d Merge branch 'stable' of https://git.mirzaev.sexy/mirzaev/skillparts into stable 2022-12-17 00:44:04 +10:00
Arsen Mirzaev Tatyano-Muradovich ec2d478a62 что это?* 2022-12-17 00:44:00 +10:00
root bfb0fbe4de изменение метров в сантиметры 2022-11-17 07:25:30 +00:00
root f57d7f6460 исправление ошибок 2022-11-17 07:09:56 +00:00
root 2488594d81 исправление ошибок 2022-11-17 07:08:25 +00:00
root ebf21a0642 Добавлены производители в фильтр 2022-11-17 06:12:15 +00:00
root c41e5d4c1c Мелкие доработки 2022-11-11 06:25:46 +00:00
root b3f2ef72da Множественное исправление багов 2022-11-01 03:37:36 +00:00
root d21c6ce0ed Merge branch 'stable' of https://git.mirzaev.sexy/mirzaev/skillparts into stable 2022-10-31 00:30:33 +00:00
root a11a21d1ab исправление иморта деловых линий 2022-10-31 00:29:53 +00:00
root c11cac01aa php 8.1 2022-10-29 00:27:08 +00:00
root fc38285d06 крупное обновление всего 2022-10-29 00:20:33 +00:00
root 4fafe85639 залупа 2022-08-04 13:37:24 +00:00
Arsen Mirzaev Tatyano-Muradovich 3ec9b920d2 Починил редактирование товара 2022-06-25 05:13:34 +10:00
Arsen Mirzaev Tatyano-Muradovich 33cc3efc7b починил редактирование изображений и артикула 2022-06-22 11:44:37 +10:00
Arsen Mirzaev Tatyano-Muradovich 2c2e830f0a крупная обнова 2022-06-01 14:24:48 +10:00
Arsen Mirzaev Tatyano-Muradovich e1fa1c1f5a Исправления 2022-04-14 09:59:17 +10:00
Arsen Mirzaev Tatyano-Muradovich 79773503eb исправления 2022-04-13 15:47:57 +10:00
Arsen Mirzaev Tatyano-Muradovich 68d26118ab Не выводить в аналогах пустые товары 2022-04-13 13:54:07 +10:00
Arsen Mirzaev Tatyano-Muradovich ab97036822 настройки маршрутизации под производителя 2022-04-13 13:18:06 +10:00
Arsen Mirzaev Tatyano-Muradovich 4f80a465bc Переработа всего под производителя 2022-04-13 12:39:58 +10:00
Arsen Mirzaev Tatyano-Muradovich e8454defc2 АНАЛоги работают 2022-03-23 12:59:31 +10:00
Arsen Mirzaev Tatyano-Muradovich bcc60fcd41 Доработка отображения почты 2022-03-11 08:51:53 +10:00
Arsen Mirzaev Tatyano-Muradovich 675c6cbc39 Обрезка почты в сайдбаре 2022-03-11 08:49:35 +10:00
Arsen Mirzaev Tatyano-Muradovich 16ce1ccf59 кнопка создать склад 2022-03-11 08:47:23 +10:00
Arsen Mirzaev Tatyano-Muradovich 4da846825b Починил импорт + доставка в тот же город 2022-03-11 08:39:10 +10:00
Arsen Mirzaev Tatyano-Muradovich 82d6747425 пошел нахуй 2022-03-10 09:00:12 +10:00
Arsen Mirzaev Tatyano-Muradovich a31214ad6a Починил поиск 2022-03-05 13:44:21 +10:00
Arsen Mirzaev Tatyano-Muradovich c5e24d7838 Починил аналоги и поиск 2022-03-05 13:44:11 +10:00
Arsen Mirzaev Tatyano-Muradovich 856d4f1995 докинул конфиг 2022-03-01 11:53:33 +10:00
Arsen Mirzaev Tatyano-Muradovich 2f4f38b2aa удаление складов + доставка 2022-03-01 11:53:28 +10:00
Arsen Mirzaev Tatyano-Muradovich 4683cf51db пульверизатор 2022-02-17 09:49:55 +10:00
Arsen Mirzaev Tatyano-Muradovich e941fbd782 Доработка 2022-02-07 14:50:21 +10:00
Arsen Mirzaev Tatyano-Muradovich 24f079f622 конфиг 2022-02-07 14:29:42 +10:00
Arsen Mirzaev Tatyano-Muradovich 4c91c3192a Активные и неактивные товары 2022-02-07 14:29:18 +10:00
Arsen Mirzaev Tatyano-Muradovich a971aef392 Нормальное исправление 2022-02-02 16:05:24 +10:00
Arsen Mirzaev Tatyano-Muradovich ab3c3b8b05 Исправление фильтра на загрузку системных файлов 2022-02-02 15:41:14 +10:00
Arsen Mirzaev Tatyano-Muradovich 8fda1af398 исправление 2022-01-31 15:00:13 +10:00
Arsen Mirzaev Tatyano-Muradovich 953f99245c Работа над сайтом 26 2022-01-31 14:55:51 +10:00
Arsen Mirzaev Tatyano-Muradovich 8abdaf4626 Топовый малыш 2022-01-25 14:46:25 +10:00
Arsen Mirzaev Tatyano-Muradovich bc98da67ce ой уволили 2022-01-18 14:39:12 +10:00
Arsen Mirzaev Tatyano-Muradovich 70d2ae2935 Терминалы 2021-12-27 14:56:06 +10:00
Arsen Mirzaev Tatyano-Muradovich 913a67d400 Исправления 2021-12-27 14:38:07 +10:00
Arsen Mirzaev Tatyano-Muradovich b08f7f7d0a Склады 2021-12-27 14:03:15 +10:00
Arsen Mirzaev Tatyano-Muradovich 7a4f12aa94 попа 2021-12-14 14:29:38 +10:00
Arsen Mirzaev Tatyano-Muradovich 9fb2e4dc90 Исправления ляляля хуй соси 2021-12-06 14:20:16 +10:00
Arsen Mirzaev Tatyano-Muradovich 3f0b342d48 ещё 2021-12-01 10:42:49 +10:00
Arsen Mirzaev Tatyano-Muradovich 6daa3112b6 Синтаксические исправления 2021-12-01 10:41:04 +10:00
Arsen Mirzaev Tatyano-Muradovich 8f32df518f Панели аккаунтов, товаров, поставок, прайсов 2021-11-29 11:00:50 +10:00
Arsen Mirzaev Tatyano-Muradovich 29ea6a9a79 небольшие исправления 2021-11-02 12:21:46 +10:00
Arsen Mirzaev Tatyano-Muradovich 1a1392d950 ПРОВЕРКА ПО CATN, PROD И ЦЕНЕ 2021-10-27 11:26:03 +10:00
Arsen Mirzaev Tatyano-Muradovich 1d06acf2e0 Исправление карты yandex 2021-10-14 16:20:10 +10:00
Leonid e6944c9b6b yandex maps 2021-10-12 14:34:31 +10:00
Arsen Mirzaev Tatyano-Muradovich 2b194457f5 composer 2021-10-12 13:37:52 +10:00
Arsen Mirzaev Tatyano-Muradovich 95b5d920b9 Мелкая графическая доработка 2021-10-12 13:27:44 +10:00
Arsen Mirzaev Tatyano-Muradovich 53281ee422 Доработка блока "Аналогичные товары" 2021-10-12 13:17:30 +10:00
Arsen Mirzaev Tatyano-Muradovich a235ed25ac Исправление ошибки в товаре + отключение отправки уведомлений при неаутентифицированной сессии 2021-10-08 16:33:38 +10:00
Arsen Mirzaev Tatyano-Muradovich 6bab42ac33 Небольшие исправления 2021-10-07 20:20:43 +10:00
Arsen Mirzaev Tatyano-Muradovich f76f15b503 nginx 2021-10-07 20:08:41 +10:00
Arsen Mirzaev Tatyano-Muradovich dbd2982115 nginx 2021-10-07 19:39:35 +10:00
Arsen Mirzaev Tatyano-Muradovich 2a90f36f0c Мелкое исправление вывода ошибок и удаление старых файлов 2021-10-07 09:18:28 +10:00
Arsen Mirzaev Tatyano-Muradovich 43272d051e Создание связей, удаление связей и удаление товаров 2021-10-04 08:25:22 +10:00
Arsen Mirzaev Tatyano-Muradovich 7e63c8be97 Загрузка из Excel 2021-09-28 07:22:17 +10:00
Arsen Mirzaev Tatyano-Muradovich 8c53872955 Принятие и отказ в регистрации 2021-09-22 11:38:53 +10:00
Arsen Mirzaev Tatyano-Muradovich 134ce8f162 Панель модератора 2021-09-16 10:58:47 +10:00
Arsen Mirzaev Tatyano-Muradovich a36f62e510 Доработка панели модератора 2021-09-14 08:06:29 +10:00
Arsen Mirzaev Tatyano-Muradovich 3cb2aa1a15 Панель модератора для регистрации поставщиков 2021-09-07 06:39:21 +10:00
Arsen Mirzaev Tatyano-Muradovich d9337944b1 Работа над сайтом 20 2021-08-30 06:41:19 +10:00
Arsen Mirzaev Tatyano-Muradovich fab247b4bc Исправление ошибок 2021-08-16 15:22:24 +10:00
Arsen Mirzaev Tatyano-Muradovich 96d9a0307c Исправление ошибок 2021-08-16 04:12:22 +10:00
Arsen Mirzaev Tatyano-Muradovich 42d20fd313 Исправление ошибок 2021-08-16 04:03:51 +10:00
Arsen Mirzaev Tatyano-Muradovich 57710d8a96 Исправление ошибок 2021-08-16 04:00:32 +10:00
Arsen Mirzaev Tatyano-Muradovich 044d80ef3e Работа над сайтом 19 2021-08-16 03:55:36 +10:00
Arsen Mirzaev Tatyano-Muradovich dc30b8c6bb Работа над сайтом 19 2021-08-16 03:53:25 +10:00
Arsen Mirzaev Tatyano-Muradovich 5e01240938 Работа над сайтом 228 2021-08-09 23:48:43 +10:00
Arsen Mirzaev Tatyano-Muradovich b7c271141f Грамматическое исправление 2021-07-30 20:51:43 +10:00
Arsen Mirzaev Tatyano-Muradovich d087256181 Обновление панели аутентификации и отображения товаров в заказах 2021-07-30 20:43:37 +10:00
Arsen Mirzaev Tatyano-Muradovich 8270eaed11 Исправления календаря 2021-07-28 15:03:18 +10:00
Arsen Mirzaev Tatyano-Muradovich 7647ca69d5 Доработка календаря (исправление) 2021-07-28 13:41:47 +10:00
Arsen Mirzaev Tatyano-Muradovich b80b887a11 Доработка календаря 2021-07-28 13:41:21 +10:00
Arsen Mirzaev Tatyano-Muradovich 49ed9bf59e Исправление отображения иконок, а так же ошибки с заголовком referrer 2021-07-28 10:39:50 +10:00
Arsen Mirzaev Tatyano-Muradovich a6d8ee5e59 Работа над сайтом 17 2021-07-27 19:59:56 +10:00
Arsen Mirzaev Tatyano-Muradovich b2b5b0737c Докидываю web.php.example 2021-07-26 05:33:37 +10:00
Arsen Mirzaev Tatyano-Muradovich 5f3623a258 Работа над сайтом 16 2021-07-26 05:27:41 +10:00
Arsen Mirzaev Tatyano-Muradovich 177de6b3ef Работа над сайтом 16 2021-07-25 09:05:14 +10:00
Arsen Mirzaev Tatyano-Muradovich b3b5111006 Работа над сайтом 15 2021-07-19 07:58:31 +10:00
Arsen Mirzaev Tatyano-Muradovich bf821a9819 Работа над сайтом 14 2021-07-12 04:21:32 +10:00
Arsen Mirzaev Tatyano-Muradovich 2ca929e122 Работа над сайтом 14 2021-07-09 06:49:07 +10:00
Arsen Mirzaev Tatyano-Muradovich 26686374f0 Исправления 2021-07-07 07:05:23 +10:00
Arsen Mirzaev Tatyano-Muradovich d5bb9137f7 Работа над сайтом 13 2021-07-07 06:56:04 +10:00
Arsen Mirzaev Tatyano-Muradovich fc59eff6db Работа над сайтом 13 2021-07-07 06:54:33 +10:00
Arsen Mirzaev Tatyano-Muradovich 83295650c9 Работа над сайтом 12 2021-06-29 09:08:01 +10:00
Arsen Mirzaev Tatyano-Muradovich c4035d8ad3 Улучшенная регистрация пользователя 2021-06-24 15:18:34 +10:00
Arsen Mirzaev Tatyano-Muradovich cc1e7e7d66 Работа над сайтом 11 2021-06-21 09:21:10 +10:00
Arsen Mirzaev Tatyano-Muradovich 8c4ca42d3c Отправка на почту 2021-06-21 04:21:28 +10:00
Arsen Mirzaev Tatyano-Muradovich cb315a9fcf Обновление панели заказов 2021-06-16 08:24:26 +10:00
Arsen Mirzaev Tatyano-Muradovich 9e120afc24 Откатываю вперёд 2021-06-15 08:59:53 +10:00
Arsen Mirzaev Tatyano-Muradovich 44ca2e9f92 bootsrap fix 2021-06-15 08:41:16 +10:00
Arsen Mirzaev Tatyano-Muradovich 0fa1c977ca пох 2021-06-15 08:11:29 +10:00
Arsen Mirzaev Tatyano-Muradovich 0d654ad790 Изменение полей в панели модератора + генерация счетов 2021-06-14 20:57:29 +10:00
Arsen Mirzaev Tatyano-Muradovich 4f898bf796 Доработка 2021-06-07 05:53:14 +10:00
Arsen Mirzaev Tatyano-Muradovich 21cec0c5b4 Изменение отображения в поиске 2021-06-07 03:49:13 +10:00
Arsen Mirzaev Tatyano-Muradovich fab290eacd Проработка панели модератора 2021-06-01 09:03:33 +10:00
Arsen Mirzaev Tatyano-Muradovich 4f8a99d8e2 Создание панели модератора и доработка уведомлений 2021-05-25 06:06:55 +10:00
Arsen Mirzaev Tatyano-Muradovich 248f5470f9 Небольшие внешние изменения 2021-05-17 01:49:45 +10:00
Arsen Mirzaev Tatyano-Muradovich 7e5f39c8e9 Страницы поставщикам и покупателям переделаны + исправление доставки 2021-05-11 06:47:04 +10:00
Arsen Mirzaev Tatyano-Muradovich 5d9228ec1b Проверка подтверждения оферты через сессии 2021-05-04 08:20:21 +10:00
Arsen Mirzaev Tatyano-Muradovich 3cf9f24a20 Докидываю файл настроек 2021-05-04 08:01:27 +10:00
Arsen Mirzaev Tatyano-Muradovich 03b52d071d Оферта, доставка, ограничения, страницы покупателям 2021-05-04 07:59:37 +10:00
Arsen Mirzaev Tatyano-Muradovich 3ea7a9e1eb Докидываю 2021-04-25 20:09:54 +10:00
Arsen Mirzaev Tatyano-Muradovich 85e3d5f1bd Исправления 2021-04-25 20:09:39 +10:00
Arsen Mirzaev Tatyano-Muradovich 0a8c74a9a3 Псевдоанонимные идентификаторы аккаунтов и комментарий к заказу 2021-04-25 20:00:05 +10:00
Arsen Mirzaev Tatyano-Muradovich ada91bd0b7 Забыл отправить шаблоны файлов настроек 2021-04-19 07:14:44 +10:00
Arsen Mirzaev Tatyano-Muradovich 7865cac5d4 Подключение к ДеловыеЛинии 2021-04-19 07:11:38 +10:00
Arsen Mirzaev Tatyano-Muradovich d4bc3e2263 Исправления и начало добавления рассчёта доставки 2021-04-13 07:21:31 +10:00
Arsen Mirzaev Tatyano-Muradovich fe0453f91b Исправления 2021-04-12 05:32:13 +10:00
Arsen Mirzaev Tatyano-Muradovich a11a5da2e1 Доработка экспорта заказов 2021-04-12 05:28:46 +10:00
Arsen Mirzaev Tatyano-Muradovich b44194d5a3 test -> dev 2021-04-11 11:50:32 +10:00
Arsen Mirzaev Tatyano-Muradovich 874d5c3f55 Исправление ошибок для импорта из 1С 2021-04-11 11:48:32 +10:00
Arsen Mirzaev Tatyano-Muradovich b50049eb67 В контроллер скопипастил модель... Исправление 2021-04-11 04:08:09 +10:00
Arsen Mirzaev Tatyano-Muradovich c169347687 Исправление контроллера ошибок и моделей продуктов 2021-04-11 04:03:32 +10:00
Arsen Mirzaev Tatyano-Muradovich 912b25bea2 Разделение индексного файла (наконец-то) 2021-04-11 03:42:10 +10:00
Arsen Mirzaev Tatyano-Muradovich 7ca19da826 Доработка страницы товаров и их администрирования 2021-04-04 23:54:34 +10:00
Arsen Mirzaev Tatyano-Muradovich 4c61ccbb65 Исправления 2021-03-29 11:23:00 +10:00
Arsen Mirzaev Tatyano-Muradovich a9259eb7dc Исправления 2021-03-29 10:26:55 +10:00
Arsen Mirzaev Tatyano-Muradovich 6dc1b081f9 Докидываю 2021-03-29 10:20:35 +10:00
Arsen Mirzaev Tatyano-Muradovich 3fea918255 Добавлены сессии, разделение по правам доступа, редактирование товаров 2021-03-29 10:19:03 +10:00
Arsen Mirzaev Tatyano-Muradovich 722bf6c378 Разработка сессий, переработка под браузеры, уведомления приходят только получателю 2021-03-22 05:03:37 +10:00
Arsen Mirzaev Tatyano-Muradovich 8cbe77e354 Исправление багов 2021-03-16 03:10:50 +10:00
Arsen Mirzaev Tatyano-Muradovich b71f6e8efc Большое исправление ВСЕГО 2021-03-16 02:50:45 +10:00
Arsen Mirzaev Tatyano-Muradovich 1fac5cdfc1 Доработка настроек 2021-03-10 12:08:25 +10:00
Arsen Mirzaev Tatyano-Muradovich 081a34a8f4 Доработка корзины, фронтенд изменений 2021-03-09 08:11:55 +10:00
Arsen Mirzaev Tatyano-Muradovich 53d7e2a048 Создание корзины 2021-03-07 20:29:19 +10:00
Arsen Mirzaev Tatyano-Muradovich b8c1f7cef0 Исправление ошибок с тегами 2021-02-25 03:10:29 +10:00
Arsen Mirzaev Tatyano-Muradovich b91ef68091 Исправление ошибок 2021-02-25 01:10:26 +10:00
Arsen Mirzaev Tatyano-Muradovich 301b2945bc Исправление багов 2021-02-24 22:07:12 +10:00
Arsen Mirzaev Tatyano-Muradovich 78ae20cdac Исправление багов 2021-02-24 22:06:59 +10:00
Arsen Mirzaev Tatyano-Muradovich 65c2e19e55 Исправление тегов вставки кода 2021-02-24 20:54:00 +10:00
Arsen Mirzaev Tatyano-Muradovich c9e3b542b7 Очистка мусора 2021-02-24 08:37:57 +10:00
Arsen Mirzaev Tatyano-Muradovich 8a3897ad8f Создан поиск, уведомления, настройки, мониторгин, админ-панель 2021-02-24 08:24:57 +10:00
RedHood 0f8e6a7a6a Исправления наследовавших классов 2021-01-31 20:39:02 +10:00
RedHood 6a7f8897d5 Исправление представлений 2021-01-31 17:23:17 +10:00
RedHood 706c9b0743 Обновление composer.json 2021-01-31 11:36:59 +10:00
RedHood 25709ee380 Доработка поиска, поиск аттрибутов 2021-01-31 11:31:22 +10:00
RedHood 5d18c95dc4 Починка представления главной страницы 2021-01-24 17:00:53 +10:00
RedHood aaec25f64c composer 2021-01-24 16:49:39 +10:00
RedHood 9887be35f9 Обновление composer.json 2021-01-24 16:27:45 +10:00
RedHood c2afcfcc5a Поиск по товарам 2021-01-24 16:25:08 +10:00
root 212694c917 Создан импорт из 1C, без поддержки групп 2021-01-19 07:42:10 +00:00
RedHood 94b719b67e Лень соединять коммиты, это туда же 2021-01-13 14:46:40 +10:00
RedHood ac48e8e064 Добавлена точка с запятой как вариант решения проблемы со сжатием кода 2021-01-13 14:46:30 +10:00
RedHood d8a1284cc5 Небольшие доработки 2021-01-13 14:15:30 +10:00
RedHood fbd64ecb96 Разработка архитектуры, профиля, загрузки из Excel и 1C, много мелких изменений 2021-01-13 14:04:59 +10:00
456 changed files with 62372 additions and 5762 deletions

5
.gitignore vendored
View File

@ -1,2 +1,3 @@
vendor/
cache/
/vendor
/cache
/.vscode

View File

@ -8,41 +8,39 @@
{
"name": "Arsen Mirzaev Tatyano-Muradovich",
"email": "red@hood.su",
"homepage": "https://hood.su/mirzaev",
"role": "Developer"
}
],
"require": {
"php": ">=7.4.0",
"twbs/bootstrap": ">=4.5",
"yiisoft/yii2": ">=2.0.14",
"php": "^8.1.0",
"ext-intl": "~8.0",
"twbs/bootstrap": "4.6.0",
"yiisoft/yii2": "2.*",
"yiisoft/yii2-bootstrap": ">=2.0.0",
"yiisoft/yii2-swiftmailer": ">=2.0.0",
"bower-asset/bootstrap": "*",
"npm-asset/jquery": "^3.5",
"bower-asset/jquery": "^3.5",
"explosivebit/arangodb": "^2.0",
"triagens/arangodb": "^3.6"
"triagens/arangodb": "^3.6",
"moonlandsoft/yii2-phpexcel": ">=2.0",
"carono/yii2-1c-exchange": "^0.3.1",
"yiisoft/yii2-imagine": "*",
"mirzaev/yii2-arangodb": ">=2.1.x-dev",
"mirzaev/yii2-arangodb-sessions": ">=1.1.x-dev",
"guzzlehttp/guzzle": "^7.3",
"phpoffice/phpspreadsheet": "^1.18",
"hflabs/dadata": "^20.12"
},
"require-dev": {
"codeception/codeception": ">=4.1",
"codeception/module-webdriver": ">=1.0.0",
"yiisoft/yii2-debug": ">=2.1.0",
"yiisoft/yii2-gii": ">=2.1.0",
"yiisoft/yii2-faker": ">=2.0.0",
"codeception/verify": ">=1.1.0",
"codeception/specify": ">=0.4.6",
"symfony/browser-kit": ">=2.7",
"codeception/module-filesystem": ">=1.0.0",
"codeception/module-yii2": ">=1.0.0",
"codeception/module-asserts": ">=1.0.0"
"yiisoft/yii2-faker": ">=2.0.0"
},
"autoload": {
"psr-4": {
"mirzaev\\skillparts\\": "mirzaev/skillparts/system"
},
"classmap": [
"vendor/explosivebit"
]
}
},
"autoload-dev": {
"psr-4": {
@ -85,18 +83,6 @@
{
"type": "composer",
"url": "https://asset-packagist.org"
},
{
"type": "package",
"package": {
"name": "explosivebit/arangodb",
"version": "2.0",
"source": {
"type": "git",
"url": "https://github.com/pBazsi/yii2-arangodb.git",
"reference": "master"
}
}
}
]
}

6049
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
/import/*
/invoices/*
/accounts/*

View File

@ -1,21 +1,10 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace app\assets;
use yii;
use yii\web\AssetBundle;
/**
* Main application asset bundle.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class AppAsset extends AssetBundle
{
public $basePath = '@webroot';
@ -25,23 +14,35 @@ class AppAsset extends AssetBundle
'css/bootstrap/bootstrap.min.css',
'css/main.css',
'css/header.css',
'css/notification.css',
'css/info_panel.css',
'css/categories_blocks_panel.css',
'css/ticker.css',
'css/footer.css'
];
public $js = [
'https://kit.fontawesome.com/d7e922c226.js',
'https://code.jquery.com/jquery-3.5.1.min.js',
'js/bootstrap/popper.min.js',
'js/bootstrap/bootstrap.min.js',
'https://cdn.jsdelivr.net/bxslider/4.1.1/jquery.bxslider.min.js',
'https://unpkg.com/cookielib/src/cookie.min.js',
'https://api-maps.yandex.ru/2.1/?apikey=ff21ed7c-2d34-4f91-8d7f-2144ec3e4397&lang=ru_RU',
'js/moment.min.js',
'js/menu.js',
'js/main.js',
'js/account.js',
'js/ticker.js',
'js/reinitialization.js'
'js/search.js',
'js/notification.js',
'js/reinitialization.js',
'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
];
public $depends = [
'yii\web\YiiAsset',
'yii\web\YiiAsset'
// 'yii\bootstrap\BootstrapAsset'
];
}

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

@ -0,0 +1,35 @@
<?php
namespace app\commands;
use yii\console\Controller;
use yii\console\ExitCode;
use app\models\connection\Dellin;
class DellinController extends Controller
{
/**
* Импортировать города из ДеловыеЛинии
*/
public function actionImportCities()
{
if (Dellin::importCities()) {
return ExitCode::OK;
}
return ExitCode::UNSPECIFIED_ERROR;
}
/**
* Импортировать терминалы из ДеловыеЛинии
*/
public function actionImportTerminals(?int $account = null)
{
if (Dellin::importTerminals($account)) {
return ExitCode::OK;
}
return ExitCode::UNSPECIFIED_ERROR;
}
}

View File

@ -0,0 +1,63 @@
<?php
namespace app\commands;
use yii\console\Controller;
use yii\console\ExitCode;
use moonland\phpexcel\Excel;
use app\models\Supply;
use app\models\File;
class ImportController extends Controller
{
/**
* Импортировать города из ДеловыеЛинии
*/
public function actionCities()
{
if (Dellin::importCities()) {
return ExitCode::OK;
}
return ExitCode::UNSPECIFIED_ERROR;
}
/**
* Импортировать терминалы из ДеловыеЛинии
*/
public function actionTerminals()
{
if (Dellin::importTerminals()) {
return ExitCode::OK;
}
return ExitCode::UNSPECIFIED_ERROR;
}
/**
* Импортировать поставки из файлов
*/
public function actionSupplies(int $amount = 3)
{
try {
$files = File::searchSuppliesNeededToLoad($amount);
if ($files > 0) {
// Найдены файлы
foreach ($files as $file) {
// Перебор файлов для загрузки
// Загрузка в базу данных
Supply::loadExcel($file);
}
}
} catch (exception $e) {
return ExitCode::UNSPECIFIED_ERROR;
}
return ExitCode::OK;
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace app\commands;
use yii\console\Controller;
use yii\console\ExitCode;
use app\models\Supply;
use app\models\File;
class SuppliesController extends Controller
{
/**
* Импортировать города из ДеловыеЛинии
*/
public function actionImport(int $amount = 3)
{
if (Dellin::importCities($amount)) {
return ExitCode::OK;
}
return ExitCode::UNSPECIFIED_ERROR;
}
}

View File

@ -0,0 +1,136 @@
<?php
namespace app\commands;
use yii\console\Controller;
use yii\console\ExitCode;
use app\models\Invoice;
use app\models\Product;
use app\models\ProductGroup;
use app\models\ImportEdgeSupply;
class TestController extends Controller
{
public function actionInvoice($buyer = 123123, $order = 0)
{
// Генерация счета
Invoice::generate($order, $this->renderPartial('/invoice/order/pattern', [
'buyer' => [
'id' => $buyer,
'info' => 'Неизвестно'
],
'order' => [
'id' => $order,
'date' => time(),
'entries' => [
[
'title' => 'Тестовое вхождение',
'amount' => [
'value' => 1,
'unit' => 'шт'
],
'cost' => [
'value' => 1000,
'unit' => 'руб'
],
'type' => 'supply'
],
[
'title' => 'Тестовое вхождение',
'amount' => [
'value' => 1,
'unit' => 'шт'
],
'cost' => [
'value' => 1000,
'unit' => 'руб'
],
'type' => 'supply'
],
[
'title' => 'Тестовое вхождение',
'amount' => [
'value' => 1,
'unit' => 'шт'
],
'cost' => [
'value' => 1000,
'unit' => 'руб'
],
'type' => 'supply'
],
[
'title' => 'Тестовое вхождение',
'amount' => [
'value' => 5,
'unit' => 'шт'
],
'cost' => [
'value' => 1000,
'unit' => 'руб'
],
'type' => 'supply'
]
]
]
]));
return ExitCode::OK;
}
public function actionAnalogs($_id = 'product/51987159')
{
return ExitCode::OK;
}
public function actionWriteAnalog($_id = 'product/51987159', $analog = 'product/12051485')
{
// Инициализация товара
$product = Product::searchById($_id);
// Инициализация аналога
$analog = Product::searchById($analog);
if (!$group = ProductGroup::searchByProduct($product)) {
// Не найдена группа товаров
// Запись новой группы
$group = ProductGroup::writeEmpty(active: true);
// Запись товара в группу
$group->writeProduct($product);
}
if ($_group = ProductGroup::searchByProduct($analog)) {
// Найдена друга группа у товара который надо добавить в группу
// Перенос всех участников (включая целевой товар)
return $group->transfer($_group);
} else {
// Не найдена группа у товара который надо добавить в группу
// Запись целевого товара в группу
return $group->writeProduct($analog);
}
return ExitCode::OK;
}
public function actionReadAnalog($_id = 'product/51987159')
{
var_dump((ProductGroup::searchByProduct(Product::searchById($_id))->searchProducts()));
return ExitCode::OK;
}
public function actionEdgeMax()
{
var_dump(ImportEdgeSupply::generateVersion());
return ExitCode::OK;
}
}

View File

@ -1,15 +1,16 @@
<?php
declare(strict_types=1);
$config = [
'id' => 'basic-console',
'id' => 'skillparts-console',
'basePath' => dirname(__DIR__),
'bootstrap' => ['log'],
'controllerNamespace' => 'app\commands',
'aliases' => [
'@vendor' => dirname(__DIR__) . '../../../../vendor',
'@vendor' => dirname(__DIR__) . '/../../../vendor',
'@bower' => '@vendor/bower-asset',
'@npm' => '@vendor/npm-asset',
'@explosivebit' => '@vendor/explosivebit',
'@tests' => '@app/tests',
],
'components' => [
@ -28,13 +29,14 @@ $config = [
],
'params' => require __DIR__ . '/params.php',
'controllerMap' => [
'arangodb-migrate' => 'explosivebit\arangodb\console\controllers\MigrateController',
'arangodb-migrate' => 'mirzaev\yii2\arangodb\console\controllers\MigrateController',
'fixture' => [
'class' => 'yii\faker\FixtureController',
],
]
];
if (YII_ENV_DEV) {
// configuration adjustments for 'dev' environment
$config['bootstrap'][] = 'gii';

View File

@ -3,7 +3,7 @@
use ArangoDBClient\ConnectionOptions;
return [
'class' => '\explosivebit\arangodb\Connection',
'class' => '\mirzaev\yii2\arangodb\Connection',
'connectionOptions' => [
ConnectionOptions::OPTION_DATABASE => '',
ConnectionOptions::OPTION_ENDPOINT => 'tcp://127.0.0.1:8529',

View File

@ -1,7 +1,28 @@
<?php
return [
'adminEmail' => 'admin@example.com',
'senderEmail' => 'noreply@example.com',
'senderName' => 'Example.com mailer',
'captcha' => [
'suppliers' => [
'public' => null,
'secret' => null
]
],
'mail' => [
'system' => null,
'info' => null
],
'dellin' => [
'nickname' => null,
'password' => null,
'key' => null
],
'cdek' => [
'nickname' => null,
'password' => null,
'key' => null
],
'dadata' => [
'key' => null,
'secret' => null
]
];

View File

@ -1,40 +0,0 @@
<?php
/**
* Application configuration shared by all test types
*/
return [
'id' => 'basic-tests',
'basePath' => dirname(__DIR__),
'aliases' => [
'@bower' => '@vendor/bower-asset',
'@npm' => '@vendor/npm-asset',
],
'language' => 'en-US',
'components' => [
'db' => require __DIR__ . '/test_db.php',
'mailer' => [
'useFileTransport' => true,
],
'assetManager' => [
'basePath' => __DIR__ . '/../web/assets',
],
'urlManager' => [
'showScriptName' => true,
],
'user' => [
'identityClass' => 'app\models\Account',
],
'request' => [
'cookieValidationKey' => 'test',
'enableCsrfValidation' => false,
// but if you absolutely need it set cookie domain to localhost
/*
'csrfCookie' => [
'domain' => 'localhost',
],
*/
],
],
'params' => require __DIR__ . '/params.php',
];

View File

@ -1,6 +0,0 @@
<?php
$db = require __DIR__ . '/db.php';
// test database! Important not to run tests on production or development databases
$db['dsn'] = 'mysql:host=localhost;dbname=yii2basic_test';
return $db;

View File

@ -1,14 +1,14 @@
<?php
$config = [
'id' => 'basic',
'id' => 'skillparts',
'basePath' => dirname(__DIR__),
'bootstrap' => ['log'],
'defaultRoute' => 'main',
'aliases' => [
'@vendor' => dirname(__DIR__) . '/../../../vendor',
'@bower' => '@vendor/bower-asset',
'@npm' => '@vendor/npm-asset',
'@explosivebit' => '@vendor/explosivebit',
'@npm' => '@vendor/npm-asset'
],
'components' => [
'request' => [
@ -20,24 +20,61 @@ $config = [
'class' => 'yii\caching\FileCache',
],
'user' => [
'identityClass' => 'app\models\Account',
'enableAutoLogin' => true,
'identityClass' => 'app\models\Account',
'loginUrl' => ['/authentication'],
// 'enableAutoLogin' => true,
'enableSession' => true
],
// 'session' => [
// 'class' => 'yii\web\Session',
// 'cookieParams' => ['lifetime' => 3600 * 24 * 30 * 12],
// 'timeout' => 3600 * 24 * 30 * 12,
// 'useCookies' => true,
// 'authManager' => [
// 'class' => 'mirzaev\yii2\arangodb\rbac\DbManager',
// ],
'errorHandler' => [
'errorAction' => 'site/error',
'session' => [
'class' => 'mirzaev\yii2\arangodb\sessions\ArangoDbSession',
'document' => 'session',
'cookieParams' => [
// 'lifetime' => 3600 * 24 * 30 * 12
'lifetime' => 3600 * 24 * 3
],
'writeCallback' => function($session): array {
// Инициализация
$data = [];
yii::$app->request->userIP and $data['ip'] = yii::$app->request->userIP;
Yii::$app->user->id and $data['account'] = Yii::$app->user->id;
return $data ?? [];
},
'timeout' => 3600 * 24 * 3,
'useStrictMode' => false,
'useCookies' => true
],
'mailer' => [
'errorHandler' => [
'errorAction' => 'error',
],
'mail_info' => [
'class' => 'yii\swiftmailer\Mailer',
// send all mails to a file by default. You have to set
// 'useFileTransport' to false and configure a transport
// for the mailer to send real emails.
'useFileTransport' => true,
'useFileTransport' => false,
'transport' => [
'class' => 'Swift_SmtpTransport',
'host' => 'smtp.yandex.com',
'username' => 'info@skillparts.ru',
'password' => 'SkillParts_1337',
'port' => '465',
'encryption' => 'ssl',
],
],
'mail_system' => [
'class' => 'yii\swiftmailer\Mailer',
'useFileTransport' => false,
'transport' => [
'class' => 'Swift_SmtpTransport',
'host' => 'smtp.yandex.com',
'username' => 'system@skillparts.ru',
'password' => 'System01001010Null',
'port' => '465',
'encryption' => 'ssl',
],
],
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
@ -50,16 +87,177 @@ $config = [
],
'arangodb' => require __DIR__ . '/db.php',
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
['class' => 'yii\rest\UrlRule', 'controller' => 'site'],
'<action>' => 'site/<action>'
[
'class' => 'yii\rest\UrlRule',
'controller' => 'main'
],
'<_key:[0-9]+>' => 'account/index',
'<_key:[0-9]+>/<target:[^/]+>/<action:(read|edit|delete|regenerate)>' => 'account/<action>',
'<_key:[0-9]+>/files/<file:[^/]+>' => 'account/file',
'<_key:[0-9]+>/<action:(accept|decline|data)>' => 'account/<action>',
'product/<prod:[^/]+>/<catn:[^/]+>' => 'product/index',
'product/<prod:[^/]+>/<catn:[^/]+>/<action:(write|delete|connect|disconnect|status)>' => 'product/<action>',
'<section:(product|cart)>/<prod:[^/]+>/<catn:[^/]+>/<action:(read|write|edit|delete)>/<target:(title|catn|name|dscr|prod|dmns|wght|image|cover|comm)>' => '<section>/<action>-<target>',
'products/<action:(read)>/<stts:[^/]+>/' => 'product/<action>',
'profile/geolocation/<action:(init|write)>' => 'profile/geolocation-<action>',
'profile/panel/<panel:(suppliers)>/<block:(requests)>/<action:(search)>' => 'profile/panel-<panel>-<block>-<action>',
'profile/imports/<action:(delete)>' => 'profile/imports-<action>',
'profile/warehouses/<action:(write|delete|rename|close|open)>' => 'profile/warehouses-<action>',
'orders' => 'order/index',
'orders/<filter:[^/]+>' => 'order/index',
'orders/<catn:[^/]+>/<action:(accept)>' => 'order/<action>',
'orders/supply/<catn:[^/]+>/<action:(read|write|edit|delete)>' => 'order/supply-<action>',
'orders/supply/<catn:[^/]+>/<action:(read|write|edit|delete)>/<target:(stts|cost|time|comm)>' => 'order/supply-<action>-<target>',
'invoices/<order:[^/]+>' => 'invoice/index',
'invoices/<order:[^/]+>/<action:(download)>' => 'invoice/<action>',
'verify/send' => 'verify/send',
'verify/<vrfy:[^/]+>' => 'verify/index',
'terminals/<action:(read|write)>' => 'terminal/<action>'
],
],
],
'modules' => [
'exchange' => [
'class' => 'carono\exchange1c\ExchangeModule',
'exchangeDocuments' => true,
'validateModelOnSave' => true,
'groupClass' => 'app\models\SupplyGroup',
'productClass' => 'app\models\Supply',
'offerClass' => 'app\models\SupplyEdgeProduct',
'partnerClass' => 'app\models\Account',
'documentClass' => 'app\models\Order',
'auth' => function ($mail, $pswd) {
// Необходимо уничтожить AccountForm
// return (new \app\models\AccountForm())->authentication($mail, $pswd);
if ($user = \app\models\Account::findByMail($mail)) {
if ($user->validatePassword($pswd)) {
return $user;
}
}
return false;
}
]
],
'params' => require __DIR__ . '/params.php',
'on beforeAction' => function ($event) {
if (yii::$app->user->isGuest) {
// Гость
} else {
// Пользователь
// Подтверждение почты
if (yii::$app->user->identity->vrfy !== true) {
// Почта не подтверждена
if (!(str_starts_with(yii::$app->request->getPathInfo(), 'verify')
|| match (yii::$app->request->getPathInfo()) {
'policy', 'notification', 'identification' => true,
default => false
})) {
// Фильтрация страниц
if (yii::$app->request->isPost) {
// POST-запрос
yii::$app->response->format = Response::FORMAT_JSON;
yii::$app->response->statusCode = 401;
return [
'main' => yii::$app->controller->renderPartial('/account/verify'),
'redirect' => '/registration',
'_csrf' => yii::$app->request->getCsrfToken()
];
} else {
// Подразумевается как GET-запрос
// Переадресация на страницу указывающую на необходимость подтвердить почту
yii::$app->response->redirect('/verify')->send();
}
}
}
// Согласие с офертой
if (
!(isset(yii::$app->session['offer_buyer_accepted'])
&& yii::$app->session['offer_buyer_accepted'] === true)
&& (!isset(yii::$app->user->identity->acpt['buyer'])
|| yii::$app->user->identity->acpt['buyer'] === false)
) {
// Нет подтверждения офферты пользователя
if (!(str_starts_with(yii::$app->request->getPathInfo(), 'verify')
|| str_starts_with(yii::$app->request->getPathInfo(), 'offer')
|| match (yii::$app->request->getPathInfo()) {
'notification', 'identification' => true,
default => false
})) {
// Фильтрация страниц
if (yii::$app->request->isPost) {
// POST-запрос
yii::$app->response->format = Response::FORMAT_JSON;
yii::$app->response->statusCode = 401;
return [
'main' => yii::$app->controller->renderPartial('/offer/index'),
'redirect' => '/registration',
'_csrf' => yii::$app->request->getCsrfToken()
];
} else {
// Подразумевается как GET-запрос
// Переадресация на оферту
yii::$app->response->redirect('/offer')->send();
}
}
} else if (
(isset(yii::$app->user->identity->agnt)
&& yii::$app->user->identity->agnt === true)
&& !(isset(yii::$app->session['offer_supplier_accepted'])
&& yii::$app->session['offer_supplier_accepted'] === true)
&& (!isset(yii::$app->user->identity->acpt['supplier'])
|| yii::$app->user->identity->acpt['supplier'] === false)
) {
// Нет подтверждения офферты поставщика
if (!(str_starts_with(yii::$app->request->getPathInfo(), 'verify')
|| str_starts_with(yii::$app->request->getPathInfo(), 'offer')
|| match (yii::$app->request->getPathInfo()) {
'notification', 'identification' => true,
default => false
})) {
// Фильтрация страниц
if (yii::$app->request->isPost) {
// POST-запрос
yii::$app->response->format = Response::FORMAT_JSON;
yii::$app->response->statusCode = 401;
return [
'main' => $this->renderPartial('/offer/supplier'),
'redirect' => '/registration',
'_csrf' => yii::$app->request->getCsrfToken()
];
} else {
// Подразумевается как GET-запрос
// Переадресация на оферту
yii::$app->response->redirect('/offer/suppliers')->send();
}
}
}
}
}
];
if (YII_ENV_DEV) {
@ -68,18 +266,9 @@ if (YII_ENV_DEV) {
'class' => 'yii\debug\Module',
'panels' => [
'ArangoDB' => [
'class' => 'explosivebit\arangodb\panels\arangodb\ArangoDbPanel'
'class' => 'mirzaev\yii2\arangodb\panels\arangodb\ArangoDbPanel'
]
],
// uncomment the following to add your IP if you are not connecting from localhost.
//'allowedIPs' => ['127.0.0.1', '::1'],
];
$config['bootstrap'][] = 'gii';
$config['modules']['gii'] = [
'class' => 'yii\gii\Module',
// uncomment the following to add your IP if you are not connecting from localhost.
//'allowedIPs' => ['127.0.0.1', '::1'],
]
];
}

View File

@ -0,0 +1,484 @@
<?php
declare(strict_types=1);
namespace app\controllers;
use yii;
use yii\web\Controller;
use yii\web\Response;
use yii\web\Cookie;
use yii\filters\AccessControl;
use app\models\Account;
use app\models\AccountForm;
class AccountController extends Controller
{
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::class,
'rules' => [
[
'allow' => true,
'actions' => [
'file',
'data',
'restore',
'generate-password'
]
],
[
'allow' => true,
'roles' => ['@'],
'actions' => [
'index',
'edit',
'regenerate'
]
],
[
'allow' => true,
'actions' => [
'read',
'accept',
'decline'],
'matchCallback' => function ($rule, $action): bool {
if (
!yii::$app->user->isGuest
&& (yii::$app->user->identity->type === 'administrator'
|| yii::$app->user->identity->type === 'moderator')
) {
return true;
}
return false;
}
],
[
'allow' => false,
'roles' => ['?'],
'denyCallback' => [$this, 'accessDenied']
]
]
]
];
}
public function accessDenied()
{
// Инициализация
$cookies = yii::$app->response->cookies;
// Запись cookie с редиректом, который выполнится после авторизации
$cookies->add(new Cookie([
'name' => 'redirect',
'value' => yii::$app->request->pathInfo
]));
if (Yii::$app->request->isPost) {
// POST-запрос
// Настройка
Yii::$app->response->format = Response::FORMAT_JSON;
// Генерация ответа
Yii::$app->response->content = json_encode([
'main' => $this->renderPartial('/account/index'),
'redirect' => yii::$app->request->pathInfo,
'_csrf' => Yii::$app->request->getCsrfToken()
]);
} else if (Yii::$app->request->isGet) {
// GET-запрос
$this->redirect('/authentication');
}
}
public function actionIndex()
{
return $this->renderPartial('/accounts/index');
}
/**
* Подтверждение
*
* @param int $_key Идентификатор аккаунта
*/
public function actionAccept(string $_key)
{
if (yii::$app->user->isGuest) {
// Аккаунт не аутентифицирован
} else {
if (yii::$app->request->isPost) {
// AJAX-POST-запрос
if (
yii::$app->user->identity->type === 'administrator'
|| yii::$app->user->identity->type === 'moderator'
) {
// Запрос произведен уполномоченным
if ($account = Account::searchById(Account::collectionName() . '/' . $_key)) {
// Аккаунт найден
$account->type = 'user';
if ($account->update() > 0) {
// Удалось перезаписать данные в хранилище
// Запись в журнал
$account->journal('accepted into suppliers');
// Отправка письма с подтверждением аккаунта
$account->sendMailVerify();
// Настройка формата ответа
yii::$app->response->format = Response::FORMAT_JSON;
return true;
}
}
}
}
}
// Запись кода ответа
yii::$app->response->statusCode = 500;
return false;
}
/**
* Отказ
*
* @param int $_key Идентификатор аккаунта
*/
public function actionDecline(string $_key)
{
if (yii::$app->user->isGuest) {
// Аккаунт не аутентифицирован
} else {
if (yii::$app->request->isPost) {
// AJAX-POST-запрос
if (
yii::$app->user->identity->type === 'administrator'
|| yii::$app->user->identity->type === 'moderator'
) {
// Запрос произведен уполномоченным
if ($account = Account::searchById(Account::collectionName() . '/' . $_key)) {
// Аккаунт найден
// Отправка письма с отказом и причиной
$account->sendMailDecline(yii::$app->request->post('reason') ?? yii::$app->request->get('reason'));
// Настройка формата ответа
yii::$app->response->format = Response::FORMAT_JSON;
// Удаление аккаунта
return $account->delete() > 0;
}
}
}
}
// Запись кода ответа
yii::$app->response->statusCode = 500;
return false;
}
/**
* Запрос файла
*
* @param int $_key Идентификатор аккаунта
* @param string $file Путь до файла от каталога аккаунта
*/
public function actionFile(string $_key, string $file)
{
// Инициализация файла
$file = YII_PATH_PUBLIC . "/../assets/accounts/$_key/files/$file";
if (file_exists($file)) {
// Удалось найти файл
return $this->response->sendFile($file);
} else {
// Не удалось найти файл
// Запись кода ответа
yii::$app->response->statusCode = 500;
// Перенаправление на страницу аккаунта
return yii::$app->response->redirect("/$_key");
}
}
/**
* Редактирование параметра
*
* @param int $_key
* @param string $target
*
* @return void
*/
public function actionEdit(int $_key, string $target)
{
if (yii::$app->user->isGuest) {
// Аккаунт не аутентифицирован
} else {
if (yii::$app->request->isPost) {
// AJAX-POST-запрос
if (
$_key === yii::$app->user->identity->_key ||
(yii::$app->user->identity->type === 'administrator'
|| yii::$app->user->identity->type === 'moderator')
) {
// Запрос произведен с изменяемого аккаунта, либо уполномоченным
if ($account = Account::searchById(Account::collectionName() . '/' . $_key)) {
// Аккаунт найден
// Запись параметра
$account->{$target} = yii::$app->request->post('value') ?? yii::$app->request->get('value') ?? 'Неизвестно';
// Настройка формата ответа
yii::$app->response->format = Response::FORMAT_JSON;
return $account->update() > 0;
}
}
}
}
// Запись кода ответа
yii::$app->response->statusCode = 500;
return false;
}
/**
* Регенерация параметра
*
* @param int $_key Идентификатор
* @param string $target
*
* @return void
*/
public function actionRegenerate(int $_key, string $target)
{
if (yii::$app->user->isGuest) {
// Аккаунт не аутентифицирован
} else {
if (yii::$app->request->isPost) {
// AJAX-POST-запрос
if ($account = Account::searchById(Account::collectionName() . '/' . $_key)) {
// Аккаунт найден
if (
$account->_key === yii::$app->user->identity->_key ||
(yii::$app->user->identity->type === 'administrator'
|| yii::$app->user->identity->type === 'moderator')
) {
// Запрос произведен с изменяемого аккаунта, либо уполномоченным
if ($target === 'indx') {
// Запрошена регенерация индекса
// Настройка формата ответа
yii::$app->response->format = Response::FORMAT_JSON;
// Регенерация индекса
return Account::generateIndexes([$account], force: true);
}
}
}
}
}
// Запись кода ответа
yii::$app->response->statusCode = 500;
return false;
}
/**
* Информация
*
* @param int $_key Идентификатор
*/
public function actionData(int $_key)
{
if (yii::$app->request->isPost) {
// AJAX-POST-запрос
// Инициализация аккаунта (тот кто выполняет запрос)
$account = account::initAccount();
// Поиск данных об аккаунта (запрашиваемом)
$data = account::initAccount($_key)->getAttributes();
if ($account->isAdmin() || $account->isModer()) {
// Авторизован как работник
} else if ((int) $account->_key === $_key) {
// Авторизован как владелец аккаунта
unset(
$data['vrfy'],
$data['geol'],
$data['auth'],
$data['jrnl'],
$data['acpt'],
$data['pswd']
);
} else {
// Не авторизован
// Очистка от защищенных свойств
$data = [
'_key' => $data['_key'],
'indx' => $data['indx'],
'agnt' => $data['agnt'],
'type' => $data['type']
];
}
// Настройка формата ответа
yii::$app->response->format = Response::FORMAT_JSON;
return [
'data' => $data,
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
}
/**
* Восстановление пароля
*
* @return string
*/
public function actionRestore()
{
// Инициализация
$model = new AccountForm(yii::$app->request->post('AccountForm'));
$type = yii::$app->request->post('type') ?? yii::$app->request->get('type');
$target = yii::$app->request->post('target') ?? yii::$app->request->get('target');
// Фильтрация
$target = match ($target) {
'panel' => 'panel',
'main' => 'main',
default => 'main'
};
// Рендер для всплывающей панели
$panel = $target === 'panel';
if (yii::$app->request->isPost) {
// AJAX-POST-запрос
// Настройка кода ответа
yii::$app->response->format = Response::FORMAT_JSON;
if (yii::$app->user->isGuest || $model->validate()) {
// Аккаунт не аутентифицирован и проверка пройдена
// Отправка запроса на генерацию пароля и запись ответа
$return = [
'status' => Account::restoreSend($model->mail),
'_csrf' => yii::$app->request->getCsrfToken()
];
return $return;
} else {
// Аккаунт аутентифицирован
// Настройка кода ответа
yii::$app->response->statusCode = 400;
return [
'redirect' => '/',
'_csrf' => yii::$app->request->getCsrfToken()
];
}
}
}
/**
* Генерация нового пароля
*
* @return string
*/
public function actionGeneratePassword(string $id, string $key)
{
if ($account = Account::searchById(Account::collectionName() . "/$id")) {
// Найден аккаунт
if ($account->chpk === $key) {
// Ключи совпадают
// Инициализация буфера пароля
$old = $account->pswd;
// Генерация пароля
$account->restoreGenerate();
if ($account->pswd !== $old) {
// Успешно сгенерирован новый пароль
// Инициализация формы аутентификации
$form = new AccountForm;
// Запись параметров
$form->mail = $account->mail;
$form->pswd = $account->pswd;
// Аутентификация
$form->authentication();
}
}
}
// Перенаправление на главную страницу
$this->redirect('/');
}
/**
* Генерация нового пароля
*
* @return string
*/
public function actionRead(int $page = 1): string|array|null
{
if (yii::$app->request->isPost) {
// POST-запрос
// Инициализация входных параметров
$amount = yii::$app->request->post('amount') ?? yii::$app->request->get('amount') ?? 20;
$order = yii::$app->request->post('order') ?? yii::$app->request->get('order') ?? ['DESC'];
// Инициализация cookie
$cookies = yii::$app->response->cookies;
// Чтение аккаунтов
$accounts = Account::read(limit: $amount, page: $page, order: $order);
// Запись формата ответа
yii::$app->response->format = Response::FORMAT_JSON;
return [
'accounts' => $this->renderPartial('/account/list', compact('accounts', 'amount', 'page')),
'_csrf' => yii::$app->request->getCsrfToken()
];
}
return false;
}
}

View File

@ -0,0 +1,129 @@
<?php
namespace app\controllers;
use app\models\AccountForm;
use app\models\Order;
use app\models\Notification;
use yii;
use yii\web\Controller;
use yii\web\Response;
use yii\filters\AccessControl;
use Throwable;
use Exception;
use yii\bootstrap\ActiveForm;
class AuthenticationController extends Controller
{
/**
* {@inheritdoc}
*/
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::class,
'rules' => [
[
'allow' => true,
'roles' => ['?'],
]
],
]
];
}
public function actionIndex()
{
// Инициализация
$model = new AccountForm(yii::$app->request->post('AccountForm'));
$type = yii::$app->request->post('type') ?? yii::$app->request->get('type');
$target = yii::$app->request->post('target') ?? yii::$app->request->get('target');
$model->scenario = $model::SCENARIO_AUTHENTICATION;
// Фильтрация
$target = match ($target) {
'panel' => 'panel',
'main' => 'main',
default => 'main'
};
// Рендер для всплывающей панели
$panel = $target === 'panel';
if (yii::$app->request->isPost) {
// AJAX-POST-запрос
// Настройка кода ответа
yii::$app->response->format = Response::FORMAT_JSON;
if (!yii::$app->user->isGuest || $model->authentication()) {
// Аккаунт аутентифицирован
// Отправка уведомления
Notification::_write('Вы аутентифицированы с устройства ' . $_SERVER['HTTP_USER_AGENT'] . ' ' . $_SERVER['REMOTE_ADDR'], true, yii::$app->user->identity->_key, Notification::TYPE_NOTICE);
// Инициализация
$notifications_button = $this->renderPartial('/notification/button');
$notifications_panel = $this->renderPartial('/notification/panel', ['notifications_panel_full' => true]);
$cart_button = $this->renderPartial('/cart/button', ['cart_amount' => Order::count(supplies: true)]);
// Запись ответа
$return = [
'menu' => $this->renderPartial('/account/panel/authenticated', compact(
'notifications_button',
'notifications_panel',
'cart_button'
)),
'_csrf' => yii::$app->request->getCsrfToken()
];
if (($cookies = yii::$app->request->cookies)->has('redirect')) {
// Найдено cookie с переадресацией
// Запись ответа
$return['redirect'] = '/' . $cookies['redirect'];
// Очистка cookie
unset(yii::$app->response->cookies['redirect']);
yii::$app->response->format = Response::FORMAT_HTML;
// Переадресация
return $this->redirect($return['redirect']);
} else {
// Не найдено cookie с переадресацией
if (yii::$app->request->pathInfo === 'authentication' || yii::$app->request->pathInfo === 'registration') {
// Если клиент на промежуточном URI
// Запись ответа
$return['redirect'] = '/';
$return['main'] = $this->renderPartial('/index');
}
}
return $return;
} else {
// Аккаунт не аутентифицирован
// Настройка кода ответа
yii::$app->response->statusCode = 400;
return [
$target => $this->renderPartial('/account/index', compact('model', 'panel')),
'redirect' => '/authentication',
'_csrf' => yii::$app->request->getCsrfToken()
];
}
}
if (!yii::$app->user->isGuest) {
yii::$app->response->redirect('/');
} else {
return $this->render('/account/index', compact('model', 'panel'));
}
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace app\controllers;
use yii\web\Controller;
class BuyersController extends Controller
{
public function actionIndex()
{
return $this->renderPartial('/buyers/index');
}
}

View File

@ -0,0 +1,240 @@
<?php
declare(strict_types=1);
namespace app\controllers;
use app\models\Account;
use yii;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\web\Response;
use yii\web\Cookie;
use app\models\Product;
use app\models\Order;
use app\models\OrderEdgeSupply;
use app\models\Notification;
use Exception;
class CartController extends Controller
{
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::class,
'rules' => [
[
'allow' => true,
'roles' => ['@'],
'actions' => [
'index',
'edit-comm',
'count'
]
],
[
'allow' => false,
'roles' => ['?'],
'denyCallback' => [$this, 'accessDenied']
]
]
]
];
}
public function accessDenied()
{
// Инициализация
$cookies = yii::$app->response->cookies;
// Запись cookie с редиректом, который выполнится после авторизации
$cookies->add(new Cookie([
'name' => 'redirect',
'value' => yii::$app->request->pathInfo
]));
if (yii::$app->request->isPost) {
// POST-запрос
// Настройка
yii::$app->response->format = Response::FORMAT_JSON;
// Генерация ответа
yii::$app->response->content = json_encode([
'main' => $this->renderPartial('/account/index'),
'redirect' => '/cart',
'_csrf' => yii::$app->request->getCsrfToken()
]);
} else if (yii::$app->request->isGet) {
// GET-запрос
$this->redirect('/authentication');
}
}
/**
* Страница: "Корзина"
*
* @see $this->behaviors Доступ только аутентифицированным
*/
public function actionIndex(): string|array|null|Response
{
// Инициализация
$page = yii::$app->request->get('page') ?? yii::$app->request->post('page') ?? 1;
$account = Account::initAccount();
// Поиск корзины (текущего заказа)
$data = Order::searchSmart()[0] ?? null;
if (empty($data['order'])) {
// Корзина не инициализирована
// Инициализация
$data['order'] = new Order();
if ($data['order']->save()) {
// Удалось инициализировать заказ
// Подключение заказа к аккаунту
$data['order']->connect($account);
} else {
throw new Exception('Не удалось инициализировать заказ');
}
}
// Инициализация содержимого корзины
$data['supplies'] = $data['order']->supplies(10, $page);
// Инициализация данных списка для выбора терминала
$delivery_to_terminal_list = $account->genListTerminalsTo();
$array_unshift_in_start = function (array &$array, string|int $key, mixed $value) {
$array = array_reverse($array, true);
$array[$key] = $value;
return $array = array_reverse($array, true);
};
$array_write_default_value = function (array &$array, string $key = 'Выберите', string $value = 'Выберите') use ($array_unshift_in_start) {
if (isset($array[$key])) {
// Смещение или ассоциация найдена
// Деинициализация
unset($array[$key]);
// Инициализация
$array_unshift_in_start($array, $key, $value);
}
};
// Сортировка по алфавиту
asort($delivery_to_terminal_list);
// Перемещение в начало массива значения "Выберите"
$array_write_default_value($delivery_to_terminal_list);
if (yii::$app->request->isPost) {
// POST-запрос
yii::$app->response->format = Response::FORMAT_JSON;
return [
'main' => $this->renderPartial('index', compact('account', 'data', 'delivery_to_terminal_list')),
'title' => 'Корзина',
'redirect' => '/cart',
'_csrf' => yii::$app->request->getCsrfToken()
];
}
return $this->render('index', compact('account', 'data', 'delivery_to_terminal_list'));
}
public function actionEditComm(string $catn, string $prod): array|string|null
{
// Инициализация
$return = [
'_csrf' => yii::$app->request->getCsrfToken()
];
if (is_null($catn) || is_null($prod)) {
// Не получен артикул
yii::$app->response->statusCode = 500;
goto end;
}
if ($edges = OrderEdgeSupply::searchBySupplyCatnAndProd($catn, $prod, Order::searchByType()[0]['order'])) {
// Рёбра найдены (связи заказа с поставкой)
// Инициализация
$amount = 0;
foreach ($edges as $edge) {
// Перебор рёбер (связей заказа с поставкой)
// Инициализация
$text = yii::$app->request->post('text') ?? yii::$app->request->get('text') ?? 'Комментарий к заказу';
$comm = $edge->comm ?? null;
$edge->comm = empty($text) ? 'Комментарий к заказу' : $text;
if ($edge->save()) {
// Ребро обновлено
// Запись в журнал
$edge->journal('update', ['comm' => ['from' => $comm, 'to' => $edge->comm]]);
// Обновление счётчика
++$amount;
// Запись в буфер ответа
$return['comm'] = $edge->comm;
}
}
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::searchByCatnAndProd($catn, $prod)) {
return $this->render('index', compact('model'));
} else {
return $this->redirect('/');
}
}
public function actionCount(): array|string|null
{
if (yii::$app->request->isPost) {
// POST-запрос
// Настройка типа ответа
yii::$app->response->format = Response::FORMAT_JSON;
return [
'button' => $this->renderPartial('/cart/button', ['cart_amount' => Order::count(supplies: true)]),
'_csrf' => yii::$app->request->getCsrfToken()
];
}
}
}

View File

@ -0,0 +1,59 @@
<?php
namespace app\controllers;
use yii;
use yii\web\Controller;
use yii\web\Response;
use yii\filters\AccessControl;
use yii\filters\VerbFilter;
use app\models\AccountForm;
class DeauthenticationController extends Controller
{
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::class,
'rules' => [
[
'allow' => true,
'roles' => ['@'],
]
],
],
'verbs' => [
'class' => VerbFilter::class,
'actions' => [
'index' => ['post'],
],
]
];
}
public function actionIndex()
{
// Инициализация
yii::$app->response->format = Response::FORMAT_JSON;
// Удаление сессии
yii::$app->session['status'] = 'inactive';
// yii::$app->session->close();
// Выход из аккаунта
yii::$app->user->logout();
// Инициализация
$model = new AccountForm(yii::$app->request->post('AccountForm') ?? yii::$app->request->get('AccountForm') ?? null);
// Ответа
return [
'menu' => $this->renderPartial('/account/panel/deauthenticated', compact('model')),
'main' => $this->renderPartial('/index'),
'redirect' => '/',
'_csrf' => yii::$app->request->getCsrfToken()
];
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace app\controllers;
use Yii;
use yii\web\Controller;
class ErrorController extends Controller
{
public function actionIndex(): string
{
$exception = Yii::$app->errorHandler->exception;
if ($exception !== null) {
// Исключение не выброшено
// Запись кода ошибки
$code = $exception->statusCode ?? $exception->getCode() ?? 0;
// Запись названия ошибки
$title = match ($code) {
404 => '404 (Не найдено)',
default => '500 (Ошибка сервера)'
};
// Запись сообщения об ошибке
$description = match ($code) {
404 => 'Страница не найдена',
default => $exception->getMessage()
};
return $this->render('/error', compact('exception', 'code', 'title', 'description'));
}
}
public static function throw(string $title, string $description): string {
return yii::$app->controller->render('/error', compact('title', 'description'));
}
}

View File

@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace app\controllers;
use yii;
use yii\web\Controller;
use yii\web\Response;
use app\models\AccountForm;
use app\models\Order;
class IdentificationController extends Controller
{
public function actionIndex()
{
if (yii::$app->request->isPost) {
// POST-запрос
yii::$app->response->format = Response::FORMAT_JSON;
if (yii::$app->user->isGuest) {
// Аккаунт не аутентифицирован
// Инициализация
$model = new AccountForm(yii::$app->request->post('AccountForm') ?? yii::$app->request->get('AccountForm') ?? null);
// Запись ответа
$return = [
'menu' => $this->renderPartial('/account/panel/deauthenticated', compact('model')),
'_csrf' => yii::$app->request->getCsrfToken()
];
} else {
// Аккаунт аутентифицирован
// Инициализация
$model = yii::$app->user;
// Инициализация
$notifications_button = $this->renderPartial('/notification/button');
$notifications_panel = $this->renderPartial('/notification/panel', ['notifications_panel_full' => true]);
$cart_button = $this->renderPartial('/cart/button', ['cart_amount' => Order::count(supplies: true)]);
// Запись ответа
$return = [
'menu' => $this->renderPartial('/account/panel/authenticated', compact(
'notifications_button',
'notifications_panel',
'cart_button'
)),
'_csrf' => yii::$app->request->getCsrfToken()
];
}
if (($cookies = yii::$app->request->cookies)->has('redirect')) {
// Найдено cookie с переадресацией
// Запись ответа
$return['redirect'] = '/' . $cookies['redirect'];
// Очистка cookie
unset(yii::$app->response->cookies['redirect']);
}
return $return;
}
}
}

View File

@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace app\controllers;
use yii;
use yii\web\Controller;
use app\models\Order;
class InvoiceController extends Controller
{
public function actionIndex(int $order)
{
if ($order = Order::searchById(Order::collectionName() . '/' . $order)) {
return $this->renderPartial('/invoice/order/pattern', [
'account' => yii::$app->user->identity,
'order' => [
'id' => $order->_key,
'date' => $order->date ?? time() // @todo доделать
]
]);
}
}
public function actionDownload(int $order)
{
// Инициализация файла
$file = YII_PATH_PUBLIC . '/../assets/invoices/' . $order . '/invoice.xlsx';
if (file_exists($file)) {
// Удалось найти файл
return $this->response->sendFile($file);
} else {
// Не удалось найти файл
// Запись кода ответа
yii::$app->response->statusCode = 500;
return yii::$app->response->redirect('/orders');
}
}
}

View File

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace app\controllers;
use Yii;
use yii\web\Controller;
use yii\web\Response;
class MainController extends Controller
{
/**
* {@inheritdoc}
*/
public function actions()
{
return [
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
],
];
}
/**
* Главная страница
*/
public function actionIndex(): string|array
{
if (Yii::$app->request->isAjax) {
// AJAX-POST-запрос
Yii::$app->response->format = Response::FORMAT_JSON;
return [
'main' => $this->renderPartial('/index'),
'redirect' => '/',
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
return $this->render('/index');
}
}

View File

@ -0,0 +1,212 @@
<?php
declare(strict_types=1);
namespace app\controllers;
use app\models\AccountEdgeNotification;
use yii;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\web\Response;
use app\models\Notification;
class NotificationController extends Controller
{
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::class,
'rules' => [
[
'allow' => true,
'roles' => ['@'],
'actions' => ['index']
]
]
]
];
}
/**
* @todo Перенести логику в модель
*/
public function actionIndex()
{
if (yii::$app->request->isPost) {
// POST-запрос
// Инициализация
$model = new Notification(yii::$app->request->post('Notification'));
$preload = (bool) (int) yii::$app->request->post('preload');
$account = yii::$app->user->identity;
yii::$app->response->format = Response::FORMAT_JSON;
$return = [
'_csrf' => yii::$app->request->getCsrfToken()
];
if (yii::$app->request->post('last')) {
// Запрос последнего уведомлений (всплывающее окно)
// Инициализация
$limit = 1;
} else if (yii::$app->request->post('stream')) {
// Запрос последних уведомлений (панель)
$limit = 10;
}
if (isset($limit)) {
// Обработка для всплывающего окна или панель
// Подзапрос для проверки статуса уведомления относительно пользователя
// Поиск рёбер: ПОЛЬЗОВАТЕЛЬ -> УВЕДОМЛЕНИЕ
$let = $model::find()
->for(['account', $model::collectionName() . '_edge_account'])
->traversal($model::collectionName(), 'OUTBOUND')
->in('account_edge_' . $model::collectionName())
->where(['account._id == "' . $account->readId() . '" && notification_edge_account._from != account_edge_notification[0]._to'])
->select($model::collectionName() . '_edge_account');
if (yii::$app->request->post('last')) {
// Запрос последнего уведомлений (всплывающее окно)
// Уведомление которое не выводилось на мониторе пользователя
$type = 'received';
} else if (yii::$app->request->post('stream')) {
// Запрос последних уведомлений (панель)
// Уведомление которое не было прочитано в окне уведомний
$type = 'checked';
}
// Генерация
$let = $let->createCommand();
/**
* Поиск рёбер: (УВЕДОМЛЕНИЕ)? -> ПОЛЬЗОВАТЕЛЬ
*
* @param bool $new Активация проверки на то, что уведомление не получено
* @param bool $count Посчитать
*/
$search = function (bool $new = false, bool $count = false) use ($model, $account, $type, $let, $limit): array|int|null|Notification {
if ($count) {
// Запрошен подсчёт непрочитанных уведомлений
// Реинициализация
$type = 'checked';
$limit = null;
}
return $model::searchByEdge(
from: 'account',
to: 'notification',
params: $new ? $let->getBindVars() : [],
subquery_where: [
[
'account._id' => $account->readId()
],
[
[
'notification.html' => null
],
'operator' => '!='
],
[
'account_edge_notification.type' => $type
]
],
let: [
'notification_edge_account',
'(' . (string) $let . ')'
],
foreach: [
'edge' => 'notification_edge_account'
],
where: $new ? [
'edge != null'
] : [],
limit: $limit,
sort: ['DESC'],
direction: 'INBOUND',
handle: $count ? function ($request) {
// Посчитать рёбра (информация о количестве непрочитанных уведомлений)
return $request->count();
} : null
);
};
// Поиск новых (непрочитанных) уведомлений пользователя
// Если заккоментировать, то вместо только новых отправит все последние уведомления
$notifications = $search(true);
// Подсчёт новых (непрочитанных) уведомлений
$return['button'] = $this->renderPartial('button', ['notifications_new_amount' => $search(new: true, count: true)]);
if (!yii::$app->request->post('last') && empty($notifications)) {
// Запрошены НЕ ВСПЛЫВАЮЩИЕ уведомления и новые уведомления не найдены
// Поиск уведомлений пользователя
$notifications = $search();
}
if (empty($notifications)) {
// Уведомления не найдены
/**
* @todo Определить какой код лучше использовать (404 не подходит)
*/
yii::$app->response->statusCode = 200;
goto end;
}
foreach (is_array($notifications) ? $notifications : [$notifications] as $notification) {
// Перебор найденных уведомлений
if ($preload) {
// Запрошены уведомления для предзагрузки (их ещё не увидели)
break;
}
// Запись ребра: ПОЛЬЗОВАТЕЛЬ -> УВЕДОМЛЕНИЕ (о том, что уведомление прочитано)
AccountEdgeNotification::write($account->readId(), $notification->readId(), $type);
}
if (yii::$app->request->post('last')) {
// Запрос последнего уведомлений (всплывающее окно)
// Реинициализация
$notification = $notifications[0];
$return['popup'] = [
'html' => $this->renderPartial('popup', compact('model', 'notification', 'account')),
'id' => 'popup/' . $notification->readId()
];
} else if (yii::$app->request->post('stream')) {
// Запрос последних уведомлений (панель)
$return['panel'] = $this->renderPartial('panel', compact('model', 'notifications'));
}
} else {
// Иначе обрабатывается как запрос страницы уведомлений
$return['main'] = $this->renderPartial('index', compact('model'));
$return['redirect'] = '/notification';
}
/**
* Конец алгоритма
*/
end:
return $return;
}
return $this->render('index', ['model' => new Notification(yii::$app->request->get('Notification'))]);
}
}

View File

@ -0,0 +1,115 @@
<?php
declare(strict_types=1);
namespace app\controllers;
use yii;
use yii\filters\AccessControl;
use yii\web\Cookie;
use yii\web\Response;
use yii\web\Controller;
class OfferController extends Controller
{
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::class,
'rules' => [
[
'allow' => true,
'actions' => ['index', 'suppliers'],
],
[
'allow' => true,
'roles' => ['@'],
'actions' => ['accept', 'accept-suppliers']
],
[
'allow' => false,
'roles' => ['?'],
'denyCallback' => [$this, 'accessDenied']
]
]
]
];
}
public function accessDenied()
{
// Инициализация
$cookies = yii::$app->response->cookies;
// Запись cookie с редиректом, который выполнится после авторизации
$cookies->add(new Cookie([
'name' => 'offer',
'value' => yii::$app->request->pathInfo
]));
if (yii::$app->request->isPost) {
// POST-запрос
// Настройка
yii::$app->response->format = Response::FORMAT_JSON;
// Генерация ответа
yii::$app->response->content = json_encode([
'redirect' => '/authentication',
'_csrf' => yii::$app->request->getCsrfToken()
]);
} else if (Yii::$app->request->isGet) {
// GET-запрос
$this->redirect('/authentication');
}
}
public function actionIndex()
{
return $this->render('/offer/index');
}
public function actionSuppliers()
{
return $this->render('/offer/supplier');
}
public function actionAccept()
{
// Инициализация
yii::$app->user->identity->acpt ?? yii::$app->user->identity->acpt = [];
// Запись
yii::$app->user->identity->acpt += ['buyer' => true];
if (yii::$app->user->identity->save()) {
// Удалось записать данные
// Запись в сессию
yii::$app->session['offer_buyer_accepted'] = true;
yii::$app->response->redirect('/');
}
}
public function actionAcceptSuppliers()
{
// Инициализация
yii::$app->user->identity->acpt ?? yii::$app->user->identity->acpt = [];
yii::$app->user->identity->acpt += ['supplier' => true];
if (yii::$app->user->identity->save()) {
// Удалось записать данные
// Запись в сессию
yii::$app->session['offer_supplier_accepted'] = true;
yii::$app->response->redirect('/');
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace app\controllers;
use yii\web\Controller;
use app\models\Terminal;
class PartnersController extends Controller
{
public function actionIndex()
{
$terminals = Terminal::read(500);
return $this->render('/partners/index', compact('terminals'));
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace app\controllers;
use yii\web\Controller;
class PolicyController extends Controller
{
public function actionIndex()
{
return $this->render('/policy/index');
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,121 @@
<?php
declare(strict_types=1);
namespace app\controllers;
use yii;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\web\Response;
use app\models\AccountForm;
use app\models\Order;
use yii\bootstrap\ActiveForm;
class RegistrationController extends Controller
{
public function actionIndex()
{
// Инициализация
$model = new AccountForm(yii::$app->request->post('AccountForm') ?? yii::$app->request->get('AccountForm'));
$type = yii::$app->request->post('type') ?? yii::$app->request->get('type');
$target = yii::$app->request->post('target') ?? yii::$app->request->get('target');
$model->scenario = $model::SCENARIO_REGISTRATION;
// Фильтрация
$target = match ($target) {
'panel' => 'panel',
'main' => 'main',
default => 'main'
};
// Рендер для всплывающей панели
$panel = $target === 'panel';
if (yii::$app->request->isPost) {
// POST-запрос
yii::$app->response->format = Response::FORMAT_JSON;
if ($type === 'registration' && (!yii::$app->user->isGuest || $model->registration())) {
// Данные прошли проверку и аккаунт был создан
// Аутентификация
$model->scenario = $model::SCENARIO_AUTHENTICATION;
if (!$model->authentication()) {
// Не удалось аутентифицироваться
yii::$app->response->statusCode = 401;
$model->scenario = $model::SCENARIO_REGISTRATION;
return [
$target => $this->renderPartial('/account/index', compact('model', 'panel') + ['registration' => true]),
'redirect' => '/registration',
'_csrf' => yii::$app->request->getCsrfToken()
];
}
// Инициализация
$notifications_button = $this->renderPartial('/notification/button');
$notifications_panel = $this->renderPartial('/notification/panel', ['notifications_panel_full' => true]);
$cart_button = $this->renderPartial('/cart/button', ['cart_amount' => Order::count(supplies: true)]);
// Запись ответа
$return = [
'menu' => $this->renderPartial('/account/panel/authenticated', compact(
'notifications_button',
'notifications_panel',
'cart_button'
)),
'_csrf' => yii::$app->request->getCsrfToken()
];
if (($cookies = yii::$app->response->cookies)->has('redirect')) {
// Найдено cookie с переадресацией
// Запись ответа
$return['redirect'] = '/' . $cookies['redirect'];
// Очистка cookie
unset(yii::$app->response->cookies['redirect']);
yii::$app->response->format = Response::FORMAT_HTML;
// Переадресация
return $this->redirect($return['redirect']);
} else {
// Не найдено cookie с переадресацией
if (yii::$app->request->pathInfo === 'authentication' || yii::$app->request->pathInfo === 'registration') {
// Если клиент на промежуточном URI
// Запись ответа
$return['redirect'] = '/';
$return['main'] = $this->renderPartial('/index');
}
}
return $return;
} else {
// Данные не прошли проверку
yii::$app->response->statusCode = 400;
return [
$target => $this->renderPartial('/account/index', compact('model', 'panel') + ['registration' => true]),
'redirect' => '/registration',
'_csrf' => yii::$app->request->getCsrfToken()
];
}
}
if (!yii::$app->user->isGuest) {
yii::$app->response->redirect('/');
} else {
return $this->render('/account/index', compact('model', 'panel') + ['registration' => true]);
}
}
}

View File

@ -0,0 +1,234 @@
<?php
declare(strict_types=1);
namespace app\controllers;
use yii;
use yii\web\Controller;
use yii\web\Response;
use app\models\Product;
use app\models\Search;
/**
* @todo
* 1. Ограничение доступа
*/
class SearchController extends Controller
{
/**
* @todo
* 1. Сессию привязать к аккаунту и проверку по нему делать, иначе её можно просто сбрасывать
* 2. Пагинация
*/
public function actionIndex(): array|string
{
// Инициализация параметров
$auth_only = false;
$account = yii::$app->user->identity;
if ($auth_only && yii::$app->user->isGuest) {
// Если активирован режим "Поиск только аутентифицированным" и запрос пришел не от аутентифицированного
// Запись кода ответа: 401 (необходима авторизация)
yii::$app->response->statusCode = 401;
// Переход к концу обработки
goto skip_search;
}
// Инициализация
$query = yii::$app->request->post('request') ?? yii::$app->request->get('q');
if (yii::$app->request->post('type') === 'product' || yii::$app->request->get('type') === 'product') {
// Поиск по продуктам
if (yii::$app->request->post('history')) {
// Запрошена история
yii::$app->response->format = Response::FORMAT_JSON;
return [
'panel' => $this->renderPartial('/search/panel', compact('account') + ['history' => true]),
'_csrf' => yii::$app->request->getCsrfToken()
];
}
// Инициализация сессии
$session = yii::$app->session;
// Инициализация ответа
$response = null;
// Инициализация параметров
$timer = 0;
// Период пропуска запросов (в секундах)
$period = 3;
$keep_connect = false;
$sanction = false;
$sanction_condition = ($session['last_request'] + $period - time()) < $period;
$sanction_time = 2;
$query_min = 2;
$query_max = 20;
if (isset($session['last_request'])) {
// Данные о времени последнего запроса не найдены
if ($sanction && $sanction_condition) {
// Наказание за повторный запрос при условии задержки
$session['last_request'] += $sanction_time;
}
} else {
// Это первый запрос
// Инициализация
$session['last_request'] = time();
// Пропуск проверок
goto first_request;
}
// Метка: "Повтор обработки поиска" (для создания непрерывного соединения)
keep_connect_wait:
// Запись времени последнего запроса и вычисление об истечении таймера
$timer = $session['last_request'] + $period - time();
if ($timer > 0) {
// Время блокировки не истекло
// Запись кода ответа: 202 (запрос не обработан)
yii::$app->response->statusCode = 202;
$return = [
'timer' => $timer,
'search_line_window_show' => 1,
'_csrf' => yii::$app->request->getCsrfToken()
];
} else {
// Запрос
// Очистка времени последнего запроса
unset($session['last_request']);
// Метка: "Первый запрос"
first_request:
if (strlen($query) < $query_min) {
// Выход за ограничения длины с недостатком
// Пропуск поиска
goto skip_query;
} else if (strlen($query) > $query_max) {
// Выход за ограничения длины с превышением
// Пропуск поиска
goto skip_query;
}
// Запись в историю
Search::write($query);
$limit = yii::$app->request->isPost ? 10 : 20;
if ($response = Product::searchByPartialCatn($query, 'active', $limit, [
'_key' => '_key',
'catn' => 'catn',
'prod' => 'prod',
// Баг с названием DESC
'dscr' => 'dscr',
'catg' => 'catg',
'imgs' => 'imgs',
'name' => 'name',
'prod' => 'prod',
'dmns' => 'dmns',
'stts' => 'stts'
])) {
// Данные найдены по поиску в полях Каталожного номера
// Генерация данных для представления
$response = Search::content(products: $response);
if (yii::$app->request->isPost) {
// POST-запрос
// Запись ответа
$return = [
'panel' => $this->renderPartial('/search/panel', compact('response', 'query')),
'search_line_window_show' => 1,
'_csrf' => yii::$app->request->getCsrfToken()
];
if ((int) yii::$app->request->post('advanced')) {
// Полноценный поиск
// Запись ответа
$return['main'] = $this->renderPartial('/search/index', compact('response', 'query'));
$return['search_line_window_show'] = 1;
$return['redirect'] = '/search?type=product&q=' . $query;
}
}
} else {
// Данные не найдены
// Запись кода ответа: 404 (запрашиваемые данные не найдены)
yii::$app->response->statusCode = 404;
}
}
// Метка: "Пропуск запроса" (для пропуска самого поиска и возврата ответа)
skip_query:
if (yii::$app->request->isPost) {
// POST-запрос
yii::$app->response->format = Response::FORMAT_JSON;
return $return ?? [
'panel' => $this->renderPartial('/search/panel'),
'search_line_window_show' => 1,
'_csrf' => yii::$app->request->getCsrfToken()
];
} else {
// GET-запрос
if (empty($return['main']) && $keep_connect && $timer > 0) {
// Режим непрерывного соединения
// Ожидание
sleep($timer);
// Повтор обработки
goto keep_connect_wait;
}
$advanced = true;
return $this->render('/search/index', compact('response', 'timer', 'advanced', 'query'));
}
}
// Метка: "Пропуск обработки"
skip_search:
if (yii::$app->request->isPost) {
// POST-запрос
yii::$app->response->format = Response::FORMAT_JSON;
return [
'panel' => $this->renderPartial('/search/panel'),
'hide' => (int) $auth_only,
'_csrf' => yii::$app->request->getCsrfToken()
];
} else {
// GET-запрос
return $this->render('/search/index');
}
}
}

View File

@ -1,285 +0,0 @@
<?php
namespace app\controllers;
use Yii;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\web\Response;
use yii\filters\VerbFilter;
use app\models\AccountForm;
use app\models\ContactForm;
class SiteController extends Controller
{
/**
* {@inheritdoc}
*/
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::class,
'only' => ['logout'],
'rules' => [
[
'actions' => ['logout'],
'allow' => true,
'roles' => ['@'],
],
],
],
'verbs' => [
'class' => VerbFilter::class,
'actions' => [
'logout' => ['post'],
],
],
];
}
/**
* {@inheritdoc}
*/
public function actions()
{
return [
'error' => [
'class' => 'yii\web\ErrorAction',
],
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
],
];
}
/**
* Displays homepage.
*
* @return string
*/
public function actionIndex()
{
return $this->render('index');
}
/**
* Identification action.
*
* @return Response
*/
public function actionIdentification()
{
if (Yii::$app->request->isAjax) {
Yii::$app->response->format = Response::FORMAT_JSON;
if (Yii::$app->user->isGuest) {
return [
'menu' => '<a onclick="authentication()">Вход</a>',
'_csrf' => Yii::$app->request->getCsrfToken()
];
} else {
$mail = Yii::$app->user->identity->mail;
return [
'menu' => <<<HTML
<p class="m-0">
<a class="text-dark" href="/cart"><i class="fas fa-shopping-cart mr-4"></i></a>
<a class="text-dark" href="/orders"><i class="fas fa-list mr-4"></i></a>
<a class="text-dark" onclick="deauthentication()">Выход ($mail)</a>
</p>
HTML,
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
}
}
/**
* Login action.
*
* @return Response|string
*/
public function actionAuthentication()
{
$model = new AccountForm(Yii::$app->request->post()['AccountForm'] ?? Yii::$app->request->get()['AccountForm'] ?? null);
if (Yii::$app->request->isAjax) {
// AJAX-POST-запрос
Yii::$app->response->format = Response::FORMAT_JSON;
if (!Yii::$app->user->isGuest) {
// Аккаунт уже аутентифицирован
Yii::$app->response->statusCode = 403;
return [
'form' => $this->renderPartial('index'),
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
if ($model->authentication()) {
// Данные прошли проверку
return [
'menu' => <<<HTML
<p class="m-0">
<a class="text-dark" href="/cart"><i class="fas fa-shopping-cart mr-4"></i></a>
<a class="text-dark" href="/orders"><i class="fas fa-list mr-4"></i></a>
<a class="text-dark" onclick="deauthentication()">Выход ($model->mail)</a>
</p>
HTML,
'form' => $this->renderPartial('index'),
'_csrf' => Yii::$app->request->getCsrfToken()
];
} else {
// Данные не прошли проверку
Yii::$app->response->statusCode = 400;
return [
'form' => $this->renderPartial('account', compact('model')),
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
} else if (Yii::$app->request->isPost) {
// POST-запрос
} else {
// GET-запрос и прочие
if (!Yii::$app->user->isGuest) {
// Аккаунт уже аутентифицирован
Yii::$app->response->redirect('/');
}
}
return $this->render('authentication', compact('model'));
}
/**
* Logout action.
*
* @return Response
*/
public function actionDeauthentication()
{
if (Yii::$app->request->isAjax) {
// AJAX-POST-запрос
Yii::$app->response->format = Response::FORMAT_JSON;
Yii::$app->user->logout();
return [
'menu' => '<a onclick="authentication()">Вход</a>',
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
Yii::$app->response->redirect('/');
}
/**
* Login action.
*
* @return Response|string
*/
public function actionRegistration()
{
$model = new AccountForm(Yii::$app->request->post()['AccountForm'] ?? Yii::$app->request->get()['AccountForm'] ?? null);
$model->type = 0;
if (Yii::$app->request->isAjax) {
// AJAX-POST-запрос
Yii::$app->response->format = Response::FORMAT_JSON;
if (!Yii::$app->user->isGuest) {
// Аккаунт уже аутентифицирован
Yii::$app->response->statusCode = 302;
return [
'form' => $this->renderPartial('index'),
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
if ($model->registration()) {
// Данные прошли проверку и аккаунт был создан
return [
'menu' => <<<HTML
<p class="m-0">
<a class="text-dark" href="/cart"><i class="fas fa-shopping-cart mr-4"></i></a>
<a class="text-dark" href="/orders"><i class="fas fa-list mr-4"></i></a>
<a class="text-dark" onclick="deauthentication()">Выход ($model->mail)</a>
</p>
HTML,
'form' => $this->renderPartial('index'),
'_csrf' => Yii::$app->request->getCsrfToken()
];
} else {
// Данные не прошли проверку
Yii::$app->response->statusCode = 400;
return [
'form' => $this->renderPartial('account', compact('model')),
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
} else if (Yii::$app->request->isPost) {
// POST-запрос
return;
} else {
// GET-запрос и прочие
if (!Yii::$app->user->isGuest) {
// Аккаунт уже аутентифицирован
Yii::$app->response->redirect('/');
}
}
return $this->render('registration', compact('model'));
}
/**
* Displays профиль
*
* @return Response|string
*/
public function actionProfile()
{
$model = new Account();
return $this->render('profile', compact('model'));
}
/**
* Displays contact page.
*
* @return Response|string
*/
public function actionContact()
{
$model = new ContactForm();
if ($model->load(Yii::$app->request->post()) && $model->contact(Yii::$app->params['adminEmail'])) {
Yii::$app->session->setFlash('contactFormSubmitted');
return $this->refresh();
}
return $this->render('contact', [
'model' => $model,
]);
}
/**
* Displays about page.
*
* @return string
*/
public function actionAbout()
{
return $this->render('about');
}
}

View File

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace app\controllers;
use yii;
use yii\web\Controller;
use yii\web\UploadedFile;
use app\models\Request;
use app\models\Account;
class SuppliersController extends Controller
{
public function actionIndex()
{
return $this->renderPartial('/suppliers/index');
}
public function actionRequest()
{
return $this->renderPartial('/suppliers/request');
}
/**
* @todo Сделать отправку только назначенным модераторам
*/
public function actionRequestSend()
{
// Инициализация данных запроса
$request = yii::$app->request->post('Request') ?? yii::$app->request->get('Request');
// Запись поставщика
Account::writeSupplier($request['name'], $request['phon'], $request['mail'], $file = UploadedFile::getInstance(new Request($request), 'file'));
yii::$app->mail_system->compose()
->setFrom(yii::$app->params['mail']['system'])
->setTo(yii::$app->params['mail']['info'])
->setSubject('Регистрация поставщика')
->setHtmlBody($this->renderPartial('/mails/supplier', $request))
->attach($file->tempName, ['fileName' => $file->name])
->send();
return $this->renderPartial('/suppliers/requested');
}
}

View File

@ -0,0 +1,101 @@
<?php
declare(strict_types=1);
namespace app\controllers;
use yii;
use yii\web\Controller;
use yii\web\Response;
use yii\web\HttpException;
use yii\web\UploadedFile;
use yii\filters\AccessControl;
use app\models\Product;
use app\models\Settings;
use app\models\SupplyEdgeProduct;
use app\models\Supply;
use app\models\Account;
use app\models\Notification;
use app\models\OrderEdgeSupply;
use Exception;
class SupplyController extends Controller
{
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::class,
'rules' => [
[
'allow' => true,
'actions' => [
'index',
]
],
[
'allow' => true,
'roles' => ['@'],
'actions' => []
],
[
'allow' => true,
'actions' => [
'read'
],
'matchCallback' => function ($rule, $action): bool {
if (
!yii::$app->user->isGuest
&& (yii::$app->user->identity->type === 'administrator'
|| yii::$app->user->identity->type === 'moderator')
) return true;
return false;
}
],
[
'allow' => false,
'roles' => ['?'],
'denyCallback' => [$this, 'accessDenied']
]
]
]
];
}
/**
* Чтение поставок
*
* @param int $page Страница
*
* @return string|array|null
*/
public function actionRead(int $page = 1): string|array|null
{
if (yii::$app->request->isPost) {
// POST-запрос
// Инициализация входных параметров
$amount = yii::$app->request->post('amount') ?? yii::$app->request->get('amount') ?? 20;
$order = yii::$app->request->post('order') ?? yii::$app->request->get('order') ?? ['DESC'];
// Инициализация cookie
$cookies = yii::$app->response->cookies;
// Чтение поставок
$supplies = Supply::read(limit: $amount, page: $page, order: $order);
// Запись формата ответа
yii::$app->response->format = Response::FORMAT_JSON;
return [
'supplies' => $this->renderPartial('/supply/list', compact('supplies', 'amount', 'page')),
'_csrf' => yii::$app->request->getCsrfToken()
];
}
return false;
}
}

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace app\controllers;
use yii;
use yii\web\Controller;
use yii\web\Response;
use app\models\Terminal;
class TerminalController extends Controller
{
public function actionRead(): ?array
{
// Инициализация входных параметров
$amount = (int) yii::$app->request->post('amount') ?? yii::$app->request->get('amount');
if ($amount < 501) {
// Пройдена проверка
if (yii::$app->request->isPost) {
// POST-запрос
// Запись формата ответа
yii::$app->response->format = Response::FORMAT_JSON;
return [
'terminals' => Terminal::read($amount),
'_csrf' => yii::$app->request->getCsrfToken()
];
}
} else {
// Не пройдена проверка
// Запись кода ответа
yii::$app->response->statusCode = 500;
}
return false;
}
}

View File

@ -0,0 +1,125 @@
<?php
declare(strict_types=1);
namespace app\controllers;
use yii;
use yii\web\Controller;
use yii\web\Response;
use app\models\Account;
class VerifyController extends Controller
{
public function actionIndex(string $vrfy = null): string|Response
{
if (isset($vrfy)) {
// Подтверждение регистрации
if (Account::verification($vrfy, auth: true)) {
// Успешно подтверждена регистрация
return $this->redirect('/profile');
}
return ErrorController::throw('Ошибка подтверждения', 'Код не совпадает с тем, что мы отправили вам на почту, либо регистрация уже была подтверждена.\n Свяжитесь с администрацией');
} else {
// Простой запрос
if (yii::$app->user->isGuest) {
// Пользователь не аутентифицирован
return yii::$app->response->redirect('/registration');
} else {
// Пользователь аутентифицирован
if (yii::$app->user->identity->vrfy === true) {
// Регистрация аккаунта уже подтверждена
// Генерация хеша пароля
yii::$app->user->identity->pswd = yii::$app->security->generatePasswordHash(yii::$app->user->identity->pswd);
// Запись в хранилище
yii::$app->user->identity->update();
if (yii::$app->request->isPost) {
// POST-запрос
// Запись формата ответа
yii::$app->response->format = Response::FORMAT_JSON;
return [
'main' => $this->renderPartial('/profile/index'),
'title' => 'Профиль',
'redirect' => '/profile',
'_csrf' => yii::$app->request->getCsrfToken()
];
} else {
// GET-запрос (подразумевается)
return $this->render('/profile/index');
}
} else {
// Регистрация аккаунта ещё не подтверждена
if (yii::$app->request->isPost) {
// POST-запрос
// Запись формата ответа
yii::$app->response->format = Response::FORMAT_JSON;
return $this->genPostVerify();
} else {
// GET-запрос (подразумевается)
return $this->render('/account/verify');
}
}
}
}
}
/**
* Отправить запрос на активацию
*
* @return string
*/
public function actionSend(): string|array
{
if (!yii::$app->user->isGuest) {
// Пользователь аутентифицирован
// Регенерация кода подтверждения
yii::$app->user->identity->verifyRegenerate();
// Отправка кода подтверждения на почту
yii::$app->user->identity->sendMailVerify();
if (yii::$app->request->isPost) {
// POST-запрос
// Запись формата ответа
yii::$app->response->format = Response::FORMAT_JSON;
return $this->genPostVerify();
} else {
// GET-запрос (подразумевается)
return $this->render('/account/verify');
}
}
}
/**
* Генерация данных для POST-запроса с переадресацией на страницу подтверждения
*/
function genPostVerify(): array {
return [
'main' => $this->renderPartial('/account/verify'),
'title' => 'Подтверждение аккаунта',
'redirect' => '/account/verify',
'_csrf' => yii::$app->request->getCsrfToken()
];
}
}

View File

@ -1,6 +1,6 @@
<?php
use explosivebit\arangodb\Migration;
use mirzaev\yii2\arangodb\Migration;
class m201219_074926_create_account_collection extends Migration
{
@ -11,10 +11,6 @@ class m201219_074926_create_account_collection extends Migration
public function down()
{
// $this->delete('accounts', 'mail');
// $this->delete('accounts', 'name');
// $this->delete('accounts', 'pswd');
$this->dropCollection('account');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210101_092505_create_product_collection extends Migration
{
public function up()
{
$this->createCollection('product');
}
public function down()
{
$this->dropCollection('product');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210107_163448_create_supply_collection extends Migration
{
public function up()
{
$this->createCollection('supply');
}
public function down()
{
$this->dropCollection('supply');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210108_014505_create_account_edge_supply_collection extends Migration
{
public function up()
{
$this->createCollection('account_edge_supply', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('account_edge_supply');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210108_212826_create_product_group_collection extends Migration
{
public function up()
{
$this->createCollection('product_group');
}
public function down()
{
$this->dropCollection('product_group');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210108_221446_create_product_edge_product_group_collection extends Migration
{
public function up()
{
$this->createCollection('product_edge_product_group', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('product_edge_product_group');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210108_222132_create_supply_edge_product_collection extends Migration
{
public function up()
{
$this->createCollection('supply_edge_product', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('supply_edge_product');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210108_222740_create_product_edge_product_collection extends Migration
{
public function up()
{
$this->createCollection('product_edge_product', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('product_edge_product');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210109_214817_create_supply_group_collection extends Migration
{
public function up()
{
$this->createCollection('supply_group');
}
public function down()
{
$this->dropCollection('supply_group');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210109_214833_create_supply_edge_supply_group_collection extends Migration
{
public function up()
{
$this->createCollection('supply_edge_supply_group', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('supply_edge_supply_group');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210111_044635_create_supply_edge_supply_collection extends Migration
{
public function up()
{
$this->createCollection('supply_edge_supply', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('supply_edge_supply');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210112_010347_create_product_group_edge_product_group_collection extends Migration
{
public function up()
{
$this->createCollection('product_group_edge_product_group', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('product_group_edge_product_group');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210112_010411_create_supply_group_edge_supply_group_collection extends Migration
{
public function up()
{
$this->createCollection('supply_group_edge_supply_group', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('supply_group_edge_supply_group');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210112_034135_create_requisite_collection extends Migration
{
public function up()
{
$this->createCollection('requisite');
}
public function down()
{
$this->dropCollection('requisite');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210112_034232_create_supply_edge_requisite_collection extends Migration
{
public function up()
{
$this->createCollection('supply_edge_requisite', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('supply_edge_requisite');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210113_021800_create_purchase_collection extends Migration
{
public function up()
{
$this->createCollection('purchase');
}
public function down()
{
$this->dropCollection('purchase');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210113_021905_create_account_edge_purchase_collection extends Migration
{
public function up()
{
$this->createCollection('account_edge_purchase', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('account_edge_purchase');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210113_021917_create_purchase_edge_supply_collection extends Migration
{
public function up()
{
$this->createCollection('purchase_edge_supply', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('purchase_edge_supply');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210206_154140_create_search_collection extends Migration
{
public function up()
{
$this->createCollection('search', []);
}
public function down()
{
$this->dropCollection('search');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210206_154210_create_account_edge_search_collection extends Migration
{
public function up()
{
$this->createCollection('account_edge_search', ['Type' => 3]);
}
public function down()
{
$this->dropCollection('account_edge_search');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210209_185314_create_notification_collection extends Migration
{
public function up()
{
$this->createCollection('notification', []);
}
public function down()
{
$this->dropCollection('notification');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210209_185328_create_account_edge_notification_collection extends Migration
{
public function up()
{
$this->createCollection('account_edge_notification', ['type' => 3]);
}
public function down()
{
$this->dropCollection('account_edge_notification');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210223_121142_create_settings_collection extends Migration
{
public function up()
{
$this->createCollection('settings', []);
}
public function down()
{
$this->dropCollection('settings');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210223_145042_create_settings_edge_settings_collection extends Migration
{
public function up()
{
$this->createCollection('settings_edge_settings', ['type' => 3]);
}
public function down()
{
$this->dropCollection('settings_edge_settings');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210303_192326_create_order_collection extends Migration
{
public function up()
{
$this->createCollection('order', []);
}
public function down()
{
$this->dropCollection('order');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210303_192451_create_order_edge_supply_collection extends Migration
{
public function up()
{
$this->createCollection('order_edge_supply', ['type' => 3]);
}
public function down()
{
$this->dropCollection('order_edge_supply');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210307_000210_create_account_edge_order_collection extends Migration
{
public function up()
{
$this->createCollection('account_edge_order', ['type' => 3]);
}
public function down()
{
$this->dropCollection('account_edge_order');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210314_133722_create_session_collection extends Migration
{
public function up()
{
$this->createCollection('session', []);
}
public function down()
{
$this->dropCollection('session');
}
}

View File

@ -0,0 +1,16 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210314_133926_create_account_edge_session_collection extends Migration
{
public function up()
{
$this->createCollection('account_edge_session', ['type' => 3]);
}
public function down()
{
$this->dropCollection('account_edge_session');
}
}

View File

@ -0,0 +1,20 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210502_102203_create_terminal_collection extends Migration
{
public function up()
{
/**
* @param string Название коллекции
* @param array Тип коллекции (2 - документ, 3 - ребро)
*/
$this->createCollection('terminal', ['type' => 2]);
}
public function down()
{
$this->dropCollection('terminal');
}
}

View File

@ -0,0 +1,20 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210502_102358_create_dellin_collection extends Migration
{
public function up()
{
/**
* @param string Название коллекции
* @param array Тип коллекции (2 - документ, 3 - ребро)
*/
$this->createCollection('dellin', ['type' => 2]);
}
public function down()
{
$this->dropCollection('dellin');
}
}

View File

@ -0,0 +1,20 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210510_180939_create_request_collection extends Migration
{
public function up()
{
/**
* @param string Название коллекции
* @param array Тип коллекции (2 - документ, 3 - ребро)
*/
$this->createCollection('request', ['type' => 2]);
}
public function down()
{
$this->dropCollection('request');
}
}

View File

@ -0,0 +1,20 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m210512_121658_create_cdek_collection extends Migration
{
public function up()
{
/**
* @param string Название коллекции
* @param array Тип коллекции (2 - документ, 3 - ребро)
*/
$this->createCollection('cdek', ['type' => 2]);
}
public function down()
{
$this->dropCollection('cdek');
}
}

View File

@ -0,0 +1,20 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m211123_114511_create_import_collection extends Migration
{
public function up()
{
/**
* @param string Название коллекции
* @param array Тип коллекции (2 - документ, 3 - ребро)
*/
$this->createCollection('import', ['type' => 2]);
}
public function down()
{
$this->dropCollection('import');
}
}

View File

@ -0,0 +1,20 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m211123_120136_create_import_edge_supply_collection extends Migration
{
public function up()
{
/**
* @param string Название коллекции
* @param array Тип коллекции (2 - документ, 3 - ребро)
*/
$this->createCollection('import_edge_supply', ['type' => 3]);
}
public function down()
{
$this->dropCollection('import_edge_supply');
}
}

View File

@ -0,0 +1,20 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m211123_173801_create_import_edge_account_collection extends Migration
{
public function up()
{
/**
* @param string Название коллекции
* @param array Тип коллекции (2 - документ, 3 - ребро)
*/
$this->createCollection('import_edge_account', ['type' => 3]);
}
public function down()
{
$this->dropCollection('import_edge_account');
}
}

View File

@ -0,0 +1,20 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m211221_183410_create_warehouse_collection extends Migration
{
public function up()
{
/**
* @param string Название коллекции
* @param array Тип коллекции (2 - документ, 3 - ребро)
*/
$this->createCollection('warehouse', ['type' => 2]);
}
public function down()
{
$this->dropCollection('warehouse');
}
}

View File

@ -0,0 +1,20 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m211221_183447_create_warehouse_edge_import_collection extends Migration
{
public function up()
{
/**
* @param string Название коллекции
* @param array Тип коллекции (2 - документ, 3 - ребро)
*/
$this->createCollection('warehouse_edge_import', ['type' => 3]);
}
public function down()
{
$this->dropCollection('warehouse_edge_import');
}
}

View File

@ -0,0 +1,20 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m211221_193454_create_account_edge_warehouse_collection extends Migration
{
public function up()
{
/**
* @param string Название коллекции
* @param array Тип коллекции (2 - документ, 3 - ребро)
*/
$this->createCollection('account_edge_warehouse', ['type' => 3]);
}
public function down()
{
$this->dropCollection('account_edge_warehouse');
}
}

View File

@ -0,0 +1,20 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m220808_185553_create_file_collection extends Migration
{
public function up()
{
/**
* @param string Название коллекции
* @param array Тип коллекции (2 - документ, 3 - ребро)
*/
$this->createCollection('file', ['type' => 2]);
}
public function down()
{
$this->dropCollection('file');
}
}

View File

@ -0,0 +1,20 @@
<?php
use mirzaev\yii2\arangodb\Migration;
class m220817_210350_create_import_edge_file_collection extends Migration
{
public function up()
{
/**
* @param string Название коллекции
* @param array Тип коллекции (2 - документ, 3 - ребро)
*/
$this->createCollection('import_edge_file', ['type' => 3]);
}
public function down()
{
$this->dropCollection('import_edge_file');
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace app\models;
class AccountEdgeNotification extends Edge
{
public static function collectionName(): string
{
return 'account_edge_notification';
}
}

View File

@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
namespace app\models;
class AccountEdgeOrder extends Edge
{
public static function collectionName(): string
{
return 'account_edge_order';
}
/**
* Свойства
*/
public function attributes(): array
{
return array_merge(
parent::attributes(),
[
'stts'
]
);
}
/**
* Метки свойств
*/
public function attributeLabels(): array
{
return array_merge(
parent::attributeLabels(),
[
'stts' => 'Статус'
]
);
}
/**
* Правила
*/
public function rules(): array
{
return array_merge(
parent::rules(),
[
[
[
'stts'
],
'string'
]
]
);
}
/**
* Перед сохранением
*
* @todo Подождать обновление от ебаного Yii2 и добавить
* проверку типов передаваемых параметров
*/
public function beforeSave($data): bool
{
if (parent::beforeSave($data)) {
if ($this->isNewRecord) {
$this->stts = 'current';
}
return true;
}
return false;
}
/**
* Поиск по идентификатору заказа
*
* @param string $order_id Идентификатор записи заказа в базе данных
* @param int $limit Ограничение
*
* @return array|null
*/
public static function searchByOrder(string $order_id, int $limit = 1): ?array
{
return self::find()->where(['_to' => $order_id])->limit($limit)->all();
}
}

View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace app\models;
class AccountEdgePurchase extends Edge
{
public static function collectionName(): string
{
return 'account_edge_purchase';
}
}

View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace app\models;
class AccountEdgeSearch extends Edge
{
public static function collectionName(): string
{
return 'account_edge_search';
}
}

View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace app\models;
class AccountEdgeSupply extends Edge
{
public static function collectionName(): string
{
return 'account_edge_supply';
}
}

View File

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace app\models;
use app\models\Account;
/**
* Связь аккаунтов и складов
*/
class AccountEdgeWarehouse extends Edge
{
/**
* Имя коллекции
*/
public static function collectionName(): string
{
return 'account_edge_warehouse';
}
}

View File

@ -1,10 +1,14 @@
<?php
declare(strict_types=1);
namespace app\models;
use Yii;
use yii;
use yii\base\Model;
use app\models\Account;
use Exception;
/**
* AccountForm is the model behind the login form.
@ -14,37 +18,62 @@ use app\models\Account;
*/
class AccountForm extends Model
{
const SCENARIO_REGISTRATION = 'registration';
const SCENARIO_REGISTRATION_END = 'registration_end';
const SCENARIO_AUTHENTICATION = 'authentication';
public $mail;
public $pswd;
public $auto = true;
/**
* Тип обработки
*
* Регистрация: 0
* Аутентификация: 1
*
* @var integer
*/
public $type = 1;
public $auto = false;
public $rept;
public $pols;
private $account = false;
/**
* @return array the validation rules.
*/
public function rules()
{
return [
// Обязательные поля
[['mail', 'pswd'], 'required', 'message' => 'Заполните поле'],
[
[
'mail',
],
'required',
'message' => 'Заполните поле'
],
// Обязательные поля для аутентификации
[
[
'pswd',
],
'required',
'message' => 'Заполните поле',
'on' => self::SCENARIO_AUTHENTICATION
],
// Функция "Запомнить меня"
['auto', 'boolean'],
// Проверка почты
['mail', 'validateMail', 'message'=>'Неправильная почта'],
[
'auto',
'boolean',
'on' => self::SCENARIO_AUTHENTICATION
],
// Проверка почты,
[
'mail',
'email',
'message' => 'Проверьте почту'
],
[
'mail',
'validateMail',
'on' => self::SCENARIO_REGISTRATION
],
// Проверка пароля
['pswd', 'validatePassword', 'message'=>'Неправильный пароль'],
[
'pswd',
'validatePassword',
'on' => self::SCENARIO_AUTHENTICATION
]
];
}
@ -53,46 +82,71 @@ class AccountForm extends Model
return [
'mail' => 'Почта',
'pswd' => 'Пароль',
'auto' => 'Запомнить'
'auto' => '<i class="fas fa-unlock"></i>',
'pols' => 'Политика конфедециальности'
];
}
/**
* @param string $attribute the attribute currently being validated
* @param array $params the additional name-value pairs given in the rule
*/
public function validateMail($attribute, $params)
{
if (!$this->hasErrors() && $this->type === 0) {
if (!$this->hasErrors()) {
// Проблем нет, обрабатывается событие регистрации
$account = $this->getAccount();
if (!$account || !$account->validateMail($this->mail)) {
if (is_null($account)) {
// Не удалось проверить аккаунт
return;
}
if (!$account || $account->validateMail($this->mail)) {
// Проверка не пройдена
$this->addError($attribute, 'Почта уже привязана к другому аккаунту');
$this->addError($attribute, 'Почта уже привязана');
}
}
}
/**
* Validates the password.
* This method serves as the inline validation for password.
*
* @param string $attribute the attribute currently being validated
* @param array $params the additional name-value pairs given in the rule
*/
public function validatePassword($attribute, $params)
{
if (!$this->hasErrors() && $this->type === 1) {
if (!$this->hasErrors()) {
// Проблем нет, обрабатывается событие аутентификации
$account = $this->getAccount();
if (!$account || !$account->validatePassword($this->pswd)) {
// Проверка не пройдена
if (is_null($account)) {
// Не удалось проверить аккаунт
$this->addError($attribute, 'Проверьте входные данные');
return;
}
if ($account) {
// Удалось инициализировать аккаунт
try {
if (!$account->validatePasswordWithHash($this->pswd)) {
// Не пройдена проверка с хешем
throw new exception;
}
} catch (Exception $e) {
// Не пройдена проверка с хешем
try {
if (!$account->validatePasswordWithoutHash($this->pswd)) {
// Не пройдена проверка с паролем
throw new exception;
}
} catch (Exception $e) {
// Проверка без хеша не пройдена
$this->addError($attribute, 'Проверьте пароль');
}
}
} else {
$this->addError($attribute, 'Не удалось идентифицировать аккаунт');
}
}
}
@ -101,13 +155,21 @@ class AccountForm extends Model
* Logs in a account using the provided accountname and password.
* @return bool whether the account is logged in successfully
*/
public function authentication()
public function authentication(string $mail = null, string $pswd = null)
{
if (isset($mail, $pswd)) {
$this->mail = $mail;
$this->pswd = $pswd;
}
// Регистронезависимая почта
$this->mail = mb_strtolower($this->mail);
if (isset($this->mail, $this->pswd) && $this->validate()) {
// Проверка пройдена
// Аутентификация
return Yii::$app->user->login($this->getAccount(), $this->auto ? 3600*24*30 : 0);
return yii::$app->user->login($this->getAccount(), $this->auto ? 3600 * 24 * 30 : 0);
}
return false;
@ -121,15 +183,33 @@ class AccountForm extends Model
// Инициализация нового аккаунта
$this->account = new Account();
if (isset($this->mail, $this->pswd) && $this->validate()) {
if ($this->validate()) {
// Проверка пройдена
// Запись параметров
$this->account->mail = $this->mail;
$this->account->pswd = Yii::$app->security->generatePasswordHash($this->pswd);
// $this->account->pswd = yii::$app->security->generatePasswordHash(Account::passwordGenerate());
$this->account->pswd = $this->pswd = Account::passwordGenerate();
if (($account = Account::findByMail($this->mail)) || isset($account) && $account->vrfy !== true) {
// Аккаунт найден, но не подтверждён
// Удаление аккаунта (сейчас его создадим снова)
$account->delete();
}
// Регистрация
return $this->account->save();
if ($this->account->save()) {
// Успешно завершена регистрация или обновлены данные не до конца зарегистрировавшегося пользователя
// Генерация индекса
Account::generateIndexes([$this->account]);
// Отправка письма для подтверждения почты
$this->account->sendMailVerify();
return true;
}
}
return false;

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace app\models;
use CdekSDK2\Client;
class Cdek extends Document
{
public static function collectionName(): string
{
return 'cdek';
}
public function attributes(): array
{
return array_merge(
parent::attributes(),
[
'data'
]
);
}
public function attributeLabels(): array
{
return array_merge(
parent::attributeLabels(),
[
'data' => 'Данные ДеловыеЛинии',
]
);
}
/**
* Поиск по идентификатору города
*/
public static function searchByCityId(string $id): ?static
{
return static::findOne(['data["id"]' => $id]);
}
}

View File

@ -1,65 +0,0 @@
<?php
namespace app\models;
use Yii;
use yii\base\Model;
/**
* ContactForm is the model behind the contact form.
*/
class ContactForm extends Model
{
public $name;
public $email;
public $subject;
public $body;
public $verifyCode;
/**
* @return array the validation rules.
*/
public function rules()
{
return [
// name, email, subject and body are required
[['name', 'email', 'subject', 'body'], 'required'],
// email has to be a valid email address
['email', 'email'],
// verifyCode needs to be entered correctly
['verifyCode', 'captcha'],
];
}
/**
* @return array customized attribute labels
*/
public function attributeLabels()
{
return [
'verifyCode' => 'Verification Code',
];
}
/**
* Sends an email to the specified email address using the information collected by this model.
* @param string $email the target email address
* @return bool whether the model passes validation
*/
public function contact($email)
{
if ($this->validate()) {
Yii::$app->mailer->compose()
->setTo($email)
->setFrom([Yii::$app->params['senderEmail'] => Yii::$app->params['senderName']])
->setReplyTo([$this->email => $this->name])
->setSubject($this->subject)
->setTextBody($this->body)
->send();
return true;
}
return false;
}
}

View File

@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace app\models;
use ArangoDBClient\Document as ArangoDBDocument;
class Dellin extends Document
{
public static function collectionName(): string
{
return 'dellin';
}
public function attributes(): array
{
return array_merge(
parent::attributes(),
[
'data'
]
);
}
public function attributeLabels(): array
{
return array_merge(
parent::attributeLabels(),
[
'data' => 'Данные ДеловыеЛинии',
]
);
}
/**
* Поиск по идентификатору города
*
* @param string $id Идентификатор города
*/
public static function searchByCityId(string $id): ?static
{
return static::findOne(['data["id"]' => $id]);
}
/**
* Поиск по КЛАДР-коду города
*
* @param string $id Код КЛАДР города
*/
public static function searchByCityKladr(string $code): ?static
{
return static::findOne(['data["code"]' => $code]);
}
/**
* Поиск по идентификатору терминала
*
* @param int $id Идентификатор терминала
* @param bool $terminal_data_only Запрос только данных терминала
*/
public static function searchByTerminalId(int $id, bool $terminal_data_only = false): bool|static|array|null|ArangoDBDocument
{
if ($terminal_data_only) {
return static::find()->foreach(['terminal' => self::collectionName() . '.data["terminals"]["terminal"]'])->where(['terminal["id"] == "' . $id . '"'])->select('terminal')->createCommand()->execute()->getAll()[0];
}
return static::find()->foreach(['terminal' => self::collectionName() . '.data["terminals"]["terminal"]'])->where(['terminal["id"] == "' . $id . '"'])->one();
}
}

View File

@ -0,0 +1,244 @@
<?php
declare(strict_types=1);
namespace app\models;
use yii;
use mirzaev\yii2\arangodb\ActiveRecord;
use Exception;
/**
* Документ
*/
abstract class Document extends ActiveRecord
{
/**
* Имя коллекции
*/
public static function collectionName(): string
{
throw new Exception('Не инициализировано название коллекции');
return 'document';
}
/**
* Свойства
*/
public function attributes(): array
{
return [
'_key',
'jrnl'
];
}
/**
* Метки свойств
*/
public function attributeLabels(): array
{
return [
'_key' => 'Ключ',
'jrnl' => 'Журнал'
];
}
/**
* Правила
*
* @todo Добавить проверку существования аккаунта
*/
public function rules(): array
{
return [];
}
/**
* Перед сохранением
*
* @todo Подождать обновление от ебаного Yii2 и добавить
* проверку типов передаваемых параметров
*/
public function beforeSave($create): bool
{
if (parent::beforeSave($create)) {
// Пройдена родительская проверка
if ($this->isNewRecord) {
// Новая запись
// Запись в журнал
$this->jrnl = array_merge(
[[
'date' => time(),
'account' => yii::$app->user->id ?? 'system',
'action' => 'create'
]],
$this->jrnl ?? []
);
}
return true;
}
return false;
}
/**
* Журнал
*
* Записывает данные в журнал
*
* @param string $action
*
* @return int|bool Время записанное в журнале или false, если не удалось записать
*/
public function journal(string $action = 'update', array ...$data): int|bool
{
// Инициализация
if (isset($this->jrnl) && is_array($this->jrnl)) {
} else {
$this->jrnl = [];
}
// Генерация
$this->jrnl = array_merge(
$this->jrnl,
[array_merge(
[
'date' => $time = time(),
'account' => yii::$app->user->id ?? 'system',
'action' => $action
],
[
'data' => $data
]
)]
);
// Запись и возврат
return $this->update() ? $time : false;
}
/**
* Чтение идентификатора
*/
public function readId(): ?string
{
return isset($this->_key) && static::collectionName() ? static::collectionName() . '/' . $this->_key : null;
}
/**
* Поиск
*/
public static function search(array $where, $limit = 1): array
{
return static::find()->where($where)->limit($limit)->all();
}
/**
* Поиск по идентификатору
*/
public static function searchById(string $_id): ?static
{
return static::findOne(['_id' => $_id]);
}
public static function readLast(): static|null|bool
{
return static::find()->orderBy(['DESC'])->one();
}
/**
* Чтение всех записей
*
* @deprecated
*/
public static function readAll(): array
{
return static::find()->all();
}
/**
* Чтение записей по максимальному ограничению
*/
public static function read(?array $where = [], int $limit = 100, int $page = 1, ?array $order = null): array
{
return static::find()->where($where)->orderby($order)->limit($limit)->offset(($page - 1) * $limit)->all();
}
/**
* Чтение количества записей
*/
public static function readAmount(): int
{
return static::find()->count();
}
/**
* Проверка на то, что в свойство передан массив
*/
public function arrayValidator(string $attribute, array $params = null): void
{
if (is_array($this->$attribute)) {
return;
} else {
$this->addError($attribute, 'Передан не массив');
}
$this->addError($attribute, 'Не пройдена проверка: "arrayValidator"');
}
/**
* Проверка на то, что в свойство передан массив и он хранит циферные значения
*/
public function arrayWithNumbersValidator(string $attribute, array $params = null): void
{
try {
if (is_array($this->$attribute)) {
foreach ($this->$attribute as $value) {
if (!(bool) preg_match('/^[0-9\.]*$/m', $value)) {
$this->addError($attribute, 'В массиве найдены запрещённые символы');
}
}
return;
} else {
$this->addError($attribute, 'Передан не массив');
}
} catch (Exception $e) {
$this->addError($attribute, $e->getMessage());
}
$this->addError($attribute, 'Не пройдена проверка: "arrayWithNumbersValidator"');
}
/**
* Конвертировать _id в _key
*/
private static function keyFromId(string $_id): ?string
{
preg_match_all('/\/([0-9]+)$/m', $_id, $mathes);
return $mathes[1][0] ?? null;
}
/**
* Статический вызов
*
* @param string $name
* @param array $args
*/
public static function __callStatic(string $name, array $args): mixed {
return match ($name) {
'keyFromId' => self::keyFromId(...$args)
};
}
}

View File

@ -0,0 +1,220 @@
<?php
declare(strict_types=1);
namespace app\models;
/**
* Ребро
*/
abstract class Edge extends Document
{
/**
* Свойства
*/
public function attributes(): array
{
return array_merge(
parent::attributes(),
[
'_from',
'_to',
'type'
]
);
}
/**
* Метки свойств
*/
public function attributeLabels(): array
{
return array_merge(
parent::attributeLabels(),
[
'_from' => 'От кого',
'_to' => 'К кому',
]
);
}
/**
* Правила
*/
public function rules(): array
{
return array_merge(
parent::rules(),
[
[
['_from', '_to'],
'required',
'message' => 'Заполните поле: {attribute}'
],
[
'type',
'string'
]
]
);
}
/**
* Перед сохранением
*
* @todo Подождать обновление от ебаного Yii2 и добавить
* проверку типов передаваемых параметров
*/
public function beforeSave($data): bool
{
if (parent::beforeSave($data)) {
if ($this->isNewRecord) {
}
return true;
}
return false;
}
/**
* Записать (с проверкой на существование)
*
* Создаст ребро только в том случае, если его аналога не существует
*
* @param string $_from Идентификатор отправителя (_id)
* @param string $_from Идентификатор получетеля (_id)
* @param string $type Дополнительное поле - тип взаимосвязи
* @param string $data Дополнительные данные
*
* @todo
* 1. Удалить $type и оставить только $data
*/
public static function writeSafe(string $_from, string $_to, string $type = '', array $data = []): ?static
{
if ($edge = self::searchByVertex($_from, $_to, limit: 1)) {
// Найдено в базе данных
return $edge[0];
}
return self::write($_from, $_to, $type, $data);
}
/**
* Записать
*
* @param string $_from Идентификатор отправителя (_id)
* @param string $_from Идентификатор получетеля (_id)
* @param string $type Дополнительное поле - тип взаимосвязи @deprecated
* @param string $data Дополнительные данные
*
* @todo
* 1. Удалить $type и оставить только $data
*/
public static function write(string $_from, string $_to, string $type = '', array $data = []): ?static
{
// Инициализация
$edge = new static;
// Настройка
$edge->_from = $_from;
$edge->_to = $_to;
if (isset($type)) $edge->type = $type;
foreach ($data as $key => $value) {
if (is_int($key)) {
// Обычная запись
$edge->{$value} = true;
} else {
// Ассоциативная запись
$edge->{$key} = $value;
}
}
return $edge->save() ? $edge : null;
}
/**
* Поиск ребра по его вершинам
*
* @param string $_from Идентификатор исходящей вершины
* @param string $_to Идентификатор входящей вершины
* @param string|null $type deprecated
* @param int $limit Ограничение по количеству
* @param array|null $filter Фильтр (where())
*/
public static function searchByVertex(string $_from, string $_to, string|null $type = null, int $limit = 1, array|null $filter = null): array|null
{
$query = self::find()->where([
'_from' => $_from,
'_to' => $_to
]);
if (isset($type)) {
$query->where(['type' => $type]);
}
if (isset($filter)) {
$query->where($filter);
}
return $query->limit($limit)->all();
}
/**
* Поиск рёбер по направлению
*/
public static function searchByDirection(string $_from, string $direction = 'OUTBOUND', string $type = '', array $where = [], int $limit = 1, int $page = 1): static|array|null
{
if (str_contains($direction, 'OUTBOUND')) {
// Исходящие рёбра
if (isset($where)) {
// Получен параметр $where
// Реинициализация
$where = array_merge($where + ['_from' => $_from]);
} else {
// Не получен параметр $where
// Реинициализация
$where = array_merge(['_from' => $_from, 'type' => $type]);
}
$query = static::find()->where($where);
} else if (str_contains($direction, 'INBOUND')) {
// Входящие рёбра
if (isset($where)) {
// Получен параметр $where
// Реинициализация
$where = array_merge($where + ['_to' => $_from]);
} else {
// Не получен параметр $where
// Реинициализация
$where = array_merge(['_to' => $_from, 'type' => $type]);
}
$query = static::find()->where($where);
} else if (str_contains($direction, 'ANY')) {
// Исходящие и входящие рёбра
return static::searchByDirection(_from: $_from, direction: 'OUTBOUND', type: $type, where: $where, limit: $limit, page: $page) + static::searchByDirection(_from: $_from, direction: 'INBOUND', type: $type, where: $where, limit: $limit, page: $page);
}
if ($limit < 2) {
// Одно ребро или меньше
return $query->one();
} else {
// Несколько рёбер
return $query->limit($limit)->offset($limit * ($page - 1))->all();
}
}
}

View File

@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
namespace app\models;
use ArangoDBClient\Document as ArangoDBDocument;
use app\models\traits\SearchByEdge;
use app\models\ImportEdgeFile;
class File extends Document
{
use SearchByEdge;
public static function collectionName(): string
{
return 'file';
}
public function attributes(): array
{
return array_merge(
parent::attributes(),
[
'type',
'path',
'name',
'user',
'stts',
'meta'
]
);
}
public function attributeLabels(): array
{
return array_merge(
parent::attributeLabels(),
[
'type' => 'Тип файла',
'path' => 'Относительный от хранилища путь до файла',
'name' => 'Название файла',
'user' => 'Пользователь управляющий файлом',
'stts' => 'Статус',
'meta' => 'Метаданные'
]
);
}
/**
* Перед сохранением
*
* @todo Подождать обновление от ебаного Yii2 и добавить
* проверку типов передаваемых параметров
*/
public function beforeSave($data): bool
{
if (parent::beforeSave($data)) {
if ($this->isNewRecord) {
if ($this->type = 'supplies excel') {
// Список поставок
$this->stts = 'needed to load';
}
}
return true;
}
return false;
}
public static function searchSuppliesNeededToLoad(int $amount = 3): array
{
return static::find()->where(['stts' => 'needed to load'])->limit($amount)->all();
}
/**
* Поиск по инстанции импорта
*
* @param Import $import Инстанция импорта
*/
public static function searchByImport(Import $import): ?File
{
return new File(self::searchByEdge(
from: 'import',
to: 'file',
subquery_where: [
[
'import._id' => $import->readId()
]
],
where: 'import_edge_file[0]._id != null',
select: 'file',
limit: 1
)[0]) ?? null;
}
}

View File

@ -0,0 +1,146 @@
<?php
declare(strict_types=1);
namespace app\models;
use yii;
use app\models\traits\SearchByEdge;
use app\models\Account;
/**
* Импорт поставок
*
* Хранит в себе связи с поставками которые были загружены вместе
*/
class Import extends Document
{
use SearchByEdge;
/**
* Имя коллекции
*/
public static function collectionName(): string
{
return 'import';
}
/**
* Свойства
*/
public function attributes(): array
{
return array_merge(
parent::attributes(),
[
'name',
'file'
]
);
}
/**
* Метки свойств
*/
public function attributeLabels(): array
{
return array_merge(
parent::attributeLabels(),
[
'name' => 'Название',
'file' => 'Файл'
]
);
}
/**
* Правила
*/
public function rules(): array
{
return array_merge(
parent::rules(),
[
[
[
'file',
'name'
],
'string'
]
]
);
}
/**
* Поиск по складу
*
* @param Warehouse $warehouse Инстанция склада
* @param int $limit Ограничение по максимальному количеству
*
* @return array Инстанции импортов
*/
public static function searchByWarehouse(Warehouse $warehouse, int $limit = 10): ?array
{
return self::searchByEdge(
from: 'warehouse',
to: 'import',
edge: 'warehouse_edge_import',
direction: 'INBOUND',
subquery_where: [
['warehouse_edge_import._from' => $warehouse->readId()],
['warehouse_edge_import.type' => 'loaded']
],
where: 'warehouse_edge_import[0] != null',
limit: $limit
);
}
/**
* Поиск по поставке
*
* @param Supply $supply Поставка
* @param int $limit Ограничение по максимальному количеству
*
* @return array Инстанции импортов
*/
public static function searchBySupply(Supply $supply, int $limit = 10): ?array
{
return self::searchByEdge(
from: 'supply',
to: 'import',
edge: 'import_edge_supply',
direction: 'OUTBOUND',
subquery_where: [
['import_edge_supply._to' => $supply->readId()],
['import_edge_supply.type' => 'imported']
],
where: 'import_edge_supply[0] != null',
limit: $limit
);
}
/**
* Поиск по файлу
*
* @param File $file Файл
*
* @return Import|null Инстанция импорта
*/
public static function searchByFile(File $file): ?Import
{
return self::searchByEdge(
from: 'file',
to: 'import',
edge: 'import_edge_file',
direction: 'OUTBOUND',
subquery_where: [
['import_edge_file._to' => $file->readId()],
['import_edge_supply.type' => 'connected']
],
where: 'import_edge_supply[0] != null',
limit: 1
)[0] ?? null;
}
}

View File

@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace app\models;
use app\models\Account;
/**
* Связь инстанций импорта с поставками
*/
class ImportEdgeAccount extends Edge
{
/**
* Имя коллекции
*/
public static function collectionName(): string
{
return 'import_edge_account';
}
/**
* Свойства
*/
public function attributes(): array
{
return array_merge(
parent::attributes(),
[
]
);
}
/**
* Метки свойств
*/
public function attributeLabels(): array
{
return array_merge(
parent::attributeLabels(),
[
]
);
}
/**
* Правила
*/
public function rules(): array
{
return array_merge(
parent::rules(),
[
]
);
}
/**
* Поиск по аккаунту
*
* @param Account $account Аккаунт
* @param int $limit Ограничение по максимальному количеству
*/
public static function searchByAccount(Account $account, int $limit = 1): array
{
$account = Account::initAccount($account);
return static::find()->where(['_from' => $account->collectionName() . "$account->_key"])->limit($limit)->all();
}
}

View File

@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
namespace app\models;
use app\models\File;
use app\models\Import;
/**
* Связь инстанций импорта с поставками
*/
class ImportEdgeFile extends Edge
{
/**
* Имя коллекции
*/
public static function collectionName(): string
{
return 'import_edge_file';
}
/**
* Свойства
*/
public function attributes(): array
{
return array_merge(
parent::attributes(),
[
]
);
}
/**
* Метки свойств
*/
public function attributeLabels(): array
{
return array_merge(
parent::attributeLabels(),
[
]
);
}
/**
* Правила
*/
public function rules(): array
{
return array_merge(
parent::rules(),
[
]
);
}
/**
* Поиск по файлу
*
* @param File $file Файл
* @param int $limit Ограничение по максимальному количеству
*/
public static function searchByFile(File $file, int $limit = 1): array
{
return static::find()->where(['_to' => $file->readId(), 'type' => 'connected'])->limit($limit)->all();
}
/**
* Поиск по инстанции импорта
*
* @param Import $import Инстанция импорта
* @param int $limit Ограничение по максимальному количеству
*/
public static function searchByImport(Import $import, int $limit = 1): array
{
return static::find()->where(['_from' => $import->readId(), 'type' => 'connected'])->limit($limit)->all();
}
}

View File

@ -0,0 +1,115 @@
<?php
declare(strict_types=1);
namespace app\models;
use mirzaev\yii2\arangodb\Query;
/**
* Связь инстанций импорта с поставками
*/
class ImportEdgeSupply extends Edge
{
/**
* Имя коллекции
*/
public static function collectionName(): string
{
return 'import_edge_supply';
}
/**
* Свойства
*/
public function attributes(): array
{
return array_merge(
parent::attributes(),
[
'vrsn'
]
);
}
/**
* Метки свойств
*/
public function attributeLabels(): array
{
return array_merge(
parent::attributeLabels(),
[
'vrsn' => 'Версия'
]
);
}
/**
* Правила
*/
public function rules(): array
{
return array_merge(
parent::rules(),
[
[
[
'vrsn'
],
'integer',
'message' => '{attribute} должен быть числом.'
]
]
);
}
/**
* Перед сохранением
*
* @todo Подождать обновление от ебаного Yii2 и добавить
* проверку типов передаваемых параметров
*/
public function beforeSave($data): bool
{
if (parent::beforeSave($data)) {
if ($this->isNewRecord) {
}
return true;
}
return false;
}
/**
* Поиск максимальной версии
*
* Ищет максимальную версию у поставок
*
* @param Supply $supply Поставка
*
* @return int Версия, если найдена
*/
public static function searchMaxVersion(Supply $supply): ?int
{
return static::find()->execute("RETURN MAX(
FOR import_edge_supply IN import_edge_supply
FILTER (import_edge_supply._to == 'supply/$supply->_key')
LIMIT 0,999
RETURN import_edge_supply.vrsn
)")[0] ?? null;
}
/**
* Поиск по поставке
*
* @param Supply $supply
*
* @return static|null
*/
public static function searchBySupply(Supply $supply): ?static
{
return static::find()->where(['_to' => $supply->readId()])->one() ?? null;
}
}

View File

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace app\models;
use yii;
use yii\base\Model;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
// use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
// use PhpOffice\PhpSpreadsheet\Writer\Html;
use PhpOffice\PhpSpreadsheet\Reader\Html as HtmlReader;
use Exception;
class Invoice extends Model
{
public static function generate(string|int $order_key, string $html): void
{
// Инициализация директории
$dir = YII_PATH_PUBLIC . '/../assets/invoices/' . $order_key;
// Сохранение на диск
if (!file_exists($dir) && !mkdir($dir, 0775, true)) throw new Exception('Не удалось записать директорию:' . $dir);
$reader = new HtmlReader();
$spreadsheet = $reader->loadFromString($html);
$writer = new Xlsx($spreadsheet);
$writer->save($dir . '/invoice.xlsx');
// $reader = new XlsxReader();
// $reader->setReadDataOnly(true);
// $spreadsheet = $reader->load('../views/invoice/order/original.xlsx');
// $writer = new Html($spreadsheet);
// $writer->save('hello world.html');
}
}

View File

@ -0,0 +1,299 @@
<?php
declare(strict_types=1);
namespace app\models;
use yii;
use app\models\traits\SearchByEdge;
use app\models\Account;
use Exception;
/**
* Поиск
*
* @see Product Поиск по товарам
*
* @todo Нет смысла каждый раз возвращать кнопку, надо написать проверки
*/
class Notification extends Document
{
use SearchByEdge;
/**
* Сценарий для доверенного пользователя с созданием уведомления
*/
const SCENARIO_TRUSTED_CREATE = 'create';
/**
* Уведомление: "памятка"
*/
const TYPE_NOTICE = 'notice';
/**
* Уведомление: "предупреждение"
*/
const TYPE_WARNING = 'warning';
/**
* Уведомление: "ошибка"
*/
const TYPE_ERROR = 'error';
/**
* Уведомление для модераторов: "новый заказ"
*/
const TYPE_MODERATOR_ORDER_NEW = 'new order';
/**
* Цель для отправки уведомления
*
* Расшифровывается как $target
*
* @see SCENARIO_TRUSTED_CREATE
*/
public string|null $account;
/**
* Текст уведомления
*/
public string $text;
/**
* Разделитель получателей уведомлений
*/
public static string $delimiter = ',';
/**
* Типы уведомлений
*/
public array $typs = [
'notice' => 'Уведомление',
'warning' => 'Предупреждение'
];
/**
* Имя коллекции
*/
public static function collectionName(): string
{
return 'notification';
}
/**
* Свойства
*/
public function attributes(): array
{
return array_merge(
parent::attributes(),
[
'html',
'type'
]
);
}
/**
* Метки свойств
*/
public function attributeLabels(): array
{
return array_merge(
parent::attributeLabels(),
[
'html' => 'HTML',
'type' => 'Тип',
'trgt' => 'Получатели',
'text' => 'Текст',
]
);
}
/**
* Правила
*/
public function rules(): array
{
return array_merge(
parent::rules(),
[
[
'html',
'required'
],
[
'type',
'default',
'value' => 'notice'
]
]
);
}
/**
* Запись
*/
public function write(): self|array|null
{
return $this::_write($this->text, $this->html, $this->account ?? null, $this->type);
}
/**
* Запись
*
* @param string $html Содержимое уведомления (HTML или текст)
* @param bool|string|null $html Содержимое уведомления (HTML или текст)
* @param string $account Получатель уведомления (_key или "@...")
* @param string $type Тип уведомления
*
* @todo Намного удобнее будет заменить _key на _id, чтобы из рёбер сразу получать аккаунт без лишних операций
*/
public static function _write(string $text, bool|string|null $html = false, string $account = '', string $type = self::TYPE_NOTICE): self|array|null
{
// Инициализация
$model = new self;
$receiver = Account::initAccount($account)->_key ?? $account ?? throw new Exception('Не удалось инициализировать получателя');
if ((bool) (int) $html) {
// Получен текст в формете HTML-кода
$model->html = $text ?? null;
} else {
// Получен необработанный текст
$text = htmlspecialchars(strip_tags($text ?? null));
$model->html = <<<HTML
<p>$text</p>
HTML;
}
if ($model->save()) {
// Уведомление записано
// Инициализация получателей и создание ребра
self::searchReceiverAndConnect($model, $receiver, $type);
}
return null;
}
/**
* Поиск получателя
*
* @param self $model Уведомление
* @param string $targets Необработанный текст с получателями
* @param string $type Тип уведомления
*/
protected static function searchReceiverAndConnect(self $model, string $targets, string $type = self::TYPE_NOTICE): AccountEdgeNotification|array|null
{
// Инициализация
$return = [];
// Конвертация
$accounts = array_map('trim', explode(self::$delimiter, $targets));
foreach ($accounts as $account) {
if (in_array('@all', $accounts, true)) {
// Всем пользователям
// Инициализация
$return = [];
foreach (Account::readAll() as $account) {
// Перебор всех аккаунтов
// Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ
$return[] = AccountEdgeNotification::writeSafe($model->readId(), $account->readId(), $type);
}
} else if (
in_array('@authorized', $accounts, true)
|| in_array('@authorizeds', $accounts, true)
|| in_array('@authorised', $accounts, true)
|| in_array('@authoriseds', $accounts, true)
|| in_array('@auth', $accounts, true)
|| in_array('@autheds', $accounts, true)
) {
// Всем авторизованным (админам и модераторам)
// Инициализация
$return = [];
foreach (Account::readAllAuthorizeds() as $account) {
// Перебор всех аккаунтов
// Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ
$return[] = AccountEdgeNotification::writeSafe($model->readId(), $account->readId(), $type);
}
} else if (
in_array('@administrator', $accounts, true)
|| in_array('@administrators', $accounts, true)
|| in_array('@admin', $accounts, true)
|| in_array('@admins', $accounts, true)
) {
// Администраторам
// Инициализация
$return = [];
foreach (Account::readAllAdministrators() as $account) {
// Перебор всех аккаунтов
// Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ
$return[] = AccountEdgeNotification::writeSafe($model->readId(), $account->readId(), $type);
}
} else if (
in_array('@moderator', $accounts, true)
|| in_array('@moderators', $accounts, true)
|| in_array('@moder', $accounts, true)
|| in_array('@moders', $accounts, true)
) {
// Модераторам
// Инициализация
$return = [];
foreach (Account::readAllModerators() as $account) {
// Перебор всех аккаунтов
// Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ
$return[] = AccountEdgeNotification::writeSafe($model->readId(), $account->readId(), $type);
}
} else if (in_array('@test', $accounts, true)) {
// Тестирование (отправка самому себе)
$return[] = AccountEdgeNotification::writeSafe($model->readId(), yii::$app->user->id, $type);
} else {
// Найден идентификатор (подразумевается)
if ($account = Account::searchById(Account::collectionName() . '/' . $account)) {
// Аккаунт найден
// Запись ребра: УВЕДОМЛЕНИЕ -> АККАУНТ
$return[] = AccountEdgeNotification::writeSafe($model->readId(), $account->readId(), $type);
}
}
}
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' => 'Ошибка'
};
}
}

Some files were not shown because too many files have changed in this diff Show More