Merge branch 'refs/heads/develop' into feature/customer_material_optimization

This commit is contained in:
liaodanlong
2024-12-28 08:45:25 +08:00
51 changed files with 932 additions and 243 deletions

View File

@@ -363,24 +363,18 @@ class MrpProduction(models.Model):
# if production.state == 'pending_cam':
# if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')):
# production.state = 'done'
if any(
(
wo.test_results == '返工' and wo.state == 'done' and production.programming_state in [
'已编程']) or (
wo.state == 'rework' and production.programming_state == '编程中') or (
wo.is_rework is True and wo.state == 'done' and production.programming_state in ['编程中',
'已编程'])
for wo in
production.workorder_ids):
if any((wo.test_results == '返工' and wo.state == 'done' and production.programming_state in ['已编程'])
or (wo.is_rework is True and wo.state == 'done' and production.programming_state in ['编程中', '已编程'])
for wo in production.workorder_ids):
production.state = 'rework'
if any(wo.test_results == '报废' and wo.state == 'done' for wo in production.workorder_ids):
production.state = 'scrap'
if any(dr.test_results == '报废' and dr.handle_result == '已处理' for dr in
production.detection_result_ids):
production.state = 'cancel'
# 如果制造订单的功能刀具为【无效刀】则制造订单状态改为返工
if production.tool_state == '2':
production.state = 'rework'
if production.workorder_ids and all(wo_state in ('done', 'rework', 'cancel') for wo_state in production.workorder_ids.mapped('state')):
if production.state not in ['scrap', 'rework', 'cancel']:
production.state = 'done'
# 退回调整
def technology_back_adjust(self):
@@ -809,45 +803,45 @@ class MrpProduction(models.Model):
workorder.move_subcontract_workorder_ids.picking_id.write({'state': 'cancel'})
consecutive_workorders = []
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.sequence)
for i, workorder in enumerate(sorted_workorders):
# 检查当前工作订单和下一个工作订单是否连续,并且供应商相同
if i == 0:
consecutive_workorders.append(workorder)
elif workorder.sequence == sorted_workorders[
i - 1].sequence + 1 and workorder.supplier_id.id == sorted_workorders[i - 1].supplier_id.id:
consecutive_workorders.append(workorder)
else:
# 处理连续组,如果它不为空
if consecutive_workorders:
proc_workorders.append(consecutive_workorders)
# 创建外协出入库单和采购订单
# self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production, sorted_workorders)
# self.env['purchase.order'].get_purchase_order(consecutive_workorders, production,
# product_id_to_production_names)
if i < len(sorted_workorders) - 1:
# 重置连续组,并添加当前工作订单
consecutive_workorders = [workorder]
else:
# 判断最后一笔:
if workorder.sequence == sorted_workorders[
i - 1].sequence and workorder.supplier_id.id == sorted_workorders[
i - 1].supplier_id.id:
consecutive_workorders = [workorder]
else:
proc_workorders.append([workorder])
# 立即创建外协出入库单和采购订单
# self.env['stock.picking'].create_outcontract_picking(workorder, production)
# self.env['purchase.order'].get_purchase_order(workorder, production,
# product_id_to_production_names)
consecutive_workorders = []
# 处理最后一个组,即使它可能只有一个工作订单
if consecutive_workorders:
proc_workorders.append(consecutive_workorders)
# for i, workorder in enumerate(sorted_workorders):
# # 检查当前工作订单和下一个工作订单是否连续,并且供应商相同
# if i == 0:
# consecutive_workorders.append(workorder)
# elif workorder.sequence == sorted_workorders[
# i - 1].sequence + 1 and workorder.supplier_id.id == sorted_workorders[i - 1].supplier_id.id:
# consecutive_workorders.append(workorder)
# else:
# # 处理连续组,如果它不为空
# if consecutive_workorders:
# proc_workorders.append(consecutive_workorders)
# # 创建外协出入库单和采购订单
# # self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production, sorted_workorders)
# # self.env['purchase.order'].get_purchase_order(consecutive_workorders, production,
# # product_id_to_production_names)
# if i < len(sorted_workorders) - 1:
# # 重置连续组,并添加当前工作订单
# consecutive_workorders = [workorder]
# else:
# # 判断最后一笔:
# if workorder.sequence == sorted_workorders[
# i - 1].sequence and workorder.supplier_id.id == sorted_workorders[
# i - 1].supplier_id.id:
# consecutive_workorders = [workorder]
# else:
# proc_workorders.append([workorder])
# # 立即创建外协出入库单和采购订单
# # self.env['stock.picking'].create_outcontract_picking(workorder, production)
# # self.env['purchase.order'].get_purchase_order(workorder, production,
# # product_id_to_production_names)
# consecutive_workorders = []
#
# # 处理最后一个组,即使它可能只有一个工作订单
# if consecutive_workorders:
# proc_workorders.append(consecutive_workorders)
# self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production)
# self.env['purchase.order'].get_purchase_order(consecutive_workorders, production,
# product_id_to_production_names)
for workorders in reversed(proc_workorders):
for workorders in reversed(sorted_workorders):
self.env['stock.picking'].create_outcontract_picking(workorders, production, sorted_workorders)
self.env['purchase.order'].get_purchase_order(workorders, production, product_id_to_production_names)

