Commit Graph

88 Commits

Author SHA1 Message Date
c070e42cab feat(discounts, orders): рефакторинг системы скидок - единый источник правды
- Добавлен combine_mode в форму создания/редактирования скидок
- Добавлена колонка "Объединение" в список скидок с иконками
- Добавлен фильтр по режиму объединения скидок
- Добавлена валидация: только одна exclusive скидка на заказ
- Удалены дублирующие поля из Order и OrderItem:
  - applied_discount, applied_promo_code, discount_amount
- Скидки теперь хранятся только в DiscountApplication
- Добавлены свойства для обратной совместимости

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 13:46:02 +03:00
f57e639dbe feat(discounts): добавлено комбинирование скидок по режимам
Добавлено поле combine_mode с тремя режимами:
- stack - складывать с другими скидками
- max_only - применять только максимальную
- exclusive - отменяет все остальные скидки

Изменения:
- Модель Discount: добавлено поле combine_mode
- Calculator: новый класс DiscountCombiner, методы возвращают списки скидок
- Applier: создание нескольких DiscountApplication записей
- Admin: отображение combine_mode с иконками
- POS API: возвращает списки применённых скидок
- POS UI: отображение нескольких скидок с иконками режимов

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 12:56:38 +03:00
42d8c34e8c feat(pos): добавлен полноценный интерфейс скидок в модальное окно продажи
- Добавлен API endpoint /pos/api/discounts/available/ для получения списка доступных скидок
- Добавлен метод DiscountApplier.apply_manual_discount() для применения ручных скидок
- Обновлен POS checkout для обработки manual_discount_id
- Расширена секция скидок в модальном окне:
  * Отображение автоматических скидок (read-only)
  * Dropdown для выбора скидки вручную
  * Подробная детализация: подитог, общая скидка, скидки на позиции
  * Поле промокода с иконкой
- Увеличен размер модального окна и изменено соотношение колонок (5/7)
- Убрана вертикальная прокрутка из модального окна

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 01:59:43 +03:00
6313b8f6e7 fix(pos): исправлена проблема с CSRF токеном при API запросах
- Заменен getCookie('csrftoken') на getCsrfToken() во всех fetch запросах
  (checkAutoDiscounts, applyPromoCode, handleCheckoutSubmit и др.)
- Это исправляет ошибку 403 Forbidden, возникающую из-за CSRF_USE_SESSIONS=True

fix(discounts): исправлен фильтр товаров в CRUD скидок

- Изменен фильтр с is_active=True на status='active' для корректной
  работы с моделью Product

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 01:41:17 +03:00
6978f4e59f feat(pos): интеграция системы скидок в POS терминал
API endpoints:
- POST /api/discounts/validate-promo/: валидация промокода
- POST /api/discounts/calculate/: расчёт скидок для корзины

Обновлён pos_checkout:
- добавлен параметр promo_code в payload
- автоматическое применение скидок к заказу

UI (terminal.html):
- секция скидок в модальном окне оплаты
- поле ввода промокода
- отображение автоматических скидок
- кнопки применения/удаления промокода

JavaScript (terminal.js):
- переменные состояния скидок
- функции applyPromoCode, removePromoCode
- checkAutoDiscounts: проверка автоматических скидок
- updateCheckoutTotalWithDiscounts: пересчёт итога
- обработчики кнопок промокода

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 00:30:55 +03:00
4ea01b8269 fix(inventory, orders, pos): удалена зависимость от django-simple-history для tenant-моделей
- Добавлен pre_save сигнал для Order вместо django-simple-history
- Переписаны все функции signals.py без использования instance.history
- Заменены .username на .name|default:.email для CustomUser в шаблонах
- Исправлен CSRF-токен в POS для работы с CSRF_USE_SESSIONS=True

Теперь создание заказов работает корректно в мультитенантной архитектуре.
2026-01-10 17:21:00 +03:00
d44ae0b598 Добавлен расчёт и отображение доступного количества комплектов
- Добавлен метод calculate_available_quantity() в модель ProductKit для точного расчёта максимального количества комплектов на основе свободных остатков компонентов
- Обновлён метод check_availability() для использования нового расчёта (обратная совместимость)
- Удалён устаревший сервис kit_availability.py

