fix: 生产提交支持单个物料

This commit is contained in:
caoqianming 2025-01-02 14:01:09 +08:00
parent 2c107d4050
commit ba41228d69
6 changed files with 63 additions and 30 deletions

View File

@ -98,7 +98,6 @@ class MIOItemwCreateSerializer(CustomModelSerializer):
fields = ["number", "note"] fields = ["number", "note"]
class MIOItemwSerializer(CustomModelSerializer): class MIOItemwSerializer(CustomModelSerializer):
test_user_name = serializers.CharField(source='test_user.name', read_only=True)
class Meta: class Meta:
model = MIOItemw model = MIOItemw
fields = "__all__" fields = "__all__"
@ -119,12 +118,12 @@ class MIOItemwTestSerializer(CustomModelSerializer):
class MIOItemCreateSerializer(CustomModelSerializer): class MIOItemCreateSerializer(CustomModelSerializer):
assemb = MIOItemACreateSerializer( assemb = MIOItemACreateSerializer(
label='组合件信息', many=True, write_only=True, required=False) label='组合件信息', many=True, write_only=True, required=False)
wprs = MIOItemwCreateSerializer(many=True, required=False) mioitemw_ = MIOItemwCreateSerializer(many=True, required=False)
class Meta: class Meta:
model = MIOItem model = MIOItem
fields = ['mio', 'warehouse', 'material', fields = ['mio', 'warehouse', 'material',
'batch', 'count', 'assemb', 'is_testok', 'wprs'] 'batch', 'count', 'assemb', 'is_testok', 'mioitemw_']
def create(self, validated_data): def create(self, validated_data):
mio = validated_data['mio'] mio = validated_data['mio']
@ -143,7 +142,7 @@ class MIOItemCreateSerializer(CustomModelSerializer):
count = validated_data["count"] count = validated_data["count"]
batch = validated_data["batch"] batch = validated_data["batch"]
assemb = validated_data.pop('assemb', []) assemb = validated_data.pop('assemb', [])
wprs = validated_data.pop('wprs', []) mioitemw_ = validated_data.pop('mioitemw_', [])
instance = super().create(validated_data) instance = super().create(validated_data)
assemb_dict = {} assemb_dict = {}
for i in assemb: for i in assemb:
@ -158,13 +157,13 @@ class MIOItemCreateSerializer(CustomModelSerializer):
else: else:
raise ParseError('缺少组合件') raise ParseError('缺少组合件')
if material.tracking == Material.MA_TRACKING_SINGLE: if material.tracking == Material.MA_TRACKING_SINGLE:
if count == 1 and len(wprs) == 0: if count == 1 and len(mioitemw_) == 0:
MIOItemw.objects.create(mioitem=instance, number=batch) MIOItemw.objects.create(mioitem=instance, number=batch)
elif count == 1 and len(wprs) >= 1: elif count == 1 and len(mioitemw_) >= 1:
MIOItemw.objects.create(mioitem=instance, number=wprs[0]['number'], note=wprs[0]['note']) MIOItemw.objects.create(mioitem=instance, number=mioitemw_[0]['number'], note=mioitemw_[0]['note'])
elif count > 1 and len(wprs) == count: elif count > 1 and len(mioitemw_) == count:
for w in wprs: for item in mioitemw_:
MIOItemw.objects.create(mioitem=instance, number=w['number'], note=w['note']) MIOItemw.objects.create(mioitem=instance, number=item['number'], note=item['note'])
else: else:
raise ParseError('单个明细信息不匹配') raise ParseError('单个明细信息不匹配')
return instance return instance
@ -191,7 +190,7 @@ class MIOItemSerializer(CustomModelSerializer):
source='material', read_only=True) source='material', read_only=True)
inout_date = serializers.DateField(source='mio.inout_date', read_only=True) inout_date = serializers.DateField(source='mio.inout_date', read_only=True)
test_user_name = serializers.CharField(source='test_user.name', read_only=True) test_user_name = serializers.CharField(source='test_user.name', read_only=True)
wprs = MIOItemwSerializer(source='w_mioitem', read_only=True, many=True) mioitemw_ = MIOItemwSerializer(source='w_mioitem', read_only=True, many=True)
class Meta: class Meta:
model = MIOItem model = MIOItem

