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
|
return True
|
||||||
|
|
||||||
|
|
||||||
def maybe_split_penalties(*, order_id: str, total_amount, reason: str, applicant_id, applicant_identity,
|
def _team_members_for_penalty_split(order_id: str):
|
||||||
affects_grabbing=1, club_id='', extra_fields=None) -> Optional[list]:
|
|
||||||
"""
|
"""
|
||||||
若有有效组队(锁定或招募中已有队友),为每人创建罚单并返回列表;否则返回 None 走原逻辑。
|
取本单应均摊罚款的组队成员。
|
||||||
|
含招募中/已锁定/已结束(商家多在结单后申请罚款;结单会把招募置为已结束)。
|
||||||
|
取消/作废的组队不均摊。
|
||||||
"""
|
"""
|
||||||
from orders.models import TeamRecruit, TeamMember, Penalty
|
from orders.models import TeamRecruit, TeamMember, TeamSettleLedger
|
||||||
from decimal import Decimal as D
|
|
||||||
|
|
||||||
rec = TeamRecruit.query.filter(
|
oid = (order_id or '').strip()
|
||||||
order_id=order_id,
|
if not oid:
|
||||||
status__in=[TeamRecruit.STATUS_RECRUITING, TeamRecruit.STATUS_LOCKED],
|
return None, []
|
||||||
).first()
|
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:
|
if not rec:
|
||||||
return None
|
return None, []
|
||||||
members = list(
|
members = list(
|
||||||
TeamMember.query.filter(recruit_id=rec.id, join_status=TeamMember.JOIN_ACTIVE)
|
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
|
return None
|
||||||
|
|
||||||
total = _d(total_amount)
|
total = _d(total_amount)
|
||||||
k = len(members)
|
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)
|
base = (total / D(k)).quantize(MONEY, rounding=ROUND_DOWN)
|
||||||
amounts = [base] * k
|
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))
|
amounts[0] = _d(total - base * (k - 1))
|
||||||
|
|
||||||
reason_text = f'【组队均摊】{reason or ""}'
|
reason_text = f'【组队均摊】{reason or ""}'
|
||||||
|
|||||||
Reference in New Issue
Block a user