118 lines
4.8 KiB
Python
118 lines
4.8 KiB
Python
"""报表模块 Repository 层
|
||
|
||
负责销售绩效报表、业绩统计等报表相关数据的复杂查询操作。
|
||
被 ReportService(报表服务)调用。
|
||
"""
|
||
|
||
from datetime import datetime, timedelta
|
||
|
||
from sqlalchemy import and_, func, not_, or_, select
|
||
from sqlalchemy.orm import Session
|
||
|
||
from backend.app.models.business import Product, ProductCategory, SalesOrder, SalesOrderItem
|
||
|
||
# 电商来源关键词,用于排除电商渠道订单
|
||
ECOMMERCE_SOURCE_KEYWORDS = ["电商", "淘宝", "天猫", "京东", "拼多多", "1688", "线上", "抖音", "快手"]
|
||
|
||
|
||
class ReportRepository:
|
||
"""报表数据仓储
|
||
|
||
封装销售绩效等报表的复杂多表联查操作。
|
||
被 ReportService(报表服务)调用。
|
||
"""
|
||
|
||
def list_performance_rows(self, session: Session, filters: dict) -> list:
|
||
"""查询销售绩效明细数据
|
||
|
||
联表查询销售订单、订单明细、产品和产品分类,
|
||
返回按订单和明细粒度的绩效数据行。
|
||
支持按日期范围、产品分类过滤,以及排除电商渠道订单。
|
||
|
||
参数:
|
||
session: 数据库会话
|
||
filters: 过滤条件字典,支持 start_date / end_date / category_id / exclude_ecommerce
|
||
|
||
返回:
|
||
包含 order_id / created_at / order_source / commission_amount /
|
||
quantity / sale_price / category_id / category_name 的行列表
|
||
"""
|
||
stmt = (
|
||
select(
|
||
SalesOrder.id.label("order_id"),
|
||
SalesOrder.created_at.label("created_at"),
|
||
SalesOrder.order_source.label("order_source"),
|
||
SalesOrder.commission_amount.label("commission_amount"),
|
||
SalesOrder.sale_price_total.label("sale_price_total"),
|
||
SalesOrder.cost_price_total.label("cost_price_total"),
|
||
SalesOrder.profit_total.label("profit_total"),
|
||
SalesOrder.profit_rate.label("profit_rate"),
|
||
SalesOrderItem.quantity.label("quantity"),
|
||
SalesOrderItem.sale_price.label("sale_price"),
|
||
Product.category_id.label("category_id"),
|
||
ProductCategory.category_name.label("category_name"),
|
||
)
|
||
.join(
|
||
SalesOrderItem,
|
||
and_(SalesOrderItem.order_id == SalesOrder.id, SalesOrderItem.deleted == 0),
|
||
)
|
||
.outerjoin(
|
||
Product,
|
||
and_(Product.id == SalesOrderItem.product_id, Product.deleted == 0),
|
||
)
|
||
.outerjoin(
|
||
ProductCategory,
|
||
and_(ProductCategory.id == Product.category_id, ProductCategory.deleted == 0),
|
||
)
|
||
.where(
|
||
SalesOrder.deleted == 0,
|
||
SalesOrder.order_status != "canceled",
|
||
)
|
||
)
|
||
|
||
if filters.get("start_date"):
|
||
stmt = stmt.where(SalesOrder.created_at >= filters["start_date"])
|
||
if filters.get("end_date"):
|
||
stmt = stmt.where(SalesOrder.created_at < filters["end_date"] + timedelta(days=1))
|
||
if filters.get("category_id") is not None:
|
||
stmt = stmt.where(Product.category_id == filters["category_id"])
|
||
if filters.get("exclude_ecommerce"):
|
||
ecommerce_conditions = [
|
||
SalesOrder.order_source.like(f"%{kw}%") for kw in ECOMMERCE_SOURCE_KEYWORDS
|
||
]
|
||
stmt = stmt.where(not_(or_(*ecommerce_conditions)))
|
||
if filters.get("salesman_id") is not None:
|
||
stmt = stmt.where(SalesOrder.salesman_id == filters["salesman_id"])
|
||
|
||
stmt = stmt.order_by(SalesOrder.created_at.asc(), SalesOrder.id.asc(), SalesOrderItem.id.asc())
|
||
return list(session.execute(stmt).all())
|
||
|
||
def count_excluded_orders(self, session: Session, filters: dict) -> int:
|
||
"""统计被电商排除条件筛掉的订单数量
|
||
|
||
参数:
|
||
session: 数据库会话
|
||
filters: 过滤条件字典,需包含 start_date / end_date
|
||
|
||
返回:
|
||
被排除的订单数量
|
||
"""
|
||
stmt = (
|
||
select(func.count(func.distinct(SalesOrder.id)))
|
||
.where(
|
||
SalesOrder.deleted == 0,
|
||
SalesOrder.order_status != "canceled",
|
||
)
|
||
)
|
||
if filters.get("start_date"):
|
||
stmt = stmt.where(SalesOrder.created_at >= filters["start_date"])
|
||
if filters.get("end_date"):
|
||
stmt = stmt.where(SalesOrder.created_at < filters["end_date"] + timedelta(days=1))
|
||
if filters.get("salesman_id") is not None:
|
||
stmt = stmt.where(SalesOrder.salesman_id == filters["salesman_id"])
|
||
ecommerce_conditions = [
|
||
SalesOrder.order_source.like(f"%{kw}%") for kw in ECOMMERCE_SOURCE_KEYWORDS
|
||
]
|
||
stmt = stmt.where(or_(*ecommerce_conditions))
|
||
return session.execute(stmt).scalar() or 0
|