View File

@ -26,8 +26,6 @@ class Migration(migrations.Migration):
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('number', models.TextField(verbose_name='单个编号')), ('number', models.TextField(verbose_name='单个编号')),
('test_json', models.JSONField(blank=True, default=dict, verbose_name='检验情况')),
('defect_json', models.JSONField(blank=True, default=list, verbose_name='缺陷情况')),
('note', models.TextField(blank=True, null=True, verbose_name='备注')), ('note', models.TextField(blank=True, null=True, verbose_name='备注')),
('mlogb', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.mlogb', verbose_name='生产记录')), ('mlogb', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.mlogb', verbose_name='生产记录')),
], ],

View File

@ -12,11 +12,6 @@ from django.utils.translation import gettext_lazy as _
# Create your models here. # Create your models here.
# class Good(BaseModel):
# """
# 产品状态
# """
# pass
class BatchSt(BaseModel): class BatchSt(BaseModel):
""" """
批次统计表 批次统计表
@ -102,6 +97,7 @@ class WmStateOption(models.IntegerChoices):
REPAIR = 30, _("返修") REPAIR = 30, _("返修")
TEST = 40, _("检验") TEST = 40, _("检验")
SCRAP = 50, _("报废") SCRAP = 50, _("报废")
class WMaterial(CommonBDModel): class WMaterial(CommonBDModel):
""" """
belong_dept是所在车间 belong_dept是所在车间
@ -119,10 +115,10 @@ class WMaterial(CommonBDModel):
batch = models.TextField('批次号') batch = models.TextField('批次号')
count = models.PositiveIntegerField('当前数量', default=0) count = models.PositiveIntegerField('当前数量', default=0)
count_eweight = models.FloatField('单数重量', default=0) count_eweight = models.FloatField('单数重量', default=0)
defect = models.ForeignKey('qm.defect', verbose_name='缺陷', on_delete=models.SET_NULL, null=True, blank=True)
notok_sign = models.CharField('不合格标记', max_length=10, null=True, blank=True) notok_sign = models.CharField('不合格标记', max_length=10, null=True, blank=True)
material_origin = models.ForeignKey(Material, verbose_name='原始物料', on_delete=models.SET_NULL, null=True, blank=True, related_name='wm_mo') material_origin = models.ForeignKey(Material, verbose_name='原始物料', on_delete=models.SET_NULL, null=True, blank=True, related_name='wm_mo')
count_xtest = models.PositiveIntegerField('已检数量', null=True, blank=True) count_xtest = models.PositiveIntegerField('已检数量', null=True, blank=True)
batch_ofrom = models.TextField('原料批次号', null=True, blank=True) batch_ofrom = models.TextField('原料批次号', null=True, blank=True)
material_ofrom = models.ForeignKey(Material, verbose_name='原料物料', on_delete=models.SET_NULL, null=True, blank=True, related_name='wm_mofrom') material_ofrom = models.ForeignKey(Material, verbose_name='原料物料', on_delete=models.SET_NULL, null=True, blank=True, related_name='wm_mofrom')
@ -339,6 +335,12 @@ class Mlogb(BaseModel):
count_n_qt = models.PositiveIntegerField('其他', default=0) count_n_qt = models.PositiveIntegerField('其他', default=0)
count_notok_json = models.JSONField('不合格情况', default=list, blank=True) count_notok_json = models.JSONField('不合格情况', default=list, blank=True)
class Mlogbw(BaseModel):
number = models.TextField('单个编号')
mlogb = models.ForeignKey(Mlogb, verbose_name='生产记录', on_delete=models.CASCADE)
note = models.TextField('备注', null=True, blank=True)
class Handover(CommonADModel): class Handover(CommonADModel):
""" """
交接记录 交接记录
@ -388,6 +390,11 @@ class Handoverb(BaseModel):
null=True, blank=True, related_name='handoverb_wm') null=True, blank=True, related_name='handoverb_wm')
count = models.PositiveIntegerField('送料数', default=0) count = models.PositiveIntegerField('送料数', default=0)
class Handoverbw(BaseModel):
handoverb = models.ForeignKey(Handoverb, verbose_name='关联交接记录', on_delete=models.CASCADE)
number = models.TextField('单个编号')
note = models.TextField('备注', null=True, blank=True)
class AttLog(CommonADModel): class AttLog(CommonADModel):
""" """
到岗记录 到岗记录

