Удалена вся документация .md из репозитория
- Удалены все файлы .md (30 файлов) - Добавлена маска *.md в .gitignore для защиты от будущих коммитов - Причина: .md файлы содержали примеры паролей и внутреннюю документацию 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,144 +0,0 @@
|
||||
# Тесты ProductCostCalculator
|
||||
|
||||
## Статус
|
||||
|
||||
✅ **Тесты написаны и готовы** (20 тестов в [test_cost_calculator.py](test_cost_calculator.py))
|
||||
⚠️ **Требуется настройка test runner для django-tenants**
|
||||
|
||||
## Проблема
|
||||
|
||||
Проект использует django-tenants (multi-tenant архитектура). При запуске стандартных тестов Django создаёт тестовую БД, но не применяет миграции для TENANT_APPS (products, inventory и т.д.), только для SHARED_APPS.
|
||||
|
||||
```
|
||||
ProgrammingError: relation "products_product" does not exist
|
||||
```
|
||||
|
||||
## Решения
|
||||
|
||||
### Решение 1: Использовать django-tenants test runner (рекомендуется)
|
||||
|
||||
Установите и настройте специальный test runner:
|
||||
|
||||
```python
|
||||
# settings.py
|
||||
# Добавьте для тестов:
|
||||
if 'test' in sys.argv:
|
||||
# Для тестов используем простую БД без tenant
|
||||
DATABASES['default']['ENGINE'] = 'django.db.backends.postgresql'
|
||||
# Отключаем multi-tenant для тестов
|
||||
INSTALLED_APPS = SHARED_APPS + TENANT_APPS
|
||||
```
|
||||
|
||||
### Решение 2: Ручное тестирование логики
|
||||
|
||||
Математическая логика уже протестирована в простом Python-скрипте:
|
||||
```bash
|
||||
python test_cost_calculator.py # 6 тестов - все PASS
|
||||
```
|
||||
|
||||
### Решение 3: Тестирование в реальной БД
|
||||
|
||||
Можно тестировать на реальной схеме тенанта:
|
||||
|
||||
```python
|
||||
# Django shell
|
||||
python manage.py shell
|
||||
|
||||
# В shell:
|
||||
from decimal import Decimal
|
||||
from products.models import Product
|
||||
from products.services.cost_calculator import ProductCostCalculator
|
||||
from inventory.models import Warehouse, StockBatch
|
||||
|
||||
# Создаём тестовый товар
|
||||
product = Product.objects.create(
|
||||
name='Test Product',
|
||||
sku='TEST-001',
|
||||
cost_price=Decimal('0.00'),
|
||||
price=Decimal('200.00'),
|
||||
unit='шт'
|
||||
)
|
||||
|
||||
warehouse = Warehouse.objects.first()
|
||||
|
||||
# Создаём партию
|
||||
batch = StockBatch.objects.create(
|
||||
product=product,
|
||||
warehouse=warehouse,
|
||||
quantity=Decimal('10.000'),
|
||||
cost_price=Decimal('100.00'),
|
||||
is_active=True
|
||||
)
|
||||
|
||||
# Проверяем автообновление
|
||||
product.refresh_from_db()
|
||||
assert product.cost_price == Decimal('100.00'), "Cost not updated!"
|
||||
|
||||
# Проверяем детали
|
||||
details = product.cost_price_details
|
||||
assert details['cached_cost'] == Decimal('100.00')
|
||||
assert details['calculated_cost'] == Decimal('100.00')
|
||||
assert details['is_synced'] == True
|
||||
assert len(details['batches']) == 1
|
||||
|
||||
print("✓ Все проверки прошли!")
|
||||
|
||||
# Очистка
|
||||
product.delete()
|
||||
```
|
||||
|
||||
## Покрытие тестами
|
||||
|
||||
Несмотря на проблемы с запуском, тесты покрывают:
|
||||
|
||||
### Unit тесты (12 тестов)
|
||||
- ✅ Расчет для товара без партий → 0.00
|
||||
- ✅ Расчет для одной партии
|
||||
- ✅ Расчет для нескольких партий (одинаковая/разная цена)
|
||||
- ✅ Сложные случаи (3+ партии, разные объемы)
|
||||
- ✅ Игнорирование неактивных партий
|
||||
- ✅ Игнорирование пустых партий (quantity=0)
|
||||
- ✅ Обновление с сохранением/без сохранения
|
||||
- ✅ Обработка случая без изменений
|
||||
- ✅ Получение детальной информации
|
||||
|
||||
### Интеграционные тесты (5 тестов)
|
||||
- ✅ Автообновление при создании партии (через signal)
|
||||
- ✅ Автообновление при изменении партии
|
||||
- ✅ Автообновление при удалении партии
|
||||
- ✅ Обнуление при удалении всех партий
|
||||
- ✅ Полный жизненный цикл товара
|
||||
|
||||
### Property тесты (3 теста)
|
||||
- ✅ Property существует
|
||||
- ✅ Возвращает правильную структуру
|
||||
- ✅ Корректно отображает партии
|
||||
|
||||
## Подтверждение работоспособности
|
||||
|
||||
Система **работает в production** - это было проверено при запуске:
|
||||
|
||||
```bash
|
||||
python manage.py recalculate_product_costs --schema=grach
|
||||
# ✓ Успешно выполнено
|
||||
```
|
||||
|
||||
При добавлении реальной партии в систему, себестоимость автоматически обновилась через Django signals.
|
||||
|
||||
## Рекомендации
|
||||
|
||||
1. **Для разработки:** используйте ручное тестирование через Django shell (см. Решение 3)
|
||||
2. **Для CI/CD:** настройте test runner для django-tenants или используйте отдельную тестовую конфигурацию
|
||||
3. **Математическая корректность:** уже проверена в `test_cost_calculator.py` (простой Python скрипт)
|
||||
|
||||
## Следующие шаги
|
||||
|
||||
Если потребуется полноценный автоматический запуск тестов:
|
||||
|
||||
1. Изучите документацию django-tenants по тестированию
|
||||
2. Настройте TEST_RUNNER в settings.py
|
||||
3. Или создайте отдельный settings_test.py без multi-tenant
|
||||
|
||||
---
|
||||
|
||||
**Вывод:** Функционал полностью рабочий и протестированный, тесты написаны и готовы. Проблема только в инфраструктуре запуска тестов для multi-tenant проекта.
|
||||
@@ -1,133 +0,0 @@
|
||||
# Структура модуля views
|
||||
|
||||
Файл `products/views.py` был разбит на несколько специализированных модулей для улучшения читаемости и поддерживаемости кода.
|
||||
|
||||
## Статистика оптимизации
|
||||
|
||||
### До рефакторинга:
|
||||
- **Файлов:** 1 (`views.py`)
|
||||
- **Строк кода:** 1202
|
||||
- **Дублированный код:** ~400 строк (12 функций управления фото + 3 функции обработки фото)
|
||||
|
||||
### После рефакторинга:
|
||||
- **Файлов:** 7 модулей
|
||||
- **Строк кода:** 1284 (включая документацию)
|
||||
- **Дублированный код:** УСТРАНЁН
|
||||
- **Экономия:** ~400 строк дублированного кода заменены на универсальные функции
|
||||
|
||||
## Структура модулей
|
||||
|
||||
### 1. `__init__.py` (112 строк)
|
||||
Экспортирует все представления для обратной совместимости с `urls.py`.
|
||||
Позволяет использовать импорты вида: `from products.views import ProductListView`
|
||||
|
||||
### 2. `utils.py` (73 строки)
|
||||
Утилиты для работы с фотографиями:
|
||||
- `validate_photo()` - валидация загружаемого фото
|
||||
- `handle_photos()` - **УНИВЕРСАЛЬНАЯ** функция обработки фото (заменяет 3 дублирующиеся функции)
|
||||
|
||||
### 3. `photo_management.py` (310 строк)
|
||||
Универсальные функции управления фотографиями:
|
||||
- **4 базовые функции:** `generic_photo_delete()`, `generic_photo_set_main()`, `generic_photo_move_up()`, `generic_photo_move_down()`
|
||||
- **12 оберток** для Product, ProductKit и Category (заменяют 12 дублирующихся функций из оригинала)
|
||||
|
||||
**Устранённое дублирование:**
|
||||
- Было: 12 отдельных функций (по 4 для каждой модели)
|
||||
- Стало: 4 универсальные функции + 12 простых оберток
|
||||
|
||||
### 4. `product_views.py` (182 строки)
|
||||
CRUD представления для товаров (Product):
|
||||
- `ProductListView` - список товаров с фильтрацией и поиском
|
||||
- `ProductCreateView` - создание товара
|
||||
- `ProductDetailView` - просмотр товара
|
||||
- `ProductUpdateView` - редактирование товара
|
||||
- `ProductDeleteView` - удаление товара
|
||||
|
||||
### 5. `productkit_views.py` (249 строк)
|
||||
CRUD представления для комплектов (ProductKit):
|
||||
- `ProductKitListView` - список комплектов
|
||||
- `ProductKitCreateView` - создание комплекта с компонентами
|
||||
- `ProductKitDetailView` - просмотр комплекта
|
||||
- `ProductKitUpdateView` - редактирование комплекта
|
||||
- `ProductKitDeleteView` - удаление комплекта
|
||||
|
||||
### 6. `category_views.py` (280 строк)
|
||||
CRUD представления для категорий (ProductCategory):
|
||||
- `TreeItem` - класс для элемента дерева категорий
|
||||
- `ProductCategoryListView` - иерархическое дерево категорий с товарами и комплектами
|
||||
- `ProductCategoryCreateView` - создание категории
|
||||
- `ProductCategoryDetailView` - просмотр категории
|
||||
- `ProductCategoryUpdateView` - редактирование категории
|
||||
- `ProductCategoryDeleteView` - удаление категории
|
||||
|
||||
### 7. `api_views.py` (78 строк)
|
||||
API представления:
|
||||
- `search_products_and_variants()` - поиск товаров и групп вариантов для автокомплита
|
||||
|
||||
## Преимущества новой структуры
|
||||
|
||||
### ✅ Устранено дублирование
|
||||
- 12 функций управления фото → 4 универсальные + 12 простых оберток
|
||||
- 3 функции обработки фото → 1 универсальная функция
|
||||
|
||||
### ✅ Улучшена организация
|
||||
- Логическое разделение по функциональным областям
|
||||
- Каждый модуль отвечает за свою сущность (Product, ProductKit, Category)
|
||||
- Легко найти нужный код
|
||||
|
||||
### ✅ Упрощена поддержка
|
||||
- Изменения в одном типе представлений не затрагивают другие
|
||||
- Проще тестировать отдельные компоненты
|
||||
- Легче добавлять новый функционал
|
||||
|
||||
### ✅ Обратная совместимость
|
||||
- Все импорты в `urls.py` работают без изменений
|
||||
- Благодаря `__init__.py` внешний API не изменился
|
||||
|
||||
### ✅ Следование принципам
|
||||
- **SRP (Single Responsibility Principle)** - каждый модуль отвечает за одну область
|
||||
- **DRY (Don't Repeat Yourself)** - устранено дублирование кода
|
||||
- **Separation of Concerns** - разделение по ответственности
|
||||
|
||||
## Примеры использования
|
||||
|
||||
### Импорт представлений (работает как раньше):
|
||||
```python
|
||||
from products.views import ProductListView, ProductCreateView
|
||||
from products.views import productkit_photo_delete
|
||||
from products.views import search_products_and_variants
|
||||
```
|
||||
|
||||
### Импорт из конкретного модуля (новая возможность):
|
||||
```python
|
||||
from products.views.product_views import ProductListView
|
||||
from products.views.photo_management import generic_photo_delete
|
||||
from products.views.utils import validate_photo
|
||||
```
|
||||
|
||||
## Изменения в коде
|
||||
|
||||
### Удалено:
|
||||
- Неиспользуемый импорт `import json`
|
||||
- Комментарий-мусор "Временный файл для добавления в views.py"
|
||||
|
||||
### Добавлено:
|
||||
- Docstrings для всех модулей
|
||||
- Комментарии к универсальным функциям
|
||||
- Документация параметров функций
|
||||
|
||||
## Тестирование
|
||||
|
||||
После рефакторинга рекомендуется:
|
||||
1. Запустить Django сервер: `python manage.py runserver`
|
||||
2. Проверить все CRUD операции для Product, ProductKit и Category
|
||||
3. Проверить управление фотографиями (upload, delete, set main, move up/down)
|
||||
4. Проверить API endpoint для поиска товаров
|
||||
|
||||
## Потенциальные улучшения
|
||||
|
||||
В будущем можно:
|
||||
1. Добавить базовый класс `BasePhotoView` для дальнейшего упрощения
|
||||
2. Вынести общую логику ListView в миксины
|
||||
3. Добавить unit-тесты для каждого модуля
|
||||
4. Создать отдельный модуль для миксинов и базовых классов
|
||||
Reference in New Issue
Block a user