lyh 1 rok pred
rodič
commit
6a0acb47ea

+ 1 - 0
apps/customer/models.py

@@ -124,6 +124,7 @@ class CustomerVehicle(models.Model):
     vin = models.CharField(max_length=20)
     number = models.CharField(max_length=20)
     model = models.CharField(max_length=200)
+    delete = models.BooleanField(verbose_name=u"删除", default=False)
 
     class Meta:
         db_table = 'customer_vehicle'

+ 2 - 0
apps/foundation/models.py

@@ -124,6 +124,8 @@ class Config(models.Model):
     KEY_ABUTMENT_XGJ = "abutment_xgj" # 对接销管佳
     KEY_XGJ_SESSION_KEY = "xgj_session_key" # 销管佳密钥
     KEY_XGJ_IP = "xgj_ip" # 销管佳地址
+    KEY_WECHAT_TENANT_NUM = "wechat_tenant_num" # 商户号
+    KEY_WECHAT_TENANT_KEY = "wechat_tenant_key" # 商户密钥
 
     property = models.CharField(max_length=100, verbose_name=u'属性')
     value = models.TextField(verbose_name=u'值')

+ 0 - 0
apps/pay/__init__.py


+ 114 - 0
apps/pay/models.py

@@ -0,0 +1,114 @@
+#coding=utf-8
+
+from django.db import models
+from django.utils import timezone
+
+from apps.customer.models import Customer
+from apps.product_order.models import ProductOrder
+from apps.product.models import Product
+from apps.customer.models import CustomerWechat
+
+from utils.exceptions import CustomError
+from utils.wechatpay import WechatPay
+
+class Pay(models.Model):
+    WAIT = 1
+    CONFIRM = 2
+    CLOSED = 3
+    STATUS_CHOICES = (
+        (WAIT, u'待付款'),
+        (CONFIRM, u'已付款'),
+        (CLOSED, u'已关闭'),
+    )
+    no = models.CharField(max_length=64, verbose_name=u"单号")
+    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"支付状态")
+    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()
+        self.updateOrderStatus(ProductOrder.REVOKE)
+
+    def payConfirm(self, amount):
+        if self.status != Pay.WAIT:
+            return
+
+        self.status = Pay.CONFIRM
+        self.amount = amount
+        self.save()
+        self.updateOrderStatus(ProductOrder.PAID)
+
+    def updateOrderStatus(self, status):
+        pay_order = PayProduct.objects.filter(main=self).first()
+        pay_order.order.status = status
+        pay_order.order.save()
+
+    @staticmethod
+    def getByNo(no):
+        instance = Pay.objects.filter(no=no).first()
+        if not instance:
+            raise CustomError(u'未找到相应的支付单!')
+        return instance
+
+    @staticmethod
+    def wechatPay(customer, amount, openid):
+        instance = Pay._addnew(customer,  amount)
+        return instance, instance._wechatUnifiedOrder(openid)
+
+    @staticmethod
+    def _addnew(customer,  amount):
+        if amount <= 0:
+            raise CustomError(u'无效的付款金额!')
+
+        no = timezone.now().strftime('%y%m%d%H%M%S') + str(customer.id)
+        instance = Pay.objects.create(
+            no=no,
+            customer=customer,
+            status=Pay.WAIT,
+            precreate_amount=amount
+        )
+        return instance
+
+    def _wechatUnifiedOrder(self, openid):
+        wechatpay = WechatPay(self.wechatapp.authorizer_appid, self.wechatapp.tenant_num, self.wechatapp.tenant_key)
+        wechatpay.unifiedOrder(self.no, self.precreate_amount, openid)
+        data = wechatpay.getAppString()
+        return data
+
+
+class PayProduct(models.Model):
+    main = models.OneToOneField(Pay, verbose_name=u'支付单', on_delete=models.PROTECT, related_name='pay_package_pay')
+    order = models.OneToOneField(ProductOrder, verbose_name=u'商品订单', on_delete=models.PROTECT, related_name='pay_package_order')
+
+    class Meta:
+        db_table = "pay_package"
+        verbose_name = u"支付商品"
+        ordering = ('-id',)
+        default_permissions = ()
+
+    @staticmethod
+    def addnew(order, openid):
+        pay, query_string = Pay.wechatPay(order.customer, order.actual_amount, openid)
+        instance = PayProduct.objects.create(main=pay, order=order)
+        return instance, query_string
+
+

+ 8 - 1
apps/product_order/models.py

@@ -1,7 +1,7 @@
 # coding=utf-8
 
 from django.db import models
-
+from django.utils import timezone
 from apps.product.models import Product
 from apps.customer.models import Customer, CustomerAddress
 
@@ -10,10 +10,12 @@ class ProductOrder(models.Model):
     WAIT_PAY = 1
     PAID = 2
     FINISHED = 3
+    REVOKE = 4
     STATUS_CHOICES = (
         (WAIT_PAY, u'待支付'),
         (PAID, u'已支付'),
         (FINISHED, u'已完成'),
+        (REVOKE, u'已取消'),
     )
     product = models.ForeignKey(Product, verbose_name=u'商品', on_delete=models.PROTECT)
     create_time = models.DateTimeField(verbose_name=u"添加时间", auto_now_add=True, editable=False)
