销售订单与制造订单关联
This commit is contained in:
@@ -8,6 +8,9 @@ import re
|
|||||||
import requests
|
import requests
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
from collections import defaultdict, namedtuple
|
from collections import defaultdict, namedtuple
|
||||||
|
|
||||||
|
from loguru import _logger
|
||||||
|
|
||||||
from odoo import api, fields, models, SUPERUSER_ID, _
|
from odoo import api, fields, models, SUPERUSER_ID, _
|
||||||
from odoo.exceptions import UserError, ValidationError
|
from odoo.exceptions import UserError, ValidationError
|
||||||
from odoo.addons.sf_base.commons.common import Common
|
from odoo.addons.sf_base.commons.common import Common
|
||||||
@@ -18,6 +21,7 @@ class MrpProduction(models.Model):
|
|||||||
_inherit = 'mrp.production'
|
_inherit = 'mrp.production'
|
||||||
_description = "制造订单"
|
_description = "制造订单"
|
||||||
_order = 'create_date desc'
|
_order = 'create_date desc'
|
||||||
|
sale_order_id = fields.Many2one('sale.order',string='销售订单', compute='_compute_sale_order_id', store=True)
|
||||||
deadline_of_delivery = fields.Date('订单交期', tracking=True, compute='_compute_deadline_of_delivery')
|
deadline_of_delivery = fields.Date('订单交期', tracking=True, compute='_compute_deadline_of_delivery')
|
||||||
# tray_ids = fields.One2many('sf.tray', 'production_id', string="托盘")
|
# tray_ids = fields.One2many('sf.tray', 'production_id', string="托盘")
|
||||||
maintenance_count = fields.Integer(compute='_compute_maintenance_count', string="Number of maintenance requests")
|
maintenance_count = fields.Integer(compute='_compute_maintenance_count', string="Number of maintenance requests")
|
||||||
@@ -34,6 +38,28 @@ class MrpProduction(models.Model):
|
|||||||
tool_state_remark = fields.Text(string='功能刀具状态备注(缺刀)', compute='_compute_tool_state_remark', store=True)
|
tool_state_remark = fields.Text(string='功能刀具状态备注(缺刀)', compute='_compute_tool_state_remark', store=True)
|
||||||
tool_state_remark2 = fields.Text(string='功能刀具状态备注(无效刀)', readonly=True)
|
tool_state_remark2 = fields.Text(string='功能刀具状态备注(无效刀)', readonly=True)
|
||||||
|
|
||||||
|
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
|
||||||
|
def _compute_sale_order_id(self):
|
||||||
|
for production in self:
|
||||||
|
# 初始化 sale_order_id 为 False
|
||||||
|
sale_order_id = False
|
||||||
|
# 使用正则表达式查找产品名称中的 'S' 开头的字母数字字符串
|
||||||
|
match = re.search(r'S\d+', production.product_id.with_context(lang='zh_CN').name) # 从字符串开始匹配
|
||||||
|
|
||||||
|
if match:
|
||||||
|
result = match.group(0)
|
||||||
|
try:
|
||||||
|
# 查找与匹配的字符串相符的销售订单
|
||||||
|
sale_order = self.env['sale.order'].search(
|
||||||
|
[('name', '=', result)], limit=1, order='id asc'
|
||||||
|
)
|
||||||
|
if sale_order:
|
||||||
|
production.sale_order_id = sale_order.id
|
||||||
|
else:
|
||||||
|
_logger.warning("No sale order found for production %s with product %s (name match: %s)",
|
||||||
|
production.id, production.product_id.name, result)
|
||||||
|
except Exception as e:
|
||||||
|
_logger.error("Error while fetching sale order for production %s: %s", production.id, str(e))
|
||||||
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
|
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
|
||||||
def _compute_deadline_of_delivery(self):
|
def _compute_deadline_of_delivery(self):
|
||||||
for production in self:
|
for production in self:
|
||||||
@@ -309,7 +335,8 @@ class MrpProduction(models.Model):
|
|||||||
for move in production.move_raw_ids if move.product_id):
|
for move in production.move_raw_ids if move.product_id):
|
||||||
production.state = 'progress'
|
production.state = 'progress'
|
||||||
# 新添加的状态逻辑
|
# 新添加的状态逻辑
|
||||||
if production.state in ['to_close', 'progress', 'technology_to_confirmed'] and production.schedule_state == '未排':
|
if production.state in ['to_close', 'progress',
|
||||||
|
'technology_to_confirmed'] and production.schedule_state == '未排':
|
||||||
if not production.workorder_ids or production.is_adjust is True:
|
if not production.workorder_ids or production.is_adjust is True:
|
||||||
production.state = 'technology_to_confirmed'
|
production.state = 'technology_to_confirmed'
|
||||||
else:
|
else:
|
||||||
@@ -320,11 +347,7 @@ class MrpProduction(models.Model):
|
|||||||
elif production.state == 'pending_cam' and production.schedule_state == '未排':
|
elif production.state == 'pending_cam' and production.schedule_state == '未排':
|
||||||
production.state = 'confirmed'
|
production.state = 'confirmed'
|
||||||
elif production.state == 'to_close' and production.schedule_state == '已排':
|
elif production.state == 'to_close' and production.schedule_state == '已排':
|
||||||
if all(
|
production.state = 'pending_cam'
|
||||||
wo_state in ('done', 'cancel') for wo_state in production.workorder_ids.mapped('state')):
|
|
||||||
production.state = 'done'
|
|
||||||
else:
|
|
||||||
production.state = 'pending_cam'
|
|
||||||
elif production.state == 'confirmed' and production.is_adjust is True:
|
elif production.state == 'confirmed' and production.is_adjust is True:
|
||||||
production.state = 'technology_to_confirmed'
|
production.state = 'technology_to_confirmed'
|
||||||
if production.state == 'confirmed' and production.schedule_state == '已排':
|
if production.state == 'confirmed' and production.schedule_state == '已排':
|
||||||
@@ -335,6 +358,9 @@ class MrpProduction(models.Model):
|
|||||||
production.state = 'pending_cam'
|
production.state = 'pending_cam'
|
||||||
if production.is_rework is True:
|
if production.is_rework is True:
|
||||||
production.state = 'rework'
|
production.state = 'rework'
|
||||||
|
if (production.state == 'rework' and production.tool_state == '0'
|
||||||
|
and production.schedule_state == '已排' and production.is_rework is False):
|
||||||
|
production.state = 'pending_cam'
|
||||||
# if production.state == 'pending_cam':
|
# if production.state == 'pending_cam':
|
||||||
# if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')):
|
# if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')):
|
||||||
# production.state = 'done'
|
# production.state = 'done'
|
||||||
@@ -721,10 +747,16 @@ class MrpProduction(models.Model):
|
|||||||
for product_id, pd in grouped_product_ids.items():
|
for product_id, pd in grouped_product_ids.items():
|
||||||
product_id_to_production_names[product_id] = [p.name for p in pd]
|
product_id_to_production_names[product_id] = [p.name for p in pd]
|
||||||
for production in production_all:
|
for production in production_all:
|
||||||
|
proc_workorders = []
|
||||||
process_parameter_workorder = self.env['mrp.workorder'].search(
|
process_parameter_workorder = self.env['mrp.workorder'].search(
|
||||||
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id),
|
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id),
|
||||||
('is_subcontract', '=', True)], order='sequence asc')
|
('is_subcontract', '=', True), ('state', '!=', 'cancel')], order='sequence asc')
|
||||||
if process_parameter_workorder:
|
if process_parameter_workorder:
|
||||||
|
# 将这些特殊表面工艺工单的采购单与调拨单置为失效
|
||||||
|
for workorder in process_parameter_workorder:
|
||||||
|
workorder._get_surface_technics_purchase_ids().write({'state': 'cancel'})
|
||||||
|
workorder.move_subcontract_workorder_ids.write({'state': 'cancel'})
|
||||||
|
workorder.move_subcontract_workorder_ids.picking_id.write({'state': 'cancel'})
|
||||||
consecutive_workorders = []
|
consecutive_workorders = []
|
||||||
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.sequence)
|
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.sequence)
|
||||||
for i, workorder in enumerate(sorted_workorders):
|
for i, workorder in enumerate(sorted_workorders):
|
||||||
@@ -737,10 +769,11 @@ class MrpProduction(models.Model):
|
|||||||
else:
|
else:
|
||||||
# 处理连续组,如果它不为空
|
# 处理连续组,如果它不为空
|
||||||
if consecutive_workorders:
|
if consecutive_workorders:
|
||||||
|
proc_workorders.append(consecutive_workorders)
|
||||||
# 创建外协出入库单和采购订单
|
# 创建外协出入库单和采购订单
|
||||||
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production)
|
# self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production, sorted_workorders)
|
||||||
self.env['purchase.order'].get_purchase_order(consecutive_workorders, production,
|
# self.env['purchase.order'].get_purchase_order(consecutive_workorders, production,
|
||||||
product_id_to_production_names)
|
# product_id_to_production_names)
|
||||||
if i < len(sorted_workorders) - 1:
|
if i < len(sorted_workorders) - 1:
|
||||||
# 重置连续组,并添加当前工作订单
|
# 重置连续组,并添加当前工作订单
|
||||||
consecutive_workorders = [workorder]
|
consecutive_workorders = [workorder]
|
||||||
@@ -751,18 +784,22 @@ class MrpProduction(models.Model):
|
|||||||
i - 1].supplier_id.id:
|
i - 1].supplier_id.id:
|
||||||
consecutive_workorders = [workorder]
|
consecutive_workorders = [workorder]
|
||||||
else:
|
else:
|
||||||
|
proc_workorders.append([workorder])
|
||||||
# 立即创建外协出入库单和采购订单
|
# 立即创建外协出入库单和采购订单
|
||||||
self.env['stock.picking'].create_outcontract_picking(workorder, production)
|
# self.env['stock.picking'].create_outcontract_picking(workorder, production)
|
||||||
self.env['purchase.order'].get_purchase_order(workorder, production,
|
# self.env['purchase.order'].get_purchase_order(workorder, production,
|
||||||
product_id_to_production_names)
|
# product_id_to_production_names)
|
||||||
consecutive_workorders = []
|
consecutive_workorders = []
|
||||||
|
|
||||||
# 处理最后一个组,即使它可能只有一个工作订单
|
# 处理最后一个组,即使它可能只有一个工作订单
|
||||||
if consecutive_workorders:
|
if consecutive_workorders:
|
||||||
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production)
|
proc_workorders.append(consecutive_workorders)
|
||||||
self.env['purchase.order'].get_purchase_order(consecutive_workorders, production,
|
# self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production)
|
||||||
product_id_to_production_names)
|
# self.env['purchase.order'].get_purchase_order(consecutive_workorders, production,
|
||||||
|
# product_id_to_production_names)
|
||||||
|
for workorders in reversed(proc_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)
|
||||||
# 工单排序
|
# 工单排序
|
||||||
def _reset_work_order_sequence1(self, k):
|
def _reset_work_order_sequence1(self, k):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
@@ -847,17 +884,22 @@ class MrpProduction(models.Model):
|
|||||||
# workorder.picking_ids.move_ids = False
|
# workorder.picking_ids.move_ids = False
|
||||||
workorder.picking_ids = False
|
workorder.picking_ids = False
|
||||||
purchase_order = self.env['purchase.order'].search(
|
purchase_order = self.env['purchase.order'].search(
|
||||||
[('state', '=', 'draft'), ('origin', '=', ','.join(production_process)),
|
[('state', '=', 'draft'), ('origin', '=', item.name),
|
||||||
('purchase_type', '=', 'consignment')])
|
('purchase_type', '=', 'consignment')])
|
||||||
for line in purchase_order.order_line:
|
server_template = self.env['product.template'].search(
|
||||||
server_template = self.env['product.template'].search(
|
[('server_product_process_parameters_id', '=',
|
||||||
[('server_product_process_parameters_id', '=', workorder.surface_technics_parameters_id.id),
|
workorder.surface_technics_parameters_id.id),
|
||||||
('detailed_type', '=', 'service')])
|
('detailed_type', '=', 'service')])
|
||||||
purchase_order_line = self.env['purchase.order.line'].search(
|
for po in purchase_order:
|
||||||
[('product_id', '=', server_template.product_variant_id.id), ('id', '=', line.id),
|
for line in po.order_line:
|
||||||
('product_qty', '=', len(production_process))], limit=1, order='id desc')
|
if line.product_id == server_template.product_variant_id:
|
||||||
if purchase_order_line:
|
continue
|
||||||
line.unlink()
|
if server_template.server_product_process_parameters_id != line.product_id.server_product_process_parameters_id:
|
||||||
|
purchase_order_line = self.env['purchase.order.line'].search(
|
||||||
|
[('product_id', '=', server_template.product_variant_id.id), ('id', '=', line.id),
|
||||||
|
('product_qty', '=', 1)], limit=1, order='id desc')
|
||||||
|
if purchase_order_line:
|
||||||
|
line.unlink()
|
||||||
|
|
||||||
def _reset_work_order_sequence(self):
|
def _reset_work_order_sequence(self):
|
||||||
"""
|
"""
|
||||||
@@ -898,8 +940,6 @@ class MrpProduction(models.Model):
|
|||||||
for cw in cancel_work_ids:
|
for cw in cancel_work_ids:
|
||||||
cw.sequence = sequence + 1
|
cw.sequence = sequence + 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _reset_work_order_sequence_1(self):
|
def _reset_work_order_sequence_1(self):
|
||||||
"""
|
"""
|
||||||
工单工序排序方法(旧)
|
工单工序排序方法(旧)
|
||||||
@@ -1012,7 +1052,7 @@ class MrpProduction(models.Model):
|
|||||||
if self.production_type == '自动化产线加工':
|
if self.production_type == '自动化产线加工':
|
||||||
date_planned_start, date_planned_end, last_time = work.auto_production_process(last_time, count,
|
date_planned_start, date_planned_end, last_time = work.auto_production_process(last_time, count,
|
||||||
type_map)
|
type_map)
|
||||||
elif self.production_type == '人工线下加工':
|
elif self.production_type == '':
|
||||||
date_planned_start, date_planned_end, last_time = work.manual_offline_process(last_time, index)
|
date_planned_start, date_planned_end, last_time = work.manual_offline_process(last_time, index)
|
||||||
work.update_work_start_end(date_planned_start, date_planned_end)
|
work.update_work_start_end(date_planned_start, date_planned_end)
|
||||||
|
|
||||||
@@ -1324,11 +1364,10 @@ class MrpProduction(models.Model):
|
|||||||
move = self.env['stock.move'].search([('origin', '=', productions.name)], order='id desc')
|
move = self.env['stock.move'].search([('origin', '=', productions.name)], order='id desc')
|
||||||
for mo in move:
|
for mo in move:
|
||||||
domain = []
|
domain = []
|
||||||
if mo.procure_method == 'make_to_order' and mo.name != productions.name:
|
if mo.location_id.barcode == 'WH-POSTPRODUCTION' and mo.rule_id.picking_type_id.barcode == 'PC':
|
||||||
if mo.name == '/':
|
domain = [('barcode', '=', 'WH-PC'), ('sequence_code', '=', 'PC')]
|
||||||
domain = [('barcode', '=', 'WH-PC'), ('sequence_code', '=', 'PC')]
|
elif mo.location_id.barcode == 'PL' and mo.rule_id.picking_type_id.barcode == 'INT':
|
||||||
elif mo.name == '拉':
|
domain = [('barcode', '=', 'WH-INTERNAL'), ('sequence_code', '=', 'INT')]
|
||||||
domain = [('barcode', '=', 'WH-INTERNAL'), ('sequence_code', '=', 'INT')]
|
|
||||||
if domain:
|
if domain:
|
||||||
picking_type = self.env['stock.picking.type'].search(domain)
|
picking_type = self.env['stock.picking.type'].search(domain)
|
||||||
mo.write({'picking_type_id': picking_type.id})
|
mo.write({'picking_type_id': picking_type.id})
|
||||||
@@ -1422,6 +1461,26 @@ class MrpProduction(models.Model):
|
|||||||
for production in self:
|
for production in self:
|
||||||
production.production_type = '自动化产线加工' if not production.product_id.is_manual_processing else '人工线下加工'
|
production.production_type = '自动化产线加工' if not production.product_id.is_manual_processing else '人工线下加工'
|
||||||
|
|
||||||
|
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
|
||||||
|
def _compute_sale_order_count(self):
|
||||||
|
for production in self:
|
||||||
|
if production.sale_order_id:
|
||||||
|
production.sale_order_count = 1
|
||||||
|
else:
|
||||||
|
production.sale_order_count = 0
|
||||||
|
def action_view_sale_orders(self):
|
||||||
|
if self.sale_order_id:
|
||||||
|
action = {
|
||||||
|
'res_model': 'sale.order',
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
}
|
||||||
|
action.update({
|
||||||
|
'view_mode': 'form',
|
||||||
|
'res_id': self.sale_order_id.id,
|
||||||
|
})
|
||||||
|
return action
|
||||||
|
|
||||||
|
|
||||||
@api.model_create_multi
|
@api.model_create_multi
|
||||||
def create(self, vals_list):
|
def create(self, vals_list):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
<field name="activity_ids" string="下一个活动" widget="list_activity" optional="hide"/>
|
<field name="activity_ids" string="下一个活动" widget="list_activity" optional="hide"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='origin']" position="replace">
|
<xpath expr="//field[@name='origin']" position="replace">
|
||||||
|
<field name="sale_order_id" optional="show"/>
|
||||||
<field name="origin" optional="hide"/>
|
<field name="origin" optional="hide"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='components_availability']" position="replace">
|
<xpath expr="//field[@name='components_availability']" position="replace">
|
||||||
@@ -123,6 +124,7 @@
|
|||||||
<field name="tool_state"
|
<field name="tool_state"
|
||||||
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"/>
|
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"/>
|
||||||
<field name="tool_state_remark" string="备注" attrs="{'invisible': [('tool_state', '!=', '1')]}"/>
|
<field name="tool_state_remark" string="备注" attrs="{'invisible': [('tool_state', '!=', '1')]}"/>
|
||||||
|
<field name="sale_order_id" readonly="1"/>
|
||||||
<field name="deadline_of_delivery" readonly="1"/>
|
<field name="deadline_of_delivery" readonly="1"/>
|
||||||
<field name="tool_state_remark2" invisible="1"/>
|
<field name="tool_state_remark2" invisible="1"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
@@ -583,6 +585,7 @@
|
|||||||
<searchpanel>
|
<searchpanel>
|
||||||
<field name="state" icon="fa-filter" enable_counters="1"/>
|
<field name="state" icon="fa-filter" enable_counters="1"/>
|
||||||
<field name="delivery_status" icon="fa-filter" enable_counters="1"/>
|
<field name="delivery_status" icon="fa-filter" enable_counters="1"/>
|
||||||
|
<field name="production_type" icon="fa-filter" enable_counters="1"/>
|
||||||
</searchpanel>
|
</searchpanel>
|
||||||
</xpath>
|
</xpath>
|
||||||
<filter name='todo' position="replace"/>
|
<filter name='todo' position="replace"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user