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)
|
name = Column(String, index=True)
|
||||||
slug = Column(String, unique=True, index=True, nullable=True)
|
slug = Column(String, unique=True, index=True, nullable=True)
|
||||||
description = Column(Text)
|
description = Column(Text)
|
||||||
price = Column(Float)
|
price = Column(Float, nullable=True) # Nullable - can inherit from model
|
||||||
discount_price = Column(Float, nullable=True)
|
discount_price = Column(Float, nullable=True)
|
||||||
category_id = Column(Integer, ForeignKey("category.id"))
|
category_id = Column(Integer, ForeignKey("category.id"))
|
||||||
model_id = Column(Integer, ForeignKey("model.id", ondelete="SET NULL"), nullable=True)
|
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")
|
model = relationship("Model", back_populates="products")
|
||||||
cart_items = relationship("CartItem", back_populates="product")
|
cart_items = relationship("CartItem", back_populates="product")
|
||||||
order_items = relationship("OrderItem", 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()
|
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:
|
for product in products:
|
||||||
if (not product.sizes or len(product.sizes) == 0) and product.model_id and product.model:
|
if (not product.sizes or len(product.sizes) == 0) and product.model_id and product.model:
|
||||||
product.sizes = product.model.sizes or []
|
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
|
return products
|
||||||
|
|
||||||
@ -72,10 +75,13 @@ def list_products(
|
|||||||
def search(q: str, skip: int = 0, limit: int = 20, db: Session = Depends(get_db)):
|
def search(q: str, skip: int = 0, limit: int = 20, db: Session = Depends(get_db)):
|
||||||
products = search_products(db, q, skip=skip, limit=limit)
|
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:
|
for product in products:
|
||||||
if (not product.sizes or len(product.sizes) == 0) and product.model_id and product.model:
|
if (not product.sizes or len(product.sizes) == 0) and product.model_id and product.model:
|
||||||
product.sizes = product.model.sizes or []
|
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
|
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 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:
|
if (not product.sizes or len(product.sizes) == 0) and product.model_id and product.model:
|
||||||
product.sizes = product.model.sizes or []
|
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
|
return product
|
||||||
|
|
||||||
@ -99,6 +108,24 @@ def create_new_product(
|
|||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
admin: User = Depends(get_current_admin_user),
|
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
|
# Auto-generate slug if not provided
|
||||||
if not product.slug:
|
if not product.slug:
|
||||||
base_slug = generate_slug(f"{product.brand} {product.name}")
|
base_slug = generate_slug(f"{product.brand} {product.name}")
|
||||||
@ -110,7 +137,18 @@ def create_new_product(
|
|||||||
counter += 1
|
counter += 1
|
||||||
product.slug = slug
|
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)
|
@router.put("/{product_id}", response_model=ProductResponse)
|
||||||
@ -123,6 +161,16 @@ def update_existing_product(
|
|||||||
product = update_product(db, product_id, product_update)
|
product = update_product(db, product_id, product_update)
|
||||||
if not product:
|
if not product:
|
||||||
raise HTTPException(status_code=404, detail="Product not found")
|
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
|
return product
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@ -8,7 +8,7 @@ class ProductCreate(BaseModel):
|
|||||||
name: str
|
name: str
|
||||||
slug: Optional[str] = None
|
slug: Optional[str] = None
|
||||||
description: str
|
description: str
|
||||||
price: float
|
price: Optional[float] = None # Optional - inherits from model if not set
|
||||||
discount_price: Optional[float] = None
|
discount_price: Optional[float] = None
|
||||||
category_id: int
|
category_id: int
|
||||||
model_id: Optional[int] = None
|
model_id: Optional[int] = None
|
||||||
@ -49,7 +49,7 @@ class ProductResponse(BaseModel):
|
|||||||
name: str
|
name: str
|
||||||
slug: Optional[str]
|
slug: Optional[str]
|
||||||
description: str
|
description: str
|
||||||
price: float
|
price: Optional[float] # May be None if inherited from model
|
||||||
discount_price: Optional[float]
|
discount_price: Optional[float]
|
||||||
category_id: int
|
category_id: int
|
||||||
model_id: Optional[int]
|
model_id: Optional[int]
|
||||||
|
|||||||
@ -209,7 +209,7 @@ export default function Admin() {
|
|||||||
|
|
||||||
const productData = {
|
const productData = {
|
||||||
...formData,
|
...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,
|
discount_price: formData.discount_price ? parseFloat(formData.discount_price) : null,
|
||||||
category_id: parseInt(formData.category_id),
|
category_id: parseInt(formData.category_id),
|
||||||
model_id: formData.model_id ? parseInt(formData.model_id) : null,
|
model_id: formData.model_id ? parseInt(formData.model_id) : null,
|
||||||
@ -821,8 +821,27 @@ export default function Admin() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>Price *</label>
|
<label>
|
||||||
<input type="number" step="0.01" name="price" value={formData.price} onChange={handleChange} required />
|
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>
|
||||||
|
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user