from typing import Union from sqlalchemy import select, func, and_, cast, String, case, or_ from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import joinedload from database import DailyStock from database.sipro import * from database.sipro.enums.product import ProductRelationType def get_marketplace_suppliers_and_company_warehouses(marketplace: Marketplace): company = marketplace.company suppliers = set() company_warehouses = set() for warehouse in marketplace.warehouses: for supplier in warehouse.suppliers: if supplier.is_pseudo: continue suppliers.add(supplier) company_warehouses.update(warehouse.company_warehouses) if marketplace.sell_warehouse_products: company_warehouse = company.warehouse if company_warehouse and not company.is_denco: company_warehouses.add(company_warehouse) return list(suppliers), list(company_warehouses) async def get_stocks_data( session: AsyncSession, marketplace: Marketplace, product_ids: Union[list[int], None] = None ): if not product_ids: product_ids = [] company = marketplace.company suppliers, company_warehouses = get_marketplace_suppliers_and_company_warehouses(marketplace) supplier_ids = [supplier.id for supplier in suppliers] company_warehouse_ids = [warehouse.id for warehouse in company_warehouses] sell_mixes: bool = marketplace.sell_mixes sell_blocks: bool = marketplace.sell_blocks sell_warehouse_products: bool = marketplace.sell_warehouse_products sell_from_price: int = marketplace.sell_from_price supplier_stock_subquery = ( select( func.greatest( func.sum(SupplierProduct.supplier_stock) - func.coalesce(DailyStock.sold_today, 0), 0 ) .label('supplier_stock'), SupplierProduct.product_id.label('product_id') ) .select_from( SupplierProduct ) .join( Product ) .outerjoin( DailyStock, DailyStock.product_id == SupplierProduct.product_id ) .where( SupplierProduct.supplier_id.in_(supplier_ids) ) .group_by( SupplierProduct.product_id ) .subquery() ) warehouse_stock_subquery = ( select( func.count(CompanyWarehouseProduct.is_sold).label('warehouse_stock'), CompanyWarehouseProduct.product_id.label('product_id') ) .select_from( CompanyWarehouseProduct ) .where( CompanyWarehouseProduct.is_sold == False, CompanyWarehouseProduct.company_warehouse_id.in_(company_warehouse_ids) ) .group_by( CompanyWarehouseProduct.product_id ) .subquery() ) mix_stock_first_subquery = ( select( func.sum(SupplierProduct.supplier_stock).label('master_stock'), SupplierProduct.product_id.label('product_id') ) .select_from( SupplierProduct ) .where( SupplierProduct.supplier_id.in_(supplier_ids) ) .group_by( SupplierProduct.product_id ) .subquery() ) mix_stock_full_subquery = ( select( func.min(SupplierProduct.in_block).label('mix_stock'), Product.id.label('product_id') ) .select_from( Product ) .join( SupplierProduct ) .join( ProductRelation, Product.id == ProductRelation.slave_product_id ) .join( mix_stock_first_subquery, mix_stock_first_subquery.c.product_id == ProductRelation.master_product_id ) .where( ProductRelation.relation_type == ProductRelationType.MAIN_PRODUCT, mix_stock_first_subquery.c.master_stock > 0 ) .group_by( Product.id ) .subquery() ) is_master_first_subquery = ( select( ProductRelation.master_product_id, (func.count(ProductRelation.master_product_id) > 0).label('is_master') ) .where( ProductRelation.relation_type == ProductRelationType.MAIN_PRODUCT ) .group_by( ProductRelation.master_product_id ) .subquery() ) is_master_subquery = ( select( Product.id.label('product_id'), func.coalesce(is_master_first_subquery.c.is_master, False).label('is_master') ) .select_from( Product ) .outerjoin( is_master_first_subquery, Product.id == is_master_first_subquery.c.master_product_id ) .subquery() ) in_block_subquery = ( select( Product.id.label('product_id'), func.min(SupplierProduct.in_block).label('in_block_value') ) .select_from( Product ) .join( SupplierProduct ) .where( SupplierProduct.supplier_id.in_(supplier_ids), SupplierProduct.supplier_stock > 0 ) .group_by( Product.id ) .subquery() ) slaves_stock_first_subquery = ( select( ProductRelation.master_product_id.label('product_id'), func.sum(SupplierProduct.supplier_stock).label('slaves_stock') ) .select_from( ProductRelation ) .join( SupplierProduct, and_( ProductRelation.slave_product_id == SupplierProduct.product_id, ProductRelation.relation_type == ProductRelationType.SAME_MIX ) ) .where( SupplierProduct.supplier_id.in_( supplier_ids ) ) .group_by( ProductRelation.master_product_id ) .subquery() ) slaves_stock_subquery = ( select( Product.id.label('product_id'), slaves_stock_first_subquery.c.slaves_stock.label('slaves_stock') ) .select_from( Product ) .join( slaves_stock_first_subquery, slaves_stock_first_subquery.c.product_id == Product.id ) .subquery() ) stmt = ( select( MarketplaceProduct, func.coalesce(Product.article, cast(Product.denco_article, String)).label('denco_article'), func.coalesce(MarketplaceProduct.mp_price_bought, 0).label('price_purchase'), func.coalesce(supplier_stock_subquery.c.supplier_stock, 0).label('supplier_stock'), case( ( sell_warehouse_products, func.coalesce(warehouse_stock_subquery.c.warehouse_stock, 0) ), else_=0) .label('warehouse_stock'), func.coalesce(mix_stock_full_subquery.c.mix_stock, 0).label('mix_stock'), func.coalesce(in_block_subquery.c.in_block_value, 1).label('in_block_value'), is_master_subquery.c.is_master.label('is_master'), func.coalesce(slaves_stock_subquery.c.slaves_stock, 0).label('slaves_stock'), MarketplaceProduct.price_recommended.label('price_recommended'), MarketplaceProduct.is_archived.label('is_archived') ) .select_from( MarketplaceProduct ) .join( Product ) .options( joinedload(MarketplaceProduct.product) ) .where( MarketplaceProduct.marketplace_id == marketplace.id, or_( len(product_ids) == 0, MarketplaceProduct.product_id.in_(product_ids) ) ) .outerjoin( supplier_stock_subquery, supplier_stock_subquery.c.product_id == MarketplaceProduct.product_id ) .outerjoin( warehouse_stock_subquery, warehouse_stock_subquery.c.product_id == MarketplaceProduct.product_id ) .outerjoin( mix_stock_full_subquery, mix_stock_full_subquery.c.product_id == MarketplaceProduct.product_id ) .outerjoin( in_block_subquery, in_block_subquery.c.product_id == MarketplaceProduct.product_id ) .outerjoin( is_master_subquery, is_master_subquery.c.product_id == MarketplaceProduct.product_id ) .outerjoin( slaves_stock_subquery, slaves_stock_subquery.c.product_id == MarketplaceProduct.product_id ) ) result = await session.execute(stmt) marketplace_products = result.all() result = [] for (marketplace_product, denco_article, price_purchase, supplier_stock, warehouse_stock, mix_stock, in_block_value, is_master, slaves_stock, price_recommended, is_archived) in marketplace_products: if is_archived or (sell_from_price > price_recommended): result.append({ 'denco_article': denco_article, 'full_stock': 0, 'marketplace_product': marketplace_product, }) continue is_mix = mix_stock is not None if all([is_mix, slaves_stock > 0]): mix_stock = 0 balance_limit = price_purchase > company.balance if balance_limit: supplier_stock = 0 full_stock = supplier_stock + warehouse_stock if all([not is_mix, not sell_blocks, in_block_value > 1]): full_stock = warehouse_stock if sell_mixes and (not balance_limit): full_stock += mix_stock if (not sell_mixes) and is_master: full_stock = warehouse_stock if (not sell_mixes) and is_mix: full_stock = warehouse_stock if 45 > company.balance: full_stock = 0 full_stock = max([0, full_stock]) result.append({ 'denco_article': denco_article, 'full_stock': full_stock, 'marketplace_product': marketplace_product, }) return result