135 lines
6.0 KiB
Python

from db.models import Box, File, StagedFileProduct, Product, OpenBoxCard, OpenBox, Inventory
from db.utils import db_transaction
from uuid import uuid4 as uuid
from datetime import datetime
from sqlalchemy.orm import Session
from sqlalchemy.engine.result import Row
from schemas.box import CreateOpenBoxResponse, CreateSealedBoxResponse, CreateBoxResponse
import logging
from typing import Any
from db.utils import db_transaction
from services.inventory import InventoryService
logger = logging.getLogger(__name__)
class BoxService:
def __init__(self, db: Session, inventory_service: InventoryService):
self.db = db
self.inventory_service = inventory_service
def validate_file_ids(self, file_ids: list[str]):
# check if all file_ids are valid
for file_id in file_ids:
if self.db.query(File).filter(File.id == file_id).first() is None:
raise Exception(f"File ID {file_id} not found")
def get_staged_product_data(self, file_ids: list[str]) -> StagedFileProduct:
staged_product_data = self.db.query(StagedFileProduct).filter(StagedFileProduct.file_id.in_(file_ids)).all()
return staged_product_data
def aggregate_staged_product_data(self, staged_product_data: list[Row]) -> dict[Product, int]:
product_data = {}
for row in staged_product_data:
product = self.db.query(Product).filter(Product.id == row.product_id).first()
if product not in product_data:
product_data[product] = 0
product_data[product] += row.quantity
return product_data
def find_product_for_box_data(self, create_box_data: dict[str, Any]) -> Product:
existing_product = self.db.query(Product).filter(
Product.name == create_box_data["name"], # TODO: needs complex enum
Product.type == "box",
Product.set_code == create_box_data["set_code"], # TODO: needs complex enum
Product.set_name == create_box_data["set_name"], # TODO: needs complex enum
Product.product_line == create_box_data["product_line"]).first()
return existing_product
def create_product_for_box(self, create_box_data: dict[str, Any]) -> Product:
product = Product(
id=str(uuid()),
name=create_box_data["name"],
type="box",
set_code=create_box_data["set_code"],
set_name=create_box_data["set_name"],
product_line=create_box_data["product_line"]
)
self.db.add(product)
return product
def create_box_db(self, product: Product, create_box_data: dict[str, Any]) -> Box:
box = Box(
product_id=product.id,
type=create_box_data["type"],
sku=create_box_data["sku"],
num_cards_expected=create_box_data["num_cards_expected"]
)
self.db.add(box)
return box
def create_open_box(self, product: Product, create_box_data: dict[str, Any]) -> OpenBox:
open_box = OpenBox(
id = str(uuid()),
product_id=product.id,
num_cards_actual=create_box_data["num_cards_actual"],
date_opened=datetime.strptime(create_box_data["date_opened"], "%Y-%m-%d")
)
self.db.add(open_box)
return open_box
def add_products_to_open_box(self, open_box: OpenBox, product_data: dict[Product, int]) -> None:
for product, quantity in product_data.items():
open_box_card = OpenBoxCard(
id=str(uuid()),
open_box_id=open_box.id,
card_id=product.id,
quantity=quantity
)
self.db.add(open_box_card)
def format_response(self, open_box: OpenBox = None, inventory: Inventory = None) -> CreateBoxResponse:
response = CreateBoxResponse(success=True)
return response
def create_box(self, create_box_data: dict[str, Any], file_ids: list[str] = None) -> CreateBoxResponse:
sealed = create_box_data["sealed"]
assert isinstance(sealed, bool)
if file_ids and not sealed:
self.validate_file_ids(file_ids)
staged_product_data = self.get_staged_product_data(file_ids)
product_data = self.aggregate_staged_product_data(staged_product_data)
elif file_ids and sealed:
raise Exception("Cannot add cards with a sealed box")
# find product with all same box data
existing_product = self.find_product_for_box_data(create_box_data)
if existing_product:
box_product = existing_product
try:
with db_transaction(self.db):
if not existing_product:
box_product = self.create_product_for_box(create_box_data)
box = self.create_box_db(box_product, create_box_data)
if not sealed:
open_box = self.create_open_box(box_product, create_box_data)
if file_ids:
process_staged_products = self.inventory_service.process_staged_products(product_data)
self.add_products_to_open_box(open_box, product_data)
# should be the file service handling this but im about to die irl
# update file id status to processed
for file_id in file_ids:
file = self.db.query(File).filter(File.id == file_id).first()
file.status = "processed"
self.db.add(file)
return self.format_response(open_box=open_box)
elif not file_ids and sealed:
# add sealed box to inventory
inventory = self.inventory_service.add_sealed_box_to_inventory(box_product, 1)
return self.format_response(inventory=inventory)
except Exception as e:
logger.error(f"Error creating box: {str(e)}")
raise e