销售发货涉及
This commit is contained in:
parent
d1a8be167c
commit
1d7aa9c279
|
@ -28,6 +28,5 @@ class IProductFilterSet(DynamicFieldsFilterMixin, filters.FilterSet):
|
|||
update_time_end = filters.DateFilter(field_name="update_time", lookup_expr='lte')
|
||||
class Meta:
|
||||
model = IProduct
|
||||
fields = ['material', 'warehouse', 'batch', 'order', 'material__type',
|
||||
'is_saled', 'update_time_start', 'update_time_end',
|
||||
fields = ['material', 'warehouse', 'batch', 'order', 'material__type', 'update_time_start', 'update_time_end',
|
||||
'to_order', 'need_to_order']
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# Generated by Django 3.2.9 on 2022-02-22 01:41
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('sam', '0013_auto_20220222_0941'),
|
||||
('inm', '0031_fifoitem_pu_order_item'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='iproduct',
|
||||
name='is_saled',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='fifo',
|
||||
name='sale',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sam.sale', verbose_name='关联销售记录'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='iproduct',
|
||||
name='state',
|
||||
field=models.IntegerField(choices=[(10, '可销售'), (20, '已锁定'), (30, '已售出')], default=10, verbose_name='状态'),
|
||||
),
|
||||
]
|
|
@ -3,6 +3,7 @@ from django.db.models.base import Model
|
|||
import django.utils.timezone as timezone
|
||||
from django.db.models.query import QuerySet
|
||||
from apps.pum.models import PuOrder, PuOrderItem, Vendor
|
||||
from apps.sam.models import Sale
|
||||
from apps.system.models import CommonADModel, CommonAModel, CommonBModel, Organization, User, Dict, File
|
||||
from utils.model import SoftModel, BaseModel
|
||||
from simple_history.models import HistoricalRecords
|
||||
|
@ -86,6 +87,8 @@ class FIFO(CommonADModel):
|
|||
on_delete=models.CASCADE, null=True, blank=True)
|
||||
pu_order = models.ForeignKey(PuOrder, verbose_name='关联采购订单',
|
||||
null=True, blank=True, on_delete=models.CASCADE)
|
||||
sale = models.ForeignKey(Sale, verbose_name='关联销售记录',
|
||||
null=True, blank=True, on_delete=models.CASCADE)
|
||||
|
||||
|
||||
class FIFOItem(BaseModel):
|
||||
|
@ -113,6 +116,14 @@ class IProduct(BaseModel):
|
|||
"""
|
||||
具体产品条目
|
||||
"""
|
||||
SALE_OK = 10
|
||||
SALE_LOCK = 20
|
||||
SALED = 30
|
||||
state_choices = (
|
||||
(SALE_OK, '可销售'),
|
||||
(SALE_LOCK, '已锁定'),
|
||||
(SALED, '已售出')
|
||||
)
|
||||
number = models.CharField('物品编号', unique=True, max_length=50)
|
||||
material = models.ForeignKey(
|
||||
Material, verbose_name='物料类型', on_delete=models.CASCADE)
|
||||
|
@ -121,7 +132,7 @@ class IProduct(BaseModel):
|
|||
batch = models.CharField('所属批次号', max_length=100, default='')
|
||||
wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False,
|
||||
null=True, blank=True, related_name='iproduct_wproduct')
|
||||
is_saled = models.BooleanField('是否售出', default=False)
|
||||
state = models.IntegerField('状态', default=SALE_OK, choices=state_choices)
|
||||
|
||||
|
||||
class FIFOItemProduct(BaseModel):
|
||||
|
@ -139,4 +150,4 @@ class FIFOItemProduct(BaseModel):
|
|||
Material, verbose_name='物料类型', on_delete=models.CASCADE)
|
||||
iproduct = models.ForeignKey(
|
||||
IProduct, verbose_name='关联库存产品',
|
||||
null=True, blank=True, on_delete=models.SET_NULL)
|
||||
null=True, blank=True, on_delete=models.SET_NULL)
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 3.2.9 on 2022-02-21 06:00
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mtm', '0047_packitem'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='step',
|
||||
name='need_test',
|
||||
),
|
||||
]
|
|
@ -0,0 +1,25 @@
|
|||
# Generated by Django 3.2.9 on 2022-02-22 01:44
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mtm', '0048_remove_step_need_test'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='packitem',
|
||||
name='product',
|
||||
field=models.ForeignKey(default=2, on_delete=django.db.models.deletion.CASCADE, related_name='pack_product', to='mtm.material', verbose_name='装箱产品'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='packitem',
|
||||
name='material',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='pack_material', to='mtm.material', verbose_name='装箱配件'),
|
||||
),
|
||||
]
|
|
@ -23,7 +23,7 @@ class Material(CommonAModel):
|
|||
(MA_TYPE_GOOD, '成品'),
|
||||
(MA_TYPE_HALFGOOD, '半成品'),
|
||||
(MA_TYPE_MAINSO, '主要原料'),
|
||||
(MA_TYPE_HELPSO, '辅助材料') ,
|
||||
(MA_TYPE_HELPSO, '辅助材料'),
|
||||
(MA_TYPE_TOOL, '加工工具'),
|
||||
(MA_TYPE_HELPTOOL, '辅助工装')
|
||||
)
|
||||
|
@ -54,8 +54,10 @@ class PackItem(CommonAModel):
|
|||
"""
|
||||
装箱项目
|
||||
"""
|
||||
material = models.ForeignKey(Material, verbose_name='关联成品',
|
||||
on_delete=models.CASCADE)
|
||||
product = models.ForeignKey(Material, verbose_name='装箱产品',
|
||||
on_delete=models.CASCADE, related_name='pack_product')
|
||||
material = models.ForeignKey(Material, verbose_name='装箱配件',
|
||||
on_delete=models.CASCADE, null=True, blank=True, related_name='pack_material')
|
||||
name = models.CharField('名称', max_length=100)
|
||||
specification = models.CharField('型号', max_length=100, null=True, blank=True)
|
||||
unit = models.CharField('单位', max_length=10)
|
||||
|
|
|
@ -32,7 +32,7 @@ class PackItemSerializer(serializers.ModelSerializer):
|
|||
class PackItemCreateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = PackItem
|
||||
fields = ['material', 'name', 'specification', 'unit', 'count', 'sort']
|
||||
fields = ['create', 'material', 'name', 'specification', 'unit', 'count', 'sort']
|
||||
|
||||
class PackItemUpdateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.9 on 2022-02-21 07:20
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('qm', '0025_alter_testrecord_type'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='testrecord',
|
||||
name='is_testok',
|
||||
field=models.BooleanField(blank=True, null=True, verbose_name='是否合格'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,53 @@
|
|||
# Generated by Django 3.2.9 on 2022-02-22 01:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('sam', '0012_alter_order_delivery_date'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='sale',
|
||||
name='count_real',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='实际发货数量'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='sale',
|
||||
name='edelivery_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='预计发货日期'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='sale',
|
||||
name='receiver',
|
||||
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='收货人'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='sale',
|
||||
name='receiver_address',
|
||||
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='收获地址'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='sale',
|
||||
name='receiver_phone',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='收货人联系电话'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='sale',
|
||||
name='remark',
|
||||
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='备注'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='sale',
|
||||
name='trans_mode',
|
||||
field=models.IntegerField(blank=True, choices=[(10, '铁路快运'), (20, '铁路慢件'), (30, '铁路整车'), (40, '汽车运输'), (50, '空运')], null=True, verbose_name='运输方式'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='sale',
|
||||
name='count',
|
||||
field=models.PositiveIntegerField(default=0, verbose_name='预计发货数量'),
|
||||
),
|
||||
]
|
|
@ -69,11 +69,30 @@ class Sale(CommonADModel):
|
|||
"""
|
||||
销售记录
|
||||
"""
|
||||
TRANS_RAIL_FAST = 10
|
||||
TRANS_RAIL_SLOW = 20
|
||||
TRANS_RAIL_WHOLE = 30
|
||||
TRANS_LORRY = 40
|
||||
TRANS_AIR = 50
|
||||
trans_choices=(
|
||||
(TRANS_RAIL_FAST, '铁路快运'),
|
||||
(TRANS_RAIL_SLOW, '铁路慢件'),
|
||||
(TRANS_RAIL_WHOLE, '铁路整车'),
|
||||
(TRANS_LORRY, '汽车运输'),
|
||||
(TRANS_AIR, '空运'),
|
||||
)
|
||||
customer = models.ForeignKey(Customer, verbose_name='客户', on_delete=models.CASCADE)
|
||||
order = models.ForeignKey(Order, verbose_name='关联订单', on_delete=models.CASCADE, null=True, blank=True)
|
||||
product = models.ForeignKey(Material, verbose_name='所需产品', on_delete=models.CASCADE)
|
||||
count = models.PositiveIntegerField('交货数量', default=0)
|
||||
count = models.PositiveIntegerField('预计发货数量', default=0)
|
||||
count_real = models.PositiveIntegerField('实际发货数量', null=True, blank=True)
|
||||
is_audited = models.BooleanField('是否审核', default=False)
|
||||
edelivery_date = models.DateField('预计发货日期', null=True, blank=True)
|
||||
trans_mode = models.IntegerField('运输方式', null=True, blank=True, choices=trans_choices)
|
||||
receiver = models.CharField('收货人', null=True, blank=True, max_length=200)
|
||||
receiver_phone = models.CharField('收货人联系电话', null=True, blank=True, max_length=20)
|
||||
receiver_address = models.CharField('收获地址', null=True, blank=True, max_length=200)
|
||||
remark = models.CharField('备注', null=True, blank=True, max_length=200)
|
||||
|
||||
|
||||
class SaleProduct(BaseModel):
|
||||
|
|
|
@ -5,12 +5,16 @@ from apps.inm.serializers import IProductListSerializer
|
|||
from apps.mtm.serializers import MaterialSimpleSerializer
|
||||
from apps.sam.models import Sale, SaleProduct
|
||||
from apps.sam.serializers import CustomerSimpleSerializer, OrderSimpleSerializer
|
||||
from django.db import transaction
|
||||
from rest_framework.exceptions import ValidationError
|
||||
class SaleCreateSerializer(serializers.ModelSerializer):
|
||||
iproducts = serializers.PrimaryKeyRelatedField(queryset=
|
||||
IProduct.objects.all(), many=True)
|
||||
class Meta:
|
||||
model = Sale
|
||||
fields = ['customer', 'order', 'product', 'iproducts']
|
||||
fields = ['customer', 'order', 'product', 'iproducts',
|
||||
'edelivery_date', 'trans_mode', 'receiver', 'receiver_phone'
|
||||
, 'receiver_address', 'remark']
|
||||
|
||||
def validate(self, attrs):
|
||||
order = attrs.get('order', None)
|
||||
|
@ -18,9 +22,11 @@ class SaleCreateSerializer(serializers.ModelSerializer):
|
|||
if order.customer:
|
||||
attrs['customer'] = order.customer
|
||||
attrs['product'] = order.product
|
||||
for i in attrs['iproducts']:
|
||||
if i.material != attrs['product']:
|
||||
raise exceptions.APIException('产品选取错误')
|
||||
ips = IProduct.objects.filter(id__in=[i.id for i in attrs['iproducts']])
|
||||
if ips.exclude(state=IProduct.SALE_OK).exists():
|
||||
raise exceptions.APIException('选取了非可用的产品')
|
||||
if ips.count() != ips.filter(material=attrs['product']).count():
|
||||
raise exceptions.APIException('产品选取错误')
|
||||
return super().validate(attrs)
|
||||
|
||||
|
||||
|
@ -44,8 +50,14 @@ class SaleProductCreateSerializer(serializers.ModelSerializer):
|
|||
fields = ['sale', 'iproduct']
|
||||
|
||||
def create(self, validated_data):
|
||||
validated_data['number'] = validated_data['iproduct'].number
|
||||
instance = SaleProduct.objects.create(**validated_data)
|
||||
instance.sale.count = SaleProduct.objects.filter(sale=instance.sale).count()
|
||||
instance.sale.save()
|
||||
with transaction.atomic():
|
||||
validated_data['number'] = validated_data['iproduct'].number
|
||||
instance = SaleProduct.objects.create(**validated_data)
|
||||
sale = instance.sale
|
||||
sale.count = SaleProduct.objects.filter(sale=instance.sale).count()
|
||||
sale.save()
|
||||
order = sale.order
|
||||
if order:
|
||||
if sale.count+order.delivered_count>order.count:
|
||||
raise exceptions.APIException('超过订单所需数量')
|
||||
return instance
|
|
@ -35,6 +35,13 @@ class SaleViewSet(CreateUpdateModelAMixin, ListModelMixin, RetrieveModelMixin, C
|
|||
elif self.action == 'retrieve':
|
||||
return SaleListSerializer
|
||||
return super().get_serializer_class()
|
||||
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
obj = self.get_object()
|
||||
if obj.is_audited:
|
||||
raise exceptions.APIException('该销售记录已审核,不可删除')
|
||||
obj.delete()
|
||||
IProduct.objects.filter(sale_iproduct__sale=obj).update()
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
data = request.data
|
||||
|
@ -49,6 +56,8 @@ class SaleViewSet(CreateUpdateModelAMixin, ListModelMixin, RetrieveModelMixin, C
|
|||
sale = Sale.objects.create(**vdata)
|
||||
i_l = []
|
||||
for i in iproducts:
|
||||
i.state = IProduct.SALE_LOCK
|
||||
i.save()
|
||||
i_d ={}
|
||||
i_d['sale'] = sale
|
||||
i_d['number'] = i.number
|
||||
|
@ -66,56 +75,59 @@ class SaleViewSet(CreateUpdateModelAMixin, ListModelMixin, RetrieveModelMixin, C
|
|||
obj = self.get_object()
|
||||
if obj.is_audited:
|
||||
raise exceptions.APIException('已审核通过')
|
||||
if obj.order:
|
||||
if obj.count + obj.order.delivered_count > obj.order.count:
|
||||
raise exceptions.APIException('超过订单所需数量')
|
||||
# 创建出库记录
|
||||
fifo = FIFO()
|
||||
fifo.sale = obj
|
||||
fifo.type = FIFO.FIFO_TYPE_SALE_OUT
|
||||
fifo.is_audited = True
|
||||
fifo.is_audited = False
|
||||
fifo.auditor = request.user
|
||||
fifo.inout_date = timezone.now()
|
||||
fifo.create_by = request.user
|
||||
fifo.number = 'CK' + ranstr(7)
|
||||
fifo.save()
|
||||
# 创建出库条目
|
||||
ips = IProduct.objects.filter(sale_iproduct__sale=obj)
|
||||
items = ips.values('warehouse', 'material', 'batch').annotate(total=Count('id'))
|
||||
for i in items:
|
||||
warehouse = WareHouse.objects.get(id=i['warehouse'])
|
||||
material = Material.objects.get(id=i['material'])
|
||||
fifoitem = FIFOItem()
|
||||
fifoitem.need_test = False
|
||||
fifoitem.warehouse = warehouse
|
||||
fifoitem.material = material
|
||||
fifoitem.count = i['total']
|
||||
fifoitem.batch = i['batch']
|
||||
fifoitem.fifo = fifo
|
||||
fifoitem.save()
|
||||
items_p = ips.filter(warehouse=warehouse, batch=i['batch'])
|
||||
ipxs = []
|
||||
for i in items_p:
|
||||
# 创建出库明细半成品
|
||||
ip = {}
|
||||
ip['fifoitem'] = fifoitem
|
||||
ip['number'] = i.number
|
||||
ip['material'] = i.material
|
||||
ip['iproduct'] = i
|
||||
ipxs.append(FIFOItemProduct(**ip))
|
||||
FIFOItemProduct.objects.bulk_create(ipxs)
|
||||
# 更新成品库情况
|
||||
ips.update(is_saled=True)
|
||||
# ips = IProduct.objects.filter(sale_iproduct__sale=obj)
|
||||
# items = ips.values('warehouse', 'material', 'batch').annotate(total=Count('id'))
|
||||
# for i in items:
|
||||
# warehouse = WareHouse.objects.get(id=i['warehouse'])
|
||||
# material = Material.objects.get(id=i['material'])
|
||||
# fifoitem = FIFOItem()
|
||||
# fifoitem.need_test = False
|
||||
# fifoitem.warehouse = warehouse
|
||||
# fifoitem.material = material
|
||||
# fifoitem.count = i['total']
|
||||
# fifoitem.batch = i['batch']
|
||||
# fifoitem.fifo = fifo
|
||||
# fifoitem.save()
|
||||
# items_p = ips.filter(warehouse=warehouse, batch=i['batch'])
|
||||
# ipxs = []
|
||||
# for i in items_p:
|
||||
# # 创建出库明细半成品
|
||||
# ip = {}
|
||||
# ip['fifoitem'] = fifoitem
|
||||
# ip['number'] = i.number
|
||||
# ip['material'] = i.material
|
||||
# ip['iproduct'] = i
|
||||
# ipxs.append(FIFOItemProduct(**ip))
|
||||
# FIFOItemProduct.objects.bulk_create(ipxs)
|
||||
|
||||
# 更新动态产品表情况
|
||||
from apps.wpm.models import WProduct
|
||||
WProduct.objects.filter(iproduct_wproduct__sale_iproduct__sale=obj).update(
|
||||
act_state=WProduct.WPR_ACT_STATE_SELLED)
|
||||
# from apps.wpm.models import WProduct
|
||||
# WProduct.objects.filter(iproduct_wproduct__sale_iproduct__sale=obj).update(
|
||||
# act_state=WProduct.WPR_ACT_STATE_SELLED)
|
||||
# 更新库存
|
||||
InmService.update_inm(fifo)
|
||||
# 变更审核状态
|
||||
# InmService.update_inm(fifo)
|
||||
# 变更销售提货审核状态
|
||||
obj.is_audited = True
|
||||
obj.save()
|
||||
# 变更订单状态
|
||||
if obj.order:
|
||||
order = obj.order
|
||||
order.delivered_count = order.delivered_count + obj.count
|
||||
order.save()
|
||||
# if obj.order:
|
||||
# order = obj.order
|
||||
# order.delivered_count = order.delivered_count + obj.count
|
||||
# order.save()
|
||||
return Response()
|
||||
|
||||
|
||||
|
@ -136,13 +148,13 @@ class SaleProductViewSet(ListModelMixin, DestroyModelMixin, CreateModelMixin, Ge
|
|||
if self.action == 'create':
|
||||
return SaleProductCreateSerializer
|
||||
return super().get_serializer_class()
|
||||
|
||||
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
obj = self.get_object()
|
||||
sale = obj.sale
|
||||
if sale.is_audited:
|
||||
raise exceptions.APIException('该销售记录已审核,不可删除产品')
|
||||
obj.delete()
|
||||
sale.count = SaleProduct.objects.filter(sale=obj.sale).count()
|
||||
sale.save()
|
||||
obj.delete()
|
||||
return Response()
|
Loading…
Reference in New Issue