feat: 增加出入库记录

This commit is contained in:
caoqianming 2023-09-21 10:20:19 +08:00
parent e4c3977c52
commit c6af3f7f62
5 changed files with 178 additions and 9 deletions

View File

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

View File

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

42
apps/inm/services.py Normal file
View File

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

View File

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

View File

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