584 lines
30 KiB
Python
584 lines
30 KiB
Python
# -*- coding: utf-8 -*-
|
||
import base64
|
||
import logging
|
||
import re
|
||
import requests
|
||
from odoo import api, fields, models, _
|
||
from odoo.exceptions import UserError
|
||
from odoo.addons.sf_base.commons.common import Common
|
||
from odoo.tools import float_compare, float_round, float_is_zero, format_datetime
|
||
|
||
|
||
class MrpProduction(models.Model):
|
||
_inherit = 'mrp.production'
|
||
_description = "制造订单"
|
||
_order = 'create_date desc'
|
||
|
||
# tray_ids = fields.One2many('sf.tray', 'production_id', string="托盘")
|
||
maintenance_count = fields.Integer(compute='_compute_maintenance_count', string="Number of maintenance requests")
|
||
request_ids = fields.One2many('maintenance.request', 'production_id')
|
||
model_file = fields.Binary('模型文件', related='product_id.model_file')
|
||
schedule_state = fields.Selection([('未排', '未排'), ('已排', '已排'), ('已完成', '已完成')],
|
||
string='排程状态', default='未排')
|
||
|
||
# state = fields.Selection(selection_add=[
|
||
# ('pending_scheduling', '待排程'),
|
||
# ('pending_processing', '待加工'),
|
||
# ('completed', '已完工')
|
||
# ])
|
||
state = fields.Selection([
|
||
('draft', 'Draft'),
|
||
('confirmed', 'Confirmed'),
|
||
('progress', '待排程'),
|
||
('pending_processing', '待加工'),
|
||
('completed', '已完工'),
|
||
('to_close', 'To Close'),
|
||
('done', 'Done'),
|
||
('cancel', 'Cancelled')], string='State',
|
||
compute='_compute_state', copy=False, index=True, readonly=True,
|
||
store=True, tracking=True,
|
||
help=" * Draft: The MO is not confirmed yet.\n"
|
||
" * Confirmed: The MO is confirmed, the stock rules and the reordering of the components are trigerred.\n"
|
||
" * In Progress: The production has started (on the MO or on the WO).\n"
|
||
" * To Close: The production is done, the MO has to be closed.\n"
|
||
" * Done: The MO is closed, the stock moves are posted. \n"
|
||
" * Cancelled: The MO has been cancelled, can't be confirmed anymore.")
|
||
|
||
check_status = fields.Boolean(string='启用状态', default=False, readonly=True)
|
||
active = fields.Boolean(string='已归档', default=True)
|
||
programming_no = fields.Char('编程单号')
|
||
work_state = fields.Char('业务状态')
|
||
programming_state = fields.Char('编程状态')
|
||
glb_file = fields.Binary("glb模型文件")
|
||
production_line_id = fields.Many2one('sf.production.line', string='生产线')
|
||
plan_start_processing_time = fields.Datetime('计划开始加工时间')
|
||
production_line_state = fields.Selection([('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')],
|
||
string='上/下产线', default='待上产线')
|
||
|
||
|
||
@api.depends(
|
||
'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state',
|
||
'workorder_ids.state', 'product_qty', 'qty_producing', 'schedule_state')
|
||
def _compute_state(self):
|
||
for production in self:
|
||
if not production.state or not production.product_uom_id:
|
||
production.state = 'draft'
|
||
elif production.state == 'cancel' or (production.move_finished_ids and all(
|
||
move.state == 'cancel' for move in production.move_finished_ids)):
|
||
production.state = 'cancel'
|
||
elif (
|
||
production.state == 'done'
|
||
or (production.move_raw_ids and all(
|
||
move.state in ('cancel', 'done') for move in production.move_raw_ids))
|
||
and all(move.state in ('cancel', 'done') for move in production.move_finished_ids)
|
||
):
|
||
production.state = 'done'
|
||
elif production.workorder_ids and all(
|
||
wo_state in ('done', 'cancel') for wo_state in production.workorder_ids.mapped('state')):
|
||
production.state = 'to_close'
|
||
elif not production.workorder_ids and float_compare(production.qty_producing, production.product_qty,
|
||
precision_rounding=production.product_uom_id.rounding) >= 0:
|
||
production.state = 'to_close'
|
||
elif any(wo_state in ('progress', 'done') for wo_state in production.workorder_ids.mapped('state')):
|
||
production.state = 'progress'
|
||
elif production.product_uom_id and not float_is_zero(production.qty_producing,
|
||
precision_rounding=production.product_uom_id.rounding):
|
||
production.state = 'progress'
|
||
elif any(not float_is_zero(move.quantity_done,
|
||
precision_rounding=move.product_uom.rounding or move.product_id.uom_id.rounding)
|
||
for move in production.move_raw_ids):
|
||
production.state = 'progress'
|
||
|
||
# 新添加的状态逻辑
|
||
if production.state == 'progress' and production.schedule_state == '已排':
|
||
production.state = 'pending_processing'
|
||
elif production.state == 'progress' and production.schedule_state == '已完成':
|
||
production.state = 'completed'
|
||
|
||
def action_check(self):
|
||
"""
|
||
审核启用
|
||
"""
|
||
self.check_status = True
|
||
|
||
def action_uncheck(self):
|
||
"""
|
||
审核禁用
|
||
"""
|
||
self.check_status = False
|
||
|
||
def archive(self):
|
||
"""
|
||
归档
|
||
"""
|
||
self.write({'active': False})
|
||
|
||
def unarchive(self):
|
||
"""
|
||
取消归档
|
||
"""
|
||
self.write({'active': True})
|
||
|
||
@api.depends('request_ids')
|
||
def _compute_maintenance_count(self):
|
||
for production in self:
|
||
production.maintenance_count = len(production.request_ids)
|
||
|
||
# cnc程序获取
|
||
def fetchCNC(self):
|
||
cnc = self.env['mrp.production'].search([('id', '=', self.id)])
|
||
try:
|
||
res = {'model_code': '' if not cnc.product_id.model_code else cnc.product_id.model_code,
|
||
'production_no': cnc.name,
|
||
'machine_tool_code': "",
|
||
'material_code': self.env['sf.production.materials'].search(
|
||
[('id', '=', cnc.product_id.materials_id.id)]).materials_no,
|
||
'material_type_code': self.env['sf.materials.model'].search(
|
||
[('id', '=', cnc.product_id.materials_type_id.id)]).materials_no,
|
||
'machining_processing_panel': cnc.product_id.model_processing_panel,
|
||
'machining_precision': cnc.product_id.model_machining_precision,
|
||
'embryo_long': cnc.product_id.bom_ids.bom_line_ids.product_id.length,
|
||
'embryo_height': cnc.product_id.bom_ids.bom_line_ids.product_id.height,
|
||
'embryo_width': cnc.product_id.bom_ids.bom_line_ids.product_id.width,
|
||
'order_no': cnc.origin,
|
||
'model_order_no': cnc.product_id.default_code.rsplit(' -', 1)[0],
|
||
'user': cnc.env.user.name,
|
||
'model_file': '' if not cnc.product_id.model_file else base64.b64encode(
|
||
cnc.product_id.model_file).decode('utf-8')
|
||
}
|
||
logging.info('res:%s' % res)
|
||
configsettings = self.env['res.config.settings'].get_values()
|
||
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
|
||
url = '/api/intelligent_programming/create'
|
||
config_url = configsettings['sf_url'] + url
|
||
res['token'] = configsettings['token']
|
||
# res_str = json.dumps(res)
|
||
ret = requests.post(config_url, json={}, data=res, headers=config_header)
|
||
ret = ret.json()
|
||
logging.info('fetchCNC-ret:%s' % ret)
|
||
if ret['status'] == 1:
|
||
self.write(
|
||
{'programming_no': ret['programming_no'], 'programming_state': '编程中', 'work_state': '编程中'})
|
||
else:
|
||
raise UserError(ret['message'])
|
||
except Exception as e:
|
||
logging.info('fetchCNC error:%s' % e)
|
||
raise UserError("cnc程序获取编程单失败,请联系管理员")
|
||
|
||
# 维修模块按钮
|
||
def button_maintenance_req(self):
|
||
self.ensure_one()
|
||
return {
|
||
'name': _('New Maintenance Request'),
|
||
'view_mode': 'form',
|
||
'res_model': 'maintenance.request',
|
||
'type': 'ir.actions.act_window',
|
||
'context': {
|
||
'default_company_id': self.company_id.id,
|
||
'default_production_id': self.id,
|
||
},
|
||
'domain': [('production_id', '=', self.id)],
|
||
}
|
||
|
||
# 打开维修模块请求
|
||
def open_maintenance_request_mo(self):
|
||
self.ensure_one()
|
||
action = {
|
||
'name': _('Maintenance Requests'),
|
||
'view_mode': 'kanban,tree,form,pivot,graph,calendar',
|
||
'res_model': 'maintenance.request',
|
||
'type': 'ir.actions.act_window',
|
||
'context': {
|
||
'default_company_id': self.company_id.id,
|
||
'default_production_id': self.id,
|
||
},
|
||
'domain': [('production_id', '=', self.id)],
|
||
}
|
||
if self.maintenance_count == 1:
|
||
production = self.env['maintenance.request'].search([('production_id', '=', self.id)])
|
||
action['view_mode'] = 'form'
|
||
action['res_id'] = production.id
|
||
return action
|
||
|
||
def action_generate_serial(self):
|
||
self.ensure_one()
|
||
iot_code = self.env['stock.lot']._get_next_serial(self.company_id, self.product_id) or self.env[
|
||
'ir.sequence'].next_by_code('stock.lot.serial')
|
||
iot_code_name = re.sub('[\u4e00-\u9fa5]', "", iot_code)
|
||
self.lot_producing_id = self.env['stock.lot'].create({
|
||
'product_id': self.product_id.id,
|
||
'company_id': self.company_id.id,
|
||
'name': iot_code_name,
|
||
})
|
||
if self.move_finished_ids.filtered(lambda m: m.product_id == self.product_id).move_line_ids:
|
||
self.move_finished_ids.filtered(
|
||
lambda m: m.product_id == self.product_id).move_line_ids.lot_id = self.lot_producing_id
|
||
if self.product_id.tracking == 'serial':
|
||
self._set_qty_producing()
|
||
|
||
# 重载根据工序生成工单的程序:如果产品BOM中没有工序时,
|
||
# 根据产品对应的模板类型中工序,去生成工单;
|
||
# CNC加工工序的选取规则:
|
||
# 如果自动报价有带过来预分配的机床,
|
||
# 则根据设备找到工作中心;否则采用前面描述的工作中心分配机制;
|
||
# 其他规则限制: 默认只分配给工作中心状态为非故障的工作中心;
|
||
|
||
def _create_workorder3(self):
|
||
for production in self:
|
||
if not production.bom_id or not production.product_id:
|
||
continue
|
||
workorders_values = []
|
||
|
||
product_qty = production.product_uom_id._compute_quantity(production.product_qty,
|
||
production.bom_id.product_uom_id)
|
||
exploded_boms, dummy = production.bom_id.explode(production.product_id,
|
||
product_qty / production.bom_id.product_qty,
|
||
picking_type=production.bom_id.picking_type_id)
|
||
|
||
for bom, bom_data in exploded_boms:
|
||
# If the operations of the parent BoM and phantom BoM are the same, don't recreate work orders.
|
||
if not (bom.operation_ids and (not bom_data['parent_line'] or bom_data[
|
||
'parent_line'].bom_id.operation_ids != bom.operation_ids)):
|
||
continue
|
||
for operation in bom.operation_ids:
|
||
if operation._skip_operation_line(bom_data['product']):
|
||
continue
|
||
workorders_values += [{
|
||
'name': operation.name,
|
||
'production_id': production.id,
|
||
'workcenter_id': operation.workcenter_id.id,
|
||
'product_uom_id': production.product_uom_id.id,
|
||
'operation_id': operation.id,
|
||
'state': 'pending',
|
||
}]
|
||
if production.product_id.categ_id.type == '成品':
|
||
production.fetchCNC()
|
||
# 根据加工面板的面数及对应的工序模板生成工单
|
||
i = 0
|
||
processing_panel_len = len(production.product_id.model_processing_panel.split(','))
|
||
for k in (production.product_id.model_processing_panel.split(',')):
|
||
product_routing_workcenter = self.env['sf.product.model.type.routing.sort'].search(
|
||
[('product_model_type_id', '=', production.product_id.product_model_type_id.id)],
|
||
order='sequence asc'
|
||
)
|
||
i += 1
|
||
for route in product_routing_workcenter:
|
||
if route.is_repeat is True:
|
||
workorders_values.append(
|
||
self.env['mrp.workorder'].json_workorder_str(k, production, route))
|
||
if i == processing_panel_len and route.routing_type == '解除装夹':
|
||
workorders_values.append(
|
||
self.env['mrp.workorder'].json_workorder_str(k, production, route))
|
||
# 表面工艺工序
|
||
# 获取表面工艺id
|
||
if production.product_id.model_process_parameters_ids:
|
||
surface_technics_arr = []
|
||
# 工序id
|
||
route_workcenter_arr = []
|
||
for item in production.product_id.product_model_type_id.surface_technics_routing_tmpl_ids:
|
||
surface_technics_arr.append(item.route_workcenter_id.surface_technics_id.id)
|
||
route_workcenter_arr.append(item.route_workcenter_id.id)
|
||
if surface_technics_arr:
|
||
production_process_category = self.env['sf.production.process.category'].search(
|
||
[('production_process_ids.id', 'in', surface_technics_arr)],
|
||
order='sequence asc'
|
||
)
|
||
# 用filter刷选表面工艺id'是否存在工艺类别对象里
|
||
if production_process_category:
|
||
for p in production_process_category:
|
||
production_process = p.production_process_ids.filtered(
|
||
lambda pp: pp.id in surface_technics_arr)
|
||
if production_process:
|
||
process_parameter = production.product_id.model_process_parameters_ids.filtered(
|
||
lambda pm: pm.process_id.id == production_process.id)
|
||
if process_parameter:
|
||
# 产品为表面工艺服务的供应商
|
||
product_production_process = self.env['product.template'].search(
|
||
[('server_product_process_parameters_id', '=', process_parameter.id)])
|
||
if product_production_process:
|
||
route_production_process = self.env[
|
||
'mrp.routing.workcenter'].search(
|
||
[('surface_technics_id', '=', production_process.id),
|
||
('id', 'in', route_workcenter_arr)])
|
||
if route_production_process:
|
||
workorders_values.append(
|
||
self.env[
|
||
'mrp.workorder']._json_workorder_surface_process_str(
|
||
production, route_production_process,
|
||
process_parameter,
|
||
product_production_process.seller_ids[0].partner_id.id))
|
||
elif production.product_id.categ_id.type == '坯料':
|
||
embryo_routing_workcenter = self.env['sf.embryo.model.type.routing.sort'].search(
|
||
[('embryo_model_type_id', '=', production.product_id.embryo_model_type_id.id)],
|
||
order='sequence asc'
|
||
)
|
||
for route in embryo_routing_workcenter:
|
||
workorders_values.append(
|
||
self.env['mrp.workorder'].json_workorder_str('', production, route))
|
||
production.workorder_ids = workorders_values
|
||
process_parameter_workorder = self.env['mrp.workorder'].search(
|
||
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id),
|
||
('is_subcontract', '=', True)])
|
||
if process_parameter_workorder:
|
||
is_pick = False
|
||
consecutive_workorders = []
|
||
m = 0
|
||
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.id)
|
||
for i in range(len(sorted_workorders) - 1):
|
||
if m == 0:
|
||
is_pick = False
|
||
if sorted_workorders[i].supplier_id.id == sorted_workorders[i + 1].supplier_id.id and \
|
||
sorted_workorders[i].is_subcontract == sorted_workorders[i + 1].is_subcontract and \
|
||
sorted_workorders[i].id == sorted_workorders[i + 1].id - 1:
|
||
if sorted_workorders[i] not in consecutive_workorders:
|
||
consecutive_workorders.append(sorted_workorders[i])
|
||
consecutive_workorders.append(sorted_workorders[i + 1])
|
||
m += 1
|
||
continue
|
||
else:
|
||
if m == len(consecutive_workorders) - 1 and m != 0:
|
||
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production)
|
||
if sorted_workorders[i] in consecutive_workorders:
|
||
is_pick = True
|
||
consecutive_workorders = []
|
||
m = 0
|
||
# 当前面的连续工序生成对应的外协出入库单再生成当前工序的外协出入库单
|
||
if is_pick is False:
|
||
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production)
|
||
if m == len(consecutive_workorders) - 1 and m != 0:
|
||
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production)
|
||
if sorted_workorders[i] in consecutive_workorders:
|
||
is_pick = True
|
||
consecutive_workorders = []
|
||
m = 0
|
||
if m == len(consecutive_workorders) - 1 and m != 0:
|
||
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production)
|
||
if is_pick is False and m == 0:
|
||
if len(sorted_workorders) == 1:
|
||
self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production)
|
||
else:
|
||
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production)
|
||
for workorder in production.workorder_ids:
|
||
workorder.duration_expected = workorder._get_duration_expected()
|
||
|
||
# 在之前的销售单上重新生成制造订单
|
||
def create_production1_values(self, production):
|
||
production_values_str = {'origin': production.origin,
|
||
'product_id': production.product_id.id,
|
||
'product_description_variants': production.product_description_variants,
|
||
'product_qty': production.product_qty,
|
||
'product_uom_id': production.product_uom_id.id,
|
||
'location_src_id': production.location_src_id.id,
|
||
'location_dest_id': production.location_dest_id.id,
|
||
'bom_id': production.bom_id.id,
|
||
'date_deadline': production.date_deadline,
|
||
'date_planned_start': production.date_planned_start,
|
||
'date_planned_finished': production.date_planned_finished,
|
||
'procurement_group_id': False,
|
||
'propagate_cancel': production.propagate_cancel,
|
||
'orderpoint_id': production.orderpoint_id.id,
|
||
'picking_type_id': production.picking_type_id.id,
|
||
'company_id': production.company_id.id,
|
||
'move_dest_ids': production.move_dest_ids.ids,
|
||
'user_id': production.user_id.id}
|
||
return production_values_str
|
||
|
||
def _get_stock_move_values_Res(self, item, location_src_id, location_dest_id, picking_type_id):
|
||
move_values = {
|
||
'name': item.name if item.name else '/',
|
||
'company_id': item.company_id.id,
|
||
'product_id': item.bom_id.bom_line_ids.product_id.id,
|
||
'product_uom': item.bom_id.bom_line_ids.product_uom_id.id,
|
||
'product_uom_qty': 1.0,
|
||
'location_id': location_src_id,
|
||
'location_dest_id': location_dest_id,
|
||
'origin': item.origin,
|
||
'picking_type_id': picking_type_id,
|
||
}
|
||
return move_values
|
||
|
||
# 工单排序
|
||
def _reset_work_order_sequence1(self, k):
|
||
sequen = 0
|
||
for rec in self:
|
||
current_sequence = 10
|
||
for work in rec.workorder_ids:
|
||
work.sequence = current_sequence
|
||
current_sequence += 10
|
||
if work.name == '后置三元质量检测' and work.processing_panel == k:
|
||
sequen = work.sequence
|
||
|
||
for work in rec.workorder_ids:
|
||
if work.name == '后置三元质量检测(返工)' and work.processing_panel == k:
|
||
work.sequence = sequen + 2
|
||
if work.name == 'CNC加工(返工)' and work.processing_panel == k:
|
||
work.sequence = sequen + 1
|
||
|
||
# 在制造订单上新增工单
|
||
def _create_workorder1(self, k):
|
||
for production in self:
|
||
if not production.bom_id or not production.product_id:
|
||
continue
|
||
workorders_values = []
|
||
|
||
product_qty = production.product_uom_id._compute_quantity(production.product_qty,
|
||
production.bom_id.product_uom_id)
|
||
exploded_boms, dummy = production.bom_id.explode(production.product_id,
|
||
product_qty / production.bom_id.product_qty,
|
||
picking_type=production.bom_id.picking_type_id)
|
||
|
||
for bom, bom_data in exploded_boms:
|
||
# If the operations of the parent BoM and phantom BoM are the same, don't recreate work orders.
|
||
if not (bom.operation_ids and (not bom_data['parent_line'] or bom_data[
|
||
'parent_line'].bom_id.operation_ids != bom.operation_ids)):
|
||
continue
|
||
for operation in bom.operation_ids:
|
||
if operation._skip_operation_line(bom_data['product']):
|
||
continue
|
||
workorders_values += [{
|
||
'name': operation.name,
|
||
'production_id': production.id,
|
||
'workcenter_id': operation.workcenter_id.id,
|
||
'product_uom_id': production.product_uom_id.id,
|
||
'operation_id': operation.id,
|
||
'state': 'pending',
|
||
}]
|
||
# 根据加工面板的面数及对应的成品工序模板生成工单
|
||
i = 0
|
||
production.product_id.model_processing_panel = k
|
||
for k in (production.product_id.model_processing_panel.split(',')):
|
||
routingworkcenter = self.env['sf.product.model.type.routing.sort'].search(
|
||
[('product_model_type_id', '=', production.product_id.product_model_type_id.id)],
|
||
order='sequence asc'
|
||
)
|
||
i += 1
|
||
|
||
for route in routingworkcenter:
|
||
|
||
# if route.routing_type == '后置三元质量检测':
|
||
# workorders_values.append(
|
||
# self.env['mrp.workorder'].json_workorder_str1(k, production, route)
|
||
# )
|
||
if route.routing_type == 'CNC加工':
|
||
workorders_values.append(
|
||
self.env['mrp.workorder'].json_workorder_str1(k, production, route))
|
||
|
||
production.workorder_ids = workorders_values
|
||
workorder = self.env['mrp.workorder'].browse(production.workorder_ids.ids)
|
||
print(workorder)
|
||
# for item in workorder:
|
||
# workorder.duration_expected = workorder._get_duration_expected()
|
||
|
||
def _create_workorder2(self, k):
|
||
self._create_workorder1(k)
|
||
self._reset_work_order_sequence1(k)
|
||
return True
|
||
|
||
def _reset_work_order_sequence(self):
|
||
for rec in self:
|
||
current_sequence = 1
|
||
for work in rec.workorder_ids:
|
||
work.sequence = current_sequence
|
||
current_sequence += 1
|
||
# if work.name == '获取CNC加工程序':
|
||
# work.button_start()
|
||
# #work.fetchCNC()
|
||
# work.button_finish()
|
||
|
||
# 创建工单并进行排序
|
||
def _create_workorder(self):
|
||
self._create_workorder3()
|
||
self._reset_work_order_sequence()
|
||
return True
|
||
|
||
# 修改标记已完成方法
|
||
def button_mark_done1(self):
|
||
self._button_mark_done_sanity_checks()
|
||
|
||
if not self.env.context.get('button_mark_done_production_ids'):
|
||
self = self.with_context(button_mark_done_production_ids=self.ids)
|
||
res = self._pre_button_mark_done()
|
||
if res is not True:
|
||
return res
|
||
|
||
if self.env.context.get('mo_ids_to_backorder'):
|
||
productions_to_backorder = self.browse(self.env.context['mo_ids_to_backorder'])
|
||
productions_not_to_backorder = self - productions_to_backorder
|
||
else:
|
||
productions_not_to_backorder = self
|
||
productions_to_backorder = self.env['mrp.production']
|
||
|
||
backorders = productions_to_backorder and productions_to_backorder._split_productions()
|
||
backorders = backorders - productions_to_backorder
|
||
|
||
productions_not_to_backorder._post_inventory(cancel_backorder=True)
|
||
productions_to_backorder._post_inventory(cancel_backorder=True)
|
||
|
||
# if completed products make other confirmed/partially_available moves available, assign them
|
||
done_move_finished_ids = (
|
||
productions_to_backorder.move_finished_ids | productions_not_to_backorder.move_finished_ids).filtered(
|
||
lambda m: m.state == 'done')
|
||
done_move_finished_ids._trigger_assign()
|
||
|
||
# Moves without quantity done are not posted => set them as done instead of canceling. In
|
||
# case the user edits the MO later on and sets some consumed quantity on those, we do not
|
||
# want the move lines to be canceled.
|
||
(productions_not_to_backorder.move_raw_ids | productions_not_to_backorder.move_finished_ids).filtered(
|
||
lambda x: x.state not in ('done', 'cancel')).write({
|
||
'state': 'done',
|
||
'product_uom_qty': 0.0,
|
||
})
|
||
|
||
for production in self:
|
||
production.write({
|
||
'date_finished': fields.Datetime.now(),
|
||
'product_qty': production.qty_produced,
|
||
'priority': '0',
|
||
'is_locked': True,
|
||
'state': 'done',
|
||
})
|
||
|
||
for workorder in self.workorder_ids.filtered(lambda w: w.state not in ('done', 'cancel')):
|
||
workorder.duration_expected = workorder._get_duration_expected()
|
||
|
||
if not backorders:
|
||
if self.env.context.get('from_workorder'):
|
||
return {
|
||
'type': 'ir.actions.act_window',
|
||
'res_model': 'mrp.production',
|
||
'views': [[self.env.ref('mrp.mrp_production_form_view').id, 'form']],
|
||
'res_id': self.id,
|
||
'target': 'main',
|
||
}
|
||
if self.user_has_groups(
|
||
'mrp.group_mrp_reception_report') and self.picking_type_id.auto_show_reception_report:
|
||
lines = self.move_finished_ids.filtered(lambda
|
||
m: m.product_id.type == 'product' and m.state != 'cancel' and m.quantity_done and not m.move_dest_ids)
|
||
if lines:
|
||
if any(mo.show_allocation for mo in self):
|
||
action = self.action_view_reception_report()
|
||
return action
|
||
return True
|
||
context = self.env.context.copy()
|
||
context = {k: v for k, v in context.items() if not k.startswith('default_')}
|
||
for k, v in context.items():
|
||
if k.startswith('skip_'):
|
||
context[k] = False
|
||
action = {
|
||
'res_model': 'mrp.production',
|
||
'type': 'ir.actions.act_window',
|
||
'context': dict(context, mo_ids_to_backorder=None, button_mark_done_production_ids=None)
|
||
}
|
||
if len(backorders) == 1:
|
||
action.update({
|
||
'view_mode': 'form',
|
||
'res_id': backorders[0].id,
|
||
})
|
||
else:
|
||
action.update({
|
||
'name': _("Backorder MO"),
|
||
'domain': [('id', 'in', backorders.ids)],
|
||
'view_mode': 'tree,form',
|
||
})
|
||
return action
|