View File

@@ -31,3 +31,13 @@ class PurchaseOrder(models.Model):
class PurchaseOrderLine(models.Model):
_inherit = 'purchase.order.line'
part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True)
related_product = fields.Many2one('product.product',compute='_compute_related_product', string='关联产品',help='经此产品工艺加工成的成品')
@api.depends('order_id.origin')
def _compute_related_product(self):
for record in self:
if record.product_id.detailed_type:
production_id = self.env['mrp.production'].search([('name', '=', record.order_id.origin)])
record.related_product = production_id.product_id if production_id else False
else:
record.related_product = False

View File

@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
from collections import Counter
from odoo import fields, models, api, _
from odoo.exceptions import ValidationError
@@ -6,7 +8,7 @@ from odoo.exceptions import ValidationError
class sf_technology_design(models.Model):
_name = 'sf.technology.design'
_description = "工艺设计"
group_uniq_id = fields.Integer('同一制造订单唯一id',default=0)
sequence = fields.Integer('序号')
route_id = fields.Many2one('mrp.routing.workcenter', '工序')
process_parameters_id = fields.Many2one('sf.production.process.parameter', string='表面工艺参数')
@@ -17,6 +19,11 @@ class sf_technology_design(models.Model):
is_auto = fields.Boolean('是否自动生成', default=False)
active = fields.Boolean('有效', default=True)
# @api.depends('production_id')
# def _compute_group_uniq_id(self):
# for record in self:
def json_technology_design_str(self, k, route, i, process_parameter):
workorders_values_str = [0, '', {
'route_id': route.id if route.routing_type in ['表面工艺'] else route.route_workcenter_id.id,
@@ -28,6 +35,9 @@ class sf_technology_design(models.Model):
'is_auto': True}]
return workorders_values_str
def write(self, vals):
print('qwfojkqwfkio')
return super(sf_technology_design, self).write(vals)
def unlink_technology_design(self):
self.active = False
@@ -37,4 +47,64 @@ class sf_technology_design(models.Model):
for vals in vals_list:
if not vals.get('route_id'):
raise ValidationError(_("工序不能为空"))
return super(sf_technology_design, self).create(vals_list)
result = super(sf_technology_design, self).create(vals_list)
for res in result:
record = self.search([('production_id', '=', res.production_id.id), ('active', 'in', [True, False])], order='group_uniq_id desc', limit=1)
res.group_uniq_id=record.group_uniq_id + 1
return result
def get_duplicates_with_inactive(self,technology_designs):
# 统计每个 'sequence' 出现的次数
sequence_count = Counter(technology_design.sequence for technology_design in technology_designs)
# 筛选出 'sequence' 重复且 'active' 为 False 的元素
result = [
technology_design for technology_design in technology_designs
if sequence_count[technology_design.sequence] > 1 and technology_design.active is False
]
return result
# def rearrange_numbering(self,self_technology_designs):
# inactive_designs = self.get_duplicates_with_inactive(self_technology_designs)
# if inactive_designs:
# max_design = max(self_technology_designs, key=lambda x: x.sequence)
# max_sequence = max_design.sequence if max_design else 0
# for designs in inactive_designs:
# max_sequence += 1
# designs.sequence = max_sequence
# self_technology_designs.sorted(key=lambda techology_design:techology_design.sequence)
# return self_technology_designs
def get_technology_design(self):
return {
'sequence':self.sequence,
'route_id': self.route_id.id,
'process_parameters_id': self.process_parameters_id.id,
'panel': self.panel,
'routing_tag': self.routing_tag,
'time_cycle_manual': self.time_cycle_manual,
'is_auto': self.is_auto,
'active': self.active,
'group_uniq_id':self.group_uniq_id,
}
def sync_technology_designs(self,production_technology_designs, self_technology_designs):
production_id = production_technology_designs[0].production_id.id
self_technology_design_dict = {item.group_uniq_id:item for item in self_technology_designs}
production_technology_designs_dict = {item.group_uniq_id:item for item in production_technology_designs}
for technology_design in production_technology_designs:
if not self_technology_design_dict.get(technology_design.group_uniq_id):
technology_design.write({'production_id': False})
else:
technology_design.write(self_technology_design_dict.get(technology_design.group_uniq_id).get_technology_design())
for technology_design in self_technology_designs:
if not production_technology_designs_dict.get(technology_design.group_uniq_id):
technology_design = technology_design.get_technology_design()
technology_design.update({'production_id': production_id})
technology_design.pop('group_uniq_id')
self.env['sf.technology.design'].create(technology_design)
def unified_procedure_multiple_work_orders(self,self_technology_designs,production_item):
technology_designs = self.env['sf.technology.design'].sudo().search(
[('production_id', '=', production_item.id), ('active', 'in', [True, False])])
self.sync_technology_designs(self_technology_designs=self_technology_designs,production_technology_designs=technology_designs)