@@ -27,6 +29,11 @@ class ProductOrder(models.Model):
     notes = models.CharField(max_length=1000, verbose_name=u'备注', null=True)
     delete = models.BooleanField(verbose_name=u'删除', default=False, editable=False)
 
+    def generateNo(self):
+        now = timezone.now()
+        no = '%s-%s-%04d' % ('TC', now.strftime('%Y%m%d'), self.id)
+        return no
+
     class Meta:
         db_table = 'product_order'
         verbose_name = u'商品订单'

+ 10 - 0
apps/wechat/customer/serializers.py

@@ -65,6 +65,16 @@ class ProductOrderSerializer(serializers.ModelSerializer):
         model = ProductOrder
         fields = '__all__'
 
+    def create(self, validated_data):
+        validated_data['customer'] = self.context['request'].customer
+        validated_data['price'] = Formater.formatPrice(validated_data['price'])
+        validated_data['amount'] = validated_data['price'] * validated_data['count']
+        instance = super(ProductOrderSerializer, self).create(validated_data)
+        no = instance.generateNo()
+        instance.no = no
+        instance.save()
+        return instance
+
 
 class VehicleDriveReserveSerializer(serializers.ModelSerializer):
     shop_name = serializers.CharField(source='shop.name', read_only=True)

+ 34 - 2
apps/wechat/customer/views.py

@@ -17,6 +17,7 @@ from apps.customer.filters import *
 from apps.vehicle_order.filters import *
 from apps.product_order.filters import *
 from apps.maint_order.filters import *
+from apps.pay.models import PayProduct
 from .xgj import XGJ
 
 
@@ -182,12 +183,18 @@ class MaintOrderReserveViewSet(generics.ListCreateAPIView):
             serializer.save()
             instance = serializer.instance
             validated_data = serializer.validated_data
+            abutment_xgj = Config.getConfigValue(Config.KEY_ABUTMENT_XGJ) or ''
+            if abutment_xgj and abutment_xgj == 'true':
+                try:
+                    XGJ.maint_reserve(instance)
+                except Exception as e:
+                    tenant_log(instance.customer.user, BizLog.INSERT, u'置换咨询同步销管佳失败[%s],id=%d' % (str(e), instance.id))
             tenant_log(instance.customer.user, BizLog.INSERT, u'客户添加售后预约单,id=%d' % instance.id, validated_data)
 
         return response_ok()
 
 
-class ProductOrderViewSet(generics.ListAPIView):
+class ProductOrderViewSet(generics.ListCreateAPIView):
     permission_classes = [IsCustomerUser, ]
     queryset = ProductOrder.objects.filter(delete=False)
     serializer_class = ProductOrderSerializer
@@ -204,6 +211,27 @@ class ProductOrderViewSet(generics.ListAPIView):
             return response_ok([])
         return data
 
+    def create(self, request, *args, **kwargs):
+        with transaction.atomic():
+            serializer = self.get_serializer(data=request.data)
+            serializer.is_valid(raise_exception=True)
+            serializer.save()
+            instance = serializer.instance
+            validated_data = serializer.validated_data
+            if instance.actual_amount == 0 and instance.xgj_member_id:
+                instance.status = ProductOrder.PAID
+                instance.save()
+                tenant_log(instance.customer.user, BizLog.INSERT, u'添加商品订单,no=%s' % instance.no, validated_data)
+            else:
+                openid = request.POST.get('openid', None)
+                if not openid:
+                    raise CustomError(u'未获取openid!')
+                pay_package, query_string = PayProduct.addnew(instance, openid)
+                tenant_log(instance.customer.user, BizLog.INSERT, u'添加商品订单,no=%s' % instance.no, validated_data)
+                if query_string:
+                    return response_ok(query_string)
+        return response_ok()
+
 
 class CustomerVehicleViewSet(generics.ListCreateAPIView):
     permission_classes = [IsCustomerUser, ]
@@ -317,9 +345,13 @@ class CustomerAddressViewSet(CustomModelViewSet):
 class MaintReserveOptionsView(APIView):
     permission_classes = [IsCustomerUser, ]
     def get(self, request):
+        vehicles = CustomerVehicle.objects.filter(customer=request.customer, delete=False).values('id', 'number')
+        vehicle_data = []
+        for vehicle in vehicles:
+            vehicle_data.append({'id': vehicle['id'], 'name': vehicle['number']})
         data = {
             'maint_types': Option.objects.filter(type=Option.MAINT_TYPE, delete=False, enable=True).values('id', 'name'), #品牌
-            'vehicles': CustomerVehicle.objects.filter(customer=request.customer, delete=False).values('id', 'number') #车辆
+            'vehicles': vehicle_data #车辆
         }
         return response_ok(data)
 

+ 8 - 8
apps/wechat/customer/xgj.py

@@ -14,6 +14,7 @@ from utils import response_ok
 from apps.account import tenant_log
 from apps.foundation.models import BizLog, Config
 from rest_framework.exceptions import NotFound
