"""回款人工确认门槛;与生成凭证、保存资料和工资审核分别管理。""" import hashlib import json from datetime import date from decimal import Decimal from sqlalchemy import select from app.core.exceptions import BadRequestError from app.models import Receipt from app.services.param_service import resolve_snapshot CONFIRMATION_PARAM = "receipt.confirmation.enabled" def enabled(session) -> bool: """操作开关取当前生效值,不按历史到账日绕过本次人工确认门槛。""" return resolve_snapshot(session, date.today()).boolean(CONFIRMATION_PARAM) def revision(receipt) -> str: """只绑定经办人核对的业务内容;备注和同步时间不使确认无故失效。""" values = [ receipt.id, receipt.business_id, receipt.shipment_id, receipt.receipt_date.isoformat(), format(Decimal(receipt.amount).quantize(Decimal("0.01")), "f"), receipt.is_barter, receipt.t_plus_id, receipt.source_no, ] return hashlib.sha256(json.dumps(values, ensure_ascii=False).encode()).hexdigest() def is_confirmed(receipt) -> bool: return bool( receipt.confirmation_status == 1 and receipt.confirmed_by and receipt.confirmed_at and receipt.confirmed_revision == revision(receipt) ) def invalidate(receipt) -> None: receipt.confirmation_status = 0 receipt.confirmed_by = receipt.confirmed_at = receipt.confirmed_revision = None def ensure_confirmed(session, receipt_ids) -> None: """在原核算事务内锁定并核实回款;关闭只跳过人工门槛,不改变凭证资格。""" ids = sorted(set(receipt_ids)) if not ids or not enabled(session): return rows = list(session.scalars( select(Receipt).where(Receipt.id.in_(ids)).order_by(Receipt.id) .with_for_update().execution_options(populate_existing=True) )) if len(rows) != len(ids) or any(not is_confirmed(row) for row in rows): raise BadRequestError("存在未人工确认或确认后内容已变化的回款,请先在回款管理逐笔确认")