dingdanquanliucheng/backend/app/services/arrears_service.py

130 lines
5.2 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.

"""欠款管理服务模块
负责订单欠款记录的自动生成。
当订单状态流转到已发货/已送达等阶段时,根据客户的结算天数和结算起始方式,
自动创建欠款记录并计算到期日。
被调用方:被 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()