Files
Django/users/fadan_fenhong_utils.py

260 lines
10 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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__)
# 身份映射字典
# 罚单 ApplicantIdentityshenqingren_shenfen→ fadan_fenhong 表 Identity + fadan_fenhong_lilv 表 shenfen
# 罚单侧1=客服 2=售后 3=管理员 4=店铺 5=商家
# fadan_fenhong 表1=商家 2=客服 3=平台管理者 4=店铺 5=售后
IDENTITY_MAP = {
1: {'fenhong': 2, 'lilv': 1}, # 客服 → 分红记录身份2利率表 shenfen=1
2: {'fenhong': 5, 'lilv': 2}, # 售后 → 分红记录身份5利率表 shenfen=2
3: {'fenhong': 3, 'lilv': 3}, # 管理员
4: {'fenhong': 4, 'lilv': 4}, # 店铺
5: {'fenhong': 1, 'lilv': 5}, # 商家 → 分红记录身份1利率表 shenfen=5钱进 user_shangjia.yue
}
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 or (
locked_rate is not None and Decimal(str(locked_rate)) > 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)}"