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