View File

@ -5,7 +5,8 @@ from rest_framework.exceptions import ValidationError, ParseError
from datetime import datetime from datetime import datetime
from .models import (SfLog, StLog, SfLogExp, WMaterial, Mlog, from .models import (SfLog, StLog, SfLogExp, WMaterial, Mlog,
Handover, Handoverb, Mlogb, AttLog, OtherLog, Fmlog, BatchSt) Handover, Handoverb, Mlogb, AttLog,
OtherLog, Fmlog, BatchSt, Mlogbw)
from apps.system.models import Dept, User from apps.system.models import Dept, User
from apps.system.serializers import UserSimpleSerializer from apps.system.serializers import UserSimpleSerializer
from apps.pm.models import Mtask, Mtaskb from apps.pm.models import Mtask, Mtaskb
@ -566,6 +567,12 @@ class MlogbInUpdateSerializer(CustomModelSerializer):
model = Mlogb model = Mlogb
fields = ['id', 'count_use', 'count_break', 'count_pn_jgqbl', 'note'] fields = ['id', 'count_use', 'count_break', 'count_pn_jgqbl', 'note']
class MlogbwCreateUpdateSerializer(CustomModelSerializer):
class Meta:
model = Mlogbw
fields = ["number", "note"]
class MlogbOutUpdateSerializer(CustomModelSerializer): class MlogbOutUpdateSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Mlogb model = Mlogb

View File

