Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化

This commit is contained in:
mgw
2025-02-24 16:59:33 +08:00
6 changed files with 103 additions and 58 deletions

View File

@@ -17,7 +17,6 @@ from odoo.exceptions import UserError, ValidationError
from odoo.addons.sf_mrs_connect.models.ftp_operate import FtpController from odoo.addons.sf_mrs_connect.models.ftp_operate import FtpController
class ResMrpWorkOrder(models.Model): class ResMrpWorkOrder(models.Model):
_inherit = 'mrp.workorder' _inherit = 'mrp.workorder'
_order = 'sequence asc' _order = 'sequence asc'
@@ -91,7 +90,7 @@ class ResMrpWorkOrder(models.Model):
('state', '!=', 'cancel') ('state', '!=', 'cancel')
]) ])
finish_move = next((move for move in moves if move.location_dest_id.name == '制造后'), None) finish_move = next((move for move in moves if move.location_dest_id.name == '制造后'), None)
if not finish_move and not cur_workorder.is_subcontract and not cur_workorder.name =='解除装夹': if not finish_move and not cur_workorder.is_subcontract and not cur_workorder.name == '解除装夹':
record.back_button_display = True record.back_button_display = True
else: else:
record.back_button_display = any( record.back_button_display = any(
@@ -104,15 +103,19 @@ class ResMrpWorkOrder(models.Model):
record.back_button_display = True record.back_button_display = True
else: else:
record.back_button_display = False record.back_button_display = False
if cur_workorder.is_subcontract or cur_workorder.name == '解除装夹':
record.back_button_display = False
else: else:
next_workorder = sorted_workorders[position + 1] next_workorder = sorted_workorders[position + 1]
next_state = next_workorder.state next_state = next_workorder.state
if ((next_state == 'ready' and not next_workorder.is_subcontract and not next_workorder.name =='解除装夹') or (next_workorder.state == 'pending' and next_workorder.is_subcontract and not next_workorder.name =='解除装夹')) and cur_workorder.state == 'done': if (next_state == 'ready' or (
next_workorder.state == 'waiting' and next_workorder.is_subcontract)) and cur_workorder.state == 'done':
record.back_button_display = True record.back_button_display = True
else: else:
record.back_button_display = False record.back_button_display = False
if cur_workorder.is_subcontract or cur_workorder.name =='解除装夹': if cur_workorder.is_subcontract or cur_workorder.name == '解除装夹':
record.back_button_display = False record.back_button_display = False
date_planned_start = fields.Datetime(tracking=True) date_planned_start = fields.Datetime(tracking=True)
@api.depends('processing_panel') @api.depends('processing_panel')
@@ -576,6 +579,7 @@ class ResMrpWorkOrder(models.Model):
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因", tracking=True) ("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因", tracking=True)
detailed_reason = fields.Text('详细原因') detailed_reason = fields.Text('详细原因')
is_rework = fields.Boolean(string='是否返工', default=False) is_rework = fields.Boolean(string='是否返工', default=False)
# rework_flag = fields.Boolean(string='返工标志', compute='_compute_rework_flag') # rework_flag = fields.Boolean(string='返工标志', compute='_compute_rework_flag')
# #
# @api.depends('state', 'production_line_state') # @api.depends('state', 'production_line_state')
@@ -1265,6 +1269,7 @@ class ResMrpWorkOrder(models.Model):
mo.get_move_line(workorder.production_id, workorder)) mo.get_move_line(workorder.production_id, workorder))
else: else:
workorder.state = 'waiting' workorder.state = 'waiting'
# 重写工单开始按钮方法 # 重写工单开始按钮方法
def button_start(self): def button_start(self):
# 判断工单状态是否为等待组件 # 判断工单状态是否为等待组件
@@ -1431,7 +1436,8 @@ class ResMrpWorkOrder(models.Model):
'detailed_reason': record.detailed_reason, 'detailed_reason': record.detailed_reason,
'processing_panel': record.processing_panel, 'processing_panel': record.processing_panel,
'routing_type': record.routing_type, 'routing_type': record.routing_type,
'handle_result': '待处理' if record.test_results in ['返工', '报废'] or record.is_rework is True else '', 'handle_result': '待处理' if record.test_results in ['返工',
'报废'] or record.is_rework is True else '',
'test_results': record.test_results, 'test_results': record.test_results,
'test_report': record.detection_report})], 'test_report': record.detection_report})],
'is_scrap': True if record.test_results == '报废' else False 'is_scrap': True if record.test_results == '报废' else False
@@ -1448,7 +1454,8 @@ class ResMrpWorkOrder(models.Model):
raise UserError('请先完成该工单的工艺外协再进行操作') raise UserError('请先完成该工单的工艺外协再进行操作')
# 表面工艺外协,最后一张工单 # 表面工艺外协,最后一张工单
workorders = self.production_id.workorder_ids workorders = self.production_id.workorder_ids
subcontract_workorders = workorders.filtered(lambda wo: wo.is_subcontract == True and wo.state != 'cancel').sorted('sequence') subcontract_workorders = workorders.filtered(
lambda wo: wo.is_subcontract == True and wo.state != 'cancel').sorted('sequence')
if self == subcontract_workorders[-1]: if self == subcontract_workorders[-1]:
# 给下一个库存移动就绪 # 给下一个库存移动就绪
self.move_subcontract_workorder_ids[0].move_dest_ids._action_done() self.move_subcontract_workorder_ids[0].move_dest_ids._action_done()
@@ -1671,6 +1678,7 @@ class ResMrpWorkOrder(models.Model):
# 根据工单对应的【作业_个性化记录】配置页签 # 根据工单对应的【作业_个性化记录】配置页签
if any(item.code == 'PTD' for item in mw.routing_workcenter_id.individuation_page_ids): if any(item.code == 'PTD' for item in mw.routing_workcenter_id.individuation_page_ids):
mw.individuation_page_PTD = True mw.individuation_page_PTD = True
# ============================================================================================= # =============================================================================================
is_inspect = fields.Boolean('需送检', compute='_compute_is_inspect', store=True, default=False) is_inspect = fields.Boolean('需送检', compute='_compute_is_inspect', store=True, default=False)

