diff --git a/apps/ecm/service.py b/apps/ecm/service.py index bdf3471d..c85e5592 100644 --- a/apps/ecm/service.py +++ b/apps/ecm/service.py @@ -663,7 +663,8 @@ def get_area_from_point(x: int, y: int, floorNo: str, area_fix_id): area_temp = None area_list = cache.get('area_list', None) if not area_list: - area_list = cache_areas_info() + cache_areas_info() + area_list = cache.get('area_list', None) point = Point(x, y) for i in area_list: if floorNo == i['floor_no']: diff --git a/apps/inm/services.py b/apps/inm/services.py index d40768fe..78c5e672 100644 --- a/apps/inm/services.py +++ b/apps/inm/services.py @@ -87,7 +87,10 @@ class InmService: material = i.material warehouse = i.warehouse mb, is_created = MaterialBatch.objects.get_or_create( - material=material, warehouse=warehouse, batch=i.batch, defaults={"material": material, "warehouse": warehouse, "count": 0, "batch": i.batch} + 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 + getattr(i, field) @@ -107,8 +110,6 @@ class InmService: mb.count = mb.count - getattr(i, field) if mb.count < 0: raise ParseError("批次库存不足,操作失败") - elif mb.count == 0: - mb.delete() else: mb.save() else: diff --git a/apps/inm/views.py b/apps/inm/views.py index 6a69ba24..5e879f1c 100644 --- a/apps/inm/views.py +++ b/apps/inm/views.py @@ -45,7 +45,7 @@ class MaterialBatchViewSet(ListModelMixin, CustomGenericViewSet): 物料批次 """ perms_map = {'get': '*'} - queryset = MaterialBatch.objects.all() + queryset = MaterialBatch.objects.filter(count__gt=0) serializer_class = MaterialBatchSerializer retrieve_serializer_class = MaterialBatchDetailSerializer select_related_fields = ['warehouse', 'material'] diff --git a/apps/mtm/services.py b/apps/mtm/services.py index 3813a6ad..06cac779 100644 --- a/apps/mtm/services.py +++ b/apps/mtm/services.py @@ -73,21 +73,24 @@ def daoru_material(path: str): type_str = sheet[f'b{i}'].value.replace(' ', '') try: type = type_dict[type_str] - number = sheet[f'a{i}'].value.replace(' ', '') - name = sheet[f'c{i}'].value.replace(' ', '') + number = sheet[f'a{i}'].value.replace(' ', '') if sheet[f'a{i}'].value else None + if sheet[f'c{i}'].value: + name = sheet[f'c{i}'].value.replace(' ', '') + else: + raise ParseError(f'{i}行物料信息错误: 物料名称必填') specification = sheet[f'd{i}'].value.replace( - '×', '*').replace(' ', '') - model = sheet[f'e{i}'].value.replace(' ', '') + '×', '*').replace(' ', '') if sheet[f'd{i}'].value else None + model = sheet[f'e{i}'].value.replace(' ', '') if sheet[f'e{i}'].value else None unit = sheet[f'f{i}'].value.replace(' ', '') count_safe = sheet[f'h{i}'].value unit_price = sheet[f'i{i}'].value except Exception as e: - raise ParseError(f'{i}行物料信息错误: {str(e)}') + raise ParseError(f'{i}行物料信息错误: {e}') if type in [20, 30]: try: process = process_d[sheet[f'g{i}'].value.replace(' ', '')] except Exception as e: - raise ParseError(f'{i}行物料信息错误: {str(e)}') + raise ParseError(f'{i}行物料信息错误: {e}') try: filters = {'type': type, 'name': name, 'specification': specification, 'model': model, 'unit__iexact': unit} @@ -127,9 +130,16 @@ def mgroup_run_change(mgroup: Mgroup, new_run: bool, last_timex: datetime): last_stlog.save() cal_exp_duration_sec(last_stlog.id) # 触发时间分配 elif last_stlog.end_time and new_run is False and last_timex > last_stlog.end_time: # 从开到停 - has_same_stlog =StLog.objects.filter(mgroup=mgroup, is_shutdown=True, start_time=last_timex).exists() - if not has_same_stlog: - StLog.objects.create(title="停机", is_shutdown=True, mgroup=mgroup, end_time=None, start_time=last_timex, sflog=get_sflog(mgroup, last_timex)) + StLog.objects.get_or_create( + mgroup=mgroup, + is_shutdown=True, + start_time=last_timex, + defaults={ + 'title': '停机', + 'end_time': None, + 'sflog': get_sflog(mgroup, last_timex) + } + ) elif new_run is False: StLog.objects.create(title="停机", is_shutdown=True, mgroup=mgroup, end_time=None, start_time=last_timex, sflog=get_sflog(mgroup, last_timex)) mgroup.is_running = False diff --git a/apps/opm/services.py b/apps/opm/services.py index c22df23f..8d425ad8 100644 --- a/apps/opm/services.py +++ b/apps/opm/services.py @@ -185,7 +185,17 @@ def check_opl_audit_imgs(ticket: Ticket, transition: Transition, new_ticket_data audit_imgs = new_ticket_data.get('audit_imgs', []) if audit_imgs: opl = Opl.objects.get(ticket=ticket) - opl.audit_imgs = audit_imgs + opl.audit_imgs.set(audit_imgs) opl.save() else: - raise Exception('需提交审核照片') \ No newline at end of file + raise ParseError('需提交审核照片') + + +def check_opl_work_imgs(ticket: Ticket, transition: Transition, new_ticket_data: dict): + work_imgs = new_ticket_data.get('work_imgs', []) + if work_imgs: + opl = Opl.objects.get(ticket=ticket) + opl.work_imgs.set(work_imgs) + opl.save() + else: + raise ParseError('需提交作业开始照片') \ No newline at end of file diff --git a/apps/qm/migrations/0021_ftestwork_mb.py b/apps/qm/migrations/0021_ftestwork_mb.py new file mode 100644 index 00000000..72b4cba5 --- /dev/null +++ b/apps/qm/migrations/0021_ftestwork_mb.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.12 on 2024-08-19 03:25 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('inm', '0001_initial'), + ('qm', '0020_auto_20240814_1756'), + ] + + operations = [ + migrations.AddField( + model_name='ftestwork', + name='mb', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='inm.materialbatch', verbose_name='关联仓库'), + ), + ] diff --git a/apps/qm/models.py b/apps/qm/models.py index 55437375..e069a46d 100644 --- a/apps/qm/models.py +++ b/apps/qm/models.py @@ -108,6 +108,7 @@ class FtestWork(CommonBDModel): type = models.CharField('检验类型', max_length=20, choices=FTEST_TYPE_CHOICES, default='prod') type2 = models.PositiveSmallIntegerField('检验类型2', choices=((10, '抽检'), (20, '全检')), default=10) wm = models.ForeignKey(WMaterial, verbose_name='关联车间库存', on_delete=models.SET_NULL, null=True, blank=True) + mb = models.ForeignKey('inm.materialbatch', verbose_name='关联仓库', on_delete=models.SET_NULL, null=True, blank=True) test_date = models.DateField('检验日期') material = models.ForeignKey( Material, verbose_name='产品', on_delete=models.CASCADE) diff --git a/apps/qm/serializers.py b/apps/qm/serializers.py index 28d12c88..47e1561b 100644 --- a/apps/qm/serializers.py +++ b/apps/qm/serializers.py @@ -6,6 +6,7 @@ from rest_framework.exceptions import ValidationError from apps.system.models import Dept, Dictionary from apps.wpm.models import SfLog, WMaterial from django.db import transaction +from apps.inm.serializers import MaterialBatchDetailSerializer class TestItemSerializer(CustomModelSerializer): @@ -62,33 +63,39 @@ class QuaStatUpdateSerializer(CustomModelSerializer): class FtestWorkCreateUpdateSerializer(CustomModelSerializer): class Meta: model = FtestWork - fields = ['id', 'wm', 'type', 'type2', 'test_date', 'count', 'count_sampling', 'count_ok', 'count_notok', 'count_notok_json', 'test_user'] + fields = ['id', 'wm', 'mb', 'type', 'type2', 'test_date', 'count', 'count_sampling', 'count_ok', 'count_notok', 'count_notok_json', 'test_user'] extra_kwargs = {'test_user': {'required': True}, 'type': {'required': True}} def validate(self, attrs): type2 = attrs.get('type2', 20) if type2 == 20: # 如果是全检 attrs['count_sampling'] = attrs['count'] - if 'wm' not in attrs: - raise ValidationError('请选择车间库存') - wm:WMaterial = attrs['wm'] - if wm.state not in [WMaterial.WM_OK, WMaterial.WM_TEST]: - raise ValidationError('不支持对该物料检验') - attrs['material'] = wm.material - attrs['batch'] = wm.batch - count_notok_json = attrs.get('count_notok_json', None) - if count_notok_json is None: - raise ValidationError('不合格项不能为空') - count_notok = 0 - for k, v in count_notok_json.items(): - k_2 = k.replace('count_n_', '') - if k_2 not in NotOkOption.values: - raise ValidationError(f'不支持的不合格项{k_2}') - if isinstance(v, int) and v >= 0: - count_notok = count_notok + v - else: - raise ValidationError(f'不合格项{k_2}必须为非负整数') - attrs['count_notok'] = count_notok + if 'wm' in attrs and attrs['wm']: + attrs['mb'] = None + wm:WMaterial = attrs['wm'] + if wm.state not in [WMaterial.WM_OK, WMaterial.WM_TEST]: + raise ValidationError('不支持对该物料检验') + attrs['material'] = wm.material + attrs['batch'] = wm.batch + count_notok_json = attrs.get('count_notok_json', None) + if count_notok_json is None: + raise ValidationError('不合格项不能为空') + count_notok = 0 + for k, v in count_notok_json.items(): + k_2 = k.replace('count_n_', '') + if k_2 not in NotOkOption.values: + raise ValidationError(f'不支持的不合格项{k_2}') + if isinstance(v, int) and v >= 0: + count_notok = count_notok + v + else: + raise ValidationError(f'不合格项{k_2}必须为非负整数') + attrs['count_notok'] = count_notok + elif 'mb' in attrs and attrs['mb']: + attrs['wm'] = None + attrs['material'] = attrs['mb'].material + attrs['batch'] = attrs['mb'].batch + else: + raise ValidationError('请选择车间/仓库库存') return attrs @@ -96,6 +103,7 @@ class FtestWorkSerializer(CustomModelSerializer): material_name = serializers.StringRelatedField( source='material', read_only=True) material_cate = serializers.CharField(source='material.cate', read_only=True) + mb_ = MaterialBatchDetailSerializer(source='mb', read_only=True) class Meta: model = FtestWork diff --git a/apps/qm/views.py b/apps/qm/views.py index 9e11f534..f03aa727 100644 --- a/apps/qm/views.py +++ b/apps/qm/views.py @@ -125,7 +125,7 @@ class FtestWorkViewSet(CustomModelViewSet): serializer_class = FtestWorkSerializer create_serializer_class = FtestWorkCreateUpdateSerializer update_serializer_class = FtestWorkCreateUpdateSerializer - select_related_fields = ['material'] + select_related_fields = ['material', 'mb', 'mb__material'] filterset_class = FtestWorkFilter def update(self, request, *args, **kwargs): @@ -148,6 +148,8 @@ class FtestWorkViewSet(CustomModelViewSet): 提交检验工作 """ ins:FtestWork = self.get_object() + if ins.wm is None: + raise ParseError('该检验工作未关联车间库存') if ins.submit_time is None: ftestwork_submit(ins, request.user) else: