Документация ЛК
Модули

Дилеры (компании)

Модуль "Дилеры (компании)"

Версия: 1.0
Дата: 2025-11-22
Статус: Реализован


Содержание

  1. Общее описание
  2. Текущая реализация
  3. Права доступа
  4. Описание полей
  5. UI/UX
  6. Бизнес-логика
  7. Выявленные проблемы
  8. Рекомендации

1. Общее описание

1.1 Предназначение модуля

Модуль "Дилеры (компании)" предназначен для управления дилерскими организациями — юридическими лицами, которые являются партнёрами компании.

Основные функции:

  • Ведение реестра дилерских компаний
  • Хранение реквизитов организаций (ИНН, ОГРН, банковские данные)
  • Назначение менеджера, ответственного за компанию
  • Настройка индивидуальных скидок по категориям товаров
  • Привязка к складу и месту производства

1.2 Связь с другими модулями

МодульСвязь
Дилеры (менеджеры)Пользователи привязаны к компании через company_id
ЗаказыЗаказы создаются дилерами компании
КаталогСкидки на категории товаров
СкладыПривязка компании к складу
ПроизводствоПривязка компании к месту производства

1.3 Статистика

МетрикаЗначение
Всего компаний183
С назначенным менеджером183
С индивидуальными скидками5

2. Текущая реализация

2.1 Архитектура

┌─────────────────────────────────────────────────────────────────────────────┐
│                    МОДУЛЬ "ДИЛЕРЫ (КОМПАНИИ)"                                │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  РОУТЫ (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     — Форма редактирования (с секцией скидок)         │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

2.2 Структура БД

Таблица companies

ПолеТипОписание
idbigintPK
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Телефон
emailstringEmail
discountintegerБазовая скидка (%)
specification_start_numberintegerНомер отсчёта спецификации
manufacture_idFKМесто производства
stock_idFKСклад
manager_idFKМенеджер компании
filesjsonПрикреплённые файлы
created_attimestamp
updated_attimestamp

Таблица category_company (скидки по категориям)

ПолеТипОписание
idbigintPK
category_idFKКатегория товаров
company_idFKКомпания
discountintegerСкидка на категорию (%)

2.3 Файлы модуля

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

3. Права доступа

3.1 Политики

ПолитикаОписание
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Удаление СВОИХ компаний

3.2 Логика разграничения

// 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;  // Только свою
}

3.3 Матрица доступа по ролям

РольПросмотрСозданиеРедактированиеУдалениеСкидки
Администратор✅ Все✅ Все
Менеджер⚠️ Свои⚠️ Свои⚠️ Свои
Дилер
Производство

4. Описание полей

4.1 Форма создания (Add.php)

ПолеТипОбязательноеОписание
orgTextInputНазвание организации
innTextInputИНН
ogrnTextInputОГРН
kppTextInputКПП
okpoTextInputОКПО
okatoTextInputОКАТО
okvedTextInputОКВЭД
addressTextInputФактический адрес
legal_addressTextInputЮридический адрес
bankTextInputБанк
bikTextInputБИК
rsTextInputРасчётный счёт
csTextInputКорр. счёт
phoneTextInput (mask)Телефон +7(999) 999 99 99
emailTextInputEmail

4.2 Форма редактирования (Edit.php)

Дополнительные поля (не в форме создания):

ПолеТипОбязательноеОписание
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Скидки по категориям

4.3 Секция "Скидки"

┌─────────────────────────────────────────────────────────────────┐
│ Скидки                                                          │
├─────────────────────────────────────────────────────────────────┤
│ Категории                                                        │
│ ┌─────────────────────────────────┬───────────────┬───────────┐ │
│ │ Категория*                      │ Скидка*       │ [Удалить] │ │
│ ├─────────────────────────────────┼───────────────┼───────────┤ │
│ │ [ТВЕРЬ LITE            ▾]       │ [25]          │ [×]       │ │
│ │ [ТВЕРЬ CLASSIC         ▾]       │ [45]          │ [×]       │ │
│ │ [ТВЕРЬ AERO            ▾]       │ [21]          │ [×]       │ │
│ │ [КЕССОНЫ               ▾]       │ [70]          │ [×]       │ │
│ └─────────────────────────────────┴───────────────┴───────────┘ │
│ [+ Добавить к категории]                                         │
└─────────────────────────────────────────────────────────────────┘

5. UI/UX

5.1 Список компаний

┌─────────────────────────────────────────────────────────────────────────────┐
│ Дилеры (компании)                                         [+ Создать]       │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                    [Поиск...] [Столбцы ▾]   │
├──────┬────────────────────────────────────────────┬─────────────┬───────────┤
│ ☐    │ Организация                                │ Менеджер    │ Действия  │
├──────┼────────────────────────────────────────────┼─────────────┼───────────┤
│ ☐    │ ООО "ЭКОРУСЬ"                              │ Дубровин В. │ [Удалить] │
│ ☐    │ ООО "ЯРТОРГ"                               │ Маркина С.  │ [Удалить] │
│ ☐    │ ИП Яковлева Анна Сергеевна                 │ Кунахович Д.│ [Удалить] │
└──────┴────────────────────────────────────────────┴─────────────┴───────────┘
│ Показано с 1 по 25 из 183              [25][50][Все]  [1][2]...[8][→]       │
└─────────────────────────────────────────────────────────────────────────────┘

