/span>
-    list_display = ('activity_name', 'model_uni_names', 'start_at', 'end_at', 'coupon_expire_at', 'coupon_value', 'status', 'created_at', 'updated_at')
189
+    list_display = ('activity_name', 'model_uni_names', 'start_at', 'end_at', 'coupon_expire_type', 'coupon_valid_period', 'coupon_expire_at', 'coupon_value', 'status', 'created_at', 'updated_at')
190
+    list_filter = ('coupon_expire_type', 'status')
190 191
 
191 192
 
192 193
 admin.site.register(AdministratorInfo, AdministratorInfoAdmin)

+ 50 - 0
mch/migrations/0036_auto_20190719_1508.py

@@ -0,0 +1,50 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.20 on 2019-07-19 07:08
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('mch', '0035_auto_20190521_1543'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AddField(
16
+            model_name='activityinfo',
17
+            name='coupon_expire_type',
18
+            field=models.IntegerField(choices=[(0, '\u56fa\u5b9a\u7ed3\u675f\u65f6\u95f4'), (1, '\u53ef\u53d8\u7ed3\u675f\u65f6\u95f4')], default=0, help_text='\u7ef4\u4fee\u5238\u7c7b\u578b', verbose_name='coupon_expire_type'),
19
+        ),
20
+        migrations.AddField(
21
+            model_name='activityinfo',
22
+            name='coupon_valid_period',
23
+            field=models.IntegerField(default=0, help_text='\u7ef4\u4fee\u5238\u6709\u6548\u65f6\u95f4\uff08\u5355\u4f4d\uff1a\u5929\uff09', verbose_name='coupon_valid_period'),
24
+        ),
25
+        migrations.AddField(
26
+            model_name='administratorinfo',
27
+            name='admin_type',
28
+            field=models.IntegerField(choices=[(0, '\u7ba1\u7406\u5458'), (1, '\u7ef4\u4fee\u5458')], db_index=True, default=0, help_text='\u7ba1\u7406\u5458\u7c7b\u578b', verbose_name='admin_type'),
29
+        ),
30
+        migrations.AddField(
31
+            model_name='consumeinfosubmitloginfo',
32
+            name='activity_id',
33
+            field=models.IntegerField(default=0, help_text='\u6d3b\u52a8\u552f\u4e00\u6807\u8bc6', verbose_name='activity_id'),
34
+        ),
35
+        migrations.AddField(
36
+            model_name='consumeinfosubmitloginfo',
37
+            name='admin_id',
38
+            field=models.CharField(blank=True, db_index=True, help_text='\u6838\u9500\u5458\u552f\u4e00\u6807\u8bc6', max_length=32, null=True, verbose_name='admin_id'),
39
+        ),
40
+        migrations.AddField(
41
+            model_name='consumeinfosubmitloginfo',
42
+            name='has_used',
43
+            field=models.BooleanField(db_index=True, default=False, help_text='\u662f\u5426\u5df2\u6838\u9500', verbose_name='has_used'),
44
+        ),
45
+        migrations.AddField(
46
+            model_name='consumeinfosubmitloginfo',
47
+            name='used_at',
48
+            field=models.DateTimeField(blank=True, help_text='\u7ef4\u4fee\u5238\u6838\u9500\u65f6\u95f4', null=True, verbose_name='used_at'),
49
+        ),
50
+    ]

+ 53 - 13
mch/models.py

@@ -11,6 +11,14 @@ from TimeConvert import TimeConvert as tc
11 11
 
12 12
 
13 13
 class AdministratorInfo(BaseModelMixin):
14
+    ADMINISTRATOR = 0
15
+    MAINTENANCE = 1
16
+
17
+    USER_TYPE_TUPLE = (
18
+        (ADMINISTRATOR, u'管理员'),
19
+        (MAINTENANCE, u'维修员'),
20
+    )
21
+
14 22
     ACTIVATED = 1
15 23
     DISABLED = 2
16 24
     DELETED = 3
@@ -23,6 +31,8 @@ class AdministratorInfo(BaseModelMixin):
23 31
 
24 32
     admin_id = ShortUUIDField(_(u'admin_id'), max_length=32, blank=True, null=True, help_text=u'管理员唯一标识', db_index=True, unique=True)
25 33
 
34
+    admin_type = models.IntegerField(_(u'admin_type'), choices=USER_TYPE_TUPLE, default=ADMINISTRATOR, help_text=u'管理员类型', db_index=True)
35
+
26 36
     phone = models.CharField(_(u'phone'), max_length=11, blank=True, null=True, help_text=u'管理员电话', db_index=True)
27 37
     password = models.CharField(_(u'password'), max_length=255, blank=True, null=True, help_text=u'管理员密码')
