feat:单个编号规则支持c_day按天占位与按天归零;并发生成加工序级advisory锁防重号;流水号宽度按规则解析并对齐排序截取;超上限报错;工序保存时校验编号规则
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
1c203325fc
commit
1584763794
|
|
@ -146,6 +146,14 @@ class ProcessSerializer(CustomModelSerializer):
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
read_only_fields = EXCLUDE_FIELDS
|
read_only_fields = EXCLUDE_FIELDS
|
||||||
|
|
||||||
|
def validate_wpr_number_rule(self, value):
|
||||||
|
if value:
|
||||||
|
try:
|
||||||
|
value.format(c_year=2026, c_year2="26", c_month=1, c_day=1, m_model="MODEL", n_count=1)
|
||||||
|
except (KeyError, IndexError, ValueError) as e:
|
||||||
|
raise ValidationError(f"编号规则不合法({e}), 可用占位符: c_year, c_year2, c_month, c_day, m_model, n_count")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
class RoutePackSerializer(CustomModelSerializer):
|
class RoutePackSerializer(CustomModelSerializer):
|
||||||
material_name = serializers.StringRelatedField(
|
material_name = serializers.StringRelatedField(
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import math
|
import math
|
||||||
|
import re
|
||||||
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
|
|
@ -1012,40 +1013,51 @@ class MlogbInViewSet(BulkCreateModelMixin, BulkUpdateModelMixin, BulkDestroyMode
|
||||||
c_year = handle_date.year
|
c_year = handle_date.year
|
||||||
c_year2 = str(c_year)[-2:]
|
c_year2 = str(c_year)[-2:]
|
||||||
c_month = handle_date.month
|
c_month = handle_date.month
|
||||||
|
c_day = handle_date.day
|
||||||
m_model = material_out.model
|
m_model = material_out.model
|
||||||
if 'm_model' in rule:
|
if 'm_model' in rule:
|
||||||
if m_model is None:
|
if m_model is None:
|
||||||
raise ParseError("生成编号出错:产品型号不能为空")
|
raise ParseError("生成编号出错:产品型号不能为空")
|
||||||
elif m_model and m_model.islower():
|
elif m_model and m_model.islower():
|
||||||
raise ParseError("生成编号出错:产品型号不能为小写")
|
raise ParseError("生成编号出错:产品型号不能为小写")
|
||||||
# 按生产日志查询
|
# 流水号宽度跟随规则中n_count的0填充宽度, 未指定默认4位
|
||||||
|
w_match = re.search(r"\{n_count:0(\d+)d\}", rule)
|
||||||
|
cq_w = int(w_match.group(1)) if w_match else 4
|
||||||
|
process = mlog.mgroup.process
|
||||||
|
# 同一工序的编号生成串行化, 防止并发取到相同的最大号; 事务结束自动释放
|
||||||
|
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:
|
||||||
|
wpr_filter["wpr_mlogbw__mlogb__mlog__handle_date__day"] = c_day
|
||||||
wpr = (
|
wpr = (
|
||||||
Wpr.objects.filter(
|
Wpr.objects.filter(**wpr_filter)
|
||||||
wpr_mlogbw__mlogb__material_out__isnull=False,
|
.annotate(last_seq=Substr("number", Length("number") - (cq_w - 1)))
|
||||||
wpr_mlogbw__mlogb__mlog__mgroup__process=mlog.mgroup.process,
|
.order_by("last_seq")
|
||||||
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,
|
|
||||||
)
|
|
||||||
.annotate(last_four=Substr("number", Length("number")-3))
|
|
||||||
.order_by("last_four")
|
|
||||||
.last()
|
.last()
|
||||||
)
|
)
|
||||||
cq_w = 4
|
|
||||||
if "n_count:02d" in rule:
|
|
||||||
cq_w = 2
|
|
||||||
n_count = 0
|
n_count = 0
|
||||||
if wpr:
|
if wpr:
|
||||||
try:
|
try:
|
||||||
n_count = int(wpr.number[-cq_w:].lstrip("0"))
|
n_count = int(wpr.number[-cq_w:])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ParseError(f"获取该类产品最后编号错误: {str(e)}")
|
raise ParseError(f"获取该类产品最后编号错误: {str(e)}")
|
||||||
|
if n_count + gen_count > 10 ** cq_w - 1:
|
||||||
|
raise ParseError(f"流水号超出{cq_w}位上限, 请调整编号规则")
|
||||||
try:
|
try:
|
||||||
if gen_count == 1:
|
if gen_count == 1:
|
||||||
return rule.format(c_year=c_year, c_month=c_month, m_model=m_model, n_count=n_count + 1, c_year2=c_year2)
|
return rule.format(c_year=c_year, c_month=c_month, c_day=c_day, m_model=m_model, n_count=n_count + 1, c_year2=c_year2)
|
||||||
else:
|
else:
|
||||||
return [rule.format(c_year=c_year, c_month=c_month, m_model=m_model, n_count=n_count + i + 1, c_year2=c_year2) for i in range(gen_count)]
|
return [rule.format(c_year=c_year, c_month=c_month, c_day=c_day, m_model=m_model, n_count=n_count + i + 1, c_year2=c_year2) for i in range(gen_count)]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ParseError(f"个号生成错误: {e}")
|
raise ParseError(f"个号生成错误: {e}")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue