def amortize(self, amortization, savepoint=True): with transaction.atomic(savepoint=savepoint): if amortization.settled: raise P2PException('amortization %s already settled.' % amortization) sub_amortizations = amortization.subs.all() description = unicode(amortization) catalog = u'分期还款' product = amortization.product matches = re.search(u'日计息', product.pay_method) if matches and matches.group(): pname = u"%s,期限%s天" % (product.name, product.period) else: pname = u"%s,期限%s个月" % (product.name, product.period) phone_list = list() message_list = list() settled_sub_amos = list() for sub_amo in sub_amortizations: user_margin_keeper = MarginKeeper(sub_amo.user) user_margin_keeper.amortize(sub_amo.principal, sub_amo.interest, sub_amo.penal_interest, sub_amo.coupon_interest, savepoint=False, description=description) sub_amo.settled = True sub_amo.settlement_time = timezone.now() sub_amo.save() amo_amount = sub_amo.principal + sub_amo.interest + sub_amo.penal_interest + sub_amo.coupon_interest # 加入重复回款的用户还需要扣回的金额及扣款操作 try: repeat_user = RepeatPaymentUser.objects.select_for_update()\ .filter(user_id=sub_amo.user.id, amount__gt=0).first() if repeat_user: repeat_amount = repeat_user.amount # if repeat_amount > 0: # 判断是否每天从回款中扣款 is_every_day = True if not repeat_user.is_every_day: product_ids = repeat_user.product_ids.split(',') product_ids = [ int(p_id) for p_id in product_ids if p_id.strip() != '' ] # 判断当期还款的产品id是否在用户新购标的id中, 不在说明该标不用扣款 if product.id not in product_ids: is_every_day = False if is_every_day: # 判断剩余应扣金额是否大于等于本期回款本息之合, 大于等于,则扣本息,否则扣剩余应扣金额 if repeat_amount >= amo_amount: reduce_amount = amo_amount reduce_amount_current = repeat_amount - amo_amount # 剩余应扣金额-本次扣除的本息之合 else: reduce_amount = repeat_amount reduce_amount_current = 0 print( "repeat_amount: %s, amo_amount: %s, type1:%s, type2:%s" % (repeat_amount, amo_amount, type(repeat_amount), type(amo_amount))) # 减账户余额 user_margin_keeper.reduce_margin( reduce_amount, u'系统重复回款扣回%s元' % reduce_amount) # 更新剩余应扣金额 repeat_user.amount = reduce_amount_current repeat_user.save() # 记录扣款流水 repeat_record = RepeatPaymentUserRecords( user_id=sub_amo.user.id, name=sub_amo.user.wanglibaouserprofile.name, phone=sub_amo.user.wanglibaouserprofile.phone, amount=reduce_amount, amount_current=reduce_amount_current, description=description) repeat_record.save() except Exception: logger.exception('err') logger.error("用户扣款失败,用户id:[%s], 回款本息合计:[%s]" % (sub_amo.user, amo_amount)) pass try: phone_list.append(sub_amo.user.wanglibaouserprofile.phone) message_list.append( messages.product_amortize( sub_amo.user.wanglibaouserprofile.name, amortization.product, # sub_amo.settlement_time, amo_amount)) title, content = messages.msg_bid_amortize( pname, timezone.now(), amo_amount) inside_message.send_one.apply_async( kwargs={ "user_id": sub_amo.user.id, "title": title, "content": content, "mtype": "amortize" }) except: logger.debug("") self.__tracer(catalog, sub_amo.user, sub_amo.principal, sub_amo.interest, sub_amo.penal_interest, amortization, description, sub_amo.coupon_interest) # 标的每一期还款完成后,检测该用户还款的本金是否有符合活动的规则,有的话触发活动规则 try: if sub_amo.principal > 0: # activity_backends.check_activity(sub_amo.user, 'repaid', 'pc', sub_amo.principal, product.id) check_activity_task.apply_async(kwargs={ "user_id": sub_amo.user.id, "trigger_node": 'repaid', "device_type": 'pc', "amount": sub_amo.principal, "product_id": product.id, }, queue='celery02') except Exception: logger.debug( "check activity on repaid, user: {}, principal: {}, product_id: {}" .format(sub_amo.user.id, sub_amo.principal, product.id)) try: weixin_user = WeixinUser.objects.filter( user=sub_amo.user).first() if weixin_user and weixin_user.subscribe: now = datetime.now().strftime('%Y年%m月%d日 %H:%M:%S') sentTemplate.apply_async(kwargs={ "kwargs": json.dumps({ "openid": weixin_user.openid, "template_id": PRODUCT_AMORTIZATION_TEMPLATE_ID, "keyword1": product.name, "keyword2": "%s 元" % str(amo_amount), "keyword3": now, }) }, queue='celery02') except Exception, e: logger.debug( ">>>> weixin msg send err, user_id:[%s], [%s] " % sub_amo.user.id, e) pass # 添加还款用户到渠道通知列表 @chenwb try: settled_sub_amos.append({ 'id': sub_amo.id, 'user': sub_amo.user.id, 'product': product.id, 'term': sub_amo.term, 'settled': sub_amo.settled, 'term_date': sub_amo.term_date.strftime('%Y-%m-%d %H:%M:%S'), 'settlement_time': sub_amo.settlement_time.strftime('%Y-%m-%d %H:%M:%S'), 'principal': float(sub_amo.principal), 'interest': float(sub_amo.interest), 'penal_interest': float(sub_amo.penal_interest), 'coupon_interest': float(sub_amo.coupon_interest), 'description': sub_amo.description, }) except: pass amortization.settled = True amortization.save() catalog = u'还款入账' send_messages.apply_async(kwargs={ "phones": phone_list, "messages": message_list }) self.__tracer(catalog, None, amortization.principal, amortization.interest, amortization.penal_interest, amortization) # 向渠道中心发送还款用户列表通知 @chenwb # Comment by hb on 2016-05-13 try: if settled_sub_amos: from .tasks import coop_amortizations_push coop_amortizations_push.apply_async(kwargs={ 'amortizations': settled_sub_amos, 'product_id': product.id, 'amo_act': 'amortize' }, queue='celery02') except: pass
def prepayment(self, penal_interest, repayment_type, payment_date, savepoint=True): """ #1. 拿到当期未还款计划 #1.11 如果是按期提前还款 #1.12 利息 = 当期利息 #1.21 如果是按日提前还款 #1.22 利息 = 年利率/360*计息天数*本金 #2. 拿到此标的年华收益 #3. 计算日收益 #4. 计算当期未计息天数 """ pname = '' amortization_records_tmp = list() phone_list = list() message_list = list() with transaction.atomic(savepoint=savepoint): amortization = ProductAmortization.objects.select_for_update().get(id=self.amortization.id) if amortization.settled: raise PrepaymentException() # 1.生成产品提前还款记录 # amortization = self.amortization product_record = self.get_product_repayment(penal_interest, repayment_type, payment_date) order_id = OrderHelper.place_order(None, order_type=self.catalog, product_id=self.product.id, status=u'新建').id product_record.order_id = order_id amortization_records = list() # 用户还款计划 user_amortizations = amortization.subs.all().select_related('user__wanglibaouserprofile') product = amortization.product import re matches = re.search(u'日计息', product.pay_method) if matches and matches.group(): pname = u"%s,期限%s天" % (product.name, product.period) else: pname = u"%s,期限%s个月" % (product.name, product.period) for user_amortization in user_amortizations: logger.error("提前还款用户: [%s], [%s]" % (user_amortization.user.id, user_amortization.user.wanglibaouserprofile.phone)) # 计算最终计算提前还款的本金, 利息, 罚息, 加息 user_record = self.get_user_repayment(user_amortization, penal_interest, repayment_type, payment_date) logger.error("新的本金: [%s], 利息:[%s], 加息:[%s], repayment_type:[%s], payment_date:[%s]" % ( user_record.principal, user_record.interest, user_record.coupon_interest, repayment_type, payment_date)) user_margin_keeper = MarginKeeper(user_record.user) # 提前还款需要将加息金额还给用户(重新计算后的该用户所用加息券的加息金额) user_margin_keeper.amortize(user_record.principal, user_record.interest, user_record.penal_interest, user_record.coupon_interest, savepoint=False, description=self.description) order_id = OrderHelper.place_order(user_record.user, order_type=self.catalog, product_id=self.product.id, status=u'新建').id user_record.order_id = order_id user_record.amortization = amortization amortization_records.append(user_record) amortization_records_tmp.append(user_record) # 发消息临时list变量(不包含product_record) logger.error("order_id: %s" % order_id) amortization_records.append(product_record) AmortizationRecord.objects.bulk_create(amortization_records) ProductAmortization.objects.filter(product=self.product, settled=False)\ .update(settled=True, settlement_time=timezone.now()) UserAmortization.objects.filter(product_amortization__product=self.product, settled=False)\ .update(settled=True, settlement_time=timezone.now()) ProductKeeper(self.product).finish(None) # 将提前还款的消息发送放到事务外边 for user_record in amortization_records_tmp: try: # 提前还款短信 # 提前还款金额 = 本金 + 利息 + 罚息 + 加息 amo_amount = user_record.principal + user_record.interest + \ user_record.penal_interest + user_record.coupon_interest user = user_record.user user_profile = user.wanglibaouserprofile phone_list.append(user_profile.phone) message_list.append(messages.product_prepayment(user_profile.name, amortization.product, amo_amount)) # 标的每一期还款完成后,检测该用户还款的本金是否有符合活动的规则,有的话触发活动规则 try: if user_record.principal > 0: # activity_backends.check_activity(user, 'repaid', 'pc', user_record.principal, product.id) check_activity_task.apply_async(kwargs={ "user_id": user.id, "trigger_node": 'repaid', "device_type": 'pc', "amount": user_record.principal, "product_id": product.id }, queue='celery02') except Exception: logger.exception("==提前还款==活动检测==") logger.debug("提前还款, user: {}, principal: {}, product_id: {}".format( user, user_record.principal, product.id )) try: # 提前还款站内信 title, content = messages.msg_bid_prepayment(pname, timezone.now(), amo_amount) inside_message.send_one.apply_async(kwargs={ "user_id": user.id, "title": title, "content": content, "mtype": "amortize" }) except Exception, e: logger.exception("==提前还款==站内信==") logger.debug(("user:%s====提前还款==站内信==" % user.id) + traceback.format_exc()) try: weixin_user = WeixinUser.objects.filter(user=user).first() if weixin_user and weixin_user.subscribe: now = datetime.now().strftime('%Y年%m月%d日 %H:%M:%S') sentTemplate.apply_async(kwargs={ "kwargs": json.dumps({ "openid": weixin_user.openid, "template_id": PRODUCT_AMORTIZATION_TEMPLATE_ID, "keyword1": product.name, "keyword2": "%s 元" % str(amo_amount), "keyword3": now, }) }, queue='celery02') except Exception, e: logger.exception("==提前还款==微信模板==") logger.debug(("user:%s====提前还款==微信模板==" % user.id) + traceback.format_exc())