1.基本設定(basic)子產品
1.1.models.py 設定
1.1.1.機關
商品的機關種類,例如:瓶/盒/次…等。
class Unit(models.Model):
title = models.CharField(max_length=16, unique=True, verbose_name='名稱')
def __str__(self):
return self.title
class Meta:
verbose_name = '機關'
verbose_name_plural = verbose_name
1.1.2.合作門店
class Store(models.Model):
title = models.CharField(max_length=16, unique=True, verbose_name='名稱')
def __str__(self):
return '{}-{}'.format(self.pk, self.title)
class Meta:
verbose_name = '合作門店'
verbose_name_plural = verbose_name
1.1.3.員工
class Employee(models.Model):
title = models.CharField(max_length=16, verbose_name='姓名')
mobile = models.CharField(max_length=11, blank=True, verbose_name='手機電話')
store = models.ForeignKey(Store, verbose_name='所屬門店', on_delete=models.PROTECT)
entry_date = models.DateField(verbose_name='入職日期')
departure_date = models.DateField(blank=True, null=True, verbose_name='離職日期')
basic_wage = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='底薪', default=0)
created = models.DateTimeField(auto_now_add=True, verbose_name='建立時間')
create_user = models.ForeignKey('auth.User', verbose_name='建立人員', on_delete=models.PROTECT,
related_name='employee_create_user')
updated = models.DateTimeField(verbose_name='異動時間', null=True)
update_user = models.ForeignKey('auth.User', verbose_name='異動人員', on_delete=models.PROTECT,
related_name='employee_update_user', null=True)
def __str__(self):
return '{}-{}'.format(self.pk, self.title)
class Meta:
verbose_name = '員工'
verbose_name_plural = verbose_name
1.1.4.顧客
class Customer(models.Model):
title = models.CharField(max_length=16, verbose_name='姓名')
mobile = models.CharField(max_length=11, blank=True, verbose_name='手機電話')
store = models.ForeignKey(Store, verbose_name='辦卡門店', on_delete=models.PROTECT)
birthday = models.DateField(verbose_name='出生日期')
weight = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='現在體重', help_text='機關:KG')
height = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='身高', help_text='機關:CM')
allergic = models.CharField(max_length=64, blank=True, verbose_name='過敏史')
address = models.CharField(max_length=32, blank=True, verbose_name='家庭位址')
depart_address = models.CharField(max_length=32, blank=True, verbose_name='工作位址')
depart_name = models.CharField(max_length=16, blank=True, verbose_name='機關名稱')
entry_date = models.DateField(verbose_name='到店日期')
birth_period = models.DecimalField(max_digits=8, decimal_places=1, default=0, verbose_name='産後', help_text='月')
notes = models.CharField(max_length=64, blank=True, verbose_name='其他說明')
created = models.DateTimeField(auto_now_add=True, verbose_name='建立時間')
create_user = models.ForeignKey('auth.User', verbose_name='建立人員', on_delete=models.PROTECT,
related_name='customer_create_user')
updated = models.DateTimeField(verbose_name='異動時間', null=True)
update_user = models.ForeignKey('auth.User', verbose_name='異動人員', on_delete=models.PROTECT,
related_name='customer_update_user', null=True)
def __str__(self):
return '{}-{}'.format(self.pk, self.title)
class Meta:
verbose_name = '顧客'
verbose_name_plural = verbose_name
1.1.5.寶寶
顧客的寶寶胎次,目前尚未使用。
DELIVERY_CHOICES = (
(1, '順産'),
(2, '剖宮産'),
)
class Baby(models.Model):
customer = models.ForeignKey(Customer, verbose_name='母親', on_delete=models.PROTECT)
serial = models.PositiveSmallIntegerField(verbose_name='胎次')
birthday = models.DateField(verbose_name='出生日期')
delivery = models.PositiveSmallIntegerField(verbose_name='出生方式', choices=DELIVERY_CHOICES)
gest_week = models.PositiveIntegerField(verbose_name='孕周(周)')
weight = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='寶寶出生體重(KG)')
bef_weight = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='媽媽孕前體重(KG)')
birth_weight = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='媽媽臨盆體重(KG)')
care = models.CharField(max_length=64, blank=True, verbose_name='42天産檢情況')
def __str__(self):
return '{}的第{}個寶寶'.format(self.customer.title, self.pk)
class Meta:
verbose_name = '寶寶'
verbose_name_plural = verbose_name
1.1.6.商品
如先前所述,分産品/論次服務/有效期間服務/套盒共四類商品,[産品類]商品出售後交易即完成,[論次服務類]與[有效期間類]服務在完成交易後,自動生成論次服務記錄或有效期間服務記錄。
TYPE_CHOICES = (
('1', '産品'),
('2', '論次服務'),
('3', '有效期間服務'),
('4', '套盒'),
)
STATUS_CHOICES = (
('0', '停止使用'),
('1', '正常'),
)
class Good(models.Model):
title = models.CharField(max_length=32, verbose_name='名稱')
type = models.CharField(max_length=1, verbose_name='類型', choices=TYPE_CHOICES,
help_text='當商品類型為時[論次服務/有效期間服務]時請添加理療人員手工費'
',商品新增後不得修改[類型]')
period = models.PositiveIntegerField(verbose_name='有效期限', default=0,
help_text='機關:月;當商品類型為[有效期間服務]時才需要添加')
price = models.DecimalField(max_digits=16, decimal_places=2, verbose_name='建議售價')
unit = models.ForeignKey(Unit, verbose_name='機關', on_delete=models.PROTECT)
sale = models.BooleanField(verbose_name='是否直接銷售', default=True)
status = models.CharField(max_length=1, verbose_name='狀态', choices=STATUS_CHOICES, default='1')
notes = models.CharField(max_length=64, blank=True, verbose_name='其他說明')
created = models.DateTimeField(auto_now_add=True, verbose_name='建立時間')
create_user = models.ForeignKey('auth.User', verbose_name='建立人員', on_delete=models.PROTECT,
related_name='good_create_user')
updated = models.DateTimeField(verbose_name='異動時間', null=True)
update_user = models.ForeignKey('auth.User', verbose_name='異動人員', on_delete=models.PROTECT,
related_name='good_update_user', null=True)
def __str__(self):
return '{}-{}'.format(self.pk, self.title)
class Meta:
verbose_name = '商品'
verbose_name_plural = verbose_name
1.1.7.套盒内容商品
套盒類商品跟産品類商品一樣,在門店要有庫存(商品庫存分布)才能販售,而套盒内容商品記錄套盒類商品有哪些商品。
class Compose(models.Model):
main = models.ForeignKey(Good, verbose_name='套盒商品', on_delete=models.PROTECT,
related_name='compose_main')
serial = models.PositiveIntegerField(verbose_name='序号', default=1)
content = models.ForeignKey(Good, verbose_name='套盒内容', on_delete=models.PROTECT,
related_name='compose_content')
quantity = models.PositiveIntegerField(verbose_name='數量')
def __str__(self):
return '{}-{}-{}'.format(self.main.title, self.serial, self.content.title)
class Meta:
verbose_name = '套盒内容商品'
verbose_name_plural = verbose_name
unique_together = ("main", "serial")
1.1.8.手工費
當客戶購買論次服務記錄或有效期間服務商品後,可以來門店享受對應的服務内容,替客戶服務的員工在當月工資中就可以增加對應的手工費金額。
class Fee(models.Model):
good = models.ForeignKey(Good, verbose_name='商品', on_delete=models.PROTECT)
employee = models.ForeignKey(Employee, verbose_name='員工', on_delete=models.PROTECT)
price = models.DecimalField(max_digits=16, decimal_places=2, verbose_name='費用')
def __str__(self):
return '{}'.format(self.id)
class Meta:
verbose_name = '手工費'
verbose_name_plural = verbose_name
1.1.9.倉庫
每個合作門店可以有多個倉庫存放商品類與套盒類商品,在本版次系統中尚無實際作用。
class Storage(models.Model):
title = models.CharField(max_length=4, verbose_name='倉庫名稱', help_text='最多四碼')
store = models.ForeignKey(Store, verbose_name='所屬門店', on_delete=models.PROTECT, help_text='新增後則不可異動')
notes = models.CharField(max_length=64, verbose_name='描述', blank=True)
def __str__(self):
return '{}-{}'.format(self.id, self.title)
class Meta:
verbose_name = '倉庫'
verbose_name_plural = verbose_name
1.2.admin.py 設定
1.2.1.UnitAdmin呈現畫面
@admin.register(Unit)
class UnitAdmin(admin.ModelAdmin):
list_display = ['id', 'title']
view_on_site = False
def has_delete_permission(self, request, obj=None):
return False

