- Add total_available, total_reserved, total_free annotations to product queries
- Display free stock (green/red) with reserved count in product list
- Show detailed stock info in product detail page (moved to top)
- Make reservation count clickable to view filtered reservations
- Add product filter support to ReservationListView
- Add product link in reservation list for easy navigation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>
- 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>
Удалены поля loyalty_tier, is_vip, get_loyalty_discount(), increment_total_spent().
Поле total_spent оставлено для будущего расчёта по заказам.
Обновлены admin, forms, views и шаблоны.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
These files are automatically generated by Celery Beat and should not be
version controlled. They contain runtime schedule state and are specific
to each environment.
Files removed from tracking:
- celerybeat-schedule
- celerybeat-schedule-shm
- celerybeat-schedule-wal
Added to .gitignore to prevent future accidental commits.
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>
## 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>
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>
Change from ProductKit.all_objects.all() to super().get_queryset()
since ProductKit model doesn't have an all_objects manager.
Fixes AttributeError: type object 'ProductKit' has no attribute 'all_objects'
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
PROBLEM:
Direct deletion/creation of reservations via web interface bypassed
POS business logic, creating data inconsistencies (orphaned showcase kits,
incorrect stock calculations).
SOLUTION:
Make reservations read-only in web interface. All reservation management
now happens only through:
- POS (showcase kits)
- Orders module
CHANGES:
- Remove reservation-create and reservation-update URL routes
- Delete ReservationCreateView and ReservationUpdateView
- Remove ReservationForm (no longer needed)
- Delete reservation_form.html and reservation_update.html templates
- Update reservation_list.html to read-only view with info banner
- Add showcase and order columns to reservation list
- Clean up imports in urls.py and views/__init__.py
Reservations are now read-only for monitoring purposes only.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add is_default to list_display and list_filter
- Include is_default in fieldsets for editing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add is_default field to Showcase model with unique constraint per warehouse
- Implement Showcase.save() to ensure only one default per warehouse
- Add SetDefaultShowcaseView for AJAX-based default selection
- Update ShowcaseForm to include is_default checkbox
- Add interactive checkbox UI in showcase list with AJAX functionality
- Update POS API to return showcase.is_default instead of warehouse.is_default
- Update terminal.js to auto-select showcase based on its is_default flag
- Add migration for is_default field
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented complete web interface for managing showcases (display areas for ready-made bouquets) with:
**Backend:**
- ShowcaseForm with validation (unique name per warehouse)
- ShowcaseListView with filtering by warehouse and status
- ShowcaseCreateView, ShowcaseUpdateView with success messages
- ShowcaseDeleteView with active reservation validation
- URL routes: list, create, edit, delete
**Frontend:**
- List page with warehouse grouping, active reservations count
- Responsive table with filters (warehouse, status)
- Create/edit form with Bootstrap styling
- Delete confirmation with active reservations check
- Breadcrumb navigation
**Features:**
✅ One warehouse can have multiple showcases (ForeignKey relationship)
✅ Unique showcase names within each warehouse
✅ Display active reservation counts for each showcase
✅ Prevent deletion if showcase has active reservations
✅ Auto-select default warehouse when creating showcase
✅ Navigation link added to main navbar between "Касса" and "Склад"
✅ Active state highlighting in navigation
**Files created:**
- inventory/forms_showcase.py (ShowcaseForm)
- inventory/views/showcase.py (4 CBV views)
- inventory/templates/inventory/showcase/ (list, form, delete templates)
**Files modified:**
- inventory/urls.py (added showcase routes)
- inventory/forms.py (added Showcase import)
- templates/navbar.html (added "Витрины" link)
URL structure:
/inventory/showcases/ - list
/inventory/showcases/create/ - create
/inventory/showcases/<id>/edit/ - edit
/inventory/showcases/<id>/delete/ - delete
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replaced static customer display in the checkout/sale modal with the same interactive button functionality from the cart sidebar.
Changes:
- **HTML**: Replaced static div with interactive button + reset button in checkout modal
- **JavaScript**:
- Updated updateCustomerDisplay() to handle both locations (cart + checkout modal)
- Added event listeners for checkout modal customer buttons
- Both buttons now synchronized and use the same selection modal
Benefits:
✅ Consistent UX across cart and checkout modal
✅ Full code reuse - same selection modal, search, and logic
✅ Both locations stay synchronized automatically
✅ Can search, select, and reset customer directly from checkout modal
Implementation:
- Cart sidebar button: customerSelectBtn, resetCustomerBtn
- Checkout modal button: checkoutCustomerSelectBtn, checkoutResetCustomerBtn
- Single updateCustomerDisplay() updates both locations
- Single selectCustomer() used by all buttons
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented Redis caching with 2-hour TTL for POS session data:
Backend changes:
- Added Redis cache configuration in settings.py
- Created save_cart() endpoint to persist cart state
- Added cart and customer loading from Redis in pos_terminal()
- Validates cart items (products/kits) still exist in DB
- Added REDIS_HOST, REDIS_PORT, REDIS_DB to .env
Frontend changes:
- Added saveCartToRedis() with 500ms debounce
- Cart auto-saves on add/remove/quantity change
- Added cart initialization from Redis on page load
- Enhanced customer button with two-line display and reset button
- Red X button appears only for non-system customers
Features:
- Cart persists across page reloads (2 hour TTL)
- Customer selection persists (2 hour TTL)
- Independent cart per user+warehouse combination
- Automatic cleanup of deleted items
- Debounced saves to reduce server load
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Добавлено поле is_system_customer в модель Customer с индексом
- Системный клиент создается автоматически при создании нового тенанта
- Реализована защита системного клиента от редактирования и удаления:
- Защита на уровне модели (save/delete методы)
- Защита на уровне формы (валидация)
- Защита на уровне представлений (проверки с дружественными сообщениями)
- Защита в админке (readonly поля, запрет удаления)
- Системный клиент скрыт из списков и поиска на фронтенде
- Создан информационный шаблон для отображения системного клиента
- Исправлена обработка NULL значений для полей email/phone (Django best practice)
- Добавлено отображение "Не указано" вместо None в карточке клиента
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Changed from table to card-based design for better space efficiency
- Reduced padding and margins to fit 15+ statuses on screen without scrolling
- Minimized font sizes and icon sizes for compact display
- Added proper styling for edit and delete buttons with hover effects
- Improved visual hierarchy with color indicators and badges
- Maintained all functionality while improving UX
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changes:
1. Removed unused attributesMetadata container from configurablekit_form.html
- Dead code from old formset-based attribute system
- 10 lines of unused HTML and templating removed
2. Enhanced formset validation in BaseConfigurableKitOptionFormSet.clean():
- If product HAS parameters: variants MUST select values for ALL parameters
- If product HAS NO parameters: variants MUST NOT be created
- Error message guides user to add parameters first
Business logic:
- ConfigurableKitProduct variants (options) are ALWAYS bound to attribute values
- You cannot create orphan variants without parameter selections
- Each variant must have a value for every product parameter
User experience:
- Clear error message if trying to add variant without parameters
- Enforces proper product structure: parameters first, then variants
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Updated UI to show ProductKit associations for attribute values:
- configurablekit_detail.html:
* Added 'Комплект' column to attributes table
* Shows linked ProductKit as clickable badge
* Shows '—' for unbound attributes
- configurablekit_list.html:
* Added 'Атрибутов' column showing attribute count
* Updated colspan for empty state message (6->7)
Now users can see which ProductKit each attribute value is bound to in:
1. Detail view: Click product -> view attributes with kit bindings
2. List view: See total attribute count for each product
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Implementation of kit binding feature for ConfigurableKitProduct variants:
- Added ForeignKey field `kit` to ConfigurableKitProductAttribute
* References ProductKit with CASCADE delete
* Optional field (blank=True, null=True)
* Indexed for efficient queries
- Created migration 0007_add_kit_to_attribute
* Handles existing data (NULL values for all current records)
* Properly indexed for performance
- Updated template configurablekit_form.html
* Injected available ProductKits into JavaScript
* Added kit selector dropdown in card interface
* Each value now has associated kit selection
* JavaScript validates kit selection alongside values
- Updated JavaScript in card interface
* serializeAttributeValues() now collects kit IDs
* Creates parallel JSON arrays: values and kits
* Stores in hidden fields: attributes-X-values and attributes-X-kits
- Updated views _save_attributes_from_cards() in both Create and Update
* Reads kit IDs from POST JSON
* Looks up ProductKit objects
* Creates ConfigurableKitProductAttribute with FK populated
* Gracefully handles missing kits
- Fixed _should_delete_form() method
* More robust handling of formset deletion_field
* Works with all formset types
- Updated __str__() method
* Handles NULL kit case
Example workflow:
Dlina: 50 -> Kit A, 60 -> Kit B, 70 -> Kit C
Upakovka: BEZ -> Kit A, V_UPAKOVKE -> (no kit)
Tested with test_kit_binding.py - all tests passing
- Kit creation and retrieval
- Attribute creation with kit FK
- Mixed kit-bound and unbound attributes
- Querying attributes by kit
- Reverse queries (get kit for attribute value)
Added documentation: KIT_BINDING_IMPLEMENTATION.md
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
The parameter name field had readonly='readonly' which prevented users from entering values.
This fix allows users to:
- Enter parameter name directly in the form field
- Modify parameter names during editing
- Type any parameter name they need
The readonly attribute was from a mistaken assumption that values would be pre-filled by JavaScript.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit introduces a new user-friendly interface for managing product attributes:
1. **Form Changes** (products/forms.py):
- Removed 'option' field from ConfigurableKitOptionForm (values now inline)
- Updated ConfigurableKitProductAttributeFormSetCreate to only include name, position, visible
- Updated BaseConfigurableKitProductAttributeFormSet validation for new structure
2. **Template Updates** (products/templates/products/configurablekit_form.html):
- Replaced row-based attribute interface with card-based design
- Each card contains:
- Parameter name field
- Position field
- Visibility toggle
- Inline value inputs with add/remove buttons
- "Add parameter" button creates new cards
- "Add value" button adds inline value inputs
3. **JavaScript Enhancements**:
- addValueField(): Creates new value input with delete button
- initAddValueBtn(): Initializes add value button for each card
- addParameterBtn: Dynamically generates new parameter cards
- serializeAttributeValues(): Converts inline values to JSON for POST submission
- Form submission intercept to serialize data before sending
4. **View Updates** (products/views/configurablekit_views.py):
- Both Create and Update views now have _save_attributes_from_cards() method
- Reads attributes-X-values JSON from POST data
- Creates ConfigurableKitProductAttribute for each parameter+value combination
- Handles parameter deletion and visibility toggling
**Key Features**:
✓ One-time parameter name entry with multiple inline values
✓ Add/remove values without reloading page
✓ Add/remove entire parameters with one click
✓ No database changes required
✓ Better UX: card layout more intuitive than rows
✓ Proper JSON serialization for value transmission
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit introduces a complete refactoring of the variable product system:
1. **New Model**: ConfigurableKitOptionAttribute - M2M relationship between variants and attribute values
- Replaces TextField-based attribute storage with proper database relationships
- Ensures one value per attribute per variant through unique_together constraint
- Includes indexes on both option and attribute fields for query performance
2. **Form Refactoring**:
- Removed static 'attributes' field from ConfigurableKitOptionForm
- Added dynamic field generation in __init__ based on parent attributes
- Creates ModelChoiceField for each attribute (e.g., attribute_Длина, attribute_Упаковка)
- Enhanced BaseConfigurableKitOptionFormSet validation to check all attributes are filled
3. **View Updates**:
- Modified ConfigurableKitProductCreateView.form_valid() to save M2M relationships
- Modified ConfigurableKitProductUpdateView.form_valid() with same logic
- Uses transaction.atomic() for data consistency
4. **Template & JS Enhancements**:
- Reordered form so attributes section appears before variants
- Fixed template syntax: changed from field.name.startswith to "attribute_" in field.name
- Updated JavaScript to dynamically generate attribute select fields when adding variants
- Properly handles formset naming convention (options-{idx}-attribute_{name})
5. **Database Migrations**:
- Created migration 0005 to alter ConfigurableKitOption.attributes to JSONField (for future use)
- Created migration 0006 to add ConfigurableKitOptionAttribute model
6. **Tests**:
- Added test_configurable_simple.py for model/form verification
- Added test_workflow.py for complete end-to-end testing
- All tests passing successfully
Features:
✓ All attributes required for each variant if defined on parent
✓ One value per attribute per variant (unique_together constraint)
✓ One default variant per product (formset validation)
✓ Dynamic form field generation based on parent attributes
✓ Atomic transactions for multi-part operations
✓ Proper error messages per variant number
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Создана модель ConfigurableKitProductAttribute с полями name, option, position, visible
- Добавлены формы и formsets для управления атрибутами родительского товара
- Обновлены CRUD представления для работы с атрибутами (создание/редактирование)
- Добавлен блок атрибутов в шаблоны создания/редактирования
- Обновлена страница детального просмотра с отображением атрибутов товара
- Добавлен JavaScript для динамического добавления форм атрибутов
- Реализована валидация дубликатов атрибутов в formset
- Атрибуты сохраняются в transaction.atomic() вместе с вариантами
Теперь можно определять схему атрибутов для экспорта на WooCommerce без использования JSON или ID, только name и option.
- Аннотация товаров остатками (available_qty) и резервами (reserved_qty) через Subquery
- Компактный формат отображения: X(−Y) где X - доступно, Y - зарезервировано
- Визуальная стилизация: крупное число для остатков, мелкое для резервов
- Цветовая индикация: зелёный (≥5), жёлтый (<5), красный (≤0)
- Без дополнительных SQL-запросов, оптимизировано через подзапросы
- Уменьшен размер шрифта с 0.95rem до 0.85rem для лучшей читаемости
- Уменьшен line-height с 1.2 до 1.1 для компактного расположения текста
- Текст теперь полностью помещается внутри кнопок без выхода за границы
- Сохранена возможность переноса текста на две строки для кнопки 'Отложенный заказ'
- Добавлено исключение для кнопки #scheduleLater
- Для этой кнопки white-space: normal вместо nowrap
- Текст переносится на две строки для лучшей читаемости
- Остальные кнопки остаются в одну строку
- Увеличен размер шрифта кнопок с 0.75rem до 0.95rem
- Добавлен жирный шрифт (font-weight: 700)
- Изменено white-space с normal на nowrap для избежания переносов строк
- Убраны word-wrap: break-word (не нужен при nowrap)
- Кнопки теперь более читабельны на планшете
- Убрана стартовая загрузка витринных комплектов (теперь только по API)
- showcase_kits_json теперь пустой массив на старте
- Витринные букеты загружаются динамически при клике на ВИТРИНА
- Оптимизирована get_showcase_kits_for_pos - устранены N+1 запросы
- Один запрос для всех резервов вместо N запросов на комплект
- Используется prefetch для kit_items (без дополнительных запросов)
- Добавлена группировка резервов в памяти вместо повторных обращений к БД
- Оптимизирована загрузка фото товаров и комплектов
- Используется Prefetch только для первого фото (thumbnail)
- Вместо photos.first() (который тянет все фото) - ограниченный queryset
- Prefetch с to_attr='first_photo_list' для минимизации запросов
- Результат: значительное сокращение нагрузки на БД при открытии POS
- Добавлены API endpoints для получения и обновления витринных комплектов
- GET /pos/api/product-kits/<id>/ - получение деталей комплекта
- POST /pos/api/product-kits/<id>/update/ - обновление комплекта
- Реализовано редактирование комплектов из POS интерфейса
- Кнопка редактирования (карандаш) на карточках витринных букетов
- Модальное окно предзаполняется данными комплекта
- Поддержка изменения состава, цен, описания и фото
- Умное управление резервами при изменении состава
- Введено изолированное состояние tempCart для модального окна
- Основная корзина (cart) больше не затрагивается при редактировании
- tempCart используется для создания и редактирования комплектов
- Автоочистка tempCart при закрытии модального окна
- Устранён побочный эффект загрузки состава комплекта в основную корзину
- Добавлен API endpoint GET /pos/api/showcase-kits/ для получения актуальных витринных букетов
- Изменена переменная SHOWCASE_KITS на изменяемую showcaseKits
- Добавлена функция refreshShowcaseKits() для обновления данных с сервера
- Кнопка ВИТРИНА теперь загружает свежие данные перед отображением
- После создания временного букета автоматически обновляется список и переключается вид на витрину
- Исправлена проблема с отображением только что созданных витринных букетов
- Reduced spacing around 'Итого:' line for more compact design
- Changed from h5 to strong tags for better space utilization
- Removed bottom margin from cart list
- Set symmetric padding (py-1) for total section
- More vertical space now available for cart items
- Added Bootstrap icon (bi-box-seam) for kit items in POS cart
- Kits and showcase kits now display with blue icon for visual distinction
- Regular products remain without icons for cleaner look
- Maintains consistency with product list view styling