28 38
     encryption = models.CharField(_(u'encryption'), max_length=255, blank=True, null=True, help_text=u'管理员密码')
@@ -503,6 +513,10 @@ class ConsumeInfoSubmitLogInfo(BaseModelMixin):
503 513
     coupon_expire_at = models.DateTimeField(_(u'coupon_expire_at'), blank=True, null=True, help_text=_(u'维修券过期时间'))
504 514
     coupon_value = models.IntegerField(_(u'coupon_value'), default=0, help_text=_(u'维修券金额(单位:分)'))
505 515
 
516
+    has_used = models.BooleanField(_(u'has_used'), default=False, help_text=_(u'是否已核销'), db_index=True)
517
+    admin_id = models.CharField(_(u'admin_id'), max_length=32, blank=True, null=True, help_text=u'核销员唯一标识', db_index=True)
518
+    used_at = models.DateTimeField(_(u'used_at'), blank=True, null=True, help_text=_(u'维修券核销时间'))
519
+
506 520
     test_user = models.BooleanField(_(u'test_user'), default=False, help_text=_(u'是否为测试用户'), db_index=True)
507 521
 
508 522
     class Meta:
@@ -532,11 +546,16 @@ class ConsumeInfoSubmitLogInfo(BaseModelMixin):
532 546
     @property
533 547
     def coupon_info(self):
534 548
         return {
535
-            'coupon_expire_at': self.final_coupon_expire_at,
536
-            'coupon_value': self.coupon_value,
549
+            'coupon_expire_at': '',
550
+            'coupon_value': 0,
551
+            'coupon_has_expired': True,
537 552
         }
538 553
 
539 554
     @property
555
+    def coupon_info2(self):
556
+        return self.coupon_info,
557
+
558
+    @property
540 559
     def data(self):
541 560
         if self.submit_during_activity:
542 561
             try:
@@ -556,11 +575,22 @@ class ConsumeInfoSubmitLogInfo(BaseModelMixin):
556 575
             'serialNo': self.serialNo,
557 576
             'verifyResult': self.verifyResult,
558 577
             'submit_during_activity': self.submit_during_activity,
559
-            'coupon_info': act.coupon_info if act else self.coupon_info,
578
+            'coupon_info': act.coupon_info2(created_at=self.created_at) if act else self.coupon_info2,
579
+            'final_coupon_info': act.coupon_info(created_at=self.created_at) if act else self.coupon_info,
580
+            'has_used': self.has_used,
581
+            'used_at': self.used_at,
560 582
         }
561 583
 
562 584
 
563 585
 class ActivityInfo(BaseModelMixin):
586
+    FIXED_EXPIRED_TIME = 0
587
+    CHANGED_EXPIRED_TIME = 1
588
+
589
+    COUPON_EXPIRED_TIME_TUPLE = (
590
+        (FIXED_EXPIRED_TIME, u'固定结束时间'),
591
+        (CHANGED_EXPIRED_TIME, u'可变结束时间'),
592
+    )
593
+
564 594
     brand_id = models.CharField(_(u'brand_id'), max_length=32, blank=True, null=True, help_text=u'品牌唯一标识', db_index=True)
565 595
 
566 596
     activity_name = models.CharField(_(u'activity_name'), max_length=255, blank=True, null=True, help_text=u'活动名称')
@@ -570,6 +600,8 @@ class ActivityInfo(BaseModelMixin):
570 600
     start_at = models.DateTimeField(_(u'start_at'), help_text=_(u'start_at'))
571 601
     end_at = models.DateTimeField(_(u'end_at'), help_text=_(u'end_at'))
572 602
 
603
+    coupon_expire_type = models.IntegerField(_(u'coupon_expire_type'), choices=COUPON_EXPIRED_TIME_TUPLE, default=FIXED_EXPIRED_TIME, help_text=_(u'维修券类型'))
604
+    coupon_valid_period = models.IntegerField(_(u'coupon_valid_period'), default=0, help_text=_(u'维修券有效时间(单位:天)'))
573 605
     coupon_expire_at = models.DateTimeField(_(u'coupon_expire_at'), blank=True, null=True, help_text=_(u'维修券过期时间'))
574 606
     coupon_value = models.IntegerField(_(u'coupon_value'), default=0, help_text=_(u'维修券金额(单位:分)'))
575 607
 
@@ -580,21 +612,29 @@ class ActivityInfo(BaseModelMixin):
580 612
     def __unicode__(self):
581 613
         return unicode(self.pk)
582 614
 