Исправлено отображение остатков комплектов:
- products_list.html: вместо прочерка показывается количество комплектов
- catalog.html: добавлено отображение доступного количества комплектов с цветовой индикацией
- POS terminal.js: в карточке товара показывается конкретное количество вместо общего 'В наличии'

Обновлены представления:
- ProductsListView: аннотирует комплекты атрибутом total_free
- CatalogView: рассчитывает доступное количество для каждого комплекта
- POS get_products(): убран хардкод, используется реальный расчёт по складу
2026-01-06 01:02:28 +03:00
a1f5557036 Исключены зарезервированные букеты из отображения в POS
- inventory/views/showcase.py: фильтр .exclude(status='reserved')
  * Витринные букеты со статусом 'reserved' не отображаются в POS
  * Защита от конфликтов: один букет - один заказ
- pos/views.py: фильтр .exclude(showcase_items__status='reserved')
  * Showcase комплекты без доступных букетов скрыты в POS
  * Фильтрация на уровне queryset для производительности
- Консистентная видимость витрины для всех кассиров
2026-01-05 01:39:14 +03:00
b1e728f91b Обновлён frontend для отложенных заказов с резервированием витринных букетов
- pos/static/pos/js/terminal.js: переработана функция createDeferredOrder()
- Новый flow:
  1. Вызывает POST /orders/api/create-from-pos/ для создания Order (draft)
  2. Получает order_number в ответе
  3. ShowcaseItem резервируются на backend (in_cart → reserved)
  4. Очищает корзину POS (cart.clear + saveCartToRedis)
  5. Перезагружает витрину (refreshShowcaseKits) для синхронизации UI
  6. Открывает /orders/<order_number>/edit/ в новой вкладке
- Устранена race condition: резервирование ДО очистки корзины
- Витринные букеты корректно исчезают из POS после резервирования
2026-01-05 01:36:40 +03:00
b7db4cd162 conventional-commit
feat(inventory): introduce stock deficit notifications and base quantity tracking

- Added `quantity_base` field to reservation model for precise inventory calculations
- Implemented non-blocking stock deficit warnings during kit creation process
- Enhanced API responses with warning details for frontend display
- Updated terminal interface to show formatted stock shortage alerts

BREAKING CHANGE: API response structure now includes `warnings` array instead of previous stock warning format
2026-01-04 16:18:57 +03:00
2995710a3e Исправлена передача единиц продажи при создании отложенного заказа из POS
- В terminal.js добавлена передача sales_unit_id в данные черновика заказа

- В order_form.html добавлено заполнение поля sales_unit при предзаполнении из черновика

- Теперь при создании отложенного заказа с товаром в единицах продажи сохраняется корректная единица измерения
2026-01-02 18:33:51 +03:00
f0327b264c Добавлена поддержка единиц продажи в POS checkout
- При создании OrderItem теперь передаётся sales_unit из данных корзины

- Это позволяет корректно рассчитывать total_amount для товаров с единицами продажи

- Исправлена ошибка когда сумма заказа была 0 при использовании единиц продажи
2026-01-02 17:51:01 +03:00
275bc1b78d Исправлена ошибка создания заказов в 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'
2026-01-02 17:46:32 +03:00
1ead77b2d8 Добавлено округление количества для корректного отображения в POS
Проблема:

- JavaScript float arithmetic даёт погрешность при вычислениях

- На карточке товара показывалось -0.050000000000044

- Происходило при: available - reserved - inCart

Решение:

- Добавлена функция roundQuantity(value, decimals=3)

- Округляет результат вычислений до 3 знаков после запятой

- Применяется ТОЛЬКО для отображения, не для расчётов

- Используется для: free, reserved, inCart в карточках товаров

Результат:

- Отображение: -0.05 вместо -0.050000000000044

- Данные с бэка остаются точными (строка)

- Погрешность устранена только визуально

Примечание:

- Округление в JS НЕИЗБЕЖНО для отображения

- Это НЕ маскировка - это правильное форматирование

- Бэкенд уже отдаёт точные данные как строки
2026-01-02 15:38:41 +03:00
4d121e95af Исправлено: передача free_qty как строки для сохранения точности
Проблема:

- 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

