Refactoring code for modular approach

This commit is contained in:
2025-09-09 22:07:21 +02:00
parent b865235f8a
commit 8fbe64687a
9 changed files with 1517 additions and 520 deletions

View File

@@ -6,10 +6,10 @@ from app.core.database import get_db
from app.api.deps import get_current_user
from app.tasks.background_tasks import process_marketplace_import
from middleware.decorators import rate_limit
from models.api_models import MarketplaceImportJobResponse, MarketplaceImportRequest, StockResponse, \
StockSummaryResponse
from models.api_models import (MarketplaceImportJobResponse, MarketplaceImportRequest, StockResponse,
StockSummaryResponse, StockCreate, StockAdd, StockUpdate)
from models.database_models import User, MarketplaceImportJob, Shop
from datetime import datetime
from stock_service import stock_service
import logging
router = APIRouter()
@@ -19,236 +19,88 @@ logger = logging.getLogger(__name__)
# Stock Management Routes (Protected)
@router.post("/stock", response_model=StockResponse)
def set_stock(stock: StockCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
def set_stock(
stock: StockCreate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""Set exact stock quantity for a GTIN at a specific location (replaces existing quantity)"""
# Normalize GTIN
def normalize_gtin(gtin_value):
if not gtin_value:
return None
gtin_str = str(gtin_value).strip()
if '.' in gtin_str:
gtin_str = gtin_str.split('.')[0]
gtin_clean = ''.join(filter(str.isdigit, gtin_str))
if len(gtin_clean) in [8, 12, 13, 14]:
return gtin_clean.zfill(13) if len(gtin_clean) == 13 else gtin_clean.zfill(12)
return gtin_clean if gtin_clean else None
normalized_gtin = normalize_gtin(stock.gtin)
if not normalized_gtin:
raise HTTPException(status_code=400, detail="Invalid GTIN format")
# Check if stock entry already exists for this GTIN and location
existing_stock = db.query(Stock).filter(
Stock.gtin == normalized_gtin,
Stock.location == stock.location.strip().upper()
).first()
if existing_stock:
# Update existing stock (SET to exact quantity)
old_quantity = existing_stock.quantity
existing_stock.quantity = stock.quantity
existing_stock.updated_at = datetime.utcnow()
db.commit()
db.refresh(existing_stock)
logger.info(f"Updated stock for GTIN {normalized_gtin} at {stock.location}: {old_quantity}{stock.quantity}")
return existing_stock
else:
# Create new stock entry
new_stock = Stock(
gtin=normalized_gtin,
location=stock.location.strip().upper(),
quantity=stock.quantity
)
db.add(new_stock)
db.commit()
db.refresh(new_stock)
logger.info(f"Created new stock for GTIN {normalized_gtin} at {stock.location}: {stock.quantity}")
return new_stock
try:
result = stock_service.set_stock(db, stock)
return result
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
logger.error(f"Error setting stock: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.post("/stock/add", response_model=StockResponse)
def add_stock(stock: StockAdd, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
def add_stock(
stock: StockAdd,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""Add quantity to existing stock for a GTIN at a specific location (adds to existing quantity)"""
# Normalize GTIN
def normalize_gtin(gtin_value):
if not gtin_value:
return None
gtin_str = str(gtin_value).strip()
if '.' in gtin_str:
gtin_str = gtin_str.split('.')[0]
gtin_clean = ''.join(filter(str.isdigit, gtin_str))
if len(gtin_clean) in [8, 12, 13, 14]:
return gtin_clean.zfill(13) if len(gtin_clean) == 13 else gtin_clean.zfill(12)
return gtin_clean if gtin_clean else None
normalized_gtin = normalize_gtin(stock.gtin)
if not normalized_gtin:
raise HTTPException(status_code=400, detail="Invalid GTIN format")
# Check if stock entry already exists for this GTIN and location
existing_stock = db.query(Stock).filter(
Stock.gtin == normalized_gtin,
Stock.location == stock.location.strip().upper()
).first()
if existing_stock:
# Add to existing stock
old_quantity = existing_stock.quantity
existing_stock.quantity += stock.quantity
existing_stock.updated_at = datetime.utcnow()
db.commit()
db.refresh(existing_stock)
logger.info(
f"Added stock for GTIN {normalized_gtin} at {stock.location}: {old_quantity} + {stock.quantity} = {existing_stock.quantity}")
return existing_stock
else:
# Create new stock entry with the quantity
new_stock = Stock(
gtin=normalized_gtin,
location=stock.location.strip().upper(),
quantity=stock.quantity
)
db.add(new_stock)
db.commit()
db.refresh(new_stock)
logger.info(f"Created new stock for GTIN {normalized_gtin} at {stock.location}: {stock.quantity}")
return new_stock
try:
result = stock_service.add_stock(db, stock)
return result
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
logger.error(f"Error adding stock: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.post("/stock/remove", response_model=StockResponse)
def remove_stock(stock: StockAdd, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
def remove_stock(
stock: StockAdd,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""Remove quantity from existing stock for a GTIN at a specific location"""
# Normalize GTIN
def normalize_gtin(gtin_value):
if not gtin_value:
return None
gtin_str = str(gtin_value).strip()
if '.' in gtin_str:
gtin_str = gtin_str.split('.')[0]
gtin_clean = ''.join(filter(str.isdigit, gtin_str))
if len(gtin_clean) in [8, 12, 13, 14]:
return gtin_clean.zfill(13) if len(gtin_clean) == 13 else gtin_clean.zfill(12)
return gtin_clean if gtin_clean else None
normalized_gtin = normalize_gtin(stock.gtin)
if not normalized_gtin:
raise HTTPException(status_code=400, detail="Invalid GTIN format")
# Find existing stock entry
existing_stock = db.query(Stock).filter(
Stock.gtin == normalized_gtin,
Stock.location == stock.location.strip().upper()
).first()
if not existing_stock:
raise HTTPException(
status_code=404,
detail=f"No stock found for GTIN {normalized_gtin} at location {stock.location}"
)
# Check if we have enough stock to remove
if existing_stock.quantity < stock.quantity:
raise HTTPException(
status_code=400,
detail=f"Insufficient stock. Available: {existing_stock.quantity}, Requested to remove: {stock.quantity}"
)
# Remove from existing stock
old_quantity = existing_stock.quantity
existing_stock.quantity -= stock.quantity
existing_stock.updated_at = datetime.utcnow()
db.commit()
db.refresh(existing_stock)
logger.info(
f"Removed stock for GTIN {normalized_gtin} at {stock.location}: {old_quantity} - {stock.quantity} = {existing_stock.quantity}")
return existing_stock
try:
result = stock_service.remove_stock(db, stock)
return result
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
logger.error(f"Error removing stock: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.get("/stock/{gtin}", response_model=StockSummaryResponse)
def get_stock_by_gtin(gtin: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
def get_stock_by_gtin(
gtin: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""Get all stock locations and total quantity for a specific GTIN"""
# Normalize GTIN
def normalize_gtin(gtin_value):
if not gtin_value:
return None
gtin_str = str(gtin_value).strip()
if '.' in gtin_str:
gtin_str = gtin_str.split('.')[0]
gtin_clean = ''.join(filter(str.isdigit, gtin_str))
if len(gtin_clean) in [8, 12, 13, 14]:
return gtin_clean.zfill(13) if len(gtin_clean) == 13 else gtin_clean.zfill(12)
return gtin_clean if gtin_clean else None
normalized_gtin = normalize_gtin(gtin)
if not normalized_gtin:
raise HTTPException(status_code=400, detail="Invalid GTIN format")
# Get all stock entries for this GTIN
stock_entries = db.query(Stock).filter(Stock.gtin == normalized_gtin).all()
if not stock_entries:
raise HTTPException(status_code=404, detail=f"No stock found for GTIN: {gtin}")
# Calculate total quantity and build locations list
total_quantity = 0
locations = []
for entry in stock_entries:
total_quantity += entry.quantity
locations.append(StockLocationResponse(
location=entry.location,
quantity=entry.quantity
))
# Try to get product title for reference
product = db.query(Product).filter(Product.gtin == normalized_gtin).first()
product_title = product.title if product else None
return StockSummaryResponse(
gtin=normalized_gtin,
total_quantity=total_quantity,
locations=locations,
product_title=product_title
)
try:
result = stock_service.get_stock_by_gtin(db, gtin)
return result
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
logger.error(f"Error getting stock for GTIN {gtin}: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.get("/stock/{gtin}/total")
def get_total_stock(gtin: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
def get_total_stock(
gtin: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""Get total quantity in stock for a specific GTIN"""
# Normalize GTIN
def normalize_gtin(gtin_value):
if not gtin_value:
return None
gtin_str = str(gtin_value).strip()
if '.' in gtin_str:
gtin_str = gtin_str.split('.')[0]
gtin_clean = ''.join(filter(str.isdigit, gtin_str))
if len(gtin_clean) in [8, 12, 13, 14]:
return gtin_clean.zfill(13) if len(gtin_clean) == 13 else gtin_clean.zfill(12)
return gtin_clean if gtin_clean else None
normalized_gtin = normalize_gtin(gtin)
if not normalized_gtin:
raise HTTPException(status_code=400, detail="Invalid GTIN format")
# Calculate total stock
total_stock = db.query(Stock).filter(Stock.gtin == normalized_gtin).all()
total_quantity = sum(entry.quantity for entry in total_stock)
# Get product info for context
product = db.query(Product).filter(Product.gtin == normalized_gtin).first()
return {
"gtin": normalized_gtin,
"total_quantity": total_quantity,
"product_title": product.title if product else None,
"locations_count": len(total_stock)
}
try:
result = stock_service.get_total_stock(db, gtin)
return result
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
logger.error(f"Error getting total stock for GTIN {gtin}: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.get("/stock", response_model=List[StockResponse])
@@ -261,55 +113,50 @@ def get_all_stock(
current_user: User = Depends(get_current_user)
):
"""Get all stock entries with optional filtering"""
query = db.query(Stock)
if location:
query = query.filter(Stock.location.ilike(f"%{location}%"))
if gtin:
# Normalize GTIN for search
def normalize_gtin(gtin_value):
if not gtin_value:
return None
gtin_str = str(gtin_value).strip()
if '.' in gtin_str:
gtin_str = gtin_str.split('.')[0]
gtin_clean = ''.join(filter(str.isdigit, gtin_str))
if len(gtin_clean) in [8, 12, 13, 14]:
return gtin_clean.zfill(13) if len(gtin_clean) == 13 else gtin_clean.zfill(12)
return gtin_clean if gtin_clean else None
normalized_gtin = normalize_gtin(gtin)
if normalized_gtin:
query = query.filter(Stock.gtin == normalized_gtin)
stock_entries = query.offset(skip).limit(limit).all()
return stock_entries
try:
result = stock_service.get_all_stock(
db=db,
skip=skip,
limit=limit,
location=location,
gtin=gtin
)
return result
except Exception as e:
logger.error(f"Error getting all stock: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.put("/stock/{stock_id}", response_model=StockResponse)
def update_stock(stock_id: int, stock_update: StockUpdate, db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)):
def update_stock(
stock_id: int,
stock_update: StockUpdate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""Update stock quantity for a specific stock entry"""
stock_entry = db.query(Stock).filter(Stock.id == stock_id).first()
if not stock_entry:
raise HTTPException(status_code=404, detail="Stock entry not found")
stock_entry.quantity = stock_update.quantity
stock_entry.updated_at = datetime.utcnow()
db.commit()
db.refresh(stock_entry)
return stock_entry
try:
result = stock_service.update_stock(db, stock_id, stock_update)
return result
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
logger.error(f"Error updating stock {stock_id}: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.delete("/stock/{stock_id}")
def delete_stock(stock_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
def delete_stock(
stock_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""Delete a stock entry"""
stock_entry = db.query(Stock).filter(Stock.id == stock_id).first()
if not stock_entry:
raise HTTPException(status_code=404, detail="Stock entry not found")
db.delete(stock_entry)
db.commit()
return {"message": "Stock entry deleted successfully"}
try:
stock_service.delete_stock(db, stock_id)
return {"message": "Stock entry deleted successfully"}
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
logger.error(f"Error deleting stock {stock_id}: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")