1.2.2.StoreAdmin呈現畫面
@admin.register(Store)
class StoreAdmin(admin.ModelAdmin):
list_display = ['id', 'title']
view_on_site = False
def has_delete_permission(self, request, obj=None):
return False
1.2.3.CustomerAdmin呈現畫面
在顧客畫面下方可以編輯寶寶胎次,并且能看到該顧客已來門店享受過的論次服務記錄。
class CerviceInline(admin.TabularInline):
model = Cervice
fields = ['updated', 'good', 'store', 'employee', 'notes']
extra = 0
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.filter(updated__isnull=False).order_by('-updated')
def has_add_permission(self, request, obj=None):
return False
def has_change_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
class BabyInline(admin.TabularInline):
model = Baby
fields = ['customer', 'serial', 'birthday', 'delivery', 'gest_week', 'weight',
'bef_weight', 'birth_weight', 'care']
extra = 0
def has_delete_permission(self, request, obj=None):
return False
@admin.register(Customer)
class CustomerAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'mobile', 'store', 'birthday', 'weight', 'height', 'allergic',
'address', 'depart_address', 'depart_name', 'entry_date', 'birth_period', 'notes']
fields = ['title', 'mobile', 'store', 'birthday', 'weight', 'height', 'allergic',
'address', 'depart_address', 'depart_name', 'entry_date', 'birth_period', 'notes',
'created', 'create_user', 'updated', 'update_user']
readonly_fields = ['created', 'create_user', 'updated', 'update_user']
inlines = [BabyInline, CerviceInline]
view_on_site = False
def save_model(self, request, obj, form, change):
if not change:
obj.create_user = request.user
else:
obj.update_user = request.user
obj.updated = datetime.now()
super().save_model(request, obj, form, change)
def has_delete_permission(self, request, obj=None):
return False
1.2.4.GoodAdmin呈現畫面
員工的手工費可以在商品處設定,也可以在員工處設定,套盒類商品可以設定套盒内的商品與數量。
"""
手工費檢查
1.商品類型為2(論次服務)與3(有效期間服務)時才需要設定手工費
"""
class FeeCheckInlineFormset(forms.models.BaseInlineFormSet):
def clean(self):
for form in self.forms:
if form.cleaned_data:
good = form.cleaned_data.get('good')
if good.type not in ('2', '3'):
raise forms.ValidationError("商品類型為[論次服務]與3[有效期間服務]時才需要設定手工費。")
class FeeInline(admin.TabularInline):
model = Fee
formset = FeeCheckInlineFormset
fields = ['good', 'employee', 'price']
extra = 0
def has_delete_permission(self, request, obj=None):
return False
"""
套盒内容商品
1.main的type必須是4[套盒]
2.content的type不能是4[套盒]
3.content的type是3[有效期間服務]時,數量隻能是1
4.内容的[序号]不可重複
"""
class ComposeCheckInlineFormset(forms.models.BaseInlineFormSet):
def clean(self):
serial_list = []
for form in self.forms:
if form.cleaned_data:
main = form.cleaned_data.get('main')
serial = form.cleaned_data.get('serial')
content = form.cleaned_data.get('content')
quantity = form.cleaned_data.get('quantity')
if main.type != '4':
raise forms.ValidationError("套盒商品的類型必須為4[套盒]。")
if content.type == '4':
raise forms.ValidationError("套盒内容的類型必須不得為4[套盒]。")
if content.type == '3':
if quantity != 1:
raise forms.ValidationError("套盒内容的類型為3[有效期間服務]時,數量隻能是1。")
if not serial in serial_list:
serial_list.append(serial)
else:
raise forms.ValidationError("套盒内容的[序号]不可重複。")
class ComposeInline(admin.TabularInline):
model = Compose
fk_name = "main"
formset = ComposeCheckInlineFormset
fields = ['main', 'serial', 'content', 'quantity']
extra = 0
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "content":
kwargs["queryset"] = Good.objects.filter(~Q(type=4), status='1')
return super().formfield_for_foreignkey(db_field, request, **kwargs)
def has_delete_permission(self, request, obj=None):
return False
"""
商品檢查
1.類型是[3有效期間服務]時period(有效期限)必須大于0
2.商品新增後不可修改[類型]
3.[套盒]類型産品必須直接銷售
"""
class GoodCheckForm(forms.ModelForm):
def clean(self):
super(GoodCheckForm, self).clean()
type = self.cleaned_data.get('type')
period = self.cleaned_data.get('period')
sale = self.cleaned_data.get('sale')
if type == '3':
if period <= 0:
raise forms.ValidationError('類型是[有效期間服務]時[有效期限]必須大于0。')
elif type == '4':
if sale != True:
raise forms.ValidationError('[套盒]類型産品必須直接銷售。')
if self.instance.type != '' and self.instance.type != type:
raise forms.ValidationError('商品新增後不可修改[類型]。')
@admin.register(Good)
class GoodAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'type', 'period', 'price', 'unit', 'sale', 'status']
fields = ['title', 'type', 'period', 'price', 'unit', 'sale', 'status', 'notes',
'created', 'create_user', 'updated', 'update_user']
readonly_fields = ['status', 'created', 'create_user', 'updated', 'update_user']
form = GoodCheckForm
inlines = [FeeInline, ComposeInline]
view_on_site = False
def save_model(self, request, obj, form, change):
if not change:
obj.create_user = request.user
else:
obj.update_user = request.user
obj.updated = datetime.now()
#當商品類型為[3/有效期間服務]時才需要添加
if obj.type != '3':
obj.period = 0
super().save_model(request, obj, form, change)
def has_delete_permission(self, request, obj=None):
return False
1.2.5.EmployeeAdmin呈現畫面
@admin.register(Employee)
class EmployeeAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'mobile', 'store', 'entry_date', 'departure_date']
fields = ['title', 'mobile', 'store', 'entry_date', 'departure_date', 'basic_wage',
'created', 'create_user', 'updated', 'update_user']
readonly_fields = ['created', 'create_user', 'updated', 'update_user']
inlines = [FeeInline]
view_on_site = False
def save_model(self, request, obj, form, change):
if not change:
obj.create_user = request.user
else:
obj.update_user = request.user
obj.updated = datetime.now()
super().save_model(request, obj, form, change)
def has_delete_permission(self, request, obj=None):
return False
1.2.6.StorageAdmin呈現畫面
"""
儲位檢查
1.所屬門店不可異動(save_model檢查)
"""
@admin.register(Storage)
class StorageAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'store', 'notes']
fields = ['title', 'store', 'notes']
view_on_site = False
def save_model(self, request, obj, form, change):
if not change:
super().save_model(request, obj, form, change)
else:
if 'store' in form.changed_data:
messages.error(request, '儲位新增後[所屬門店]不可異動。')
messages.set_level(request, messages.ERROR)
else:
super().save_model(request, obj, form, change)
def has_delete_permission(self, request, obj=None):
return False