- Нет округления на фронте - видны реальные данные

- Сортировка по остаткам работает корректно
2026-01-02 15:30:00 +03:00
bb821f9ef4 Исправление отображения фото в POS и улучшение обработки изображений
- Исправлен POS для использования миниатюр вместо оригиналов для быстрой загрузки
- Убран fallback на оригиналы - показываем миниатюру или ничего (лучше видно ошибки)
- Исправлен ImageService - возвращает пустую строку если миниатюра обработанного файла не найдена
- Исправлена ошибка JavaScript при массовом удалении фото (insertAdjacentElement на null)
- Добавлен контейнер photos-messages-container для надежного отображения сообщений
- Улучшено логирование ImageService для отладки путей к файлам
- Добавлена проверка exists() с детальным логированием в TenantAwareFileSystemStorage
2025-12-21 19:52:55 +03:00
34e5a0143b Исправлено время заказов: переход на минский часовой пояс (Europe/Minsk)
- Изменён TIME_ZONE с Europe/Moscow на Europe/Minsk в settings.py
- Исправлено создание заказов в POS: теперь используется timezone.localtime() для корректной конвертации UTC → Minsk
- delivery_date и delivery_time_start/end теперь сохраняются в минском времени, а не UTC
- Исправлена разница в 3 часа между временем создания заказа и фактическим временем
- Удалено устаревшее поле payment_method из showcase_manager.py (поле было удалено из модели Order)

Теперь все временные метки заказов соответствуют реальному минскому времени
2025-12-14 15:27:03 +03:00
ce2cfca3f2 Добавлена возможность добавлять новые товары в витринный комплект при редактировании
- Интегрирован готовый компонент ProductSearchPicker в модалку редактирования
- Добавлен collapse-блок с поиском товаров, который отображается только в режиме редактирования
- При выборе товара он автоматически добавляется в tempCart или увеличивается количество если уже есть
- Добавлен CSS и JS для компонента product-search-picker
- В view передаётся categories QuerySet для работы фильтров компонента
- Блок добавления товаров показывается только при редактировании, скрыт при создании нового комплекта
2025-12-14 14:00:51 +03:00
aff25d0317 Реализована возможность редактирования состава витринного комплекта с поддержкой отрицательных резервов
- Добавлены методы reserve_product_to_showcase и release_showcase_reservation в ShowcaseManager
- Методы работают с резервами для всех активных экземпляров витринного комплекта
- НЕ блокируют создание резерва при нехватке товара, возвращают информацию о дефиците (overdraft)
- Обновлён API endpoint update_product_kit для корректировки резервов при изменении состава
- Добавлено визуальное предупреждение на фронте о нехватке товара на складе
- В модалке редактирования комплекта добавлены контролы для изменения количества товаров (+/-, поле ввода, удаление)
- Автоматический пересчёт цен при изменении состава
- Очистка корзины POS после успешного создания витринного комплекта
2025-12-14 13:49:13 +03:00
4ce610985b Исправлен порядок операций при конвертации резервов в продажи
Проблема:
При продаже витринного комплекта резервы оставались в статусе 'reserved'
вместо 'converted_to_sale'. Товары из состава комплекта не освобождались.

Причина:
В методе sell_showcase_items порядок операций был неправильный:
1. create_sale_from_reservation вызывался ПЕРВЫМ
2. reservation.order_item устанавливался ПОСЛЕ

В SaleProcessor.create_sale_from_reservation есть логика:
  if order and reservation.order_item:
      sale_price = reservation.order_item.price
  else:
      sale_price = reservation.product.actual_price

Так как order_item был None, цена бралась из product.actual_price,
а не из OrderItem, и резерв не конвертировался корректно.

Решение:
Правильный порядок операций:
1. Устанавливаем reservation.order_item = order_item
2. Сохраняем reservation
3. Вызываем create_sale_from_reservation (теперь order_item доступен)
4. Обновляем статус на 'converted_to_sale'
5. Сохраняем финальное состояние

Теперь резервы корректно преобразуются в продажи с правильной ценой
из позиции заказа, и товары освобождаются после продажи.
2025-12-11 22:55:06 +03:00
95cb1c4bac Рефакторинг: убрано дублирование валидации витринных комплектов
Проблема:
Валидация showcase_items выполнялась в двух местах:
1. В pos/views.py - проверка status='in_cart' БЕЗ блокировки БД
2. В showcase_manager.py - перезагрузка с select_for_update() и повторная проверка

