dingdanquanliucheng/backend/app/repositories/order_repository.py

264 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
订单数据访问层。
负责封装销售订单SalesOrder、订单明细SalesOrderItem
供应商Supplier及供应商短信确认日志OrderSupplierTextLog的数据库查询操作。
提供订单的增删改查、状态流转、取消流程、供应商确认等功能。
被 OrderService、LogisticsService 调用。
"""
from datetime import datetime
from sqlalchemy import func, select
from sqlalchemy.orm import Session
from backend.app.models.business import OrderSupplierTextLog, SalesOrder, SalesOrderItem, Supplier
class OrderRepository:
"""订单数据访问层,封装销售订单及其相关表的数据库操作。
被 OrderService、LogisticsService 调用。
"""
def list_orders(self, session: Session) -> list[SalesOrder]:
"""查询所有未删除的订单列表(不做分页),按 id 降序排列。
:param session: 数据库会话
:return: 订单列表
被 OrderService 内部使用。
"""
stmt = select(SalesOrder).where(SalesOrder.deleted == 0).order_by(SalesOrder.id.desc())
return list(session.execute(stmt).scalars())
def list_orders_by_filters(self, session: Session, filters: dict) -> list[SalesOrder]:
"""根据筛选条件查询订单列表,支持订单号、状态、客户名、手机号、业务员、工厂、来源、
时间范围等条件的模糊/精确匹配,并支持分页。
:param session: 数据库会话
:param filters: 筛选条件字典,可包含 order_no、order_status、customer_name、
customer_mobile、salesman_id、factory_id、order_source、
start_time、end_time、page_no、page_size 等键
:return: 字典,包含 total总数和 list订单列表若未传分页参数则 total 为 None
被 OrderService.list_orders 调用。
"""
stmt = select(SalesOrder).where(SalesOrder.deleted == 0)
if filters.get("order_no"):
stmt = stmt.where(SalesOrder.order_no.contains(filters["order_no"]))
if filters.get("order_status"):
stmt = stmt.where(SalesOrder.order_status == filters["order_status"])
if filters.get("customer_name"):
stmt = stmt.where(SalesOrder.customer_name.contains(filters["customer_name"]))
if filters.get("customer_mobile"):
stmt = stmt.where(SalesOrder.customer_mobile.contains(filters["customer_mobile"]))
if filters.get("salesman_id") is not None:
stmt = stmt.where(SalesOrder.salesman_id == filters["salesman_id"])
if filters.get("factory_id") is not None:
stmt = stmt.where(SalesOrder.factory_id == filters["factory_id"])
if filters.get("order_source"):
stmt = stmt.where(SalesOrder.order_source == filters["order_source"])
if filters.get("order_type"):
stmt = stmt.where(SalesOrder.order_type == filters["order_type"])
if filters.get("need_invoice") is not None:
stmt = stmt.where(SalesOrder.need_invoice == filters["need_invoice"])
if filters.get("start_time"):
start_time = datetime.strptime(filters["start_time"], "%Y-%m-%d %H:%M:%S")
stmt = stmt.where(SalesOrder.created_at >= start_time)
if filters.get("end_time"):
end_time = datetime.strptime(filters["end_time"], "%Y-%m-%d %H:%M:%S")
stmt = stmt.where(SalesOrder.created_at <= end_time)
stmt = stmt.order_by(SalesOrder.id.desc())
page_no = filters.get("page_no")
page_size = filters.get("page_size")
if page_no is not None and page_size is not None:
count_stmt = select(func.count()).select_from(stmt.subquery())
total = session.execute(count_stmt).scalar() or 0
offset = max(page_no - 1, 0) * page_size
stmt = stmt.offset(offset).limit(page_size)
rows = list(session.execute(stmt).scalars())
return {"total": total, "list": rows}
return {"total": None, "list": list(session.execute(stmt).scalars())}
def get_order(self, session: Session, order_id: int) -> SalesOrder | None:
"""根据 ID 获取单个订单详情。
:param session: 数据库会话
:param order_id: 订单主键 ID
:return: 订单对象,不存在则返回 None
被 OrderService.get_order 等方法调用。
"""
stmt = select(SalesOrder).where(SalesOrder.id == order_id, SalesOrder.deleted == 0)
return session.execute(stmt).scalar_one_or_none()
def get_supplier(self, session: Session, supplier_id: int | None) -> Supplier | None:
"""根据供应商 ID 获取供应商信息。
:param session: 数据库会话
:param supplier_id: 供应商主键 ID为 None 时直接返回 None
:return: 供应商对象,不存在则返回 None
被 OrderService 在查询订单时关联获取供应商信息调用。
"""
if supplier_id is None:
return None
stmt = select(Supplier).where(Supplier.id == supplier_id, Supplier.deleted == 0)
return session.execute(stmt).scalar_one_or_none()
def list_order_items(self, session: Session, order_id: int) -> list[SalesOrderItem]:
"""查询指定订单下的所有订单明细项。
:param session: 数据库会话
:param order_id: 订单主键 ID
:return: 订单明细项列表
被 OrderService.get_order、create_order 等方法调用。
"""
stmt = select(SalesOrderItem).where(SalesOrderItem.order_id == order_id, SalesOrderItem.deleted == 0)
return list(session.execute(stmt).scalars())
def create_order(self, session: Session, payload: dict, items: list[dict]) -> SalesOrder:
"""创建订单及其明细项(在一个事务中完成)。
:param session: 数据库会话
:param payload: 订单主表字段字典
:param items: 订单明细项字段字典列表
:return: 新创建的订单对象(含自增 ID 和关联的明细项)
被 OrderService.create_order 调用。
"""
order = SalesOrder(**payload)
session.add(order)
session.flush()
for item_payload in items:
session.add(SalesOrderItem(order_id=order.id, **item_payload))
session.flush()
return order
def update_order_with_items(self, session: Session, order: SalesOrder, payload: dict, items: list[dict]) -> SalesOrder:
"""更新订单主表信息并替换其全部明细项(先删后插)。
:param session: 数据库会话
:param order: 待更新的订单对象
:param payload: 订单主表更新字段字典
:param items: 新的订单明细项字段字典列表(将完全替换原有明细)
:return: 更新后的订单对象
被 OrderService.update_order 调用。
"""
order.customer_id = payload["customer_id"]
order.customer_name = payload["customer_name"]
order.customer_mobile = payload["customer_mobile"]
order.customer_address = payload.get("customer_address")
order.salesman_id = payload.get("salesman_id")
order.order_source = payload.get("order_source")
order.delivery_type = payload.get("delivery_type")
order.factory_id = payload.get("factory_id")
order.contract_amount = payload.get("contract_amount", 0)
order.sale_price_total = payload["sale_price_total"]
order.cost_price_total = payload["cost_price_total"]
order.rebate_total = payload["rebate_total"]
order.freight_total = payload["freight_total"]
order.tax_total = payload["tax_total"]
order.other_fee_total = payload["other_fee_total"]
order.profit_total = payload["profit_total"]
order.profit_rate = payload["profit_rate"]
order.commission_amount = payload["commission_amount"]
order.payment_method = payload.get("payment_method")
order.tax_amount = payload.get("tax_amount", 0)
order.need_invoice = payload.get("need_invoice", 0)
order.order_type = payload.get("order_type")
order.self_delivery = payload.get("self_delivery", 0)
order.tracking_number = payload.get("tracking_number")
order.remark = payload.get("remark")
session.add(order)
session.flush()
session.query(SalesOrderItem).filter(SalesOrderItem.order_id == order.id, SalesOrderItem.deleted == 0).delete(synchronize_session=False)
for item_payload in items:
session.add(SalesOrderItem(order_id=order.id, **item_payload))
session.flush()
return order
def update_order_status(self, session: Session, order: SalesOrder, target_status: str) -> SalesOrder:
"""更新订单状态(用于流程流转)。
:param session: 数据库会话
:param order: 订单对象
:param target_status: 目标状态值
:return: 更新后的订单对象
被 OrderService 中各状态流转方法approve、reject 等)调用。
"""
order.order_status = target_status
session.add(order)
session.flush()
return order
def update_cancel_fields(self, session: Session, order: SalesOrder, payload: dict) -> SalesOrder:
"""更新订单的取消相关信息(取消请求发起时调用)。
:param session: 数据库会话
:param order: 订单对象
:param payload: 取消相关字段字典,包含 order_status、cancel_requested_by、
cancel_requested_at、cancel_reason、cancel_opinion、
cancel_previous_status 等键
:return: 更新后的订单对象
被 OrderService.cancel_order 调用。
"""
order.order_status = payload["order_status"]
order.cancel_requested_by = payload.get("cancel_requested_by")
order.cancel_requested_at = payload.get("cancel_requested_at")
order.cancel_reason = payload["cancel_reason"]
order.cancel_opinion = payload.get("cancel_opinion")
order.cancel_previous_status = payload.get("cancel_previous_status")
session.add(order)
session.flush()
return order
def clear_cancel_request(self, session: Session, order: SalesOrder) -> SalesOrder:
"""清除订单的取消请求信息(审批驳回取消请求时调用)。
:param session: 数据库会话
:param order: 订单对象
:return: 清除取消信息后的订单对象
被 OrderService.reject_cancel_order 调用。
"""
order.cancel_requested_by = None
order.cancel_requested_at = None
order.cancel_reason = None
order.cancel_opinion = None
order.cancel_previous_status = None
session.add(order)
session.flush()
return order
def update_supplier_text_confirm(self, session: Session, order: SalesOrder, operator_id: int | None = None) -> SalesOrder:
"""确认供应商短信通知,将订单状态流转为 pending_logistics待绑定物流单号
:param session: 数据库会话
:param order: 订单对象
:param operator_id: 操作人 ID可选
:return: 更新后的订单对象
被 OrderService.confirm_supplier_text 调用。
"""
order.order_status = "pending_logistics"
order.supplier_text_confirmed_at = datetime.now()
order.supplier_text_confirmed_by = operator_id
session.add(order)
session.flush()
return order
def create_supplier_text_log(self, session: Session, payload: dict) -> OrderSupplierTextLog:
"""创建供应商短信确认日志记录。
:param session: 数据库会话
:param payload: 日志字段字典
:return: 新创建的日志对象
被 OrderService.confirm_supplier_text 调用,记录短信确认操作。
"""
log = OrderSupplierTextLog(**payload)
session.add(log)
session.flush()
return log