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>
This commit is contained in:
@@ -328,7 +328,7 @@ class IncomingModelForm(forms.ModelForm):
|
||||
super().__init__(*args, **kwargs)
|
||||
# Фильтруем только активные товары
|
||||
self.fields['product'].queryset = Product.objects.filter(
|
||||
is_active=True
|
||||
status='active'
|
||||
).order_by('name')
|
||||
|
||||
def clean_quantity(self):
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Generated by Django 5.0.10 on 2025-11-20 12:09
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0004_showcase_is_default_and_more'),
|
||||
('orders', '0003_historicalorderitem_is_from_showcase_and_more'),
|
||||
('products', '0008_productkit_showcase_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='reservation',
|
||||
name='product_kit',
|
||||
field=models.ForeignKey(blank=True, help_text='Временный комплект, для которого создан резерв', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='reservations', to='products.productkit', verbose_name='Комплект'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='reservation',
|
||||
index=models.Index(fields=['product_kit'], name='inventory_r_product_70aed5_idx'),
|
||||
),
|
||||
]
|
||||
@@ -412,6 +412,10 @@ class Reservation(models.Model):
|
||||
related_name='reservations', verbose_name="Витрина",
|
||||
null=True, blank=True,
|
||||
help_text="Витрина, на которой выложен букет")
|
||||
product_kit = models.ForeignKey('products.ProductKit', on_delete=models.CASCADE,
|
||||
related_name='reservations', verbose_name="Комплект",
|
||||
null=True, blank=True,
|
||||
help_text="Временный комплект, для которого создан резерв")
|
||||
product = models.ForeignKey(Product, on_delete=models.CASCADE,
|
||||
related_name='reservations', verbose_name="Товар")
|
||||
warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE,
|
||||
@@ -433,11 +437,14 @@ class Reservation(models.Model):
|
||||
models.Index(fields=['status']),
|
||||
models.Index(fields=['order_item']),
|
||||
models.Index(fields=['showcase']),
|
||||
models.Index(fields=['product_kit']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
if self.order_item:
|
||||
context = f" (заказ {self.order_item.order.order_number})"
|
||||
elif self.product_kit:
|
||||
context = f" (комплект {self.product_kit.name})"
|
||||
elif self.showcase:
|
||||
context = f" (витрина {self.showcase.name})"
|
||||
else:
|
||||
|
||||
@@ -67,6 +67,7 @@ class ShowcaseManager:
|
||||
product=kit_item.product,
|
||||
warehouse=warehouse,
|
||||
showcase=showcase,
|
||||
product_kit=product_kit,
|
||||
quantity=component_quantity,
|
||||
status='reserved'
|
||||
)
|
||||
@@ -84,6 +85,7 @@ class ShowcaseManager:
|
||||
product=first_variant.product,
|
||||
warehouse=warehouse,
|
||||
showcase=showcase,
|
||||
product_kit=product_kit,
|
||||
quantity=component_quantity,
|
||||
status='reserved'
|
||||
)
|
||||
@@ -250,10 +252,8 @@ class ShowcaseManager:
|
||||
)
|
||||
|
||||
if product_kit:
|
||||
# Если указан конкретный комплект, фильтруем резервы
|
||||
# TODO: добавить связь резерва с конкретным экземпляром комплекта
|
||||
# Пока освобождаем все резервы витрины
|
||||
pass
|
||||
# Если указан конкретный комплект, фильтруем только его резервы
|
||||
reservations = reservations.filter(product_kit=product_kit)
|
||||
|
||||
released_count = reservations.count()
|
||||
|
||||
@@ -264,6 +264,11 @@ class ShowcaseManager:
|
||||
'message': f'На витрине "{showcase.name}" нет активных резервов'
|
||||
}
|
||||
|
||||
# Сохраняем список затронутых товаров и склад ДО обновления резервов
|
||||
from inventory.models import Stock
|
||||
affected_products = list(reservations.values_list('product_id', flat=True).distinct())
|
||||
warehouse = showcase.warehouse
|
||||
|
||||
# Освобождаем резервы
|
||||
reservations.update(
|
||||
status='released',
|
||||
@@ -272,13 +277,11 @@ class ShowcaseManager:
|
||||
)
|
||||
|
||||
# Обновляем агрегаты Stock
|
||||
from inventory.models import Stock
|
||||
affected_products = reservations.values_list('product_id', flat=True).distinct()
|
||||
for product_id in affected_products:
|
||||
try:
|
||||
stock = Stock.objects.get(
|
||||
product_id=product_id,
|
||||
warehouse=showcase.warehouse
|
||||
warehouse=warehouse
|
||||
)
|
||||
stock.refresh_from_batches()
|
||||
except Stock.DoesNotExist:
|
||||
|
||||
Reference in New Issue
Block a user