Это создавало:
- Дублирование кода и логики
- Возможность race condition между двумя запросами
- Избыточные обращения к БД
- Мертвый код (неработающие logger.info)

Решение (best practices):
1. Views только загружают объекты по ID без фильтров по статусу
2. ВСЯ валидация и бизнес-логика в одном месте - ShowcaseManager.sell_showcase_items
3. select_for_update() гарантирует актуальность данных и блокировку на уровне БД
4. Удален мертвый код (logger.info которые не выполнялись)
5. Убрано избыточное логирование ошибок валидации

Результат:
- Единое место ответственности (Single Responsibility)
- Нет дублирования
- Атомарная транзакция с блокировкой
- Чистый, понятный код без костылей
2025-12-11 22:27:15 +03:00
8dc6594334 Добавлено подробное логирование ошибок валидации в POS checkout
Для диагностики проблемы с продажей витринных комплектов добавлено
логирование ValidationError с полным traceback. Это поможет определить
в какой именно момент и почему происходит ошибка валидации.
2025-12-11 22:21:11 +03:00
37c203a783 Добавлена валидация и логирование для продажи витринных комплектов в POS
Проблема: Продолжает возникать ошибка 'Один из экземпляров уже был продан'
при попытке продажи витринных букетов.

Изменения для диагностики:
1. Добавлена валидация showcase_item_ids ПЕРЕД передачей в sell_showcase_items
2. Проверка что все экземпляры имеют status='in_cart' и locked_by_user=текущий
3. Фильтр по статусу исключает уже проданные/разобранные экземпляры
4. Добавлено детальное логирование:
   - Запрошенные showcase_item_ids
   - Количество найденных заблокированных экземпляров
   - Недостающие IDs если не все найдены

Улучшенное сообщение об ошибке:
Вместо 'уже был продан' теперь 'уже не заблокированы на вас' с просьбой
обновить страницу - более понятно для пользователя.

Логи помогут выявить:
- Передаются ли дубликаты в showcase_item_ids
- Истекают ли блокировки до момента продажи
- Меняется ли статус экземпляров между добавлением в корзину и checkout
2025-12-11 22:18:16 +03:00
741fdc97a8 Добавлены кнопки +/- для управления количеством витринных комплектов в корзине
Теперь витринные букеты можно увеличивать и уменьшать по экземплярам:

UI изменения:
- Заменен badge на полноценные кнопки +/- как у обычных товаров
- Поле количества readonly с желтым фоном для визуального отличия
- Кнопки используют тот же дизайн что и для обычных товаров

Функционал увеличения (increaseShowcaseKitQty):
- Блокирует еще один доступный экземпляр через API
- Проверяет наличие свободных букетов на витрине
- Показывает сообщение если нет доступных
- Обновляет showcase_item_ids и qty в корзине

Функционал уменьшения (decreaseShowcaseKitQty):
- Снимает блокировку с последнего экземпляра из списка
- При qty=1 полностью удаляет из корзины
- Обновляет список витрины после изменения

Все операции синхронизируются с сервером и Redis.
2025-12-11 22:10:06 +03:00
b396029554 Исправлено восстановление витринных комплектов из корзины при перезагрузке страницы
Проблема: Витринные букеты исчезали из корзины после перезагрузки страницы,
но оставались заблокированными на пользователя через ShowcaseItem.

Решение:
- Изменена валидация корзины при загрузке из Redis
- Теперь проверяется наличие showcase_item_ids в данных корзины
- Блокировки валидируются через ShowcaseItem (не Reservation)
- Проверяется: status='in_cart', locked_by_user, cart_lock_expires_at > now()
- Обновляется qty на актуальное количество действующих блокировок
- Если ни один ShowcaseItem не заблокирован - не восстанавливается в корзину