@ -11,13 +11,15 @@ from apps.system.models import User
from apps.pm.models import Mtask from apps.pm.models import Mtask
from apps.mtm.models import Mgroup, Shift, Material, Route, RoutePack, Team, Srule from apps.mtm.models import Mgroup, Shift, Material, Route, RoutePack, Team, Srule
from .models import SfLog, WMaterial, Mlog, Mlogb, Handover, Handoverb from .models import SfLog, WMaterial, Mlog, Mlogb, Mlogbw, Handover, Handoverb
from apps.mtm.services import cal_material_count from apps.mtm.services import cal_material_count
from apps.wf.models import Ticket from apps.wf.models import Ticket
from apps.utils.thread import MyThread from apps.utils.thread import MyThread
import logging import logging
from apps.wpm.services_2 import get_alldata_with_batch_and_store from apps.wpm.services_2 import get_alldata_with_batch_and_store
from datetime import timedelta from datetime import timedelta
from apps.wpmw.models import Wpr, WprDefect
myLogger = logging.getLogger('log') myLogger = logging.getLogger('log')
def generate_new_batch(old_batch: str, mlog: Mlog): def generate_new_batch(old_batch: str, mlog: Mlog):
@ -151,8 +153,8 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
mgroup = mlog.mgroup mgroup = mlog.mgroup
belong_dept = mgroup.belong_dept belong_dept = mgroup.belong_dept
material_out = mlog.material_out material_out: Material = mlog.material_out
material_in = mlog.material_in material_in: Material = mlog.material_in
supplier = mlog.supplier # 外协 supplier = mlog.supplier # 外协
m_ins_list = [] m_ins_list = []
if material_in: # 需要进行车间库存管理 if material_in: # 需要进行车间库存管理
@ -161,17 +163,17 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
m_ins = Mlogb.objects.filter(mlog=mlog, material_in__isnull=False) m_ins = Mlogb.objects.filter(mlog=mlog, material_in__isnull=False)
if m_ins.exists(): if m_ins.exists():
for mi in m_ins.all(): for mi in m_ins.all():
m_ins_list.append((mi.material_in, mi.batch, mi.count_use, mi.wm_in)) m_ins_list.append((mi.material_in, mi.batch, mi.count_use, mi))
if mi.count_pn_jgqbl > 0: if mi.count_pn_jgqbl > 0:
m_ins_bl_list.append((mi.material_in, mi.batch, mi.count_pn_jgqbl, mi)) m_ins_bl_list.append((mi.material_in, mi.batch, mi.count_pn_jgqbl, mi))
else: else:
m_ins_list = [(material_in, mlog.batch, mlog.count_use, mlog.wm_in)] m_ins_list = [(material_in, mlog.batch, mlog.count_use, mlog)]
for mi in m_ins_list: for mi in m_ins_list:
mi_ma, mi_batch, mi_count, mi_wm_in = mi mi_ma, mi_batch, mi_count, mlog_or_b = mi
# 需要判断领用数是否合理 # 需要判断领用数是否合理
# 优先使用工段库存 # 优先使用工段库存
if mi_wm_in: if isinstance(mlog_or_b, Mlogb) and mlog_or_b.wm_in:
wm_qs = WMaterial.objects.filter(id=mi_wm_in.id) wm_qs = WMaterial.objects.filter(id=mlog_or_b.wm_in.id)
else: else:
wm_qs = WMaterial.objects.filter(batch=mi_batch, material=mi_ma, mgroup=mgroup, state=WMaterial.WM_OK) wm_qs = WMaterial.objects.filter(batch=mi_batch, material=mi_ma, mgroup=mgroup, state=WMaterial.WM_OK)
if not wm_qs.exists(): if not wm_qs.exists():
@ -194,6 +196,12 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
wm.count = wm.count - mi_count wm.count = wm.count - mi_count
wm.update_by = user wm.update_by = user
wm.save() wm.save()
if material_in.tracking == Material.MA_TRACKING_SINGLE:
mlogbws = Mlogbw.objects.filter(mlogb=mlog_or_b)
if mlogbws.count() != mi.count:
raise ParseError("日志与明细数量不一致,操作失败")
Wpr.clear([item.number for item in mlogbws])
# 针对加工前不良的暂时额外处理 # 针对加工前不良的暂时额外处理
for item in m_ins_bl_list: for item in m_ins_bl_list:
material, batch, count_pn_jgqbl, mi_ = item material, batch, count_pn_jgqbl, mi_ = item
@ -207,6 +215,8 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
wm.material_ofrom = mi_.material_ofrom wm.material_ofrom = mi_.material_ofrom
wm.update_by = user wm.update_by = user
wm.save() wm.save()
if material_in.tracking == Material.MA_TRACKING_SINGLE:
raise ParseError("加工前不良的物料暂不支持单件")
if material_out: # 需要入车间库存 if material_out: # 需要入车间库存
@ -255,6 +265,12 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
wm.batch_ofrom = mlog_or_b.batch_ofrom wm.batch_ofrom = mlog_or_b.batch_ofrom
wm.material_ofrom = mlog_or_b.material_ofrom wm.material_ofrom = mlog_or_b.material_ofrom
wm.save() wm.save()
if material_out.tracking == Material.MA_TRACKING_SINGLE:
mlgbws = Mlogbw.objects.filter(mlogb=mlog_or_b)
if mlgbws.count() != mo_count:
raise ParseError("日志与明细数量不一致,操作失败")
for item in mlgbws:
Wpr.change_or_new(item.number, material_out, mb=None, wm=wm)
mlog.submit_time = now mlog.submit_time = now
mlog.submit_user = user mlog.submit_user = user

View File

@ -20,17 +20,23 @@ class Wpr(BaseModel):
@classmethod @classmethod
def change_or_new(cls, number, material, mb=None, wm=None, state=10): def change_or_new(cls, number, material, can_new=False, mb=None, wm=None, state=10):
ins = cls.objects.filter(number=number).first() ins = cls.objects.filter(number=number).first()
if ins: if ins:
ins.material = material ins.material = material
else: elif can_new:
ins = cls(number=number, material=material) ins = cls(number=number, material=material)
else:
raise ParseError("物料不存在")
ins.state = state ins.state = state
ins.mb = mb ins.mb = mb
ins.wm = wm ins.wm = wm
ins.save() ins.save()
@classmethod
def clear(cls, number_list):
cls.objects.filter(number__in=number_list).update(mb=None, wm=None)
class WprDefect(BaseModel): class WprDefect(BaseModel):
wpr = models.ForeignKey(Wpr, verbose_name="关联产物", on_delete=models.CASCADE) wpr = models.ForeignKey(Wpr, verbose_name="关联产物", on_delete=models.CASCADE)