|
@@ -1,11 +1,20 @@
|
|
|
# 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
|
|
|
+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):
|
|
@@ -116,3 +125,221 @@ class CustomerRebate(object):
|
|
|
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!')
|
|
|
+
|
|
|
+ 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)
|
|
|
+ transfer_amount = 0
|
|
|
+ transfer_num = 0
|
|
|
+ for row in rows:
|
|
|
+ if row.customer.balance < row.amount:
|
|
|
+ raise CustomError(u'客户[{}]余额不足!'.format(row.customer.name))
|
|
|
+ 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
|
|
|
+
|
|
|
+ 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()
|