Теперь витринные букеты корректно восстанавливаются при перезагрузке
и автоматически удаляются из корзины при истечении блокировки.
2025-12-11 22:05:15 +03:00
65ffed2f9b Блокировка кнопки 'НА ВИТРИНУ' при наличии витринного комплекта в корзине
- Добавлена проверка наличия витринных комплектов (showcase_kit) в корзине
- Кнопка 'НА ВИТРИНУ' блокируется при наличии витринного букета
- Добавлено визуальное оформление: opacity 0.5, disabled state, tooltip
- Показывается предупреждение при попытке создать новый букет
- Функция updateShowcaseButtonState() вызывается при каждом изменении корзины
2025-12-11 22:01:32 +03:00
c76163640e Рефакторинг POS терминала: устранение дублирования кода и оптимизация UI
- Удалены дублирующиеся функции getCookie() и getCsrfToken() в terminal.js
- Оставлена единая версия getCookie() с алиасом getCsrfToken для совместимости
- Удалены неиспользуемые пустые кнопки из панели действий
- Добавлена логика скрытия поля 'Количество букетов' в режиме редактирования комплекта
- Оптимизирована компоновка кнопок действий (используется offset-4)
- Улучшены комментарии в коде

Результат: -44 строки, код стал чище и поддерживаемее
2025-12-10 00:35:56 +03:00
5c94a5ab95 POS: улучшения работы с витринными букетами
- Упрощено добавление в корзину: 1 клик = 1 шт (без prompt)
- API показывает все букеты (available + in_cart), не только доступные
- Карточка показывает available/total и сколько в корзине
- Корзина показывает реальное количество витринных букетов
- Кнопка "Очистить" сбрасывает блокировки и обновляет отображение
- API release-all-my-locks для сброса зависших блокировок
- Автоочистка истёкших блокировок при загрузке витрины

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 00:23:00 +03:00
cfc6ce451e ShowcaseItem: защита от двойной продажи витринных букетов
Новая архитектура:
- ShowcaseItem модель - физический экземпляр букета на витрине
- OneToOneField(sold_order_item) - БД-уровневая защита от двойной продажи
- Поддержка создания нескольких экземпляров одного букета
- Возможность продавать N из M доступных (например 2 из 5)

Изменения:
- inventory/models.py: добавлена модель ShowcaseItem с методами lock/unlock/mark_sold
- inventory/services/showcase_manager.py: переработан для работы с ShowcaseItem
- pos/views.py: API поддерживает quantity и showcase_item_ids
- pos/templates/pos/terminal.html: поле "Сколько букетов создать"
- pos/static/pos/js/terminal.js: выбор количества, передача showcase_item_ids

Миграции:
- 0007: создание модели ShowcaseItem
- 0008: data migration существующих букетов
- 0009: очистка ShowcaseItem для уже проданных букетов

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 23:51:37 +03:00
40b180171a Исправлена кнопка ОТЛОЖЕННЫЙ заказ - удален старый обработчик 2025-12-08 19:00:54 +03:00
6c19c9e093 POS deferred order feature 2025-12-08 18:56:14 +03:00
2f7fed4a1a Fix: FIFO учитывает резервы, автоматическая дата/время в POS, исправлен фильтр Product в transfers
- Исправлен метод write_off_by_fifo() для учета зарезервированных партий
- Добавлено автоматическое проставление даты и времени при создании заказов в POS
- Исправлена ошибка фильтрации Product (is_active -> status='active') в transfers
- Предотвращает списание из зарезервированных партий, устраняя отрицательные остатки

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 23:40:03 +03:00
18a6c5fa05 FIX: Обновление баланса в виджете оплаты при смене клиента
Проблема:
- При сбросе клиента на системного в модальном окне продажи баланс кошелька
  в виджете оплаты (возле кнопки "С баланса счёта") не обновлялся
- Виджет PaymentWidget сохранял данные предыдущего клиента

Исправления:
- Добавлена функция updatePaymentWidgetCustomer() для переинициализации виджета
- Функция updateCheckoutWalletBalance() теперь вызывает updatePaymentWidgetCustomer()
- При смене клиента виджет оплаты автоматически переинициализируется с новыми данными

Результат:
При смене клиента баланс кошелька обновляется везде, включая виджет оплаты

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 13:35:40 +03:00
4817bc388b FIX: Обновление баланса кошелька при смене клиента в модальном окне
Проблема:
- При сбросе клиента на системного в модальном окне продажи баланс кошелька
  оставался от предыдущего клиента и не обновлялся

