"""欠款管理服务模块 负责订单欠款记录的自动生成。 当订单状态流转到已发货/已送达等阶段时,根据客户的结算天数和结算起始方式, 自动创建欠款记录并计算到期日。 被调用方:被 order_service(订单状态变更时)和 logistics_service(物流状态变更时)调用。 """ from datetime import date, timedelta from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session from backend.app.repositories.config_repository import ConfigRepository from backend.app.repositories.reminder_repository import ReminderRepository class ArrearsService: """欠款业务服务。 依赖: - ConfigRepository:读取欠款生成模式配置。 - ReminderRepository:欠款记录的查询和创建、客户信息查询。 """ def __init__(self) -> None: self.config_repository = ConfigRepository() self.reminder_repository = ReminderRepository() def sync_order_arrears(self, session: Session, order) -> None: """根据订单状态同步生成欠款记录。 根据系统配置的触发模式(已发货或已送达),判断是否需要生成欠款。 若订单已有欠款记录则跳过;若金额为 0 也跳过。 根据客户的结算天数和结算起始方式计算到期日。 参数: session: 数据库会话(调用方负责 commit/rollback)。 order: 订单模型对象,需包含 id、order_status、customer_id、sale_price_total、created_at 等字段。 被调用方:order_service(订单状态变更时)、logistics_service(物流状态变更时)。 """ trigger_mode = self._get_config_value(session, "arrears_generate_mode", "pending_logistics") if not self._should_generate(trigger_mode, order.order_status): return existed = self.reminder_repository.get_arrears_by_order_id(session, order.id) if existed is not None: return settlement_days = 0 customer = None settlement_start_type = "order_date" if order.customer_id: customer = self.reminder_repository.get_customer(session, order.customer_id) settlement_days = int(customer.settlement_days or 0) if customer else 0 # 从系统配置获取欠款起算节点,而非客户字段 settlement_start_type = "order_date" settlement_start_date = self._resolve_settlement_start_date(order, settlement_start_type) due_date = settlement_start_date + timedelta(days=max(settlement_days, 0)) arrears_amount = float(order.contract_amount or 0) if arrears_amount <= 0: return self.reminder_repository.create_arrears( session, { "customer_id": order.customer_id or 0, "order_id": order.id, "arrears_amount": arrears_amount, "settlement_start_date": settlement_start_date, "due_date": due_date, "status": "pending", }, ) def _resolve_settlement_start_date(self, order, settlement_start_type: str) -> date: """根据结算起始方式确定欠款计算的起始日期。 参数: order: 订单模型对象。 settlement_start_type: 结算起始方式,'order_date' 取订单创建日期, 'delivery_date' 取供应商确认日期。 返回: 结算起始日期,无法确定时默认使用今天。 """ if settlement_start_type == "order_date": if order.created_at: return order.created_at.date() return date.today() if settlement_start_type == "delivery_date": if order.supplier_text_confirmed_at: return order.supplier_text_confirmed_at.date() return date.today() return date.today() def _get_config_value(self, session: Session, config_key: str, default: str) -> str: """从系统配置表读取配置值,不存在时返回默认值。 参数: session: 数据库会话。 config_key: 配置键名。 default: 当配置不存在或为空时的默认值。 返回: 配置值字符串。 """ config = self.config_repository.get_by_key(session, config_key) return config.config_value if config is not None and config.config_value else default def _should_generate(self, trigger_mode: str, order_status: str) -> bool: """判断当前订单状态是否满足欠款生成条件。 参数: trigger_mode: 触发模式,'shipped'(已发货)或 'pending_logistics'(待绑定物流单号)。 order_status: 订单当前状态。 返回: 满足条件返回 True,否则返回 False。 """ if trigger_mode == "shipped": return order_status in {"shipped", "pending_logistics", "in_transit", "completed", "settled"} return order_status in {"pending_logistics", "in_transit", "completed", "settled"} arrears_service = ArrearsService()