"""往来单位服务:开单取价 + 信用额度。 取价优先级(quote_price): 1. 客户专属价 CustomerProductPrice 2. 价格等级价 PriceLevel.discount_rate × 默认售价(rate>0 且 <1 时生效) 3. 最近成交价(该客户+商品最近一张已过账销售单行单价) 4. 默认售价 Product.sale_price """ from __future__ import annotations from decimal import Decimal from .models import Customer, CustomerProductPrice, PriceLevel def quote_price(*, tenant, customer: Customer, product) -> dict: """返回 {price, source, last_price, min_price, max_price}。 price 为基本单位价;last/min/max 为该客户历史成交参考(基本单位口径)。 """ # 1. 客户专属价 cpp = CustomerProductPrice.objects.filter( tenant=tenant, customer=customer, product=product ).first() if cpp is not None: price, source = cpp.price, "customer" else: # 2. 价格等级价 level: PriceLevel | None = customer.price_level rate = level.discount_rate if level else Decimal("0") if rate and Decimal("0") < rate < Decimal("1"): price, source = product.sale_price * rate, "level" else: # 3. 最近成交价 last = _history(tenant, customer, product).first() if last is not None: price, source = last.unit_price, "last" else: price, source = product.sale_price, "default" hist = list(_history(tenant, customer, product).values_list( "unit_price", flat=True )[:50]) return { "price": price, "source": source, "last_price": hist[0] if hist else None, "min_price": min(hist) if hist else None, "max_price": max(hist) if hist else None, } def _history(tenant, customer: Customer, product): """该客户+商品的已过账销售行(新→旧)。""" from apps.sales.models import SalesBillLine return ( SalesBillLine.objects.filter( tenant=tenant, bill__customer=customer, bill__state="confirmed", product=product, ) .order_by("-bill_id", "-id") ) def credit_usage(*, tenant, customer: Customer) -> dict: """客户信用额度占用:{limit, outstanding, available}。 limit=0 表示未启用管控;available = limit - outstanding(未启用时为 None)。 """ from django.db.models import Sum from apps.finance.models import Receivable agg = Receivable.objects.filter( tenant=tenant, customer=customer, status__in=["open", "partial"], is_deleted=False, ).aggregate(total=Sum("total_amount"), paid=Sum("paid_amount")) outstanding = ((agg["total"] or Decimal("0")) - (agg["paid"] or Decimal("0"))).quantize( Decimal("0.0001") ) limit = customer.credit_limit or Decimal("0") return { "limit": limit, "outstanding": outstanding, "available": (limit - outstanding) if limit > 0 else None, } def check_credit(*, tenant, customer: Customer, extra_amount: Decimal) -> dict: """下单/过账前检查额度。返回 usage;超限抛 CreditLimitExceeded。""" from apps.sales.services import CreditLimitExceeded usage = credit_usage(tenant=tenant, customer=customer) if usage["limit"] <= 0: return usage if usage["outstanding"] + extra_amount > usage["limit"]: raise CreditLimitExceeded( customer.code, usage["outstanding"], usage["limit"], extra_amount ) return usage