View File

@@ -452,25 +452,14 @@ class ProductionLot(models.Model):
"""Return the next serial number to be attributed to the product.""" """Return the next serial number to be attributed to the product."""
if product.tracking == "serial": if product.tracking == "serial":
last_serial = self.env['stock.lot'].search( last_serial = self.env['stock.lot'].search(
[('company_id', '=', company.id), ('product_id', '=', product.id)], [('company_id', '=', company.id), ('product_id', '=', product.id), ('name', 'ilike', product.name)],
limit=1, order='name desc') limit=1, order='name desc')
if last_serial: move_line_id = self.env['stock.move.line'].sudo().search(
if product.categ_id.name == '刀具': [('product_id', '=', product.id), ('lot_name', 'ilike', product.name)], limit=1, order='lot_name desc')
return self.env['stock.lot'].get_tool_generate_lot_names1(company, product) if last_serial or move_line_id:
else: return self.env['stock.lot'].generate_lot_names1(product.name, last_serial.name if (
# 对last_serial的name进行检测如果不是以产品名称+数字的形式的就重新搜索 not move_line_id or
if product.name.split('[')[0] not in last_serial.name: (last_serial and last_serial.name > move_line_id.lot_name)) else move_line_id.lot_name, 2)[1]
last_serial = self.env['stock.lot'].search(
[('company_id', '=', company.id), ('product_id', '=', product.id),
('name', 'ilike', product.name.split('[')[0])],
limit=1, order='name desc')
if not last_serial:
return "%s-%03d" % (product.name, 1)
return self.env['stock.lot'].generate_lot_names1(product.name, last_serial.name, 2)[1]
now = datetime.now().strftime("%Y%m%d")
if product.cutting_tool_model_id:
split_codes = product.cutting_tool_model_id.code.split('-')
return "%s-T-%s-%s-%03d" % (split_codes[0], now, product.specification_id.name, 1)
return "%s-%03d" % (product.name, 1) return "%s-%03d" % (product.name, 1)
qr_code_image = fields.Binary(string='二维码', compute='_generate_qr_code') qr_code_image = fields.Binary(string='二维码', compute='_generate_qr_code')
@@ -724,6 +713,34 @@ class StockPicking(models.Model):
'draft', 'sent']: 'draft', 'sent']:
picking.state = 'waiting' picking.state = 'waiting'
# def write(self, vals):
#
# old_state = None
# if 'state' in vals:
# old_state = self.state
# res = super(StockPicking, self).write(vals)
# if (self.picking_type_id.use_existing_lots is False and self.picking_type_id.use_create_lots is True and
# (('move_ids_without_package' in vals and self.state == 'assigned')
# or ('state' in vals and vals['state'] == 'assigned' and old_state not in ['assigned', 'done']))):
# if self.move_ids_without_package:
# for move_id in self.move_ids_without_package:
# move_id.action_show_details()
# return res
@api.constrains('state', 'move_ids_without_package')
def _check_move_ids_without_package(self):
"""
凡库存调拨单的【作业类型】=“收料入库、客供料入库”且其产品行的【产品_库存_追溯】="按唯一序列号/按批次”的,当调拨单的【状态】=就绪时
自动生成预分配序列号
"""
for sp in self:
if (sp.picking_type_id.use_existing_lots is False and sp.picking_type_id.use_create_lots is True
and sp.state == 'assigned'):
if sp.move_ids_without_package:
for move_id in sp.move_ids_without_package:
if move_id.product_id.tracking in ['serial', 'lot'] and not move_id.move_line_nosuggest_ids:
move_id.action_show_details()
class ReStockMove(models.Model): class ReStockMove(models.Model):
_inherit = 'stock.move' _inherit = 'stock.move'
@@ -864,6 +881,7 @@ class ReStockMove(models.Model):
and self.picking_type_id.use_create_lots is True and not self.move_line_nosuggest_ids): and self.picking_type_id.use_create_lots is True and not self.move_line_nosuggest_ids):
self.action_assign_serial_show_details() self.action_assign_serial_show_details()
elif self.product_id.tracking == "lot": elif self.product_id.tracking == "lot":
if self.product_id.categ_id.name == '刀具':
self._put_tool_lot(self.company_id, self.product_id, self.origin) self._put_tool_lot(self.company_id, self.product_id, self.origin)
return { return {
@@ -889,23 +907,6 @@ class ReStockMove(models.Model):
), ),
} }
def put_move_line(self):
"""
确认订单时,自动分配序列号
"""
if self.product_id.tracking == "serial":
if self.product_id.categ_id.name == '刀具':
self.next_serial = self._get_tool_next_serial(self.company_id, self.product_id, self.origin)
else:
self.next_serial = self.env['stock.lot']._get_next_serial(self.company_id, self.product_id)
self._generate_serial_numbers()
for item in self.move_line_nosuggest_ids:
if item.lot_name:
lot_name = item.lot_name
if item.product_id.categ_id.name == '坯料':
lot_name = lot_name.split('[', 1)[0]
item.lot_qr_code = self.compute_lot_qr_code(lot_name)
def _put_tool_lot(self, company, product, origin): def _put_tool_lot(self, company, product, origin):
if product.tracking == "lot" and self.product_id.categ_id.name == '刀具': if product.tracking == "lot" and self.product_id.categ_id.name == '刀具':
if not self.move_line_nosuggest_ids: if not self.move_line_nosuggest_ids:
@@ -920,6 +921,8 @@ class ReStockMove(models.Model):
lot_code = '%s-%03d' % (lot_code, int(move_line_ids.lot_name[-3:]) + 1) lot_code = '%s-%03d' % (lot_code, int(move_line_ids.lot_name[-3:]) + 1)
lot_names = self.env['stock.lot'].generate_lot_names(lot_code, 1) lot_names = self.env['stock.lot'].generate_lot_names(lot_code, 1)
move_lines_commands = self._generate_serial_move_line_commands_tool_lot(lot_names) move_lines_commands = self._generate_serial_move_line_commands_tool_lot(lot_names)
for move_lines_command in move_lines_commands:
move_lines_command[2]['qty_done'] = self.product_uom_qty
self.write({'move_line_nosuggest_ids': move_lines_commands}) self.write({'move_line_nosuggest_ids': move_lines_commands})
for item in self.move_line_nosuggest_ids: for item in self.move_line_nosuggest_ids:
if item.lot_name: if item.lot_name:
@@ -947,10 +950,15 @@ class ReStockMove(models.Model):
last_serial = self.env['stock.lot'].search( last_serial = self.env['stock.lot'].search(
[('company_id', '=', company.id), ('product_id', '=', product.id), ('name', 'ilike', origin)], [('company_id', '=', company.id), ('product_id', '=', product.id), ('name', 'ilike', origin)],
limit=1, order='id DESC') limit=1, order='id DESC')
move_line_id = self.env['stock.move.line'].sudo().search(
[('product_id', '=', product.id), ('lot_name', 'ilike', origin)], limit=1, order='lot_name desc')
split_codes = product.cutting_tool_model_id.code.split('-') split_codes = product.cutting_tool_model_id.code.split('-')
if last_serial: if last_serial or move_line_id:
return "%s-T-%s-%s-%03d" % ( return "%s-T-%s-%s-%03d" % (
split_codes[0], origin, product.specification_id.name, int(last_serial.name[-3:]) + 1) split_codes[0], origin, product.specification_id.name,
int(last_serial.name[-3:] if (not move_line_id or
(last_serial and last_serial.name > move_line_id.lot_name))
else move_line_id.lot_name[-3:]) + 1)
else: else:
return "%s-T-%s-%s-%03d" % (split_codes[0], origin, product.specification_id.name, 1) return "%s-T-%s-%s-%03d" % (split_codes[0], origin, product.specification_id.name, 1)
@@ -1046,6 +1054,13 @@ class ReStockMove(models.Model):
subcontract_workorder_id = fields.Many2one('mrp.workorder', '外协工单组件', check_company=True, subcontract_workorder_id = fields.Many2one('mrp.workorder', '外协工单组件', check_company=True,
index='btree_not_null') index='btree_not_null')
def button_update_the_sequence_number(self):
"""
更新序列号 功能按钮
"""
self.move_line_nosuggest_ids.unlink()
return self.action_show_details()
class ReStockQuant(models.Model): class ReStockQuant(models.Model):
_inherit = 'stock.quant' _inherit = 'stock.quant'

