赞
踩
订单模块是本项目最起始的模块,有了订单之后才会有接下去的所有动作,本模块一样分成单头与单身。
CAT_CHOICES = [ ('1', 'CAT1'), ('2', 'CAT2'), ('3', 'CAT3'), ] ORDER_CHOICES = [ ('N', u'尚未出货'), ('A', u'全部出货'), ('P', u'部分出货'), ('O', u'超额出货'), ] class Order(models.Model): cat_id = models.CharField(max_length=2, choices=CAT_CHOICES, blank=False, verbose_name=u'Cat.') pi_no = models.CharField(max_length=16, verbose_name='PI #', blank=False, null=False, unique=True) po_no = models.CharField(max_length=16, verbose_name='PO #', blank=True) customer = models.ForeignKey(Customer, verbose_name=u'客户', on_delete=models.PROTECT, limit_choices_to={'status': 'A'},) is_active = models.BooleanField(verbose_name=u'是否有效', default=True) is_urgency = models.BooleanField(verbose_name=u'是否为急单', default=False) status = models.CharField(max_length=1, choices=ORDER_CHOICES, default='N', verbose_name=u'状态') etd = models.DateField(blank=False, null=False, verbose_name=u'预定出货日期') created = models.DateTimeField(auto_now_add=True, verbose_name=u'订购时间') create_user = models.ForeignKey('auth.User', verbose_name=u'建立人员', on_delete=models.PROTECT) def get_absolute_url(self): return reverse('sale:order_detail', kwargs={'pk': self.pk}) def __str__(self): return 'Id:{}-PI #:{}- PO #:{}'.format(self.id, self.pi_no, self.po_no) class Meta: verbose_name = '订单' verbose_name_plural = verbose_name permissions = ( ('active_order', '可中止订单'), )
class OrderProduct(models.Model): num = models.CharField(max_length=2, default='', verbose_name=u'单身项次') order = models.ForeignKey(Order, verbose_name=u'订单单头', on_delete=models.CASCADE) product = models.ForeignKey(Product, verbose_name=u'商品', on_delete=models.DO_NOTHING, limit_choices_to={'status': 'A'},) quantity = models.PositiveIntegerField(blank=False, verbose_name=u'订购数量') price = models.DecimalField(max_digits=16, decimal_places=4, blank=False, verbose_name=u'未税金额') tax = models.DecimalField(max_digits=16, decimal_places=4, blank=False, verbose_name=u'税金') tax_price = models.DecimalField(max_digits=16, decimal_places=4, blank=False, verbose_name=u'含税金额') subtotal = models.DecimalField(max_digits=16, decimal_places=4, blank=False, verbose_name=u'小计未税金额') tax_subtotal = models.DecimalField(max_digits=16, decimal_places=4, blank=False, verbose_name=u'小计含税金额') currency = models.ForeignKey(Currency, verbose_name=u'币别', blank=False, on_delete=models.DO_NOTHING) description = models.CharField(max_length=256, verbose_name=u'描述', blank=True) def save(self, *args, **kwargs): if self.num == '': #项次的格式是01,02... order_products = OrderProduct.objects.filter(order=self.order) num = "{0:02d}".format(order_products.count() + 1) self.num = "{0:02d}".format(int(num)) product = Product.objects.get(title=self.product) self.price = product.price self.tax = product.tax self.currency = product.currency self.tax_price = product.tax_price self.subtotal = product.price * self.quantity self.tax_subtotal = product.tax_price * self.quantity super().save(*args, **kwargs) def __str__(self): return '{}'.format(self.num) class Meta: verbose_name = '订单单身' verbose_name_plural = verbose_name
接到订单后,如果商品库存有的话则可以安排出给客户,如果没有的话则要使用制程单制作好商品后出货,出货单一样分单头单身。
class Ship(models.Model): order = models.ForeignKey(Order, verbose_name=u'对应订单', on_delete=models.PROTECT) customer = models.ForeignKey(Customer, verbose_name=u'客户', on_delete=models.PROTECT, limit_choices_to={'status': 'A'}) is_active = models.BooleanField(verbose_name=u'是否有效', default=False) is_delay = models.BooleanField(verbose_name=u'是否延迟', default=False) created = models.DateTimeField(auto_now_add=True, verbose_name=u'出货时间') create_user = models.ForeignKey('auth.User', verbose_name=u'建立人员', on_delete=models.PROTECT) def get_absolute_url(self): return reverse('sale:ship_detail', kwargs={'pk': self.pk}) def __str__(self): return '{}'.format(self.id) class Meta: verbose_name = '出货单' verbose_name_plural = verbose_name permissions = ( ('active_ship', '可中止出货单'), )
class ShipDetail(models.Model): num = models.CharField(max_length=2, default='', verbose_name=u'单身项次') ship = models.ForeignKey(Ship, verbose_name=u'出货单单头', on_delete=models.CASCADE) product = models.ForeignKey(Product, verbose_name=u'出货商品', on_delete=models.CASCADE) quantity = models.PositiveIntegerField(blank=False, verbose_name=u'出货数量') description = models.CharField(max_length=256, verbose_name=u'描述', blank=True) def save(self, *args, **kwargs): if self.num == '': #项次的格式是01,02... ship_detail = ShipDetail.objects.filter(ship=self.ship) num = "{0:02d}".format(ship_detail.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
本模块的admin设定也相对简单,所以也不多说明了。
from django import forms from django.contrib import admin, messages from django.contrib.auth import get_permission_codename import datetime from .models import Order, OrderProduct, Ship, ShipDetail from finance.models import Receivable, ReceivableDetail from inventory.models import Ptran """ 订单单身检查 1.最少要有一个单身 2.商品不可重复 3.商品数量是否大于0 """ class OrderProductCheckInlineFormset(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_id = form.cleaned_data.get('product') if product_id in product_list: raise forms.ValidationError(u'订单中商品不得重复。') else: product_list.append(product_id) if count < 1: raise forms.ValidationError(u'您最少必须输入一笔订单单身。') class OrderProductInline(admin.TabularInline): formset = OrderProductCheckInlineFormset model = OrderProduct fields = ['product', 'quantity', 'description'] raw_id_fields = ['product'] extra = 0 class OrderAdmin(admin.ModelAdmin): list_display = ['id', 'cat_id', 'pi_no', 'po_no', 'customer', 'is_urgency', 'status', 'etd', 'created', 'create_user'] fields = ['cat_id', 'po_no', 'customer', 'is_urgency', 'etd'] list_filter = ['is_urgency', 'status'] actions = ['make_actived'] inlines = [OrderProductInline] view_on_site = False list_per_page = 10 list_max_show_all = 100 date_hierarchy = 'created' def save_model(self, request, obj, form, change): if not change: obj.create_user = request.user """ pi_no的格式是R201901121 R:开头 201901:2019年1月 121:三码流水码 """ pattern = 'R' + datetime.datetime.now().strftime("%Y%m") orders = Order.objects.filter(pi_no__startswith=pattern) obj.pi_no = pattern + "{0:03d}".format(orders.count() + 1) super().save_model(request, obj, form, change) def make_actived(self, request, queryset): rows = queryset.update(is_active=False) if rows > 0: self.message_user(request, u'已完成终止订单动作') make_actived.allowed_permissions = ('active',) make_actived.short_description = u'终止订单' def has_active_permission(self, request): opts = self.opts codename = get_permission_codename('active', opts) return request.user.has_perm('%s.%s' % (opts.app_label, codename)) admin.site.register(Order, OrderAdmin) """ 出货单单身检查 1.最少要有一个单身 2.商品库存是否足够 3.出货数量是否至少一笔大于0 4.出货数量不得是负数 4.单身商品不得重复 """ class ShipDetailCheckInlineFormset(forms.models.BaseInlineFormSet): def clean(self): detail_count = 0 detail_amount = 0 product_list = [] for form in self.forms: if form.cleaned_data: detail_count += 1 quantity = form.cleaned_data.get('quantity') if quantity < 0: raise forms.ValidationError(u'出货单中商品数量不得是负数。') elif quantity > 0: detail_amount += 1 product = form.cleaned_data.get('product') quantity = int(form.cleaned_data.get('quantity')) if product.stock < quantity: raise forms.ValidationError(u"商品[{}-{}]库存 {},无法满足此次出货量 {}," 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 detail_count < 1: raise forms.ValidationError(u'您必须最少输入一笔订单单身') if detail_amount < 1: raise forms.ValidationError(u'您必须最少一笔出货单单身数量大于0') class ShipDetailInline(admin.TabularInline): formset = ShipDetailCheckInlineFormset model = ShipDetail fields = ['product', 'quantity', 'description'] raw_id_fields = ['product'] extra = 0 """ 出货时的相关动作如下: 1.对于每个出货单单身而言 i.新增 商品库存异动(Ptran) ii.如果出货数量>0时则新增 应收帐款单身(ReceivableDetail) iii.减少 商品库存量(product.stock) 2.对于整个出货单而言 i.将出货单的 状态修改为有效出货(is_active=True) 检查出货日期是否有延迟(is_delay) ii.如果出货数量>0时则新增一笔 应收帐款(Receivable) iii.检查订单数量是否满足,决定订单"状态",('A', '全部出货'),('P', '部分出货') """ class ShipAdmin(admin.ModelAdmin): list_display = ['id', 'order', 'customer', 'is_active', 'is_delay', 'created', 'create_user'] fields = ['order'] actions = ['make_actived'] inlines = [ShipDetailInline] view_on_site = False list_per_page = 10 list_max_show_all = 100 date_hierarchy = 'created' def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == 'order': kwargs['queryset'] = Order.objects.filter(is_active=True).exclude(status='A') return super(ShipAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs) def save_model(self, request, obj, form, change): if not change: obj.create_user = request.user order = obj.order obj.customer = order.customer super().save_model(request, obj, form, change) def save_related(self, request, form, formsets, change): super().save_related(request, form, formsets, change) ship = form.save(commit=False) #新增应收帐款单头 receivable = Receivable() receivable.ship = ship receivable.customer = ship.customer #应收日期=出货日期 + 帐期 receivable.receivabled = datetime.datetime.now().date() + datetime.timedelta(days=ship.customer.period.period) receivable.create_user = ship.create_user receivable.save() ship_details = ShipDetail.objects.filter(ship=ship) for ship_detail in ship_details: #新增商品库存异动 product = ship_detail.product stock = product.stock tran_quantity = ship_detail.quantity ptran = Ptran() ptran.product = product ptran.source_form = 'SHIP' ptran.source_id = ship.id ptran.from_quantity = stock ptran.tran_quantity = 0 - tran_quantity ptran.to_quantity = stock - tran_quantity ptran.create_user = request.user ptran.save() #新增应收帐款单身 receivable_detail = ReceivableDetail() receivable_detail.receivable = receivable receivable_detail.product = product receivable_detail.amount = product.tax_price * tran_quantity receivable_detail.currency = product.currency receivable_detail.save() #减少商品库存量 product.stock = stock - tran_quantity product.save() order = form.cleaned_data.get('order') #修改出货单状态 ship.is_active = True today = datetime.datetime.now().date() if today > order.etd: ship.is_delay = True ship.save() #决定对应订单状态('A', '全部出货'),('P', '部分出货'),('O', '超额出货')如果有一个订单单身数量没有全部出完的话,则状态为P order_products = OrderProduct.objects.filter(order=order) ships = Ship.objects.filter(order=order) status = 'A' for order_product in order_products: product = order_product.product shipped_quantity = 0 for ship in ships.all(): ship_details = ShipDetail.objects.filter(product=product, ship=ship) if ship_details.all().count() > 0: for ship_detail in ship_details.all(): shipped_quantity += ship_detail.quantity if shipped_quantity < order_product.quantity: status = 'P' else: if shipped_quantity > order_product.quantity: #如果有商品是部分出货P,就算是有超额出货也还是算成P if status is 'P': status = 'P' else: status = 'O' order.status = status order.save() #如果没有发生错误则修改应收帐款状态 receivable.is_active = True receivable.save() def make_actived(self, request, queryset): rows = queryset.update(is_active=False) if rows > 0: self.message_user(request, u'已完成终止出货单动作') make_actived.allowed_permissions = ('active',) make_actived.short_description = u'终止出货单' def has_active_permission(self, request): opts = self.opts codename = get_permission_codename('active', opts) return request.user.has_perm('%s.%s' % (opts.app_label, codename)) admin.site.register(Ship, ShipAdmin)
销货模块里面只有订单与出货单两个主体,出货单我希望的呈现方式如下:
1.先让使用者选择对应的订单
2.选择好对应订单后再显示其他字段,并在单身中带出订单单身
使用的方式一样是jquery(change_form.html)与views.py,位置与代码如下:
{% 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 = $('#ship_form'); var formset_name = 'shipdetail_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'){ $('div.submit-row').html('<a href="/admin/sale/ship/" class="closelink">Close</a>'); $('div.submit-row').show(); } 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(); <!-- 选择"对应订单"之后 --> $('#id_order').change(function(){ var order_id = $(this).val(); if(order_id != ''){ $(this).attr("disabled","disabled"); <!-- 移除已有的出货单身 --> <!-- $formset_div.find('table tbody .form-row').remove(); --> <!-- 根据对应订单产生出货单身 --> $.get('{% url 'sale:order_ajax_product_list' %}', {'id':order_id}, function(data){ if(data['code'] == 0){ $form.find('fieldset:first>div').each(function(){ if(!$(this).hasClass('field-order')){ $(this).show(); } }) $formset_div.show(); $submit_row_div.show(); $submit_row_div.find('input').each(function(){ $(this).click(function(){ $('#id_order').removeAttr("disabled"); }); }); <!-- 将数据填入窗体中 --> $tbody = $formset_table.find('tbody'); var products = data['products']; var index = 0; for(p in products){ 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__/, total)); $(this).attr('id', id.replace(/__prefix__/, total)); name_list = name.split('-'); if(name_list[name_list.length -1] == "product"){ $(this).val(products[p]['product']['id']); $(this).attr('readonly', 'readonly'); $(this).parent().append(products[p]['product']['title']); } else if(name_list[name_list.length -1] == "quantity"){ $(this).val(products[p]['quantity']); } }); $new_row.find('a').remove(); $formset_empty_row.before($new_row); total++; $formset_total_input.val(total); index++; } if (data['msg'] != '') alert(data['msg']); } else{ alert(data['msg']); } }, 'json') } }); } else{ $('#id_order').attr("disabled","disabled"); } }); })(django.jQuery); </script> </div> </form></div> {% endblock %}
from django.urls import path
from django.contrib.auth.decorators import login_required
from . import views
app_name = 'sale'
urlpatterns = [
path('order/ajax/list/', views.order_ajax_product_list, name='order_ajax_product_list'),
]
from django.http import HttpResponse, JsonResponse, HttpResponseRedirect from .models import Order, OrderProduct, Ship, ShipDetail def order_ajax_product_list(request): return_dict = {} #判断用户是否有权限检视订单 if request.user.has_perm('sale.view_order'): 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 quantity = p.quantity # 如果该订单有其他对应出货单,则需扣掉已出货的商品数量 ships = Ship.objects.filter(order=order, is_active=True) if ships.count() > 0: return_dict['msg'] = u'此订单已有对应出货单,出货单单号如下:' for s in ships.all(): return_dict['msg'] += ' ' + str(s.id) ship_product = ShipDetail.objects.filter(ship=s, product=product) if ship_product.all().count() > 0: quantity -= ship_product[0].quantity product_info = {} product_info['id'] = product.id product_info['title'] = product.title product_dict = {'product': product_info, 'quantity': quantity} return_dict['products'].append(product_dict) else: return_dict['code'] = 1 return_dict['msg'] = u"您无权限浏览订单" return JsonResponse(return_dict)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。