Исправления:
- Добавлена функция updateCheckoutWalletBalance() для обновления баланса
- Функция updateCustomerDisplay() теперь вызывает updateCheckoutWalletBalance()
- Исправлены все кнопки сброса/выбора системного клиента - теперь передают wallet_balance

Результат:
При смене клиента баланс кошелька в модальном окне обновляется корректно

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 13:14:47 +03:00
16234b0a1f FIX: Добавлен баланс кошелька клиента в модальное окно продажи POS
Проблема:
- Баланс кошелька клиента не отображался в модальном окне при нажатии "ПРОДАТЬ"
- Данные о балансе не передавались из backend в frontend

Исправления:

1. pos/views.py:
   - Добавлен wallet_balance в selected_customer при загрузке из Redis
   - Добавлен wallet_balance в system_customer
   - Добавлен wallet_balance в API set_customer (Redis + response)
   - Используется json.dumps() для корректной сериализации данных клиента

2. customers/views.py:
   - Добавлен wallet_balance в API поиска клиентов (api_search_customers)
   - Добавлен wallet_balance в API создания клиента (api_create_customer)

3. pos/static/pos/js/terminal.js:
   - Обновлена функция selectCustomer() для получения walletBalance
   - Обновлены все вызовы selectCustomer() для передачи баланса
   - selectedCustomer теперь содержит wallet_balance

4. pos/templates/pos/terminal.html:
   - Используются готовые JSON-строки из backend (system_customer_json, selected_customer_json)
   - Исправлена проблема с локализацией чисел в JSON

Результат:
Баланс кошелька клиента теперь корректно отображается в модальном окне продажи

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 13:11:50 +03:00
12282a8ce4 FIX: Исправлена автоподстановка суммы при смешанной оплате + убраны версии JS
1. Убран параметр версии ?v=4 из подключения terminal.js (для разработки не нужен)

2. Исправлена логика автоподстановки суммы в PaymentWidget:
   - Теперь при выборе способа оплаты подставляется ОСТАТОЧНАЯ сумма
   - Остаток = amount_due - уже добавленные платежи
   - Добавлена подсказка "Осталось оплатить: X руб."

Пример:
- Заказ на 30 руб.
- Добавили платеж 10 руб. наличными
- Выбираем картой → автоматически подставится 20 руб. (а не 30!)

Это предотвращает ошибки и переплаты при смешанной оплате.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 21:31:45 +03:00
0ed60954c4 UX: Удалена лишняя кнопка "Оплатить" из виджета оплаты
Убрана кнопка "Оплатить" внутри PaymentWidget, чтобы избежать путаницы.
Теперь есть только одна кнопка "Подтвердить продажу" внизу модального окна.

При смешанной оплате:
- Кнопка "Добавить платеж" для добавления способов оплаты
- Кнопка "Подтвердить продажу" для завершения (внизу модалки)

Это удобнее и безопаснее - пользователи не будут случайно нажимать
не ту кнопку при добавлении нескольких платежей.

Версия JS: v4

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 20:42:48 +03:00
f290ae4102 FIX: Убрана ошибка updateCartCount + добавлена перезагрузка страницы после продажи
Исправления:
1. Удален вызов несуществующей функции updateCartCount()
2. Добавлена автоматическая перезагрузка страницы через 500ms после успешной продажи
3. Добавлены console.log для отладки процесса продажи

Теперь после успешной продажи:
-  Заказ создается
-  Корзина очищается
-  Модалка закрывается
-  Страница автоматически перезагружается
-  Остатки товаров обновляются
-  Никаких ошибок в консоли

Версия JS: v3

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 20:37:56 +03:00
aa7085d6e1 FIX: Добавлена версия JS для очистки кэша + автообновление товаров после продажи
1. Добавлен параметр ?v=2 к terminal.js для принудительной загрузки новой версии
2. Добавлен вызов loadItems() после успешной продажи для обновления списка товаров

