提交 65c0a9cb authored 作者: 张颂恩's avatar 张颂恩

Merge branch 'dev' of http://124.225.116.118:35236/tj_django/datacenter into dev

......@@ -45,8 +45,7 @@ class Policy(models.Model):
(2,_('以宴席桌数为准')),
(3,_('严格的箱与桌公式设定')),
(4,_('箱数为主要条件,桌数为次要条件')),
(5,_('桌数为主要条件,箱数为次要条件')),
(6,_('按扫码瓶数换算箱数计算'))),default=3)
(5,_('桌数为主要条件,箱数为次要条件'))),default=3)
grades=models.JSONField(_('奖励等级'),default=list,blank=True,null=True)
# [{
......@@ -171,6 +170,7 @@ class Apply(models.Model):
(4, _('结算中')),
(5, _('已结算')),
(6, _('开瓶数量不足,未通过结算奖励')),
(7, _('宴席资料异常')),
), default=0)
reason = models.CharField(_('原因'), max_length=500, blank=True, null=True)
annotation = models.CharField(_('批注'), max_length=500, blank=True, null=True)
......@@ -186,7 +186,9 @@ class Apply(models.Model):
actual_table_num = models.SmallIntegerField(_('实际宴席桌数'), blank=True, null=True, default=0)
actual_qty_x=models.IntegerField(_('实际箱数'),default=0)
actual_qty_p=models.IntegerField(_('实际瓶数'),default=0)
salesman_info = models.CharField(_('业务员信息'), max_length=200, blank=True, null=True)
scan_num=models.IntegerField(_('扫码数量'),default=0)
bottle_num=models.IntegerField(_('瓶数'),default=0)
is_ck=models.BooleanField(_('是否出库'),default=False,db_index=True)
class Meta:
verbose_name = _('宴会申请')
......
......@@ -25,9 +25,9 @@ sK = 'fgfdvgu$#&3t@j'
from utils.views import time_key_check
from inventory.views import LabelQuery, decrypt_11h
from wx.views import transfer_batch
from mall.views import update_member_points_agent
import redis
import math
from mall.views import update_member_points_agent
#2020-12-21 微信redis库
......@@ -99,6 +99,8 @@ def banquetList(request,obj):
3: ['结算审核中','blue'],
4: ['结算中','blue'],
5: ['已结算','#07c160'],
6: ['开瓶数量不足,未通过结算奖励','red'],
7: ['宴席资料异常','red'],
}
status = dict[r[16]]
color = 'blue'
......@@ -107,6 +109,8 @@ def banquetList(request,obj):
btns = "<a href='/banquet/list/apply_codes/?id=" + str(
r[0]) + "' target='_blank' class='btn btn-default btn-sm btn-only-dlg'>申请扫码详情</a>"
btns = btns + "<a href='/banquet/list/apply_permit_detail/?id=" + str(
r[0]) + "' target='_blank' class='btn btn-default btn-sm btn-only-dlg'>查看备注</a>"
btns = btns + "<a href='/banquet/list/writeoff/?id=" + str(
r[0]) + "' target='_blank' class='btn btn-default btn-sm btn-only-dlg'>核销详情</a>"
btns = btns + "<a href='/banquet/list/information_edit/?id=" + str(
......@@ -319,6 +323,17 @@ def banquetList(request,obj):
])
elif obj == "code_search":
return render(request, 'banquet/code_search.html', data)
elif obj == "apply_permit_detail":#申请审批详情
id = int(request.GET.get('id','0'))
if id:
apply = Apply.objects.get(id=id)
user = User.objects.get(id=apply.permiter_id)
data["id"] = id
data["tm"] = apply.tm
data["permiter"] = user.last_name
data["reason"] = apply.reason
data["status"] = apply.get_status_display()
return render(request, 'banquet/apply_permit_detail.html', data)
return render(request, 'obj/list_obj.html', data)
......@@ -390,7 +405,6 @@ def ajax_banquet(request,obj):
elif obj == "code_search":
return ajaxBanquetCodeSearch(request)
def ajaxBanquetApply(request):
res = {
'e': ''
......@@ -400,7 +414,7 @@ def ajaxBanquetApply(request):
'company_id' in d and
'applyer_id' in d and
'category_id' in d and
'policy' in d and
'policy' in d and
'table_num' in d and
'user_name' in d and
'user_phone' in d and
......@@ -410,7 +424,7 @@ def ajaxBanquetApply(request):
company_id = d['company_id']
applyer_id = d['applyer_id']
category_id = d['category_id']
policy = d['policy']
policy = d['policy']
table_num = d['table_num']
user_name = d['user_name']
user_phone = d['user_phone']
......@@ -434,6 +448,10 @@ def ajaxBanquetApply(request):
apply.ancestor_id = c.ancestor_id
apply.applyer_id = applyer_id
apply.company = c
if apply.status == 6:
res['e'] = '当前状态不允许修改'
return JsonResponse(res)
apply.category_id = category_id
if 'product_id' in d and d['product_id']:
......@@ -443,9 +461,6 @@ def ajaxBanquetApply(request):
if 'address' in d and d['address']:
apply.address = d['address']
if 'salesman_info' in d and d['salesman_info']:
apply.salesman_info = d['salesman_info']
apply.policy = policy
apply.table_num = table_num
apply.user_name = user_name
......@@ -879,9 +894,9 @@ def ajaxBanquetCancel(request):
if obj.status == 0:
obj.delete()
else:
res['e'] = '无法删除已审核申请'
data['e'] = '无法删除已审核申请'
else:
res['e'] = '数据丢失'
data['e'] = '数据丢失'
return JsonResponse(res)
......@@ -1103,10 +1118,15 @@ def ajaxBanquetSettle(request):
# 如果提交箱数和核销相同 且 实际桌数与申请桌数相同 则自动返利 并修改状态为结算中
if apply_codes_cnt == writeoff_cnt and apply.actual_table_num == apply.table_num:
if apply.ancestor_id == 208869:
d = ajaxBanquetConsumerScanAnalysis(id)
#print(d)
if d['scan_rate'] >= 0.5:
apply.status = 4 #结算中
apply.status = 3 #结算审核中
#pids = [10558, 10559, 10560, 10561, 10562]
# 如果是一代产品则直接结算
#if apply.product_id in pids:
# apply.status = 4 #结算中
#else:
# d = ajaxBanquetConsumerScanAnalysis(id)
# if d['scan_rate'] >= 1:
# apply.status = 4 #结算中
else:
apply.status = 4 #结算中
......@@ -1176,7 +1196,7 @@ def ajaxBanquetConsumerScanAnalysis(id):
data['e'] = '数据丢失'
return data
def ajaxBanquetManualSettle(request):
data = {'e': ''}
id = int(request.POST.get('id','0'))
......@@ -1219,7 +1239,7 @@ def ajaxBanquetCodeSearch(request):
else:
data['e'] = '数据丢失'
return JsonResponse(data)
# 2024-06-24
def policy(request,action='list',id=None):
u=request.user
......@@ -1244,7 +1264,7 @@ def policy(request,action='list',id=None):
else:
form=PolicyForm(request.POST,instance=obj)
print(form)
if form.is_valid():
form.save()
......@@ -1473,7 +1493,7 @@ def close_applys(request,agent_id,apply_id=None):
amt='0'
for r in rs:
id=str(r[0])
app=str(r[1])
app=str(r[1])
# 宴席桌数
z=str(r[2])
......@@ -1486,7 +1506,7 @@ def close_applys(request,agent_id,apply_id=None):
p=ps[0]
if p.sql:
amt=p.sql
if p.consumer_scan_strategy:
cur.execute(f"""
select count(0) from sales_consumerwinner where company_id = {str(ancestor_id)} and xcode in (
......@@ -1529,39 +1549,27 @@ def close_applys(request,agent_id,apply_id=None):
if points:
update_member_points_agent(agent_id,points,'消费者宴会扫码返利')
data['apply_ids'].append(r[1])
# 箱数
x_num = 'count(*)'
data['apply_ids'].append(r[1])
# 查询手动结算记录,如果有手动结算,则以手动结算为准
cur.execute("select amt from banquet_settlerecord where apply_id = "+app+" order by id desc limit 1")
r = cur.fetchone()
if r:
amt = str(r[0])
else:
# # 查询扫码率 过滤不需要扫码的产品
if ancestor_id == 208869:
include_product_ids = [10124, 10125, 10126, 10127, 10128]
if product_id in include_product_ids:
analysis = ajaxBanquetConsumerScanAnalysis(int(app))
if analysis['bottle_num']:
x_num = str(math.floor(analysis['bottle_num'] / 6))
print(amt)
if amt !='0':
# 写入奖励记录产品名称
# 写入奖励记录
s="insert into banquet_bonus(ancestor_id,agent_id,policy_id,apply_id,product_id,qty_x,table_num,amt,qty_p) "+\
"select "+aid+","+agent_id+","+id+","+app+",product_id,X,Z,"+amt+",P from "+\
"(select product_id,"+x_num+" X, "+z+" Z,sum(P) P from "+\
"(select product_id,count(*) X, "+z+" Z,sum(P) P from "+\
"(select label_pk_id,product_id,"+\
"(select count(*) from banquet_writeoff where apply_id="+app+" and label_pk_id=w.label_pk_id and label_pkg!=3) P "+\
"from banquet_writeoff w where apply_id="+app+\
" group by label_pk_id,product_id"+\
") g group by product_id"+\
") gg"
print(s)
cur.execute(s)
#print(s)
# 写入剩余实物
s="INSERT INTO banquet_surplus(ancestor_id,agent_id,policy_id,apply_id,product_id,qty_x,qty_p,dt,tm) "+\
......
......@@ -7092,7 +7092,36 @@ def ajax_company(request,obj):
return ajaxGetWorkCardDetail(request)
elif obj == 'getWorkCardByLabelCode':
return ajaxGetWorkCardByLabelCode(request)
elif obj == 'getCompanyUserbyName':#通过名称模糊查询user
return ajaxGetCompanyUserbyName(request)
#用户名称模糊搜索
def ajaxGetCompanyUserbyName(request):
data={
'e':'',
'list':[]
}
name=request.GET.get('name','')
company_id=request.GET.get('company_id','')
agent_id=request.GET.get('agent_id','')
if name:
cur=connection.cursor()
sql = f"""
select id,last_name from company_user
where ancestor_id={company_id} and company_id={agent_id} and last_name like '%{name}%'
"""
cur.execute(sql)
for r in cur.fetchall():
data['list'].append({
'id':r[0],
'name':r[1],
})
else:
data['e'] ='数据丢失'
return JsonResponse(data)
def ajaxAddDetail(request):
......
......@@ -119,6 +119,9 @@ urlpatterns = [
#2020-10-19
url(r'^obj/ajax/subsaldiv/(?P<division_id>\w+)/$', list_views.ajax_subdiv),
#2024-11-21 根据系列查产品
url(r'^obj/ajax/series_to_product/$', list_views.ajax_series_to_product),
......@@ -1113,6 +1116,7 @@ urlpatterns = [
url(r'^sales/do_hexiao_dispatch/$', sal_views.do_hexiao_dispatch),
url(r'^sales/do_hexiao_accept/$', sal_views.do_hexiao_accept),
url(r'^sales/hexiao_record/$', sal_views.hexiao_record),
url(r'^sales/no_hexiao_record/$', sal_views.no_hexiao_record),
url(r'^sales/hexiao_record/detail/(?P<id>\d+)/$', sal_views.hexiao_record_detail),
url(r'^sales/hexiao_record/permit/(?P<id>\d+)/$', sal_views.hexiao_record_permit),
url(r'^sales/hexiao_dispatch_record/$', sal_views.hexiao_dispatch_record),
......@@ -1181,6 +1185,8 @@ urlpatterns = [
url(r'^test/send_hb/$', wx_views.test_send_hb),
url(r'^test/consumer_winner_hit/$', sal_views.test_consumer_winner_hit),
url(r'^test/zsk_pay/$', wx_views.test_zsk),
url(r'^wx/kangnian/terminal/$', wx_views.kangnian_getTerminal),
......
......@@ -704,9 +704,13 @@ def edit_obj(request,obj_name,id=None):
if id:
s = "SELECT id,name,(select count(id) from company_user where product_series @> Array[s.id] and id = "+id+") "+\
"FROM product_series s where company_id = "+str(c.ancestor_id)
cur.execute(s)
for r in cur.fetchall():
series.append([r[0], r[1],r[2]])
else:
s = "SELECT id,name,0"+\
"FROM product_series s where company_id = "+str(c.ancestor_id)
cur.execute(s)
for r in cur.fetchall():
series.append([r[0], r[1],r[2]])
products = []
if id:
......@@ -2991,7 +2995,7 @@ def list_obj(request, obj):
r[0],r[15], r[1], r[2], r[3], img, r[5],r[6], r[7],r[8],r[9],r[10],r[11],r[12],r[13],r[14]
])
else:
data['clmns'] = [_('活动名称'),_('奖项'), _('奖品'), _('红包金额'), _('复购次数'), _('微信昵称'), _('微信头像'), _('姓名'), _('电话'), _('地址'), _('中奖时间'), _('领奖时间'), _('经销商'), _('区域'),_('生产批次'),_('标签序号'), _('备注')]
data['clmns'] = [_('活动名称'),_('奖项'), _('奖品'), _('红包金额'), _('复购次数'), _('微信昵称'), _('微信头像'), _('姓名'), _('电话'), _('地址'), _('中奖时间'), _('领奖时间'), _('经销商'), _('区域'),_('生产批次'),_('标签序号'),_('箱码'), _('备注')]
s="select " +\
"coalesce((select name from sales_activities where id=a.activity_id),'')," +\
"coalesce(b.name,'未中奖'),"+\
......@@ -3009,7 +3013,8 @@ def list_obj(request, obj):
"coalesce(label_code,''), "+\
"coalesce(remark,''),"+\
"coalesce(cast(a.award_amount as text), ''),"+\
"coalesce(cast((select fg_num from sales_award where id=b.award_id) as integer), 0) " + \
"coalesce(cast((select fg_num from sales_award where id=b.award_id) as integer), 0)," + \
"coalesce(xcode,'') " + \
"from "+\
"(select * from sales_consumerwinner "+sWhere+" and company_id="+str(c.id) +ssql+") a "+\
"left outer join "+\
......@@ -3024,7 +3029,7 @@ def list_obj(request, obj):
if r[4]:
img = "<img src='"+r[4]+"' style='height:30px;cursor:pointer;' tt='"+r[3]+"'></img>"
data['data'].append([
r[0], r[1], r[2], r[15], r[16] if r[16] else '/', r[3], img, r[5],r[6], r[7],r[8],r[9],r[10],r[11],r[12],r[13],r[14]
r[0], r[1], r[2], r[15], r[16] if r[16] else '/', r[3], img, r[5],r[6], r[7],r[8],r[9],r[10],r[11],r[12],r[13],r[17],r[14]
])
#2020-11-30
elif obj == 'consumerredeem':
......@@ -6395,3 +6400,20 @@ def agent_is_active_star(request,id=None):
"""
cur.execute(s)
return JsonResponse(data)
def ajax_series_to_product(request):
cpid = request.GET.get('company_id', '')
series_ids = request.GET.get('series_ids', '')
s=f"""
select id,name from product_product where series_id in ({series_ids}) and company_id={cpid}
"""
cur = connection.cursor()
cur.execute(s)
data = []
for r in cur.fetchall():
data.append({
'id': r[0],
'name': r[1]
})
return JsonResponse(data, safe=False)
差异被折叠。
......@@ -18,7 +18,7 @@
event.preventDefault();
var status = $("#status_select option:selected").val();
var remark = $("#txt_remark").val();
$.ajax({
url: '/banquet/ajax/permit',
data:{
......@@ -30,7 +30,7 @@
success:function(res){
if(res.e === ''){
$(".alert-success").fadeIn();
changeTableData(status)
changeTableData(status)
//$.RefreshContent("/banquet/list/apply/");
setTimeout(function(){
$(".alert-success").fadeOut();
......@@ -49,11 +49,13 @@
var status = {{status}}
if(status >= 3){
$("#status_select").html(`
<option value="2">驳回</option>
<option value="4">通过</option>
<option value="1">驳回(重新核销)</option>
<option value="2">驳回(重新申请)</option>
<option value="6">开瓶数量不足,未通过结算奖励</option>
`)
}
function changeTableData(status){
var status_dict = {
0: ['未审核','blue'],
......@@ -62,6 +64,8 @@
3: ['结算审核中','blue'],
4: ['结算中','blue'],
5: ['已结算','#07c160'],
6: ['开瓶数量不足,未通过结算奖励','red'],
7: ['宴席资料异常','red'],
}
var firstLink = $(window.click_row).find('td:first a');
$(firstLink).text(status_dict[status][0])
......@@ -86,6 +90,7 @@
<option value="0">未审核</option>
<option value="1">通过</option>
<option value="2">未通过</option>
<option value="7">宴席资料异常</option>
</select>
</div>
</div>
......@@ -95,6 +100,7 @@
<textarea id="txt_remark" cols="30" rows="10" placeholder="填写备注" style="width: 100%;"></textarea>
</div>
</div>
<h3 style="text-align: center; margin-top:20px;">宴会信息审核</h3>
<div>
<button class="btn btn-primary" id="btn_permit" style="float:right;">保存</button>
</div>
......
{% extends 'obj_form.html' %}
{% load i18n %}
{% block subtitle %}
<ol class="breadcrumb">
<li>{% trans '宴会管理' %}</li>
<li>{% trans '审核详情' %}</li>
</ol>
{% endblock %}
{% block form_content %}
<script>
$(document).ready(function () {
})
</script>
<style>
input[type=checkbox]{
display: inline-block;
width: auto;
height: auto;
margin-top: 10px;
}
</style>
{% if status|add:0 > 2 %}
<h3 style="text-align: center; margin-top:20px;">宴席结算审核</h3>
{% else %}
<h3 style="text-align: center; margin-top:20px;">宴席审核</h3>
{% endif %}
<div class="form-group">
<label class="">审核状态:{{status }}</label>
</div>
<div class="form-group">
<label class=" ">审核时间:{{tm}}</label>
</div>
<div class="form-group">
<label class=" ">审核人:{{permiter}}</label>
</div>
<div class="form-group">
<label class="">备注: {{reason}}</label>
</div>
<div>
<!-- <button class="btn btn-primary" id="btn_permit" style="float:right;">保存</button> -->
</div>
{% endblock %}
\ No newline at end of file
......@@ -10,6 +10,13 @@
});
var company_id = {{request.user.company.id}}||'';
if(company_id=='255721'){//上海悠氧 调整命名
$('#store_name').text('供应链中心')
$('#store_manager').text('供应链管理员')
$('#store_address').text('供应链地址')
}
$('#keeper_label').autocomplete({
source: function(request, response) {
$.ajax({
......@@ -77,16 +84,16 @@
</div>
{% endif %}
<div class="form-group ">
<label class="col-sm-2 control-label ">{% get_verbose_field_name form.instance 'name' %} </label>
<label id="store_name" class="col-sm-2 control-label ">{% get_verbose_field_name form.instance 'name' %} </label>
<div class="col-sm-10">{{ form.name }}</div>
</div>
<div class="form-group ">
<label class="col-sm-2 control-label ">库房管理员</label>
<label id="store_manager" class="col-sm-2 control-label ">库房管理员</label>
<div class="col-sm-10"><input type="text" class="form-control" id="keeper_label" ></div>
</div>
<div class="form-group ">
<label class="col-sm-2 control-label ">仓库地址</label>
<label id="store_address" class="col-sm-2 control-label ">仓库地址</label>
<div class="col-sm-10">{{ form.address }}</div>
</div>
<div class="form-group ">
......
......@@ -69,10 +69,16 @@
{% endfor %}
</div>
</div>
<style>
.fs-wrap{
vertical-align: middle;
}
</style>
<div class="form-group">
<label class="col-md-2 control-label">产品系列授权</label>
<div class='col-sm-4'>
<div class='col-sm-10'>
<select id="ySelectSeries" multiple="multiple">
{% for s in series %}
{% ifequal s.2 1 %}
......@@ -82,6 +88,13 @@
{% endifequal %}
{% endfor %}
</select>
{% ifequal request.user.company_id 255721 %}
<span id="series_to_product" style="display: inline-block; 'vertical-align':middle;" class="input-group-btn">
<button type="button" class="btn btn-success">
<span class='glyphicon glyphicon-arrow-down'>添加选取系列到产品授权</span>
</button>
</span>
{% endifequal%}
</div>
</div>
......@@ -123,6 +136,44 @@
{% block js %}
{{ block.super }}
//把授权系列产品添加到产品授权列表
$('#series_to_product').on('click',function(){
var series= $("#ySelectSeries").ySelectedValues(",");
if(!series) return;
if($("#ySelectSeries")){
$.ajax({
url: '/obj/ajax/series_to_product',
dataType: "json",
data: {
//term: $("#id_company_name").val() ,//搜索栏里的内容
company_id: {{ request.user.company_id }},//额外参数
series_ids : series
},
success: (data)=>{
console.log('data',data);
if(data.length==0) return;
for(var i=0;i<data.length;i++){
var e=false;
$("#sel-product option").each(function(){
if($(this).val() == data[i].id){
e=true;
}
});
if(e){
toastr.error(data[i].label+"已在列表中!", "操作错误:",
{timeOut: 800, positionClass: "toast-center-center"}
);
console.log('已在列表中!',data[i].label);
}else{
$('#sel-product').append("<option value='"+data[i].id+"'>"+data[i].name+"</option>");
}
}
}
});
};
})
$('.btn-save').click(function(){
$(this).hide();
var g_ids=[];
......@@ -201,10 +252,7 @@
});
$('#obj_form .btn-showpassword').click(function(){
event.preventDefault();
$('#password').show();
});
//2024-05-31
$('#sel_is_active').val('{{form.instance.is_active}}');
......@@ -248,6 +296,7 @@
}
);
$('#product_label').autocomplete({
source: function(request, response) {
......
{% load i18n %}
<style>
.ui-autocomplete {
z-index: 9999;
}
</style>
<script type="text/javascript">
$(document).ready(function () {
var page_length = 10;
var table = $('#tb').DataTable({
dom: 'Bfrtip',
autoWidth: true,
pageLength: page_length,
buttons: [{ extend: 'excelHtml5', text: '导出到Excel' }],
language: lng,
data: {{ data| safe}}
});
table.columns().every(function () {
var that = this;
$('input', this.footer()).on('keyup change clear', function () {
if (that.search() !== this.value) {
that.search(this.value).draw();
}
});
});
$('#tb').on("click", "td a", function (event) {
event.preventDefault();
$.RefreshContent($(this).attr('href'));
});
$('.datepicker').datepicker({ dateFormat: 'yy-mm-dd' });
$('.datepicker').datepicker('setDate', new Date());
setDatePickerZh();
$('#dt_fr').val('{{date_from}}');
$('#dt_to').val('{{date_to}}');
$('.btn-qry').click(function () {
var date_from, date_to;
date_fr = $('#dt_fr').val();
date_to = $('#dt_to').val();
var params = [];
if (date_fr != '') { params.push('date_from=' + date_fr) };
if (date_to != '') { params.push('date_to=' + date_to) };
if (params.length == 0) {
bootbox.alert("{% trans '请先输入查询条件!'%}");
} else {
$.RefreshContent('{{request.path}}?' + params.join('&'));
}
});
});
function deleteItem(id) {
var r = confirm('您确定要删除吗?');
if (r) {
var durl = '{{request.path}}delete/' + id + '/'
$.RefreshContent(durl);
}
}
function permitIt(id,e) {
var event = e || window.event;
var btn = event.target;
var r = confirm('是否将状态改为允许?');
if (r) {
var durl = '{{request.path}}permit/' + id + '/'
//$.RefreshContent(durl);
$.ajax({
url:durl,
success:function(res){
if(res.e === ''){
$(btn).hide();
$(btn).siblings().text('是').css({'color':'green'})
}
else{
alert(res.e);
}
}
})
}
}
$('#user_label').autocomplete({
source: function (request, response) {
$.ajax({
url: '/obj/ajax/autocomplete/user/',
dataType: "json",
data: {
term: $("#user_label").val(),//搜索栏里的内容
company_id: '{{request.user.company.id}}',//额外参数
},
success: function (data) {
response(data);
}
});
},
select: function (event, ui) {
$("#id_user").val(ui.item.value);
$("#user_label").val(ui.item.label)
return false;
}
});
$('#modal_order').on('shown.bs.modal', function (event) {
var button = $(event.relatedTarget) // Button that triggered the modal
var id = button.data('id') // Extract info from data-* attributes
$('#id_record').val(id)
})
$("#btn-save").click(function () {
if ($("#id_user").val() !== '') {
$.SubmitForm('#form_1', '{{request.path}}');
$('#modal_order').modal('hide')
}
else {
alert('请选择业务员')
}
})
</script>
<ol class="breadcrumb">
<li>{{subtt.0}}</li>
<li>{{subtt.1}}</li>
</ol>
<form role="form" class="form-inline" style="margin-top:0px;padding-left:10px;">
<div class="col-sm-2 input-group">
<span class="input-group-addon">{% trans "开始日期"%}</span>
<input class="datepicker form-control" id="dt_fr" />
</div>
<div class="col-sm-2 input-group">
<span class="input-group-addon">{% trans "结束日期"%}</span>
<input class="datepicker form-control" id="dt_to" />
</div>
<div class="col-sm-2 input-group">
<span class='input-group-btn'>
<a type="button" class='btn btn-primary btn-qry'>
<span class='glyphicon glyphicon-search' style='padding-right:5px;'></span>{% trans '查找' %}
</a>
</span>
</div>
</form>
<div class="clearfix"></div>
<div class="col-md-12 col-sm-12 col-xs-12">
<div class="x_panel">
<div class="table-responsive">
<table id='tb' class="table table-striped table-bordered dt-responsive nowrap" cellspacing="0" width="100%">
<thead>
{% for c in clmns %}
<th>{{c}}</th>
{% endfor %}
</thead>
<tbody></tbody>
<tfoot>
{% for c in clmns %}
<th><input type="text" placeholder="{{c}}"></th>
{% endfor %}
</tfoot>
</table>
</div>
</div>
</div>
<div class="modal fade" id="modal_order" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">分配业务员</h4>
</div>
<div class="modal-body">
<form role="form" method='POST' enctype="multipart/form-data" class="form-horizontal" id="form_1">
{% csrf_token %}
<div class="form-group">
<label for="user_label">用户账号</label>
<div>
<input id='user_label'>
</div>
</div>
<input type="hidden" id="id_user" name="user_id" />
<input type="hidden" id="id_record" name="id" />
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="btn-save">确定</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
\ No newline at end of file
-----BEGIN CERTIFICATE-----
MIIEKDCCAxCgAwIBAgIUJ0XANv9J3dPSsKnxD1nTF2XX6+8wDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjQxMTE1MDYyMTA3WhcNMjkxMTE0MDYyMTA3WjCBgTETMBEGA1UEAwwK
MTY1MTIxNTEyOTEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMS0wKwYDVQQL
DCTpmZXopb/lhbHpgJrphZLkuJrmnInpmZDotKPku7vlhazlj7gxCzAJBgNVBAYT
AkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBANI75yKUP9aDrnOoPIZT9QInz4m54rS1fy3P9bFTHlwsh9j2Ul8MDb6U
5FWfqVcpBIlUMe3rvJTApU7H7AiwleKt5o+Il3i+3nKtlpAa/+t82BymwuNbethi
BpR58jy0OdR/x4R94QkgLxmCznqn6FK6yB9xkMByXIHR9coX0QdaMoHVmW5F/WjN
cBsVPDbD7GBNsnOLRyJ2EQYjL4pc8x/xmsRVuLVM3Oe88GB28GuqworcqbAh+zJa
2Bgyp2JpuUi245IatCzOzUrD8UX5VHJT5/c/2OKq9rVqStFc6TfhxOWTbFN3EhuO
reMRC321MVqJh3ENgHKddxhnwNPpIBsCAwEAAaOBuTCBtjAJBgNVHRMEAjAAMAsG
A1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDovL2V2Y2Eu
aXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJDMDRC
MDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJFMTJCMjdB
OUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IBAQAh9oSV
wAuK28wR36eB/kPlESVH8kD1sO8W1M9TbgSnpy4bxhXqBAdWJOy4WK8fJpX/oMMX
RcM5fzBdzxs4J9UjS9M9nDAqxDz1Ir5QNGi4otN8sVxR6wmxsQmq3i40bDG1p8V/
E/C8BLYKdY4hzbHXlpap+r6rEaTq46yxjrRtr2LybbG85bvvVAY/1ga84KRI7/Yz
Mqdb6QmtuwtVZhpjDkWRFuTB0H7NBQ5UlrtxmVoUgoVJ7u7OvWy/Vdlwu5Sq2PXm
/ITP9GdOd0CTUr7qGQmTlrwIBUT+sENJokpHM7oEoE9qTFLYdL0dY4xYvR4z62cT
qP6Mtt4nqP7qLcE4
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDSO+cilD/Wg65z
qDyGU/UCJ8+JueK0tX8tz/WxUx5cLIfY9lJfDA2+lORVn6lXKQSJVDHt67yUwKVO
x+wIsJXireaPiJd4vt5yrZaQGv/rfNgcpsLjW3rYYgaUefI8tDnUf8eEfeEJIC8Z
gs56p+hSusgfcZDAclyB0fXKF9EHWjKB1ZluRf1ozXAbFTw2w+xgTbJzi0cidhEG
Iy+KXPMf8ZrEVbi1TNznvPBgdvBrqsKK3KmwIfsyWtgYMqdiablItuOSGrQszs1K
w/FF+VRyU+f3P9jiqva1akrRXOk34cTlk2xTdxIbjq3jEQt9tTFaiYdxDYBynXcY
Z8DT6SAbAgMBAAECggEAAMAqARuaZr+tncgZ/zjJhSpzOUCqqI6FV7qIXAu57gZS
rdmjTaeTXPzXG98oEaGZ3IuqafkPgVB2xqM8GoPW5SpNynFoaQv1a19nTDhsdYDK
nL+TrpAjDia7c0hELydNlY6nPbfdV9T/lg27gwCaecEk+cfEBovh/KytPf17x5Tv
NObDM30mbMGyzW+sil0pb64sKrP19QCmMfj3onvq7xts9dzyAAZIQ/Q2fuZiIe6f
ipNtG+tQc4FapTixp1xiSxuDslXC6NxZEAHvTWMlvxLd4AOlMVESQPPKmKzjVi7u
WV443tP+jr+vldzsPQixXgJDSPyD3G3nMrJ+4ufrsQKBgQDvntNYkGtP4FTK+Wmz
jLYDI4aoVTcBhbDW7wenc71lYOPbBbew+1drOyd+vBoQsr/ItM3WZDP5J3/MO7qe
7OTNJ+Kkj815PtfaCM1IfEvY22pxuDXn8Fb4iOyLsddWL1pbJRjoL20EpNTYJdn3
SiDpBLC46zaANz9xd+ySCJv6hQKBgQDgmtZhaYEQGL8k+/PDsyHSb4fA9J3xachK
6hM1ixQn+by3xUjYt3Y4ht0oOC/70lj0JfTp5KEFhZk9jmwip82BeI+JkR3res+m
SZ/kmzynG7YZYpJ3jWPHcnV8+LOCi4SkgGaQHreUTxMtwQkE7rs6cIIOzBNWuFfd
leBJtInCHwKBgGjdgC6tHVmammEt39VlCi67eG3oxSbUZDzb5OSioUthqLYsfWRl
yq8x9yneliWipI3omoBt39T1NVei+BwfHk6uWuCb0ldyajKQUY6cXvErrRzRej3k
D5u7CJJmJRAhrxBeXtUWFEbJACEMB6TSey6fD9wNjA92njxBOHX8cI6lAoGAG8bq
Zmeu33fAA+no7rXzo9vJNTLLabTEYYdrUr7dgkgfb7bFWI/v3fPH3BZ+0Kzl9WPQ
UzDEPTOVjQhVFUAdaTqovcGlqp6omY8XaCVOrff+YEtRg9mHkn30S71SLf3lLJb4
mDBEUeIppAnwfJytqP+LBht09M+dhjjn4RWjx88CgYEA7L9EM+wF/1vruhnhFHc9
mNud6mSwcX+eQu6w/kWYviDkNlR1+QVoUVIQQubWkRC2WRzMp3tDXce+vBxSZmmw
bD1d32yMiJ/DMkw9bZ76190d+fhuNVCR+HBxeiNHzaHfdfwmt7kgrhdDgIoRqOZ8
SQTmU6EAoGDIOJDba3Ws66M=
-----END PRIVATE KEY-----
欢迎使用微信支付!
附件中的三份文件(证书pkcs12格式、证书pem格式、证书密钥pem格式),为接口中强制要求时需携带的证书文件。
证书属于敏感信息,请妥善保管不要泄露和被他人复制。
不同开发语言下的证书格式不同,以下为说明指引:
证书pkcs12格式(apiclient_cert.p12)
包含了私钥信息的证书文件,为p12(pfx)格式,由微信支付签发给您用来标识和界定您的身份
部分安全性要求较高的API需要使用该证书来确认您的调用身份
windows上可以直接双击导入系统,导入过程中会提示输入证书密码,证书密码默认为您的商户号(如:1900006031)
证书pem格式(apiclient_cert.pem)
从apiclient_cert.p12中导出证书部分的文件,为pem格式,请妥善保管不要泄漏和被他人复制
部分开发语言和环境,不能直接使用p12文件,而需要使用pem,所以为了方便您使用,已为您直接提供
您也可以使用openssl命令来自己导出:openssl pkcs12 -clcerts -nokeys -in apiclient_cert.p12 -out apiclient_cert.pem
证书密钥pem格式(apiclient_key.pem)
从apiclient_cert.p12中导出密钥部分的文件,为pem格式
部分开发语言和环境,不能直接使用p12文件,而需要使用pem,所以为了方便您使用,已为您直接提供
您也可以使用openssl命令来自己导出:openssl pkcs12 -nocerts -in apiclient_cert.p12 -out apiclient_key.pem
备注说明:
由于绝大部分操作系统已内置了微信支付服务器证书的根CA证书, 2018年3月6日后, 不再提供CA证书文件(rootca.pem)下载
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCefZbdsT6RiaCRqWKKIi4rWrntio3oX1RbCFSGHI3o2jS34Aew0QwBDDftdPS6ohgMRksIykoTTmXE8/1xgLc43Ed1kAQ2AHUkTdIRzhI3Sp5Ac6sD9B/Ys3Adb05PRvfCEcqDlRVjsLYCeFFpi9djZev97ojmKSajJ3kS7OnmuQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA6NLHoZOiPT0wgnfg0nRl3CqcyNGWDBoCDkJ5Cw/AHt5IAEQ1
R81w+5vBcLxGCttz4Qapcbpy+ck9zYiiVtOjInhS14tbgJad9jCTWm8+ejShVp/M
k9Xe1irORrxbj9YJ3L3BbYr5K3wTuAMd43Wp+7Ia7LpplmGSvOZumTRYRyFQgSs6
Vfv0x4fRQI4/JdUp+sX3DUUns0kXy8ggoI/B6kUYS6+Th+YWjcztdliJtRMb9sM5
BIJGWuGUNCDo93+UNyTxrEI8cTczfxqlzKRZSoadHa4xJF3TBKrgm4D5fZNF/ZEs
WmfsC7qalc0x0sHuPBnXTKM97d4t0WosTJkvAQIDAQABAoIBABT6Fr/9Vf8IAhSj
OeekUqAfW8BxN8Bv4HMJ9K91NBVj6B9UBevp++3Dhw6B/BqS0rI54C7twlm0ksOv
tZa2m0pqBmbCuNQRXfqWPlSF1N58WvP70loYiSsJMMwpQOOpAHr5wtF8lb4gAAPS
jhEWIyJZFflxwz0oHek0ps7Za0G/N+cFLZAdju9OXoO3mQMvuP8AQ7qT3A3sGJP4
d6K5YzMZSNT2SrwLR18VUpaFIDkCLp4XW5LZ7SJP5Ps2OoAPGawDA8g5OFC3z3J2
lZvT9BlOhkETimhwkXjuJV68RD+YmMulIxU5Q+erljACImfP43r6aOgIs7mezm2R
bSvQXmUCgYEA7N6GuphLIOV8tOfmDpCWKgmsArTVD2t/wLa4yYpffN57gz9NZ/oa
LsOIrl62MRmybWoVa/WlP9d3cNbS5NsyJeispKftMqzdiWi2d78X358GtgebxpzG
2s+e4k4pWvVSPW98YcqBn0xa0RWVtfREyyQ6ZJCxOMxShCgPKWXJZc0CgYEA+6CZ
9ctoRWGXXGC3VRYbwVEWtu3g40cTUILX7ZA1weXilE+9pr5BgmOPqm9i16GMjTKe
92rn6H2VBm5o9nlLX1KR2IiY3YEZ77QgQuOGNrJrk9GXRGOVoR4553ACaqzD/d5N
NTMYGSLlUMvu2K74S7Y1tTMfle7wKw9M0utA+gUCgYEAjENZVLVKB7jdF+MagBVu
VoZTrWaXpAHsLJoG4pFl6fN1AEtD8wZO6tIu5dJ/4KNu3Qusk7lqmVtmBxOu3oZO
M9SMJxSpiRCEPxIaut4VrP3mY3Qt8nZz6q53T1spcgtDH84cwWvBRitYu+ckOBLl
icVyLorf5tpXIpr2H3V+xSECgYBu9PPPGiHhrlYTSyaso3IbvckZYy3uqrHOp9PV
kujRlrBiHAOSGoVCnOqGClX4pekXfkHw8jhPIclsK5WnUfc1knBznlBiJIKuW6x6
MmBFkEm1xg9A/fTGNlDsSE0YgxRtxvAezGgM9YFPLo4lCIq+wBtQyrlb4SAQrM+A
lnJxjQKBgA9YtJU5xpIR9KkKqqyCZINi+XmAwffh6XoT0e8DgOHXHnc8GgODCasL
wtCqmARfdnSy8UE2NSdHHPJUVx299t6wL0VLv11MEZqKu8mJwCIiAdR5YsHbaKTp
H+qprpac3lhMgEh25H93mxjZBxaRSjLzy2+n6XQtr2vYt5sdztVQ
-----END RSA PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6NLHoZOiPT0wgnfg0nRl
3CqcyNGWDBoCDkJ5Cw/AHt5IAEQ1R81w+5vBcLxGCttz4Qapcbpy+ck9zYiiVtOj
InhS14tbgJad9jCTWm8+ejShVp/Mk9Xe1irORrxbj9YJ3L3BbYr5K3wTuAMd43Wp
+7Ia7LpplmGSvOZumTRYRyFQgSs6Vfv0x4fRQI4/JdUp+sX3DUUns0kXy8ggoI/B
6kUYS6+Th+YWjcztdliJtRMb9sM5BIJGWuGUNCDo93+UNyTxrEI8cTczfxqlzKRZ
SoadHa4xJF3TBKrgm4D5fZNF/ZEsWmfsC7qalc0x0sHuPBnXTKM97d4t0WosTJkv
AQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDFROPf3SA7LPmGcNioy4u1ub+tSR9bNhwYc1vdWhCp6Q3AjNSdXQNy3nTUFxtehmmFXTd2nHoY1ogApvecL+6Uks4ExakQTv20UUbXoRZsAUW3QU018+ljqwkB+DcxWViwiXdCBIMvsNSHIDIxx/PyX1HO3Ok6iB3bhQkrm7zlhQIDAQAB
-----END PUBLIC KEY-----
......@@ -26,7 +26,6 @@ class Pay(models.Model):
cert_serial_no=models.CharField(_('商户证书序列号'),max_length=100,blank=True,null=True)
cert_path=models.CharField(_('证书地址'),max_length=200)
pay_id=models.CharField(_('商户号'),max_length=100)
remark=models.CharField(_('备注'),max_length=200,blank=True,null=True)
comp_abbr=models.CharField(_('公司缩写'),max_length=100,blank=True,null=True)
send_hb_method = models.SmallIntegerField(_('发放类型'),blank=True,null=True)
......@@ -37,7 +36,8 @@ class Pay(models.Model):
pay_platform_pn=models.CharField(_('支付平台项目编码'),max_length=50,blank=True,null=True)
aes_key=models.CharField(_('aes密码'),max_length=50,blank=True,null=True)
aes_iv=models.CharField(_('aes偏移值'),max_length=50,blank=True,null=True)
public_key_id=models.CharField(_('公钥id'),max_length=500,blank=True,null=True)
public_key=models.CharField(_('公钥'),max_length=500,blank=True,null=True)
class Meta:
verbose_name = _('微信商户平台')
......
差异被折叠。
#橙券支付
#中数科支付
#接口文档 https://doc.apipost.net/docs/detail/34ff33e7bce7000?target_id=ef551d7ba901a
import hashlib
import datetime
......@@ -8,46 +9,173 @@ import urllib.request
import urllib.parse
import random
import base64
import os
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
domain=''
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding
from wx.models import *
env = 'prod'
domainMap={
'dev':'http://ywymtest-api.azhiyun.cn',
'prod':'https://ywym-api.qidianpw.com'
}
domain = domainMap[env]
class ZSKPay:
def __init__(self, appid, k, aes_key='', aes_iv=''):
def __init__(self, appid, taxId, ymId, ymKey, cert):
self.MAX_ENCRYPTION_SIZE = 117 #214
self.appid = appid
self.k =k
self.aes_key = aes_key
self.aes_iv = aes_iv
# 计算签名
def generate_sign(self,data):
return ''
def http_post(self,data, type, url):
timestamp = int(round(time.time() * 1000))
data['timestamp'] = timestamp
data['ym-id'] = self.appid
if type:
data['type'] = type
sign = self.generate_sign(data)
data['ym-key'] = sign
data = json.dumps(data)
data = bytes(data,"utf8")
headers={'Content-Type':'application/json'}
request = urllib.request.Request(domain+url, data, headers)
res = urllib.request.urlopen(request).read().decode('utf-8')
return json.loads(res)
self.domain = domain
self.taxId = taxId
self.ymId = ymId
self.ymKey = ymKey
self.cert = cert
def encrypt_data(self, data):
encrypted_chunks = []
current_directory = os.getcwd()
with open(self.cert + "public_key.txt", "rb") as public_file:
public_key_content = public_file.read()
public_key_loaded = serialization.load_pem_public_key(
public_key_content,
backend=default_backend()
)
# 将数据分块
for i in range(0, len(data), self.MAX_ENCRYPTION_SIZE):
chunk = data[i:i + self.MAX_ENCRYPTION_SIZE]
try:
# 使用公钥加密内容
ciphertext = public_key_loaded.encrypt(
chunk,
padding.PKCS1v15()
)
encrypted_chunks.append(ciphertext)
except Exception as e:
print("加密块失败:", e)
# 将所有加密后的块连接在一起
resp = b''.join(encrypted_chunks)
return base64.b64encode(resp).decode('utf-8')
def decrypt_data(self, data):
current_directory = os.getcwd()
with open(current_directory+"/wx/cert/zsk/resp/private_key.txt", "rb") as public_file:
private_key_content = public_file.read()
# 加载私钥
private_key_loaded = serialization.load_pem_private_key(
private_key_content,
password=None,
backend=default_backend()
)
# 解密
data = json.jmson.loads(data)
plaintext = private_key_loaded.decrypt(
base64.b64decode(data),
padding.PKCS1v15()
)
return plaintext.decode('utf-8')
def http_post(self, data={}, url=''):
data = json.dumps(data).encode('utf-8')
# 加密数据
data = self.encrypt_data(data)
# print("加密后数据:", type(data), data)
headers = {
'Content-Type': 'application/json',
'ym-id':self.ymId,
'ym-key':self.ymKey
}
request = urllib.request.Request(self.domain + url, bytes(json.dumps({'data': data}), "utf8"), headers, method='POST')
# 发送请求并获取响应
try:
response = urllib.request.urlopen(request)
# 读取响应体
res_body = response.read().decode('utf-8')
return json.loads(res_body)
except urllib.error.HTTPError as e:
print("HTTP错误:", e.code, e.read().decode())
except urllib.error.URLError as e:
print("网络错误:", e.reason)
def transfer(self):
data = {
#转账
def transfer(self,data):
if not data['transferNo'] or not data['transferAmount'] or not data['transferRemark'] or not data['openid'] :
raise ValueError("transferDetailList 必传")
company_id = data.get('company_id','')
batchNo = data.get('transferNo', self.generate_batchNo())
batchName = data.get('batchName','活动批次')
batchRemark = data.get('batchRemark','无')
transferDetailList = []
transferDetailList.append(data)
handle_data = {
"taxId": self.taxId,
"batchNo": batchNo,
"appid": self.appid,
"batchName": batchName,
"batchRemark": batchRemark,
"totalAmount": 0,
"totalNum": 0, #转账总笔数
"transferSceneId": "", #转账场景ID
"notifyUrl": "", #回调地址
#转账明细列表
"transferDetailList": transferDetailList
# [
# {
# "transferNo": "tj_consumer_winner_001", //商家明细单号
# "transferAmount": 30, //转账金额单位为“分”
# "transferRemark": "红包", //转账备注,【转账备注】 单条转账备注(微信用户会收到该备注),UTF8编码,最多允许32个字符
# "openid": "oCZuC5RiJ8RId4qacC0inhy74vRc", //【收款用户openid】商户appid下,某用户的openid
# "userName": "
# }
# ]
}
return self.http_post_form(data, '', '')
for item in transferDetailList:#计算总金额和总笔数
# if not item.get('transferNo') or not item.get('transferAmount') or not item.get('transferRemark') or not item.get('openid'):
# raise ValueError("缺少转账参数")
handle_data['totalAmount'] += item['transferAmount']
handle_data['totalNum'] += 1
# print('加密前数据----:',handle_data)
body = self.http_post(data=handle_data,url='/wx-pay/transfer')
print('resp body', body)
# if body and body.get('code', '') == 200:
return body
#转账信息查询
def tranfer_info(self,data={}):
if not data.get('batchNo') or data.get('batch',''):
raise ValueError("缺少参数")
encrypt_body = self.http_post(data=data,url='/wx-pay/transfer-info')
decrypted_body = self.decrypt_data(encrypt_body['data'])
# print('解密后的查询数据',decrypted_body)
return json.loads(decrypted_body)
# 生成订单号
def generate_order_number():
def generate_batchNo(self):
timestamp = int(time.time())
random_number = random.randint(1000, 9999)
order_number = str(timestamp) + str(random_number)
return order_number
batchNo ='tj'+ str(timestamp) + str(random_number)
return batchNo
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论