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>
173 lines
6.3 KiB
Python
173 lines
6.3 KiB
Python
#!/usr/bin/env python
|
|
"""
|
|
Workflow test: Create a full configurable product with attributes and variants
|
|
"""
|
|
import os
|
|
import sys
|
|
import django
|
|
|
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
|
|
django.setup()
|
|
|
|
from products.models.kits import (
|
|
ConfigurableKitProduct,
|
|
ConfigurableKitOption,
|
|
ConfigurableKitProductAttribute,
|
|
ConfigurableKitOptionAttribute,
|
|
ProductKit
|
|
)
|
|
from django_tenants.utils import tenant_context
|
|
from tenants.models import Client
|
|
from django.db import transaction
|
|
|
|
try:
|
|
client = Client.objects.get(schema_name='grach')
|
|
print(f"Found tenant: {client.name}\n")
|
|
except Client.DoesNotExist:
|
|
print("Tenant 'grach' not found")
|
|
sys.exit(1)
|
|
|
|
with tenant_context(client):
|
|
print("=" * 70)
|
|
print("WORKFLOW TEST: Complete ConfigurableKitProduct Creation")
|
|
print("=" * 70)
|
|
|
|
# Step 1: Create ConfigurableKitProduct
|
|
print("\n[1] Creating ConfigurableKitProduct...")
|
|
with transaction.atomic():
|
|
try:
|
|
# Delete old test products
|
|
ConfigurableKitProduct.objects.filter(name__icontains="workflow").delete()
|
|
|
|
product = ConfigurableKitProduct.objects.create(
|
|
name="Workflow Test Product",
|
|
sku="WORKFLOW-TEST-001",
|
|
description="Test product for workflow validation"
|
|
)
|
|
print(f" OK: Created product: {product.name} (ID: {product.id})")
|
|
except Exception as e:
|
|
print(f" ERROR: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
sys.exit(1)
|
|
|
|
# Step 2: Create attributes with values
|
|
print("\n[2] Creating product attributes...")
|
|
try:
|
|
# Delete old attributes
|
|
ConfigurableKitProductAttribute.objects.filter(parent=product).delete()
|
|
|
|
attrs_data = [
|
|
("Dlina", ["50", "60", "70"]),
|
|
("Упаковка", ["BEZ", "V_UPAKOVKE"])
|
|
]
|
|
|
|
created_attrs = {}
|
|
for attr_name, values in attrs_data:
|
|
print(f" Creating attribute: {attr_name}")
|
|
created_attrs[attr_name] = []
|
|
|
|
for pos, value in enumerate(values):
|
|
attr = ConfigurableKitProductAttribute.objects.create(
|
|
parent=product,
|
|
name=attr_name,
|
|
option=value,
|
|
position=pos,
|
|
visible=True
|
|
)
|
|
created_attrs[attr_name].append(attr)
|
|
print(f" - Created value: {value}")
|
|
|
|
print(f" OK: Created {len(created_attrs)} attribute(s)")
|
|
except Exception as e:
|
|
print(f" ERROR: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
sys.exit(1)
|
|
|
|
# Step 3: Get or create ProductKits
|
|
print("\n[3] Getting ProductKits for variants...")
|
|
try:
|
|
kits = ProductKit.objects.all()[:3]
|
|
if kits.count() == 0:
|
|
print(" WARNING: No ProductKit found in database")
|
|
print(" INFO: Skipping variant creation (need ProductKits in DB)")
|
|
print("\n To complete testing:")
|
|
print(" 1. Create some ProductKit objects in admin")
|
|
print(" 2. Then run this script again")
|
|
else:
|
|
print(f" OK: Found {kits.count()} ProductKit(s)")
|
|
for kit in kits:
|
|
print(f" - {kit.name} (SKU: {kit.sku})")
|
|
|
|
# Step 4: Create variants with attribute values
|
|
print("\n[4] Creating ConfigurableKitOption variants...")
|
|
try:
|
|
# Delete old options
|
|
ConfigurableKitOption.objects.filter(parent=product).delete()
|
|
|
|
variant_configs = [
|
|
(kits[0], created_attrs["Dlina"][0], created_attrs["Упаковка"][0], True), # 50, BEZ, default
|
|
(kits[1], created_attrs["Dlina"][1], created_attrs["Упаковка"][1], False), # 60, V_UPAKOVKE
|
|
(kits[2], created_attrs["Dlina"][2], created_attrs["Упаковка"][0], False), # 70, BEZ
|
|
]
|
|
|
|
for kit, dlina_attr, upakovka_attr, is_default in variant_configs:
|
|
option = ConfigurableKitOption.objects.create(
|
|
parent=product,
|
|
kit=kit,
|
|
is_default=is_default
|
|
)
|
|
print(f" Created variant {option.id} for kit: {kit.name}")
|
|
|
|
# Create M2M relationships
|
|
ConfigurableKitOptionAttribute.objects.create(
|
|
option=option,
|
|
attribute=dlina_attr
|
|
)
|
|
ConfigurableKitOptionAttribute.objects.create(
|
|
option=option,
|
|
attribute=upakovka_attr
|
|
)
|
|
print(f" - Linked attributes: Dlina={dlina_attr.option}, Upakovka={upakovka_attr.option}")
|
|
|
|
print(f" OK: Created {len(variant_configs)} variant(s)")
|
|
except Exception as e:
|
|
print(f" ERROR: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
sys.exit(1)
|
|
|
|
# Step 5: Verify data retrieval
|
|
print("\n[5] Verifying variant data...")
|
|
try:
|
|
options = ConfigurableKitOption.objects.filter(parent=product)
|
|
print(f" Found {options.count()} variant(s)")
|
|
|
|
for opt in options:
|
|
print(f"\n Variant {opt.id}:")
|
|
print(f" - Kit: {opt.kit.name}")
|
|
print(f" - Default: {opt.is_default}")
|
|
|
|
# Get attributes through M2M
|
|
opt_attrs = opt.attributes_set.all()
|
|
print(f" - Attributes ({opt_attrs.count()}):")
|
|
for opt_attr in opt_attrs:
|
|
print(f" * {opt_attr.attribute.name} = {opt_attr.attribute.option}")
|
|
|
|
print("\n OK: All data retrieves correctly")
|
|
except Exception as e:
|
|
print(f" ERROR: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
sys.exit(1)
|
|
except Exception as e:
|
|
print(f" ERROR: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
sys.exit(1)
|
|
|
|
print("\n" + "=" * 70)
|
|
print("OK: WORKFLOW TEST COMPLETED SUCCESSFULLY!")
|
|
print("=" * 70)
|