583
-    @property
584
-    def final_coupon_expire_at(self):
585
-        if not self.coupon_expire_at:
615
+    def final_expire_at(self, created_at=None):
616
+        if self.coupon_expire_type == ActivityInfo.FIXED_EXPIRED_TIME:
617
+            return self.coupon_expire_at
618
+        return tc.utc_datetime(dt=created_at, days=self.coupon_valid_period)
619
+
620
+    def final_coupon_expire_at(self, created_at=None):
621
+        final_expire_at = self.final_expire_at(created_at=created_at)
622
+        if not final_expire_at:
586 623
             return ''
587
-        y = tc.local_string(self.coupon_expire_at, format='%Y')
588
-        m = tc.local_string(self.coupon_expire_at, format='%m')
589
-        d = tc.local_string(self.coupon_expire_at, format='%d')
624
+        y = tc.local_string(final_expire_at, format='%Y')
625
+        m = tc.local_string(final_expire_at, format='%m')
626
+        d = tc.local_string(final_expire_at, format='%d')
590 627
         return u'{}年{}月{}日'.format(y, m, d)
591 628
 
592 629
     def has_unexpired_activity(self, model_name):
593 630
         return (self.model_uni_names and model_name in self.model_uni_names) and (self.start_at <= tc.utc_datetime() < self.end_at)
594 631
 
595
-    @property
596
-    def coupon_info(self):
632
+    def coupon_info(self, created_at=None):
597 633
         return {
598
-            'coupon_expire_at': self.final_coupon_expire_at,
634
+            'coupon_expire_at': self.final_coupon_expire_at(created_at=created_at),
599 635
             'coupon_value': self.coupon_value,
600
-        },
636
+            'coupon_has_expired': tc.utc_datetime() >= self.final_expire_at(created_at=created_at),
637
+        }
638
+
639
+    def coupon_info2(self, created_at=None):
640
+        return self.coupon_info(created_at=created_at),

+ 7 - 2
page/sale_views.py

@@ -108,7 +108,12 @@ def clerk_sale_submit_api(request):
108 108
 
109 109
     if settings.CHECK_DUPLOAD_ENABLED:
110 110
         try:
111
-            sci = SaleclerkIntegralIncomeExpensesInfo.objects.get(code=serialNo, status=True)
111
+            sci = SaleclerkIntegralIncomeExpensesInfo.objects.get(
112
+                brand_id=brand.brand_id,
113
+                model_id=model.model_id,
114
+                code=serialNo,
115
+                status=True
116
+            )
112 117
         except SaleclerkIntegralIncomeExpensesInfo.DoesNotExist:
113 118
             sci = None
114 119
     else:
@@ -159,7 +164,7 @@ def clerk_sale_submit_api(request):
159 164
         )
160 165
 
161 166
     # TODO: Make statistic async
162
-    if (not settings.CHECK_DUPLOAD_ENABLED) or (not sci):
167
+    if (not settings.CHECK_DUPLOAD_ENABLED) or (not clerk.test_user and not sci):
163 168
         ymd = tc.local_string(format='%Y%m%d')
164 169
 
165 170
         # 日销量统计

+ 7 - 9
requirements.txt

@@ -1,19 +1,17 @@
1 1
 CodeConvert==2.0.5
2 2
 MySQL-python==1.2.5
3
-Pillow==5.0.0
3
+Pillow==6.1.0
4 4
 StatusCode==1.0.0
5
-TimeConvert==1.4.3
6
-cryptography==1.5.2
5
+TimeConvert==1.4.4
7 6
 furl==2.0.0
8 7
 isoweek==1.3.3
9 8
 jsonfield==2.0.2
10
-mock==2.0.0
11
-monetary==1.0.2
12
-pysnippets==1.0.8
13
-qiniu==7.2.2
14
-requests==2.21.0
9
+monetary==1.0.3
10
+pysnippets==1.0.9
11
+qiniu==7.2.6
12
+requests==2.22.0
15 13
 rlog==0.3
16
-rsa==3.4.2
14
+rsa==4.0
17 15
 shortuuid==0.5.0
18 16
 versions==0.10.0
19 17
 -r requirements_dj.txt

+ 6 - 6
requirements_dj.txt

@@ -1,19 +1,19 @@
1
-Django==1.11.20
1
+Django==1.11.22
2 2
 django-admin==1.3.2
3
-django-cors-headers==2.4.0
3
+django-cors-headers==3.0.2
4 4
 django-curtail-uuid==1.0.4
5
-django-detect==1.0.8
5
+django-detect==1.0.12
6 6
 django-file-md5==1.0.2
7 7
 django-file-upload==1.1.0
8 8
 django-ip==1.0.2
9
-django-json-render==1.0.2
9
+django-json-render==1.0.3
10 10
 django-json-response==1.1.5
11 11
 django-logit==1.1.3
12 12
 django-mobi==0.1.7
