Merge branch 'develop' of https://e.coding.net/ctcdevteam/hberp/hberp into develop

This commit is contained in:
shijing 2021-10-12 10:38:14 +08:00
commit d1fd46446a
21 changed files with 255 additions and 12 deletions

View File

@ -129,7 +129,7 @@ class RecordFormField(CommonAModel):
class ProductProcess(CommonAModel):
"""
产品生产工艺
产品生产工艺
"""
product = models.ForeignKey(Material, verbose_name='产品', on_delete=models.CASCADE)
process = models.ForeignKey(Process, verbose_name='工序', on_delete=models.CASCADE)

View File

View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@ -0,0 +1,7 @@
from django.apps import AppConfig
class SamConfig(AppConfig):
name = 'apps.pm'
verbose_name = '生产计划管理'

View File

@ -0,0 +1,41 @@
# Generated by Django 3.2.6 on 2021-10-08 08:02
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('mtm', '0018_material_count'),
('sam', '0004_order_planed_count'),
]
operations = [
migrations.CreateModel(
name='ProductionPlan',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('number', models.CharField(max_length=50, unique=True, verbose_name='编号')),
('count', models.IntegerField(default=0, verbose_name='生产数量')),
('start_date', models.DateField(verbose_name='计划开工日期')),
('end_date', models.DateField(verbose_name='计划完工日期')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='productionplan_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('order', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='sam.order', verbose_name='关联订单')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='生产产品')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='productionplan_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'verbose_name': '生产计划',
'verbose_name_plural': '生产计划',
},
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-10-08 08:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pm', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='productionplan',
name='count',
field=models.IntegerField(default=1, verbose_name='生产数量'),
),
]

View File

View File

@ -0,0 +1,30 @@
from apps.system.models import CommonAModel
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.db.models.base import Model
import django.utils.timezone as timezone
from django.db.models.query import QuerySet
from utils.model import SoftModel, BaseModel
from apps.mtm.models import Material
from apps.sam.models import Order
class ProductionPlan(CommonAModel):
"""
生产计划
"""
number = models.CharField('编号', max_length=50, unique=True)
order = models.ForeignKey(Order, verbose_name='关联订单', null=True, blank=True, on_delete=models.SET_NULL)
product = models.ForeignKey(Material, verbose_name='生产产品', on_delete=models.CASCADE)
count = models.IntegerField('生产数量', default=1)
start_date = models.DateField('计划开工日期')
end_date = models.DateField('计划完工日期')
class Meta:
verbose_name = '生产计划'
verbose_name_plural = verbose_name
def __str__(self):
return self.number

View File

@ -0,0 +1,17 @@
from apps.pm.models import ProductionPlan
from rest_framework import serializers
from apps.sam.serializers import OrderSerializer
from apps.mtm.serializers import MaterialSimpleSerializer
class ProductionPlanCreateFromOrderSerializer(serializers.ModelSerializer):
class Meta:
model = ProductionPlan
fields = ['order', 'number', 'count', 'start_date', 'end_date']
class ProductionPlanSerializer(serializers.ModelSerializer):
order_ = OrderSerializer(source='order', read_only=True)
product_ = MaterialSimpleSerializer(source='product', read_only=True)
class Meta:
model = ProductionPlan
fields ='__all__'

View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

13
hb_server/apps/pm/urls.py Normal file
View File

@ -0,0 +1,13 @@
from apps.pm.views import ProductionPlanViewSet
from django.db.models import base
from rest_framework import urlpatterns
from django.urls import path, include
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('productionplan', ProductionPlanViewSet, basename='productionplan')
urlpatterns = [
path('', include(router.urls)),
]

View File

@ -0,0 +1,52 @@
from apps.system.mixins import CreateUpdateModelAMixin
from apps.pm.serializers import ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer
from rest_framework.mixins import CreateModelMixin, ListModelMixin
from apps.pm.models import ProductionPlan
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from django.shortcuts import render
from apps.sam.models import Order
from rest_framework.exceptions import APIException
from rest_framework.response import Response
# Create your views here.
def updateOrderPlanedCount(order):
"""
更新订单已排数量
"""
planed_count = 0
plans = ProductionPlan.objects.filter(order=order)
for i in plans:
planed_count = planed_count + i.count
order.planed_count = planed_count
order.save()
class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModelMixin, GenericViewSet):
"""
生产计划
"""
perms_map = {'*': '*'}
queryset = ProductionPlan.objects.select_related('order', 'order__contract', 'product')
serializer_class = ProductionPlanSerializer
search_fields = ['number']
filterset_fields = []
ordering_fields = ['id']
ordering = ['-id']
def get_serializer_class(self):
if self.action in ['create']:
return ProductionPlanCreateFromOrderSerializer
return ProductionPlanSerializer
def create(self, request, *args, **kwargs):
data = request.data
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
if data.get('order', None):
order = Order.objects.get(pk=data['order'])
if order.planed_count >= data['count'] or data['count'] > 0:
pass
else:
raise APIException('排产数量错误')
instance = serializer.save(create_by=request.user, product=order.product)
updateOrderPlanedCount(instance.order)
return Response()

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-10-08 07:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sam', '0003_contract_invoice'),
]
operations = [
migrations.AddField(
model_name='order',
name='planed_count',
field=models.IntegerField(default=0, verbose_name='已排数量'),
),
]

View File

@ -5,6 +5,7 @@ from django.db.models.base import Model
import django.utils.timezone as timezone
from django.db.models.query import QuerySet
from utils.model import SoftModel, BaseModel
from apps.mtm.models import Material
@ -62,6 +63,7 @@ class Order(CommonAModel):
contract = models.ForeignKey(Contract, verbose_name='所属合同', null=True, blank=True, on_delete=models.SET_NULL)
product = models.ForeignKey(Material, verbose_name='所需产品', on_delete=models.CASCADE)
count = models.IntegerField('所需数量', default=0)
planed_count = models.IntegerField('已排数量', default=0)
delivery_date = models.DateField('交货日期')
class Meta:
verbose_name = '订单信息'

