From ddb3cc6f3f7296f115cd6b15226c9bbda5f864c9 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 4 Aug 2026 13:42:59 +0800 Subject: [PATCH] fix(wpm): derive number date filters from rule --- apps/wpm/tests/test_number_rule.py | 64 ++++++++++++++++++++++++++++++ apps/wpm/views.py | 18 ++++++--- 2 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 apps/wpm/tests/test_number_rule.py diff --git a/apps/wpm/tests/test_number_rule.py b/apps/wpm/tests/test_number_rule.py new file mode 100644 index 00000000..ee8e7d5e --- /dev/null +++ b/apps/wpm/tests/test_number_rule.py @@ -0,0 +1,64 @@ +from datetime import date +from types import SimpleNamespace +from unittest.mock import MagicMock, patch + +from django.test import SimpleTestCase + +from apps.wpm.views import MlogbInViewSet + + +class GenNumberWithRuleFilterTests(SimpleTestCase): + def test_date_filters_follow_rule_placeholders(self): + cases = [ + ("SN-{n_count:04d}", {}), + ("{c_year}-{n_count:04d}", {"year": 2026}), + ("{c_year2}-{n_count:04d}", {"year": 2026}), + ("{c_month:02d}-{n_count:04d}", {"month": 8}), + ("{c_day:02d}-{n_count:04d}", {"day": 4}), + ( + "{c_year}{c_month:02d}{c_day:02d}-{n_count:04d}", + {"year": 2026, "month": 8, "day": 4}, + ), + ] + material = SimpleNamespace(model=None) + mlog = SimpleNamespace( + handle_date=date(2026, 8, 4), + mgroup=SimpleNamespace(process=SimpleNamespace(id=123)), + ) + + for rule, expected_dates in cases: + with self.subTest(rule=rule): + queryset = MagicMock() + queryset.annotate.return_value = queryset + queryset.order_by.return_value = queryset + queryset.last.return_value = None + + with patch("apps.wpmw.models.Wpr.objects.filter", return_value=queryset) as mock_filter: + MlogbInViewSet.gen_number_with_rule(rule, material, mlog) + + filters = mock_filter.call_args.kwargs + date_prefix = "wpr_mlogbw__mlogb__mlog__handle_date__" + actual_dates = { + key.removeprefix(date_prefix): value + for key, value in filters.items() + if key.startswith(date_prefix) + } + self.assertEqual(actual_dates, expected_dates) + + def test_escaped_date_placeholder_text_does_not_add_filter(self): + queryset = MagicMock() + queryset.annotate.return_value = queryset + queryset.order_by.return_value = queryset + queryset.last.return_value = None + material = SimpleNamespace(model=None) + mlog = SimpleNamespace( + handle_date=date(2026, 8, 4), + mgroup=SimpleNamespace(process=SimpleNamespace(id=123)), + ) + + with patch("apps.wpmw.models.Wpr.objects.filter", return_value=queryset) as mock_filter: + MlogbInViewSet.gen_number_with_rule("{{c_year}}-{n_count:04d}", material, mlog) + + self.assertFalse( + any("handle_date" in key for key in mock_filter.call_args.kwargs) + ) diff --git a/apps/wpm/views.py b/apps/wpm/views.py index d7f01582..60e747ed 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -1,5 +1,6 @@ import math import re +from string import Formatter from django.db import transaction from rest_framework.decorators import action @@ -1010,13 +1011,18 @@ class MlogbInViewSet(BulkCreateModelMixin, BulkUpdateModelMixin, BulkDestroyMode def gen_number_with_rule(cls, rule, material_out: Material, mlog: Mlog, gen_count=1): from apps.wpmw.models import Wpr + rule_fields = { + field_name + for _, field_name, _, _ in Formatter().parse(rule) + if field_name + } handle_date = mlog.handle_date c_year = handle_date.year c_year2 = str(c_year)[-2:] c_month = handle_date.month c_day = handle_date.day m_model = material_out.model - if 'm_model' in rule: + if "m_model" in rule_fields: if m_model is None: raise ParseError("生成编号出错:产品型号不能为空") elif m_model and m_model.islower(): @@ -1029,16 +1035,18 @@ class MlogbInViewSet(BulkCreateModelMixin, BulkUpdateModelMixin, BulkDestroyMode if connection.vendor == "postgresql" and connection.in_atomic_block: with connection.cursor() as cursor: cursor.execute("SELECT pg_advisory_xact_lock(hashtextextended(%s, 0))", [f"wpr_number_rule:{process.id}"]) - # 按生产日志查询, 流水号归零周期跟随规则中最细的日期占位符 + # 只按规则中实际使用的日期占位符筛选历史编号 wpr_filter = { "wpr_mlogbw__mlogb__material_out__isnull": False, "wpr_mlogbw__mlogb__mlog__mgroup__process": process, "wpr_mlogbw__mlogb__mlog__is_fix": False, "wpr_mlogbw__mlogb__mlog__submit_time__isnull": False, - "wpr_mlogbw__mlogb__mlog__handle_date__year": c_year, - "wpr_mlogbw__mlogb__mlog__handle_date__month": c_month, } - if "c_day" in rule: + if rule_fields & {"c_year", "c_year2"}: + wpr_filter["wpr_mlogbw__mlogb__mlog__handle_date__year"] = c_year + if "c_month" in rule_fields: + wpr_filter["wpr_mlogbw__mlogb__mlog__handle_date__month"] = c_month + if "c_day" in rule_fields: wpr_filter["wpr_mlogbw__mlogb__mlog__handle_date__day"] = c_day wpr = ( Wpr.objects.filter(**wpr_filter)