13 13
 django-models-ext==1.1.8
14 14
 django-multidomain==1.1.4
15 15
 django-paginator2==1.0.4
16
-django-query==1.0.3
16
+django-query==1.0.5
17 17
 django-redis-connector==1.0.1
18 18
 django-response==1.1.1
19 19
 django-rlog==1.0.7
@@ -21,4 +21,4 @@ django-shortuuidfield==0.1.3
21 21
 django-six==1.0.4
22 22
 django-uniapi==1.0.5
23 23
 django-we==1.4.2
24
-djangorestframework==3.7.7
24
+djangorestframework==3.9.4

+ 1 - 1
requirements_pywe.txt

@@ -5,7 +5,7 @@ pywe-jssdk==1.1.0
5 5
 pywe-membercard==1.0.1
6 6
 pywe-miniapp==1.1.5
7 7
 pywe-oauth==1.0.7
8
-pywe-pay==1.0.12
8
+pywe-pay==1.0.13
9 9
 pywe-pay-notify==1.0.4
10 10
 pywe-response==1.0.1
11 11
 pywe-sign==1.0.8

+ 1 - 1
statistic/views.py

@@ -251,7 +251,7 @@ def ytj(brand_id):
251 251
         '-num'
252 252
     )[:20]
253 253
     clerks = SaleclerkInfo.objects.filter(brand_id=brand_id, status=True)
254
-    clerks = {clerk.clerk_id: {'distributor_id': clerk.distributor_id, 'distributor_name': clerk.distributor_name, 'clerk_name': clerk.clerk_name} for clerk in clerks}
254
+    clerks = {clerk.clerk_id: {'distributor_id': clerk.distributor_id, 'distributor_name': clerk.distributor_name, 'clerk_name': clerk.clerk_name, 'salesman_id': clerk.clerk_id, 'salesman_name': clerk.clerk_name} for clerk in clerks}
255 255
     salesmen = [dict(sm, **clerks.get(sm.get('clerk_id', ''), {})) for sm in salesmen]
256 256
 
257 257
     # [收费者维度] 统计周期内省份销量排行数据,请按顺序返回

+ 15 - 1
utils/error/errno_utils.py

@@ -28,10 +28,22 @@ class ProductModelStatusCode(BaseStatusCode):
28 28
 
29 29
 
30 30
 class ProductDistributorStatusCode(BaseStatusCode):
31
-    """ 经销商相关错误码 5011xx """
31
+    """ 经销商相关错误码 5012xx """
32 32
     DISTRIBUTOR_NOT_FOUND = StatusCodeField(501201, 'Distributor Not Found', description=u'经销商不存在')
33 33
 
34 34
 
35
+class ProductMachineStatusCode(BaseStatusCode):
36
+    """ 机器相关错误码 5013xx """
37
+    SN_NOT_FOUND = StatusCodeField(501301, 'SN Not Found', description=u'序列号不存在')
38
+
39
+
40
+class ProductCouponStatusCode(BaseStatusCode):
41
+    """ 优惠券相关错误码 5014xx """
42
+    COUPON_NOT_FOUND = StatusCodeField(501401, 'Coupon Not Found', description=u'优惠券不存在')
43
+
44
+    COUPON_HAS_EXPIRED = StatusCodeField(501411, 'Coupon Has Expired', description=u'优惠券已过期')
45
+
46
+
35 47
 class ProductStatusCode(BaseStatusCode):
36 48
     """ 产品相关错误码 5020xx """
37 49
     PRODUCT_NOT_FOUND = StatusCodeField(502001, 'Product Not Found', description=u'产品不存在')
@@ -77,6 +89,8 @@ class AdministratorStatusCode(BaseStatusCode):
77 89
     ADMINISTRATOR_NOT_ACTIVATED = StatusCodeField(400215, 'Administrator Not Activated', description=u'管理员未激活')
78 90
     ADMINISTRATOR_HAS_DISABLED = StatusCodeField(400216, 'Administrator Has Disabled', description=u'管理员已禁用')
79 91
     ADMINISTRATOR_HAS_DELETED = StatusCodeField(400217, 'Administrator Has Deleted', description=u'管理员已删除')
92
+    # 管理员
93
+    MAINTENANCE_NOT_FOUND = StatusCodeField(400251, 'Maintenance Not Found', description=u'核销员不存在')
80 94
 
81 95
 
82 96
 class OperatorStatusCode(BaseStatusCode):

Kodo/kodo - Gogs: Go Git Service

1 Commits (ac7054dabafbeae78caaca1952a5d67da5a8ae7e)

Auteur SHA1 Bericht Datum
  Brightcells 49634dc097 Add api operator login 7 jaren geleden