|
|
@@ -0,0 +1,298 @@
|
|
|
+# Модуль управления запчастями
|
|
|
+
|
|
|
+## Обзор
|
|
|
+
|
|
|
+Модуль управления запчастями добавляет полнофункциональную систему для:
|
|
|
+- Ведения каталога запчастей
|
|
|
+- Управления заказами деталей
|
|
|
+- Автоматического списания при использовании в рекламациях
|
|
|
+- Контроля наличия на складе
|
|
|
+
|
|
|
+## Основные возможности
|
|
|
+
|
|
|
+### 1. Каталог запчастей
|
|
|
+
|
|
|
+**ВАЖНО:** Каталог запчастей является **общим для всех лет**. Одна запчасть (по артикулу) существует во всех годах, но количества рассчитываются только для текущего выбранного года.
|
|
|
+
|
|
|
+**Доступ:** Меню → Запчасти → вкладка "Каталог"
|
|
|
+
|
|
|
+**Функции:**
|
|
|
+- Просмотр всех запчастей с вычисляемыми остатками
|
|
|
+- Добавление/редактирование запчастей (только admin)
|
|
|
+- Загрузка изображений запчастей
|
|
|
+- Экспорт/импорт каталога в Excel
|
|
|
+- Управление ценами (закупка, заказчик, экспертиза)
|
|
|
+- Установка минимального остатка
|
|
|
+
|
|
|
+**Вычисляемые поля:**
|
|
|
+- **Кол-во без док** - количество на складе без документов (для текущего года)
|
|
|
+- **Кол-во с док** - количество на складе с документами (для текущего года)
|
|
|
+- **Кол-во общее** - сумма двух предыдущих
|
|
|
+
|
|
|
+**Кликабельные ячейки:**
|
|
|
+Клик по ячейке с количеством открывает вкладку "Заказы деталей" с автоматическим фильтром по:
|
|
|
+- Артикулу
|
|
|
+- Наличию документов (если кликнули на "без док" или "с док")
|
|
|
+- Статусу "На складе"
|
|
|
+- Остатку > 0
|
|
|
+
|
|
|
+### 2. Заказы деталей
|
|
|
+
|
|
|
+**ВАЖНО:** Заказы **разделены по годам**. Каждый заказ принадлежит конкретному году.
|
|
|
+
|
|
|
+**Доступ:** Меню → Запчасти → вкладка "Заказы деталей"
|
|
|
+
|
|
|
+**Функции:**
|
|
|
+- Создание заказа детали (admin, manager)
|
|
|
+- Изменение статуса (Заказано → На складе → Отгружено)
|
|
|
+- Отгрузка детали (списание) с указанием куда/для чего
|
|
|
+- Просмотр истории списаний
|
|
|
+- Автоматическое изменение статуса на "Отгружено" при полном списании
|
|
|
+
|
|
|
+**Статусы заказа:**
|
|
|
+- **Заказано** - деталь в процессе заказа
|
|
|
+- **На складе** - деталь поступила и доступна для использования
|
|
|
+- **Отгружено** - деталь полностью списана
|
|
|
+
|
|
|
+**История списаний:**
|
|
|
+- Дата и время списания
|
|
|
+- Количество
|
|
|
+- Примечание (куда/для чего)
|
|
|
+- Пользователь
|
|
|
+- Флаг "автоматическое" (если списано через рекламацию)
|
|
|
+- Ссылка на рекламацию (если применимо)
|
|
|
+
|
|
|
+### 3. Контроль наличия
|
|
|
+
|
|
|
+**Доступ:** Меню → Запчасти → вкладка "Контроль наличия"
|
|
|
+
|
|
|
+**Две таблицы:**
|
|
|
+
|
|
|
+**Критический недостаток (красные строки):**
|
|
|
+- Запчасти с **отрицательным количеством**
|
|
|
+- Означает, что была попытка списать больше, чем есть на складе
|
|
|
+- Автоматически создаётся виртуальный заказ с отрицательным остатком
|
|
|
+
|
|
|
+**Ниже минимального остатка (желтые строки):**
|
|
|
+- Запчасти, у которых общее количество < минимальный остаток
|
|
|
+- Предупреждение о необходимости пополнения
|
|
|
+
|
|
|
+### 4. Интеграция с рекламациями
|
|
|
+
|
|
|
+**Автоматическое списание:**
|
|
|
+При добавлении детали в рекламацию система:
|
|
|
+1. Проверяет, есть ли запчасть с таким артикулом в каталоге
|
|
|
+2. Если есть - автоматически списывает из подходящего заказа (FIFO)
|
|
|
+3. Если нет достаточного количества - создаёт виртуальный заказ с недостачей
|
|
|
+4. Сохраняет связь запчасть-рекламация
|
|
|
+
|
|
|
+**Чекбокс "С док.":**
|
|
|
+- В форме деталей рекламации добавлен чекбокс "С док."
|
|
|
+- Определяет, нужна ли запчасть с документами или без
|
|
|
+- Влияет на выбор заказа для списания
|
|
|
+
|
|
|
+### 5. Справочник расценок
|
|
|
+
|
|
|
+**Доступ:** Меню → Запчасти → (отдельный раздел, только для admin)
|
|
|
+
|
|
|
+**Функции:**
|
|
|
+- Ведение справочника кодов расценок
|
|
|
+- Добавление/редактирование/удаление кодов
|
|
|
+- Расшифровка кодов
|
|
|
+- Tooltip при наведении на код в каталоге (планируется)
|
|
|
+
|
|
|
+## Мультигодовая архитектура
|
|
|
+
|
|
|
+**КРИТИЧНО ВАЖНО:**
|
|
|
+
|
|
|
+### Каталог запчастей (БЕЗ year):
|
|
|
+- Таблица `spare_parts` **НЕ имеет** поле `year`
|
|
|
+- Модель `SparePart` **НЕ использует** `YearScope`
|
|
|
+- Одна запчасть видна во всех годах
|
|
|
+
|
|
|
+### Заказы деталей (С year):
|
|
|
+- Таблица `spare_part_orders` **имеет** поле `year`
|
|
|
+- Модель `SparePartOrder` **использует** `YearScope`
|
|
|
+- Заказы разделены по годам
|
|
|
+
|
|
|
+### Логика вычисления количеств:
|
|
|
+- Вычисляемые поля (`quantity_without_docs`, `quantity_with_docs`) в модели `SparePart`
|
|
|
+- Учитывают **только заказы текущего года** из сессии `year()`
|
|
|
+- При смене года количества пересчитываются автоматически
|
|
|
+
|
|
|
+**Пример:**
|
|
|
+```php
|
|
|
+// Запчасть "BOLT-001" существует одна
|
|
|
+$sparePart = SparePart::where('article', 'BOLT-001')->first();
|
|
|
+
|
|
|
+// Год 2024: есть заказ на 100 шт
|
|
|
+session(['year' => 2024]);
|
|
|
+echo $sparePart->total_quantity; // 100
|
|
|
+
|
|
|
+// Год 2025: есть заказ на 50 шт
|
|
|
+session(['year' => 2025]);
|
|
|
+echo $sparePart->total_quantity; // 50
|
|
|
+
|
|
|
+// Год 2026: нет заказов
|
|
|
+session(['year' => 2026]);
|
|
|
+echo $sparePart->total_quantity; // 0
|
|
|
+```
|
|
|
+
|
|
|
+## Структура базы данных
|
|
|
+
|
|
|
+### Таблицы:
|
|
|
+1. `spare_parts` - каталог запчастей (БЕЗ year)
|
|
|
+2. `spare_part_orders` - заказы деталей (С year)
|
|
|
+3. `spare_part_order_shipments` - история списаний (БЕЗ year, привязана к заказу)
|
|
|
+4. `pricing_codes` - справочник расценок
|
|
|
+5. `reclamation_spare_part` - pivot таблица запчасти-рекламации
|
|
|
+
|
|
|
+### View:
|
|
|
+- `spare_part_orders_view` - представление для заказов с join'ами
|
|
|
+
|
|
|
+## Экспорт/Импорт
|
|
|
+
|
|
|
+### Экспорт каталога:
|
|
|
+- Кнопка "Экспорт" (только admin)
|
|
|
+- Выполняется через очередь (`ExportSparePartsJob`)
|
|
|
+- Шаблон: `/templates/SpareParts.xlsx`
|
|
|
+- **ВАЖНО:** Вычисляемые поля (количества) экспортируются для текущего года!
|
|
|
+
|
|
|
+### Формат Excel (13 колонок, БЕЗ года):
|
|
|
+| ID | Артикул | Где используется | Кол-во без док | Кол-во с док | Кол-во общее | Примечание | Цена закупки | Цена для заказчика | Цена экспертизы | № по ТСН | Шифр расценки | Минимальный остаток |
|
|
|
+
|
|
|
+## Изображения
|
|
|
+
|
|
|
+**Расположение:** `/public/images/spare_parts/`
|
|
|
+
|
|
|
+**Формат имени:** `{article}.jpg`
|
|
|
+
|
|
|
+**Пример:** `TEST-001.jpg`
|
|
|
+
|
|
|
+**Загрузка:**
|
|
|
+- Форма редактирования запчасти → кнопка "Загрузить изображение" (только admin)
|
|
|
+- Автоматическое отображение в каталоге и формах
|
|
|
+
|
|
|
+## Роли и права доступа
|
|
|
+
|
|
|
+### Admin:
|
|
|
+- Полный доступ ко всем функциям
|
|
|
+- Создание/редактирование/удаление запчастей
|
|
|
+- Экспорт/импорт
|
|
|
+- Управление заказами и списание
|
|
|
+- Доступ к ценам закупки
|
|
|
+
|
|
|
+### Manager:
|
|
|
+- Просмотр каталога
|
|
|
+- Создание/редактирование заказов деталей
|
|
|
+- Списание деталей
|
|
|
+- Просмотр контроля наличия
|
|
|
+- **НЕТ доступа** к ценам закупки
|
|
|
+
|
|
|
+### Другие роли:
|
|
|
+- Только просмотр
|
|
|
+
|
|
|
+## Технические детали
|
|
|
+
|
|
|
+### Сервисы:
|
|
|
+- `SparePartInventoryService` - логика автосписания и контроля
|
|
|
+- `Export/ExportSparePartsService` - экспорт в Excel
|
|
|
+
|
|
|
+### Jobs:
|
|
|
+- `Export/ExportSparePartsJob` - фоновая задача экспорта
|
|
|
+
|
|
|
+### Form Requests:
|
|
|
+- `StoreSparePartRequest` - валидация запчасти
|
|
|
+- `StoreSparePartOrderRequest` - валидация заказа
|
|
|
+- `ShipSparePartOrderRequest` - валидация отгрузки
|
|
|
+- `StoreReclamationDetailsRequest` - обновлён для поддержки `with_documents`
|
|
|
+
|
|
|
+### Контроллеры:
|
|
|
+- `SparePartController` - каталог
|
|
|
+- `SparePartOrderController` - заказы
|
|
|
+- `SparePartInventoryController` - контроль наличия
|
|
|
+- `PricingCodeController` - справочник расценок
|
|
|
+- `ReclamationController` - обновлён для интеграции
|
|
|
+
|
|
|
+## Маршруты
|
|
|
+
|
|
|
+```php
|
|
|
+// Каталог
|
|
|
+GET /spare-parts
|
|
|
+POST /spare-parts (admin)
|
|
|
+GET /spare-parts/create (admin)
|
|
|
+GET /spare-parts/{id}
|
|
|
+PUT /spare-parts/{id} (admin)
|
|
|
+DELETE /spare-parts/{id} (admin)
|
|
|
+POST /spare-parts/export (admin)
|
|
|
+POST /spare-parts/{id}/upload-image (admin)
|
|
|
+
|
|
|
+// Заказы
|
|
|
+GET /spare-part-orders
|
|
|
+POST /spare-part-orders (admin, manager)
|
|
|
+GET /spare-part-orders/create (admin, manager)
|
|
|
+GET /spare-part-orders/{id}
|
|
|
+PUT /spare-part-orders/{id} (admin, manager)
|
|
|
+DELETE /spare-part-orders/{id} (admin)
|
|
|
+POST /spare-part-orders/{id}/ship (admin, manager)
|
|
|
+POST /spare-part-orders/{id}/set-in-stock (admin, manager)
|
|
|
+
|
|
|
+// Контроль
|
|
|
+GET /spare-part-inventory
|
|
|
+
|
|
|
+// Справочник
|
|
|
+GET /pricing-codes (admin)
|
|
|
+POST /pricing-codes (admin)
|
|
|
+PUT /pricing-codes/{id} (admin)
|
|
|
+DELETE /pricing-codes/{id} (admin)
|
|
|
+
|
|
|
+// API
|
|
|
+GET /api/pricing-codes/{code}
|
|
|
+```
|
|
|
+
|
|
|
+## Тестирование
|
|
|
+
|
|
|
+### Создание тестовых данных:
|
|
|
+```bash
|
|
|
+docker exec dkr-app-1 php artisan tinker
|
|
|
+```
|
|
|
+
|
|
|
+```php
|
|
|
+// Создать запчасть
|
|
|
+$sp = SparePart::create([
|
|
|
+ 'article' => 'TEST-001',
|
|
|
+ 'used_in_maf' => 'Качели',
|
|
|
+ 'customer_price' => 150.00,
|
|
|
+ 'min_stock' => 10
|
|
|
+]);
|
|
|
+
|
|
|
+// Создать заказ
|
|
|
+$order = SparePartOrder::create([
|
|
|
+ 'spare_part_id' => $sp->id,
|
|
|
+ 'source_text' => 'Поставщик А',
|
|
|
+ 'status' => 'in_stock',
|
|
|
+ 'ordered_quantity' => 100,
|
|
|
+ 'with_documents' => true,
|
|
|
+ 'user_id' => 1
|
|
|
+]);
|
|
|
+
|
|
|
+// Проверить количество
|
|
|
+echo $sp->quantity_with_docs; // 100
|
|
|
+```
|
|
|
+
|
|
|
+### Тест автосписания:
|
|
|
+```php
|
|
|
+$service = new SparePartInventoryService();
|
|
|
+$service->deductForReclamation('TEST-001', 10, true, $reclamationId);
|
|
|
+
|
|
|
+// Проверить
|
|
|
+$sp->refresh();
|
|
|
+echo $sp->quantity_with_docs; // 90
|
|
|
+```
|
|
|
+
|
|
|
+## Известные особенности
|
|
|
+
|
|
|
+1. **Отрицательные остатки** - разрешены и означают недостачу
|
|
|
+2. **Виртуальные заказы** - автоматически создаются при недостаче со статусом "На складе" и отрицательным остатком
|
|
|
+3. **FIFO** - при списании выбирается самый ранний заказ с подходящими параметрами
|
|
|
+4. **Пересчёт количеств** - происходит динамически при каждом обращении к атрибутам модели
|