View File

@@ -74,5 +74,30 @@
<field name="view_ids" eval="[(5, 0, 0), <field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('stock.vpicktree')})]"/> (0, 0, {'view_mode': 'tree', 'view_id': ref('stock.vpicktree')})]"/>
</record> </record>
<record id="sf_view_stock_move_operations" model="ir.ui.view">
<field name="name">sf.stock.move.operations.form</field>
<field name="model">stock.move</field>
<field name="inherit_id" ref="stock.view_stock_move_operations"/>
<field name="arch" type="xml">
<xpath expr="//form//field[@name='next_serial']" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//form//field[@name='next_serial_count']" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//form//button[@name='action_assign_serial_show_details']" position="after">
<button name="button_update_the_sequence_number" type="object" class="btn-link" data-hotkey="k" title="Assign Serial Numbers">
<span>更新序列号</span>
</button>
</xpath>
<xpath expr="//form//button[@name='action_assign_serial_show_details']" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//form//button[@name='action_clear_lines_show_details']" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
</field>
</record>
</data> </data>
</odoo> </odoo>

View File

@@ -401,12 +401,6 @@ class RePurchaseOrder(models.Model):
def button_confirm(self): def button_confirm(self):
result = super(RePurchaseOrder, self).button_confirm() result = super(RePurchaseOrder, self).button_confirm()
for item in self: for item in self:
# 确认订单时,自动分配序列号
if item.picking_ids:
for picking_id in item.picking_ids:
if picking_id.move_ids:
for move_id in picking_id.move_ids:
move_id.put_move_line()
for line in item.order_line: for line in item.order_line:
if line.product_id.categ_type == '表面工艺': if line.product_id.categ_type == '表面工艺':
if item.origin: if item.origin:

