fix: 组队结单后罚款仍按队员均摊
商家常在结单后申请罚款,招募已是结束态;现含已结束组队,并从结算流水兜底还原队员。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -741,35 +741,75 @@ def settle_team_or_legacy(order, *, set_idle_leader=False) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def maybe_split_penalties(*, order_id: str, total_amount, reason: str, applicant_id, applicant_identity,
|
||||
affects_grabbing=1, club_id='', extra_fields=None) -> Optional[list]:
|
||||
def _team_members_for_penalty_split(order_id: str):
|
||||
"""
|
||||
若有有效组队(锁定或招募中已有队友),为每人创建罚单并返回列表;否则返回 None 走原逻辑。
|
||||
取本单应均摊罚款的组队成员。
|
||||
含招募中/已锁定/已结束(商家多在结单后申请罚款;结单会把招募置为已结束)。
|
||||
取消/作废的组队不均摊。
|
||||
"""
|
||||
from orders.models import TeamRecruit, TeamMember, Penalty
|
||||
from decimal import Decimal as D
|
||||
from orders.models import TeamRecruit, TeamMember, TeamSettleLedger
|
||||
|
||||
rec = TeamRecruit.query.filter(
|
||||
order_id=order_id,
|
||||
status__in=[TeamRecruit.STATUS_RECRUITING, TeamRecruit.STATUS_LOCKED],
|
||||
).first()
|
||||
oid = (order_id or '').strip()
|
||||
if not oid:
|
||||
return None, []
|
||||
rec = (
|
||||
TeamRecruit.query.filter(
|
||||
order_id=oid,
|
||||
status__in=[
|
||||
TeamRecruit.STATUS_RECRUITING,
|
||||
TeamRecruit.STATUS_LOCKED,
|
||||
TeamRecruit.STATUS_FINISHED,
|
||||
],
|
||||
)
|
||||
.order_by('-id')
|
||||
.first()
|
||||
)
|
||||
if not rec:
|
||||
return None
|
||||
return None, []
|
||||
members = list(
|
||||
TeamMember.query.filter(recruit_id=rec.id, join_status=TeamMember.JOIN_ACTIVE)
|
||||
)
|
||||
if len(members) < 2:
|
||||
# 结单后偶发清退:用同单结算流水还原参与人
|
||||
if len(members) < 2 and int(rec.status or 0) == TeamRecruit.STATUS_FINISHED:
|
||||
ledger_uids = list(
|
||||
TeamSettleLedger.query.filter(order_id=oid)
|
||||
.values_list('user_uid', flat=True)
|
||||
.distinct()
|
||||
)
|
||||
if len(ledger_uids) >= 2:
|
||||
members = list(TeamMember.query.filter(recruit_id=rec.id, user_uid__in=ledger_uids))
|
||||
if len(members) < 2:
|
||||
# 无成员行时,用流水 UID 构造轻量对象供均摊
|
||||
class _M:
|
||||
def __init__(self, uid, role):
|
||||
self.user_uid = uid
|
||||
self.role = role
|
||||
leader = rec.leader_uid or ''
|
||||
members = [
|
||||
_M(u, TeamMember.ROLE_LEADER if u == leader else TeamMember.ROLE_MEMBER)
|
||||
for u in ledger_uids
|
||||
]
|
||||
return rec, members
|
||||
|
||||
|
||||
def maybe_split_penalties(*, order_id: str, total_amount, reason: str, applicant_id, applicant_identity,
|
||||
affects_grabbing=1, club_id='', extra_fields=None) -> Optional[list]:
|
||||
"""
|
||||
若有组队且至少 2 人在队(含结单后已结束),为每人创建罚单并返回列表;否则返回 None 走原逻辑。
|
||||
"""
|
||||
from orders.models import Penalty
|
||||
from decimal import Decimal as D
|
||||
|
||||
rec, members = _team_members_for_penalty_split(order_id)
|
||||
if not rec or len(members) < 2:
|
||||
return None
|
||||
|
||||
total = _d(total_amount)
|
||||
k = len(members)
|
||||
# 队长优先拿尾差
|
||||
members.sort(key=lambda m: 0 if getattr(m, 'role', '') == 'leader' else 1)
|
||||
base = (total / D(k)).quantize(MONEY, rounding=ROUND_DOWN)
|
||||
amounts = [base] * k
|
||||
amounts[0] = _d(total - base * (k - 1)) # 尾差给列表第一人(队长优先排前面)
|
||||
|
||||
# 队长优先
|
||||
members.sort(key=lambda m: 0 if m.role == TeamMember.ROLE_LEADER else 1)
|
||||
amounts = [base] * k
|
||||
amounts[0] = _d(total - base * (k - 1))
|
||||
|
||||
reason_text = f'【组队均摊】{reason or ""}'
|
||||
|
||||
Reference in New Issue
Block a user