- Создан элегантный тег для автоматического сохранения GET-параметров
- Код пагинации сократился в 10 раз
- Переиспользуется в любых шаблонах проекта
- 100 процентов Django-way без хаков
- Убран нерабочий хак с params.pop
- Все ссылки пагинации теперь явно передают параметры:
* q (поисковый запрос)
* has_notes (фильтр заметок)
* no_phone (фильтр отсутствия телефона)
* no_email (фильтр отсутствия email)
- Пагинация теперь работает корректно с сохранением всех фильтров
- Вместо текста 'X / Y' теперь кликабельные цифры страниц
- Показывается до 10 страниц (±5 от текущей)
- Текущая страница выделена (active)
- Умная логика: если на странице 15, показывает 10-20
- Все параметры фильтров сохраняются при клике на номер страницы
- Пагинация теперь сохраняет ВСЕ GET-параметры (query, has_notes, no_phone, no_email)
- Использован request.GET.copy() и params.urlencode для передачи всех параметров
- Фильтры больше не сбрасываются при переходе между страницами
- Создан CustomerFilter с тремя фильтрами:
* Есть заметки (has_notes)
* Нет телефона (no_phone)
* Нет email (no_email)
- Обновлен views.py для использования фильтров
- Добавлены чекбоксы фильтров в шаблон списка клиентов
- Фильтры работают совместно с поиском
- Кнопка Очистить отображается при активных фильтрах или поиске
- Новый столбец Notes в таблице клиентов
- Текст обрезается до 10 слов (truncatewords)
- Максимальная ширина 300px с ellipsis
- При наведении показывается полный текст через title
- Если заметок нет, отображается тире (—)
- Форма начинает отправку сразу при submit
- Прогресс-бар и защита включаются через 10ms (после начала отправки)
- Предупреждение появляется только при попытке закрыть страницу во время импорта
- Импорт корректно выполняется на сервере
- Добавлена предобработка email перед валидацией:
* Исправление типичных опечаток (mail ru -> mail.ru, .ry -> .ru)
* Удаление пробелов и двойных @@
* Умное добавление @ для популярных доменов
* Исправление доменов без точки (gmail -> gmail.com)
- Улучшена нормализация телефонов:
* Умное добавление кода страны (+375, +7, +380)
* Конверсия старого формата 8XXXXXXXXXX -> +7XXXXXXXXXX
* Проверка длины номера (10-15 символов)
* Поддержка локальных белорусских номеров (9 цифр)
- Реализована идемпотентность импорта:
* Notes не раздуваются при повторных импортах (метод _append_unique_note)
* ContactChannel не дублируется для одного клиента
* Проверка существования альтернативных контактов по customer+type+value
- Добавлен прогресс-бар и защита от закрытия:
* Визуальный прогресс-бар с анимацией и динамическим текстом
* Блокировка формы во время импорта
* Предупреждение браузера при попытке закрыть страницу
- Создана команда clear_anatol_customers для тестирования
- Добавлен тестовый файл test_customer_preprocess.csv с примерами исправляемых ошибок
- Обновлены начальные миграции для всех приложений
- Удалены устаревшие миграции для единиц измерения и SKU
- Добавлен новый сервис unit_service.py для управления единицами
- Обновлены команды инициализации данных тенанта
На странице детализации товара теперь отображается таблица с единицами
продажи: название, единица измерения, коэффициент, цена, мин. количество
и шаг. Единица по умолчанию выделена зелёным.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Изменён floatformat с :0 на :-3 для корректного отображения
дробных остатков товаров (до 3 знаков после запятой).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
При редактировании товара проверка зарезервированных префиксов теперь
пропускается, если артикул не изменился. Это позволяет редактировать
товары с автоматически сгенерированными артикулами.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Удалена функция parse_variant_suffix и логика автоматического добавления
суффикса варианта к артикулу товара. SKU теперь всегда имеет формат
PROD-XXXXXX без дополнительных суффиксов.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Проблема: при приёмке товаров отображались только товары с ненулевым
остатком на складе, товары с нулевым остатком не находились.
Решение: добавлен параметр skip_stock_filter в компонент поиска товаров,
который отключает фильтрацию по остаткам. Для приёмки этот параметр
включён по умолчанию.
Изменения:
- api_views.py: добавлен параметр skip_stock_filter в _apply_product_filters
- product_search_picker.html: добавлен data-атрибут skip_stock_filter
- product-search-picker.js: передача параметра в API
- incoming_document_detail.html: включён skip_stock_filter=True
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Добавлена функция loadAndDisplaySalesUnitsFromHidden для загрузки UI единиц продажи из черновика
- При загрузке товара из черновика теперь автоматически отображается и устанавливается сохранённая единица продажи
- Теперь при открытии отложенного заказа с 0.3 кг корректно отображается килограмм, а не базовая единица
- В terminal.js добавлена передача sales_unit_id в данные черновика заказа
- В order_form.html добавлено заполнение поля sales_unit при предзаполнении из черновика
- Теперь при создании отложенного заказа с товаром в единицах продажи сохраняется корректная единица измерения
- Поле quantity изменено с PositiveIntegerField на DecimalField(max_digits=10, decimal_places=3)
- Это необходимо для корректной работы с единицами продажи (например, 2.5 банча)
- Создана миграция 0004_change_orderitem_quantity_to_decimal
- Теперь POS корректно обрабатывает товары с дробными количествами в единицах продажи
- При создании OrderItem теперь передаётся sales_unit из данных корзины
- Это позволяет корректно рассчитывать total_amount для товаров с единицами продажи
- Исправлена ошибка когда сумма заказа была 0 при использовании единиц продажи
- Изменена проверка с >= на > в Delivery.clean()
- Равные времена разрешены для POS-продаж (самовывоз в точное время)
- Обновлены сообщения об ошибках валидации
- Обновлён pos/views.py: метод pos_checkout теперь создаёт Order и связанную модель Delivery
- Обновлён showcase_manager.py: метод sell_showcase_item_to_customer использует новую архитектуру
- Удалён устаревший скрипт create_demo_orders.py
- Исправлена ошибка 'property is_delivery of Order object has no setter'
Проблема:
- JavaScript float arithmetic даёт погрешность при вычислениях
- На карточке товара показывалось -0.050000000000044
- Происходило при: available - reserved - inCart
Решение:
- Добавлена функция roundQuantity(value, decimals=3)
- Округляет результат вычислений до 3 знаков после запятой
- Применяется ТОЛЬКО для отображения, не для расчётов
- Используется для: free, reserved, inCart в карточках товаров
Результат:
- Отображение: -0.05 вместо -0.050000000000044
- Данные с бэка остаются точными (строка)
- Погрешность устранена только визуально
Примечание:
- Округление в JS НЕИЗБЕЖНО для отображения
- Это НЕ маскировка - это правильное форматирование
- Бэкенд уже отдаёт точные данные как строки
Проблема:
- free_qty передавался как float(decimal) в JSON API
- При конвертации Decimal→float терялась точность
- JavaScript показывал -0.050000000000044 вместо -0.05
Решение:
- free_qty теперь передаётся как строка: str(free_qty)
- Добавлено отдельное поле free_qty_sort (float) для сортировки
- После сортировки free_qty_sort удаляется из результата
- JavaScript parseFloat() корректно парсит строку без потери точности
Результат:
- Отображение остатков точное: -0.05 вместо -0.050000000000044
- Нет округления на фронте - видны реальные данные
- Сортировка по остаткам работает корректно
КРИТИЧНО: При создании Sale использовалось неправильное поле!
Проблема:
- При проведении заказа Sale создавался с reservation.quantity
- Это количество в ЕДИНИЦАХ ПРОДАЖИ, а не в базовых!
- Пример: 1 ветка списывала 1 банч вместо 0.05 банча
Решение:
- Строка 410: sale_quantity = reservation.quantity_base (для товаров)
- Строка 368: quantity=reservation.quantity_base (для комплектов)
- Fallback на .quantity для обратной совместимости
Теперь:
- Sale.quantity всегда в базовых единицах
- FIFO списание корректно
- StockBatch уменьшается на правильное количество
- Добавлено 'quantity_base' в список полей, влияющих на Stock
- Теперь Stock пересчитывается при изменении quantity_base
- Обновлена документация сигнала update_stock_on_reservation_change
КРИТИЧНО: Все агрегации Reservation.quantity заменены на quantity_base
Проблемы и решения:
🔴 КРИТИЧНО - BatchManager.write_off_by_fifo():
- Проблема: суммировал quantity вместо quantity_base
- Влияние: FIFO расчет свободного товара был некорректен
- Решение: aggregate(Sum('quantity_base')) в строках 118, 125
🟡 СРЕДНЯЯ ВАЖНОСТЬ - ShowcaseManager:
- reserve_showcase_item(): обновление quantity и quantity_base (строка 403)
- release_showcase_reservation(): обновление обоих полей (строка 481)
- Теперь витринные резервы полностью консистентны
🟡 СРЕДНЯЯ ВАЖНОСТЬ - TransformationService:
- confirm(): проверка доступности через quantity_base (строка 254)
- Корректная валидация при трансформации товаров
🟢 НИЗКАЯ ВАЖНОСТЬ - WriteOffDocumentService:
- update_item(): синхронизация quantity и quantity_base (строка 175)
- Полнота данных в резервах документов списания
🟢 НИЗКАЯ ВАЖНОСТЬ - Сигналы (signals.py):
- update_order_item_reservation(): обновление обоих полей для товаров
- Для обычных товаров: quantity_base = quantity_in_base_units (строка 1081)
- Для комплектов: quantity_base = quantity (компоненты в базовых) (строка 1107)
- Добавлено обновление sales_unit при изменении OrderItem
Архитектура:
- Принцип: quantity_base ВСЕГДА содержит количество в базовых единицах
- Все агрегации резервов используют quantity_base для корректных расчетов
- quantity сохраняется для совместимости и отображения
- sales_unit хранит ссылку на единицу продажи для аудита
- Проблема: debug_page показывал quantity вместо quantity_base для резервов
- Проблема: Stock.refresh_from_batches() суммировал quantity вместо quantity_base
- Решение:
- debug_page.html: добавлены колонки для единиц продажи и базовых единиц
- debug_page.html: теперь показывается quantity (ед.прод.) и quantity_base (базовые)
- models.py: Stock.refresh_from_batches() теперь суммирует quantity_base
- Теперь quantity_reserved и quantity_free отображаются корректно в базовых единицах
- Проблема: сигнал на Order срабатывал ДО вычисления quantity_in_base_units в OrderItem.save()
- Решение: переместили резервирование на сигнал post_save для OrderItem
- Теперь quantity_in_base_units гарантированно вычислено перед резервированием
- Изменения:
- signals.py: reserve_stock_on_order_create → reserve_stock_on_item_create
- Сигнал теперь на OrderItem вместо Order
- Резервы создаются для каждой позиции отдельно после её сохранения
- Проблема: при смене единицы продажи с базовой на другую поле quantity визуально показывало 1, но при отправке формы значение терялось
- Решение: при смене единицы проверяем quantity и устанавливаем минимальное значение если оно пустое/нулевое
- Изменения:
- sales-units.js: добавлена проверка и установка min_quantity при смене единицы
- Проблема: при выборе товара через ProductSearchPicker, select оставался пустым
- Решение: динамическое создание <option> элемента с выбранным товаром
- Затронутые файлы:
- incoming_document_detail.html: функции selectProduct() и clearSelectedProduct()
- writeoff_document/detail.html: аналогичные исправления
- Теперь компонент корректно работает во всех документах системы
Добавлена полноценная интеграция единиц измерения (UoM) для продажи
товаров в разных единицах с автоматическим пересчётом цен и остатков.
## Основные изменения:
### Backend
- Расширен API поиска товаров (api_views.py): добавлена сериализация sales_units
- Создан новый endpoint get_product_sales_units_api для загрузки единиц с остатками
- Добавлено поле sales_unit в OrderItemForm и SaleForm с валидацией
- Созданы CRUD views для управления единицами продажи (uom_views.py)
- Обновлена ProductForm: использует base_unit вместо устаревшего unit
### Frontend
- Создан модуль sales-units.js с функциями для работы с единицами
- Интегрирован в select2-product-search.js: автозагрузка единиц при выборе товара
- Добавлены контейнеры для единиц в order_form.html и sale_form.html
- Реализовано автоматическое обновление цены при смене единицы продажи
- При выборе базовой единицы цена возвращается к базовой цене товара
### UI
- Добавлены страницы управления единицами продажи в навбар
- Созданы шаблоны: sales_unit_list.html, sales_unit_form.html, sales_unit_delete.html
- Добавлены фильтры по товару, единице, активности и дефолтности
## Исправленные ошибки:
- Порядок инициализации: обработчики устанавливаются ДО триггера события change
- Цена корректно обновляется при выборе единицы продажи
- При выборе "Базовая единица" возвращается базовая цена товара
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add new models UnitOfMeasure and ProductSalesUnit to enable selling products in different units (e.g., bunches, kg). Update Product model with base_unit field and methods for unit conversions and availability. Extend Sale, Reservation, and OrderItem models with sales_unit fields and snapshots. Modify SaleProcessor to handle quantity conversions. Include admin interfaces for managing units. Add corresponding database migrations.
- Удалены из версионирования: check_duplicates.py, cleanup_stuck_photos.py, cleanup_commands.txt, start_all.bat, ГИД ПО ЗАПУСКУ
- Обновлён .gitignore для предотвращения отслеживания временных скриптов обслуживания
- Добавлены шаблоны для исключения личных заметок и руководств
- Файлы сохранены локально для использования при необходимости
Эти файлы являются временными инструментами диагностики и личными заметками разработчика, которые не должны находиться под контролем версий.
- Replace large operation cards with smaller, uniform inventory-card components
- Update icon sizes and text styles for better visual hierarchy
- Group operations into a cleaner 4x3 grid layout using Bootstrap columns
- Simplify card hover effects with subtler shadows and background gradients
- Remove unused purple utility classes and old card-body styles
- Add focus outline support for accessibility on inventory cards
fix(customers): show total debt only when applicable and add external links
- Display total debt row only if debt is greater than zero in customer detail
- Remove redundant conditional inside debt display cell
- Add target="_blank" and rel attributes to order detail links to open in new tab
Проблема: при входе в localhost/admin/ (public схема) возникала ошибка
"relation user_roles_userrole does not exist", так как tenant-only
таблицы не существуют в public схеме.
Решение:
- Создан TenantAdminOnlyMixin для скрытия tenant-only моделей от public admin
- Применён миксин ко всем ModelAdmin классам в tenant-only приложениях:
user_roles, customers, orders, inventory, products
- Добавлена проверка _is_public_schema() в RoleBasedPermissionBackend
для предотвращения запросов к tenant-only таблицам в public схеме
Теперь:
- localhost/admin/ показывает только public модели (Client, Domain, User)
- shop.localhost/admin/ показывает все модели магазина
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Создан TenantOnboardingService как единый источник истины для:
- Активации заявки на регистрацию тенанта
- Создания Client, Domain, Subscription
- Инициализации системных данных (Customer, статусы, способы оплаты, склад, витрина)
Новые сервисы:
- TenantOnboardingService (tenants/services/onboarding.py)
- WarehouseService (inventory/services/warehouse_service.py)
- ShowcaseService (inventory/services/showcase_service.py)
- PaymentMethodService (orders/services/payment_method_service.py)
Рефакторинг:
- admin.py: 220 строк → 5 строк (делегирование сервису)
- init_tenant_data.py: 259 строк → 68 строк
- activate_registration.py: использует сервис
- Тесты обновлены для вызова сервиса напрямую
При создании тенанта автоматически создаются склад и витрина по умолчанию.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Добавлены imports для ConfigurableProduct, ConfigurableProductOption, ConfigurableProductAttribute.
Создан ConfigurableProductAdmin с инлайнами для вариантов и атрибутов.
Поля variant_sku отображается в readonly режиме.
Добавлен счетчик вариантов в list_display с цветовой индикацией.
Организованы fieldsets для удобного редактирования.
Добавлено поле variant_sku в модель ConfigurableProductOption.
Артикул варианта генерируется автоматически в формате VAR-XXXXXX-V1, VAR-XXXXXX-V2 и т.д.
Счетчик не переиспользуется при удалении вариантов для защиты интеграций.
Переименован property variant_sku в variant_base_sku для основного SKU.
Обновлен шаблон с колонкой артикула варианта.
Создана миграция для добавления поля и data migration для существующих записей.
Назначение: дополнительный артикул для интеграций с внешними площадками.
- В миксин SKUUniqueMixin добавлен словарь RESERVED_PREFIXES
- Префиксы PROD-, KIT-, CAT-, VAR- зарезервированы для автогенерации
- При попытке ручного ввода артикула с зарезервированным префиксом выдается понятная ошибка
- Проверка регистронезависимая (prod-123 тоже будет заблокирован)
- Пользователю предлагается либо использовать другой артикул, либо оставить поле пустым для автогенерации
- Решение элегантное, централизованное в миксине, работает для всех форм товаров
- Добавлен миксин SKUUniqueMixin для единообразной валидации артикулов
- Валидация проверяет уникальность SKU среди Product, ProductKit, ProductCategory, ConfigurableProduct
- Реализована автогенерация артикулов для ConfigurableProduct (формат VAR-XXXXXX)
- Добавлен новый тип счетчика 'configurable' в SKUCounter
- Обновлены формы Product, ProductKit, ProductCategory, ConfigurableProduct
- Рефакторинг методов clean() в формах: валидация имени вынесена в clean_name()
- Добавлена функция generate_configurable_sku() в sku_generator.py
- Обновлена функция ensure_sku_unique() для проверки ConfigurableProduct
- Добавлен метод save() в модель ConfigurableProduct для автогенерации SKU
- Обновлен шаблон configurableproduct_form.html с отображением help_text для SKU
Код стал чистым, без дублирования логики валидации.
- Переиспользован модуль select2-product-search.js из orders
- Заменен простой select на Select2 с AJAX поиском через API search_products_and_variants
- Добавлена поддержка привязки как ProductKit, так и Product к значениям атрибутов
- Обновлен метод _save_attributes_from_cards для обработки item_ids и item_types
- Удалены дублирующиеся подключения jQuery и Select2 (используются из base.html)
- Улучшен UX: живой поиск, отображение типа товара (🌹/💐), цены и наличия
- URL paths: configurable-kits/ → configurable/
- URL names: configurablekit-list → configurableproduct-list и т.д.
- Обновлены все ссылки в шаблонах и views
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>