View File

@@ -684,14 +684,13 @@ class StockPicking(models.Model):
'partner_id': self.partner_id.id,
})
move_dest_id = False
# 如果当前工单是是制造订单的最后一个工单
if workorder == item.workorder_ids[-1]:
# 如果当前工单是是制造订单的最后一个工艺外协工
if workorder == next((workorder for workorder in reversed(item.workorder_ids) if workorder.is_subcontract), None):
move_dest_id = item.move_raw_ids[0].id
else:
# 从sorted_workorders中找到上一工单的move
if sorted_workorders.index(workorder) > 0:
move_dest_id = \
sorted_workorders[sorted_workorders.index(workorder) - 1].move_subcontract_workorder_ids[1].id
if len(sorted_workorders) > 1:
move_dest_id = sorted_workorders[sorted_workorders.index(workorder)+1].move_subcontract_workorder_ids[1].id
new_picking = True
outcontract_picking_type_in = self.env.ref(
'sf_manufacturing.outcontract_picking_in').id,

View File

@@ -131,11 +131,6 @@
<field name="deadline_of_delivery" readonly="1"/>
<field name="tool_state_remark2" invisible="1"/>
</xpath>
<xpath expr="//header//button[@name='action_cancel']" position="replace">
<button name="action_cancel" type="object" string="取消" data-hotkey="z"
attrs="{'invisible': ['|', '|', ('id', '=', False), ('state', 'in', ('done', 'cancel')), ('confirm_cancel', '=', True)]}"
groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="(//header//button[@name='button_mark_done'])[1]" position="replace">
<button name="button_mark_done"
attrs="{'invisible': ['|', '|', ('state', 'in', ('draft', 'cancel', 'done', 'to_close')), ('qty_producing', '=', 0), ('move_raw_ids', '!=', [])]}"
@@ -260,13 +255,6 @@
type="object" groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="//header//button[@name='action_cancel']" position="replace">
<button name="action_cancel" type="object" string="Cancel" data-hotkey="z"
attrs="{'invisible': ['|', '|', ('id', '=', False), ('state', 'in', ('done', 'cancel')), ('confirm_cancel', '=', False)]}"
confirm="Some product moves have already been confirmed, this manufacturing order can't be completely cancelled. Are you still sure you want to process ?"
groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="//header//button[@name='button_unbuild']" position="replace">
<button name="button_unbuild" type="object" string="拆单"
attrs="{'invisible': [('state', '!=', 'done')]}" data-hotkey="shift+v"
@@ -310,12 +298,14 @@
type="object" groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="//header//button[@name='action_cancel']" position="replace">
<xpath expr="//header//button[@name='action_cancel'][2]" position="replace">
<button name="action_cancel" type="object" string="取消" data-hotkey="z"
attrs="{'invisible': ['|', '|', ('id', '=', False), ('state', 'in', ('done', 'cancel')), ('confirm_cancel', '=', False)]}"
attrs="{'invisible': ['|', '|', ('id', '=', False), ('state', 'in', ('done', 'rework', 'cancel')), ('confirm_cancel', '=', False)]}"
confirm="Some product moves have already been confirmed, this manufacturing order can't be completely cancelled. Are you still sure you want to process ?"
groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="//header//button[@name='action_cancel'][1]" position="replace"></xpath>
<xpath expr="//header//button[@name='button_unbuild']" position="replace">
<button name="button_unbuild" type="object" string="拆单"
attrs="{'invisible': [('state', '!=', 'done')]}" data-hotkey="shift+v"

