fix: 组队仅队长提交修改,组员可见交付;罚单聚合改撤联动

详情返回 can_submit/can_modify;交付图按订单聚合;组队均摊罚单展示总额并联动修改/撤销;罚单 ClubID 归属订单店。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-07-27 08:05:34 +08:00
parent c5ceacf3d3
commit 7d366e8075
4 changed files with 275 additions and 73 deletions

View File

@@ -385,9 +385,23 @@ class ShangjiaFakuaiXiugaiView(APIView):
dingdan.save()
return Response({'code': 0, 'msg': '退款理由修改成功'})
def _team_split_siblings(self, fadan, *, include_rejected=False):
"""同订单组队均摊罚单;非均摊则只返回自身。"""
from orders.services.team_recruit import list_team_split_penalties
reason = fadan.Reason or ''
if reason.startswith('【组队均摊】') and fadan.RelatedOrderID:
sibs = list_team_split_penalties(
fadan.RelatedOrderID, include_rejected=include_rejected,
)
if sibs:
return sibs
return [fadan]
def _modify_penalty(self, fadan, liyou, jine_str, processor_id=None):
"""修改罚款(待缴纳 / 申诉中 / 平台审核中)"""
"""修改罚款(待缴纳 / 申诉中 / 平台审核中);组队均摊联动改总额并重分。"""
from utils.penalty_status import PENALTY_PENDING_PAY, PENALTY_APPEALING, PENALTY_PLATFORM_AUDIT
from orders.services.team_recruit import _d, MONEY
from decimal import ROUND_DOWN
if fadan.Status not in (PENALTY_PENDING_PAY, PENALTY_APPEALING, PENALTY_PLATFORM_AUDIT):
return Response({'code': 400, 'msg': '当前罚单状态不可修改'})
if not liyou.strip():
@@ -396,37 +410,70 @@ class ShangjiaFakuaiXiugaiView(APIView):
jine = Decimal(jine_str)
if jine <= 0:
raise ValueError
except:
except Exception:
return Response({'code': 400, 'msg': '罚款金额必须为正数'})
siblings = self._team_split_siblings(fadan, include_rejected=False)
for s in siblings:
if s.Status not in (PENALTY_PENDING_PAY, PENALTY_APPEALING, PENALTY_PLATFORM_AUDIT):
return Response({'code': 400, 'msg': '组队均摊罚单状态不一致,无法修改'})
reason_text = liyou[:500]
if (fadan.Reason or '').startswith('【组队均摊】') and not reason_text.startswith('【组队均摊】'):
reason_text = f'【组队均摊】{reason_text}'
with transaction.atomic():
fadan.Reason = liyou[:500]
fadan.FineAmount = jine
if processor_id:
fadan.ProcessorID = processor_id
fadan.ProcessorIdentity = 5 # 小程序商家自行处理
fadan.save()
lock_penalty_bonus(fadan)
k = len(siblings)
if k > 1:
total = _d(jine)
base = (total / Decimal(k)).quantize(MONEY, rounding=ROUND_DOWN)
amounts = [base] * k
amounts[0] = _d(total - base * (k - 1))
# 队长优先(与创建时一致)
siblings = sorted(
siblings,
key=lambda p: 0 if p.id == fadan.id else 1,
)
for i, s in enumerate(siblings):
s.Reason = reason_text
s.FineAmount = amounts[i]
if processor_id:
s.ProcessorID = processor_id
s.ProcessorIdentity = 5
s.save()
lock_penalty_bonus(s)
else:
fadan.Reason = reason_text
fadan.FineAmount = jine
if processor_id:
fadan.ProcessorID = processor_id
fadan.ProcessorIdentity = 5
fadan.save()
lock_penalty_bonus(fadan)
return Response({'code': 0, 'msg': '罚款修改成功'})
def _cancel_penalty(self, fadan, liyou, processor_id=None):
"""撤销罚款(待缴纳 / 申诉中 / 平台审核中 → 已驳回)"""
"""撤销罚款;组队均摊一并驳回,避免只撤一张仍挡抢单。"""
from utils.penalty_status import PENALTY_PENDING_PAY, PENALTY_APPEALING, PENALTY_PLATFORM_AUDIT, PENALTY_REJECTED
if fadan.Status not in (PENALTY_PENDING_PAY, PENALTY_APPEALING, PENALTY_PLATFORM_AUDIT):
return Response({'code': 400, 'msg': '当前罚单状态不可撤销'})
if not liyou.strip():
return Response({'code': 400, 'msg': '驳回理由不能为空'})
siblings = self._team_split_siblings(fadan, include_rejected=False)
with transaction.atomic():
fadan.Status = PENALTY_REJECTED # 已驳回
fadan.RejectReason = liyou[:500]
if processor_id:
fadan.ProcessorID = processor_id
fadan.ProcessorIdentity = 5 # 小程序商家撤销=商家作为处理人
fadan.save()
for s in siblings:
if s.Status not in (PENALTY_PENDING_PAY, PENALTY_APPEALING, PENALTY_PLATFORM_AUDIT):
continue
s.Status = PENALTY_REJECTED
s.RejectReason = liyou[:500]
if processor_id:
s.ProcessorID = processor_id
s.ProcessorIdentity = 5
s.save()
return Response({'code': 0, 'msg': '罚款已撤销(驳回)'})
def _resubmit_penalty(self, fadan, liyou, jine_str, processor_id=None):
"""再次申请罚款(已驳回 → 平台审核中)"""
"""再次申请罚款;组队均摊联动总额重分。"""
from utils.penalty_status import PENALTY_REJECTED, PENALTY_PLATFORM_AUDIT
from orders.services.team_recruit import _d, MONEY
from decimal import ROUND_DOWN
if fadan.Status != PENALTY_REJECTED:
return Response({'code': 400, 'msg': '只有已驳回的罚单可再次申请'})
if not liyou.strip():
@@ -435,19 +482,33 @@ class ShangjiaFakuaiXiugaiView(APIView):
jine = Decimal(jine_str)
if jine <= 0:
raise ValueError
except:
except Exception:
return Response({'code': 400, 'msg': '罚款金额必须为正数'})
siblings = self._team_split_siblings(fadan, include_rejected=True)
siblings = [s for s in siblings if s.Status == PENALTY_REJECTED] or [fadan]
reason_text = liyou[:500]
if (fadan.Reason or '').startswith('【组队均摊】') and not reason_text.startswith('【组队均摊】'):
reason_text = f'【组队均摊】{reason_text}'
with transaction.atomic():
fadan.Status = PENALTY_PLATFORM_AUDIT
fadan.Reason = liyou[:500]
fadan.FineAmount = jine
fadan.RejectReason = '' # 清空驳回理由
if processor_id:
fadan.ProcessorID = processor_id
fadan.ProcessorIdentity = 5
fadan.save()
lock_penalty_bonus(fadan)
logger.info(f"商家重新申请罚单{fadan.id},金额={jine}")
k = len(siblings)
total = _d(jine)
if k > 1:
base = (total / Decimal(k)).quantize(MONEY, rounding=ROUND_DOWN)
amounts = [base] * k
amounts[0] = _d(total - base * (k - 1))
else:
amounts = [total]
for i, s in enumerate(siblings):
s.Status = PENALTY_PLATFORM_AUDIT
s.Reason = reason_text
s.FineAmount = amounts[i] if i < len(amounts) else amounts[-1]
s.RejectReason = ''
if processor_id:
s.ProcessorID = processor_id
s.ProcessorIdentity = 5
s.save()
lock_penalty_bonus(s)
logger.info(f"商家重新申请组队罚单 order={fadan.RelatedOrderID} 总额={jine} n={k}")
return Response({'code': 0, 'msg': '已重新申请罚款'})
def _manage_penalty_evidence(self, fadan, data):