新手分享边爬文边写的 Django 用 admin 开发的简易进销存系统-4

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>
&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; {% if has_view_permission %}<a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}
&rsaquo; {% 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)
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值