Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化
This commit is contained in:
@@ -20,7 +20,9 @@ class AgvScheduling(models.Model):
|
||||
|
||||
name = fields.Char('任务单号', index=True, copy=False)
|
||||
agv_route_id = fields.Many2one('sf.agv.task.route', '任务路线')
|
||||
agv_route_type = fields.Selection(related='agv_route_id.route_type', string='任务类型', required=True)
|
||||
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)
|
||||
start_site_id = fields.Many2one('sf.agv.site', '起点接驳站', required=True)
|
||||
end_site_id = fields.Many2one('sf.agv.site', '终点接驳站', tracking=True)
|
||||
site_state = fields.Selection([
|
||||
|
||||
@@ -69,6 +69,50 @@ class ResMrpWorkOrder(models.Model):
|
||||
|
||||
delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效',
|
||||
tracking=True)
|
||||
back_button_display = fields.Boolean(default=False, compute='_compute_back_button_display', store=True)
|
||||
|
||||
@api.depends('state')
|
||||
def _compute_back_button_display(self):
|
||||
for record in self:
|
||||
sorted_workorders = record.production_id.workorder_ids.filtered(lambda w: w.state != 'cancel').sorted(
|
||||
key=lambda w: w.sequence)
|
||||
if not sorted_workorders:
|
||||
continue
|
||||
position = next((idx for idx, workorder in enumerate(sorted_workorders) if workorder.id == record.id), -1)
|
||||
cur_workorder = sorted_workorders[position]
|
||||
if position == len(sorted_workorders) - 1:
|
||||
picking_ids = cur_workorder.production_id.sale_order_id.picking_ids
|
||||
finished_product_area = picking_ids.filtered(
|
||||
lambda picking: picking.location_dest_id.name == '成品存货区' and picking.state == 'done'
|
||||
)
|
||||
if finished_product_area:
|
||||
moves = self.env['stock.move'].search([
|
||||
('name', '=', cur_workorder.production_id.name),
|
||||
('state', '!=', 'cancel')
|
||||
])
|
||||
finish_move = next((move for move in moves if move.location_dest_id.name == '制造后'), None)
|
||||
if not finish_move and not cur_workorder.is_subcontract and not cur_workorder.name =='解除装夹':
|
||||
record.back_button_display = True
|
||||
else:
|
||||
record.back_button_display = any(
|
||||
finish_move.move_dest_ids.ids not in move.ids and record.state == 'done'
|
||||
for picking in finished_product_area
|
||||
for move in picking.move_ids
|
||||
)
|
||||
else:
|
||||
if record.state == 'done':
|
||||
record.back_button_display = True
|
||||
else:
|
||||
record.back_button_display = False
|
||||
else:
|
||||
next_workorder = sorted_workorders[position + 1]
|
||||
next_state = next_workorder.state
|
||||
if ((next_state == 'ready' and not next_workorder.is_subcontract and not next_workorder.name =='解除装夹') or (next_workorder.state == 'pending' and next_workorder.is_subcontract and not next_workorder.name =='解除装夹')) and cur_workorder.state == 'done':
|
||||
record.back_button_display = True
|
||||
else:
|
||||
record.back_button_display = False
|
||||
if cur_workorder.is_subcontract or cur_workorder.name =='解除装夹':
|
||||
record.back_button_display = False
|
||||
date_planned_start = fields.Datetime(tracking=True)
|
||||
|
||||
@api.depends('processing_panel')
|
||||
@@ -86,6 +130,82 @@ class ResMrpWorkOrder(models.Model):
|
||||
|
||||
manual_quotation = fields.Boolean('人工编程', default=False, compute=_compute_manual_quotation, store=True)
|
||||
|
||||
def button_back(self):
|
||||
if self.production_id.state == 'rework':
|
||||
raise UserError('制造订单为返工时不能进行工单回退')
|
||||
sorted_workorders = self.production_id.workorder_ids.filtered(lambda w: w.state != 'cancel').sorted(
|
||||
key=lambda w: w.sequence)
|
||||
position = next((idx for idx, workorder in enumerate(sorted_workorders) if workorder.id == self.id), -1)
|
||||
cur_workorder = sorted_workorders[position]
|
||||
if position == len(sorted_workorders) - 1:
|
||||
# 末工序
|
||||
picking_ids = cur_workorder.production_id.sale_order_id.picking_ids
|
||||
finished_product_area = picking_ids.filtered(
|
||||
lambda picking: picking.location_dest_id.name == '成品存货区' and picking.state == 'done'
|
||||
)
|
||||
moves = self.env['stock.move'].search([
|
||||
('name', '=', cur_workorder.production_id.name),
|
||||
('state', '!=', 'cancel')
|
||||
])
|
||||
finish_move = next((move for move in moves if move.location_dest_id.name == '制造后'), None) or []
|
||||
if any(
|
||||
finish_move.move_dest_ids.ids in move.ids
|
||||
for picking in finished_product_area
|
||||
for move in picking.move_ids
|
||||
):
|
||||
raise UserError('已入库,无法回退')
|
||||
else:
|
||||
moves = self.env['stock.move'].search([
|
||||
('name', '=', cur_workorder.production_id.name),
|
||||
('state', '!=', 'cancel')
|
||||
])
|
||||
move_lines = self.env['stock.move.line'].search([
|
||||
('reference', '=', cur_workorder.production_id.name),
|
||||
('state', '!=', 'cancel')
|
||||
])
|
||||
moves.state = 'assigned'
|
||||
external_assistance = move_lines.filtered(
|
||||
lambda picking: picking.location_id.name != '外协线边仓'
|
||||
)
|
||||
external_assistance.state = 'assigned'
|
||||
# move_lines.state = 'assigned'
|
||||
self.time_ids.date_end = None
|
||||
cur_workorder.state = 'progress'
|
||||
cur_workorder.production_id.state = 'progress'
|
||||
quality_check = self.env['quality.check'].search(
|
||||
[('workorder_id', '=', self.id)])
|
||||
for check_order in quality_check:
|
||||
if check_order.point_id.is_inspect:
|
||||
check_order.quality_state = 'waiting'
|
||||
else:
|
||||
check_order.quality_state = 'none'
|
||||
# move_dest_ids
|
||||
finished_quants = moves.mapped('move_line_ids.lot_id.quant_ids')
|
||||
finished_quants.quantity = 0
|
||||
finish_move = next((move for move in moves if move.location_dest_id.name == '制造后'), None)
|
||||
finish_move.move_dest_ids.reserved_availability = 0
|
||||
finish_move.move_dest_ids.move_line_ids.state = 'draft'
|
||||
finish_move.move_dest_ids.move_line_ids.unlink()
|
||||
# finish_move.move_dest_ids.move_line_ids.reserved_uom_qty = 0
|
||||
else:
|
||||
next_workorder = sorted_workorders[position + 1]
|
||||
next_state = next_workorder.state
|
||||
if next_state not in ['pending', 'waiting', 'ready']:
|
||||
raise UserError('下工序已经开始,无法回退')
|
||||
if next_workorder.is_subcontract:
|
||||
next_workorder.picking_ids.write({'state': 'waiting'})
|
||||
next_workorder.state = 'pending'
|
||||
self.time_ids.date_end = None
|
||||
cur_workorder.state = 'progress'
|
||||
cur_workorder.production_id.state = 'progress'
|
||||
quality_check = self.env['quality.check'].search(
|
||||
[('workorder_id', '=', self.id)])
|
||||
for check_order in quality_check:
|
||||
if check_order.point_id.is_inspect:
|
||||
check_order.quality_state = 'waiting'
|
||||
else:
|
||||
check_order.quality_state = 'none'
|
||||
|
||||
def _compute_working_users(self):
|
||||
super()._compute_working_users()
|
||||
for item in self:
|
||||
@@ -1860,7 +1980,10 @@ class WorkPieceDelivery(models.Model):
|
||||
feeder_station_destination_id = fields.Many2one('sf.agv.site', '目的接驳站')
|
||||
task_delivery_time = fields.Datetime('任务下发时间')
|
||||
task_completion_time = fields.Datetime('任务完成时间')
|
||||
type = fields.Selection(related='route_id.route_type', string='类型')
|
||||
|
||||
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='类型')
|
||||
delivery_duration = fields.Float('配送时长', compute='_compute_delivery_duration')
|
||||
status = fields.Selection(
|
||||
[('待下发', '待下发'), ('已下发', '待配送'), ('已配送', '已配送'), ('已取消', '已取消')], string='状态',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import logging
|
||||
import json
|
||||
from odoo import models, fields, api
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
@@ -24,6 +24,8 @@ class SaleOrder(models.Model):
|
||||
self.state = 'supply method'
|
||||
|
||||
def action_confirm(self):
|
||||
if self._get_forbidden_state_confirm() & set(self.mapped('state')):
|
||||
raise UserError(_('订单状态已发生变化,请刷新当前页面'))
|
||||
# 判断是否所有产品都选择了供货方式
|
||||
filter_line = self.order_line.filtered(lambda line: not line.supply_method)
|
||||
if filter_line:
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import base64
|
||||
import random
|
||||
import re
|
||||
|
||||
import qrcode
|
||||
from itertools import groupby
|
||||
from collections import defaultdict, namedtuple
|
||||
@@ -738,25 +741,26 @@ class ReStockMove(models.Model):
|
||||
move.part_number = move.product_id.part_number
|
||||
move.part_name = move.product_id.part_name
|
||||
elif move.product_id.categ_id.type == '坯料':
|
||||
if move.origin:
|
||||
origin = move.origin.split(',')[0] if ',' in move.origin else move.origin
|
||||
mrp_productio_info = self.env['mrp.production'].sudo().search(
|
||||
[('name', '=', origin)])
|
||||
if mrp_productio_info:
|
||||
move.part_number = mrp_productio_info.part_number
|
||||
move.part_name = mrp_productio_info.part_name
|
||||
else:
|
||||
purchase_order_info = self.env['purchase.order'].sudo().search(
|
||||
[('name', '=', origin)])
|
||||
if purchase_order_info:
|
||||
mrp_production_ids = purchase_order_info._get_mrp_productions().ids
|
||||
if mrp_production_ids:
|
||||
mrp_productio_info = self.env['mrp.production'].sudo().search(
|
||||
[('id', '=', mrp_production_ids[0])])
|
||||
if mrp_productio_info:
|
||||
move.part_number = mrp_productio_info.part_number
|
||||
move.part_name = mrp_productio_info.part_name
|
||||
|
||||
product_name = ''
|
||||
match = re.search(r'(S\d{5}-\d)', move.product_id.name)
|
||||
# 如果匹配成功,提取结果
|
||||
if match:
|
||||
product_name = match.group(0)
|
||||
if move.picking_id.sale_order_id:
|
||||
sale_order = move.picking_id.sale_order_id
|
||||
else:
|
||||
sale_order_name = ''
|
||||
match = re.search(r'(S\d+)', move.product_id.name)
|
||||
if match:
|
||||
sale_order_name = match.group(0)
|
||||
sale_order = self.env['sale.order'].sudo().search(
|
||||
[('name', '=', sale_order_name)])
|
||||
filtered_order_line = sale_order.order_line.filtered(
|
||||
lambda production: product_name in production.product_id.name
|
||||
)
|
||||
if filtered_order_line:
|
||||
move.part_number = filtered_order_line.part_number
|
||||
move.part_name = filtered_order_line.part_name
|
||||
def _get_stock_move_values_Res(self, item, picking_type_id, group_id, move_dest_ids=False):
|
||||
route_id = self.env.ref('sf_manufacturing.route_surface_technology_outsourcing').id
|
||||
stock_rule = self.env['stock.rule'].sudo().search(
|
||||
|
||||
@@ -795,7 +795,7 @@
|
||||
groups="sf_base.group_plan_dispatch,sf_base.group_sf_mrp_manager"
|
||||
sequence="1"/>
|
||||
|
||||
<menuitem id="stock_picking_type_menu"
|
||||
<menuitem id="stock.stock_picking_type_menu"
|
||||
name="驾驶舱"
|
||||
parent="stock.menu_stock_root"
|
||||
action="stock.stock_picking_type_action"
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='date_planned_finished']" position="replace">
|
||||
<field name="date_planned_finished" string="计划结束日期" optional="hide"/>
|
||||
<field name="back_button_display" invisible="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='button_start']" position="attributes">
|
||||
<!-- <attribute name="attrs">{'invisible': ['|', '|', '|','|','|', ('production_state','in', ('draft',-->
|
||||
@@ -58,6 +59,11 @@
|
||||
<attribute name="attrs">{'invisible': [('state', '!=', 'ready')]}
|
||||
</attribute>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//button[@name='button_start']" position="after">
|
||||
<button name="button_back" string="回退" type="object" class="btn-primary"
|
||||
attrs="{'invisible': [('back_button_display', '=', False)]}" confirm="是否确认回退"/>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='%(mrp.act_mrp_block_workcenter_wo)d']" position="attributes">
|
||||
<attribute name="attrs">{'invisible':
|
||||
['|',("user_permissions","=",False),("name","=","获取CNC加工程序")]}
|
||||
@@ -163,7 +169,8 @@
|
||||
<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="back_button_display" 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" -->
|
||||
@@ -178,6 +185,8 @@
|
||||
|
||||
<!-- <button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"-->
|
||||
<!-- 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_back" string="回退" type="object" class="btn-primary"
|
||||
attrs="{'invisible': [('back_button_display', '=', False)]}" confirm="是否确认回退"/>
|
||||
<button name="button_start" type="object" string="开始" class="btn-success"
|
||||
attrs="{'invisible': [('state', '!=', 'ready')]}"/>
|
||||
<button name="button_pending" type="object" string="暂停" class="btn-warning"
|
||||
@@ -212,12 +221,13 @@
|
||||
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="是否返工"/>-->
|
||||
<!-- <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",("切割", "线切割", "人工线下加工"))]}'>
|
||||
<page string="开料要求"
|
||||
attrs='{"invisible": [("routing_type","not in",("切割", "线切割", "人工线下加工"))]}'>
|
||||
<group>
|
||||
<group>
|
||||
<field name="product_tmpl_id_materials_id" widget="many2one"/>
|
||||
@@ -258,8 +268,8 @@
|
||||
<field name='tag_type' readonly="1" attrs='{"invisible": [("tag_type","=",False)]}'
|
||||
decoration-danger="tag_type == '重新加工'"/>
|
||||
<field name="is_test_env" invisible="1"/>
|
||||
<field name="rfid_code" force_save="1" readonly="1" cache="True"
|
||||
attrs="{'invisible': [('rfid_code_old', '!=', False)]}" widget="qrcode_widget"/>
|
||||
<field name="rfid_code" force_save="1" readonly="1" cache="True"
|
||||
attrs="{'invisible': [('rfid_code_old', '!=', False)]}" widget="qrcode_widget"/>
|
||||
<field name="rfid_code" string="RFID码(手动输入框)" force_save="1" readonly="0" cache="True"
|
||||
attrs="{'invisible': ['|',('rfid_code_old', '!=', False), ('is_test_env', '=', False)]}"/>
|
||||
<field name="rfid_code_old" readonly="1" attrs="{'invisible': [('rfid_code_old', '=', False)]}"/>
|
||||
@@ -316,7 +326,7 @@
|
||||
<xpath expr="//page[1]" position="before">
|
||||
<page string="工件装夹" attrs='{"invisible": [("routing_type","!=","装夹预调")]}'>
|
||||
<group>
|
||||
<!-- <field name="_barcode_scanned" widget="barcode_handler"/> -->
|
||||
<!-- <field name="_barcode_scanned" widget="barcode_handler"/> -->
|
||||
<group string="托盘">
|
||||
<field name="tray_serial_number" readonly="1" string="序列号"/>
|
||||
</group>
|
||||
@@ -493,7 +503,8 @@
|
||||
</group>
|
||||
</page>
|
||||
|
||||
<page string="2D加工图纸" attrs='{"invisible": [("routing_type","not in",["装夹预调", "人工线下加工"])]}'>
|
||||
<page string="2D加工图纸"
|
||||
attrs='{"invisible": [("routing_type","not in",["装夹预调", "人工线下加工"])]}'>
|
||||
<field name="machining_drawings" widget="adaptive_viewer"/>
|
||||
</page>
|
||||
|
||||
@@ -642,7 +653,8 @@
|
||||
<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', 'not in', ['装夹预调', '人工线下加工'])]}">
|
||||
<group string="装夹图纸"
|
||||
attrs="{'invisible': [('routing_type', 'not in', ['装夹预调', '人工线下加工'])]}">
|
||||
<!-- 隐藏加工图纸字段名 -->
|
||||
<field name="processing_drawing" widget="pdf_viewer" string="" readonly="1"/>
|
||||
<!-- <field name="production_id" invisible="0"/>-->
|
||||
|
||||
@@ -54,7 +54,9 @@ class WorkpieceDeliveryWizard(models.TransientModel):
|
||||
}
|
||||
}
|
||||
|
||||
delivery_type = fields.Selection(related='route_id.route_type', string='类型')
|
||||
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='类型')
|
||||
|
||||
def dispatch_confirm(self):
|
||||
if len(self.workorder_ids) < 4:
|
||||
|
||||
Reference in New Issue
Block a user