python+Django CRM客户关系管理系统开发(十四)--报名流程开发

一、本节目标

前几节我们开发了数据展示,增删改查,本节开始开发业务流程,报名流程。

二、需求分析

1、报名流程如以下描述:

  • 销售   发起报名流程,选择班级,发报名链接给学员
  • 学员   填写在线报名表,提交个人信息,上传证件信息,同意培训协议
  • 销售    审核报名表,审核通过后,创建一条缴费记录,自动把学员添加到相应的班级,报名成功

三、功能开发

1、表设计

因为需要存储学生报名,缴费信息,因此需要设计表结构,同时合同信息也需要表来存储,因此添加model

class ContractTemplate(models.Model):
'''存储合同模板'''
name = models.CharField(max_length=64)
content = models.TextField()
date = models.DateField(auto_now_add=True)

class StudentEnrollment(models.Model):
'''学员报名表'''
customer = models.ForeignKey('CustomerInfo',on_delete=models.CASCADE)
class_grade = models.ForeignKey('ClassList',on_delete=models.CASCADE)
consultant = models.ForeignKey('UserProfile',on_delete=models.CASCADE)
contract_agreed = models.BooleanField(default=False)
contract_signed_date = models.DateTimeField(blank=True,null=True)
contract_approved = models.BooleanField(default=False)
contract_approved_date = models.DateTimeField('合同审核时间',blank=True,null=True)

class Meta:
unique_together = ('customer','class_grade')

def __str__(self):
return '%s'%self.customer

class PaymentRecord(models.Model):
'''存储学员缴费记录'''
enrollment = models.ForeignKey('StudentEnrollment',on_delete=models.CASCADE)
payment_type_choices = ((0,'报名费'),(1,'学费'),(2,'退费'))
payment_type = models.SmallIntegerField(choices=payment_type_choices,default=0)
amount = models.IntegerField('费用',default=500)
consultant = models.ForeignKey('UserProfile',on_delete=models.CASCADE)
date = models.DateTimeField(auto_now_add=True)

def __str__(self):
return '%s'%self.enrollment

 

2、按照流程,开发报名流程页面:

销售人员根据学员id,以及班级名称,生成一个报名链接:

在crm应用下,添加url:

 编写视图函数:

 创建一个名为:stu_enrollment的前端页面:

<link href="/static/css/bootstrap.css" rel="stylesheet">
<body>
<h3>学员报名页</h3>
<form class="form-horizontal" method="post">
{% csrf_token %}
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">客户</label>
<div class="col-sm-10">
<select name="customer_id" class="form-control">
{% for customer in customers %}
<option value="{{ customer.id }}">{{ customer.name }}</option>
{% endfor %}
</select>
</div>
</div>

<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">报名班级</label>
<div class="col-sm-10">
<select name="class_grade_id" class="form-control">
{% for class_grade in class_lists %}
<option value="{{ class_grade.id }}">{{ class_grade }}</option>
{% endfor %}
</select>
</div>
</div>
<input type="submit" class="btn btn-success pull-right" value="下一步">
</form>

{% if enrollment_link %}
<p>请将此报名链接复制并发送给学员填写 {{ enrollment_link }}</p>
{% endif %}

</body>

效果如图:

 

3、开发学员填写注册信息页面:

学员填写注册信息,需要有一个页面,因此,还是在crm应用下,首先添加url:

customerinfo添加如下字段:

在crm创建from.py文件,生成customerinfo的form

防止用户通过前端改html代码的方式改只读字段的信息,所以在form.py里面添加了一个自定义的验证方法(clean),如果只读字段提交的时候信息跟数据库中默认的不一样,就报错

from django import forms
from django.forms import ModelForm
from crm import models

class CustomerForm(ModelForm):
class Meta:
model = models.CustomerInfo
fields = "__all__"
#不显示的字段
exclude = ['consult_content','status','consult_courses']
#只读的字段
readonly_fields = ['contact_type','contract','consultant','referral_from','source']

#Django是通过“__new__”方法,找到ModelForm里的每个字段的,然后循环处每个字段添加自定义样式
def __new__(cls, *args, **kwargs):
#cls.base_fields是一个元组,里面的数据结构是 [(字段名,字段的对象),(),()]
for field_name in cls.base_fields:
field_obj = cls.base_fields[field_name]
#添加属性
field_obj.widget.attrs.update({'disable':'true'})
return ModelForm.__new__(cls)

#只读字段不让用户通过浏览器改HTML代码的方式修改
def clean(self):
#表单级别的错误
if self.errors:
raise forms.ValidationError(("please fix errors before re-submit"))
#如果有instance id,表名这是一个修改的表单,应该检查只读字段
if self.instance.id is not None:
#取出只读字段,是一个字符串形式
for field in self.Meta.readonly_fields:
#通过反射取出字段的值(数据库里的数据)
old_field_val = getattr(self.instance,field)
#提交过来的数据
form_val = self.cleaned_data.get(field)
#如果两个数据不匹配
if old_field_val != form_val:
#提示只读字段不能修改
#add_error是字段级别的错误提示
self.add_error(field,"Readonly Field:field should be '{value}',not '{new_value}'".format(**{'value':old_field_val,'new_value':form_val}))