View File

@ -3,7 +3,9 @@ from apps.sam.models import Contract, Customer, Order
from rest_framework.viewsets import ModelViewSet
from apps.system.mixins import CreateUpdateCustomMixin
from django.shortcuts import render
from rest_framework.decorators import action
from django.db.models import F
from rest_framework.response import Response
# Create your views here.
class CustomerViewSet(CreateUpdateCustomMixin, ModelViewSet):
"""
@ -55,4 +57,14 @@ class OrderViewSet(CreateUpdateCustomMixin, ModelViewSet):
def get_serializer_class(self):
if self.action in ['create', 'update']:
return OrderCreateUpdateSerializer
return OrderSerializer
return OrderSerializer
@action(methods=['get'], detail=False, perms_map={'get':'*'})
def toplan(self, request, pk=None):
queryset = Order.objects.filter(count__gt=F('planed_count')).order_by('-id')
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)

View File

@ -115,6 +115,13 @@ class TicketFlowSerializer(serializers.ModelSerializer):
model = TicketFlow
fields = '__all__'
class TicketFlowSimpleSerializer(serializers.ModelSerializer):
participant_ = UserSimpleSerializer(source='participant', read_only=True)
state_ = StateSimpleSerializer(source='state', read_only=True)
class Meta:
model = TicketFlow
exclude = ['ticket_data']
class TicketHandleSerializer(serializers.Serializer):
transition = serializers.IntegerField(label="流转id")

View File

@ -57,10 +57,11 @@ class WfService(object):
@classmethod
def get_ticket_steps(cls, ticket:Ticket):
steps = cls.get_worlflow_states(ticket.workflow)
nsteps_list = []
for i in steps:
if ticket.state.is_hidden and ticket.state != i:
steps.remove(i)
return steps
if ticket.state == i or (not i.is_hidden):
nsteps_list.append(i)
return nsteps_list
@classmethod
def get_ticket_transitions(cls, ticket:Ticket):
@ -104,7 +105,7 @@ class WfService(object):
expression = i['expression'].format(**ticket_all_value)
import datetime, time # 用于支持条件表达式中对时间的操作
if eval(expression):
destination_state = State.objects.get(i['expression'].get('target_state'))
destination_state = State.objects.get(pk=i['target_state'])
return destination_state
@classmethod

View File

@ -1,6 +1,6 @@
from django.db.models import base
from rest_framework import urlpatterns
from apps.wf.views import CustomFieldViewSet, StateViewSet, TicketViewSet, TransitionViewSet, WorkflowViewSet
from apps.wf.views import CustomFieldViewSet, StateViewSet, TicketFlowViewSet, TicketViewSet, TransitionViewSet, WorkflowViewSet
from django.urls import path, include
from rest_framework.routers import DefaultRouter
@ -10,6 +10,7 @@ router.register('state', StateViewSet, basename='wf_state')
router.register('transition', TransitionViewSet, basename='wf_transitions')
router.register('customfield', CustomFieldViewSet, basename='wf_customfield')
router.register('ticket', TicketViewSet, basename='wf_ticket')
router.register('ticketflow', TicketFlowViewSet, basename='wf_ticketflow')
urlpatterns = [
path('', include(router.urls)),
]

View File

@ -3,7 +3,7 @@ from django.core.exceptions import AppRegistryNotReady
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketCreateSerializer, TicketFlowSerializer, TicketHandleSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer
from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketCreateSerializer, TicketFlowSerializer, TicketFlowSimpleSerializer, TicketHandleSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer
from django.shortcuts import get_object_or_404, render
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from rest_framework.decorators import action, api_view
@ -247,6 +247,16 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
ticket = self.get_object()
steps = WfService.get_ticket_steps(ticket)
return Response(StateSerializer(instance=steps, many=True).data)
@action(methods=['get'], detail=True, perms_map={'get':'*'})
def flowlogs(self, request, pk=None):
"""
工单流转记录
"""
ticket = self.get_object()
flowlogs = TicketFlow.objects.filter(ticket=ticket).order_by('-create_time')
serializer = TicketFlowSerializer(instance=flowlogs, many=True)
return Response(serializer.data)
@action(methods=['get'], detail=True, perms_map={'get':'*'})
def transitions(self, request, pk=None):
@ -276,6 +286,13 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
return Response()
else:
raise APIException('无需接单')
@action(methods=['post'], detail=True, perms_map={'post':'*'})
def retreat(self, request, pk=None):
"""
撤回工单允许创建人在指定状态撤回工单至初始状态状态设置中开启允许撤回
"""
pass
class TicketFlowViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
"""
@ -285,5 +302,5 @@ class TicketFlowViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
queryset = TicketFlow.objects.all()
serializer_class = TicketFlowSerializer
search_fields = ['suggestion']
filterset_fields = ['paticipant', 'state', 'ticket']
filterset_fields = ['ticket']
ordering = ['-create_time']

View File

@ -54,7 +54,8 @@ INSTALLED_APPS = [
'apps.mtm',
'apps.inm',
'apps.sam',
'apps.qm'
'apps.qm',
'apps.pm'
]
MIDDLEWARE = [

View File

@ -67,7 +67,7 @@ urlpatterns = [
path('api/inm/', include('apps.inm.urls')),
path('api/sam/', include('apps.sam.urls')),
path('api/qm/', include('apps.qm.urls')),
path('api/pm/', include('apps.pm.urls')),
# 工具
path('api/utils/signature/', GenSignature.as_view()),