refactor: product independence - remove inheritance pattern
Change Product/ProductTranslation from "override/inheritance" pattern (NULL = inherit from marketplace) to "independent copy" pattern (all fields populated at creation). Key changes: - Remove OVERRIDABLE_FIELDS, effective_* properties, reset_* methods - Rename get_override_info() → get_source_comparison_info() - Update copy_to_vendor_catalog() to copy ALL fields + translations - Replace effective_* with direct field access in services - Remove *_overridden fields from schema, keep *_source for comparison - Add migration to populate NULL fields from marketplace products The marketplace_product_id FK is kept for "view original source" feature. Rollback tag: v1.0.0-pre-product-independence 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -130,103 +130,76 @@ class TestProductModel:
|
||||
assert product.marketplace_product is not None
|
||||
assert product.inventory_entries == [] # No inventory yet
|
||||
|
||||
def test_product_effective_properties(
|
||||
def test_product_get_source_comparison_info(
|
||||
self, db, test_vendor, test_marketplace_product
|
||||
):
|
||||
"""Test Product effective properties with override pattern."""
|
||||
# First, set some values on the marketplace product
|
||||
test_marketplace_product.price_numeric = 100.00
|
||||
test_marketplace_product.brand = "SourceBrand"
|
||||
db.commit()
|
||||
db.refresh(test_marketplace_product)
|
||||
"""Test get_source_comparison_info method for 'view original source' feature.
|
||||
|
||||
# Create product without overrides
|
||||
product = Product(
|
||||
vendor_id=test_vendor.id,
|
||||
marketplace_product_id=test_marketplace_product.id,
|
||||
)
|
||||
db.add(product)
|
||||
db.commit()
|
||||
db.refresh(product)
|
||||
|
||||
# Should inherit from marketplace product
|
||||
assert product.effective_price == 100.00
|
||||
assert product.effective_brand == "SourceBrand"
|
||||
|
||||
# Now override the price
|
||||
product.price = 89.99
|
||||
db.commit()
|
||||
db.refresh(product)
|
||||
|
||||
# Should use the override
|
||||
assert product.effective_price == 89.99
|
||||
# Brand still inherited
|
||||
assert product.effective_brand == "SourceBrand"
|
||||
|
||||
def test_product_reset_to_source(self, db, test_vendor, test_marketplace_product):
|
||||
"""Test reset_to_source methods."""
|
||||
# Set up marketplace product values (use cents internally)
|
||||
Products are independent entities with all fields populated at creation.
|
||||
Source values are kept for comparison only, not inheritance.
|
||||
"""
|
||||
# Set up marketplace product values
|
||||
test_marketplace_product.price_cents = 10000 # €100.00
|
||||
test_marketplace_product.brand = "SourceBrand"
|
||||
db.commit()
|
||||
|
||||
# Create product with overrides (price property converts euros to cents)
|
||||
# Create product with its own values (independent copy pattern)
|
||||
product = Product(
|
||||
vendor_id=test_vendor.id,
|
||||
marketplace_product_id=test_marketplace_product.id,
|
||||
price_cents=8999, # €89.99
|
||||
brand="OverrideBrand",
|
||||
price_cents=8999, # €89.99 - vendor's price
|
||||
brand="VendorBrand", # Vendor's brand
|
||||
)
|
||||
db.add(product)
|
||||
db.commit()
|
||||
db.refresh(product)
|
||||
|
||||
assert product.effective_price == 89.99
|
||||
assert product.effective_brand == "OverrideBrand"
|
||||
info = product.get_source_comparison_info()
|
||||
|
||||
# Reset price_cents to source (OVERRIDABLE_FIELDS now uses _cents names)
|
||||
product.reset_field_to_source("price_cents")
|
||||
db.commit()
|
||||
db.refresh(product)
|
||||
# Product has its own price
|
||||
assert info["price"] == 89.99
|
||||
assert info["price_cents"] == 8999
|
||||
assert info["price_source"] == 100.00 # Original marketplace price
|
||||
|
||||
assert product.price_cents is None
|
||||
assert product.price is None # Property returns None when cents is None
|
||||
assert product.effective_price == 100.00 # Now inherits from marketplace
|
||||
# Product has its own brand
|
||||
assert info["brand"] == "VendorBrand"
|
||||
assert info["brand_source"] == "SourceBrand" # Original marketplace brand
|
||||
|
||||
# Reset all fields
|
||||
product.reset_all_to_source()
|
||||
db.commit()
|
||||
db.refresh(product)
|
||||
# No more *_overridden keys in the pattern
|
||||
assert "price_overridden" not in info
|
||||
assert "brand_overridden" not in info
|
||||
|
||||
assert product.brand is None
|
||||
assert product.effective_brand == "SourceBrand" # Now inherits
|
||||
def test_product_fields_are_independent(
|
||||
self, db, test_vendor, test_marketplace_product
|
||||
):
|
||||
"""Test that product fields don't inherit from marketplace product.
|
||||
|
||||
def test_product_get_override_info(self, db, test_vendor, test_marketplace_product):
|
||||
"""Test get_override_info method."""
|
||||
test_marketplace_product.price_numeric = 100.00
|
||||
Products are independent entities - NULL fields stay NULL,
|
||||
no inheritance/fallback logic.
|
||||
"""
|
||||
# Set up marketplace product values
|
||||
test_marketplace_product.price_cents = 10000
|
||||
test_marketplace_product.brand = "SourceBrand"
|
||||
db.commit()
|
||||
|
||||
# Create product without copying values
|
||||
product = Product(
|
||||
vendor_id=test_vendor.id,
|
||||
marketplace_product_id=test_marketplace_product.id,
|
||||
price=89.99, # Override
|
||||
# brand not set - will inherit
|
||||
# Not copying price_cents or brand
|
||||
)
|
||||
db.add(product)
|
||||
db.commit()
|
||||
db.refresh(product)
|
||||
|
||||
info = product.get_override_info()
|
||||
# Fields should be NULL (not inherited)
|
||||
assert product.price_cents is None
|
||||
assert product.price is None
|
||||
assert product.brand is None
|
||||
|
||||
# Price is overridden
|
||||
assert info["price"] == 89.99
|
||||
assert info["price_overridden"] is True
|
||||
# But we can still see the source values for comparison
|
||||
info = product.get_source_comparison_info()
|
||||
assert info["price_source"] == 100.00
|
||||
|
||||
# Brand is inherited
|
||||
assert info["brand"] == "SourceBrand"
|
||||
assert info["brand_overridden"] is False
|
||||
assert info["brand_source"] == "SourceBrand"
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user