编写视图函数:

 前端页面:

<link href="/static/css/bootstrap.css" rel="stylesheet">

<body>

<div class="container">
<h3>在线报名信息填写</h3>

<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">学员在线报名</h3>
</div>

<div class="panel-body">

<form class="form" method="post" onsubmit="return BeforeFormSubmit(this)">
{% csrf_token %}
{% for field in customer_form %}
<div class="form-group col-lg-6">
<label class="col-sm-2 control-label">{{ field.label }}</label>
<div class="col-sm-10">
{{ field }}
<span style="color: red;">{{ field.errors.0 }}</span>
</div>
</div>
{% endfor %}

<div class="form-group col-lg-6">
<label class="col-sm-2 control-label">报名班级</label>
<div class="col-sm-10">
{{ enrollment_obj.class_grade }}
</div>
</div>

<div class="form-group col-lg-6">
<label class="col-sm-2 control-label">学费</label>
<div class="col-sm-10">
{{ enrollment_obj.class_grade.course.price }}
</div>
</div>

<div class="col-sm-offset-11 col-sm-2">
<input type="submit" class="btn btn-success " value="提交">
</div>

</form>

</div>

<div class="panel-footer"><a href="http://www.fafafa.com"></a></div>
</div>


</div>

<script>

function BeforeFormSubmit(ele) {
$(":disabled").removeAttr("disabled");
}

</script>

页面效果如图:

4、如果修改了只读字段,会提示错误:

5、开发学生签署合同信息以及上传证件照片功能

首先,添加一个上传文件的url:

建一个存储上传文件的文件夹:

在settings里添加上传文件路径:

CRM_FILE_UPLOAD_DIR = os.path.join(BASE_DIR,'crm/upload_files/enrollment_data')

编写视图函数:

@csrf_exempt
def enrollment_fileupload(request,enrollment_id):
'''学员报名文件上传'''
enrollment_upload_dir = os.path.join(conf.settings.CRM_FILE_UPLOAD_DIR,enrollment_id)
#第一次上传图片时创建目录,学员上传第二张图片的时候,会判断目录是否存在
#如果目录存在还继续mkdir就会报错,所以这里要做判断
if not os.path.isdir(enrollment_upload_dir):
os.mkdir(enrollment_upload_dir)
#获取上传文件的对象
file_obj = request.FILES.get('file')
#最多只允许上传3个文件
if len(os.listdir(enrollment_upload_dir)) <= 3:
#把图片名字拼接起来(file.name:上传的文件名字)
with open(os.path.join(enrollment_upload_dir,file_obj.name),'wb') as f:
for chunks in file_obj.chunks():
f.write(chunks)
else:
return HttpResponse(json.dumps({'status':False,'err_msg':'最多只能上传三个文件'}))
return HttpResponse(json.dumps({'status':True}),)

前端页面完善修改:

这里使用到了dropzone: https://www.dropzonejs.com/#installation,下载将css和js文件放到static里:

9、点击后提示报名成功:

10、接下来该审核合同了

首先,添加审核的url:

编写视图函数:

def contract_audit(request,enrollment_id):
enrollment_obj = models.StudentEnrollment.objects.get(id=enrollment_id)
if request.method == 'POST':
enrollment_form = form.EnrollmentForm(instance=enrollment_obj, data=request.POST)
if enrollment_form.is_valid():
enrollment_form.save()
stu_obj = models.Student.objects.get_or_create(customer=enrollment_obj.customer)[0]
# m2m, 添加班级
stu_obj.class_grades.add(enrollment_obj.class_grade_id)
stu_obj.save()
# 改变报名
enrollment_obj.customer.status = 1
enrollment_obj.save()
return redirect("/backadmin/crm/customerinfo/%s/change" % enrollment_obj.customer.id)
else:
# 拿到客户信息的表单
customer_form = form.CustomerForm(instance=enrollment_obj.customer)
enrollment_form = form.EnrollmentForm(instance=enrollment_obj)
return render(request, 'contract_audit.html', locals())

添加enrollment的form

class EnrollmentForm(ModelForm):
class Meta:
model = models.StudentEnrollment
fields = '__all__'

def __new__(cls, *args, **kwargs):
# cls.base_fields是一个元组,里面是 所有的 【(字段名,字段的对象),(),()】
for field_name in cls.base_fields:
# 获取每个字段的对象
field_obj = cls.base_fields[field_name]
# 添加属性
field_obj.widget.attrs.update({'class': 'form-control'})
return ModelForm.__new__(cls)

设计html页面:

<h3>学员报名|合同审核</h3>

<form class="form-horizontal" method="post" onsubmit="BeforeFormSubmit(this)">{% csrf_token %}

{{ customer_form }}
{{ enrollment_form }}

<input type="submit" class="btn btn-success pull-right" value="审核通过" >

</form>

<script>
function BeforeFormSubmit(ele) {
$(":disabled").removeAttr("disabled");
}

</script>

效果如图:

原文地址:https://www.cnblogs.com/realizetomoney/p/13942147.html