diff --git a/hb_server/apps/inm/filters.py b/hb_server/apps/inm/filters.py index 73f92a6..41dcf61 100644 --- a/hb_server/apps/inm/filters.py +++ b/hb_server/apps/inm/filters.py @@ -15,7 +15,8 @@ class MbFilterSet(filters.FilterSet): def filter_tag(self, queryset, name, value): if value == 'expired': - queryset = queryset.exclude(expiration_date=None).filter(expiration_date__lte=timezone.now()) + queryset = queryset.exclude(expiration_date=None).filter( + expiration_date__lte=timezone.now()) return queryset diff --git a/hb_server/apps/inm/serializers.py b/hb_server/apps/inm/serializers.py index 766a164..3569848 100644 --- a/hb_server/apps/inm/serializers.py +++ b/hb_server/apps/inm/serializers.py @@ -51,10 +51,9 @@ class MaterialBatchSerializer(serializers.ModelSerializer): class IProductListSerializer(serializers.ModelSerializer): material_ = MaterialSimpleSerializer(source='material', read_only=True) warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True) - is_mtested = serializers.BooleanField(source='wproduct.is_mtested', read_only=True) is_mtestok = serializers.BooleanField(source='wproduct.is_mtestok', read_only=True) remark_mtest = serializers.CharField(source='wproduct.remark_mtest', read_only=True) - + class Meta: model = IProduct fields = '__all__' diff --git a/hb_server/apps/sam/filters.py b/hb_server/apps/sam/filters.py index 779e7b4..a0c3626 100644 --- a/hb_server/apps/sam/filters.py +++ b/hb_server/apps/sam/filters.py @@ -1,22 +1,36 @@ from django_filters import rest_framework as filters from apps.sam.models import Order +from django.db.models import F +from datetime import datetime, timedelta class OrderFilterSet(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') material = filters.NumberFilter(method='filter_material') + tag = filters.CharFilter(method='filter_tag') class Meta: model = Order - fields = ['product', 'contract', 'customer', 'create_time_start', 'create_time_end'] + fields = ['product', 'contract', 'customer', 'create_time_start', + 'create_time_end', 'tag'] def filter_material(self, queryset, name, value): """ 按物料筛选 """ - return queryset.filter(plan_order__subplan_plan__progress_subplan__material__id=value).distinct() + return queryset.filter( + plan_order__subplan_plan__progress_subplan__material__id=value).distinct() + def filter_tag(self, queryset, name, value): + if value == 'near_delivery': + day7_after = datetime.now() + timedelta(days=7) + queryset = queryset.filter(delivered_count__lt=F('count'), + delivery_date__lte = datetime.date(day7_after)) + elif value == 'out_delivery': + queryset = queryset.filter(delivered_count__lt=F('count'), + delivery_date__gt = datetime.date(datetime.now())) + return queryset class ContractFilterSet(filters.FilterSet): create_time_start = filters.DateFilter(field_name="create_time", lookup_expr='gte') diff --git a/hb_server/apps/sam/migrations/0011_order_need_mtest.py b/hb_server/apps/sam/migrations/0011_order_need_mtest.py new file mode 100644 index 0000000..81ebb27 --- /dev/null +++ b/hb_server/apps/sam/migrations/0011_order_need_mtest.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2022-01-24 05:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sam', '0010_auto_20211208_1408'), + ] + + operations = [ + migrations.AddField( + model_name='order', + name='need_mtest', + field=models.BooleanField(default=False, verbose_name='是否需要军检'), + ), + ] diff --git a/hb_server/apps/sam/models.py b/hb_server/apps/sam/models.py index 3e51a97..bceb63c 100644 --- a/hb_server/apps/sam/models.py +++ b/hb_server/apps/sam/models.py @@ -66,6 +66,7 @@ class Order(CommonAModel): planed_count = models.PositiveIntegerField('已排数量', default=0) delivered_count = models.PositiveIntegerField('已交货数量', default=0) delivery_date = models.DateField('交货日期') + need_mtest = models.BooleanField('是否需要军检', default=False) class Meta: verbose_name = '订单信息' verbose_name_plural = verbose_name diff --git a/hb_server/apps/sam/serializers.py b/hb_server/apps/sam/serializers.py index 154b1e7..81ed0df 100644 --- a/hb_server/apps/sam/serializers.py +++ b/hb_server/apps/sam/serializers.py @@ -63,7 +63,7 @@ class OrderSimpleSerializer(serializers.ModelSerializer): customer_ = CustomerSimpleSerializer(source='customer', read_only=True) class Meta: model = Order - fields = '__all__' + fields = ['id', 'number', 'contract_', 'customer_', 'need_mtest'] class SaleCreateSerializer(serializers.ModelSerializer): iproducts = serializers.PrimaryKeyRelatedField(queryset=IProduct.objects.all(), many=True) diff --git a/hb_server/apps/sam/views.py b/hb_server/apps/sam/views.py index a239d8e..357706e 100644 --- a/hb_server/apps/sam/views.py +++ b/hb_server/apps/sam/views.py @@ -136,13 +136,6 @@ class SaleViewSet(CreateUpdateCustomMixin, ListModelMixin, RetrieveModelMixin, C fifo.inout_date = timezone.now() fifo.create_by = request.user fifo.save() - # 出库条目 暂时不校验是否军检 - # spds = SaleProduct.objects.filter(sale=obj) - # for i in spds: - # if i.is_mtested and i.is_mtestok: - # pass - # else: - # raise exceptions.APIException('存在未军检产品') # 创建出库条目 ips = IProduct.objects.filter(sale_iproduct__sale=obj) items = ips.values('warehouse', 'material', 'batch').annotate(total=Count('id')) diff --git a/hb_server/apps/system/filters.py b/hb_server/apps/system/filters.py index 4199790..22ef179 100644 --- a/hb_server/apps/system/filters.py +++ b/hb_server/apps/system/filters.py @@ -1,11 +1,10 @@ from django_filters import rest_framework as filters from .models import User +from utils.mixins import DynamicFieldsFilterMixin -class UserFilter(filters.FilterSet): +class UserFilter(DynamicFieldsFilterMixin, filters.FilterSet): + name = filters.CharFilter(field_name='name', lookup_expr='contains') class Meta: model = User - fields = { - 'name': ['exact', 'contains'], - 'is_active': ['exact'], - } + fields = ['name', 'is_active'] \ No newline at end of file diff --git a/hb_server/apps/system/serializers.py b/hb_server/apps/system/serializers.py index 8d644b3..1e397c5 100644 --- a/hb_server/apps/system/serializers.py +++ b/hb_server/apps/system/serializers.py @@ -5,6 +5,7 @@ from rest_framework import serializers from .models import (Dict, DictType, File, Organization, Permission, Position, Role, User) +from utils.mixins import DynamicFieldsSerializerMixin class IntervalSerializer(serializers.ModelSerializer): class Meta: @@ -132,7 +133,7 @@ class UserSimpleSerializer(serializers.ModelSerializer): # fields = ['id', 'username', 'name', 'is_active', 'dept_name', 'dept'] -class UserListSerializer(serializers.ModelSerializer): +class UserListSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializer): """ 用户列表序列化 """ diff --git a/hb_server/apps/wpm/filters.py b/hb_server/apps/wpm/filters.py index 54454f8..c4db494 100644 --- a/hb_server/apps/wpm/filters.py +++ b/hb_server/apps/wpm/filters.py @@ -2,6 +2,7 @@ from django_filters import rest_framework as filters from apps.mtm.models import Material, Step from apps.wpm.services import WpmServies +from utils.mixins import DynamicFieldsFilterMixin from .models import Operation, OperationMaterial, OperationRecord, WMaterial, WProduct @@ -28,11 +29,15 @@ class WMaterialFilterSet(filters.FilterSet): return queryset -class WProductFilterSet(filters.FilterSet): +class WProductFilterSet(DynamicFieldsFilterMixin, filters.FilterSet): tag = filters.CharFilter(method='filter_tag') production_plan = filters.NumberFilter( field_name='subproduction_plan__production_plan') + def filter_fields(self, queryset, name, value): + return queryset + def filter_omit(self, queryset, name, value): + return queryset class Meta: model = WProduct fields = ['step', 'subproduction_plan', 'material', diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index c52a061..9027301 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -120,7 +120,6 @@ class WProduct(CommonAModel): on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_ticket') to_order = models.ForeignKey('sam.order', verbose_name='指派的订单', null=True, blank=True, on_delete = models.CASCADE) - is_mtested = models.BooleanField('是否军检', default=False) is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True) remark_mtest = models.TextField('军检备注', null=True, blank=True) last_test_result = models.BooleanField('最后一次检验结果', null=True, blank=True) @@ -194,7 +193,6 @@ class WproductFlow(CommonAModel): ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单', on_delete=models.SET_NULL, null=True, blank=True) to_order = models.ForeignKey('sam.order', verbose_name='指派的订单', null=True, blank=True, on_delete = models.CASCADE) - is_mtested = models.BooleanField('是否军检', default=False) is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True) remark_mtest = models.TextField('军检备注', null=True, blank=True) last_test_result = models.BooleanField('最后一次检验结果', null=True, blank=True) diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index 3181088..cf7ca24 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -20,6 +20,7 @@ from apps.system.serializers import UserSimpleSerializer from apps.wpm.models import Operation, OperationEquip, OperationMaterial, OperationWproduct, Pick, WMaterial, WProduct, OperationRecord, OperationRecordItem, WprouctTicket from django.db import transaction from apps.sam.models import Order +from utils.mixins import DynamicFieldsSerializerMixin class PickHalfSerializer(serializers.Serializer): id = serializers.PrimaryKeyRelatedField(queryset=SubProductionProgress.objects.all(), label='子计划进度ID') @@ -147,7 +148,7 @@ class WProductBaseSerializer(serializers.ModelSerializer): model = WProduct fields = '__all__' -class WProductListSerializer(serializers.ModelSerializer): +class WProductListSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializer): """ 半成品列表 """ @@ -157,6 +158,7 @@ class WProductListSerializer(serializers.ModelSerializer): warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True) children = serializers.SerializerMethodField() to_order_ = OrderSimpleSerializer(source='to_order', read_only=True) + order_ = serializers.SerializerMethodField() class Meta: model = WProduct fields = '__all__' @@ -166,6 +168,11 @@ class WProductListSerializer(serializers.ModelSerializer): if wps.exists(): return WProductBaseSerializer(instance=wps, many=True).data return [] + + def get_order_(self, obj): + order = Order.objects.select_related('contract', 'customer').filter( + plan_order__subplan_plan__wproduct_subplan=obj).first() + return OrderSimpleSerializer(instance=order).data class WProductCardBaseSerializer(serializers.ModelSerializer): """ @@ -224,6 +231,8 @@ class WProductDetailSerializer(serializers.ModelSerializer): subproduction_plan_ = SubproductionPlanSimpleSerializer(source='subproduction_plan', read_only=True) warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True) children = serializers.SerializerMethodField() + to_order_ = OrderSimpleSerializer(source='to_order', read_only=True) + order_ = OrderSimpleSerializer(source='subproduction_plan__production_plan__order', read_only=True) class Meta: model = WProduct fields = '__all__' @@ -231,7 +240,6 @@ class WProductDetailSerializer(serializers.ModelSerializer): def get_children(self, obj): wps = WProduct.objects.filter(child=obj) if wps.exists(): - print(wps) return WProductBaseSerializer(instance=wps, many=True).data return [] diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index e300f40..d1f9c5a 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -148,7 +148,8 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): """ perms_map = {'*': '*'} queryset = WProduct.objects.select_related('step', 'material', - 'subproduction_plan', 'warehouse', 'to_order').prefetch_related('wproduct_child') + 'subproduction_plan', 'warehouse', 'subproduction_plan__production_plan__order', + 'to_order').prefetch_related('wproduct_child') serializer_class = WProductListSerializer filterset_class = WProductFilterSet search_fields = ['number'] @@ -398,12 +399,11 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): 军检 """ obj = self.get_object() - if obj.is_mtested: + if obj.is_mtestok is None: raise exceptions.APIException('已进行军检') if obj.material.type != Material.MA_TYPE_GOOD: raise exceptions.APIException('军检必须是成品') obj.remark_mtest = request.data.get('remark_mtest', None) - obj.is_mtested = True is_mtestok = request.data.get('is_mtestok') obj.is_mtestok = is_mtestok if is_mtestok: diff --git a/hb_server/utils/mixins.py b/hb_server/utils/mixins.py new file mode 100644 index 0000000..d62062d --- /dev/null +++ b/hb_server/utils/mixins.py @@ -0,0 +1,96 @@ +""" +Mixin to dynamically select only a subset of fields per DRF resource. +""" +# import warnings +from django_filters import rest_framework as filters + +# from django.conf import settings +class DynamicFieldsFilterMixin(object): + fields = filters.CharFilter(method='filter_fields') + omit = filters.CharFilter(method='filter_omit') + def filter_fields(self, queryset, name, value): + return queryset + + def filter_omit(self, queryset, name, value): + return queryset + + @property + def fields(self): + fields = super(DynamicFieldsFilterMixin, self).fields + fields.extend(['fields', 'omit']) + return fields + +class DynamicFieldsSerializerMixin(object): + """ + A serializer mixin that takes an additional `fields` argument that controls + which fields should be displayed. + """ + + @property + def fields(self): + """ + Filters the fields according to the `fields` query parameter. + A blank `fields` parameter (?fields) will remove all fields. Not + passing `fields` will pass all fields individual fields are comma + separated (?fields=id,name,url,email). + """ + fields = super(DynamicFieldsSerializerMixin, self).fields + + if not hasattr(self, '_context'): + # We are being called before a request cycle + return fields + + # Only filter if this is the root serializer, or if the parent is the + # root serializer with many=True + is_root = self.root == self + parent_is_list_root = self.parent == self.root and getattr(self.parent, 'many', False) + if not (is_root or parent_is_list_root): + return fields + + try: + request = self.context['request'] + except KeyError: + # conf = getattr(settings, 'DRF_DYNAMIC_FIELDS', {}) + # if not conf.get('SUPPRESS_CONTEXT_WARNING', False) is True: + # warnings.warn('Context does not have access to request. ' + # 'See README for more information.') + return fields + + # NOTE: drf test framework builds a request object where the query + # parameters are found under the GET attribute. + params = getattr( + request, 'query_params', getattr(request, 'GET', None) + ) + # if params is None: + # warnings.warn('Request object does not contain query paramters') + + try: + filter_fields = params.get('fields', None).split(',') + except AttributeError: + filter_fields = None + + try: + omit_fields = params.get('omit', None).split(',') + except AttributeError: + omit_fields = [] + + # Drop any fields that are not specified in the `fields` argument. + existing = set(fields.keys()) + if filter_fields is None: + # no fields param given, don't filter. + allowed = existing + else: + allowed = set(filter(None, filter_fields)) + + # omit fields in the `omit` argument. + omitted = set(filter(None, omit_fields)) + + for field in existing: + + if field not in allowed: + fields.pop(field, None) + + if field in omitted: + fields.pop(field, None) + + return fields \ No newline at end of file