Auto set the price for product when model is set
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
dvirlabs 2026-05-08 18:23:16 +03:00
parent 9176e32e6f
commit 7fbc2f7d41
8 changed files with 85 additions and 9 deletions

View File

@ -11,7 +11,7 @@ class Product(Base):
name = Column(String, index=True)
slug = Column(String, unique=True, index=True, nullable=True)
description = Column(Text)
price = Column(Float)
price = Column(Float, nullable=True) # Nullable - can inherit from model
discount_price = Column(Float, nullable=True)
category_id = Column(Integer, ForeignKey("category.id"))
model_id = Column(Integer, ForeignKey("model.id", ondelete="SET NULL"), nullable=True)
@ -31,3 +31,12 @@ class Product(Base):
model = relationship("Model", back_populates="products")
cart_items = relationship("CartItem", back_populates="product")
order_items = relationship("OrderItem", back_populates="product")
def get_effective_price(self) -> float:
"""Get the effective price - either product's own price or model's base_price"""
if self.price is not None:
return float(self.price)
elif self.model and self.model.base_price is not None:
return float(self.model.base_price)
else:
return 0.0 # Default fallback

View File

@ -60,10 +60,13 @@ def list_products(
products = query.offset(skip).limit(limit).all()
# Inherit sizes from model if product doesn't have sizes
# Inherit sizes and price from model if product doesn't have them
for product in products:
if (not product.sizes or len(product.sizes) == 0) and product.model_id and product.model:
product.sizes = product.model.sizes or []
# Inherit price from model if not set
if product.price is None and product.model_id and product.model and product.model.base_price:
product.price = float(product.model.base_price)
return products
@ -72,10 +75,13 @@ def list_products(
def search(q: str, skip: int = 0, limit: int = 20, db: Session = Depends(get_db)):
products = search_products(db, q, skip=skip, limit=limit)
# Inherit sizes from model if product doesn't have sizes
# Inherit sizes and price from model if product doesn't have them
for product in products:
if (not product.sizes or len(product.sizes) == 0) and product.model_id and product.model:
product.sizes = product.model.sizes or []
# Inherit price from model if not set
if product.price is None and product.model_id and product.model and product.model.base_price:
product.price = float(product.model.base_price)
return products
@ -89,6 +95,9 @@ def get_product(product_id: int, db: Session = Depends(get_db)):
# If product doesn't have sizes but has a model, inherit sizes from model
if (not product.sizes or len(product.sizes) == 0) and product.model_id and product.model:
product.sizes = product.model.sizes or []
# Inherit price from model if not set
if product.price is None and product.model_id and product.model and product.model.base_price:
product.price = float(product.model.base_price)
return product
@ -99,6 +108,24 @@ def create_new_product(
db: Session = Depends(get_db),
admin: User = Depends(get_current_admin_user),
):
# Validate price: either product has price OR model has base_price
if product.price is None:
if not product.model_id:
raise HTTPException(
status_code=400,
detail="Price is required when no model is assigned"
)
# Check if model exists and has base_price
from app.models import Model
model = db.query(Model).filter(Model.id == product.model_id).first()
if not model:
raise HTTPException(status_code=404, detail="Model not found")
if model.base_price is None:
raise HTTPException(
status_code=400,
detail="Model must have a base_price set if product price is not provided"
)
# Auto-generate slug if not provided
if not product.slug:
base_slug = generate_slug(f"{product.brand} {product.name}")
@ -110,7 +137,18 @@ def create_new_product(
counter += 1
product.slug = slug
return create_product(db, product)
created_product = create_product(db, product)
# Refresh to get the model relationship
db.refresh(created_product)
# Inherit price and sizes from model if needed
if created_product.price is None and created_product.model_id and created_product.model:
created_product.price = float(created_product.model.base_price)
if (not created_product.sizes or len(created_product.sizes) == 0) and created_product.model_id and created_product.model:
created_product.sizes = created_product.model.sizes or []
return created_product
@router.put("/{product_id}", response_model=ProductResponse)
@ -123,6 +161,16 @@ def update_existing_product(
product = update_product(db, product_id, product_update)
if not product:
raise HTTPException(status_code=404, detail="Product not found")
# Refresh to ensure model relationship is loaded
db.refresh(product)
# Inherit price and sizes from model if needed
if product.price is None and product.model_id and product.model and product.model.base_price:
product.price = float(product.model.base_price)
if (not product.sizes or len(product.sizes) == 0) and product.model_id and product.model:
product.sizes = product.model.sizes or []
return product

View File

@ -8,7 +8,7 @@ class ProductCreate(BaseModel):
name: str
slug: Optional[str] = None
description: str
price: float
price: Optional[float] = None # Optional - inherits from model if not set
discount_price: Optional[float] = None
category_id: int
model_id: Optional[int] = None
@ -49,7 +49,7 @@ class ProductResponse(BaseModel):
name: str
slug: Optional[str]
description: str
price: float
price: Optional[float] # May be None if inherited from model
discount_price: Optional[float]
category_id: int
model_id: Optional[int]

View File

@ -209,7 +209,7 @@ export default function Admin() {
const productData = {
...formData,
price: parseFloat(formData.price),
price: formData.price ? parseFloat(formData.price) : null, // Allow null - inherits from model
discount_price: formData.discount_price ? parseFloat(formData.discount_price) : null,
category_id: parseInt(formData.category_id),
model_id: formData.model_id ? parseInt(formData.model_id) : null,
@ -821,8 +821,27 @@ export default function Admin() {
</div>
<div className="form-group">
<label>Price *</label>
<input type="number" step="0.01" name="price" value={formData.price} onChange={handleChange} required />
<label>
Price {formData.model_id && models.find(m => m.id === parseInt(formData.model_id))?.base_price ? '(Optional - inherits from model)' : '*'}
</label>
<input
type="number"
step="0.01"
name="price"
value={formData.price}
onChange={handleChange}
required={!formData.model_id || !models.find(m => m.id === parseInt(formData.model_id))?.base_price}
placeholder={
formData.model_id && models.find(m => m.id === parseInt(formData.model_id))?.base_price
? `Model price: ₪${parseFloat(models.find(m => m.id === parseInt(formData.model_id)).base_price).toFixed(2)}`
: 'Enter price'
}
/>
{formData.model_id && models.find(m => m.id === parseInt(formData.model_id))?.base_price && (
<small style={{ color: '#666', fontSize: '0.85em' }}>
Leave empty to use model's base price of {parseFloat(models.find(m => m.id === parseInt(formData.model_id)).base_price).toFixed(2)}
</small>
)}
</div>
<div className="form-group">