Модули
Дилеры (компании)
Версия: 1.0
Дата: 2025-11-22
Статус: Реализован
Общее описание
Текущая реализация
Права доступа
Описание полей
UI/UX
Бизнес-логика
Выявленные проблемы
Рекомендации
Модуль "Дилеры (компании)" предназначен для управления дилерскими организациями — юридическими лицами, которые являются партнёрами компании.
Основные функции:
Ведение реестра дилерских компаний
Хранение реквизитов организаций (ИНН, ОГРН, банковские данные)
Назначение менеджера, ответственного за компанию
Настройка индивидуальных скидок по категориям товаров
Привязка к складу и месту производства
Модуль Связь Дилеры (менеджеры) Пользователи привязаны к компании через company_id Заказы Заказы создаются дилерами компании Каталог Скидки на категории товаров Склады Привязка компании к складу Производство Привязка компании к месту производства
Метрика Значение Всего компаний 183 С назначенным менеджером 183 С индивидуальными скидками 5
┌─────────────────────────────────────────────────────────────────────────────┐
│ МОДУЛЬ "ДИЛЕРЫ (КОМПАНИИ)" │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ РОУТЫ (routes/web.php) │
│ ├── GET /companies → CompanyController@index → Список компаний │
│ ├── GET /company/add → CompanyController@create → Создание │
│ └── GET /company/edit/{id} → CompanyController@show → Редактирование │
│ │
│ КОНТРОЛЛЕР (CompanyController.php) │
│ └── Проверка прав: company_can_view OR company_manager_can_view │
│ │
│ LIVEWIRE КОМПОНЕНТЫ │
│ ├── Company/Listing.php — Filament таблица компаний │
│ ├── Company/Add.php — Форма создания компании │
│ └── Company/Edit.php — Форма редактирования (с секцией скидок) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Поле Тип Описание idbigint PK orgstring Название организации contract_numberstring Номер договора contract_datedatetime Дата договора innstring ИНН ogrnstring ОГРН kppstring КПП okpostring ОКПО okatostring ОКАТО okvedstring ОКВЭД addressstring Фактический адрес legal_addressstring Юридический адрес director_name_fullstring Полное имя руководителя director_name_shortstring Сокращенное имя руководителя bankstring Банк bikstring БИК rsstring Расчётный счёт csstring Корр. счёт phonestring Телефон emailstring Email discountinteger Базовая скидка (%) specification_start_numberinteger Номер отсчёта спецификации manufacture_idFK Место производства stock_idFK Склад manager_idFK Менеджер компании filesjson Прикреплённые файлы created_attimestamp — updated_attimestamp —
Поле Тип Описание idbigint PK category_idFK Категория товаров company_idFK Компания discountinteger Скидка на категорию (%)
app/
├── Http/Controllers/
│ └── CompanyController.php
├── Livewire/Company/
│ ├── Add.php
│ ├── Edit.php
│ └── Listing.php
└── Models/
├── Company.php
└── CategoryCompany.php
database/migrations/
├── 2025_07_04_075435_create_companies_table.php
├── 2025_07_09_150759_create_category_companies_table.php
├── 2025_09_21_180100_add_specification_start_number_to_companies_table.php
└── 2025_09_30_074437_add_director_name_fields_to_companies_table.php
resources/views/templates/company/
├── table.blade.php
├── add.blade.php
└── edit.blade.php
Политика Описание company_can_viewПросмотр ВСЕХ компаний company_can_addСоздание компании company_can_editРедактирование ЛЮБОЙ компании company_can_deleteУдаление компании company_can_edit_discountРедактирование скидок company_manager_can_viewПросмотр СВОИХ компаний (где менеджер) company_manager_can_editРедактирование СВОИХ компаний company_manager_can_deleteУдаление СВОИХ компаний
// Listing.php — фильтрация списка
if ( $this -> authUser -> check_access ( 'company_can_view' )) {
// Видит ВСЕ компании
$query = Company :: query ();
} elseif ( $this -> authUser -> check_access ( 'company_manager_can_view' )) {
// Видит только СВОИ (где manager_id = текущий пользователь)
$query = Company :: query () -> where ( 'manager_id' , $this -> authUser -> id);
}
// Edit.php — разрешение редактирования
if ( $this -> authUser -> check_access ( 'company_can_edit' )) {
$disableEdit = false ; // Может редактировать любую
} elseif ( $this -> authUser -> check_access ( 'company_manager_can_edit' )
and $this -> record -> manager_id == $this -> authUser -> id) {
$disableEdit = false ; // Только свою
}
Роль Просмотр Создание Редактирование Удаление Скидки Администратор ✅ Все ✅ ✅ Все ✅ ✅ Менеджер ⚠️ Свои ❌ ⚠️ Свои ⚠️ Свои ❌ Дилер ❌ ❌ ❌ ❌ ❌ Производство ❌ ❌ ❌ ❌ ❌
Поле Тип Обязательное Описание orgTextInput ✅ Название организации innTextInput ❌ ИНН ogrnTextInput ❌ ОГРН kppTextInput ❌ КПП okpoTextInput ❌ ОКПО okatoTextInput ❌ ОКАТО okvedTextInput ❌ ОКВЭД addressTextInput ❌ Фактический адрес legal_addressTextInput ❌ Юридический адрес bankTextInput ❌ Банк bikTextInput ❌ БИК rsTextInput ❌ Расчётный счёт csTextInput ❌ Корр. счёт phoneTextInput (mask) ❌ Телефон +7(999) 999 99 99 emailTextInput ✅ Email
Дополнительные поля (не в форме создания):
Поле Тип Обязательное Описание contract_numberTextInput ❌ Номер договора contract_dateDatePicker ❌ Дата договора director_name_fullTextInput ❌ Полное имя руководителя director_name_shortTextInput ❌ Сокращенное имя discountNumber ❌ Базовая скидка (0-100%) specification_start_numberNumber ❌ Стартовый номер спецификации manufacture_idSelect ❌ Место производства stock_idSelect ❌ Склад manager_idSelect ❌ Менеджер (group_id=3) filesRepeater ❌ Файлы [{name, file}] categoriesRepeater ❌ Скидки по категориям
┌─────────────────────────────────────────────────────────────────┐
│ Скидки │
├─────────────────────────────────────────────────────────────────┤
│ Категории │
│ ┌─────────────────────────────────┬───────────────┬───────────┐ │
│ │ Категория* │ Скидка* │ [Удалить] │ │
│ ├─────────────────────────────────┼───────────────┼───────────┤ │
│ │ [ТВЕРЬ LITE ▾] │ [25] │ [×] │ │
│ │ [ТВЕРЬ CLASSIC ▾] │ [45] │ [×] │ │
│ │ [ТВЕРЬ AERO ▾] │ [21] │ [×] │ │
│ │ [КЕССОНЫ ▾] │ [70] │ [×] │ │
│ └─────────────────────────────────┴───────────────┴───────────┘ │
│ [+ Добавить к категории] │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ Дилеры (компании) [+ Создать] │
├─────────────────────────────────────────────────────────────────────────────┤
│ [Поиск...] [Столбцы ▾] │
├──────┬────────────────────────────────────────────┬─────────────┬───────────┤
│ ☐ │ Организация │ Менеджер │ Действия │
├──────┼────────────────────────────────────────────┼─────────────┼───────────┤
│ ☐ │ ООО "ЭКОРУСЬ" │ Дубровин В. │ [Удалить] │
│ ☐ │ ООО "ЯРТОРГ" │ Маркина С. │ [Удалить] │
│ ☐ │ ИП Яковлева Анна Сергеевна │ Кунахович Д.│ [Удалить] │
└──────┴────────────────────────────────────────────┴─────────────┴───────────┘
│ Показано с 1 по 25 из 183 [25][50][Все] [1][2]...[8][→] │
└─────────────────────────────────────────────────────────────────────────────┘
Функции:
✅ Поиск по названию организации и ФИО менеджера
✅ Сортировка по дате создания (desc)
✅ Удаление (одиночное и массовое)
✅ Переход к редактированию по клику
✅ Скрытые колонки (created_at, updated_at)
┌─────────────────────────────────────────────────────────────────────────────┐
│ [← Назад] Редактирование дилера (компании) [Сохранить] │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────┐ ┌─────────────────────┐ │
│ │ СЕКЦИЯ 1: Реквизиты (2 колонки) │ │ СЕКЦИЯ 2: Настройки │ │
│ │ │ │ │ │
│ │ Организация* [ООО "Компания" ] │ │ Скидка [20 ] │ │
│ │ Номер договора [1125 ] Дата [05.07.2005 □] │ │ Номер спец. [10 ] │ │
│ │ ИНН [1234567890] ОГРН [1234567890123 ] │ │ Производство [▾ ] │ │
│ │ КПП [123456789 ] ОКПО [12345678 ] │ │ Склад [▾ ] │ │
│ │ ОКАТО [12345678 ] ОКВЭД [12.34 ] │ │ Менеджер [▾ ] │ │
│ │ Адрес [г. Москва, Профсоюзная 93А ] │ │ Телефон [+7... ] │ │
│ │ Юр.адрес [г. Москва, Профсоюзная 93А ] │ │ Email [a@a.ru ] │ │
│ │ Рук.полн [Иванов Иван Иванович ] │ └─────────────────────┘ │
│ │ Рук.сокр [Иванов И.И. ] │ │
│ │ Банк [Сбербанк ] БИК [044525225 ] │ │
│ │ Р/с [40702810... ] К/с [30101810... ] │ │
│ │ │ │
│ │ Файлы [+ Добавить] │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────────────────┐
│ │ СЕКЦИЯ 3: Скидки │
│ │ ┌───────────────────────────────────┬────────────┬─────────┐ │
│ │ │ Категория* │ Скидка* % │ │ │
│ │ ├───────────────────────────────────┼────────────┼─────────┤ │
│ │ │ [ТВЕРЬ LITE ▾] │ [25 ] │ [Удал.] │ │
│ │ │ [ТВЕРЬ CLASSIC ▾] │ [45 ] │ [Удал.] │ │
│ │ └───────────────────────────────────┴────────────┴─────────┘ │
│ │ [+ Добавить к категории] │
│ └───────────────────────────────────────────────────────────────────────────┘
└─────────────────────────────────────────────────────────────────────────────┘
// Company::discount($category_id)
public function discount ($category_id)
{
// 1. Ищем индивидуальную скидку на категорию
$discountCategory = $this -> categories -> where ( 'category_id' , $category_id) -> first ();
if ($discountCategory) {
return $discountCategory -> discount; // Индивидуальная скидка
}
// 2. Если нет — возвращаем базовую скидку компании
return $this -> discount;
}
Компания Базовая скидка Скидка на ТВЕРЬ LITE Итоговая скидка ООО "А" 20% 25% 25% (индивидуальная)ООО "Б" 30% — 30% (базовая)
// При создании заказа
$companyOrdersCount = Order :: query ()
-> whereHas ( 'dealer' , function ($q) use ($company) {
$q -> where ( 'company_id' , $company -> id);
})
-> count ();
$start = ( int ) ($company -> specification_start_number ?? 0 );
$specificationNumber = $start + $companyOrdersCount + 1 ;
Пример:
specification_start_number = 10
У компании уже 5 заказов
Новый заказ получит номер: 10 + 5 + 1 = 16
Company (1) ────────────┬───────────────> User (N)
│ └── company_id
│
└── manager_id ──> User (1) [Менеджер компании]
Менеджер (manager_id) — пользователь из группы "Менеджеры" (group_id=3)
Дилеры — пользователи с company_id = ID компании (group_id=2)
Поле Связь Назначение manufacture_id→ Manufacture Заказы компании производятся на этом заводе stock_id→ Stock Склад для доставки manager_id→ User Менеджер, ответственный за компанию
# Проблема Файл Влияние 1 Противоречие в disabled() Edit.php:132-133Скидка всегда редактируема
// Строки 131-133 — противоречие!
TextInput :: make ( 'discount' )
-> disabled (($disableEdit and ! $this -> authUser -> check_access ( 'company_can_edit_discount' )))
-> disabled ( false ) // ⚠️ Перезаписывает предыдущее!
# Проблема Описание Решение 2 Нет кнопки удаления в карточке deleteAction() объявлен, но не используетсяДобавить в view 3 Файлы скрыты от дилеров ->hidden($this->authUser->group_id == 2)Намеренно? 4 Нет валидации ИНН/ОГРН Принимаются любые строки Добавить правила
# Проблема Описание 5 Нет истории изменений Кто и когда менял данные 6 Нет проверки дублей Можно создать компании с одинаковым ИНН 7 Нет экспорта Выгрузка списка компаний
// Было (Edit.php:131-133):
TextInput :: make ( 'discount' )
-> disabled (($disableEdit and ! $this -> authUser -> check_access ( 'company_can_edit_discount' )))
-> disabled ( false )
// Стало:
TextInput :: make ( 'discount' )
-> disabled ($disableEdit && ! $this -> authUser -> check_access ( 'company_can_edit_discount' ))
TextInput :: make ( 'inn' )
-> maxLength ( 12 )
-> rules ([ 'nullable' , 'regex:/^\d{10}(\d{2})?$/' ])
-> helperText ( '10 цифр для ЮЛ, 12 для ИП' ),
-> headerActions ([
Tables\Actions\ExportAction :: make ()
-> exporter ( CompanyExporter ::class )
])
SELECT c . id , c . org , c . manager_id
FROM companies c
WHERE NOT EXISTS (
SELECT 1 FROM orders o
JOIN users u ON o . dealer_id = u . id
WHERE u . company_id = c . id
);
SELECT c . org , cat . name as category, cc . discount
FROM companies c
JOIN category_company cc ON c . id = cc . company_id
JOIN categories cat ON cc . category_id = cat . id
ORDER BY c . org , cat . name ;
Документ подготовлен на основе анализа исходного кода проекта