Merge branch 'refs/heads/develop' into feature/tool_standard_library_process
# Conflicts: # sf_manufacturing/models/mrp_workorder.py
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
'wizard/production_technology_re_adjust_wizard_views.xml',
|
||||
'wizard/mrp_workorder_batch_replan_wizard_views.xml',
|
||||
'wizard/sf_programming_reason_views.xml',
|
||||
'wizard/sale_order_cancel_views.xml',
|
||||
'views/mrp_views_menus.xml',
|
||||
'views/agv_scheduling_views.xml',
|
||||
'views/stock_lot_views.xml',
|
||||
|
||||
@@ -19,12 +19,8 @@ class AgvScheduling(models.Model):
|
||||
_order = 'id desc'
|
||||
|
||||
name = fields.Char('任务单号', index=True, copy=False)
|
||||
|
||||
def _get_agv_route_type_selection(self):
|
||||
return self.env['sf.agv.task.route'].fields_get(['route_type'])['route_type']['selection']
|
||||
|
||||
agv_route_type = fields.Selection(selection=_get_agv_route_type_selection, string='任务类型', required=True)
|
||||
agv_route_id = fields.Many2one('sf.agv.task.route', '任务路线')
|
||||
agv_route_type = fields.Selection(related='agv_route_id.route_type', string='任务类型', required=True)
|
||||
start_site_id = fields.Many2one('sf.agv.site', '起点接驳站', required=True)
|
||||
end_site_id = fields.Many2one('sf.agv.site', '终点接驳站', tracking=True)
|
||||
site_state = fields.Selection([
|
||||
|
||||
@@ -235,7 +235,7 @@ class MrpProduction(models.Model):
|
||||
programming_no = fields.Char('编程单号')
|
||||
work_state = fields.Char('业务状态')
|
||||
programming_state = fields.Selection(
|
||||
[('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'), ('已下发', '已下发')],
|
||||
[('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'), ('已下发', '已下发'), ('已取消', '已取消')],
|
||||
string='编程状态',
|
||||
tracking=True)
|
||||
glb_file = fields.Binary("glb模型文件")
|
||||
@@ -577,16 +577,19 @@ class MrpProduction(models.Model):
|
||||
|
||||
# 编程单更新
|
||||
# 增加触发时间参数
|
||||
def update_programming_state(self, trigger_time=None):
|
||||
def update_programming_state(self, trigger_time=None, reprogramming_reason=None):
|
||||
try:
|
||||
manufacturing_type = 'rework'
|
||||
manufacturing_type = None
|
||||
if self.is_scrap:
|
||||
manufacturing_type = 'scrap'
|
||||
elif self.tool_state == '2':
|
||||
manufacturing_type = 'invalid_tool_rework'
|
||||
elif self.is_rework:
|
||||
manufacturing_type = 'rework'
|
||||
res = {'programming_no': self.programming_no,
|
||||
'manufacturing_type': manufacturing_type,
|
||||
'trigger_time': trigger_time}
|
||||
'trigger_time': trigger_time,
|
||||
'reprogramming_reason': reprogramming_reason}
|
||||
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'])
|
||||
@@ -643,6 +646,28 @@ class MrpProduction(models.Model):
|
||||
logging.info('update_programming_state error:%s' % e)
|
||||
raise UserError("更新编程单状态失败,请联系管理员")
|
||||
|
||||
# 修改编程单状态
|
||||
def _change_programming_state(self):
|
||||
try:
|
||||
res = {"programming_no": self.programming_no, "state": "已取消"}
|
||||
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/set_state'
|
||||
config_url = configsettings['sf_url'] + url
|
||||
ret = requests.post(config_url, json=res, data=None, headers=config_header)
|
||||
ret = ret.json()
|
||||
result = json.loads(ret['result'])
|
||||
logging.info('change_programming_state-ret:%s' % result)
|
||||
if result['status'] == 1:
|
||||
self.write({'programming_state': '已取消'})
|
||||
else:
|
||||
raise UserError(ret['message'])
|
||||
except Exception as e:
|
||||
logging.info('change_programming_state error:%s' % e)
|
||||
raise UserError("修改编程单状态失败,请联系管理员")
|
||||
|
||||
|
||||
# cnc程序获取
|
||||
def fetchCNC(self, production_names):
|
||||
cnc = self.env['mrp.production'].search([('id', '=', self.id)])
|
||||
@@ -655,6 +680,8 @@ class MrpProduction(models.Model):
|
||||
programme_way = 'manual operation'
|
||||
else:
|
||||
programme_way = 'auto'
|
||||
if cnc.production_type == '人工线下加工':
|
||||
programme_way = 'manual operation'
|
||||
if quick_order:
|
||||
programme_way = 'manual operation'
|
||||
try:
|
||||
@@ -1254,6 +1281,7 @@ class MrpProduction(models.Model):
|
||||
'target': 'new',
|
||||
'context': {
|
||||
'default_production_id': self.id,
|
||||
'default_is_clamping': True if self.workorder_ids.filtered(lambda wk: wk.routing_type == '装夹预调') else False,
|
||||
'default_workorder_ids': workorder_ids.ids if workorder_ids.ids != [] else self.workorder_ids.ids,
|
||||
'default_hidden_workorder_ids': ','.join(map(str, work_id_list)) if work_id_list != [] else '',
|
||||
'default_reprogramming_num': cloud_programming.get('reprogramming_num') if cloud_programming else '',
|
||||
@@ -1292,7 +1320,7 @@ class MrpProduction(models.Model):
|
||||
for rework_item in rework_workorder:
|
||||
pending_workorder = production.workorder_ids.filtered(
|
||||
lambda m1: m1.state in [
|
||||
'pending'] and m1.processing_panel == rework_item.processing_panel and m1.routing_type == 'CNC加工')
|
||||
'pending'] and m1.processing_panel == rework_item.processing_panel and m1.routing_type in ['CNC加工', '人工线下加工'])
|
||||
if not pending_workorder.cnc_ids:
|
||||
production.get_new_program(rework_item.processing_panel)
|
||||
# production.write({'state': 'progress', 'programming_state': '已编程', 'is_rework': False})
|
||||
@@ -1302,6 +1330,7 @@ class MrpProduction(models.Model):
|
||||
# 对制造订单所以面的cnc工单的程序用刀进行校验
|
||||
try:
|
||||
logging.info(f'已更新制造订单:{productions_not_delivered}')
|
||||
productions = productions.filtered(lambda p: p.production_type == '自动化产线加工')
|
||||
productions.production_cnc_tool_checkout()
|
||||
except Exception as e:
|
||||
logging.info(f'对cnc工单的程序用刀进行校验报错:{e}')
|
||||
@@ -1334,7 +1363,7 @@ class MrpProduction(models.Model):
|
||||
if productions:
|
||||
for production in productions:
|
||||
panel_workorder = production.workorder_ids.filtered(lambda
|
||||
pw: pw.processing_panel == processing_panel and pw.routing_type == 'CNC加工' and pw.state not in (
|
||||
pw: pw.processing_panel == processing_panel and pw.routing_type in ['CNC加工', '人工线下加工'] and pw.state not in (
|
||||
'rework', 'done'))
|
||||
if panel_workorder:
|
||||
if panel_workorder.cmm_ids:
|
||||
@@ -1360,7 +1389,7 @@ class MrpProduction(models.Model):
|
||||
'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
|
||||
logging.info('len(cnc_worksheet):%s' % len(panel_workorder.cnc_worksheet))
|
||||
pre_workorder = production.workorder_ids.filtered(lambda
|
||||
ap: ap.routing_type == '装夹预调' and ap.processing_panel == processing_panel and ap.state not in (
|
||||
ap: ap.routing_type in ['装夹预调', '人工线下加工'] and ap.processing_panel == processing_panel and ap.state not in (
|
||||
'rework', 'done'))
|
||||
if pre_workorder:
|
||||
pre_workorder.write(
|
||||
@@ -1698,7 +1727,7 @@ class sf_programming_record(models.Model):
|
||||
programming_method = fields.Selection([
|
||||
('auto', '自动'),
|
||||
('manual operation', '人工')], string="编程方式")
|
||||
current_programming_count = fields.Integer('当前编程次数')
|
||||
current_programming_count = fields.Integer('重新编程次数')
|
||||
target_production_id = fields.Char('目标制造单号')
|
||||
apply_time = fields.Datetime('申请时间')
|
||||
send_time = fields.Datetime('下发时间')
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import random
|
||||
import re
|
||||
import json
|
||||
import logging
|
||||
@@ -114,6 +113,7 @@ class ResMrpWorkOrder(models.Model):
|
||||
record.back_button_display = False
|
||||
if cur_workorder.is_subcontract:
|
||||
record.back_button_display = False
|
||||
date_planned_start = fields.Datetime(tracking=True)
|
||||
|
||||
@api.depends('processing_panel')
|
||||
def _compute_processing_panel_selection(self):
|
||||
@@ -578,6 +578,32 @@ class ResMrpWorkOrder(models.Model):
|
||||
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因", tracking=True)
|
||||
detailed_reason = fields.Text('详细原因')
|
||||
is_rework = fields.Boolean(string='是否返工', default=False)
|
||||
# rework_flag = fields.Boolean(string='返工标志', compute='_compute_rework_flag')
|
||||
#
|
||||
# @api.depends('state', 'production_line_state')
|
||||
# def _compute_rework_flag(self):
|
||||
# for record in self:
|
||||
# if record.state == 'done' and record.routing_type == '装夹预调':
|
||||
# next_workorder = record.production_id.workorder_ids.filtered(
|
||||
# lambda w: w.sequence == record.sequence + 1)
|
||||
# if next_workorder and next_workorder.routing_type == 'CNC加工' and next_workorder.state in ['ready',
|
||||
# 'waiting',
|
||||
# 'pending'] and next_workorder.production_line_state == '待上产线':
|
||||
# record.rework_flag = False
|
||||
# elif next_workorder and next_workorder.routing_type == '表面工艺' and next_workorder.state in ['ready',
|
||||
# 'waiting',
|
||||
# 'pending']:
|
||||
# record.rework_flag = False
|
||||
# else:
|
||||
# record.rework_flag = True
|
||||
# else:
|
||||
# record.rework_flag = True
|
||||
#
|
||||
# def button_rework(self):
|
||||
# for item in self:
|
||||
# item.state = 'progress'
|
||||
# for time_id in item.time_ids:
|
||||
# time_id.write({'date_end': None})
|
||||
|
||||
def button_change_env(self):
|
||||
self.is_test_env = not self.is_test_env
|
||||
@@ -1183,7 +1209,8 @@ class ResMrpWorkOrder(models.Model):
|
||||
if (workorder.production_id.production_type == '人工线下加工'
|
||||
and workorder.production_id.schedule_state == '已排'
|
||||
and len(workorder.production_id.picking_ids.filtered(
|
||||
lambda w: w.state not in ['done', 'cancel'])) == 0):
|
||||
lambda w: w.state not in ['done', 'cancel'])) == 0
|
||||
and workorder.production_id.programming_state == '已编程'):
|
||||
if workorder.is_subcontract is True:
|
||||
purchase_orders_id = self._get_surface_technics_purchase_ids()
|
||||
if purchase_orders_id.state == 'purchase':
|
||||
@@ -1240,7 +1267,6 @@ class ResMrpWorkOrder(models.Model):
|
||||
mo.get_move_line(workorder.production_id, workorder))
|
||||
else:
|
||||
workorder.state = 'waiting'
|
||||
|
||||
# 重写工单开始按钮方法
|
||||
def button_start(self):
|
||||
# 判断工单状态是否为等待组件
|
||||
@@ -1407,8 +1433,7 @@ class ResMrpWorkOrder(models.Model):
|
||||
'detailed_reason': record.detailed_reason,
|
||||
'processing_panel': record.processing_panel,
|
||||
'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_report': record.detection_report})],
|
||||
'is_scrap': True if record.test_results == '报废' else False
|
||||
@@ -1425,8 +1450,7 @@ class ResMrpWorkOrder(models.Model):
|
||||
raise UserError('请先完成该工单的工艺外协再进行操作')
|
||||
# 表面工艺外协,最后一张工单
|
||||
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]:
|
||||
# 给下一个库存移动就绪
|
||||
self.move_subcontract_workorder_ids[0].move_dest_ids._action_done()
|
||||
@@ -1649,7 +1673,6 @@ class ResMrpWorkOrder(models.Model):
|
||||
# 根据工单对应的【作业_个性化记录】配置页签
|
||||
if any(item.code == 'PTD' for item in mw.routing_workcenter_id.individuation_page_ids):
|
||||
mw.individuation_page_PTD = True
|
||||
|
||||
# =============================================================================================
|
||||
|
||||
is_inspect = fields.Boolean('需送检', compute='_compute_is_inspect', store=True, default=False)
|
||||
@@ -1786,8 +1809,8 @@ class CNCprocessing(models.Model):
|
||||
|
||||
# 将FTP的多面的程序单文件下载到临时目录
|
||||
def download_file_tmp(self, production_no, processing_panel):
|
||||
remotepath = os.path.join('/home/ftp/ftp_root/NC', 'production_no', 'return', processing_panel)
|
||||
serverdir = os.path.join('/tmp', 'production_no', 'return', processing_panel)
|
||||
remotepath = os.path.join('/home/ftp/ftp_root/NC', production_no, 'return', processing_panel)
|
||||
serverdir = os.path.join('/tmp', production_no, 'return', processing_panel)
|
||||
ftp_resconfig = self.env['res.config.settings'].get_values()
|
||||
ftp = FtpController(str(ftp_resconfig['ftp_host']), int(ftp_resconfig['ftp_port']), ftp_resconfig['ftp_user'],
|
||||
ftp_resconfig['ftp_password'])
|
||||
@@ -1959,11 +1982,7 @@ class WorkPieceDelivery(models.Model):
|
||||
feeder_station_destination_id = fields.Many2one('sf.agv.site', '目的接驳站')
|
||||
task_delivery_time = fields.Datetime('任务下发时间')
|
||||
task_completion_time = fields.Datetime('任务完成时间')
|
||||
|
||||
def _get_agv_route_type_selection(self):
|
||||
return self.env['sf.agv.task.route'].fields_get(['route_type'])['route_type']['selection']
|
||||
|
||||
type = fields.Selection(selection=_get_agv_route_type_selection, string='类型')
|
||||
type = fields.Selection(related='route_id.route_type', string='类型')
|
||||
delivery_duration = fields.Float('配送时长', compute='_compute_delivery_duration')
|
||||
status = fields.Selection(
|
||||
[('待下发', '待下发'), ('已下发', '待配送'), ('已配送', '已配送'), ('已取消', '已取消')], string='状态',
|
||||
|
||||
@@ -149,6 +149,23 @@ class SaleOrder(models.Model):
|
||||
product_bom_purchase.with_user(self.env.ref("base.user_admin")).bom_create_line_has(
|
||||
purchase_embryo)
|
||||
return super(SaleOrder, self).action_confirm()
|
||||
|
||||
def action_show_cancel_wizard(self):
|
||||
wizard = self.env['sf.sale.order.cancel.wizard'].create({
|
||||
'order_id': self.id,
|
||||
})
|
||||
|
||||
# 创建关联单据行
|
||||
self.env['sf.sale.order.cancel.line'].create_from_order(wizard.id, self)
|
||||
|
||||
return {
|
||||
'name': '取消销售订单',
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'sf.sale.order.cancel.wizard',
|
||||
'view_mode': 'form',
|
||||
'target': 'new',
|
||||
'res_id': wizard.id,
|
||||
}
|
||||
|
||||
class SaleOrderLine(models.Model):
|
||||
_inherit = 'sale.order.line'
|
||||
@@ -166,4 +183,6 @@ class SaleOrderLine(models.Model):
|
||||
for line in self:
|
||||
if vals['supply_method'] == 'automation' and line.manual_quotation:
|
||||
raise UserError('当前(%s)产品为人工编程产品,不能选择自动化产线加工' % ','.join(line.mapped('product_id.name')))
|
||||
if vals['supply_method'] == 'purchase' and line.is_incoming_material:
|
||||
raise UserError('当前(%s)产品为客供料,不能选择外购' % ','.join(line.mapped('product_id.name')))
|
||||
return super(SaleOrderLine, self).write(vals)
|
||||
|
||||
@@ -197,7 +197,7 @@ class StockRule(models.Model):
|
||||
'''
|
||||
# productions._create_workorder()
|
||||
#
|
||||
self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
|
||||
# self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
|
||||
productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \
|
||||
(
|
||||
p.move_dest_ids.procure_method != 'make_to_order' and not
|
||||
@@ -289,7 +289,7 @@ class StockRule(models.Model):
|
||||
if production_item.product_id.id in product_id_to_production_names:
|
||||
# 同一个产品多个制造订单对应一个编程单和模型库
|
||||
# 只调用一次fetchCNC,并将所有生产订单的名称作为字符串传递
|
||||
if not production_item.programming_no and production_item.production_type == '自动化产线加工':
|
||||
if not production_item.programming_no and production_item.production_type in ['自动化产线加工', '人工线下加工']:
|
||||
if not production_programming.programming_no:
|
||||
production_item.fetchCNC(
|
||||
', '.join(product_id_to_production_names[production_item.product_id.id]))
|
||||
@@ -314,6 +314,11 @@ class StockRule(models.Model):
|
||||
i += 1
|
||||
technology_design_values.append(
|
||||
self.env['sf.technology.design'].json_technology_design_str(k, route, i, False))
|
||||
elif production_item.production_type == '人工线下加工':
|
||||
for route in product_routing_workcenter:
|
||||
i += 1
|
||||
technology_design_values.append(
|
||||
self.env['sf.technology.design'].json_technology_design_str('ZM', route, i, False))
|
||||
else:
|
||||
for route in product_routing_workcenter:
|
||||
i += 1
|
||||
@@ -606,6 +611,18 @@ class StockPicking(models.Model):
|
||||
return sequence_id
|
||||
|
||||
def button_validate(self):
|
||||
# 校验“收料入库单、客供料入库单”是否已经分配序列号,如果没有分配则自动分配
|
||||
if self.picking_type_id.use_existing_lots is False and self.picking_type_id.use_create_lots is True:
|
||||
for move in self.move_ids:
|
||||
if not move.move_line_nosuggest_ids:
|
||||
move.action_show_details()
|
||||
else:
|
||||
# 对已经生成的序列号做唯一性校验,如果重复则重新生成新的序列号
|
||||
line_lot_name = [line_id.lot_name for line_id in move.move_line_nosuggest_ids]
|
||||
lot_ids = self.env['stock.lot'].sudo().search([('name', 'in', line_lot_name)])
|
||||
if lot_ids:
|
||||
move.action_clear_lines_show_details()
|
||||
move.action_show_details()
|
||||
res = super().button_validate()
|
||||
picking_type_in = self.env.ref('sf_manufacturing.outcontract_picking_in').id
|
||||
if res is True and self.picking_type_id.id == picking_type_in:
|
||||
@@ -839,7 +856,8 @@ class ReStockMove(models.Model):
|
||||
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)
|
||||
if self.picking_type_id.sequence_code == 'DL' and not self.move_line_nosuggest_ids:
|
||||
if (self.picking_type_id.use_existing_lots is False
|
||||
and self.picking_type_id.use_create_lots is True and not self.move_line_nosuggest_ids):
|
||||
self.action_assign_serial_show_details()
|
||||
elif self.product_id.tracking == "lot":
|
||||
self._put_tool_lot(self.company_id, self.product_id, self.origin)
|
||||
|
||||
@@ -192,3 +192,5 @@ access_sf_programming_reason,sf_programming_reason,model_sf_programming_reason,b
|
||||
access_sf_programming_record,sf_programming_record,model_sf_programming_record,base.group_user,1,1,1,0
|
||||
access_sf_work_individuation_page,sf_work_individuation_page,model_sf_work_individuation_page,sf_base.group_sf_mrp_user,1,1,1,0
|
||||
access_sf_work_individuation_page_group_plan_dispatch,sf_work_individuation_page_group_plan_dispatch,model_sf_work_individuation_page,sf_base.group_plan_dispatch,1,1,0,0
|
||||
access_sf_sale_order_cancel_wizard,sf_sale_order_cancel_wizard,model_sf_sale_order_cancel_wizard,sf_base.group_sf_order_user,1,1,1,0
|
||||
access_sf_sale_order_cancel_line,sf_sale_order_cancel_line,model_sf_sale_order_cancel_line,sf_base.group_sf_order_user,1,1,1,0
|
||||
|
@@ -117,11 +117,11 @@
|
||||
<xpath expr="//sheet//group//group//div[3]" position="after">
|
||||
<field name="production_type" readonly="1"/>
|
||||
<field name="manual_quotation" readonly="1"
|
||||
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"/>
|
||||
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工', '人工线下加工'])]}"/>
|
||||
<field name="programming_no" readonly="1"
|
||||
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"/>
|
||||
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工', '人工线下加工'])]}"/>
|
||||
<field name="programming_state" readonly="1"
|
||||
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"
|
||||
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工', '人工线下加工'])]}"
|
||||
decoration-success="programming_state == '已编程'"
|
||||
decoration-warning="programming_state =='编程中'"
|
||||
decoration-danger="programming_state =='已编程未下发'"/>
|
||||
@@ -632,6 +632,8 @@
|
||||
<field name="state" 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"/>
|
||||
<field name="programming_state" icon="fa-filter" enable_counters="1"/>
|
||||
|
||||
</searchpanel>
|
||||
</xpath>
|
||||
<filter name='todo' position="replace"/>
|
||||
|
||||
@@ -168,6 +168,7 @@
|
||||
<field name='is_delivery' invisible="1"/>
|
||||
<field name="is_trayed" invisible="1"/>
|
||||
<field name="is_inspect" invisible="1"/>
|
||||
<!-- <field name="rework_flag" invisible="1"/>-->
|
||||
<!-- <field name='is_send_program_again' invisible="1"/>-->
|
||||
<!-- 工单form页面的开始停工按钮等 -->
|
||||
<!-- <button name="button_start" type="object" string="开始" class="btn-success" -->
|
||||
@@ -216,6 +217,9 @@
|
||||
attrs="{'invisible': ['|', '|', '|', ('routing_type','!=','装夹预调'),('state','!=','progress'), ('is_trayed', '=', False), ('state', 'in', ('done'))]}"/>
|
||||
<button name="print_method" type="object" string="打印二维码" class="btn-primary"
|
||||
attrs="{'invisible': ['|',('routing_type','!=','解除装夹'),('state','!=','done')]}"/>
|
||||
<!-- <button type="object" class="oe_highlight jikimo_button_confirm" name="button_rework"-->
|
||||
<!-- string="返工"-->
|
||||
<!-- attrs='{"invisible": [("rework_flag","=",True)]}' confirm="是否返工"/>-->
|
||||
</xpath>
|
||||
<xpath expr="//page[1]" position="before">
|
||||
<page string="开料要求" attrs='{"invisible": [("routing_type","not in",("切割", "线切割", "人工线下加工"))]}'>
|
||||
@@ -244,7 +248,7 @@
|
||||
invisible="1" sum="real duration"/>
|
||||
<field name="glb_file" readonly="1" widget="Viewer3D" string="加工模型"/>
|
||||
<field name="manual_quotation" readonly="1"
|
||||
attrs="{'invisible': [('routing_type', '!=', 'CNC加工')]}"/>
|
||||
attrs="{'invisible': [('routing_type', 'not in', ['CNC加工', '人工线下加工'])]}"/>
|
||||
<field name="processing_panel" readonly="1"
|
||||
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
|
||||
<field name="equipment_id" readonly="1"
|
||||
@@ -494,7 +498,7 @@
|
||||
</group>
|
||||
</page>
|
||||
|
||||
<page string="2D加工图纸" attrs="{'invisible': [('routing_type','!=','装夹预调')]}">
|
||||
<page string="2D加工图纸" attrs='{"invisible": [("routing_type","not in",["装夹预调", "人工线下加工"])]}'>
|
||||
<field name="machining_drawings" widget="adaptive_viewer"/>
|
||||
</page>
|
||||
|
||||
@@ -566,7 +570,7 @@
|
||||
</page>
|
||||
</xpath>
|
||||
<xpath expr="//page[1]" position="before">
|
||||
<page string="CNC程序" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
|
||||
<page string="CNC程序" attrs='{"invisible": [("routing_type","not in",["CNC加工", "人工线下加工"])]}'>
|
||||
<field name="cnc_ids" widget="one2many" string="工作程序" default_order="sequence_number,id"
|
||||
readonly="0">
|
||||
<tree>
|
||||
@@ -590,7 +594,7 @@
|
||||
</field>
|
||||
|
||||
</page>
|
||||
<page string="CMM程序" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
|
||||
<page string="CMM程序" attrs='{"invisible": [("routing_type","not in",["CNC加工", "人工线下加工"])]}'>
|
||||
<field name="cmm_ids" widget="one2many" string="CMM程序" readonly="1">
|
||||
<tree>
|
||||
<field name="sequence_number"/>
|
||||
@@ -643,7 +647,7 @@
|
||||
<field name="inherit_id" ref="sf_manufacturing.view_mrp_production_workorder_tray_form_inherit_sf"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//form//sheet//group//group[2]" position="replace">
|
||||
<group string="装夹图纸" attrs="{'invisible': [('routing_type', '!=', '装夹预调')]}">
|
||||
<group string="装夹图纸" attrs="{'invisible': [('routing_type', 'not in', ['装夹预调', '人工线下加工'])]}">
|
||||
<!-- 隐藏加工图纸字段名 -->
|
||||
<field name="processing_drawing" widget="pdf_viewer" string="" readonly="1"/>
|
||||
<!-- <field name="production_id" invisible="0"/>-->
|
||||
@@ -786,6 +790,10 @@
|
||||
<filter name="filter_to_be_issued" string="待下发" domain="[('status', 'in', ['待下发'])]"/>
|
||||
<filter name="filter_issued" string="已下发" domain="[('status', 'in', ['已下发'])]"/>
|
||||
<filter name="filter_delivered" string="已配送" domain="[('status', 'in', ['已配送'])]"/>
|
||||
<separator/>
|
||||
<filter name="filter_type_to_production_line" string="上产线" domain="[('type', '=', '上产线')]"/>
|
||||
<filter name="filter_type_to_empty_racks" string="运送空料架" domain="[('type', '=', '运送空料架')]"/>
|
||||
<filter name="filter_type_production_line_back" string="下产线" domain="[('type', '=', '下产线')]"/>
|
||||
<field name="rfid_code"/>
|
||||
<field name="production_id"/>
|
||||
<field name="feeder_station_start_id"/>
|
||||
@@ -808,7 +816,7 @@
|
||||
<field name="res_model">sf.workpiece.delivery</field>
|
||||
<field name="search_view_id" ref="sf_workpiece_delivery_search"/>
|
||||
<field name="context">{'search_default_filter_to_be_issued': 1,
|
||||
'search_default_filter_issued': 1}
|
||||
'search_default_filter_type_to_production_line': 1}
|
||||
</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">
|
||||
|
||||
@@ -19,18 +19,34 @@
|
||||
<field name="supply_method" attrs="{'invisible': [('state', '=', 'draft')], 'required': [('state', '=', 'supply method')]}" />
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='order_line']/tree/field[@name='model_glb_file']" position="before">
|
||||
<field name="part_number" optional="show"/>
|
||||
<field name="part_number" optional="show" class="section_and_note_text"/>
|
||||
</xpath>
|
||||
|
||||
<!-- <xpath expr="//header/button[@name='action_cancel']" position="attributes"> -->
|
||||
<!-- <attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute> -->
|
||||
<!-- </xpath> -->
|
||||
<xpath expr="//header/button[@name='action_cancel']" position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//header/button[@name='action_cancel']" position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute>
|
||||
<attribute name="attrs">{'invisible': [('state', 'not in', ['draft', 'supply method'])]}</attribute>
|
||||
<attribute name="confirm">警告:取消操作将不可逆,是否确定要取消该单据?</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//header/button[@name='action_quotation_send'][5]" position="attributes">
|
||||
<attribute name="attrs">{'invisible': ['|','&',('check_status', '!=', 'approved'),('state', 'in', ['draft','cancel','supply method']),'&',('check_status', '=', 'approved'),('state', 'in', ['sale','cancel','supply method'])]}</attribute>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//header/button[@name='action_cancel']" position="after">
|
||||
<button
|
||||
name="action_show_cancel_wizard"
|
||||
string="取消"
|
||||
type="object"
|
||||
attrs="{'invisible': [('state', 'not in', ['sale', 'processing'])]}"
|
||||
/>
|
||||
<button
|
||||
name="action_show_cancel_wizard"
|
||||
string="取消清单"
|
||||
type="object"
|
||||
attrs="{'invisible': [('state', 'not in', ['cancel'])]}"
|
||||
/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -5,3 +5,4 @@ from . import production_technology_wizard
|
||||
from . import production_technology_re_adjust_wizard
|
||||
from . import mrp_workorder_batch_replan_wizard
|
||||
from . import sf_programming_reason
|
||||
from . import sale_order_cancel
|
||||
|
||||
@@ -35,6 +35,7 @@ class ReworkWizard(models.TransientModel):
|
||||
is_reprogramming = fields.Boolean(string='申请重新编程', default=False)
|
||||
is_reprogramming_readonly = fields.Boolean(string='申请重新编程(只读)', default=False)
|
||||
is_clamp_measure = fields.Boolean(string='保留装夹测量数据', default=True)
|
||||
is_clamping = fields.Boolean(string='制造订单是否存在装夹预调工单')
|
||||
reprogramming_num = fields.Integer('重新编程次数', default=0)
|
||||
programming_state = fields.Selection(
|
||||
[('待编程', '待编程'), ('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'),
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<field name="routing_type" invisible="True"/>
|
||||
<field name="processing_panel_id" invisible="1"/>
|
||||
<field name="hidden_workorder_ids" class="css_not_available_msg"/>
|
||||
<field name="is_clamping" invisible="1"/>
|
||||
<group>
|
||||
<field name="hidden_workorder_ids" invisible="1"/>
|
||||
<field options="{'no_create': True,'no_open': True}" readonly="1" name="workorder_ids"
|
||||
@@ -26,7 +27,7 @@
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
<div attrs='{"invisible": [("routing_type","=","装夹预调")]}'>
|
||||
<div attrs='{"invisible": ["|", ("routing_type","=","装夹预调"), ("is_clamping", "=", False)]}'>
|
||||
<span style='font-weight:bold;'>保留装夹测量数据
|
||||
<field name="is_clamp_measure" force_save="1"/>
|
||||
</span>
|
||||
|
||||
394
sf_manufacturing/wizard/sale_order_cancel.py
Normal file
394
sf_manufacturing/wizard/sale_order_cancel.py
Normal file
@@ -0,0 +1,394 @@
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class SFSaleOrderCancelWizard(models.TransientModel):
|
||||
_name = 'sf.sale.order.cancel.wizard'
|
||||
_description = '销售订单取消向导'
|
||||
|
||||
order_id = fields.Many2one('sale.order', string='销售订单')
|
||||
related_docs = fields.One2many('sf.sale.order.cancel.line', 'wizard_id', string='相关单据')
|
||||
has_movement = fields.Boolean(compute='_compute_has_movement', string='是否有异动')
|
||||
display_message = fields.Char(compute='_compute_display_message', string='显示消息')
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields_list):
|
||||
defaults = super().default_get(fields_list)
|
||||
if self._context.get('active_id'):
|
||||
order = self.env['sale.order'].browse(self._context.get('active_id'))
|
||||
defaults['order_id'] = order.id
|
||||
# 创建向导时自动创建关联单据行
|
||||
wizard = self.create(defaults)
|
||||
self.env['sf.sale.order.cancel.line'].create_from_order(wizard.id, order)
|
||||
defaults['related_docs'] = wizard.related_docs.ids
|
||||
return defaults
|
||||
|
||||
@api.depends('related_docs.cancel_reason')
|
||||
def _compute_has_movement(self):
|
||||
for wizard in self:
|
||||
docs_has_movement = any(doc.cancel_reason for doc in wizard.related_docs)
|
||||
order_canceled = wizard.order_id.state == 'cancel'
|
||||
wizard.has_movement = docs_has_movement or order_canceled
|
||||
|
||||
@api.depends('has_movement', 'related_docs', 'related_docs.doc_state')
|
||||
def _compute_display_message(self):
|
||||
for wizard in self:
|
||||
# 如果没有相关记录,显示为空
|
||||
if not wizard.related_docs:
|
||||
wizard.display_message = '无下游单据'
|
||||
continue
|
||||
|
||||
# 检查是否所有记录都是已取消状态
|
||||
all_canceled = all(doc.doc_state == '已取消' for doc in wizard.related_docs)
|
||||
if all_canceled:
|
||||
wizard.display_message = '取消的下游单据如下:'
|
||||
else:
|
||||
wizard.display_message = '部分或全部下游单据存在异动,无法取消,详情如下:' if wizard.has_movement else '确认所有下游单据全部取消?'
|
||||
|
||||
def action_confirm_cancel(self):
|
||||
self.ensure_one()
|
||||
|
||||
# 取消销售订单关联的采购单
|
||||
purchase_orders = self.env['purchase.order'].search([
|
||||
('origin', '=', self.order_id.name)
|
||||
])
|
||||
if purchase_orders:
|
||||
purchase_orders.write({'state': 'cancel'})
|
||||
|
||||
# 取消销售订单
|
||||
result = self.order_id.action_cancel()
|
||||
|
||||
# 取消关联的制造订单及其采购单
|
||||
manufacturing_orders = self.env['mrp.production'].search([
|
||||
('origin', '=', self.order_id.name)
|
||||
])
|
||||
for mo in manufacturing_orders:
|
||||
# 取消制造订单关联的采购单,但保持关联关系
|
||||
mo_purchase_orders = self.env['purchase.order'].search([
|
||||
('origin', '=', mo.name)
|
||||
])
|
||||
if mo_purchase_orders:
|
||||
mo_purchase_orders.write({'state': 'cancel'})
|
||||
|
||||
# 取消制造订单的质检单
|
||||
mo_quality_checks = self.env['quality.check'].search([
|
||||
('production_id', '=', mo.id)
|
||||
])
|
||||
if mo_quality_checks:
|
||||
mo_quality_checks.write({'quality_state': 'cancel'})
|
||||
|
||||
# 取消制造订单
|
||||
mo.action_cancel()
|
||||
|
||||
# 取消制造订单关联的编程单
|
||||
mo._change_programming_state()
|
||||
|
||||
# 取消组件的制造单关联的采购单
|
||||
for comp_mo in self.env['mrp.production'].search([
|
||||
('origin', '=', mo.name)
|
||||
]):
|
||||
comp_purchase_orders = self.env['purchase.order'].search([
|
||||
('origin', '=', comp_mo.name)
|
||||
])
|
||||
if comp_purchase_orders:
|
||||
comp_purchase_orders.button_cancel()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class SFSaleOrderCancelLine(models.TransientModel):
|
||||
_name = 'sf.sale.order.cancel.line'
|
||||
_description = '销售订单取消行'
|
||||
|
||||
wizard_id = fields.Many2one('sf.sale.order.cancel.wizard')
|
||||
sequence = fields.Integer('序号')
|
||||
category = fields.Char('大类')
|
||||
doc_name = fields.Char('单据名称')
|
||||
operation_type = fields.Char('作业类型')
|
||||
doc_number = fields.Char('单据编号')
|
||||
line_number = fields.Char('行号')
|
||||
product_name = fields.Char('产品名称')
|
||||
quantity = fields.Float('数量')
|
||||
doc_state = fields.Char('单据状态')
|
||||
cancel_reason = fields.Char('禁止取消原因')
|
||||
|
||||
@api.model
|
||||
def create_from_order(self, wizard_id, order):
|
||||
sequence = 1
|
||||
lines = []
|
||||
map_dict = {
|
||||
'waiting': '等待其他作业',
|
||||
'to approve': '待批准',
|
||||
'technology_to_confirmed': '待工艺确认',
|
||||
'confirmed': '已确认',
|
||||
'pending': '等待其他工单',
|
||||
'none': '待处理',
|
||||
'draft': '询价',
|
||||
'cancel': '已取消',
|
||||
'pass': '通过的',
|
||||
'fail': '失败的',
|
||||
'done': '已完成',
|
||||
'rework': '返工',
|
||||
'purchase': '采购订单',
|
||||
'ready': '就绪',
|
||||
'approved': '已批准',
|
||||
'pending_cam': '待加工',
|
||||
'progress': '加工中',
|
||||
'assigned': '就绪'
|
||||
}
|
||||
|
||||
# 检查销售订单
|
||||
if order.invoice_ids:
|
||||
a = 0
|
||||
for invoice in order.invoice_ids:
|
||||
a += 1
|
||||
vals = {
|
||||
'wizard_id': wizard_id,
|
||||
'sequence': sequence,
|
||||
'category': '销售',
|
||||
'doc_name': '销售订单',
|
||||
'operation_type': '',
|
||||
'doc_number': invoice.name,
|
||||
'line_number': a,
|
||||
'product_name': invoice.product_id.name,
|
||||
'quantity': invoice.quantity,
|
||||
'doc_state': invoice.state,
|
||||
'cancel_reason': '已有异动' if invoice.state != 'draft' else ''
|
||||
}
|
||||
lines.append(self.create(vals))
|
||||
sequence += 1
|
||||
|
||||
# 检查交货单
|
||||
if order.picking_ids:
|
||||
b = 0
|
||||
for picking in order.picking_ids:
|
||||
b += 1
|
||||
vals = {
|
||||
'wizard_id': wizard_id,
|
||||
'sequence': sequence,
|
||||
'category': '库存',
|
||||
'doc_name': '交货单',
|
||||
'operation_type': '调拨',
|
||||
'doc_number': picking.name,
|
||||
'line_number': b,
|
||||
'product_name': picking.product_id.name if picking.product_id else '',
|
||||
# 'quantity': picking.product_qty if hasattr(picking, 'product_qty') else 0,
|
||||
'quantity': sum(picking.move_ids.mapped('product_uom_qty') or [0]),
|
||||
'doc_state': map_dict.get(picking.state, picking.state),
|
||||
'cancel_reason': '已有异动' if picking.state not in ['draft', 'cancel', 'waiting'] else ''
|
||||
}
|
||||
lines.append(self.create(vals))
|
||||
sequence += 1
|
||||
|
||||
# 检查销售订单直接关联的采购单
|
||||
purchase_orders = self.env['purchase.order'].search([
|
||||
('origin', '=', order.name)
|
||||
])
|
||||
if purchase_orders:
|
||||
c = 0
|
||||
for po in purchase_orders:
|
||||
c += 1
|
||||
vals = {
|
||||
'wizard_id': wizard_id,
|
||||
'sequence': sequence,
|
||||
'category': '采购',
|
||||
'doc_name': '询价单',
|
||||
'operation_type': po.picking_type_id.name,
|
||||
'doc_number': po.name,
|
||||
'line_number': c,
|
||||
'product_name': po.order_line[0].product_id.name if po.order_line else '',
|
||||
'quantity': po.order_line[0].product_qty if po.order_line else 0,
|
||||
'doc_state': map_dict.get(po.state, po.state),
|
||||
'cancel_reason': '已有异动' if po.state not in ['draft', 'cancel'] else ''
|
||||
}
|
||||
lines.append(self.create(vals))
|
||||
sequence += 1
|
||||
|
||||
# 检查制造订单
|
||||
manufacturing_orders = self.env['mrp.production'].search([
|
||||
('origin', '=', order.name)
|
||||
])
|
||||
d = 0
|
||||
for mo in manufacturing_orders:
|
||||
# 添加制造订单本身
|
||||
d += 1
|
||||
vals = {
|
||||
'wizard_id': wizard_id,
|
||||
'sequence': sequence,
|
||||
'category': '制造',
|
||||
'doc_name': '制造订单',
|
||||
'doc_number': mo.name,
|
||||
'operation_type': '',
|
||||
'line_number': d,
|
||||
'product_name': mo.product_id.name,
|
||||
'quantity': mo.product_qty,
|
||||
'doc_state': map_dict.get(mo.state, mo.state),
|
||||
'cancel_reason': '已有异动' if mo.state not in ['technology_to_confirmed', 'cancel'] else ''
|
||||
}
|
||||
lines.append(self.create(vals))
|
||||
sequence += 1
|
||||
|
||||
# 检查制造订单关联的采购单
|
||||
purchase_orders = self.env['purchase.order'].search([
|
||||
('origin', '=', mo.name)
|
||||
])
|
||||
if purchase_orders:
|
||||
e = 0
|
||||
for po in purchase_orders:
|
||||
e += 1
|
||||
vals = {
|
||||
'wizard_id': wizard_id,
|
||||
'sequence': sequence,
|
||||
'category': '制造',
|
||||
'doc_name': '询价单',
|
||||
'doc_number': po.name,
|
||||
'line_number': e,
|
||||
'operation_type': po.picking_type_id.name,
|
||||
'product_name': po.order_line[0].product_id.name if po.order_line else '',
|
||||
'quantity': po.order_line[0].product_qty if po.order_line else 0,
|
||||
'doc_state': map_dict.get(po.state, po.state),
|
||||
'cancel_reason': '已有异动' if po.state not in ['draft', 'cancel'] else ''
|
||||
}
|
||||
lines.append(self.create(vals))
|
||||
sequence += 1
|
||||
|
||||
# 检查制造订单的领料单
|
||||
if mo.picking_ids:
|
||||
f = 0
|
||||
for picking in mo.picking_ids:
|
||||
f += 1
|
||||
vals = {
|
||||
'wizard_id': wizard_id,
|
||||
'sequence': sequence,
|
||||
'category': '制造',
|
||||
'doc_name': '库存移动',
|
||||
'doc_number': picking.name,
|
||||
'line_number': f,
|
||||
'operation_type': picking.picking_type_id.name,
|
||||
'product_name': picking.product_id.name if picking.product_id else '',
|
||||
'quantity': sum(picking.move_ids.mapped('product_uom_qty') or [0]),
|
||||
'doc_state': map_dict.get(picking.state, picking.state),
|
||||
'cancel_reason': '已有异动' if picking.state not in ['draft', 'cancel', 'waiting'] else ''
|
||||
}
|
||||
lines.append(self.create(vals))
|
||||
sequence += 1
|
||||
|
||||
# 检查制造订单的工单
|
||||
if mo.workorder_ids:
|
||||
g = 0
|
||||
for workorder in mo.workorder_ids:
|
||||
g += 1
|
||||
vals = {
|
||||
'wizard_id': wizard_id,
|
||||
'sequence': sequence,
|
||||
'category': '制造',
|
||||
'doc_name': '工单',
|
||||
'doc_number': workorder.name,
|
||||
'line_number': g,
|
||||
'operation_type': workorder.workcenter_id.name,
|
||||
'product_name': mo.product_id.name,
|
||||
'quantity': workorder.qty_production,
|
||||
'doc_state': map_dict.get(workorder.state, workorder.state),
|
||||
'cancel_reason': '已有异动' if workorder.state not in ['draft', 'cancel', 'pending',
|
||||
'waiting'] else ''
|
||||
}
|
||||
lines.append(self.create(vals))
|
||||
sequence += 1
|
||||
|
||||
# 检查制造订单组件的采购单和制造单
|
||||
for move in mo.move_raw_ids:
|
||||
# # 检查组件的采购单
|
||||
# component_pos = self.env['purchase.order'].search([
|
||||
# ('origin', '=', mo.name),
|
||||
# ('order_line.product_id', '=', move.product_id.id)
|
||||
# ])
|
||||
# for po in component_pos:
|
||||
# vals = {
|
||||
# 'wizard_id': wizard_id,
|
||||
# 'sequence': sequence,
|
||||
# 'category': '制造',
|
||||
# 'doc_name': '组件采购单',
|
||||
# 'operation_type': '组件采购',
|
||||
# 'doc_number': po.name,
|
||||
# 'product_name': move.product_id.name,
|
||||
# 'quantity': po.order_line[0].product_qty if po.order_line else 0,
|
||||
# 'doc_state': po.state,
|
||||
# 'cancel_reason': '已有异动' if po.state not in ['draft', 'cancel'] else ''
|
||||
# }
|
||||
# lines.append(self.create(vals))
|
||||
# sequence += 1
|
||||
|
||||
# 检查组件的制造单
|
||||
component_mos = self.env['mrp.production'].search([
|
||||
('origin', '=', mo.name),
|
||||
('product_id', '=', move.product_id.id)
|
||||
])
|
||||
h = 0
|
||||
for comp_mo in component_mos:
|
||||
h += 1
|
||||
vals = {
|
||||
'wizard_id': wizard_id,
|
||||
'sequence': sequence,
|
||||
'category': '制造',
|
||||
'doc_name': '组件制造单',
|
||||
'operation_type': '',
|
||||
'doc_number': comp_mo.name,
|
||||
'line_number': h,
|
||||
'product_name': move.product_id.name,
|
||||
'quantity': comp_mo.product_qty,
|
||||
'doc_state': map_dict.get(comp_mo.state, comp_mo.state),
|
||||
'cancel_reason': '已有异动' if comp_mo.state not in ['technology_to_confirmed'] else ''
|
||||
}
|
||||
lines.append(self.create(vals))
|
||||
sequence += 1
|
||||
|
||||
# 检查制造订单的质检单
|
||||
quality_checks = self.env['quality.check'].search([
|
||||
('production_id', '=', mo.id)
|
||||
])
|
||||
if quality_checks:
|
||||
i = 0
|
||||
for check in quality_checks:
|
||||
i += 1
|
||||
vals = {
|
||||
'wizard_id': wizard_id,
|
||||
'sequence': sequence,
|
||||
'category': '制造',
|
||||
'doc_name': '质检单',
|
||||
'operation_type': '',
|
||||
'doc_number': check.name,
|
||||
'line_number': i,
|
||||
'product_name': check.product_id.name,
|
||||
'quantity': 1,
|
||||
'doc_state': map_dict.get(check.quality_state, check.quality_state),
|
||||
'cancel_reason': '已有异动' if check.quality_state not in ['none', 'cancel'] else ''
|
||||
}
|
||||
lines.append(self.create(vals))
|
||||
sequence += 1
|
||||
|
||||
# 检查制造订单的编程单
|
||||
cloud_programming = mo._cron_get_programming_state()
|
||||
if cloud_programming:
|
||||
vals = {
|
||||
'wizard_id': wizard_id,
|
||||
'sequence': sequence,
|
||||
'category': '编程',
|
||||
'doc_name': '编程单',
|
||||
'operation_type': '',
|
||||
'doc_number': cloud_programming['programming_no'],
|
||||
'line_number': 1,
|
||||
'product_name': cloud_programming['production_order_no'],
|
||||
'quantity': 1,
|
||||
'doc_state': cloud_programming['programming_state'],
|
||||
'cancel_reason': ''
|
||||
}
|
||||
lines.append(self.create(vals))
|
||||
sequence += 1
|
||||
|
||||
unique_lines = {}
|
||||
for line in lines:
|
||||
doc_number = line.doc_number
|
||||
if doc_number not in unique_lines:
|
||||
unique_lines[doc_number] = line
|
||||
|
||||
# 返回去重后的记录列表
|
||||
return list(unique_lines.values())
|
||||
42
sf_manufacturing/wizard/sale_order_cancel_views.xml
Normal file
42
sf_manufacturing/wizard/sale_order_cancel_views.xml
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_sf_sale_order_cancel_wizard" model="ir.ui.view">
|
||||
<field name="name">sf.sale.order.cancel.wizard.form</field>
|
||||
<field name="model">sf.sale.order.cancel.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="下游单据清单">
|
||||
<group>
|
||||
<field name="order_id" invisible="1"/>
|
||||
<field name="has_movement" invisible="1"/>
|
||||
</group>
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<field name="display_message" readonly="1" nolabel="1"/>
|
||||
</div>
|
||||
<field name="related_docs">
|
||||
<tree string="下游单据" create="false" edit="false" delete="false">
|
||||
<!-- <field name="sequence" string="序号"/> -->
|
||||
<field name="category" string="大类"/>
|
||||
<field name="doc_name" string="单据名称"/>
|
||||
<field name="operation_type" string="作业类型"/>
|
||||
<field name="doc_number" string="单据编号"/>
|
||||
<field name="line_number" string="行号"/>
|
||||
<field name="product_name" string="产品名称"/>
|
||||
<field name="quantity" string="数量"/>
|
||||
<field name="doc_state" string="单据状态"/>
|
||||
<field name="cancel_reason" string="禁止取消原因"/>
|
||||
</tree>
|
||||
</field>
|
||||
<footer>
|
||||
<button name="action_confirm_cancel"
|
||||
string="确认取消"
|
||||
type="object"
|
||||
class="btn-primary"
|
||||
attrs="{'invisible': [('has_movement', '=', True)]}"/>
|
||||
<button string="关闭"
|
||||
class="btn-secondary"
|
||||
special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -54,10 +54,7 @@ class WorkpieceDeliveryWizard(models.TransientModel):
|
||||
}
|
||||
}
|
||||
|
||||
def _get_agv_route_type_selection(self):
|
||||
return self.env['sf.agv.task.route'].fields_get(['route_type'])['route_type']['selection']
|
||||
|
||||
delivery_type = fields.Selection(selection=_get_agv_route_type_selection, string='类型')
|
||||
delivery_type = fields.Selection(related='route_id.route_type', string='类型')
|
||||
|
||||
def dispatch_confirm(self):
|
||||
if len(self.workorder_ids) < 4:
|
||||
|
||||
Reference in New Issue
Block a user