3.存货(inventory)模块
3.1.models.py 设定
3.1.1.原料库存异动申请单
原料库存,除了正常的进货单之外,可能会因为其他原因(ex:报废或遗失等)而有所变动,所以我就新增了本单据,申请单分单头单身,单头设定如下:
class Mcheck(models.Model):
is_active = models.BooleanField(verbose_name=u'是否有效', default=False)
description = models.CharField(max_length=256, verbose_name=u'申请原因')
created = models.DateTimeField(auto_now=True, verbose_name=u'异动日期')
create_user = models.ForeignKey('auth.User', verbose_name=u'异动人员', on_delete=models.PROTECT)
def get_absolute_url(self):
return reverse('inventory:mcheck_detail', kwargs={'pk': self.pk})
def __str__(self):
return '{}'.format(self.id)
class Meta:
verbose_name = '原料库存异动申请单'
verbose_name_plural = verbose_name
3.1.2.原料库存异动申请单单身
class McheckDetail(models.Model):
num = models.CharField(max_length=2, default='', verbose_name=u'单身项次')
mcheck = models.ForeignKey(Mcheck, verbose_name=u'原料库存异动申请单', on_delete=models.CASCADE)
material = models.ForeignKey(Material, verbose_name=u'原料', on_delete=models.PROTECT,
limit_choices_to={'status': 'A'},)
quantity = models.IntegerField(verbose_name=u'异动数量',
help_text=u'数量为正表示新增一个库存,数量为负表示减少一个库存')
def save(self, *args, **kwargs):
if self.num == '':
#项次的格式是01,02...
mcheck_details = McheckDetail.objects.filter(mcheck=self.mcheck)
num = "{0:02d}".format(mcheck_details.count() + 1)
self.num = "{0:02d}".format(int(num))
super().save(*args, **kwargs)
def __str__(self):
return '{}'.format(self.num)
class Meta:
verbose_name = '原料库存异动申请单单身'
verbose_name_plural = verbose_name
3.1.3.商品库存异动申请单
等同于原料库存异动申请单的还有本单据,商品库存异动申请单分单头单身,单头设定如下:
class Pcheck(models.Model):
is_active = models.BooleanField(verbose_name=u'是否有效', default=False)
description = models.CharField(max_length=256, verbose_name=u'申请原因')
created = models.DateTimeField(auto_now=True, verbose_name=u'异动日期')
create_user = models.ForeignKey('auth.User', verbose_name=u'异动人员', on_delete=models.PROTECT)
def get_absolute_url(self):
return reverse('inventory:pcheck_detail', kwargs={'pk': self.pk})
def __str__(self):
return '{}'.format(self.id)
class Meta:
verbose_name = '商品库存异动申请单'
verbose_name_plural = verbose_name
3.1.4.商品库存异动申请单单身
class PcheckDetail(models.Model):
num = models.CharField(max_length=2, default='', verbose_name=u'单身项次')
pcheck = models.ForeignKey(Pcheck, verbose_name=u'商品库存异动申请单', on_delete=models.CASCADE)
product = models.ForeignKey(Product, verbose_name=u'商品', on_delete=models.PROTECT,
limit_choices_to={'status': 'A'},)
quantity = models.IntegerField(verbose_name=u'异动数量',
help_text=u'数量为正表示新增一个库存,数量为负表示减少一个库存')
def save(self, *args, **kwargs):
if self.num == '':
#项次的格式是01,02...
pcheck_details = PcheckDetail.objects.filter(pcheck=self.pcheck)
num = "{0:02d}".format(pcheck_details.count() + 1)
self.num = "{0:02d}".format(int(num))
super().save(*args, **kwargs)
def __str__(self):
return '{}'.format(self.num)
class Meta:
verbose_name = '商品库存异动申请单单身'
verbose_name_plural = verbose_name
3.1.5.制程单
制程单的用意是,如新手分享边爬文边写的 Django 用 admin 开发的简易进销存系统-1的[2.需求说明]说到:
2.客户下订单之后,业主根据库存情况向供应商购买原料,制作成商品出货给客户。
3.商品与原料有一BOM表对应关系。
4.商品制程约莫四到八周。
也就是说,从仓库领出原料到将制作好的商品放回仓库中间会有一个时间差,于是我就增加了本单据,避免库存数量不对的情况,实现方式在后面的段落中会提到,本单据依然分为单头与单身。
PROCESS_CHOICES = (
('A', u'立单申请'),
('C', u'原料已出库'),
('R', u'商品已入库'),
)
class Process(models.Model):
order = models.ForeignKey(Order, verbose_name=u'对应订单', limit_choices_to={'status': 'N'},
help_text=u'仅显示"尚未出货"的订单', on_delete=models.CASCADE)
product = models.ForeignKey(Product, verbose_name=u'对应商品', limit_choices_to={'status': 'A'},
on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(verbose_name=u'商品制程数量',
help_text=u'请注意,存盘后,下方制程单单身的"原料数量"将会自动乘上此"商品制程数量"')
description = models.CharField(max_length=256, verbose_name=u'描述', blank=True)
status = models.CharField(max_length=1, choices=PROCESS_CHOICES, default='A', verbose_name=u'制程状况')
claimed = models.DateTimeField(null=True, verbose_name=u'原料出库日期')
claim_user = models.ForeignKey('auth.User', related_name='claim_process', verbose_name=u'原料出库确认人员',
null=True, on_delete=models.PROTECT)
receipted = models.DateTimeField(null=True, verbose_name=u'商品入库时间')
receipt_user = models.ForeignKey('auth.User', related_name='receipt_process', verbose_name=u'商品入库确认人员',
null=True, on_delete=models.PROTECT)
created = models.DateTimeField(auto_now=True, verbose_name=u'申请日期')
create_user = models.ForeignKey('auth.User', verbose_name=u'申请人员', on_delete=models.PROTECT)
def get_absolute_url(self):
return reverse('inventory:process_detail', kwargs={'pk': self.pk})
def __str__(self):
return '{}'.format(self.id)
class Meta:
verbose_name = '制程单'
verbose_name_plural = verbose_name
permissions = (
('check_process', '可确认制程单内商品与原料的出入库'),
)
3.1.6.制程单单身
class ProcessDetail(models.Model):
num = models.CharField(max_length=2, default='', verbose_name=u'单身项次')
process = models.ForeignKey(Process, verbose_name=u'制程单', on_delete=models.CASCADE)
material = models.ForeignKey(Material, verbose_name=u'原料', on_delete=models.PROTECT)
quantity = models.PositiveIntegerField(verbose_name=u'数量')
def save(self, *args, **kwargs):
if self.num == '':
#项次的格式是01,02...
process_details = ProcessDetail.objects.filter(process=self.process)
num = "{0:02d}".format(process_details.count() + 1)
self.num = "{0:02d}".format(int(num))
super().save(*args, **kwargs)
def __str__(self):
return '{}'.format(self.num)
class Meta:
verbose_name = '制程单单身'
verbose_name_plural = verbose_name
3.1.7.原料库存异动
原料库存异动的作用是在记录每个原料因新增/减少的历史纪录,在本需求内,会造成原料异动的总共有三种单据:1.到货单;2.原料库存异动申请单;3.制程单。
MTRAIN_FORM_CHOICES = (
('ARRIVE', u'到货单'),
('MCHECK', u'原料库存异动申请单'),
('PROCESS', u'制程单'),
)
class Mtran(models.Model):
material = models.ForeignKey(Material, verbose_name=u'原料', on_delete=models.CASCADE)
source_form = models.CharField(max_length=12, choices=MTRAIN_FORM_CHOICES, blank=False,
default='', verbose_name=u'来源单类别')
source_id = models.PositiveIntegerField(default=0, verbose_name=u'来源单号')
from_quantity = models.PositiveIntegerField(default=0, verbose_name=u'异动前数量')
tran_quantity = models.IntegerField(default=0, verbose_name=u'异动数量')
to_quantity = models.PositiveIntegerField(default=0, verbose_name=u'异动后数量')
created = models.DateTimeField(auto_now=True, verbose_name=u'异动日期')
create_user = models.ForeignKey('auth.User', verbose_name=u'异动人员', on_delete=models.PROTECT)
def get_absolute_url(self):
return reverse('inventory:mtran_detail', kwargs={'pk': self.pk})
def __str__(self):
return '{}'.format(self.id)
class Meta:
verbose_name = '原料库存异动'
verbose_name_plural = verbose_name
3.1.8.商品库存异动
在本需求内会造成商品异动的总共有三种单据:1.出货单;2.商品库存异动申请单;3.制程单。
PTRAIN_FORM_CHOICES = (
('SHIP', u'出货单'),
('PCHECK', u'商品库存异动申请单'),
('PROCESS', u'制程单'),
)
class Ptran(models.Model):
product = models.ForeignKey(Product, verbose_name=u'商品', on_delete=models.CASCADE)
source_form = models.CharField(max_length=12, choices=PTRAIN_FORM_CHOICES, blank=False,
default='', verbose_name=u'来源单类别')
source_id = models.PositiveIntegerField(default=0, verbose_name=u'来源单号')
from_quantity = models.PositiveIntegerField(default=0, verbose_name=u'异动前数量')
tran_quantity = models.IntegerField(default=0, verbose_name=u'异动数量')
to_quantity = models.PositiveIntegerField(default=0, verbose_name=u'异动后数量')
created = models.DateTimeField(auto_now=True, verbose_name=u'异动日期')
create_user = models.ForeignKey('auth.User', verbose_name=u'异动人员', on_delete=models.PROTECT)
def get_absolute_url(self):
return reverse('inventory:ptran_detail', kwargs={'pk': self.pk})
def __str__(self):
return '{}'.format(self.id)
class Meta:
verbose_name = '商品库存异动'
verbose_name_plural = verbose_name
3.2.admin.py 设定
admin.py设定其实除了确定原料/商品在异动前后的数量都必须大于等于0之外,其他的也没什嬷特别的。
from django import forms
from django.contrib import admin
from .models import Mcheck, McheckDetail, Mtran, Pcheck, PcheckDetail, Process, ProcessDetail, Ptran
class MtranAdmin(admin.ModelAdmin):
list_display = ['id', 'material', 'source_form', 'source_id', 'from_quantity', 'tran_quantity',
'to_quantity', 'created', 'create_user']
list_filter = ['material']
view_on_site = False
list_per_page = 10
list_max_show_all = 100
date_hierarchy = 'created'
admin.site.register(Mtran, MtranAdmin)
class PtranAdmin(admin.ModelAdmin):
list_display = ['id', 'product', 'source_form', 'source_id', 'from_quantity', 'tran_quantity',
'to_quantity', 'created', 'create_user']
list_filter = ['product']
view_on_site = False
list_per_page = 10
list_max_show_all = 100
date_hierarchy = 'created'
admin.site.register(Ptran, PtranAdmin)
"""
原料库存异动申请单单身检查
1.最少要有一个单身
2.单身中原料不得重复
3.单身异动数量不得为0
4.单身原料异动后的库存数量不得小于0
"""
class McheckDetailCheckInlineFormset(forms.models.BaseInlineFormSet):
def clean(self):
count = 0
material_list = []
for form in self.forms:
if form.cleaned_data:
count += 1
quantity = form.cleaned_data.get('quantity')
if quantity == 0:
raise forms.ValidationError(u'原料库存异动申请单中单身数量不得为0。')
material = form.cleaned_data.get('material')
if material.stock + quantity < 0:
raise forms.ValidationError(u"单身原料[{}-{}]库存量为 {},加上异动量 {} "
u"后库存小于0不合法,请重新填写单身中的异动"
u"数量。".format(material.id, material.title, material.stock, quantity))
if material.id in material_list:
raise forms.ValidationError(u"单身原料[{}-{}]已重复,"
u"请重新填写原料库存异动申请单。".format(material.id, material.title))
else:
material_list.append(material.id)
if count < 1:
raise forms.ValidationError(u'您必须最少输入一笔原料库存异动申请单单身')
class McheckDetailInline(admin.TabularInline):
formset = McheckDetailCheckInlineFormset
model = McheckDetail
fields = ['material', 'quantity']
raw_id_fields = ['material']
extra = 0
class McheckAdmin(admin.ModelAdmin):
list_display = ['id', 'is_active', 'description', 'created', 'create_user']
fields = ['description']
inlines = [McheckDetailInline]
view_on_site = False
def save_model(self, request, obj, form, change):
if not change:
obj.create_user = request.user
super().save_model(request, obj, form, change)
def save_related(self, request, form, formsets, change):
super().save_related(request, form, formsets, change)
mcheck = form.save(commit=False)
mcheck_details = McheckDetail.objects.filter(mcheck=mcheck)
for mcheck_detail in mcheck_details.all():
#新增原料库存异动
material = mcheck_detail.material
stock = material.stock
tran_quantity = mcheck_detail.quantity
mtran = Mtran()
mtran.material = material
mtran.source_form = 'MCHECK'
mtran.source_id = mcheck.id
mtran.from_quantity = stock
mtran.tran_quantity = tran_quantity
mtran.to_quantity = stock + tran_quantity
mtran.create_user = request.user
mtran.save()
#异动原料库存量
material.stock = stock + tran_quantity
material.save()
#如果没有发生错误则修改原料库存异动申请单状态
mcheck.is_active = True
mcheck.save()
admin.site.register(Mcheck, McheckAdmin)
"""
商品库存异动申请单单身检查
1.最少要有一个单身
2.单身中商品不得重复
3.单身异动数量不得为0
4.单身商品异动后的库存数量不得小于0
"""
class PcheckDetailCheckInlineFormset(forms.models.BaseInlineFormSet):
def clean(self):
count = 0
product_list = []
for form in self.forms:
if form.cleaned_data:
count += 1
quantity = form.cleaned_data.get('quantity')
if quantity == 0:
raise forms.ValidationError(u'商品库存异动申请单中单身数量不得为0。')
product = form.cleaned_data.get('product')
if product.stock + quantity < 0:
raise forms.ValidationError(u"单身商品[{}-{}]库存量为 {},加上异动量 {} "
u"后库存小于0不合法,请重新填写单身中的异动"
u"数量。".format(product.id, product.title, product.stock, quantity))
if product.id in product_list:
raise forms.ValidationError(u"单身商品[{}-{}]已重复,"
u"请重新填写商品库存异动申请单。".format(product.id, product.title))
else:
product_list.append(product.id)
if count < 1:
raise forms.ValidationError(u'您必须最少输入一笔商品库存异动申请单单身')
class PcheckDetailInline(admin.TabularInline):
formset = PcheckDetailCheckInlineFormset
model = PcheckDetail
fields = ['product', 'quantity']
raw_id_fields = ['product']
extra = 0
class PcheckAdmin(admin.ModelAdmin):
list_display = ['id', 'is_active', 'description', 'created', 'create_user']
fields = ['description']
inlines = [PcheckDetailInline]
view_on_site = False
def save_model(self, request, obj, form, change):
if not change:
obj.create_user = request.user
super().save_model(request, obj, form, change)
def save_related(self, request, form, formsets, change):
super().save_related(request, form, formsets, change)
pcheck = form.save(commit=False)
pcheck_details = PcheckDetail.objects.filter(pcheck=pcheck)
for pcheck_detail in pcheck_details.all():
#新增商品库存异动
product = pcheck_detail.product
stock = product.stock
tran_quantity = pcheck_detail.quantity
ptran = Ptran()
ptran.product = product
ptran.source_form = 'PCHECK'
ptran.source_id = pcheck.id
ptran.from_quantity = stock
ptran.tran_quantity = tran_quantity
ptran.to_quantity = stock + tran_quantity
ptran.create_user = request.user
ptran.save()
#异动商品库存量
product.stock = stock + tran_quantity
product.save()
#如果没有发生错误则修改商品库存异动申请单状态
pcheck.is_active = True
pcheck.save()
admin.site.register(Pcheck, PcheckAdmin)
"""
制程单单身检查
1.最少要有一个单身
"""
class ProcessDetailCheckInlineFormset(forms.models.BaseInlineFormSet):
def clean(self):
count = 0
product_list = []
for form in self.forms:
if form.cleaned_data:
count += 1
if count < 1:
raise forms.ValidationError(u'此商品无制程BOM表资料,请修改好该商品BOM表数据再填写制程单')
class ProcessDetailInline(admin.TabularInline):
formset = ProcessDetailCheckInlineFormset
model = ProcessDetail
fields = ['material', 'quantity']
raw_id_fields = ['material']
extra = 0
class ProcessAdmin(admin.ModelAdmin):
list_display = ['id', 'order', 'product', 'quantity', 'description', 'status', 'claimed',
'receipted', 'created', 'create_user']
fields = ['order', 'product', 'quantity', 'description']
inlines = [ProcessDetailInline]
view_on_site = False
list_filter = ['order', 'product', 'status']
def save_model(self, request, obj, form, change):
if not change:
obj.create_user = request.user
super().save_model(request, obj, form, change)
def save_formset(self, request, form, formset, change):
if not change:
process = form.save(commit=False)
instances = formset.save(commit=False)
for instance in instances:
instance.quantity = instance.quantity * process.quantity
super().save_formset(request, form, formset, change)
admin.site.register(Process, ProcessAdmin)
3.3.templates 设定
我说明一下我希望制程单达到的效果,由于原料出库到商品制作完成入库需要四到八周的时间,于是我希望制程单能有[原料出库]与[商品入库]这两个动作,也就是如下图的效果:
除了呈现上图的效果外,在按下[确认原料出库]与[确认商品入库]后会跳出如图二与图四的对话视窗,除了跳出视窗之外,也会异动原料与商品的库存量,这样就需要借助jquery(覆盖默认change_form.html)与视图函式(views.py)的帮助了。
制程单的change_form.html如下:
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static admin_modify %}
{% block extrahead %}{{ block.super }}
<script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
{{ media }}
{% endblock %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}">{% endblock %}
{% block coltype %}colM{% endblock %}
{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-form{% endblock %}
{% if not is_popup %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
› {% if has_view_permission %}<a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}
› {% if add %}{% blocktrans with name=opts.verbose_name %}Add {{ name }}{% endblocktrans %}{% else %}{{ original|truncatewords:"18" }}{% endif %}
</div>
{% endblock %}
{% endif %}
{% block content %}<div id="content-main">
{% block object-tools %}
{% if change %}{% if not is_popup %}
<ul class="object-tools">
{% block object-tools-items %}
{% change_form_object_tools %}
{% endblock %}
</ul>
{% endif %}{% endif %}
{% endblock %}
<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.model_name }}_form" novalidate>{% csrf_token %}{% block form_top %}{% endblock %}
<div>
{% if is_popup %}<input type="hidden" name="{{ is_popup_var }}" value="1">{% endif %}
{% if to_field %}<input type="hidden" name="{{ to_field_var }}" value="{{ to_field }}">{% endif %}
{% if save_on_top %}{% block submit_buttons_top %}{% submit_row %}{% endblock %}{% endif %}
{% if errors %}
<p class="errornote">
{% if errors|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %}
</p>
{{ adminform.form.non_field_errors }}
{% endif %}
{% block field_sets %}
{% for fieldset in adminform %}
{% include "./includes/fieldset.html" %}
{% endfor %}
{% endblock %}
{% block after_field_sets %}{% endblock %}
{% block inline_field_sets %}
{% for inline_admin_formset in inline_admin_formsets %}
{% include inline_admin_formset.opts.template %}
{% endfor %}
{% endblock %}
{% block after_related_objects %}{% endblock %}
{% block submit_buttons_bottom %}{% submit_row %}{% endblock %}
{% block admin_change_form_document_ready %}
<script type="text/javascript"
id="django-admin-form-add-constants"
src="{% static 'admin/js/change_form.js' %}"
{% if adminform and add %}
data-model-name="{{ opts.model_name }}"
{% endif %}>
</script>
{% endblock %}
{# JavaScript for prepopulated fields #}
{% prepopulated_fields_js %}
<script>
(function($) {
$(document).ready(function(){
var $form = $('#process_form');
var formset_name = 'processdetail_set';
var $formset_div = $('#' + formset_name + '-group');
var $formset_table = $formset_div.find('table');
var $formset_add_row_tr = $formset_div.find('tr.add-row')
var $submit_row_div = $form.find('div.submit-row');
var $formset_empty_row = $formset_table.find('tbody tr#' + formset_name + '-empty');
var $formset_total_input = $formset_div.find('input#id_' + formset_name + '-TOTAL_FORMS')
$formset_add_row_tr.hide();
$submit_row_div.hide();
var href = location.href;
href_list = href.split('/');
<!-- 表示为change -->
if (href_list[href_list.length - 2] == 'change'){
var process_id = $('#id_processdetail_set-0-process').val();
var html = '<a id="receipt" href="javascript:void(0);" class="closelink">确认商品入库</a><a id="claim" href="javascript:void(0);" class="closelink">确认原料出库</a><a href="/admin/inventory/process/" class="closelink">Close</a>';
$('div.submit-row').html(html);
$('div.submit-row').show();
$.get('{% url 'inventory:process_ajax_status' %}', {'id': process_id}, function(data){
if(data['code'] == 0){
<!-- 立单申请 -->
if(data['status'] == 'A'){
$('a#receipt').hide();
}
<!-- 原料已领料 -->
else if(data['status'] == 'C'){
$('a#claim').hide();
}
else{
$('a#receipt').hide();
$('a#claim').hide();
}
}
else{
alert(data['msg']);
}
}, 'json')
}
if($('#id_order').val() == '')
{
<!-- 把非"对应订单"的输入字段隐藏起来 -->
$form.find('fieldset:first>div').each(function(){
if(!$(this).hasClass('field-order')){
$(this).hide();
}
})
<!-- 隐藏"formset",""储存窗体按钮"" -->
$formset_div.hide();
$submit_row_div.hide();
$submit_row_div.find('input').each(function(){
$(this).click(function(){
$('#id_order').removeAttr("disabled");
$('#id_product').removeAttr("disabled");
});
});
<!-- 选择"对应订单"之后 -->
$('#id_order').change(function(){
var order_id = $(this).val();
if(order_id != ''){
$(this).attr("disabled","disabled");
$form.find('fieldset:first>div').each(function(){
if($(this).hasClass('field-product')){
$(this).show();
<!-- 根据订单编号取得该订单单身商品 -->
var id_str = '';
$.get('{% url 'inventory:process_ajax_order_product_list' %}', {'id': order_id}, function(data){
if(data['code'] == 0){
$("#id_product option:gt(0)").remove();
for(p in data['products']){
$('#id_product').append("<option value='" + data['products'][p]['id'] + "'>" + data['products'][p]['title'] + "</option>");
}
if (data['msg'] != '')
alert(data['msg']);
}
else{
alert(data['msg']);
}
}, 'json')
}
})
<!-- 选择"对应商品"之后 -->
$('#id_product').change(function(){
var product_id = $(this).val();
if(product_id != ''){
$(this).attr("disabled","disabled");
$form.find('fieldset:first>div').each(function(){
if(!$(this).hasClass('field-order') && !$(this).hasClass('field-product')){
$(this).show();
}
})
$.get('{% url 'inventory:process_ajax_bom_list' %}', {'order_id': order_id, 'product_id': product_id}, function(data){
if(data['code'] == 0){
$formset_div.show();
$submit_row_div.show();
<!-- 将数据填入窗体中 -->
$tbody = $formset_table.find('tbody');
var boms = data['boms'];
var index = 0;
for(b in boms){
total = $formset_total_input.val();
$new_row = $formset_empty_row.clone(true);
$formset_empty_row.removeClass('row' + (index + 1) % 3);
$formset_empty_row.addClass('row' + (index + 2) % 3);
$new_row.attr('id', formset_name + '-' + total);
$new_row.removeClass('empty-form');
$new_row.find('input').each(function(){
name = $(this).attr('name');
id = $(this).attr('id');
$(this).attr('name', name.replace(/__prefix__/, index));
$(this).attr('id', id.replace(/__prefix__/, index));
name_list = name.split('-');
if(name_list[name_list.length -1] == "material"){
$(this).val(boms[b]['material']['id']);
$(this).attr('readonly', 'readonly');
$(this).parent().append(boms[b]['material']['title']);
}
else if(name_list[name_list.length -1] == "quantity"){
$(this).val(boms[b]['quantity']);
$(this).attr('readonly', 'readonly');
}
});
$new_row.find('a').remove();
$formset_empty_row.before($new_row);
total++;
$formset_total_input.val(total);
index++;
}
$form.find('fieldset:first>div').each(function(){
if($(this).hasClass('field-quantity')){
$(this).find('input').val(data['quantity']);
}
})
}
else{
alert(data['msg']);
}
}, 'json')
}
});
}
});
}
else{
$('#id_order').attr("disabled","disabled");
$('#id_product').attr("disabled","disabled");
}
$(document).on("click", "a#claim", function(data){
$.get('{% url 'inventory:process_ajax_claim_material' %}', {'id': process_id}, function(data){
if(data['code'] == 0){
alert(data['msg']);
$('a#claim').hide();
}
else{
alert(data['msg']);
}
}, 'json')
});
$(document).on("click", "a#receipt", function(data){
$.get('{% url 'inventory:process_ajax_receipt_product' %}', {'id': process_id}, function(data){
if(data['code'] == 0){
alert(data['msg']);
$('a#receipt').hide();
}
else{
alert(data['msg']);
}
}, 'json')
});
});
})(django.jQuery);
</script>
</div>
</form></div>
{% endblock %}
其中值得留意的部份如下:
var html = '<a id="receipt" href="javascript:void(0);" class="closelink">确认商品入库</a><a id="claim" href="javascript:void(0);" class="closelink">确认原料出库</a><a href="/admin/inventory/process/" class="closelink">Close</a>';
$('div.submit-row').html(html);
$('div.submit-row').show();
$.get('{% url 'inventory:process_ajax_status' %}', {'id': process_id}, function(data){
if(data['code'] == 0){
<!-- 立单申请 -->
if(data['status'] == 'A'){
$('a#receipt').hide();
}
<!-- 原料已领料 -->
else if(data['status'] == 'C'){
$('a#claim').hide();
}
else{
$('a#receipt').hide();
$('a#claim').hide();
}
}
else{
alert(data['msg']);
}
}, 'json')
3.4.urls.py 设定
from django.urls import path
from . import views
app_name = 'inventory'
urlpatterns = [
path('process/ajax/order/product/list/', views.process_ajax_order_product_list, name='process_ajax_order_product_list'),
path('process/ajax/bom/list/', views.process_ajax_bom_list, name='process_ajax_bom_list'),
path('process/ajax/status/', views.process_ajax_status, name='process_ajax_status'),
path('process/ajax/claim/material/', views.process_ajax_claim_material, name='process_ajax_claim_material'),
path('process/ajax/receipt/product/', views.process_ajax_receipt_product, name='process_ajax_receipt_product'),
]
3.5.views.py 设定
from django.http import HttpResponse, JsonResponse, HttpResponseRedirect
from .models import Mtran, Ptran, Process, ProcessDetail
from basic.models import Bom, Product
from sale.models import Order, OrderProduct
import datetime
def process_ajax_order_product_list(request):
return_dict = {}
#判断使用者是否有权限检视制程单
if request.user.has_perm('inventory.view_process'):
order_id = request.GET.get('id')
order = Order.objects.get(id=order_id)
return_dict['code'] = 0
return_dict['msg'] = ''
return_dict['products'] = []
order_products = OrderProduct.objects.filter(order=order)
for p in order_products.all():
product = p.product
product_info = {}
product_info['id'] = product.id
product_info['title'] = product.title
return_dict['products'].append(product_info)
other_processes = Process.objects.filter(order=order)
if other_processes.count() > 0:
return_dict['msg'] = u'该订单已有其他制程单,制程单号(商品名称):'
for p in other_processes.all():
other_product = p.product
return_dict['msg'] += ' {}({})'.format(str(p.id), other_product.title)
else:
return_dict['code'] = 1
return_dict['msg'] = u"您无权限浏览制程单"
return JsonResponse(return_dict)
def process_ajax_bom_list(request):
return_dict = {}
#判断使用者是否有权限检视制程单
if request.user.has_perm('inventory.view_process'):
order_id = request.GET.get('order_id')
order = Order.objects.get(id=order_id)
product_id = request.GET.get('product_id')
product = Product.objects.get(id=product_id)
boms = Bom.objects.filter(product=product)
return_dict['code'] = 0
return_dict['msg'] = ''
return_dict['boms'] = []
order_products = OrderProduct.objects.filter(order=order, product=product)
if order_products.count() > 0:
return_dict['quantity'] = order_products[0].quantity
else:
return_dict['quantity'] = 0
for b in boms.all():
material = b.material
quantity = b.quantity
material_info = {}
material_info['id'] = material.id
material_info['title'] = material.title
material_dict = {'material': material_info, 'quantity': quantity}
return_dict['boms'].append(material_dict)
else:
return_dict['code'] = 1
return_dict['msg'] = u"您无权限浏览制程单"
return JsonResponse(return_dict)
def process_ajax_status(request):
return_dict = {}
#判断使用者是否有权限检视制程单
if request.user.has_perm('inventory.view_process'):
process_id = request.GET.get('id')
process = Process.objects.get(id=process_id)
return_dict['code'] = 0
return_dict['status'] = process.status
else:
return_dict['code'] = 1
return_dict['msg'] = u"您无权限浏览制程单"
return JsonResponse(return_dict)
def process_ajax_claim_material(request):
return_dict = {}
#判断使用者是否有权限检视制程单
if request.user.has_perm('inventory.view_process'):
process_id = request.GET.get('id')
process = Process.objects.get(id=process_id)
process_details = ProcessDetail.objects.filter(process=process)
if process_details.count() > 0:
stock_enough_bool = True
for process_detail in process_details:
quantity = process_detail.quantity
material = process_detail.material
if material.stock < quantity:
return_dict['code'] = 1
return_dict['msg'] = '原料[{}-{}]库存量只有{},不足领出量{}'.format(material.id, material.title, material.stock, quantity)
stock_enough_bool = False
if stock_enough_bool:
for process_detail in process_details:
quantity = process_detail.quantity
material = process_detail.material
mtran = Mtran()
mtran.material = material
mtran.source_form = 'PROCESS'
mtran.source_id = process_id
mtran.from_quantity = material.stock
mtran.tran_quantity = 0 - quantity
mtran.to_quantity = material.stock - quantity
mtran.create_user = request.user
mtran.save()
material.stock = material.stock - quantity
material.save()
process.status = 'C'
process.claimed = datetime.datetime.now()
process.claim_user = request.user
process.save()
return_dict['code'] = 0
return_dict['msg'] = u"原料出库已确认完毕"
else:
return_dict['code'] = 1
return_dict['msg'] = u"该制程单无单身资料"
else:
return_dict['code'] = 1
return_dict['msg'] = u"您无权限浏览制程单"
return JsonResponse(return_dict)
def process_ajax_receipt_product(request):
return_dict = {}
#判断使用者是否有权限检视制程单
if request.user.has_perm('inventory.view_process'):
process_id = request.GET.get('id')
process = Process.objects.get(id=process_id)
product = process.product
quantity = process.quantity
ptran = Ptran()
ptran.product = product
ptran.source_form = 'PROCESS'
ptran.source_id = process_id
ptran.from_quantity = product.stock
ptran.tran_quantity = quantity
ptran.to_quantity = product.stock + quantity
ptran.create_user = request.user
ptran.save()
product.stock = product.stock + quantity
product.save()
process.status = 'R'
process.receipted = datetime.datetime.now()
process.receipt_user = request.user
process.save()
return_dict['code'] = 0
return_dict['msg'] = u"商品入库已确认完毕"
else:
return_dict['code'] = 1
return_dict['msg'] = u"您无权限浏览制程单"
return JsonResponse(return_dict)