Merge branch 'develop' of https://e.coding.net/ctcdevteam/hberp/hberp into develop
This commit is contained in:
commit
3a346e3be5
|
@ -1,16 +1,38 @@
|
|||
from django_filters import rest_framework as filters
|
||||
from apps.hrm.models import ClockRecord, Employee
|
||||
from apps.hrm.models import ClockRecord, Employee, NotWorkRemark
|
||||
from utils.mixins import DynamicFieldsFilterMixin
|
||||
|
||||
class ClockRecordFilterSet(filters.FilterSet):
|
||||
create_time_start = filters.DateFilter(field_name="create_time", lookup_expr='gte')
|
||||
create_time_end = filters.DateFilter(field_name="create_time", lookup_expr='lte')
|
||||
year = filters.NumberFilter(method='filter_year')
|
||||
month = filters.NumberFilter(method='filter_month')
|
||||
class Meta:
|
||||
model = ClockRecord
|
||||
fields = ['create_by', 'create_time_start', 'create_time_end']
|
||||
fields = ['create_by', 'create_time_start', 'create_time_end', 'year', 'month']
|
||||
|
||||
def filter_year(self, queryset, name, value):
|
||||
return queryset.filter(create_time_date__year=value)
|
||||
|
||||
def filter_month(self, queryset, name, value):
|
||||
return queryset.filter(create_time_date__month=value)
|
||||
|
||||
class EmployeeFilterSet(DynamicFieldsFilterMixin, filters.FilterSet):
|
||||
|
||||
class Meta:
|
||||
model = Employee
|
||||
fields = ['job_state']
|
||||
fields = ['job_state']
|
||||
|
||||
|
||||
class NotWorkRemarkFilterSet(filters.FilterSet):
|
||||
year = filters.NumberFilter(method='filter_year')
|
||||
month = filters.NumberFilter(method='filter_month')
|
||||
class Meta:
|
||||
model = NotWorkRemark
|
||||
fields = ['year', 'month', 'user']
|
||||
|
||||
def filter_year(self, queryset, name, value):
|
||||
return queryset.filter(not_work_date__year=value)
|
||||
|
||||
def filter_month(self, queryset, name, value):
|
||||
return queryset.filter(not_work_date__month=value)
|
|
@ -0,0 +1,32 @@
|
|||
# Generated by Django 3.2.9 on 2022-02-22 03:12
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hrm', '0007_auto_20220218_0843'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='notworkremark',
|
||||
name='day',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='notworkremark',
|
||||
name='month',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='notworkremark',
|
||||
name='year',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='notworkremark',
|
||||
name='not_work_date',
|
||||
field=models.DateField(default=django.utils.timezone.now, verbose_name='未打卡日期'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
|
@ -44,9 +44,7 @@ class NotWorkRemark(CommonAModel):
|
|||
"""
|
||||
离岗说明
|
||||
"""
|
||||
year = models.PositiveSmallIntegerField('年', default=2022)
|
||||
month = models.PositiveSmallIntegerField('月', default=2)
|
||||
day = models.PositiveSmallIntegerField('日', default=1)
|
||||
not_work_date = models.DateField('未打卡日期')
|
||||
user = models.ForeignKey(User, verbose_name='用户', on_delete=models.CASCADE)
|
||||
remark = models.CharField('未打卡说明', null=True, blank=True, max_length=200)
|
||||
|
||||
|
@ -58,4 +56,6 @@ class ClockRecord(CommonADModel):
|
|||
type_choice = (
|
||||
(ClOCK_WORK1, '上班打卡'),
|
||||
)
|
||||
type = models.PositiveSmallIntegerField('打卡类型', choices=type_choice, default=ClOCK_WORK1)
|
||||
type = models.PositiveSmallIntegerField('打卡类型', choices=type_choice, default=ClOCK_WORK1)
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework.serializers import ModelSerializer
|
|||
from rest_framework import serializers
|
||||
|
||||
from utils.mixins import DynamicFieldsSerializerMixin
|
||||
from .models import ClockRecord, Employee
|
||||
from .models import ClockRecord, Employee, NotWorkRemark
|
||||
from apps.system.serializers import OrganizationSimpleSerializer, UserListSerializer, UserSimpleSerializer
|
||||
from django.db.models.query import Prefetch
|
||||
|
||||
|
@ -30,3 +30,8 @@ class ClockRecordListSerializer(serializers.ModelSerializer):
|
|||
class Meta:
|
||||
model = ClockRecord
|
||||
fields = '__all__'
|
||||
|
||||
class NotWorkRemarkListSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = NotWorkRemark
|
||||
fields = '__all__'
|
|
@ -10,7 +10,7 @@ def update_all_employee_not_atwork():
|
|||
"""
|
||||
将所有员工设为非在岗状态
|
||||
"""
|
||||
Employee.objects.all().update(is_atwork=False, last_check_time = None)
|
||||
Employee.objects.all().update(is_atwork=False, last_check_time = None, not_work_remark=None)
|
||||
|
||||
@shared_task
|
||||
def update_all_user_facedata_cache():
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
from django.db.models import base
|
||||
from rest_framework import urlpatterns
|
||||
from apps.hrm.views import ClockRecordViewSet, EmployeeViewSet, FaceLogin
|
||||
from apps.hrm.views import ClockRecordViewSet, EmployeeViewSet, FaceLogin, NotWorkRemarkViewSet
|
||||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('employee', EmployeeViewSet, basename='employee')
|
||||
router.register('clock_record', ClockRecordViewSet, basename='clock_record')
|
||||
router.register('not_work_remark', NotWorkRemarkViewSet, basename='not_work_reamrk')
|
||||
urlpatterns = [
|
||||
path('facelogin/', FaceLogin.as_view()),
|
||||
path('', include(router.urls)),
|
||||
|
|
|
@ -5,12 +5,12 @@ from django.utils import timezone
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ModelViewSet, GenericViewSet
|
||||
from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin, CreateModelMixin, ListModelMixin
|
||||
from apps.hrm.filters import ClockRecordFilterSet, EmployeeFilterSet
|
||||
from apps.hrm.filters import ClockRecordFilterSet, EmployeeFilterSet, NotWorkRemarkFilterSet
|
||||
from apps.hrm.services import HRMService
|
||||
from apps.hrm.tasks import update_all_user_facedata_cache
|
||||
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
|
||||
from apps.hrm.models import ClockRecord, Employee, NotWorkRemark
|
||||
from apps.hrm.serializers import ClockRecordListSerializer, EmployeeNotWorkRemarkSerializer, EmployeeSerializer, FaceClockCreateSerializer, FaceLoginSerializer
|
||||
from apps.hrm.serializers import ClockRecordListSerializer, EmployeeNotWorkRemarkSerializer, EmployeeSerializer, FaceClockCreateSerializer, FaceLoginSerializer, NotWorkRemarkListSerializer
|
||||
|
||||
|
||||
|
||||
|
@ -67,16 +67,12 @@ class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMix
|
|||
remark = request.data.get('not_work_remark', '')
|
||||
obj.not_work_remark = remark
|
||||
obj.save()
|
||||
now_local = timezone.localtime()
|
||||
now = timezone.now()
|
||||
instance, created = NotWorkRemark.objects.get_or_create(
|
||||
year = now_local.year,
|
||||
month = now_local.month,
|
||||
day = now_local.day,
|
||||
not_work_date = now.date(),
|
||||
user = obj.user,
|
||||
defaults={
|
||||
"year":now_local.year,
|
||||
"month":now_local.month,
|
||||
"day":now_local.day,
|
||||
"not_work_date":now.date(),
|
||||
"user":obj.user,
|
||||
"remark":remark,
|
||||
"create_by":request.user,
|
||||
|
@ -134,7 +130,15 @@ class ClockRecordViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
|
|||
return Response('非打卡时间范围', status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
|
||||
class NotWorkRemarkViewSet(ListModelMixin, GenericViewSet):
|
||||
"""
|
||||
离岗说明
|
||||
"""
|
||||
perms_map = {'get':'*'}
|
||||
queryset = NotWorkRemark.objects.select_related('user').all()
|
||||
serializer_class = NotWorkRemarkListSerializer
|
||||
filterset_class = NotWorkRemarkFilterSet
|
||||
ordering = ['-pk']
|
||||
|
||||
|
||||
import base64
|
||||
|
|
|
@ -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)
|
||||
|
@ -105,7 +107,6 @@ class Step(CommonAModel):
|
|||
name = models.CharField('工序步骤名称', max_length=100)
|
||||
number = models.CharField('步骤编号', max_length=100, null=True, blank=True)
|
||||
instruction_content = models.TextField('相应操作指导', null=True, blank=True)
|
||||
need_test = models.BooleanField('是否需要检验', default=False)
|
||||
sort = models.IntegerField('排序号', default=1)
|
||||
equipments = models.ManyToManyField(Equipment, verbose_name='使用设备', related_name='step_equips')
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ class PackItemSerializer(serializers.ModelSerializer):
|
|||
class PackItemCreateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = PackItem
|
||||
fields = ['material', 'name', 'specification', 'unit', 'count', 'sort']
|
||||
fields = ['product', 'material', 'name', 'specification', 'unit', 'count', 'sort']
|
||||
|
||||
class PackItemUpdateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
|
|
|
@ -40,7 +40,7 @@ class PackItemViewSet(CreateUpdateModelAMixin, ModelViewSet):
|
|||
queryset = PackItem.objects.all()
|
||||
serializer_class = PackItemSerializer
|
||||
search_fields = ['name', 'number']
|
||||
filterset_fields = ['material']
|
||||
filterset_fields = ['material', 'product']
|
||||
ordering = ['sort']
|
||||
|
||||
def get_serializer_class(self):
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# Generated by Django 3.2.9 on 2022-02-22 01:58
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('qm', '0026_alter_testrecord_is_testok'),
|
||||
('pm', '0027_auto_20220221_1027'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='subproductionplan',
|
||||
name='first_test',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='qm.testrecord'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,20 @@
|
|||
# Generated by Django 3.2.9 on 2022-02-22 02:45
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('qm', '0026_alter_testrecord_is_testok'),
|
||||
('pm', '0028_alter_subproductionplan_first_test'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='subproductionplan',
|
||||
name='first_test',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='qm.testrecord', verbose_name='首件检验'),
|
||||
),
|
||||
]
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
from apps.qm.models import TestRecord
|
||||
from apps.system.models import CommonAModel, Organization, User
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
|
@ -92,7 +93,8 @@ class SubProductionPlan(CommonAModel):
|
|||
end_date_real = models.DateField('实际完工日期', null=True, blank=True)
|
||||
is_picked = models.BooleanField('是否已领料', default=False)
|
||||
|
||||
first_test = models.ForeignKey('qm.testrecord', on_delete=models.CASCADE, null=True, blank=True)
|
||||
first_test = models.ForeignKey('qm.testrecord', on_delete=models.SET_NULL,
|
||||
null=True, blank=True, verbose_name='首件检验')
|
||||
leader_1 = models.ForeignKey(User, on_delete=models.CASCADE,
|
||||
verbose_name="工序负责人", null=True, blank=True, related_name='first_leader_1')
|
||||
leader_2 = models.ForeignKey(User, on_delete=models.CASCADE,
|
||||
|
@ -105,6 +107,10 @@ class SubProductionPlan(CommonAModel):
|
|||
class Meta:
|
||||
verbose_name = '子生产计划'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
# @property
|
||||
# def first_test(self):
|
||||
# return self.test_subplan.filter(type=TestRecord.TEST_FIRST, is_deleted=False).first()
|
||||
|
||||
class SubProductionProgress(BaseModel):
|
||||
"""
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from apps.mtm.models import RecordForm
|
||||
from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress
|
||||
from rest_framework import serializers
|
||||
from apps.qm.models import TestRecord
|
||||
from apps.qm.serializers import TestRecordShortSerializer
|
||||
from apps.sam.serializers import OrderSerializer, OrderSimpleSerializer
|
||||
from apps.mtm.serializers import MaterialSimpleSerializer, ProcessSimpleSerializer, RecordFormSimpleSerializer, SubProductionSimpleSerializer
|
||||
|
@ -44,6 +45,7 @@ class SubProductionPlanListSerializer(DynamicFieldsSerializerMixin, serializers.
|
|||
leader_2_ = UserSimpleSerializer(source='leader_2', read_only=True)
|
||||
leader_3_ = UserSimpleSerializer(source='leader_3', read_only=True)
|
||||
first_test_ = TestRecordShortSerializer(source='first_test', read_only=True)
|
||||
# first_test_ = serializers.SerializerMethodField()
|
||||
class Meta:
|
||||
model=SubProductionPlan
|
||||
fields = '__all__'
|
||||
|
@ -51,7 +53,10 @@ class SubProductionPlanListSerializer(DynamicFieldsSerializerMixin, serializers.
|
|||
def get_plan_product_(self, obj):
|
||||
return MaterialSimpleSerializer(instance=obj.production_plan.product).data
|
||||
|
||||
|
||||
# def get_first_test_(self, obj):
|
||||
# if obj.first_test:
|
||||
# return TestRecordShortSerializer(instance=obj.first_test).data
|
||||
# return None
|
||||
|
||||
|
||||
class SubProductionPlanUpdateSerializer(serializers.ModelSerializer):
|
||||
|
@ -88,5 +93,4 @@ class FirstTestInitSerializer(serializers.Serializer):
|
|||
|
||||
|
||||
class FirstTestAuditSerializer(serializers.Serializer):
|
||||
leader = serializers.CharField()
|
||||
base64 = serializers.CharField()
|
||||
leader = serializers.CharField()
|
|
@ -11,6 +11,7 @@ from apps.inm.serializers import MaterialBatchSerializer
|
|||
from apps.mtm.models import Material, RecordFormField, Step, SubProduction, SubprodctionMaterial
|
||||
from apps.pm.filters import PlanFilterSet, SubproductionProgressFilterSet
|
||||
from apps.qm.models import TestRecord, TestRecordItem
|
||||
from apps.qm.serializers import TestRecordDetailBaseSerializer
|
||||
from apps.system.mixins import CreateUpdateModelAMixin
|
||||
from apps.pm.serializers import FirstTestAuditSerializer, FirstTestInitSerializer, GenSubPlanSerializer, PickNeedSerializer, PlanDestorySerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, ResourceConvertListSerializer, ResourceConvertSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer
|
||||
from rest_framework.mixins import CreateModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
|
||||
|
@ -22,6 +23,7 @@ from rest_framework.exceptions import APIException, ParseError, ValidationError
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.decorators import action
|
||||
from django.db.models import F
|
||||
from apps.system.serializers import UserSimpleSerializer
|
||||
from utils.tools import ranstr
|
||||
from django.db import transaction
|
||||
from rest_framework import status
|
||||
|
@ -151,7 +153,7 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
|
|||
perms_map = {'get': '*', 'put':'subplan_update'}
|
||||
queryset = SubProductionPlan.objects.select_related('process',
|
||||
'workshop', 'subproduction', 'product',
|
||||
'production_plan__product', 'leader_1', 'leader_2', 'leader_3')
|
||||
'production_plan__product', 'leader_1', 'leader_2', 'leader_3', 'first_test')
|
||||
search_fields = []
|
||||
serializer_class = SubProductionPlanListSerializer
|
||||
filterset_fields = ['production_plan', 'process', 'state', 'product', 'workshop']
|
||||
|
@ -242,7 +244,10 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
|
|||
首件检查表初始化
|
||||
"""
|
||||
obj = self.get_object()
|
||||
if obj.first_test is None:
|
||||
first_test = obj.first_test
|
||||
if first_test:
|
||||
return Response(TestRecordDetailBaseSerializer(instance=first_test).data)
|
||||
else:
|
||||
rdata = request.data
|
||||
serializer = self.get_serializer(data=rdata)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
@ -260,10 +265,11 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
|
|||
tri.is_hidden = i.is_hidden
|
||||
tri.create_by = request.user
|
||||
tri.save()
|
||||
return Response()
|
||||
raise APIException('首件检查已存在')
|
||||
obj.first_test = tr
|
||||
obj.save()
|
||||
return Response(TestRecordDetailBaseSerializer(instance=tr).data)
|
||||
|
||||
@action(methods=['post'], detail=True, perms_map={'post':'first_test_audit'}, serializer_class=FirstTestAuditSerializer)
|
||||
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=FirstTestAuditSerializer)
|
||||
@transaction.atomic
|
||||
def first_audit(self, request, pk=None):
|
||||
obj = self.get_object()
|
||||
|
@ -278,21 +284,18 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
|
|||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
vdata = serializer.validated_data
|
||||
user, msg = HRMService.face_compare_from_base64(vdata.get('base64'))
|
||||
if user:
|
||||
le = vdata.get('leader')
|
||||
if le not in ['leader_1', 'leader_2', 'leader_3']:
|
||||
return Response('审核人有误', status=status.HTTP_400_BAD_REQUEST)
|
||||
if vdata.get('leader') == 'leader_1':
|
||||
obj.leader_1 = user
|
||||
elif vdata.get('leader') == 'leader_2':
|
||||
obj.leader_2 = user
|
||||
else:
|
||||
obj.leader_3 = user
|
||||
obj.first_sign_time = timezone.now()
|
||||
obj.save()
|
||||
return Response()
|
||||
return Response(msg, status=status.HTTP_400_BAD_REQUEST)
|
||||
le = vdata.get('leader')
|
||||
if le not in ['leader_1', 'leader_2', 'leader_3']:
|
||||
return Response('审核人有误', status=status.HTTP_400_BAD_REQUEST)
|
||||
if vdata.get('leader') == 'leader_1':
|
||||
obj.leader_1 = request.user
|
||||
elif vdata.get('leader') == 'leader_2':
|
||||
obj.leader_2 = request.user
|
||||
else:
|
||||
obj.leader_3 = request.user
|
||||
obj.first_sign_time = timezone.now()
|
||||
obj.save()
|
||||
return Response(UserSimpleSerializer(instance=request.user).data)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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,20 @@
|
|||
# Generated by Django 3.2.9 on 2022-02-22 02:45
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pm', '0029_alter_subproductionplan_first_test'),
|
||||
('qm', '0026_alter_testrecord_is_testok'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='testrecord',
|
||||
name='subproduction_plan',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='test_subplan', to='pm.subproductionplan', verbose_name='关联的生产子计划'),
|
||||
),
|
||||
]
|
|
@ -66,7 +66,7 @@ class TestRecord(CommonADModel):
|
|||
wproduct = models.ForeignKey('wpm.wproduct', verbose_name='关联的动态产品', on_delete=models.CASCADE, null=True, blank=True, related_name='test_wproduct')
|
||||
material = models.ForeignKey('mtm.material', verbose_name='关联的物料状态', on_delete=models.CASCADE, null=True, blank=True)
|
||||
step = models.ForeignKey('mtm.step', verbose_name='关联的工序步骤', on_delete=models.CASCADE, null=True, blank=True)
|
||||
subproduction_plan = models.ForeignKey('pm.subproductionplan', verbose_name='关联的生产子计划', on_delete=models.CASCADE, null=True, blank=True)
|
||||
subproduction_plan = models.ForeignKey('pm.subproductionplan', verbose_name='关联的生产子计划', on_delete=models.CASCADE, null=True, blank=True, related_name='test_subplan')
|
||||
fifo_item = models.ForeignKey('inm.fifoitem', verbose_name='关联的出入库批次', on_delete=models.CASCADE, null=True, blank=True)
|
||||
origin_test = models.ForeignKey('self', verbose_name='原检验记录', on_delete=models.CASCADE, null=True, blank=True)
|
||||
is_submited = models.BooleanField('是否提交', default=False)
|
||||
|
|
|
@ -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='预计发货数量'),
|
||||
),
|
||||
]
|
|
@ -7,7 +7,7 @@ from django.db.models.query import QuerySet
|
|||
|
||||
|
||||
from utils.model import SoftModel, BaseModel
|
||||
from apps.mtm.models import Material
|
||||
from apps.mtm.models import Material, PackItem
|
||||
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
@ -84,11 +103,21 @@ class SaleProduct(BaseModel):
|
|||
number = models.CharField('物品编号', max_length=50)
|
||||
iproduct = models.ForeignKey('inm.iproduct', verbose_name='关联库存产品', on_delete=models.CASCADE, related_name='sale_iproduct')
|
||||
remark = models.TextField('备注', null=True, blank=True)
|
||||
|
||||
packnum = models.CharField('装箱单号', max_length=100, null=True, blank=True)
|
||||
class Meta:
|
||||
unique_together = (
|
||||
('sale','iproduct'), # 联合唯一
|
||||
)
|
||||
|
||||
class SalePack(BaseModel):
|
||||
"""
|
||||
销售打包
|
||||
"""
|
||||
sale_product = models.ForeignKey(SaleProduct, verbose_name='关联销售产品',
|
||||
on_delete=models.CASCADE)
|
||||
packitem = models.ForeignKey(PackItem, verbose_name='打包项目',
|
||||
on_delete=models.CASCADE)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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