# coding=utf-8 import json import datetime from django.utils import timezone from django.db.models import Q from apps.rebate.models import CashRebate, PointRebate, CashRebateLog, PointRebateLog, PointGive, PointGiveLog, TransferCashRebate, TransferCashRebateDetail, CashTransferLog from apps.customer.models import SuperiorDistributor from apps.order.models import Order from apps.config.models import Config from apps.WechatApplet.models import WechatApplet from apps.customer.models import CustomerWechat from utils.wechatpayv3.transfer import Transfer from utils.exceptions import CustomError class CustomerRebate(object): def __init__(self, pay): self.pay = pay self.order = Order.objects.filter(pay=self.pay).first() def cash_rebate(self, ratio, amount, customer): ''' 计算现金返利 1、2、3级分销商 返现金 :param ratio: :param amount: :param customer: :return: ''' rebate = CashRebate.objects.create( order=self.order, ratio=ratio, amount=amount, customer=customer, create_time=timezone.now() ) CashRebateLog.addnew(rebate) def point_rebate(self, ratio, amount, customer): ''' 计算积分返利 4、5级分销商 返积分 :param ratio: :param amount: :param customer: :return: ''' rebate = PointRebate.objects.create( order=self.order, ratio=ratio, amount=amount, customer=customer, create_time=timezone.now() ) PointRebateLog.addnew(rebate) def point_give(self, ratio, amount, customer): ''' 计算购买商品赠送的积分 购买商品时根据后台设置的比例赠送相应的积分给购买者 :param ratio: :param amount: :param customer: :return: ''' give = PointGive.objects.create( order=self.order, ratio=ratio, amount=amount, customer=customer, create_time=timezone.now() ) PointGiveLog.addnew(give) def calcul_rebate(self): ''' 计算返利并保存log :return: ''' if not self.order: return if self.order.total_amount <= 0: return if self.order.status == Order.WAIT_PAY or self.order.status == Order.CANCEL: return customer = self.order.customer sd = SuperiorDistributor.objects.filter(customer=customer).first() if not sd: return # 判断是首次购买还是再次购买 首次购买与再次购买返利的比例不同 count = Order.objects.filter(customer=customer, status__in=[Order.WAIT_DISPATCH, Order.CONFIRM_DISPATCH]).count() rebate_dic = {} if count > 1: rebate_dic = Config.get_rebate(False) elif count == 1: rebate_dic = Config.get_rebate(True) # 现金保留两位小数, 积分整数 total_amount = float(self.order.total_amount or 0) if sd.one_level and sd.one_level.is_distributor and rebate_dic['one_level'] > 0: amount = int(round(total_amount * rebate_dic['one_level'] / 100.0, 0)) self.cash_rebate(rebate_dic['one_level'], amount, sd.one_level) if sd.two_level and sd.two_level.is_distributor and rebate_dic['two_level'] > 0: amount = int(round(total_amount * rebate_dic['two_level']/ 100.0, 0)) self.cash_rebate(rebate_dic['two_level'], amount, sd.two_level) if sd.three_level and sd.three_level.is_distributor and rebate_dic['three_level'] > 0: amount = int(round(total_amount * rebate_dic['three_level'] / 100.0, 0)) self.cash_rebate(rebate_dic['three_level'], amount, sd.three_level) if sd.four_level and sd.four_level.is_distributor and rebate_dic['four_level'] > 0: amount = int(round((total_amount / 100.0) * (rebate_dic['four_level'] / 100.0), 0)) self.point_rebate(rebate_dic['four_level'], amount, sd.four_level) if sd.five_level and sd.five_level.is_distributor and rebate_dic['five_level'] > 0: amount = int(round((total_amount / 100.0) * (rebate_dic['five_level'] / 100.0), 0)) self.point_rebate(rebate_dic['five_level'], amount, sd.five_level) # 计算购买商品 赠送积分 rule = Config.get_value(Config.KEY_POINT_RULE) if rule > 0: amount = int(round((total_amount / 100.0) * (rule / 100.0), 0)) self.point_give(rule, amount, customer) class CustomerRebateTransfer(object): def __init__(self, order): wx = WechatApplet.objects.filter().first() if not wx: raise Exception(u'小程序appid认证失败!') self.order = order self.wx = wx def apply_transfer_again(self, instance): '''转账批次明细失败 重新申请新的批次进行转账 要判断该明细有没有已经申请重新转账 和 判断用户余额''' count = TransferCashRebateDetail.objects.filter(Q(rebate=instance.rebate), ~Q(status=TransferCashRebateDetail.FAIL)).count() if count: raise CustomError(u'该转账明细,正在支付,禁止重复申请!') if instance.customer.balance < instance.amount: raise CustomError(u'申请转账金额超出客户[{}]余额,禁止申请!'.format(instance.customer.name)) if instance.amount > self.order.total_amount: raise CustomError(u'转账总金额大于收款总金额!') if instance.amount == 0: raise CustomError(u'转账总金额为0!') if instance.rebate.status != CashRebate.DEFAULT: raise CustomError(u'该返利已转账,禁止重复申请!') instance.rebate.status = CashRebate.TRANSFERING instance.rebate.save() tc = TransferCashRebate.objects.create(order=instance.main.order, transfer_num=1, transfer_amount=instance.amount) tc_no = 'TC{}{:0>4}'.format(timezone.now().strftime('%Y%m%d%H%M%S'), tc.id) tc.no = tc_no tc.save() tcd = TransferCashRebateDetail.objects.create(main=tc, rebate=instance.rebate, amount=instance.amount, customer=instance.customer) tcd_no = 'TCD{}{:0>4}'.format(timezone.now().strftime('%Y%m%d%H%M%S'), tcd.id) tcd.no = tcd_no tcd.save() return tc def create_transfer(self): ''' 转账的时候创建转账批次单 和转账批次单明细 如果是没有转账批次的 添加新的转账批次和转账批次明细 如果有转账批次的 只创建失败的 要判断收款人的余额 如果订单已经申请过转账批次 就不能再申请了 只能在付款失败的批次明细里边重新申请(没有考虑订单关闭的请框) :return: ''' count = TransferCashRebate.objects.filter(order=self.order).count() if count: raise CustomError(u'该返利已申请转账,禁止重复申请!') tc = TransferCashRebate.objects.create(order=self.order) tc_no = 'TC{}{:0>4}'.format(timezone.now().strftime('%Y%m%d%H%M%S'), tc.id) rows = CashRebate.objects.filter(order=self.order, status=CashRebate.DEFAULT) transfer_amount = 0 transfer_num = 0 for row in rows: if row.customer.balance < row.amount: raise CustomError(u'客户[{}]余额不足!'.format(row.customer.name)) row.status = CashRebate.TRANSFERING row.save() transfer_num += 1 transfer_amount += row.amount tcd = TransferCashRebateDetail.objects.create(main=tc, rebate=row, amount=row.amount, customer=row.customer) tcd_no = 'TCD{}{:0>4}'.format(timezone.now().strftime('%Y%m%d%H%M%S'), tcd.id) tcd.no = tcd_no tcd.save() if transfer_amount > self.order.total_amount: raise CustomError(u'转账总金额大于收款总金额!') if transfer_num == 0: raise CustomError(u'转账总笔数为0!') if transfer_amount == 0: raise CustomError(u'转账总金额为0!') tc.no = tc_no tc.transfer_num = transfer_num tc.transfer_amount = transfer_amount tc.save() return tc def transfer_rebate(self, detail=None): ''' 现金返利转账申请 :return: ''' if detail: tc = self.apply_transfer_again(detail) else: tc = self.create_transfer() transfer_detail_list = [] rows = TransferCashRebateDetail.objects.filter(main=tc) for row in rows: wx_c = CustomerWechat.objects.filter(wechat_app=self.wx, customer=row.customer).first() if not wx_c: continue item = { 'out_detail_no': row.no, 'transfer_amount': row.amount, 'transfer_remark': '订单{}返利'.format(self.order.no), 'openid': wx_c.openid } # 注意 明细转账金额 大于等于 2000 收款用户姓名必须 transfer_detail_list.append(item) appid = self.wx.authorizer_appid out_batch_no = self.order.no batch_name = '订单{}返利'.format(self.order.no) batch_remark = '订单{}返利'.format(self.order.no) total_amount = tc.transfer_amount total_num = tc.transfer_num t = Transfer(self.wx) code, message = t.transfer_batch(appid, out_batch_no, batch_name, batch_remark, total_amount, total_num, transfer_detail_list) result = json.loads(message) if code != 200: raise Exception(u'[{}]转账申请失败!原因:{}'.format(self.order.no, result)) batch_id = result.get('batch_id') create_time = result.get('create_time') tc.transfer_status = TransferCashRebate.ACCEPTED if batch_id: tc.batch_id = batch_id if create_time: tc.transfer_time = datetime.datetime.strptime(create_time, "%Y-%m-%dT%H:%M:%S+08:00") tc.save() TransferCashRebateDetail.objects.filter(main=tc).update(status=TransferCashRebateDetail.PENDING) def transfer_refresh(transfer): # 如果转账批次单 已经受理30天了 中间没有刷新 状态是已受理 然后受理时间到当前时间超过30 wx = WechatApplet.objects.filter().first() if not wx: raise Exception(u'小程序appid认证失败!') t = Transfer(wx) code, message = t.transfer_query_out_batch_no(transfer.no, need_query_detail=False, offset=0, limit=20, detail_status='ALL') result = json.loads(message) if code != 200: # 如果查询结果 NOT_FOUND 记录不存在 且转账批次单的状态是已申请 把状态改成 未处理 重新申请 raise Exception(u'转账[{}]查询失败!原因:{}'.format(transfer.no, result)) transfer_batch = result.get('transfer_batch') if not transfer_batch: return batch_id = transfer_batch.get('batch_id') batch_status = transfer_batch.get('batch_status') close_reason = transfer_batch.get('close_reason') create_time = transfer_batch.get('create_time') update_time = transfer_batch.get('update_time') success_amount = transfer_batch.get('success_amount') success_num = transfer_batch.get('success_num') fail_amount = transfer_batch.get('fail_amount') fail_num = transfer_batch.get('fail_num') if batch_id and not transfer.batch_id: transfer.batch_id = batch_id if batch_status: if batch_status == 'WAIT_PAY': transfer.transfer_status = TransferCashRebate.WAIT_PAY elif batch_status == 'ACCEPTED': transfer.transfer_status = TransferCashRebate.ACCEPTED elif batch_status == 'PROCESSING': transfer.transfer_status = TransferCashRebate.PROCESSING elif batch_status == 'FINISHED': transfer.transfer_status = TransferCashRebate.FINISHED elif batch_status == 'CLOSED': transfer.transfer_status = TransferCashRebate.CLOSED if close_reason: transfer.close_reason = close_reason if create_time and not transfer.transfer_time: transfer.transfer_time = datetime.datetime.strptime(create_time, "%Y-%m-%dT%H:%M:%S+08:00") if update_time: transfer.update_time = datetime.datetime.strptime(update_time, "%Y-%m-%dT%H:%M:%S+08:00") if success_amount: transfer.success_amount = success_amount if success_num: transfer.success_num = success_num if fail_amount: transfer.fail_amount = fail_amount if fail_num: transfer.fail_num = fail_num transfer.save() # 转账结束的时候 自动更新明细信息 if batch_status and batch_status == 'FINISHED': detail = TransferCashRebateDetail.objects.filter(main=transfer, status__in=[TransferCashRebateDetail.DEFAULT, TransferCashRebateDetail.PENDING]) for item in detail: transfer_detail_refresh(item) def transfer_detail_refresh(detail): wx = WechatApplet.objects.filter().first() if not wx: raise Exception(u'小程序appid认证失败!') out_detail_no = detail.no or None out_batch_no = detail.main and detail.main.no or None t = Transfer(wx) code, message = t.transfer_query_out_detail_no(out_detail_no, out_batch_no) result = json.loads(message) if code != 200: raise Exception(u'转账明细[{}]查询失败!原因:{}'.format(out_detail_no, result)) # 更新明细状态 detail_id = result.get('detail_id') # 微信明细单号 detail_status = result.get('detail_status') # 明细装填 PROCESSING:转账中。正在处理中,转账结果尚未明确 SUCCESS:转账成功 FAIL:转账失败。需要确认失败原因后,再决定是否重新发起对该笔明细单的转账(并非整个转账批次单) fail_reason = result.get('fail_reason') # 失败原因 initiate_time = result.get('initiate_time') # 转账发起时间 update_time = result.get('update_time') # 明细更新时间 if detail_id: detail.detail_no = detail_id if detail_status: if detail_status == 'PROCESSING': detail.status = TransferCashRebateDetail.PENDING elif detail_status == 'SUCCESS': detail.status = TransferCashRebateDetail.SUCCESS # 在这将收款人的余额减掉相应的金额 # 转账已经成功了 就算是客户的余额是负数 也要统计成负数 转出去的钱不能返回了 所以只有在创建转账明细的时候要控制 转账金额不能大于客户余额 CashTransferLog.addnew(detail) elif detail_status == 'FAIL': detail.status = TransferCashRebateDetail.FAIL # 转账失败 将返利表状态改回 未处理 后边手动处理或重新申请转账都可以 detail.rebate.status = CashRebate.DEFAULT detail.rebate.save() if fail_reason: detail.fail_reason = fail_reason if initiate_time: detail.initiate_time = datetime.datetime.strptime(initiate_time, "%Y-%m-%dT%H:%M:%S+08:00") if update_time: detail.update_time = datetime.datetime.strptime(update_time, "%Y-%m-%dT%H:%M:%S+08:00") detail.save()