123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561 |
- #coding=utf-8
- import datetime
- from django.db import models
- from django.utils import timezone
- from django.conf import settings
- from apps.exceptions import CustomError
- from apps.account.models import Branch
- from apps.customer.models import Customer, CustomerWechat
- from util.wechatpay import WechatPay, WeChatResponse, SplitAccountFuc
- from util.format import Formater
- class Activity(models.Model):
- branch = models.ForeignKey(Branch, verbose_name=u"门店", on_delete=models.PROTECT)
- title = models.CharField(max_length=200, verbose_name=u"标题")
- describe = models.TextField(verbose_name=u'活动介绍', null=True, blank=True)
- pic = models.CharField(max_length=500,verbose_name=u'活动图片', null=True, blank=True)
- end_date = models.DateField(verbose_name=u"截止日期")
- amount = models.FloatField(verbose_name=u"费用", default=0)
- rebate = models.FloatField(verbose_name=u"返利", default=0)
- check_status = models.PositiveSmallIntegerField(choices=settings.CHECK_STATUS_CHOICES, verbose_name=u"审核", editable=False, default=settings.DEFAULT)
- branch_name = models.CharField(max_length=200, verbose_name=u"门店名称", null=True, blank=True)
- branch_tel = models.CharField(max_length=50, verbose_name=u"门店电话", null=True, blank=True)
- branch_address = models.CharField(max_length=200, verbose_name=u"门店地址", null=True, blank=True)
- required_signs = models.CharField(verbose_name=u'必填项', max_length=300, null=True, editable=False)
- create_time = models.DateTimeField(verbose_name=u"添加时间", auto_now_add=True, editable=False)
- wxapp_img = models.CharField(verbose_name=u'活动二维码', max_length=250, null=True)
- enabled = models.BooleanField(verbose_name=u"在用", default=True)
- delete = models.BooleanField(verbose_name=u"删除", default=False)
- @staticmethod
- def getById(id):
- try:
- id = int(id)
- except:
- raise CustomError(u'无效的活动Id')
- instance = Activity.objects.filter(pk=id).first()
- if not instance:
- raise CustomError(u'未找到相应的活动')
- return instance
- class Meta:
- db_table = "activity"
- ordering = ['-id']
- verbose_name = u"活动管理"
- def checkStatus(self):
- if not self.enabled:
- raise CustomError(u'该活动已禁用!')
- if self.delete:
- raise CustomError(u'该活动已删除!')
- if self.check_status != settings.PASS:
- raise CustomError(u'该活动尚未生效!')
- now = datetime.datetime.date(timezone.now())
- if self.end_date and now > self.end_date:
- raise CustomError(u'该活动已过期!')
- @staticmethod
- def getById(id):
- try:
- id = int(id)
- except:
- raise CustomError(u'无效的活动ID')
- instance = Activity.objects.filter(pk=id, delete=False, enabled=True).first()
- if not instance:
- raise CustomError(u'未找到相应的活动')
- return instance
- class SplitAccount(models.Model):
- WAIT = 1
- PROCESSING = 2
- FINISHED = 3
- STATUS_CHOICES = (
- (WAIT, u'待分账'),
- (PROCESSING, u'处理中'),
- (FINISHED, u'分账完成'),
- )
- branch = models.ForeignKey(Branch, verbose_name=u"门店", on_delete=models.PROTECT, editable=False)
- no = models.CharField(max_length=64, verbose_name=u"单号")
- create_time = models.DateTimeField(verbose_name=u"创建时间", default=timezone.now)
- status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, verbose_name=u"分账状态", default=WAIT)
- amount = models.BigIntegerField(verbose_name=u"分账金额", null=True) # 单位分
- order_id = models.CharField(max_length=100, verbose_name=u"微信分账单号", null=True, editable=False) # 微信分账单号,微信支付系统返回的唯一标识
- class Meta:
- db_table = "split_account"
- verbose_name = u"分账"
- ordering = ('-id',)
- index_together = (
- 'create_time',
- 'status',
- )
- unique_together = (
- 'no',
- )
- default_permissions = ()
- def unfreezeAccount(self):
- # 解冻剩余资金 默认资金冻结30天 如果由于客户或其他原因 无法完成分账 就手动解除资金冻结
- pass
- def handSplitAccont(self, spd, pay, app):
- '''手动申请分账(在自动申请分账失败后 手动申请分账)'''
- spc = SplitAccountFuc(app.authorizer_appid, app.agent_num, app.cert_serial_no, app.apiv3_key)
- # 状态是"待分账" 的查询分账 (状态是默认的怎么处理?没有申请过的 查询分账结果会报错--如果返回错误信息根据错误信息 如果没有发现该订单就重新申请 其他原因就不再申请)
- if spd.status == SplitAccountDetail.PENDING:
- self.splictAccountQuery(spc, pay.transaction_id, self.no, spd)
- if spd.status == SplitAccountDetail.SUCCESS:
- return
- if spd.status == SplitAccountDetail.PENDING:
- return
- spr = SplitAccountReceiver.objects.filter(account=spd.account).first()
- if not spr:
- spr = SplitAccountReceiver.objects.create(account=spd.account)
- # 添加分账接收人
- self.splictAccountAddReceiver(spc, spr)
- # 分账申请
- receivers = [{'account': spd.account, 'amount': spd.amount, 'description': "支付推荐佣金"}]
- result = spc.splitaccount_order(pay.transaction_id, self.no, receivers)
- # 分账请求成功 结果不一定成功
- self.order_id = result.get('order_id')
- if result.get('state') == 'PROCESSING':
- self.status = SplitAccount.PROCESSING
- if result.get('state') == 'FINISHED':
- self.status = SplitAccount.FINISHED
- self.save()
- for item in result.get('receivers'):
- # 现在只对一个推荐人分账
- if item['account'] == spd.account:
- if item['result'] == 'PENDING':
- spd.status = SplitAccountDetail.PENDING
- elif item['result'] == 'SUCCESS':
- spd.finish_time = datetime.datetime.strptime(item['finish_time'], "%Y-%m-%dT%H:%M:%S+08:00")
- spd.status = SplitAccountDetail.SUCCESS
- elif item['result'] == 'CLOSED':
- spd.status = SplitAccountDetail.CLOSED
- spd.fail_reason = item['fail_reason']
- spd.create_time = datetime.datetime.strptime(item['create_time'], "%Y-%m-%dT%H:%M:%S+08:00")
- spd.detail_no = item['detail_id']
- spd.save()
- def updateAccountSplit(self, spd, pay, app):
- spc = SplitAccountFuc(app.authorizer_appid, app.agent_num, app.cert_serial_no, app.apiv3_key)
- self.splictAccountQuery(spc, pay.transaction_id, self.no, spd)
- def splictAccountAddReceiver(self, split_account_func, spr):
- '''添加分账接收人'''
- if spr.status != SplitAccountReceiver.ADD:
- split_account_func.splitaccount_addreceiver(spr.account)
- spr.status = SplitAccountReceiver.ADD
- spr.save()
- def splictAccountQuery(self, split_account_func, transaction_id, out_order_no, detail):
- '''查询分账结果'''
- result = split_account_func.splitaccount_orderquery(transaction_id, out_order_no)
- if result.get('state') == 'PROCESSING':
- self.status = SplitAccount.PROCESSING
- if result.get('state') == 'FINISHED':
- self.status = SplitAccount.FINISHED
- self.save()
- for item in result.get('receivers'):
- if detail and detail.account == item['account']:
- if item['result'] == 'SUCCESS':
- detail.status = SplitAccountDetail.SUCCESS
- detail.finish_time = datetime.datetime.strptime(item['finish_time'], "%Y-%m-%dT%H:%M:%S+08:00")
- elif item['result'] == 'CLOSED':
- detail.status = SplitAccountDetail.CLOSED
- detail.fail_reason = item['fail_reason']
- detail.create_time = datetime.datetime.strptime(item['create_time'], "%Y-%m-%dT%H:%M:%S+08:00")
- detail.detail_no = item['detail_id']
- detail.save()
- def splitAccount(self, app, spd, spr, pay):
- '''自动分账 支付成功后自动分账'''
- if pay.status != Pay.CONFIRM:
- return
- if spd.status != SplitAccountDetail.DEFAULT:
- return
- spc = SplitAccountFuc(app.authorizer_appid, app.agent_num, app.cert_serial_no, app.apiv3_key)
- if spr.status != SplitAccountReceiver.ADD:
- spc.splitaccount_addreceiver(spr.account)
- spr.status = SplitAccountReceiver.ADD
- spr.save()
- receivers = [{'account': spd.account, 'amount': spd.amount, 'description': "支付推荐佣金"}]
- result = spc.splitaccount_order(pay.transaction_id, self.no, receivers)
- self.order_id = result.get('order_id')
- if result.get('state') == 'PROCESSING':
- self.status = SplitAccount.PROCESSING
- if result.get('state') == 'FINISHED':
- self.status = SplitAccount.FINISHED
- self.save()
- for item in result.get('receivers'):
- if item['account'] == spd.account:
- if item['result'] == 'PENDING':
- spd.status = SplitAccountDetail.PENDING
- elif item['result'] == 'SUCCESS':
- spd.status = SplitAccountDetail.SUCCESS
- spd.finish_time = datetime.datetime.strptime(item['finish_time'], "%Y-%m-%dT%H:%M:%S+08:00")
- elif item['result'] == 'CLOSED':
- spd.status = SplitAccountDetail.CLOSED
- spd.fail_reason = item['fail_reason']
- spd.create_time = datetime.datetime.strptime(item['create_time'], "%Y-%m-%dT%H:%M:%S+08:00")
- spd.detail_no = item['detail_id']
- spd.save()
- @staticmethod
- def _addnew(branch, order_id, rebate, customer):
- if rebate <= 0:
- return
- customer_wechat = CustomerWechat.objects.filter(customer=customer).first()
- if not customer_wechat:
- return
- no = "FZ" + timezone.now().strftime('%y%m%d%H%M%S') + str(order_id)
- instance = SplitAccount.objects.create(
- branch=branch,
- no=no,
- status=SplitAccount.WAIT,
- amount=rebate
- )
- detail = SplitAccountDetail.objects.create(
- main=instance,
- customer=customer,
- account=customer_wechat.openid,
- amount=rebate
- )
- receiver = SplitAccountReceiver.objects.filter(account=detail.account).first()
- if not receiver:
- SplitAccountReceiver.objects.create(
- account=detail.account
- )
- return instance, detail, receiver
- class SplitAccountDetail(models.Model):
- DEFAULT = 0
- PENDING = 1
- SUCCESS = 2
- CLOSED = 3
- STATUS_CHOICES = (
- (DEFAULT, u'未处理'),
- (PENDING, u'待分账'),
- (SUCCESS, u'分账成功'),
- (CLOSED, u'已关闭'),
- )
- main = models.ForeignKey(SplitAccount, verbose_name=u'分账单', on_delete=models.PROTECT, editable=False)
- customer = models.ForeignKey(Customer, verbose_name=u'客户', on_delete=models.PROTECT, editable=False)
- account = models.CharField(max_length=100, verbose_name=u'接收账户') # 接受方的openid
- amount = models.BigIntegerField(verbose_name=u'分账金额') # 单位是分
- status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, verbose_name=u"分账结果", default=DEFAULT)
- fail_reason = models.CharField(max_length=1000, verbose_name=u"分账失败原因", null=True, blank=True)
- create_time = models.DateTimeField(verbose_name=u"分账创建时间", null=True, blank=True)
- finish_time = models.DateTimeField(verbose_name=u"分账完成时间", null=True, blank=True)
- detail_no = models.CharField(verbose_name=u'分账明细单号', max_length=100, null=True, editable=False)
- class Meta:
- db_table = "split_account_detail"
- verbose_name = u"分账详细"
- ordering = ('-id',)
- index_together = ('status',)
- default_permissions = ()
- class SplitAccountReceiver(models.Model):
- UN_ADD = 1
- ADD = 2
- DELETE = 3
- STATUS_CHOICES = (
- (UN_ADD, u'待添加'),
- (ADD, u'已添加'),
- (DELETE, u'已删除'),
- )
- account = models.CharField(max_length=100, verbose_name=u'接收账户') # 接受方的openid
- status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, verbose_name=u"状态", default=UN_ADD)
- create_time = models.DateTimeField(verbose_name=u"创建时间", default=timezone.now)
- class Meta:
- db_table = "split_account_receiver"
- verbose_name = u"分账接收人"
- ordering = ('-id',)
- index_together = ('account', 'status', )
- unique_together = (
- 'account',
- )
- default_permissions = ()
- class Pay(models.Model):
- WAIT = 1
- CONFIRM = 2
- CLOSED = 3
- STATUS_CHOICES = (
- (WAIT, u'待付款'),
- (CONFIRM, u'已付款'),
- (CLOSED, u'已关闭'),
- )
- branch = models.ForeignKey(Branch, verbose_name=u"门店", on_delete=models.PROTECT, editable=False)
- no = models.CharField(max_length=64, verbose_name=u"单号")
- transaction_id = models.CharField(max_length=100, verbose_name=u"微信支付订单号", null=True)
- create_time = models.DateTimeField(verbose_name=u"创建时间", default=timezone.now)
- customer = models.ForeignKey(Customer, verbose_name=u'客户', on_delete=models.PROTECT)
- status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, verbose_name=u"支付状态")
- profit_sharing = models.CharField(max_length=10, verbose_name=u"是否分账", default="N")
- precreate_amount = models.BigIntegerField(verbose_name=u"预支付金额") # 单位分
- amount = models.BigIntegerField(verbose_name=u"实际支付金额", null=True) # 单位分
- class Meta:
- db_table = "pay"
- verbose_name = u"支付"
- ordering = ('-id',)
- index_together = (
- 'create_time',
- 'status',
- )
- unique_together = (
- 'no',
- )
- default_permissions = ()
- def payClosed(self):
- if self.status != Pay.WAIT:
- return
- self.status = Pay.CLOSED
- self.save()
- def payConfirm(self, amount, transaction_id):
- if self.status != Pay.WAIT:
- return
- self.status = Pay.CONFIRM
- self.amount = Formater.formatPrice(amount)
- self.transaction_id = transaction_id
- self.save()
- order = Order.objects.filter(pay=self).first()
- if order:
- order.orderPayConfirm(self.amount)
- @staticmethod
- def getByNo(no):
- instance = Pay.objects.filter(no=no).first()
- if not instance:
- raise CustomError(u'未找到相应的支付单!')
- return instance
- @staticmethod
- def wechatPay(app, branch, customer, amount, openid, profit_sharing):
- instance = Pay._addnew(branch, customer, amount, profit_sharing)
- return instance, instance._wechatUnifiedOrder(app, openid)
- @staticmethod
- def _addnew(branch, customer, amount, profit_sharing):
- if amount <= 0:
- raise CustomError(u'无效的付款金额!')
- no = timezone.now().strftime('%y%m%d%H%M%S') + str(customer.id)
- instance = Pay.objects.create(
- branch=branch,
- no=no,
- customer=customer,
- status=Pay.WAIT,
- precreate_amount=amount,
- profit_sharing=profit_sharing
- )
- return instance
- def _wechatUnifiedOrder(self, app, openid):
- wechatpay = WechatPay(app.authorizer_appid, app.agent_num, app.agent_key)
- wechatpay.unifiedOrder(self.no, self.precreate_amount, openid, self.profit_sharing)
- data = wechatpay.getAppString()
- return data
- def split_account(self, app):
- order = Order.objects.filter(pay=self).first()
- if order and order.rebate > 0 and order.recommend_member and self.status == Pay.CONFIRM and self.profit_sharing == "Y":
- sp, spd, spr = SplitAccount._addnew(self.branch, order.id, order.rebate, order.recommend_member)
- order.splitaccount = sp
- order.save()
- sp.splitAccount(app, spd, spr, self)
- class Order(models.Model):
- DEFAULT = 0
- FINISH = 1
- FAIL = 2
- STATUS_CHOICES = (
- (DEFAULT, u'待付款'),
- (FINISH, u'已完成'),
- (FAIL, u'失败'),
- )
- branch = models.ForeignKey(Branch, verbose_name=u"门店", on_delete=models.PROTECT, editable=False)
- activity = models.ForeignKey(Activity, verbose_name=u"活动", on_delete=models.PROTECT)
- pay = models.ForeignKey(Pay, verbose_name='支付信息', on_delete=models.PROTECT, null=True)
- splitaccount = models.ForeignKey(SplitAccount, verbose_name='分账信息', on_delete=models.PROTECT, null=True)
- member = models.ForeignKey(Customer, verbose_name=u"会员", on_delete=models.PROTECT, editable=False)
- amount = models.BigIntegerField(verbose_name=u"费用", default=0) # 单位分
- status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, verbose_name=u"状态", default=DEFAULT, editable=False)
- model = models.CharField(max_length=200, verbose_name=u"车型", null=True, blank=True)
- tel = models.CharField(max_length=100, verbose_name=u"电话", null=True, blank=True)
- number = models.CharField(max_length=50, verbose_name=u"车牌号", null=True, blank=True)
- name = models.CharField(max_length=50, verbose_name=u"姓名", null=True, blank=True)
- recommend_member = models.ForeignKey(Customer, verbose_name=u"推荐人", related_name='recommend_member_ref_customer', on_delete=models.PROTECT, null=True, blank=True)
- rebate = models.BigIntegerField(verbose_name=u"返利", default=0) # 单位分
- create_time = models.DateTimeField(verbose_name=u"添加时间", auto_now_add=True, editable=False)
- delete = models.BooleanField(verbose_name=u"删除", default=False)
- class Meta:
- db_table = "order"
- ordering = ['-id']
- verbose_name = u"订单管理"
- def payOrder(self, openid, app):
- # 如果订单上的金额不等于活动金额 (比如下单没有支付,后来活动金额修改,现在活动金额不等于订单上活动金额) 还按下单时保存到额金额支付
- if self.status != Order.DEFAULT:
- raise CustomError(u'订单非代付款状态,禁止付款')
- if self.pay:
- pay_no = self.pay.no
- # 先查询订单状态
- checkRexponse = WeChatResponse(app.authorizer_appid, app.agent_num, app.agent_key)
- total_fee = checkRexponse.orderquery(pay_no)
- if int(total_fee) == int(self.amount):
- wechatpay = WechatPay(app.authorizer_appid, app.agent_num, app.agent_key)
- wechatpay.unifiedOrder(pay_no, self.amount, openid, self.pay.profit_sharing)
- data = wechatpay.getAppString()
- return data
- self.pay.payClosed()
- profit_sharing = "N"
- if self.rebate > 0 and self.recommend_member:
- profit_sharing = "Y"
- pay, data = Pay.wechatPay(app, self.branch, self.member, self.amount, openid, profit_sharing)
- self.pay = pay
- self.save()
- return data
- def orderPayConfirm(self, amount):
- # 在这进行分账 先去查分账接收人的状态 如果没有就创建一条记录 然后添加分账接收人 然后分账
- # 如果有且状态是已添加 就直接分账
- # 如果有且状态是待添加或已删除 就先添加分账接收人 然后分账
- self.status = Order.FINISH
- self.amount = amount
- self.save()
- coupons = ActivityCoupon.objects.filter(activity=self.activity, coupon__enabled=True)
- now = datetime.datetime.date(timezone.now())
- for item in coupons:
- coupon = item.coupon
- if coupon.off_type == Coupon.FIXED_DATE:
- end_date = coupon.end_date
- else:
- end_date = now + datetime.timedelta(days=coupon.end_days)
- MemberCoupon.objects.create(
- activity=self.activity,
- member=self.member,
- coupon=coupon,
- receive_date=now,
- end_date=end_date
- )
- class Coupon(models.Model):
- FIXED_DATE = 0
- RECEIVE_TIMING = 1
- OFF_TYPE_CHOICES = (
- (FIXED_DATE, u'固定日期'),
- (RECEIVE_TIMING, u'领取计时'),
- )
- branch = models.ForeignKey(Branch, verbose_name=u"门店", on_delete=models.PROTECT)
- name = models.CharField(max_length=200, verbose_name=u"名称")
- off_type = models.PositiveSmallIntegerField(choices=OFF_TYPE_CHOICES, verbose_name=u"核销方式", default=FIXED_DATE)
- end_date = models.DateField(verbose_name=u"截止日期", null=True, blank=True)
- end_days = models.IntegerField(verbose_name=u"截止天数", null=True, blank=True)
- create_time = models.DateTimeField(verbose_name=u"添加时间", auto_now_add=True, editable=False)
- enabled = models.BooleanField(verbose_name=u"在用", default=True)
- class Meta:
- db_table = "coupon"
- ordering = ['-id']
- verbose_name = u"优惠券"
- @staticmethod
- def getById(id):
- try:
- id = int(id)
- except:
- raise CustomError(u'无效的优惠券')
- instance = Coupon.objects.filter(pk=id).first()
- if not instance:
- raise CustomError(u'未找到相应的优惠券')
- return instance
- class MemberCoupon(models.Model):
- activity = models.ForeignKey(Activity, verbose_name=u"活动", on_delete=models.PROTECT)
- member = models.ForeignKey(Customer, verbose_name=u"会员", on_delete=models.PROTECT)
- coupon = models.ForeignKey(Coupon, verbose_name=u"优惠券", on_delete=models.PROTECT)
- receive_date = models.DateField(verbose_name=u"领取日期", null=True, blank=True)
- end_date = models.DateField(verbose_name=u"有效期至", null=True, blank=True)
- write_off = models.BooleanField(verbose_name=u"核销", default=False)
- write_off_time = models.DateTimeField(verbose_name=u"核销时间", editable=False, null=True, blank=True)
- class Meta:
- db_table = "member_coupon"
- ordering = ['-id']
- verbose_name = u"会员优惠券"
- def writeOff(self):
- now = datetime.datetime.date(timezone.now())
- if self.write_off:
- raise CustomError(u'该优惠券已核销,禁止重复使用!')
- if self.end_date and now > self.end_date:
- raise CustomError(u'该优惠券已过有效期,禁止使用!')
- self.write_off = True
- self.write_off_time = timezone.now()
- self.save()
- def cancelWriteOff(self):
- if not self.write_off:
- raise CustomError(u'该优惠券尚未核销,禁止撤销!')
- self.write_off = False
- self.write_off_time = None
- self.save()
- @staticmethod
- def getById(id):
- try:
- id = int(id)
- except:
- raise CustomError(u'无效的会员优惠券ID')
- instance = MemberCoupon.objects.filter(pk=id).first()
- if not instance:
- raise CustomError(u'未找到相应的会员优惠券')
- return instance
- class ActivityCoupon(models.Model):
- activity = models.ForeignKey(Activity, verbose_name=u"活动", on_delete=models.PROTECT)
- coupon = models.ForeignKey(Coupon, verbose_name=u"优惠券", on_delete=models.PROTECT)
- class Meta:
- db_table = "activity_coupon"
- ordering = ['-id']
- verbose_name = u"活动优惠券"
|