View File

@@ -174,8 +174,6 @@
<!-- attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>-->
<button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"
attrs="{'invisible': [('state', '!=', 'ready')]}"/>
<button name="button_start" type="object" string="开始" class="btn-success"
attrs="{'invisible': [('state', '!=', 'ready')]}"/>
<button name="button_pending" type="object" string="暂停" class="btn-warning"
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>
<button name="button_finish" type="object" string="完成" class="btn-success" confirm="是否确认完工"

View File

@@ -7,6 +7,7 @@
<field name="inherit_id" ref="purchase.purchase_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='order_line']/tree/field[@name='name']" position="after">
<field name="related_product" optional="show"/>
<field name="part_number" optional="show"/>
</xpath>
</field>

View File

@@ -10,7 +10,7 @@ class ProductionTechnologyReAdjustWizard(models.TransientModel):
production_id = fields.Many2one('mrp.production', string='制造订单号')
origin = fields.Char(string='源单据')
is_technology_re_adjust = fields.Boolean(default=False)
is_technology_re_adjust = fields.Boolean(default=True)
def confirm(self):
if self.is_technology_re_adjust is True:
@@ -24,38 +24,39 @@ class ProductionTechnologyReAdjustWizard(models.TransientModel):
for production_item in productions:
# 该制造订单的其他同一销售订单的制造订单的工艺设计处理
if production_item != self.production_id:
for td_other in production_item.technology_design_ids:
if td_other.is_auto is False:
td_del = technology_designs.filtered(lambda tdo: tdo.route_id.id == td_other.route_id.id)
if not td_del or td_del.active is False:
td_other.write({'active': False})
for td_main in technology_designs:
route_other = production_item.technology_design_ids.filtered(
lambda td: td.route_id.id == td_main.route_id.id)
if not route_other and td_main.active is True:
production_item.write({'technology_design_ids': [(0, 0, {
'route_id': td_main.route_id.id,
'process_parameters_id': False if td_main.process_parameters_id is False else
self.env[
'sf.production.process.parameter'].search(
[('id', '=', td_main.process_parameters_id.id)]).id,
'sequence': td_main.sequence,
'is_auto': td_main.is_auto})]})
else:
for ro in route_other:
domain = [('production_id', '=', self.production_id.id),
('active', 'in', [True, False]),
('route_id', '=', ro.route_id.id)]
if ro.route_id.routing_type == '表面工艺':
domain += [('process_parameters_id', '=', ro.process_parameters_id.id)]
elif ro.route_id.routing_tag == 'special' and ro.is_auto is False:
# display_name = ro.route_id.display_name
domain += [('id', '=', ro.id)]
elif ro.panel is not False:
domain += [('panel', '=', ro.panel)]
td_upd = self.env['sf.technology.design'].sudo().search(domain)
if td_upd:
ro.write({'sequence': td_upd.sequence, 'active': td_upd.active})
self.env['sf.technology.design'].sudo().unified_procedure_multiple_work_orders(technology_designs, production_item)
# for td_other in production_item.technology_design_ids:
# # if td_other.is_auto is False:
# # td_del = technology_designs.filtered(lambda tdo: tdo.route_id.id == td_other.route_id.id)
# # if not td_del or td_del.active is False:
# # td_other.write({'active': False})
# for td_main in technology_designs:
# route_other = production_item.technology_design_ids.filtered(
# lambda td: td.route_id.id == td_main.route_id.id)
# if not route_other and td_main.active is True:
# production_item.write({'technology_design_ids': [(0, 0, {
# 'route_id': td_main.route_id.id,
# 'process_parameters_id': False if td_main.process_parameters_id is False else
# self.env[
# 'sf.production.process.parameter'].search(
# [('id', '=', td_main.process_parameters_id.id)]).id,
# 'sequence': td_main.sequence,
# 'is_auto': td_main.is_auto})]})
# else:
# for ro in route_other:
# domain = [('production_id', '=', self.production_id.id),
# ('active', 'in', [True, False]),
# ('route_id', '=', ro.route_id.id)]
# if ro.route_id.routing_type == '表面工艺':
# domain += [('process_parameters_id', '=', ro.process_parameters_id.id)]
# elif ro.route_id.routing_tag == 'special' and ro.is_auto is False:
# # display_name = ro.route_id.display_name
# domain += [('id', '=', ro.id)]
# elif ro.panel is not False:
# domain += [('panel', '=', ro.panel)]
# td_upd = self.env['sf.technology.design'].sudo().search(domain)
# if td_upd:
# ro.write({'sequence': td_upd.sequence, 'active': td_upd.active})
special_design = self.env['sf.technology.design'].sudo().search(
[('routing_tag', '=', 'special'), ('production_id', '=', production_item.id),
('is_auto', '=', False), ('active', 'in', [True, False])])
@@ -124,3 +125,4 @@ class ProductionTechnologyReAdjustWizard(models.TransientModel):
if workorders[
0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程':
workorders[0].state = 'waiting'

View File

@@ -11,7 +11,7 @@ class ProductionTechnologyWizard(models.TransientModel):
production_id = fields.Many2one('mrp.production', string='制造订单号')
origin = fields.Char(string='源单据')
is_technology_confirm = fields.Boolean(default=False)
is_technology_confirm = fields.Boolean(default=True)
def confirm(self):
if self.is_technology_confirm is True and self.production_id.product_id.categ_id.type in ['成品', '坯料']:
@@ -19,40 +19,44 @@ class ProductionTechnologyWizard(models.TransientModel):
('product_id', '=', self.production_id.product_id.id)]
else:
domain = [('id', '=', self.production_id.id)]
technology_designs = self.production_id.technology_design_ids
technology_designs = self.env['sf.technology.design'].sudo().search(
[('production_id', '=', self.production_id.id), ('active', 'in', [True, False])])
# technology_designs = self.production_id.technology_design_ids
productions = self.env['mrp.production'].search(domain)
for production in productions:
if production != self.production_id:
for td_other in production.technology_design_ids:
if td_other.is_auto is False:
td_del = technology_designs.filtered(lambda tdo: tdo.route_id.id == td_other.route_id.id)
if not td_del or td_del.active is False:
td_other.write({'active': False})
for td_main in technology_designs:
route_other = production.technology_design_ids.filtered(
lambda td: td.route_id.id == td_main.route_id.id)
if not route_other and td_main.active is True:
production.write({'technology_design_ids': [(0, 0, {
'route_id': td_main.route_id.id,
'process_parameters_id': False if td_main.process_parameters_id is False else self.env[
'sf.production.process.parameter'].search(
[('id', '=', td_main.process_parameters_id.id)]).id,
'sequence': td_main.sequence})]})
else:
for ro in route_other:
domain = [('production_id', '=', self.production_id.id),
('active', 'in', [True, False]),
('route_id', '=', ro.route_id.id)]
if ro.route_id.routing_type == '表面工艺':
domain += [('process_parameters_id', '=', ro.process_parameters_id.id)]
elif ro.route_id.routing_tag == 'special' and ro.is_auto is False:
# display_name = ro.route_id.display_name
domain += [('id', '=', ro.id)]
elif ro.panel is not False:
domain += [('panel', '=', ro.panel)]
td_upd = self.env['sf.technology.design'].sudo().search(domain)
if td_upd:
ro.write({'sequence': td_upd.sequence, 'active': td_upd.active})
self.env['sf.technology.design'].sudo().unified_procedure_multiple_work_orders(technology_designs,
production)
# for td_other in production.technology_design_ids:
# if td_other.is_auto is False:
# td_del = technology_designs.filtered(lambda tdo: tdo.route_id.id == td_other.route_id.id)
# if not td_del or td_del.active is False:
# td_other.write({'active': False})
# for td_main in technology_designs:
# route_other = production.technology_design_ids.filtered(
# lambda td: td.route_id.id == td_main.route_id.id)
# if not route_other and td_main.active is True:
# production.write({'technology_design_ids': [(0, 0, {
# 'route_id': td_main.route_id.id,
# 'process_parameters_id': False if td_main.process_parameters_id is False else self.env[
# 'sf.production.process.parameter'].search(
# [('id', '=', td_main.process_parameters_id.id)]).id,
# 'sequence': td_main.sequence})]})
# else:
# for ro in route_other:
# domain = [('production_id', '=', self.production_id.id),
# ('active', 'in', [True, False]),
# ('route_id', '=', ro.route_id.id)]
# if ro.route_id.routing_type == '表面工艺':
# domain += [('process_parameters_id', '=', ro.process_parameters_id.id)]
# elif ro.route_id.routing_tag == 'special' and ro.is_auto is False:
# # display_name = ro.route_id.display_name
# domain += [('id', '=', ro.id)]
# elif ro.panel is not False:
# domain += [('panel', '=', ro.panel)]
# td_upd = self.env['sf.technology.design'].sudo().search(domain)
# if td_upd:
# ro.write({'sequence': td_upd.sequence, 'active': td_upd.active})
special_design = self.env['sf.technology.design'].sudo().search(
[('routing_tag', '=', 'special'), ('production_id', '=', production.id),
('is_auto', '=', False), ('active', 'in', [True, False])])

View File

@@ -287,6 +287,8 @@ class ReworkWizard(models.TransientModel):
self.production_id.update_programming_state()
self.production_id.write(
{'programming_state': '编程中', 'work_state': '编程中', 'state': 'progress'})
# ================= 返工完成,制造订单状态置为加工中 ==============
self.production_id.write({'state': 'progress', 'is_rework': False})
@api.onchange('production_id')
def onchange_processing_panel_id(self):