1
0
forked from erp-dev/erp

feat: purchase order item model

This commit is contained in:
2025-11-27 16:58:23 +08:00
parent 0fa153849c
commit 53f2a24b20
10 changed files with 298 additions and 6 deletions

View File

@@ -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()

View File

@@ -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,
),
]

View File

@@ -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='状态'),
),
]

View File

@@ -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': '采购单明细',
},
),
]

View File

@@ -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,
),
]

View File

@@ -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',
),
]

View File

@@ -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='批次号'),
),
]

View File

@@ -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)

View File

@@ -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='图片名称'),
),
]

View File

@@ -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='复版原因')