Теперь после продажи:
- Корзина очищается
- Список товаров слева автоматически обновляется (показывает актуальные остатки)
- Витринные комплекты перезагружаются
- Не требуется ручное обновление страницы

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 20:17:42 +03:00
4160c015f8 FIX: Исправлена ошибка updateCartUI is not defined после оплаты
Заменен вызов несуществующей функции updateCartUI() на корректный renderCart().
Теперь после успешной оплаты корзина корректно очищается и обновляется в UI.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 20:11:42 +03:00
ad7808cd06 Fix: добавлен импорт get_object_or_404 в pos/views.py
Исправлена ошибка 'get_object_or_404 is not defined' при попытке
провести оплату через POS-терминал.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 20:06:59 +03:00
fe6e4d6682 Fix: добавлена передача данных о текущем складе в JavaScript
Исправлена ошибка 'currentWarehouse is not defined' при проведении продажи.
Добавлен JSON блок currentWarehouseData в template и инициализация
переменной currentWarehouse в terminal.js.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 20:05:11 +03:00
1cda9086d0 Реализована полноценная система оплаты для POS-терминала
Добавлена интеграция оплаты в POS с поддержкой одиночной и смешанной оплаты,
работой с кошельком клиента и автоматическим созданием заказов.

Backend изменения:
- TransactionService: добавлены методы get_available_payment_methods() и create_multiple_payments()
  для фильтрации способов оплаты и атомарного создания нескольких платежей
- POS API: новый endpoint pos_checkout() для создания заказов со статусом "Выполнен"
  с обработкой платежей, освобождением блокировок и очисткой корзины
- Template tags: payment_tags.py для получения способов оплаты в шаблонах

Frontend изменения:
- PaymentWidget: переиспользуемый ES6 класс с поддержкой single/mixed режимов,
  автоматической валидацией и интеграцией с кошельком клиента
- terminal.html: компактное модальное окно (70vw) с оптимизированной компоновкой,
  удален функционал скидок, добавлен показ баланса кошелька
- terminal.js: динамическая загрузка PaymentWidget, интеграция с backend API,
  обработка успешной оплаты и ошибок

Поддерживаемые способы оплаты: наличные, карта, онлайн, баланс счёта.
Смешанная оплата позволяет комбинировать несколько способов в одной транзакции.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 15:38:35 +03:00
87cba63c47 Fix: Add _open() and path() methods to TenantAwareFileSystemStorage
Critical fix for Celery photo processing. The storage class now correctly
handles file reading operations by automatically adding tenant_id prefix
when opening files.

Problems fixed:
- Celery tasks could not open image files from storage
- PIL/Pillow couldn't locate files in tenant-specific directories
- temp file deletion was failing due to path validation

Changes:
- Added _open() method to add tenant_id prefix when opening files
- Added path() method to convert relative paths to full filesystem paths
- Updated delete() method to handle paths with or without tenant prefix
- All methods include security checks to prevent cross-tenant access

Testing:
- All 5 existing tests pass
- Verified photo processing task works end-to-end:
  * Reads temp image file from disk
  * Processes and creates all image versions
  * Saves processed files to tenant-specific directory
  * Cleans up temporary files correctly
- Files correctly stored in: media/tenants/{tenant_id}/products/{product_id}/{photo_id}/

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 20:30:52 +03:00
856e1ca4c1 Fix select_for_update with nullable FK outer join error
Remove select_related('locked_by_user') from select_for_update query
to avoid PostgreSQL error "FOR UPDATE cannot be applied to the
nullable side of an outer join". Username is now fetched via lazy load.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-22 23:33:52 +03:00
38ee2f5df7 Fix race condition and expired lock validation for showcase kit cart
- Add select_for_update() with transaction.atomic() to prevent race condition
  when multiple users try to add the same showcase kit simultaneously
- Validate cart lock expiration when loading cart from Redis - showcase kits
  with expired locks are now filtered out instead of being displayed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-22 19:00:48 +03:00
c572cdcc26 Filter POS showcase kits by showcase__isnull=False
Fix issue where temporary kits created for orders (with order field set
but showcase=NULL) were appearing in POS terminal showcase list.

Problem:
- When creating temporary kit from order edit page, it has order=<Order>
  and showcase=NULL
- get_showcase_kits_for_pos() was showing ALL temporary kits with
  reserved components, regardless of showcase field
- This caused order kits to appear in POS showcase view
- Users couldn't add them to cart (showcase=NULL check failed)

