123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- # 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()
|