feat: 增加出入库记录
This commit is contained in:
parent
e4c3977c52
commit
c6af3f7f62
|
@ -1,5 +1,7 @@
|
|||
from django.db import models
|
||||
from apps.utils.models import BaseModel, CommonBModel
|
||||
from apps.utils.models import BaseModel, CommonBModel, CommonBDModel
|
||||
from apps.pum.models import Supplier, PuOrder
|
||||
from apps.sam.models import Customer, Order
|
||||
from apps.mtm.models import Material
|
||||
# Create your models here.
|
||||
|
||||
|
@ -23,3 +25,52 @@ class MaterialBatch(BaseModel):
|
|||
WareHouse, on_delete=models.CASCADE, verbose_name='所在仓库')
|
||||
count = models.PositiveIntegerField('存量', default=0)
|
||||
expiration_date = models.DateField('有效期', null=True, blank=True)
|
||||
|
||||
|
||||
class MIO(CommonBDModel):
|
||||
"""
|
||||
出入库记录
|
||||
"""
|
||||
MIO_TYPE_DO_OUT = 'do_out'
|
||||
MIO_TYPE_SALE_OUT = 'sale_out'
|
||||
MIO_TYPE_PUR_IN = 'pur_in'
|
||||
MIO_TYPE_DO_IN = 'do_in'
|
||||
MIO_TYPE_OTHER_IN = 'other_in'
|
||||
MIO_TYPE_OTHER_OUT = 'other_out'
|
||||
|
||||
MIO_TYPES = (
|
||||
(MIO_TYPE_DO_OUT, '生产领料'),
|
||||
(MIO_TYPE_SALE_OUT, '销售发货'),
|
||||
(MIO_TYPE_PUR_IN, '采购入库'),
|
||||
(MIO_TYPE_DO_IN, '生产入库'),
|
||||
(MIO_TYPE_OTHER_IN, '其他入库'),
|
||||
(MIO_TYPE_OTHER_OUT, '其他出库')
|
||||
)
|
||||
MIO_CREATE = 10
|
||||
MIO_SUBMITED = 20
|
||||
MIO_STATES = (
|
||||
(MIO_CREATE, '创建中'),
|
||||
(MIO_SUBMITED, '已提交')
|
||||
)
|
||||
number = models.CharField('编号', max_length=20)
|
||||
state = models.PositiveSmallIntegerField('状态', choices=MIO_STATES, default=10, help_text=str(MIO_CREATE))
|
||||
type = models.CharField('出入库类型', max_length=10, default=MIO_TYPE_DO_OUT, choices=MIO_TYPES, help_text=str(MIO_TYPES))
|
||||
inout_date = models.DateField('出入库日期', null=True, blank=True)
|
||||
supplier = models.ForeignKey(Supplier, verbose_name='供应商', on_delete=models.CASCADE, null=True, blank=True)
|
||||
customer = models.ForeignKey(Customer, verbose_name='客户', on_delete=models.CASCADE, null=True, blank=True)
|
||||
pu_order = models.ForeignKey(PuOrder, verbose_name='关联采购订单', on_delete=models.CASCADE, null=True, blank=True)
|
||||
order = models.ForeignKey(Order, verbose_name='关联订单', on_delete=models.CASCADE, null=True, blank=True)
|
||||
note = models.CharField('备注', max_length=1000, default='')
|
||||
expiration_date = models.DateField('有效期', null=True, blank=True)
|
||||
submit_time = models.DateTimeField('提交时间', null=True, blank=True)
|
||||
|
||||
|
||||
class MIOItem(BaseModel):
|
||||
"""
|
||||
出入库明细
|
||||
"""
|
||||
mio = models.ForeignKey(MIO, verbose_name='关联出入库', on_delete=models.CASCADE)
|
||||
warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='仓库')
|
||||
material = models.ForeignKey(Material, verbose_name='物料', on_delete=models.CASCADE)
|
||||
batch = models.CharField('批次号', max_length=20)
|
||||
count = models.PositiveIntegerField('数量', default=0)
|
|
@ -1,7 +1,9 @@
|
|||
from rest_framework import serializers
|
||||
from apps.utils.serializers import CustomModelSerializer
|
||||
from apps.inm.models import WareHouse, MaterialBatch
|
||||
from apps.inm.models import WareHouse, MaterialBatch, MIO, MIOItem
|
||||
from apps.utils.constants import EXCLUDE_FIELDS_DEPT, EXCLUDE_FIELDS_BASE
|
||||
from apps.pum.models import PuOrder
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
|
||||
class WareHourseSerializer(CustomModelSerializer):
|
||||
|
@ -17,4 +19,33 @@ class MaterialBatchSerializer(CustomModelSerializer):
|
|||
class Meta:
|
||||
model = MaterialBatch
|
||||
fields = '__all__'
|
||||
read_only_fields = EXCLUDE_FIELDS_BASE
|
||||
read_only_fields = EXCLUDE_FIELDS_BASE
|
||||
|
||||
|
||||
class MIOSerializer(CustomModelSerializer):
|
||||
create_by_name = serializers.CharField(source='create_by.name', read_only=True)
|
||||
class Meta:
|
||||
model = MIO
|
||||
fields = '__all__'
|
||||
read_only_fields = EXCLUDE_FIELDS_DEPT + ['state', 'submit_time']
|
||||
|
||||
def validate(self, attrs):
|
||||
if 'pu_order' in attrs and attrs['pu_order']:
|
||||
attrs['supplier'] = attrs['pu_order'].supplier
|
||||
elif 'order' in attrs and attrs['order']:
|
||||
attrs['customer'] = attrs['order'].customer
|
||||
return super().validate(attrs)
|
||||
|
||||
def create(self, validated_data):
|
||||
type = validated_data['type']
|
||||
if type == MIO.MIO_TYPE_PUR_IN:
|
||||
pu_order = validated_data.get('pu_order', None)
|
||||
if pu_order and pu_order.state in [PuOrder.PUORDER_SUBMITED, PuOrder.PUORDER_SHIP]:
|
||||
pass
|
||||
else:
|
||||
raise ValidationError('该采购订单不可选')
|
||||
return super().create(validated_data)
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
validated_data.pop('type')
|
||||
return super().update(instance, validated_data)
|
|
@ -0,0 +1,42 @@
|
|||
from apps.inm.models import MIO, MIOItem, MaterialBatch
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
|
||||
class InmService:
|
||||
|
||||
@classmethod
|
||||
def update_inm(cls, instance: MIO):
|
||||
"""
|
||||
更新库存, 暂不支持反向操作
|
||||
"""
|
||||
if instance.type in [MIO.MIO_TYPE_PUR_IN, MIO.MIO_TYPE_DO_IN, MIO.MIO_TYPE_OTHER_IN]: # 采购入库, 生产入库, 其他入库
|
||||
cls.update_mb(instance)
|
||||
if instance.type == MIO.MIO_TYPE_PUR_IN: # 需要更新订单
|
||||
from apps.pum.services import PumService
|
||||
PumService.mio_purin(instance)
|
||||
elif instance.type in [MIO.MIO_TYPE_DO_OUT, MIO.MIO_TYPE_SALE_OUT, MIO.MIO_TYPE_OTHER_OUT]: # 生产领料 销售出库
|
||||
cls.update_mb(instance, -1)
|
||||
if instance.type == MIO.MIO_TYPE_SALE_OUT:
|
||||
from apps.sam.services import SamService
|
||||
SamService.mio_saleout(instance)
|
||||
|
||||
@classmethod
|
||||
def update_mb(cls, instance: MIO, in_or_out: int =1):
|
||||
"""
|
||||
更新物料批次
|
||||
"""
|
||||
for i in MIOItem.objects.filter(mio=instance):
|
||||
material = i.material
|
||||
warehouse = i.warehouse
|
||||
mb, _ = MaterialBatch.objects.get_or_create(material=material, warehouse=warehouse, batch=i.batch,\
|
||||
defaults={'material':material, 'warehouse':warehouse, 'count':0, 'batch':i.batch})
|
||||
if in_or_out == 1:
|
||||
mb.count = mb.count + i.count
|
||||
if mb.expiration_date is None:
|
||||
mb.expiration_date = i.expiration_date
|
||||
mb.save()
|
||||
elif in_or_out == -1:
|
||||
mb.count = mb.count - i.count
|
||||
if mb.count < 0:
|
||||
raise ValidationError('批次库存不足,操作失败')
|
||||
mb.save()
|
|
@ -1,6 +1,6 @@
|
|||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from apps.inm.views import (WarehouseVIewSet, MaterialBatchViewSet)
|
||||
from apps.inm.views import (WarehouseVIewSet, MaterialBatchViewSet, MIOViewSet)
|
||||
|
||||
API_BASE_URL = 'api/inm/'
|
||||
HTML_BASE_URL = 'inm/'
|
||||
|
@ -8,6 +8,7 @@ HTML_BASE_URL = 'inm/'
|
|||
router = DefaultRouter()
|
||||
router.register('warehouse', WarehouseVIewSet, basename='warehouse')
|
||||
router.register('materialbatch', MaterialBatchViewSet, basename='materialbatch')
|
||||
router.register('mio', MIOViewSet, basename='mio')
|
||||
urlpatterns = [
|
||||
path(API_BASE_URL, include(router.urls)),
|
||||
]
|
|
@ -1,10 +1,16 @@
|
|||
from django.shortcuts import render
|
||||
from rest_framework.mixins import ListModelMixin
|
||||
from rest_framework.exceptions import ParseError
|
||||
from rest_framework.mixins import ListModelMixin, DestroyModelMixin
|
||||
from rest_framework.exceptions import ParseError, PermissionDenied
|
||||
from rest_framework.decorators import action
|
||||
from django.db import transaction
|
||||
from rest_framework import serializers
|
||||
from django.utils import timezone
|
||||
from rest_framework.response import Response
|
||||
|
||||
from apps.inm.models import WareHouse, MaterialBatch
|
||||
from apps.inm.serializers import MaterialBatchSerializer, WareHourseSerializer
|
||||
from apps.inm.models import WareHouse, MaterialBatch, MIO
|
||||
from apps.inm.serializers import (MaterialBatchSerializer, WareHourseSerializer, MIOSerializer)
|
||||
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
|
||||
from apps.inm.services import InmService
|
||||
|
||||
|
||||
# Create your views here.
|
||||
|
@ -36,4 +42,42 @@ class MaterialBatchViewSet(ListModelMixin, CustomGenericViewSet):
|
|||
serializer_class = MaterialBatchSerializer
|
||||
select_related_fields = ['warehouse', 'material']
|
||||
filterset_fields = ['warehouse', 'material']
|
||||
|
||||
|
||||
|
||||
class MIOViewSet(ListModelMixin, DestroyModelMixin, CustomGenericViewSet):
|
||||
"""
|
||||
list: 出入库记录
|
||||
|
||||
出入库记录
|
||||
"""
|
||||
perms_map = {'get': '*', 'delete': 'mio.delete'}
|
||||
queryset = MIO.objects.all()
|
||||
select_related_fields = ['create_by']
|
||||
serializer_class = MIOSerializer
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
if instance.state != MIO.MIO_CREATE:
|
||||
raise ParseError('非创建中不可删除')
|
||||
return super().perform_destroy(instance)
|
||||
|
||||
@action(methods=['post'], detail=True, perms_map={'post': 'mio.update'}, serializer_class=serializers.Serializer)
|
||||
@transaction.atomic
|
||||
def submit(self, request, *args, **kwargs):
|
||||
"""提交
|
||||
|
||||
提交
|
||||
"""
|
||||
ins = self.get_object()
|
||||
user = request.user
|
||||
if ins.create_by != user:
|
||||
raise PermissionDenied('非创建人不可提交')
|
||||
if ins.state != MIO.MIO_CREATE:
|
||||
raise ParseError('订单非创建中')
|
||||
ins.submit_time = timezone.now()
|
||||
ins.state = MIO.MIO_SUBMITED
|
||||
ins.save()
|
||||
InmService.update_inm(ins)
|
||||
return Response()
|
||||
|
||||
class MIOItemViewSet(CustomModelViewSet):
|
||||
pass
|
Loading…
Reference in New Issue