import logging from sqlalchemy.orm import Session from app.services.base_service import BaseService from app.models.inventory_management import InventoryItem from app.models.tcgplayer_inventory import TCGPlayerInventory from app.models.pricing import PricingEvent from app.db.database import transaction from decimal import Decimal logger = logging.getLogger(__name__) class PricingService(BaseService): def __init__(self): super().__init__(None) async def set_price(self, db: Session, inventory_item: InventoryItem) -> float: """ TODO This sets listed_price per inventory_item but listed_price can only be applied to a product however, this may be desired on other marketplaces when generating pricing file for tcgplayer, give the option to set min, max, avg price for product? """ # Fetch base pricing data cost_basis = Decimal(str(inventory_item.cost_basis)) market_price = Decimal(str(inventory_item.physical_item.sku.product.most_recent_tcgplayer_price.market_price)) tcg_low = Decimal(str(inventory_item.physical_item.sku.product.most_recent_tcgplayer_price.low_price)) tcg_mid = Decimal(str(inventory_item.physical_item.sku.product.most_recent_tcgplayer_price.mid_price)) listed_price = Decimal(str(inventory_item.marketplace_listing.listed_price)) if inventory_item.marketplace_listing else None logger.info(f"listed_price: {listed_price}") logger.info(f"market_price: {market_price}") logger.info(f"tcg_low: {tcg_low}") logger.info(f"tcg_mid: {tcg_mid}") logger.info(f"cost_basis: {cost_basis}") # TODO: Add logic to fetch lowest price for seller with same quantity in stock # NOT IMPLEMENTED YET lowest_price_for_quantity = Decimal('0.0') # Hardcoded configuration values (should be parameterized later) shipping_cost = Decimal('1.0') tcgplayer_shipping_fee = Decimal('1.31') average_cards_per_order = Decimal('3.0') marketplace_fee_percentage = Decimal('0.20') target_margin = Decimal('0.10') velocity_multiplier = Decimal('0.0') global_margin_multiplier = Decimal('0.00') min_floor_price = Decimal('0.25') price_drop_threshold = Decimal('0.20') # TODO add age of inventory price decrease multiplier age_of_inventory_multiplier = Decimal('0.0') # card cost margin multiplier if market_price > 0 and market_price < 2: card_cost_margin_multiplier = Decimal('-0.075') elif market_price >= 2 and market_price < 10: card_cost_margin_multiplier = Decimal('-0.025') elif market_price >= 10 and market_price < 30: card_cost_margin_multiplier = Decimal('0.025') elif market_price >= 30 and market_price < 50: card_cost_margin_multiplier = Decimal('0.05') elif market_price >= 50 and market_price < 100: card_cost_margin_multiplier = Decimal('0.075') elif market_price >= 100 and market_price < 200: card_cost_margin_multiplier = Decimal('0.10') # Fetch current total quantity in stock for SKU quantity_record = db.query(TCGPlayerInventory).filter( TCGPlayerInventory.tcgplayer_sku_id == inventory_item.physical_item.tcgplayer_sku_id ).first() quantity_in_stock = quantity_record.total_quantity if quantity_record else 0 # Determine quantity multiplier based on stock levels if quantity_in_stock < 4: quantity_multiplier = Decimal('0.0') elif quantity_in_stock == 4: quantity_multiplier = Decimal('0.1') elif 5 <= quantity_in_stock < 10: quantity_multiplier = Decimal('0.2') elif quantity_in_stock >= 10: quantity_multiplier = Decimal('0.3') else: quantity_multiplier = Decimal('0.0') # Calculate adjusted target margin from base and global multipliers adjusted_target_margin = target_margin + global_margin_multiplier + card_cost_margin_multiplier # limit shipping cost offset to 10% of market price shipping_cost_offset = min(shipping_cost / average_cards_per_order, market_price * Decimal('0.1')) # Calculate base price considering cost, shipping, fees, and margin targets base_price = (cost_basis + shipping_cost_offset) / ( (Decimal('1.0') - marketplace_fee_percentage) - adjusted_target_margin ) # Adjust base price by quantity and velocity multipliers, limit markup to amount of shipping fee adjusted_price = min( base_price * (Decimal('1.0') + quantity_multiplier + velocity_multiplier - age_of_inventory_multiplier), base_price + tcgplayer_shipping_fee ) # Enforce minimum floor price to ensure profitability if adjusted_price < min_floor_price: adjusted_price = min_floor_price # Adjust price based on market prices (TCG low and TCG mid) if adjusted_price < tcg_low: adjusted_price = tcg_mid price_used = "tcg mid" price_reason = "adjusted price below tcg low" elif adjusted_price > tcg_low and adjusted_price < (market_price * Decimal('0.8')) and adjusted_price < (tcg_mid * Decimal('0.8')): adjusted_price = tcg_mid price_used = "tcg mid" price_reason = f"adjusted price below 80% of market price and tcg mid" else: price_used = "adjusted price" price_reason = "valid price assigned based on margin targets" # TODO: Add logic to adjust price to beat competitor price with same quantity # NOT IMPLEMENTED YET if adjusted_price < lowest_price_for_quantity: adjusted_price = lowest_price_for_quantity - Decimal('0.01') price_used = "lowest price for quantity" price_reason = "adjusted price below lowest price for quantity" # Fine-tune price to optimize for free shipping promotions free_shipping_adjustment = False for x in range(1, 5): quantity = Decimal(str(x)) if Decimal('5.00') <= adjusted_price * quantity <= Decimal('5.05'): adjusted_price = Decimal('4.99') / quantity free_shipping_adjustment = True break # prevent price drop over price drop threshold if listed_price and adjusted_price < (listed_price * (1 - price_drop_threshold)): adjusted_price = listed_price price_used = "listed price" price_reason = "adjusted price below price drop threshold" # Record pricing event in database transaction with transaction(db): pricing_event = PricingEvent( inventory_item_id=inventory_item.id, price=float(adjusted_price), price_used=price_used, price_reason=price_reason, free_shipping_adjustment=free_shipping_adjustment ) db.add(pricing_event) return pricing_event def set_price_for_unmanaged_inventory(self, db: Session, tcgplayer_sku_id: int, quantity: int) -> float: pass