View File

@@ -956,7 +956,7 @@ class SfStockPicking(models.Model):
qc_ids = self.env['quality.check'].sudo().search( qc_ids = self.env['quality.check'].sudo().search(
[('picking_id', 'in', sp_ids), ('quality_state', 'in', ['waiting', 'none'])]) [('picking_id', 'in', sp_ids), ('quality_state', 'in', ['waiting', 'none'])])
if qc_ids: if qc_ids:
raise ValidationError(f'单据{[qc.picking_id.name for qc in qc_ids]}未完成质量检查,完成后再试。') raise ValidationError(f'单据{list(set(qc.picking_id.name for qc in qc_ids))}未完成质量检查,完成后再试。')
for record in self: for record in self:
if record.state != 'assigned': if record.state != 'assigned':
continue continue

View File

@@ -79,6 +79,9 @@
<xpath expr="//field[@name='lot_name']" position="after"> <xpath expr="//field[@name='lot_name']" position="after">
<field name="rfid"/> <field name="rfid"/>
</xpath> </xpath>
<xpath expr="//field[@name='lot_name']" position="attributes">
<attribute name="readonly">True</attribute>
</xpath>
<xpath expr="//field[@name='product_uom_id']" position="after"> <xpath expr="//field[@name='product_uom_id']" position="after">
<field name="lot_qr_code" widget="image"/> <field name="lot_qr_code" widget="image"/>
<button name="print_single_method" string="打印编码" type="object" class="oe_highlight"/> <button name="print_single_method" string="打印编码" type="object" class="oe_highlight"/>