254 lines
9.5 KiB
Python
254 lines
9.5 KiB
Python
# yonghu/fadan_fenhong_utils.py
|
||
# 罚款缴纳分红处理公共方法(支持更新罚单中的申请人分红金额)
|
||
|
||
import logging
|
||
from decimal import Decimal
|
||
from django.db import transaction
|
||
|
||
from orders.models import Penalty, PenaltyBonus, PenaltyBonusRate
|
||
from users.models import UserShangjia
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
# 身份映射字典
|
||
IDENTITY_MAP = {
|
||
1: {'fenhong': 2, 'lilv': 1}, # 客服
|
||
2: {'fenhong': 5, 'lilv': 2}, # 售后
|
||
3: {'fenhong': 3, 'lilv': 3}, # 管理员
|
||
4: {'fenhong': 4, 'lilv': 4}, # 店铺
|
||
5: {'fenhong': 1, 'lilv': 5}, # 商家
|
||
}
|
||
|
||
MERCHANT_LILV_SHENFEN = 5
|
||
MERCHANT_FENHONG_SHENFEN = 1
|
||
|
||
|
||
def _normalize_club_id(club_id):
|
||
cid = (club_id or '').strip()
|
||
return cid or 'xq'
|
||
|
||
|
||
def _read_global_fenhong_rate(lilv_shenfen):
|
||
try:
|
||
global_rate = PenaltyBonusRate.objects.get(Identity=lilv_shenfen)
|
||
rate = global_rate.Rate
|
||
if rate is not None and Decimal(str(rate)) > 0:
|
||
return rate
|
||
except PenaltyBonusRate.DoesNotExist:
|
||
pass
|
||
return None
|
||
|
||
|
||
def _get_fenhong_rate(fenhongzhe_id, fenhong_shenfen, lilv_shenfen, club_id='xq'):
|
||
"""
|
||
获取分红利率:个人单独利率 > 0 时优先;否则回退全局 fadan_fenhong_lilv。
|
||
"""
|
||
club_id = _normalize_club_id(club_id)
|
||
try:
|
||
individual = PenaltyBonus.objects.get(
|
||
BeneficiaryID=fenhongzhe_id,
|
||
Identity=fenhong_shenfen,
|
||
ClubID=club_id,
|
||
IsIndividualRate=True,
|
||
)
|
||
ind_rate = individual.IndividualRate
|
||
if ind_rate is not None and Decimal(str(ind_rate)) > 0:
|
||
return ind_rate
|
||
except PenaltyBonus.DoesNotExist:
|
||
pass
|
||
|
||
return _read_global_fenhong_rate(lilv_shenfen)
|
||
|
||
|
||
def _calc_bonus_amount(fine_amount, rate):
|
||
if fine_amount is None or rate is None:
|
||
return Decimal('0.00')
|
||
fine = Decimal(str(fine_amount))
|
||
r = Decimal(str(rate))
|
||
if fine <= 0 or r <= 0:
|
||
return Decimal('0.00')
|
||
return (fine * r).quantize(Decimal('0.01'))
|
||
|
||
|
||
def lock_penalty_bonus(fadan):
|
||
"""
|
||
申请/改价/再次申请时锁定应得分红利率与金额,写入罚单供前端展示;
|
||
缴纳时按此锁定值入账,不再现场重算。
|
||
"""
|
||
if getattr(fadan, 'BonusCredited', False):
|
||
return fadan
|
||
|
||
zero_rate = Decimal('0.0000')
|
||
zero_amount = Decimal('0.00')
|
||
applicant_id = fadan.ApplicantID
|
||
src_shenfen = fadan.ApplicantIdentity
|
||
|
||
if not applicant_id or src_shenfen not in IDENTITY_MAP:
|
||
fadan.ApplicantBonusRate = zero_rate
|
||
fadan.ApplicantBonusAmount = zero_amount
|
||
fadan.save(update_fields=['ApplicantBonusRate', 'ApplicantBonusAmount'])
|
||
return fadan
|
||
|
||
mapped = IDENTITY_MAP[src_shenfen]
|
||
club_id = _normalize_club_id(fadan.ClubID)
|
||
rate = _get_fenhong_rate(
|
||
applicant_id, mapped['fenhong'], mapped['lilv'], club_id=club_id,
|
||
)
|
||
if rate is None:
|
||
fadan.ApplicantBonusRate = zero_rate
|
||
fadan.ApplicantBonusAmount = zero_amount
|
||
else:
|
||
fadan.ApplicantBonusRate = Decimal(str(rate))
|
||
fadan.ApplicantBonusAmount = _calc_bonus_amount(fadan.FineAmount, rate)
|
||
|
||
fadan.save(update_fields=['ApplicantBonusRate', 'ApplicantBonusAmount'])
|
||
return fadan
|
||
|
||
|
||
def get_merchant_fenhong_lilv(yonghuid, club_id='xq'):
|
||
"""商家申请罚单页预览利率(尚无罚单时)。"""
|
||
club_id = _normalize_club_id(club_id)
|
||
rate = _get_fenhong_rate(
|
||
yonghuid,
|
||
MERCHANT_FENHONG_SHENFEN,
|
||
MERCHANT_LILV_SHENFEN,
|
||
club_id=club_id,
|
||
)
|
||
return float(rate) if rate is not None else 0.0
|
||
|
||
|
||
def process_fadan_fenhong(fadan_id):
|
||
"""
|
||
罚款缴纳后的分红入账:优先使用申请时已锁定的利率/金额,不现场重算。
|
||
"""
|
||
try:
|
||
try:
|
||
fadan = Penalty.objects.get(id=fadan_id, Status=2)
|
||
except Penalty.DoesNotExist:
|
||
logger.error(f"罚单 {fadan_id} 不存在或状态不是已缴纳")
|
||
return False, "罚单不存在或状态异常"
|
||
|
||
if fadan.BonusCredited:
|
||
logger.info(f"罚单 {fadan_id} 分红已入账,跳过")
|
||
return True, "已分红"
|
||
|
||
shenqing_chufa = fadan.ApplicantID
|
||
src_shenfen = fadan.ApplicantIdentity
|
||
|
||
if not shenqing_chufa:
|
||
logger.info(f"罚单 {fadan_id} 无申请处罚者,不进行分红")
|
||
return True, "无申请处罚者,无需分红"
|
||
|
||
if src_shenfen not in IDENTITY_MAP:
|
||
logger.error(f"未知的申请处罚者身份: {src_shenfen},罚单 {fadan_id}")
|
||
return False, f"未知身份 {src_shenfen}"
|
||
|
||
mapped = IDENTITY_MAP[src_shenfen]
|
||
fenhong_shenfen = mapped['fenhong']
|
||
club_id = _normalize_club_id(fadan.ClubID)
|
||
|
||
locked_amount = Decimal(str(fadan.ApplicantBonusAmount or 0))
|
||
locked_rate = fadan.ApplicantBonusRate
|
||
|
||
if locked_amount > 0:
|
||
fenhong_jine = locked_amount
|
||
rate = locked_rate
|
||
else:
|
||
# 旧数据兼容:未锁定时按当前利率现场算一次并回写
|
||
fakuan_jine = fadan.FineAmount
|
||
if fakuan_jine <= 0:
|
||
fadan.ApplicantBonusAmount = Decimal('0.00')
|
||
fadan.save(update_fields=['ApplicantBonusAmount'])
|
||
return True, "罚款金额为0,无分红"
|
||
|
||
rate = _get_fenhong_rate(
|
||
shenqing_chufa, fenhong_shenfen, mapped['lilv'], club_id=club_id,
|
||
)
|
||
if rate is None:
|
||
logger.warning(f"罚单 {fadan_id} 未配置分红利率")
|
||
fadan.ApplicantBonusAmount = Decimal('0.00')
|
||
fadan.save(update_fields=['ApplicantBonusAmount'])
|
||
return True, "未配置分红利率,无分红"
|
||
|
||
fenhong_jine = _calc_bonus_amount(fakuan_jine, rate)
|
||
fadan.ApplicantBonusRate = Decimal(str(rate))
|
||
fadan.ApplicantBonusAmount = fenhong_jine
|
||
fadan.save(update_fields=['ApplicantBonusRate', 'ApplicantBonusAmount'])
|
||
|
||
if fenhong_jine <= 0:
|
||
logger.info(f"罚单 {fadan_id} 锁定分红金额为0,无需入账")
|
||
fadan.BonusCredited = True
|
||
fadan.save(update_fields=['BonusCredited'])
|
||
return True, "分红金额为0"
|
||
|
||
if src_shenfen == 5:
|
||
with transaction.atomic():
|
||
fadan = Penalty.objects.select_for_update().get(id=fadan_id, Status=2)
|
||
if fadan.BonusCredited:
|
||
return True, "已分红"
|
||
|
||
if not UserShangjia.objects.filter(user__UserUID=shenqing_chufa).exists():
|
||
logger.error(f"商家 {shenqing_chufa} 扩展表不存在,无法分红入账")
|
||
return False, "商家资料不存在,无法分红"
|
||
|
||
fenhong_record, _created = PenaltyBonus.objects.get_or_create(
|
||
BeneficiaryID=shenqing_chufa,
|
||
Identity=fenhong_shenfen,
|
||
ClubID=club_id,
|
||
defaults={
|
||
'TotalAmount': Decimal('0.00'),
|
||
'WithdrawableAmount': Decimal('0.00'),
|
||
'BonusCount': 0,
|
||
'IndividualRate': rate,
|
||
'IsIndividualRate': False,
|
||
},
|
||
)
|
||
fenhong_record.TotalAmount += fenhong_jine
|
||
fenhong_record.BonusCount += 1
|
||
fenhong_record.save()
|
||
|
||
shangjia = UserShangjia.objects.select_for_update().get(user__UserUID=shenqing_chufa)
|
||
shangjia.yue += fenhong_jine
|
||
shangjia.save(update_fields=['yue'])
|
||
logger.info(f"商家 {shenqing_chufa} 余额增加 {fenhong_jine} 元(锁定值)")
|
||
|
||
fadan.BonusCredited = True
|
||
fadan.save(update_fields=['BonusCredited'])
|
||
|
||
else:
|
||
with transaction.atomic():
|
||
fadan = Penalty.objects.select_for_update().get(id=fadan_id, Status=2)
|
||
if fadan.BonusCredited:
|
||
return True, "已分红"
|
||
|
||
fenhong_record, _created = PenaltyBonus.objects.get_or_create(
|
||
BeneficiaryID=shenqing_chufa,
|
||
Identity=fenhong_shenfen,
|
||
ClubID=club_id,
|
||
defaults={
|
||
'TotalAmount': Decimal('0.00'),
|
||
'WithdrawableAmount': Decimal('0.00'),
|
||
'BonusCount': 0,
|
||
'IndividualRate': rate,
|
||
'IsIndividualRate': False,
|
||
},
|
||
)
|
||
fenhong_record.TotalAmount += fenhong_jine
|
||
fenhong_record.WithdrawableAmount += fenhong_jine
|
||
fenhong_record.BonusCount += 1
|
||
fenhong_record.save()
|
||
|
||
fadan.BonusCredited = True
|
||
fadan.save(update_fields=['BonusCredited'])
|
||
|
||
logger.info(
|
||
f"罚单 {fadan_id} 分红入账成功: 申请者 {shenqing_chufa}, "
|
||
f"锁定金额 {fenhong_jine} 元, 锁定利率 {rate}"
|
||
)
|
||
return True, "分红处理成功"
|
||
|
||
except Exception as e:
|
||
logger.error(f"罚单 {fadan_id} 分红处理异常: {str(e)}", exc_info=True)
|
||
return False, f"分红处理异常: {str(e)}"
|