from collections import defaultdict from django.db.models import Sum, Count, Case, When, Q, Value from django.db.models.functions import Coalesce from apps.warehouse.models import InventoryQuotaSaleTransaction, InventoryQuotaSaleItem class TransactionDashboardService: @staticmethod def get_dashboard(org): transactions = InventoryQuotaSaleTransaction.objects.filter( seller_organization=org ) transaction_stats = transactions.aggregate( total_transactions=Count("id"), success_transactions=Count("id", filter=Q(transaction_status="success")), failed_transactions=Count("id", filter=Q(transaction_status="failed")), waiting_transactions=Count("id", filter=Q(transaction_status="waiting")), total_amount=Coalesce(Sum("price_paid"), 0), total_weight=Coalesce(Sum("weight"), 0), unique_ranchers=Count("rancher", distinct=True), ) items = InventoryQuotaSaleItem.objects.filter( transaction__seller_organization=org ).select_related("gov_product", "free_product") products_stats = items.values( product_id=Case( When(gov_product__isnull=False, then="gov_product_id"), When(free_product__isnull=False, then="free_product_id"), ), product_name=Case( When(gov_product__isnull=False, then="gov_product__name"), When(free_product__isnull=False, then="free_product__product__name"), ), product_type=Case( When(gov_product__isnull=False, then=Value("gov")), When(free_product__isnull=False, then=Value("free")), ) ).annotate( total_sales=Count("id"), total_weight=Coalesce(Sum("weight"), 0), # total_price=Coalesce(Sum("total_price"), 0), # avg_unit_price=Coalesce(Sum("total_price") / Sum("weight"), 0), success_sales=Count("id", filter=Q(transaction__transaction_status="success")), failed_sales=Count("id", filter=Q(transaction__transaction_status="failed")), waiting_sales=Count("id", filter=Q(transaction__transaction_status="waiting")), card_payments=Count("id", filter=Q(transaction__price_type="card")), cash_payments=Count("id", filter=Q(transaction__price_type="cash")), check_payments=Count("id", filter=Q(transaction__price_type="check")), credit_payments=Count("id", filter=Q(transaction__price_type="credit")), extra_items=Count("id", filter=Q(is_extra=True)), pre_sale_items=Count("id", filter=Q(is_pre_sale=True)), ).order_by("-total_sales") # calculate sum of item share percentage by product items_by_product = defaultdict(list) for item in items: pid = item.gov_product_id or item.free_product_id items_by_product[pid].append(item) for product in products_stats: pid = product["product_id"] share_totals = defaultdict(lambda: {"total_price": 0, "count": 0}) for item in items_by_product.get(pid, []): if item.item_share: for share in item.item_share: # share: {"name": ..., "price": ..., "shaba": ...} name = share.get("name") price = share.get("price", 0) share_totals[name]["total_price"] += price share_totals[name]["count"] += 1 product["item_share_stats"] = sorted( [ {"name": name, **val} for name, val in share_totals.items() ], key=lambda x: -x["total_price"] ) return { "transaction_summary": transaction_stats, "product_summary": list(products_stats), }