Функции:
✅ Поиск по названию организации и ФИО менеджера
✅ Сортировка по дате создания (desc)
✅ Удаление (одиночное и массовое)
✅ Переход к редактированию по клику
✅ Скрытые колонки (created_at, updated_at)

5.2 Карточка компании

┌─────────────────────────────────────────────────────────────────────────────┐
│ [← Назад]  Редактирование дилера (компании)                   [Сохранить]   │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│ ┌─────────────────────────────────────────────────┐ ┌─────────────────────┐ │
│ │ СЕКЦИЯ 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      ] │ [Удал.] │             │
│ │ └───────────────────────────────────┴────────────┴─────────┘             │
│ │ [+ Добавить к категории]                                                  │
│ └───────────────────────────────────────────────────────────────────────────┘
└─────────────────────────────────────────────────────────────────────────────┘

6. Бизнес-логика

6.1 Система скидок

Приоритет скидок

// 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% (базовая)

6.2 Нумерация спецификаций

// При создании заказа
$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

6.3 Связь Компания ↔ Дилеры

Company (1) ────────────┬───────────────> User (N)
                        │                  └── company_id

                        └── manager_id ──> User (1)  [Менеджер компании]
  • Менеджер (manager_id) — пользователь из группы "Менеджеры" (group_id=3)
  • Дилеры — пользователи с company_id = ID компании (group_id=2)

6.4 Привязки

ПолеСвязьНазначение
manufacture_id→ ManufactureЗаказы компании производятся на этом заводе
stock_id→ StockСклад для доставки
manager_id→ UserМенеджер, ответственный за компанию

7. Выявленные проблемы

7.1 Критические

#ПроблемаФайлВлияние
1Противоречие в disabled()Edit.php:132-133Скидка всегда редактируема
// Строки 131-133 — противоречие!
TextInput::make('discount')
    ->disabled(($disableEdit and ! $this->authUser->check_access('company_can_edit_discount')))
    ->disabled(false)  // ⚠️ Перезаписывает предыдущее!

7.2 Средние

#ПроблемаОписаниеРешение
2Нет кнопки удаления в карточкеdeleteAction() объявлен, но не используетсяДобавить в view
3Файлы скрыты от дилеров->hidden($this->authUser->group_id == 2)Намеренно?
4Нет валидации ИНН/ОГРНПринимаются любые строкиДобавить правила

7.3 Низкие

#ПроблемаОписание
5Нет истории измененийКто и когда менял данные
6Нет проверки дублейМожно создать компании с одинаковым ИНН
7Нет экспортаВыгрузка списка компаний

8. Рекомендации

8.1 Приоритет: Высокий

Исправить противоречие disabled()

// Было (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'))

8.2 Приоритет: Средний

Добавить валидацию ИНН

TextInput::make('inn')
    ->maxLength(12)
    ->rules(['nullable', 'regex:/^\d{10}(\d{2})?$/'])
    ->helperText('10 цифр для ЮЛ, 12 для ИП'),

8.3 Приоритет: Низкий

Добавить экспорт

->headerActions([
    Tables\Actions\ExportAction::make()
        ->exporter(CompanyExporter::class)
])

Приложение: SQL для аналитики

Компании без заказов

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;

Документ подготовлен на основе анализа исходного кода проекта

On this page

Модуль "Дилеры (компании)"Содержание1. Общее описание1.1 Предназначение модуля1.2 Связь с другими модулями1.3 Статистика2. Текущая реализация2.1 Архитектура2.2 Структура БДТаблица companiesТаблица category_company (скидки по категориям)2.3 Файлы модуля3. Права доступа3.1 Политики3.2 Логика разграничения3.3 Матрица доступа по ролям4. Описание полей4.1 Форма создания (Add.php)4.2 Форма редактирования (Edit.php)4.3 Секция "Скидки"5. UI/UX5.1 Список компаний5.2 Карточка компании6. Бизнес-логика6.1 Система скидокПриоритет скидокПример расчёта6.2 Нумерация спецификаций6.3 Связь Компания ↔ Дилеры6.4 Привязки7. Выявленные проблемы7.1 Критические7.2 Средние7.3 Низкие8. Рекомендации8.1 Приоритет: ВысокийИсправить противоречие disabled()8.2 Приоритет: СреднийДобавить валидацию ИНН8.3 Приоритет: НизкийДобавить экспортПриложение: SQL для аналитикиКомпании без заказовКомпании со скидками по категориям