Auto set the price for product when model is set
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
parent
9176e32e6f
commit
7fbc2f7d41
Binary file not shown.
Binary file not shown.
@ -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
|
||||
|
||||
Binary file not shown.
@ -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
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
@ -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]
|
||||
|
||||
@ -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">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user