+from utils.format import *
 
 
 
@@ -44,7 +45,7 @@ class XGJ():
                 'model': instance.model.series.brand.name + '-' + instance.model.series.name + '-' + instance.model.name,
                 'guide_price': Formater.formatPriceShow(instance.model.price),
                 'shop_price': Formater.formatPriceShow(instance.model.sale_price),
-                'notes': instance.notes
+                'notes': instance.notes or u'小程序咨询'
             }
             result = requests.post(url=url, data=json.dumps(param))
             result = result.json()
@@ -73,8 +74,8 @@ class XGJ():
                 'tel': instance.tel,
                 'name': instance.name,
                 'model': instance.model.series.brand.name + '-' + instance.model.series.name + '-' + instance.model.name,
-                'date': instance.date,
-                'notes': instance.notes
+                'date': strfdate(instance.date),
+                'notes': instance.notes or u'小程序预约'
             }
             result = requests.post(url=url, data=json.dumps(param))
             result = result.json()
@@ -89,7 +90,6 @@ class XGJ():
         else:
             tenant_log(instance.customer.user, BizLog.INSERT, u'试驾预约同步销管佳失败,没有设置销管佳地址或密钥,id=%d' % (instance.id))
 
-
     @staticmethod
     def vehicle_estimate(instance): #置换咨询
         xgj_ip = Config.getConfigValue(Config.KEY_XGJ_IP)
@@ -103,10 +103,10 @@ class XGJ():
                 'company': instance.shop.xgj_id,
                 'tel': instance.customer.tel,
                 'name': instance.customer.name,
-                'model': instance.name + '-' + instance.model,
-                'plate_date': instance.plate_date,
+                'model': instance.brand.name + '-' + instance.model,
+                'plate_date': strfdate(instance.plate_date),
                 'mileage': instance.mileage,
-                'notes': instance.notes
+                'notes': instance.notes or u'小程序咨询'
             }
             result = requests.post(url=url, data=json.dumps(param))
             result = result.json()
@@ -137,7 +137,7 @@ class XGJ():
                 'model': instance.vehicle.name,
                 'number': instance.vehicle.number,
                 'vin': instance.vehicle.vin,
-                'date': instance.date,
+                'date': strfdate(instance.date),
                 'notes': instance.notes
             }
             result = requests.post(url=url, data=json.dumps(param))

+ 1 - 1
apps/wechat/product/views.py

@@ -17,7 +17,7 @@ from .serializers import ProductSerializer, ProductDetailSerializer, ProductType
 
 class ProductListView(generics.ListAPIView):
     permission_classes = [IsCustomerUser, ]
-    queryset = Product.objects.filter(delete=False)
+    queryset = Product.objects.filter(delete=False, enabled=True)
     serializer_class = ProductSerializer
 
     def filter_queryset(self, queryset):

+ 1 - 1
shop/settings.py

@@ -56,7 +56,7 @@ INSTALLED_APPS = [
     'apps.vehicle_order',
     'apps.maint_order',
     'apps.wechat',
-    # 'apps.pay',
+    'apps.pay',
     # 'apps.poster',
     # 'apps.activity',
     # 'apps.message',

+ 18 - 0
uis/views/config/index.html

@@ -49,6 +49,14 @@
                     <label ><font color='red' size="4">*</font>小程序密钥:</label>
                     <input type="text" name="wechat_session_key" lay-verify="required" autocomplete="off" class="input">
                 </div>
+                <div>
+                  <label><font color='red' size="4">*</font>微信商户号:</label>
+                  <input type="text" name="wechat_tenant_num" lay-verify="required" autocomplete="off" class="input">
+                </div>
+                <div>
+                    <label ><font color='red' size="4">*</font>商户密钥:</label>
+                    <input type="text" name="wechat_tenant_key" lay-verify="required" autocomplete="off" class="input">
+                </div>
                 <div class="layui-input-block">
                   <button class="layui-btn" lay-submit lay-filter="component-form-element">保存</button>
                 </div>
@@ -93,6 +101,10 @@
                     edit_data['xgj_session_key'] = data[i].value
                 }else if(data[i].property === 'xgj_ip'){
                     edit_data['xgj_ip'] = data[i].value
+                }else if(data[i].property === 'wechat_tenant_num'){
+                    edit_data['wechat_tenant_num'] = data[i].value
+                }else if(data[i].property === 'wechat_tenant_key'){
+                    edit_data['wechat_tenant_key'] = data[i].value
                 }
             }
             form.val("component-form-element", edit_data);
@@ -119,6 +131,12 @@
       if (data.field['xgj_ip']){
           save_data.push({key: 'xgj_ip',  value: data.field['xgj_ip']})
       }
+      if (data.field['wechat_tenant_num']){
+          save_data.push({key: 'wechat_tenant_num',  value: data.field['wechat_tenant_num']})
+      }
+      if (data.field['wechat_tenant_key']){
+          save_data.push({key: 'wechat_tenant_key',  value: data.field['wechat_tenant_key']})
+      }
       admin.req({
         url: '/foundation/config/'
         ,data: {data: JSON.stringify(save_data)}