天天看點

Django用admin開發的産康中心進銷存系統-31.基本設定(basic)子產品

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
           
Django用admin開發的産康中心進銷存系統-31.基本設定(basic)子產品

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
           
Django用admin開發的産康中心進銷存系統-31.基本設定(basic)子產品

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
           
Django用admin開發的産康中心進銷存系統-31.基本設定(basic)子產品
Django用admin開發的産康中心進銷存系統-31.基本設定(basic)子產品

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
           
Django用admin開發的産康中心進銷存系統-31.基本設定(basic)子產品
Django用admin開發的産康中心進銷存系統-31.基本設定(basic)子產品

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
           
Django用admin開發的産康中心進銷存系統-31.基本設定(basic)子產品
Django用admin開發的産康中心進銷存系統-31.基本設定(basic)子產品

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
           
Django用admin開發的産康中心進銷存系統-31.基本設定(basic)子產品