Solution:
- Add showcase__isnull=False filter to ProductKit query
- This ensures only kits explicitly assigned to showcases are shown
- Order kits (showcase=NULL) are now excluded from POS showcase list

Clear separation:
- Temporary kits for orders: order != NULL, showcase = NULL
- Temporary kits for showcases: showcase != NULL

File: pos/views.py:85

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 00:32:49 +03:00
08a5527ba7 Fix cart lock validation and error handling improvements
## 1. Add cart lock validation to sell_from_showcase()
- Prevent selling showcase kits locked in another cashier's cart
- Check cart_lock_expires_at before allowing direct sales
- Return clear error message with lock holder's name and time remaining
- File: inventory/services/showcase_manager.py

## 2. Improve error handling in POS create_temp_kit_to_showcase()
- Add detailed logging for all error types (JSON, validation, generic)
- Provide user-friendly error messages instead of generic 500
- Log full context (kit name, showcase ID, items, user) for debugging
- Categorize errors: stock issues, integrity, locks, not found
- File: pos/views.py

## 3. Fix critical bug in create_temporary_kit()
- Replace non-existent is_active field with status='active'
- Affects 3 locations: kit creation, product lookup, kit duplication
- This was causing 500 errors when creating temporary kits from order edit
- File: products/services/kit_service.py

## 4. Improve error handling in create_temporary_kit_api()
- Add comprehensive logging for order creation endpoint
- Provide specific error messages for common failure scenarios
- Help diagnose issues when creating kits from order editing UI
- File: products/views/api_views.py

These changes complete the Soft Lock system and fix the 500 error issue.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 00:24:59 +03:00
33e33ecbac Добавлена система Soft Lock для витринных комплектов в POS-терминале
Реализована элегантная блокировка витринных букетов при добавлении в корзину,
предотвращающая многократную продажу одного физического комплекта.

## Изменения в БД:
- Добавлены поля в Reservation: cart_lock_expires_at, locked_by_user, cart_session_id
- Созданы индексы для оптимизации запросов блокировок
- Миграция 0006: добавление полей Soft Lock

## Backend (pos/views.py):
- add_showcase_kit_to_cart: создание блокировки на 30 минут с проверкой конфликтов
- remove_showcase_kit_from_cart: снятие блокировки при удалении из корзины
- get_showcase_kits_api: возврат статусов блокировок (is_locked, locked_by_me)

## Frontend (terminal.js):
- addToCart: AJAX запрос для создания блокировки, запрет qty > 1
- removeFromCart: автоматическое снятие блокировки
- renderCart: желтый фон, badge "1 шт (витрина)", скрыты кнопки +/−
- UI индикация: зеленый badge "В корзине" (свой), красный "Занят" (чужой)

## Автоматизация (inventory/tasks.py):
- cleanup_expired_cart_locks: Celery periodic task (каждые 5 минут)
- Автоматическое освобождение истекших блокировок (30 минут timeout)
- Логирование очистки для мониторинга

## Маршруты (pos/urls.py):
- POST /api/showcase-kits/<id>/add-to-cart/ - создание блокировки
- POST /api/showcase-kits/<id>/remove-from-cart/ - снятие блокировки

## Документация:
- ЗАПУСК.md: инструкция по запуску Celery Beat

Преимущества:
✓ Предотвращает конфликты между кассирами
✓ Автоматическое освобождение при таймауте
✓ Понятный UX с визуальной индикацией
✓ Совместимость с существующей логикой резервирования

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 23:45:34 +03:00
ff0756498c Fix Product filtering and add kit disassembly functionality
Fixed:
- Replace is_active with status='active' for Product filtering in IncomingModelForm
- Product model uses status field instead of is_active

Added:
- Showcase field to ProductKit for tracking showcase placement
- product_kit field to Reservation for tracking kit-specific reservations
- Disassemble button in POS terminal for showcase kits
- API endpoint for kit disassembly (release reservations, mark discontinued)
- Improved reservation filtering when dismantling specific kits

Changes:
- ShowcaseManager now links reservations to specific kit instances
- POS terminal modal shows disassemble button in edit mode
- Kit disassembly properly updates stock aggregates

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 23:03:47 +03:00