From 53f2a24b206d34552a0aab1b45a860e92dc8ecf3 Mon Sep 17 00:00:00 2001 From: colaftc Date: Thu, 27 Nov 2025 16:58:23 +0800 Subject: [PATCH] feat: purchase order item model --- business/admin.py | 32 ++++++- ..._order_date_purchaseorder_kind_and_more.py | 36 ++++++++ .../migrations/0003_purchaseorder_status.py | 18 ++++ ...ove_purchaseorder_total_amount_and_more.py | 48 ++++++++++ ...5_alter_purchaseorder_operator_and_more.py | 27 ++++++ ...6_remove_purchaseorderitem_total_amount.py | 17 ++++ ...07_alter_purchaseorderitem_batch_number.py | 18 ++++ business/models.py | 88 ++++++++++++++++++- .../0017_alter_plateorder_image_name.py | 18 ++++ printing/models.py | 2 +- 10 files changed, 298 insertions(+), 6 deletions(-) create mode 100644 business/migrations/0002_remove_purchaseorder_order_date_purchaseorder_kind_and_more.py create mode 100644 business/migrations/0003_purchaseorder_status.py create mode 100644 business/migrations/0004_remove_purchaseorder_total_amount_and_more.py create mode 100644 business/migrations/0005_alter_purchaseorder_operator_and_more.py create mode 100644 business/migrations/0006_remove_purchaseorderitem_total_amount.py create mode 100644 business/migrations/0007_alter_purchaseorderitem_batch_number.py create mode 100644 printing/migrations/0017_alter_plateorder_image_name.py diff --git a/business/admin.py b/business/admin.py index 09af82b..faeb743 100644 --- a/business/admin.py +++ b/business/admin.py @@ -5,7 +5,33 @@ from . import models @admin.register(models.PurchaseOrder) class PurchaseOrderAdmin(admin.ModelAdmin): - list_display = ('id', 'supplier', 'order_date', 'total_amount') + list_display = ('id', 'supplier', 'purchase_date', 'operator', 'created_at') search_fields = ('supplier__name',) - list_filter = ('order_date',) - ordering = ('-order_date',) + list_filter = ('operator', 'warehouse', 'created_at') + ordering = ('-created_at',) + + +@admin.register(models.PurchaseOrderItem) +class PurchaseOrderItemAdmin(admin.ModelAdmin): + list_display = ( + 'id', 'purchase_order', 'product', + 'quantity', 'unit', 'price', 'batch_number', + '_total_amount', 'empty_diff_percent', + '_real_quantity', '_diff_quantity', + 'num_of_rolls', + ) + search_fields = ('purchase_order__id', 'product__name') + list_filter = ('purchase_order__operator', 'purchase_order__warehouse', 'created_at') + ordering = ('-created_at',) + + @admin.display(description='实际数量') + def _real_quantity(self, obj: models.PurchaseOrderItem): + return obj.real_quantity() + + @admin.display(description='差异数量') + def _diff_quantity(self, obj: models.PurchaseOrderItem): + return obj.diff_quantity() + + @admin.display(description='总金额') + def _total_amount(self, obj: models.PurchaseOrderItem): + return obj.total_amount() diff --git a/business/migrations/0002_remove_purchaseorder_order_date_purchaseorder_kind_and_more.py b/business/migrations/0002_remove_purchaseorder_order_date_purchaseorder_kind_and_more.py new file mode 100644 index 0000000..ca38c77 --- /dev/null +++ b/business/migrations/0002_remove_purchaseorder_order_date_purchaseorder_kind_and_more.py @@ -0,0 +1,36 @@ +# Generated by Django 5.2.7 on 2025-11-27 07:29 + +import datetime +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('basic_info', '0013_alter_customer_options'), + ('business', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='purchaseorder', + name='order_date', + ), + migrations.AddField( + model_name='purchaseorder', + name='kind', + field=models.IntegerField(choices=[(1, '大货'), (2, '样板')], default=1, verbose_name='采购单类型'), + ), + migrations.AddField( + model_name='purchaseorder', + name='operator', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='purchase_orders', to='basic_info.employee', verbose_name='经办人'), + ), + migrations.AddField( + model_name='purchaseorder', + name='purchase_date', + field=models.DateField(default=datetime.datetime(2025, 11, 27, 15, 29, 17, 309981), verbose_name='采购日期'), + preserve_default=False, + ), + ] diff --git a/business/migrations/0003_purchaseorder_status.py b/business/migrations/0003_purchaseorder_status.py new file mode 100644 index 0000000..1fc6d1e --- /dev/null +++ b/business/migrations/0003_purchaseorder_status.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2025-11-27 07:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('business', '0002_remove_purchaseorder_order_date_purchaseorder_kind_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='purchaseorder', + name='status', + field=models.IntegerField(choices=[(1, '审批中'), (2, '审批通过'), (3, '作废')], default=1, verbose_name='状态'), + ), + ] diff --git a/business/migrations/0004_remove_purchaseorder_total_amount_and_more.py b/business/migrations/0004_remove_purchaseorder_total_amount_and_more.py new file mode 100644 index 0000000..a9cc941 --- /dev/null +++ b/business/migrations/0004_remove_purchaseorder_total_amount_and_more.py @@ -0,0 +1,48 @@ +# Generated by Django 5.2.7 on 2025-11-27 08:08 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('basic_info', '0013_alter_customer_options'), + ('business', '0003_purchaseorder_status'), + ] + + operations = [ + migrations.RemoveField( + model_name='purchaseorder', + name='total_amount', + ), + migrations.AddField( + model_name='purchaseorder', + name='warehouse', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='purchase_orders', to='basic_info.warehouse', verbose_name='仓库'), + ), + migrations.CreateModel( + name='PurchaseOrderItem', + fields=[ + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')), + ('id', models.BigAutoField(primary_key=True, serialize=False)), + ('price', models.DecimalField(decimal_places=2, max_digits=15, verbose_name='单价')), + ('color', models.CharField(blank=True, max_length=50, null=True, verbose_name='颜色')), + ('quantity', models.PositiveIntegerField(verbose_name='数量')), + ('unit', models.CharField(max_length=50, verbose_name='单位')), + ('empty_diff_percent', models.DecimalField(decimal_places=2, max_digits=15, verbose_name='空差百分比')), + ('quantity_of_rolls', models.CharField(blank=True, max_length=255, null=True, verbose_name='各条数数量')), + ('num_of_rolls', models.PositiveIntegerField(default=1, verbose_name='条数')), + ('batch_number', models.PositiveIntegerField(default=1, verbose_name='批次号')), + ('total_amount', models.DecimalField(decimal_places=2, max_digits=15, verbose_name='总金额')), + ('remarks', models.TextField(blank=True, null=True, verbose_name='备注')), + ('product', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='purchase_order_items', to='basic_info.product', verbose_name='产品')), + ('purchase_order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='business.purchaseorder', verbose_name='采购单')), + ], + options={ + 'verbose_name': '采购单明细', + 'verbose_name_plural': '采购单明细', + }, + ), + ] diff --git a/business/migrations/0005_alter_purchaseorder_operator_and_more.py b/business/migrations/0005_alter_purchaseorder_operator_and_more.py new file mode 100644 index 0000000..aedfe31 --- /dev/null +++ b/business/migrations/0005_alter_purchaseorder_operator_and_more.py @@ -0,0 +1,27 @@ +# Generated by Django 5.2.7 on 2025-11-27 08:10 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('basic_info', '0013_alter_customer_options'), + ('business', '0004_remove_purchaseorder_total_amount_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='purchaseorder', + name='operator', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, related_name='purchase_orders', to='basic_info.employee', verbose_name='经办人'), + preserve_default=False, + ), + migrations.AlterField( + model_name='purchaseorder', + name='warehouse', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, related_name='purchase_orders', to='basic_info.warehouse', verbose_name='仓库'), + preserve_default=False, + ), + ] diff --git a/business/migrations/0006_remove_purchaseorderitem_total_amount.py b/business/migrations/0006_remove_purchaseorderitem_total_amount.py new file mode 100644 index 0000000..a43e856 --- /dev/null +++ b/business/migrations/0006_remove_purchaseorderitem_total_amount.py @@ -0,0 +1,17 @@ +# Generated by Django 5.2.7 on 2025-11-27 08:18 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('business', '0005_alter_purchaseorder_operator_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='purchaseorderitem', + name='total_amount', + ), + ] diff --git a/business/migrations/0007_alter_purchaseorderitem_batch_number.py b/business/migrations/0007_alter_purchaseorderitem_batch_number.py new file mode 100644 index 0000000..b1aba71 --- /dev/null +++ b/business/migrations/0007_alter_purchaseorderitem_batch_number.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2025-11-27 08:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('business', '0006_remove_purchaseorderitem_total_amount'), + ] + + operations = [ + migrations.AlterField( + model_name='purchaseorderitem', + name='batch_number', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='批次号'), + ), + ] diff --git a/business/models.py b/business/models.py index 8891ebd..ce9fd89 100644 --- a/business/models.py +++ b/business/models.py @@ -3,6 +3,19 @@ from flower.common import ModelBase from basic_info import models as basic_info_models +class PurchaseOrderKindEnum(models.IntegerChoices): + """采购单类型""" + WHOLESALE = 1, '大货' + SAMPLE = 2, '样板' + + +class PurchaseOrderStatusEnum(models.IntegerChoices): + """采购单状态""" + PENDING = 1, '审批中' + APPROVED = 2, '审批通过' + CANCELLED = 3, '作废' + + class PurchaseOrder(ModelBase): """采购单模型(业务模块)""" @@ -19,8 +32,29 @@ class PurchaseOrder(ModelBase): related_name='purchase_orders', verbose_name='供应商', ) - order_date = models.DateField(verbose_name='订单日期') - total_amount = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='总金额') + purchase_date = models.DateField(verbose_name='采购日期') + kind = models.IntegerField( + choices=PurchaseOrderKindEnum.choices, + default=PurchaseOrderKindEnum.WHOLESALE, + verbose_name='采购单类型', + ) + operator = models.ForeignKey( + basic_info_models.Employee, + on_delete=models.PROTECT, + related_name='purchase_orders', + verbose_name='经办人', + ) + warehouse = models.ForeignKey( + basic_info_models.WareHouse, + on_delete=models.PROTECT, + related_name='purchase_orders', + verbose_name='仓库', + ) + status = models.IntegerField( + choices=PurchaseOrderStatusEnum.choices, + default=PurchaseOrderStatusEnum.PENDING, + verbose_name='状态', + ) remarks = models.TextField(blank=True, null=True, verbose_name='备注') def __str__(self): @@ -29,3 +63,53 @@ class PurchaseOrder(ModelBase): class Meta: verbose_name = '采购单' verbose_name_plural = '采购单' + + +class PurchaseOrderItem(ModelBase): + """采购单明细模型""" + + id = models.BigAutoField(primary_key=True) + purchase_order = models.ForeignKey( + PurchaseOrder, + on_delete=models.CASCADE, + related_name='items', + verbose_name='采购单', + ) + product = models.ForeignKey( + basic_info_models.Product, + on_delete=models.PROTECT, + related_name='purchase_order_items', + verbose_name='产品', + ) + price = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='单价') + color = models.CharField(max_length=50, null=True, blank=True, verbose_name='颜色') + quantity = models.PositiveIntegerField(verbose_name='数量') + unit = models.CharField(max_length=50, verbose_name='单位') + empty_diff_percent = models.DecimalField( + max_digits=15, + decimal_places=2, + verbose_name='空差百分比', + ) + # if purchase_order.warehouse.mode != WarehouseModeEnum.UNRESTRICTED, then quantity_of_rolls is required + quantity_of_rolls = models.CharField( + max_length=255, + null=True, + blank=True, + verbose_name='各条数数量', + ) + num_of_rolls = models.PositiveIntegerField(default=1, verbose_name='条数') + batch_number = models.CharField(null=True, blank=True, max_length=100, verbose_name='批次号') + remarks = models.TextField(blank=True, null=True, verbose_name='备注') + + class Meta: + verbose_name = '采购单明细' + verbose_name_plural = '采购单明细' + + def real_quantity(self): + return round(self.quantity * (1 - self.empty_diff_percent / 100), 2) + + def diff_quantity(self): + return round(self.quantity * (self.empty_diff_percent / 100), 2) + + def total_amount(self): + return round(self.price * self.real_quantity(), 2) diff --git a/printing/migrations/0017_alter_plateorder_image_name.py b/printing/migrations/0017_alter_plateorder_image_name.py new file mode 100644 index 0000000..f465653 --- /dev/null +++ b/printing/migrations/0017_alter_plateorder_image_name.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2025-11-27 07:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('printing', '0016_plateorder_image_name'), + ] + + operations = [ + migrations.AlterField( + model_name='plateorder', + name='image_name', + field=models.TextField(blank=True, null=True, verbose_name='图片名称'), + ), + ] diff --git a/printing/models.py b/printing/models.py index 96f55bd..b976922 100644 --- a/printing/models.py +++ b/printing/models.py @@ -16,7 +16,7 @@ class PlateOrder(ModelBase): plate_date = models.DateTimeField(null=True, blank=True, verbose_name='下版时间') plate_method = models.CharField(max_length=50, blank=True, null=True, verbose_name='开版方式') plate_image = models.FileField(upload_to='plate_images/', null=True, blank=True, verbose_name='开版图') - image_name = models.CharField(max_length=100, blank=True, null=True, verbose_name='图片名称') + image_name = models.TextField(blank=True, null=True, verbose_name='图片名称') plate_notes = models.TextField(blank=True, null=True, verbose_name='打版注意事项') reprint_reason = models.TextField(blank=True, null=True, verbose_name='复版原因')