Files
shanxia2.0/backend/app/services/wechat_notice_crm_sources.py
T

97 lines
4.7 KiB
Python

"""CRM/运费审批通知快照;只读业务状态,不发送消息、不提交事务。"""
from urllib.parse import quote
from app.models.crm_models import CrmContract, CrmContractApproval, CrmQuote, CrmQuoteApproval
from app.models.pl_models import PlFreightSettlement, PlFreightSettlementApproval
from app.services.wechat_notice_types import NoticeSnapshot, PendingApprover
_SOURCES = {
"crm_quote": (CrmQuote, CrmQuoteApproval, "quote_id", "quote_no", "报价单审批"),
"crm_contract": (CrmContract, CrmContractApproval, "contract_id", "contract_no", "合同审批"),
"pl_freight": (PlFreightSettlement, PlFreightSettlementApproval, "settlement_id", "settlement_no", "运费结算审批"),
}
# 保留本轮审批结论,后续签订/执行/转合同/发放不撤销已产生的结果通知。
# 合同签订直接进入 executing,当前业务没有 signed 状态。
_APPROVED_LIFECYCLE_STATUSES = {
"crm_quote": {"accepted", "converted", "viewed", "expired"},
"crm_contract": {"approved", "executing", "finished"},
"pl_freight": {"approved", "confirmed"},
}
def get_snapshot(db, biz_type, biz_id, tenant_no):
source = _SOURCES.get(biz_type)
if not source:
return None
model, approval_model, foreign_key, number_field, name = source
document = db.query(model).filter(
model.id == biz_id, model.tenant_no == tenant_no, model.del_flag == 0
).first()
if document is None:
return None
approvals = db.query(approval_model).filter(
getattr(approval_model, foreign_key) == biz_id,
approval_model.tenant_no == tenant_no,
approval_model.del_flag == 0,
).order_by(approval_model.step.asc(), approval_model.create_time.asc(), approval_model.id.asc()).all()
applicant_id = next((row.create_by for row in approvals if row.create_by), document.create_by or "")
started_at = min((row.create_time for row in approvals if row.create_time), default=None)
status = document.settlement_status if biz_type == "pl_freight" else document.status
active = status == ("submitted" if biz_type == "crm_quote" else "approving")
pending = []
if active:
for row in approvals:
if row.approval_result != "pending":
continue
if biz_type == "crm_quote":
if any(prior.step < row.step and prior.approval_result != "approved" for prior in approvals):
continue
elif row.is_current != 1:
continue
pending.append(PendingApprover(task_key=f"{row.id}:{row.approver_id}", user_id=row.approver_id))
result = None
if biz_type == "crm_quote":
if status in _APPROVED_LIFECYCLE_STATUSES[biz_type]:
result = "approved"
elif status == "rejected":
result = "rejected"
elif biz_type == "crm_contract":
if status in _APPROVED_LIFECYCLE_STATUSES[biz_type] and document.approval_status == "approved":
result = "approved"
elif status == "draft" and document.approval_status == "rejected":
result = "rejected"
else:
if status in _APPROVED_LIFECYCLE_STATUSES[biz_type]:
result = "approved"
elif status == "rejected":
result = "rejected"
if result == "approved":
allowed_results = {"approved", "skipped"} if biz_type == "pl_freight" else {"approved"}
# 手工改为 accepted / approved 或旧状态修复不能代替完整的本轮审批。
if not approvals or any(row.approval_result not in allowed_results for row in approvals):
result = None
completed = [row for row in approvals if row.approval_result == result and row.approval_time]
terminal = max(completed, key=lambda row: (row.approval_time, row.step, row.id)) if completed else None
# Old status-only records do not establish a real approval result.
if terminal is None:
result = None
if result:
name = (document.quote_title if biz_type == "crm_quote" else
document.contract_name if biz_type == "crm_contract" else "运费结算单") or name
page = ""
if biz_type != "pl_freight":
domain = "quote" if biz_type == "crm_quote" else "contract"
path = f"/m/crm/{domain}/{quote(str(biz_id), safe='')}"
if active:
path += "?from=approve"
page = "pages/common/mp-webview?path=" + quote(path, safe="")
return NoticeSnapshot(
business_name=name, business_no=getattr(document, number_field) or "",
applicant_id=applicant_id, pending=pending, result=result,
approver_id=terminal.approver_id if terminal else "",
occurred_at=terminal.approval_time if terminal else started_at,
page=page,
)