1.基本设定(basic)模块
1.1.models.py 设定
1.1.1.币别
这个是唯一一个主键值不是1,2,…的模型,主是要用来测试用这样的做法是否会有不同,测试了之后决定还是用默认的方式遇到的问题会比较少。这个模型是用来提供商品与原料所使用的报价币别选择。
class Currency(models.Model):
id = models.CharField(max_length=3, primary_key=True, verbose_name=u'币别代号')
title = models.CharField(max_length=8, blank=False, verbose_name=u'币别')
rate = models.DecimalField(max_digits=16, decimal_places=4, blank=False, verbose_name=u'币别')
def __str__(self):
return self.id
class Meta:
verbose_name = '币别'
verbose_name_plural = verbose_name
1.1.2.账期
提供客户与供应商的帐期选择。
class Period(models.Model):
title = models.CharField(max_length=32, blank=False, verbose_name=u'帐期名称')
period = models.PositiveIntegerField(blank=False, verbose_name=u'天数', help_text=u'天数为整数')
def __str__(self):
return self.title
class Meta:
verbose_name = '帐期'
verbose_name_plural = verbose_name
1.1.3.供应商
供应商在建立之后需要通过审核。
STATUS_CHOICES = (
('W', u'等待审核'),
('A', u'已审核'),
('S', u'停止使用'),
)
class Supplier(models.Model):
title = models.CharField(max_length=128, unique=True, blank=False, verbose_name=u'名称')
contacter = models.CharField(max_length=32, verbose_name=u'联络人', blank=True)
period = models.ForeignKey(Period, verbose_name=u'帐期', on_delete=models.PROTECT)
landline = models.CharField(max_length=16, verbose_name=u'联络市话', blank=True)
mobile = models.CharField(max_length=16, verbose_name=u'联络手机号', help_text='ex:12345678901',blank=True)
wechat_account = models.CharField(max_length=16, verbose_name=u'联络微信账号', blank=True)
email = models.EmailField(verbose_name=u'联络email', blank=True)
address = models.CharField(max_length=256, verbose_name=u'联络地址', blank=True)
description = models.CharField(max_length=256, verbose_name=u'描述', blank=True)
status = models.CharField(max_length=1, choices=STATUS_CHOICES, default='W', verbose_name=u'状态')
def get_absolute_url(self):
return reverse('basic:supplier_detail', args=[self.id])
def __str__(self):
return self.title
class Meta:
verbose_name = '供货商'
verbose_name_plural = verbose_name
permissions = (
('audit_supplier', '可审核供货商'),
('stop_supplier', '可终止供货商'),
)
1.1.4.客户
客户在建立之后需要通过审核。
class Customer(models.Model):
title = models.CharField(max_length=128, unique=True, blank=False, verbose_name=u'名称')
contacter = models.CharField(max_length=32, verbose_name=u'联络人', blank=True)
period = models.ForeignKey(Period, verbose_name=u'帐期', on_delete=models.PROTECT)
landline = models.CharField(max_length=16, verbose_name=u'联络市话', blank=True)
mobile = models.CharField(max_length=16, verbose_name=u'联络手机号', help_text='ex:12345678901', blank=True)
wechat_account = models.CharField(max_length=16, verbose_name=u'联络微信账号', blank=True)
email = models.EmailField(verbose_name=u'联络email', blank=True)
address = models.CharField(max_length=256, verbose_name=u'联络地址', blank=True)
description = models.CharField(max_length=256, verbose_name=u'描述', blank=True)
status = models.CharField(max_length=1, choices=STATUS_CHOICES, default='W', verbose_name=u'状态')
def get_absolute_url(self):
return reverse('basic:customer_detail', kwargs={'pk': self.pk})
def __str__(self):
return self.title
class Meta:
verbose_name = '客户'
verbose_name_plural = verbose_name
permissions = (
('audit_customer', '可审核客户'),
('stop_customer', '可终止客户'),
)
1.1.5.原料种类
原料的一个标签,在建立之后需要通过审核。
class Category(models.Model):
title = models.CharField(max_length=64, unique=True, blank=False, verbose_name=u'Part Number')
description = models.CharField(max_length=256, verbose_name=u'描述', blank=True)
status = models.CharField(max_length=1, choices=STATUS_CHOICES, default='W', verbose_name=u'状态')
def get_absolute_url(self):
return reverse('basic:category_detail', kwargs={'pk': self.pk})
def __str__(self):
return self.title
class Meta:
verbose_name = '原料种类'
verbose_name_plural = verbose_name
permissions = (
('audit_category', '可审核原料种类'),
('stop_category', '可终止原料种类'),
)
1.1.6.商品Part
商品的一个标签,在建立之后需要通过审核。
class Part(models.Model):
title = models.CharField(max_length=64, unique=True, blank=False, verbose_name=u'Part Number')
description = models.CharField(max_length=256, verbose_name=u'描述', blank=True)
status = models.CharField(max_length=1, choices=STATUS_CHOICES, default='W', verbose_name=u'状态')
def get_absolute_url(self):
return reverse('basic:part_detail', kwargs={'pk': self.pk})
def __str__(self):
return self.title
class Meta:
verbose_name = '商品Part'
verbose_name_plural = verbose_name
permissions = (
('audit_part', '可审核商品Part'),
('stop_part', '可终止商品Part'),
)
1.1.7.商品Size
商品的一个标签,在建立之后需要通过审核。
class Size(models.Model):
title = models.CharField(max_length=64, unique=True, blank=False, verbose_name=u'size')
description = models.CharField(max_length=256, verbose_name=u'描述', blank=True)
status = models.CharField(max_length=1, choices=STATUS_CHOICES, default='W', verbose_name=u'状态')
def get_absolute_url(self):
return reverse('basic:size_detail', kwargs={'pk': self.pk})
def __str__(self):
return self.title
class Meta:
verbose_name = '商品Size'
verbose_name_plural = verbose_name
permissions = (
('audit_size', '可审核商品Size'),
('stop_size', '可终止商品Size'),
)
1.1.8.原料
在建立之后需要通过审核。
class Material(models.Model):
title = models.CharField(max_length=64, unique=True, blank=False, verbose_name=u'料号')
image_sub = models.CharField(max_length=64, verbose_name=u'图文件在google共享文件夹的位置', blank=False, null=False)
supplier = models.ForeignKey(Supplier, verbose_name=u'供货商', on_delete=models.PROTECT)
category = models.ForeignKey(Category, verbose_name=u'种类', on_delete=models.PROTECT, limit_choices_to={'status': 'A'},)
price = models.DecimalField(max_digits=16, decimal_places=4, verbose_name=u'未税价', null=False, blank=False)
tax = models.DecimalField(max_digits=16, decimal_places=4, verbose_name=u'税金', null=False, blank=False)
tax_price = models.DecimalField(max_digits=16, decimal_places=4, verbose_name=u'含税价', null=False, blank=False)
currency = models.ForeignKey(Currency, verbose_name=u'报价币别', blank=False, null=False, on_delete=models.PROTECT)
description = models.CharField(max_length=256, verbose_name=u'描述', blank=True)
status = models.CharField(max_length=1, choices=STATUS_CHOICES, default='W', verbose_name=u'状态')
stock = models.PositiveIntegerField(default=0, verbose_name=u'库存数量')
def get_absolute_url(self):
return reverse('basic:material_detail', kwargs={'pk': self.pk})
def __str__(self):
return self.title
class Meta:
verbose_name = '原料'
verbose_name_plural = verbose_name
permissions = (
('audit_material', '可审核原料'),
('stop_material', '可终止原料'),
)
1.1.9.商品
在建立之后需要通过审核。
class Product(models.Model):
title = models.CharField(max_length=64, unique=True, blank=False, verbose_name='Model Name')
part = models.ForeignKey(Part, verbose_name='Part Number', null=True, blank=True,
on_delete=models.PROTECT, limit_choices_to={'status': 'A'},)
size = models.ForeignKey(Size, verbose_name='size', on_delete=models.PROTECT, limit_choices_to={'status': 'A'},)
image_sub = models.CharField(max_length=64, verbose_name=u'图文件在google共享文件夹的位置', blank=False, null=False)
price = models.DecimalField(max_digits=16, decimal_places=4, verbose_name=u'未税价', null=False, blank=False)
tax = models.DecimalField(max_digits=16, decimal_places=4, verbose_name=u'税金', null=False, blank=False)
tax_price = models.DecimalField(max_digits=16, decimal_places=4, verbose_name=u'含税价', null=False, blank=False)
currency = models.ForeignKey(Currency, verbose_name=u'报价币别', blank=False, null=False, on_delete=models.PROTECT)
description = models.CharField(max_length=256, verbose_name=u'描述', blank=True)
combines = models.ManyToManyField(Material, through='Bom')
status = models.CharField(max_length=1, choices=STATUS_CHOICES, default='W', verbose_name=u'状态')
stock = models.PositiveIntegerField(default=0, verbose_name=u'库存数量')
def get_absolute_url(self):
return reverse('basic:product_detail', kwargs={'pk': self.pk})
def __str__(self):
return self.title
class Meta:
verbose_name = u'商品'
verbose_name_plural = verbose_name
permissions = (
('audit_product', '可审核商品'),
('stop_product', '可终止商品'),
)
1.1.10.BOM表
商品与原料之间的关系,简单来说就是商品A需要原料1几个+原料2几个。
class Bom(models.Model):
num = models.CharField(max_length=2, default='', verbose_name=u'单身项次')
product = models.ForeignKey(Product, verbose_name='商品', on_delete=models.CASCADE)
material = models.ForeignKey(Material, verbose_name='原料', on_delete=models.PROTECT, limit_choices_to={'status': 'A'},)
quantity = models.PositiveIntegerField(default=1, verbose_name=u'组成数量')
def save(self, *args, **kwargs):
if self.num == '':
#项次的格式是01,02...
boms = Bom.objects.filter(product=self.product)
num = "{0:02d}".format(boms.count() + 1)
self.num = "{0:02d}".format(int(num))
super().save(*args, **kwargs)
def __str__(self):
return '{}'.format(self.num)
class Meta:
verbose_name = 'BOM表'
verbose_name_plural = verbose_name
1.2.admin.py 设定
1.2.1.其他model
由于admin的显示方式除了商品-BOM表之外没比较特别的,所以我其他的就带过,下面再来解释商品-BOM表的显示方式。
from django.contrib import admin
from django.contrib.auth import get_permission_codename
from django.db import models
from .models import Bom, Currency, Period, Supplier, Customer, Category, Part, Size, Material, Product
admin.site.site_header = '进销存系统'
@admin.register(Period)
class PeriodAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'period']
view_on_site = False
@admin.register(Currency)
class CurencyAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'rate']
view_on_site = False
class SupplierAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'contacter', 'period', 'status']
fields = ['title', 'contacter', 'period', 'landline', 'mobile', 'wechat_account', 'email', 'address',
'description']
actions = ['make_audited', 'make_stopped']
view_on_site = False
list_filter = ['status']
list_per_page = 10
list_max_show_all = 100
def make_audited(self, request, queryset):
rows = queryset.update(status='A')
if rows > 0:
self.message_user(request, u'已完成审核动作')
make_audited.allowed_permissions = ('audit',)
make_audited.short_description = u'通过审核'
def has_audit_permission(self, request):
opts = self.opts
codename = get_permission_codename('audit', opts)
return request.user.has_perm('%s.%s' % (opts.app_label, codename))
def make_stopped(self, request, queryset):
rows = queryset.update(status='S')
if rows > 0:
self.message_user(request, u'已完成终止动作')
make_stopped.allowed_permissions = ('stop',)
make_stopped.short_description = u'终止使用'
def has_stop_permission(self, request):
opts = self.opts
codename = get_permission_codename('stop', opts)
return request.user.has_perm('%s.%s' % (opts.app_label, codename))
admin.site.register(Supplier, SupplierAdmin)
class CustomerAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'contacter', 'period', 'status']
fields = ['title', 'contacter', 'period', 'landline', 'mobile', 'wechat_account', 'email', 'address',
'description']
actions = ['make_audited']
view_on_site = False
list_filter = ['status']
list_per_page = 10
list_max_show_all = 100
def make_audited(self, request, queryset):
rows = queryset.update(status='A')
if rows > 0:
self.message_user(request, u'已完成审核动作')
make_audited.allowed_permissions = ('audit',)
make_audited.short_description = u'通过审核'
def has_audit_permission(self, request):
opts = self.opts
codename = get_permission_codename('audit', opts)
return request.user.has_perm('%s.%s' % (opts.app_label, codename))
def make_stopped(self, request, queryset):
rows = queryset.update(status='S')
if rows > 0:
self.message_user(request, u'已完成终止动作')
make_stopped.allowed_permissions = ('stop',)
make_stopped.short_description = u'终止使用'
def has_stop_permission(self, request):
opts = self.opts
codename = get_permission_codename('stop', opts)
return request.user.has_perm('%s.%s' % (opts.app_label, codename))
admin.site.register(Customer, CustomerAdmin)
class CategoryAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'description', 'status']
fields = ['title', 'description']
actions = ['make_audited', 'make_stopped']
view_on_site = False
list_filter = ['status']
list_per_page = 10
list_max_show_all = 100
def make_audited(self, request, queryset):
rows = queryset.update(status='A')
if rows > 0:
self.message_user(request, u'已完成审核动作')
make_audited.allowed_permissions = ('audit',)
make_audited.short_description = u'通过审核'
def has_audit_permission(self, request):
opts = self.opts
codename = get_permission_codename('audit', opts)
return request.user.has_perm('%s.%s' % (opts.app_label, codename))
def make_stopped(self, request, queryset):
rows = queryset.update(status='S')
if rows > 0:
self.message_user(request, u'已完成终止动作')
make_stopped.allowed_permissions = ('stop',)
make_stopped.short_description = u'终止使用'
def has_stop_permission(self, request):
opts = self.opts
codename = get_permission_codename('stop', opts)
return request.user.has_perm('%s.%s' % (opts.app_label, codename))
admin.site.register(Category, CategoryAdmin)
class PartAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'description', 'status']
fields = ['title', 'description']
actions = ['make_audited', 'make_stopped']
view_on_site = False
list_filter = ['status']
list_per_page = 10
list_max_show_all = 100
def make_audited(self, request, queryset):
rows = queryset.update(status='A')
if rows > 0:
self.message_user(request, u'已完成审核动作')
make_audited.allowed_permissions = ('audit',)
make_audited.short_description = u'通过审核'
def has_audit_permission(self, request):
opts = self.opts
codename = get_permission_codename('audit', opts)
return request.user.has_perm('%s.%s' % (opts.app_label, codename))
def make_stopped(self, request, queryset):
rows = queryset.update(status='S')
if rows > 0:
self.message_user(request, u'已完成终止动作')
make_stopped.allowed_permissions = ('stop',)
make_stopped.short_description = u'终止使用'
def has_stop_permission(self, request):
opts = self.opts
codename = get_permission_codename('stop', opts)
return request.user.has_perm('%s.%s' % (opts.app_label, codename))
admin.site.register(Part, PartAdmin)
class SizeAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'description', 'status']
fields = ['title', 'description']
actions = ['make_audited', 'make_stopped']
view_on_site = False
list_filter = ['status']
list_per_page = 10
list_max_show_all = 100
def make_audited(self, request, queryset):
rows = queryset.update(status='A')
if rows > 0:
self.message_user(request, u'已完成审核动作')
make_audited.allowed_permissions = ('audit',)
make_audited.short_description = u'通过审核'
def has_audit_permission(self, request):
opts = self.opts
codename = get_permission_codename('audit', opts)
return request.user.has_perm('%s.%s' % (opts.app_label, codename))
def make_stopped(self, request, queryset):
rows = queryset.update(status='S')
if rows > 0:
self.message_user(request, u'已完成终止动作')
make_stopped.allowed_permissions = ('stop',)
make_stopped.short_description = u'终止使用'
def has_stop_permission(self, request):
opts = self.opts
codename = get_permission_codename('stop', opts)
return request.user.has_perm('%s.%s' % (opts.app_label, codename))
admin.site.register(Size, SizeAdmin)
class MaterialAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'image_sub', 'supplier', 'category', 'tax_price', 'currency', 'stock', 'status']
fields = ['title', 'image_sub', 'supplier', 'category', 'price', 'tax', 'tax_price',
'description', 'currency']
actions = ['make_audited', 'make_stopped']
view_on_site = False
list_filter = ['status']
list_per_page = 10
list_max_show_all = 100
def make_audited(self, request, queryset):
rows = queryset.update(status='A')
if rows > 0:
self.message_user(request, u'已完成审核动作')
make_audited.allowed_permissions = ('audit',)
make_audited.short_description = u'通过审核'
def has_audit_permission(self, request):
opts = self.opts
codename = get_permission_codename('audit', opts)
return request.user.has_perm('%s.%s' % (opts.app_label, codename))
def make_stopped(self, request, queryset):
rows = queryset.update(status='S')
if rows > 0:
self.message_user(request, u'已完成终止动作')
make_stopped.allowed_permissions = ('stop',)
make_stopped.short_description = u'终止使用'
def has_stop_permission(self, request):
opts = self.opts
codename = get_permission_codename('stop', opts)
return request.user.has_perm('%s.%s' % (opts.app_label, codename))
admin.site.register(Material, MaterialAdmin)
1.2.2.商品model
由于我希望如下在编辑商品时也一起编辑BOM表,所以我用inline的方式将BOM表放入商品信息编辑中;并且我希望在商品通过审核之后,除了授权的使用者之外,不可以再编辑BOM表了;也就是说当使用者有权限新增商品(add_product)且也有权限新增BOM表(add_bom)时可以在新增商品画面中新增BOM表信息,没有权限修改BOM表(change_bom)的话在修改商品则不能新增与修改商品,我使用的是复写商品change_form.html(后面将再说明)的方式。
商品与BOM表的写法如下:
class BomInline(admin.TabularInline):
model = Bom
fields = ['material', 'quantity']
raw_id_fields = ['material']
extra = 0
class ProductAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'part', 'size', 'image_sub', 'tax_price', 'currency', 'stock', 'status']
fields = ['title', 'part', 'size', 'image_sub', 'price', 'tax', 'tax_price',
'currency', 'description']
actions = ['make_audited', 'make_stopped']
inlines = [BomInline]
view_on_site = False
list_filter = ['status']
list_per_page = 10
list_max_show_all = 100
def make_audited(self, request, queryset):
rows = queryset.update(status='A')
if rows > 0:
self.message_user(request, u'已完成审核动作')
make_audited.allowed_permissions = ('audit',)
make_audited.short_description = u'通过审核'
def has_audit_permission(self, request):
opts = self.opts
codename = get_permission_codename('audit', opts)
return request.user.has_perm('%s.%s' % (opts.app_label, codename))
def make_stopped(self, request, queryset):
rows = queryset.update(status='S')
if rows > 0:
self.message_user(request, u'已完成终止动作')
make_stopped.allowed_permissions = ('stop',)
make_stopped.short_description = u'终止使用'
def has_stop_permission(self, request):
opts = self.opts
codename = get_permission_codename('stop', opts)
return request.user.has_perm('%s.%s' % (opts.app_label, codename))
admin.site.register(Product, ProductAdmin)
1.3.templates 设定
change_form.html 放置的位置如下:
我复制的来源位置是 C:\django\CSDN\Lib\site-packages\django\contrib\admin\templates\admin,因只复写商品(product)的画面,所以视你实际路径取得该 html 档后放到自己建立的 basic\templates\admin\basic\product 目录下,然后在该档案中新增如下 jquery 语法:
{% if not perms.basic.change_bom %} #特别注意
<script>
(function($) {
$(document).ready(function(){
var href = location.href;
href_list = href.split('/');
<!-- 表示为change -->
if (href_list[href_list.length - 2] == 'change'){
$('tr.add-row').hide(); #特别注意
$('div.submit-row').html('<a href="/admin/sale/order/" class="closelink">Close</a>'); #特别注意
}
});
})(django.jQuery);
</script>
{% endif %}
有了上方代码中**#特别注意**的三行,如果使用者没有修改BOM表权限(change_bom)的话,代码就直接不让使用者新增bom表[ ( ′ t r . a d d − r o w ′ ) . h i d e ( ) ; ] , 且 直 接 将 新 增 的 按 钮 改 成 关 闭 [ ('tr.add-row').hide();],且直接将新增的按钮改成关闭[ (′tr.add−row′).hide();],且直接将新增的按钮改成关闭[(‘div.submit-row’).html(‘Close’);]。
1.4.urls.py 设定
虽然basic模块没有用到 urls.py,但是我还是在这个档内新增了一个 app_name 的设定。
from django.urls import path
from django.contrib.auth.decorators import login_required
from . import views
app_name = 'basic'
urlpatterns = [
]