Merge remote-tracking branch 'origin/develop' into feature/仓库发货下发bfm

This commit is contained in:
hujiaying
2024-09-05 09:38:40 +08:00
36 changed files with 819 additions and 539 deletions

View File

@@ -324,4 +324,4 @@ def unlink(self):
BaseModel._create = _create BaseModel._create = _create
BaseModel.unlink = unlink # BaseModel.unlink = unlink

View File

@@ -84,10 +84,12 @@ class MrsProductionProcessCategory(models.Model):
class MrsProductionProcess(models.Model): class MrsProductionProcess(models.Model):
_name = 'sf.production.process' _name = 'sf.production.process'
_description = '表面工艺' _description = '表面工艺'
order = 'sequence asc'
code = fields.Char("编码") code = fields.Char("编码")
name = fields.Char('名称') name = fields.Char('名称')
remark = fields.Text("备注") remark = fields.Text("备注")
sequence = fields.Integer('排序')
# processing_order_ids = fields.One2many('sf.processing.order', 'production_process_id', string='工序') # processing_order_ids = fields.One2many('sf.processing.order', 'production_process_id', string='工序')
partner_process_ids = fields.Many2many('res.partner', 'process_ids', '加工工厂') partner_process_ids = fields.Many2many('res.partner', 'process_ids', '加工工厂')
active = fields.Boolean('有效', default=True) active = fields.Boolean('有效', default=True)

View File

@@ -19,7 +19,12 @@ class IrSequence(models.Model):
# date mode # date mode
dt = sequence_date or self._context.get('ir_sequence_date', fields.Date.today()) dt = sequence_date or self._context.get('ir_sequence_date', fields.Date.today())
seq_date = self.env['ir.sequence.date_range'].search( seq_date = self.env['ir.sequence.date_range'].search(
[('sequence_id', '=', self.id), ('date_from', '<=', dt), ('date_to', '>=', dt)], limit=1) [
('sequence_id', '=', self.id),
('date_from', '<=', dt),
('date_to', '>=', dt),
('date_range_period', '=', self.date_range_period)
], limit=1)
if not seq_date: if not seq_date:
if self.date_range_period: if self.date_range_period:
seq_date = self._create_date_range_seq_by_period(dt, self.date_range_period) seq_date = self._create_date_range_seq_by_period(dt, self.date_range_period)

View File

@@ -1,10 +0,0 @@
diff a/sf_base/models/tool_base_new.py b/sf_base/models/tool_base_new.py (rejected hunks)
@@ -108,6 +108,4 @@
cutting_speed_ids = fields.One2many('sf.cutting.speed', 'standard_library_id', string='切削速度Vc')
- feed_per_tooth_ids = fields.One2many('sf.feed.per.tooth', 'standard_library_id', '每齿走刀量fz',
- domain=[('cutting_speed', '!=', False)])
- feed_per_tooth_ids_3 = fields.One2many('sf.feed.per.tooth', 'standard_library_id', '每齿走刀量fz',
- domain=[('cutting_speed', '!=', False)])
+ feed_per_tooth_ids = fields.One2many('sf.feed.per.tooth', 'standard_library_id', '每齿走刀量fz')
+ feed_per_tooth_ids_3 = fields.One2many('sf.feed.per.tooth', 'standard_library_id', '每齿走刀量fz')

View File

@@ -164,7 +164,7 @@
<field name="model">sf.production.process</field> <field name="model">sf.production.process</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="表面工艺" create="0" edit="0" delete="0"> <tree string="表面工艺" create="0" edit="0" delete="0">
<field name="sequence" widget="handle" string="序" readonly="1"/> <field name="sequence" string="加工顺序" readonly="1"/>
<field name="code"/> <field name="code"/>
<field name="name" string="名称"/> <field name="name" string="名称"/>
<field name="remark"/> <field name="remark"/>

View File

@@ -17,6 +17,11 @@ class StatusChange(models.Model):
logging.info('函数已经执行=============') logging.info('函数已经执行=============')
server_product_none = [] server_product_none = []
for order in self.order_line: for order in self.order_line:
gain_way_no = order.product_template_id.model_process_parameters_ids.filtered(lambda a: not a.gain_way)
if gain_way_no:
process_parameters = [item.name for item in gain_way_no]
raise UserError(
_("请先至【制造】-【配置】中【表面工艺可选参数】为【%s】填写获取方式", ", ".join(process_parameters)))
for item in order.product_template_id.model_process_parameters_ids: for item in order.product_template_id.model_process_parameters_ids:
if item.gain_way == '外协': if item.gain_way == '外协':
server_product = self.env['product.template'].search( server_product = self.env['product.template'].search(
@@ -25,11 +30,11 @@ class StatusChange(models.Model):
if not server_product: if not server_product:
server_product_none.append(item.name) server_product_none.append(item.name)
if server_product_none: if server_product_none:
raise UserError(_("请先至【产品】中创建【表面工艺参数】为%s的服务产品", ", ".join(server_product_none))) raise UserError(_("请先至【产品】中创建【表面工艺参数】为%s的服务产品", ", ".join(server_product_none)))
# 使用super()来调用原始方法(在本例中为'sale.order'模型的'action_confirm'方法) # 使用super()来调用原始方法(在本例中为'sale.order'模型的'action_confirm'方法)
try:
res = super(StatusChange, self).action_confirm() res = super(StatusChange, self).action_confirm()
# 原有方法执行后进行额外的操作如调用外部API # 原有方法执行后进行额外的操作如调用外部API
process_start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') process_start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
config = self.env['res.config.settings'].get_values() config = self.env['res.config.settings'].get_values()
@@ -43,9 +48,16 @@ class StatusChange(models.Model):
}, },
} }
url1 = config['bfm_url_new'] + '/api/get/state/get_order' url1 = config['bfm_url_new'] + '/api/get/state/get_order'
requests.post(url1, json=json1, data=None) ret = requests.post(url1, json=json1, data=None)
ret = ret.json()
if not ret.get('error'):
logging.info('接口已经执行=============') logging.info('接口已经执行=============')
else:
logging.error('工厂加工同步订单状态失败 {}'.format(ret.text))
raise UserError('工厂加工同步订单状态失败')
except UserError as e:
logging.error('工厂加工同步订单状态失败 {}'.format(e))
raise UserError('工厂加工同步订单状态失败')
return res return res
def action_cancel(self): def action_cancel(self):

View File

@@ -163,6 +163,7 @@ class Sf_Dashboard_Connect(http.Controller):
# 停机时间:关机时间 - 运行时间 # 停机时间:关机时间 - 运行时间
# 停机时长:关机时间 - 初次上线时间 # 停机时长:关机时间 - 初次上线时间
'img': f'data:image/png;base64,{machine_data.machine_tool_picture.decode("utf-8")}', 'img': f'data:image/png;base64,{machine_data.machine_tool_picture.decode("utf-8")}',
'equipment_type': machine_data.category_id.name,
}) })
return json.dumps(res) return json.dumps(res)
@@ -172,49 +173,6 @@ class Sf_Dashboard_Connect(http.Controller):
res['message'] = '前端请求机床数据失败,原因:%s' % e res['message'] = '前端请求机床数据失败,原因:%s' % e
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
# @http.route('/api/logs/list', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
# cors="*")
# def logs_list(self, **kw):
# """
# 拿到日志数据返回给大屏展示
# :param kw:
# :return:
# """
# res = {'status': 1, 'message': '成功', 'data': []}
# logging.info('前端请求日志数据的参数为:%s' % kw)
#
# try:
# # 获取请求的日志数据
# logs_obj = request.env['maintenance.equipment.oee.log.detail'].sudo()
# # 获取请求的机床数据
# machine_list = ast.literal_eval(kw['machine_list'])
# begin_time_str = kw['begin_time'].strip('"')
# end_time_str = kw['end_time'].strip('"')
#
# begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
# end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
#
# print('begin_time: %s' % begin_time)
# for item in machine_list:
# log_datas = logs_obj.search(
# [('equipment_code', '=', item), ('time', '>=', begin_time), ('time', '<=', end_time)])
# print('log_datas: %s' % log_datas)
# for log_data in log_datas:
# res['data'].append({
# 'equipment_code': log_data.equipment_code,
# 'time': log_data.time.strftime('%Y-%m-%d %H:%M:%S'),
# 'state': log_data.state
#
# })
#
# return json.JSONEncoder().encode(res)
#
# except Exception as e:
# logging.info('前端请求日志数据失败,原因:%s' % e)
# res['status'] = -1
# res['message'] = '前端请求日志数据失败,原因:%s' % e
# return json.JSONEncoder().encode(res)
@http.route('/api/logs/list', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*") @http.route('/api/logs/list', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
def logs_list(self, **kw): def logs_list(self, **kw):
""" """
@@ -226,9 +184,9 @@ class Sf_Dashboard_Connect(http.Controller):
logging.info('前端请求日志数据的参数为:%s' % kw) logging.info('前端请求日志数据的参数为:%s' % kw)
try: try:
# 获取请求的日志数据 # 连接数据
logs_obj = request.env['maintenance.equipment.oee.log.detail'].sudo() conn = psycopg2.connect(**db_config)
# 获取请求的机床数据 cur = conn.cursor()
machine_list = ast.literal_eval(kw['machine_list']) machine_list = ast.literal_eval(kw['machine_list'])
begin_time_str = kw['begin_time'].strip('"') begin_time_str = kw['begin_time'].strip('"')
end_time_str = kw['end_time'].strip('"') end_time_str = kw['end_time'].strip('"')
@@ -239,19 +197,25 @@ class Sf_Dashboard_Connect(http.Controller):
print('begin_time: %s' % begin_time) print('begin_time: %s' % begin_time)
for item in machine_list: for item in machine_list:
log_datas = logs_obj.search( sql = '''
[('equipment_code', '=', item), ('time', '>=', begin_time), ('time', '<=', end_time)]) SELECT time, device_state, program_name
print('log_datas: %s' % log_datas) FROM device_data
WHERE device_name = %s AND time >= %s AND time <= %s
ORDER BY time DESC;
'''
# 执行SQL命令使用参数绑定
cur.execute(sql, (item, begin_time, end_time))
results = cur.fetchall()
# 将数据按照 equipment_code 进行分组 # 将数据按照 equipment_code 进行分组
if item not in res['data']: if item not in res['data']:
res['data'][item] = [] res['data'][item] = []
for log_data in log_datas: for result in results:
res['data'][item].append({ res['data'][item].append({
'time': log_data.time.strftime('%Y-%m-%d %H:%M:%S'), 'time': result[0].strftime('%Y-%m-%d %H:%M:%S'),
'state': log_data.state, 'state': result[1],
'production_name': log_data.production_name, 'production_name': result[2],
}) })
return json.dumps(res) # 注意使用 json.dumps 而不是直接用 json.JSONEncoder().encode() return json.dumps(res) # 注意使用 json.dumps 而不是直接用 json.JSONEncoder().encode()
@@ -344,7 +308,7 @@ class Sf_Dashboard_Connect(http.Controller):
plan_data_total_counts = plan_obj.search_count([('production_line_id.name', '=', line)]) plan_data_total_counts = plan_obj.search_count([('production_line_id.name', '=', line)])
# 工单完成量 # 工单完成量
plan_data_finish_counts = plan_obj.search_count( plan_data_finish_counts = plan_obj.search_count(
[('production_line_id.name', '=', line), ('state', 'not in', ['draft'])]) [('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
# 工单计划量 # 工单计划量
plan_data_plan_counts = plan_obj.search_count( plan_data_plan_counts = plan_obj.search_count(
[('production_line_id.name', '=', line), ('state', 'not in', ['finished'])]) [('production_line_id.name', '=', line), ('state', 'not in', ['finished'])])
@@ -385,7 +349,6 @@ class Sf_Dashboard_Connect(http.Controller):
return json.dumps(res) return json.dumps(res)
# 日完成量统计 # 日完成量统计
class DailyFinishCount(http.Controller):
@http.route('/api/DailyFinishCount', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*") @http.route('/api/DailyFinishCount', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
def DailyFinishCount(self, **kw): def DailyFinishCount(self, **kw):
""" """
@@ -418,18 +381,18 @@ class Sf_Dashboard_Connect(http.Controller):
for date in date_list: for date in date_list:
next_day = date + timedelta(days=1) next_day = date + timedelta(days=1)
orders = plan_obj.search([('production_line_id.name', '=', line), ('state', 'not in', ['draft']), orders = plan_obj.search([('production_line_id.name', '=', line), ('state', 'in', ['finished']),
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')), (date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00')) (date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
]) ])
rework_orders = plan_obj.search( rework_orders = plan_obj.search(
[('production_line_id.name', '=', line), ('state', 'in', ['rework']), [('production_line_id.name', '=', line), ('production_id.state', 'in', ['rework']),
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')), (date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00')) (date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
]) ])
not_passed_orders = plan_obj.search( not_passed_orders = plan_obj.search(
[('production_line_id.name', '=', line), ('state', 'in', ['scrap', 'cancel']), [('production_line_id.name', '=', line), ('production_id.state', 'in', ['scrap', 'cancel']),
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')), (date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00')) (date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
]) ])
@@ -623,7 +586,7 @@ class Sf_Dashboard_Connect(http.Controller):
# 查询pg库来获得待机次数 # 查询pg库来获得待机次数
@http.route('/api/IdleAlarmCount', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*") @http.route('/api/IdleAlarmCount', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
def idle_count(self, **kw): def idle_alarm_count(self, **kw):
""" """
查询设备的待机次数 查询设备的待机次数
""" """
@@ -636,69 +599,50 @@ class Sf_Dashboard_Connect(http.Controller):
try: try:
# 获取请求的机床数据 # 获取请求的机床数据
machine_list = ast.literal_eval(kw['machine_list']) machine_list = ast.literal_eval(kw['machine_list'])
idle_times = [] total_alarm_time = 0
idle_dict = {} alarm_count_num = 0
for item in machine_list: for item in machine_list:
sql = ''' sql = '''
SELECT idle_start_time,alarm_time,alarm_repair_time FROM device_data WHERE device_name = %s; SELECT COUNT(*)
FROM (
SELECT DISTINCT ON (idle_start_time) idle_start_time
FROM device_data
WHERE device_name = %s AND idle_start_time IS NOT NULL
ORDER BY idle_start_time, time
) subquery;
'''
sql2 = '''
SELECT DISTINCT ON (alarm_time) alarm_time, alarm_repair_time
FROM device_data
WHERE device_name = %s AND alarm_time IS NOT NULL
ORDER BY alarm_time, time;
''' '''
# 执行SQL命令 # 执行SQL命令
cur.execute(sql, (item,)) cur.execute(sql, (item,))
result = cur.fetchall() result = cur.fetchall()
# # print('result', result) print('result========', result)
#
# # 将查询结果添加到idle_times列表中
# idle_times = [row[0] for row in result if row[0] is not None]
#
# # 对结果去重
# unique_idle_times = set(idle_times)
# # print('unique_idle_times', unique_idle_times)
#
# # 统计去重后的数量
# idle_count = len(unique_idle_times)
# # idle_dict[item] = idle_count
#
# res['data'][item] = idle_count
total_alarm_time = 0
alarm_count = 0
alarm_time_list = []
idle_times = []
alarm_times = []
cur.execute(sql2, (item,))
result2 = cur.fetchall()
print('result2========', result2)
#
for row in result: for row in result:
idle_start_time = row[0] res['data'][item] = {'idle_count': row[0]}
alarm_time = row[1] alarm_count = []
alarm_repair_time = row[2] for row in result2:
alarm_count.append(row[0])
alarm_time_list.append(alarm_time) # 将时长累加,以秒为单位 total_alarm_time += abs(float(row[0]))
idle_times.append(idle_start_time) if len(list(set(alarm_count))) == 1:
# if alarm_repair_time is not None: if list(set(alarm_count))[0] is None:
# alarm_times.append(alarm_repair_time) alarm_count_num = 0
alarm_times.append(alarm_repair_time) else:
alarm_count_num = 1
# 对结果去重 else:
unique_total_alarm_time = set(alarm_time_list) alarm_count_num = len(list(set(alarm_count)))
unique_idle_times = set(idle_times) res['data'][item]['total_alarm_time'] = total_alarm_time / 3600
unique_alarm_times = set(alarm_times) res['data'][item]['alarm_count_num'] = alarm_count_num
# 统计去重后的数量
idle_count = len(unique_idle_times)
for alarm_time in unique_total_alarm_time:
if alarm_time is not None:
total_alarm_time += abs(float(alarm_time))
alarm_count = len(unique_alarm_times) if unique_alarm_times else 0
alarm_count = alarm_count if total_alarm_time else 0
# 存储待机次数和总待机时长(单位:秒)
res['data'][item] = {
'idle_count': idle_count,
'total_alarm_time': total_alarm_time / 3600, # 以秒为单位
'alarm_count': alarm_count
}
# 返回统计结果 # 返回统计结果
return json.dumps(res) return json.dumps(res)
@@ -711,7 +655,7 @@ class Sf_Dashboard_Connect(http.Controller):
# 查询pg库来获得异常情况 # 查询pg库来获得异常情况
@http.route('/api/alarm/logs', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*") @http.route('/api/alarm/logs', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
def idle_count(self, **kw): def alarm_logs(self, **kw):
""" """
查询设备的异常情况 查询设备的异常情况
""" """
@@ -763,3 +707,126 @@ class Sf_Dashboard_Connect(http.Controller):
finally: finally:
cur.close() cur.close()
conn.close() conn.close()
# 设备oee
@http.route('/api/OEE', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
def OEE(self, **kw):
"""
获取产线等oee
"""
res = {'status': 1, 'message': '成功', 'data': {}}
logging.info('前端请求oee数据的参数为:%s' % kw)
try:
count_oee = 1
workcenter_obj = request.env['mrp.workcenter'].sudo()
workcenter_list = ast.literal_eval(kw['workcenter_list'])
print('workcenter_list: %s' % workcenter_list)
for line in workcenter_list:
res['data'][line] = workcenter_obj.search([('name', '=', line)]).oee
count_oee *= workcenter_obj.search([('name', '=', line)]).oee
res['data']['综合oee'] = count_oee / 1000000
except Exception as e:
print(f"An error occurred: {e}")
return json.dumps(res)
# # 查询某段时间的设备oee
# @http.route('/api/OEEByTime', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
# def OEEByTime(self, **kw):
# """
# 获取某段时间的oee
# """
# res = {'status': 1, 'message': '成功', 'data': {}}
# logging.info('前端请求获取某段时间的oee的参数为:%s' % kw)
# workcenter_list = ast.literal_eval(kw['workcenter_list'])
# begin_time_str = kw['begin_time'].strip('"')
# end_time_str = kw['end_time'].strip('"')
# begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
# end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
# print('workcenter_list: %s' % workcenter_list)
# # 连接数据库
# conn = psycopg2.connect(**db_config)
# cur = conn.cursor()
# # 查询并计算OEE平均值
# oee_data = {}
# for workcenter in workcenter_list:
# cur.execute("""
# SELECT AVG(oee) as avg_oee
# FROM oee_data
# WHERE workcenter_name = %s
# AND time BETWEEN %s AND %s
# """, (workcenter, begin_time, end_time))
#
# result = cur.fetchone()
# avg_oee = result[0] if result else 0.0
# oee_data[workcenter] = avg_oee
#
# # 返回数据
# res['data'] = oee_data
# return json.dumps(res)
@http.route('/api/OEEByTime', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
def OEEByTime(self, **kw):
"""
获取某段时间的OEE根据用户指定的时间单位day或hour返回对应的平均值。
如果不传time_unit则默认按天返回并补全没有数据的时间段填充0值。
"""
res = {'status': 1, 'message': '成功', 'data': {}}
logging.info('前端请求获取某段时间的OEE的参数为:%s' % kw)
# 获取并解析参数
workcenter_list = ast.literal_eval(kw['workcenter_list'])
begin_time_str = kw['begin_time'].strip('"')
end_time_str = kw['end_time'].strip('"')
time_unit = kw.get('time_unit', 'day') # 默认单位为天
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
# 连接数据库
conn = psycopg2.connect(**db_config)
cur = conn.cursor()
# 根据时间单位选择不同的时间格式
if time_unit == 'hour':
time_format = 'YYYY-MM-DD HH24:00:00'
time_delta = timedelta(hours=1)
else: # 默认为'day'
time_format = 'YYYY-MM-DD'
time_delta = timedelta(days=1)
# 查询并计算OEE平均值
oee_data = {}
for workcenter in workcenter_list:
cur.execute(f"""
SELECT to_char(time, '{time_format}') as time_unit, AVG(oee) as avg_oee
FROM oee_data
WHERE workcenter_name = %s
AND time BETWEEN %s AND %s
GROUP BY time_unit
ORDER BY time_unit
""", (workcenter, begin_time, end_time))
results = cur.fetchall()
# 初始化当前产线的OEE数据字典
workcenter_oee = {row[0]: row[1] for row in results}
# 补全缺失的时间段
current_time = begin_time
if time_unit != 'hour':
while current_time <= end_time:
time_key = current_time.strftime('%Y-%m-%d')
if time_key not in workcenter_oee:
workcenter_oee[time_key] = 0
current_time += time_delta
# 按时间排序
oee_data[workcenter] = dict(sorted(workcenter_oee.items()))
# 关闭数据库连接
cur.close()
conn.close()
# 返回数据
res['data'] = oee_data
return json.dumps(res)

View File

@@ -32,6 +32,7 @@
'views/model_type_view.xml', 'views/model_type_view.xml',
'views/agv_setting_views.xml', 'views/agv_setting_views.xml',
'views/sf_maintenance_equipment.xml', 'views/sf_maintenance_equipment.xml',
'views/res_config_settings_views.xml',
], ],
'assets': { 'assets': {

View File

@@ -668,7 +668,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('AGVDownProduct error:%s' % e) logging.info('AGVDownProduct error:%s' % e)
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/AgvStationState', type='json', auth='none', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/AgvStationState', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
def AGVStationState(self, **kw): def AGVStationState(self, **kw):
""" """

View File

@@ -10,3 +10,4 @@ from . import res_user
from . import production_line_base from . import production_line_base
from . import agv_setting from . import agv_setting
from . import agv_scheduling from . import agv_scheduling
from . import res_config_setting

View File

@@ -231,7 +231,9 @@ class AgvScheduling(models.Model):
rec.site_state = '空闲' rec.site_state = '空闲'
rec.end_site_id = agv_task_route.end_site_id.id rec.end_site_id = agv_task_route.end_site_id.id
rec.agv_route_id = agv_task_route.id rec.agv_route_id = agv_task_route.id
# rec._delivery_avg() is_agv_task_dispatch = self.env['ir.config_parameter'].sudo().get_param('is_agv_task_dispatch')
if is_agv_task_dispatch:
rec._delivery_avg()
# 更新接驳站状态 # 更新接驳站状态
rec.env['sf.agv.site'].update_site_state({rec.end_site_id.name: '占用'}, False) rec.env['sf.agv.site'].update_site_state({rec.end_site_id.name: '占用'}, False)

View File

@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import base64 import base64
import datetime
import logging import logging
import json import json
import os import os
import re import re
import requests import requests
from itertools import groupby from itertools import groupby
from datetime import datetime
from collections import defaultdict, namedtuple from collections import defaultdict, namedtuple
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
@@ -123,9 +123,39 @@ class MrpProduction(models.Model):
# 上传零件图纸 # 上传零件图纸
part_drawing = fields.Binary('零件图纸') part_drawing = fields.Binary('零件图纸')
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True) @api.depends('product_id.manual_quotation')
def _compute_manual_quotation(self):
for item in self:
item.manual_quotation = item.product_id.manual_quotation
manual_quotation = fields.Boolean('人工编程', default=False, compute=_compute_manual_quotation, store=True)
is_scrap = fields.Boolean('是否报废', default=False) is_scrap = fields.Boolean('是否报废', default=False)
is_remanufacture = fields.Boolean('是否重新制造', default=False) is_remanufacture = fields.Boolean('是否重新制造', default=False)
remanufacture_count = fields.Integer("重新制造订单数量", compute='_compute_remanufacture_production_ids')
remanufacture_production_id = fields.Many2one('mrp.production', string='')
@api.depends('remanufacture_production_id')
def _compute_remanufacture_production_ids(self):
for production in self:
if production.remanufacture_production_id:
remanufacture_production = self.env['mrp.production'].search(
[('id', '=', production.remanufacture_production_id.id)])
if remanufacture_production:
production.remanufacture_count = len(remanufacture_production)
else:
production.remanufacture_count = 0
def action_view_remanufacture_productions(self):
self.ensure_one()
mrp_production = self.env['mrp.production'].search(
[('id', '=', self.remanufacture_production_id.id)])
action = {
'res_model': 'mrp.production',
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_id': mrp_production.id,
}
return action
@api.depends( @api.depends(
'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state', 'tool_state', 'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state', 'tool_state',
@@ -446,28 +476,29 @@ class MrpProduction(models.Model):
# self.env['mrp.workorder'].json_workorder_str(k, production, route)) # self.env['mrp.workorder'].json_workorder_str(k, production, route))
# 表面工艺工序 # 表面工艺工序
# 获取表面工艺id # 获取表面工艺id
if production.product_id.model_process_parameters_ids:
logging.info('model_process_parameters_ids:%s' % production.product_id.model_process_parameters_ids)
surface_technics_arr = []
# 工序id # 工序id
surface_technics_arr = []
route_workcenter_arr = [] route_workcenter_arr = []
for item in production.product_id.product_model_type_id.surface_technics_routing_tmpl_ids: for item in production.product_id.product_model_type_id.surface_technics_routing_tmpl_ids:
if item.route_workcenter_id.surface_technics_id.id:
for process_param in production.product_id.model_process_parameters_ids:
logging.info('process_param:%s%s' % (process_param.id, process_param.name))
if item.route_workcenter_id.surface_technics_id == process_param.process_id:
logging.info(
'surface_technics_id:%s%s' % (item.route_workcenter_id.surface_technics_id.id,
item.route_workcenter_id.surface_technics_id.name))
surface_technics_arr.append(item.route_workcenter_id.surface_technics_id.id) surface_technics_arr.append(item.route_workcenter_id.surface_technics_id.id)
route_workcenter_arr.append(item.route_workcenter_id.id) route_workcenter_arr.append(item.route_workcenter_id.id)
if surface_technics_arr: if surface_technics_arr:
production_process_category = self.env['sf.production.process.category'].search( production_process = self.env['sf.production.process'].search(
[('production_process_ids.id', 'in', surface_technics_arr)], [('id', 'in', surface_technics_arr)],
order='sequence asc' order='sequence asc'
) )
# 用filter刷选表面工艺id'是否存在工艺类别对象里 for p in production_process:
if production_process_category: logging.info('production_process:%s' % p.name)
for p in production_process_category: # if production_process:
logging.info('production_process_category:%s' % p.name)
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( process_parameter = production.product_id.model_process_parameters_ids.filtered(
lambda pm: pm.process_id.id == production_process.id) lambda pm: pm.process_id.id == p.id)
if process_parameter: if process_parameter:
# 产品为表面工艺服务的供应商 # 产品为表面工艺服务的供应商
product_production_process = self.env['product.template'].search( product_production_process = self.env['product.template'].search(
@@ -475,7 +506,7 @@ class MrpProduction(models.Model):
if product_production_process: if product_production_process:
route_production_process = self.env[ route_production_process = self.env[
'mrp.routing.workcenter'].search( 'mrp.routing.workcenter'].search(
[('surface_technics_id', '=', production_process.id), [('surface_technics_id', '=', p.id),
('id', 'in', route_workcenter_arr)]) ('id', 'in', route_workcenter_arr)])
if route_production_process: if route_production_process:
workorders_values.append( workorders_values.append(
@@ -633,7 +664,7 @@ class MrpProduction(models.Model):
# 表面工艺工序 # 表面工艺工序
# 模型类型的表面工艺工序模版 # 模型类型的表面工艺工序模版
surface_tmpl_ids = model_type_id.surface_technics_routing_tmpl_ids surface_tmpl_ids = model_type_id.surface_technics_routing_tmpl_ids
# 产品选择的表面工艺 # 产品选择的表面工艺参数
model_process_parameters_ids = rec.product_id.model_process_parameters_ids model_process_parameters_ids = rec.product_id.model_process_parameters_ids
process_dict = {} process_dict = {}
if model_process_parameters_ids: if model_process_parameters_ids:
@@ -642,7 +673,7 @@ class MrpProduction(models.Model):
for surface_tmpl_id in surface_tmpl_ids: for surface_tmpl_id in surface_tmpl_ids:
if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id: if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id:
surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name
process_dict.update({int(process_id.category_id.code): '%s-%s' % ( process_dict.update({int(process_id.sequence): '%s-%s' % (
surface_tmpl_name, process_parameters_id.name)}) surface_tmpl_name, process_parameters_id.name)})
process_list = sorted(process_dict.keys()) process_list = sorted(process_dict.keys())
for process_num in process_list: for process_num in process_list:
@@ -659,14 +690,16 @@ class MrpProduction(models.Model):
raise ValidationError('该产品【加工面板】为空!') raise ValidationError('该产品【加工面板】为空!')
else: else:
raise ValidationError('该产品没有选择【模版类型】!') raise ValidationError('该产品没有选择【模版类型】!')
logging.info('sequence_list: %s' % sequence_list)
for work in rec.workorder_ids: for work in rec.workorder_ids:
if sequence_list.get(work.name): work_name = work.name
work.sequence = sequence_list[work.name] logging.info(work_name)
if sequence_list.get(work_name):
work.sequence = sequence_list[work_name]
elif sequence_list.get(work.processing_panel): elif sequence_list.get(work.processing_panel):
processing_panel = sequence_list.get(work.processing_panel) processing_panel = sequence_list.get(work.processing_panel)
if processing_panel.get(work.name): if processing_panel.get(work_name):
work.sequence = processing_panel[work.name] work.sequence = processing_panel[work_name]
else: else:
raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name) raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
else: else:
@@ -692,8 +725,9 @@ class MrpProduction(models.Model):
sequence_max += 1 sequence_max += 1
panel_sequence_list.update({tmpl_id.route_workcenter_id.name: sequence_max}) panel_sequence_list.update({tmpl_id.route_workcenter_id.name: sequence_max})
for work_id in work_ids: for work_id in work_ids:
if panel_sequence_list.get(work_id.name): work_name = work_id.name
work_id.sequence = panel_sequence_list[work_id.name] if panel_sequence_list.get(work_name):
work_id.sequence = panel_sequence_list[work_name]
# 创建工单并进行排序 # 创建工单并进行排序
def _create_workorder(self, item): def _create_workorder(self, item):
@@ -701,6 +735,53 @@ class MrpProduction(models.Model):
self._reset_work_order_sequence() self._reset_work_order_sequence()
return True return True
def process_range_time(self):
for production in self:
works = production.workorder_ids
pro_plan = self.env['sf.production.plan'].search([('production_id', '=', production.id)], limit=1)
if not pro_plan:
continue
type_map = {'装夹预调': False, 'CNC加工': False, '解除装夹': False}
# 最后一次加工结束时间
last_time = pro_plan.date_planned_start
# 预置时间
for work in works:
count = type_map.get(work.routing_type)
date_planned_end = None
date_planned_start = None
duration_expected = datetime.timedelta(minutes=work.duration_expected)
reserve_time = datetime.timedelta(minutes=work.reserved_duration)
if not count:
# 第一轮加工
if work.routing_type == '装夹预调':
date_planned_end = last_time - reserve_time
date_planned_start = date_planned_end - duration_expected
elif work.routing_type == 'CNC加工':
date_planned_start = last_time
date_planned_end = last_time + duration_expected
last_time = date_planned_end
else:
date_planned_start = last_time + reserve_time
date_planned_end = date_planned_start + duration_expected
last_time = date_planned_end
type_map.update({work.routing_type: True})
else:
date_planned_start = last_time + reserve_time
date_planned_end = date_planned_start + duration_expected
last_time = date_planned_end
work.leave_id.write({
'date_from': date_planned_start,
'date_to': date_planned_end,
})
# work.write({'date_planned_start': date_planned_start, 'date_planned_finished': date_planned_end})
work.date_planned_start = date_planned_start
work.date_planned_finished = date_planned_end
routing_workcenter = self.env['mrp.routing.workcenter'].sudo().search(
[('name', '=', work.routing_type)])
work.write({'date_planned_start': date_planned_start, 'date_planned_finished': date_planned_end,
'duration_expected': routing_workcenter.time_cycle})
# 修改标记已完成方法 # 修改标记已完成方法
def button_mark_done1(self): def button_mark_done1(self):
if not self.workorder_ids.filtered(lambda w: w.routing_type not in ['表面工艺']): if not self.workorder_ids.filtered(lambda w: w.routing_type not in ['表面工艺']):
@@ -808,8 +889,8 @@ class MrpProduction(models.Model):
'target': 'new', 'target': 'new',
'context': { 'context': {
'default_production_id': self.id, 'default_production_id': self.id,
'default_programming_state': '编程中' if cloud_programming[ 'default_reprogramming_num': cloud_programming['reprogramming_num'],
'programming_state'] != '已下发' else '已下发', 'default_programming_states': cloud_programming['programming_state'],
'default_is_reprogramming': True if cloud_programming['programming_state'] in ['已下发'] else False 'default_is_reprogramming': True if cloud_programming['programming_state'] in ['已下发'] else False
} }
} }
@@ -954,34 +1035,21 @@ class MrpProduction(models.Model):
[('origin', '=', sale_order.name), ('name', 'ilike', 'WH/OUT/')]) [('origin', '=', sale_order.name), ('name', 'ilike', 'WH/OUT/')])
move = out_picking.move_ids.filtered(lambda pd: pd.product_id == self.product_id) move = out_picking.move_ids.filtered(lambda pd: pd.product_id == self.product_id)
move_values = {'product_description_variants': '', move_values = {'product_description_variants': '',
'date_planned': datetime.now(), 'date_planned': fields.Datetime.now(),
'date_deadline': datetime.now(), 'date_deadline': fields.Datetime.now(),
# 'move_dest_ids': self.env['stock.move'].search([('id', '=', move.id)]),
'move_dest_ids': move, 'move_dest_ids': move,
'group_id': False, 'group_id': move.group_id,
'route_ids': [], 'route_ids': [],
'warehouse_id': self.warehouse_id, 'warehouse_id': self.warehouse_id,
'priority': 0, 'priority': 0,
'orderpoint_id': False, 'orderpoint_id': False,
'product_packaging_id': False} 'product_packaging_id': False}
rule = self.env['stock.rule'].search(
[('action', '=', 'pull'), ('procure_method', '=', 'mts_else_mto'), (
'location_dest_id', '=', self.env['stock.location'].search([('parent_path', '=', '2/5/')]).id),
('location_src_id', '=', self.env['stock.location'].search(
[('barcode', '=', 'CP')]).id)])
# origin = move._prepare_procurement_origin()
procurement_requests.append(self.env['procurement.group'].Procurement( procurement_requests.append(self.env['procurement.group'].Procurement(
move.product_id, 1.0, move.product_uom, move.product_id, 1.0, move.product_uom,
self.env['stock.location'].search([('barcode', '=', 'CP')]), move.location_id, move.rule_id and move.rule_id.name or "/",
rule and rule.name or "/",
sale_order.name, move.company_id, move_values)) sale_order.name, move.company_id, move_values))
self.env['procurement.group'].run(procurement_requests, self.env['procurement.group'].run(procurement_requests,
raise_user_error=not self.env.context.get('from_orderpoint')) raise_user_error=not self.env.context.get('from_orderpoint'))
# 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 p.move_raw_ids and not p.workorder_ids)).action_confirm()
productions = self.env['mrp.production'].sudo().search( productions = self.env['mrp.production'].sudo().search(
[('origin', '=', self.origin)], order='id desc', limit=1) [('origin', '=', self.origin)], order='id desc', limit=1)
move = self.env['stock.move'].search([('origin', '=', productions.name)], order='id desc') move = self.env['stock.move'].search([('origin', '=', productions.name)], order='id desc')
@@ -1009,74 +1077,41 @@ class MrpProduction(models.Model):
'picking_id': sfp_move.picking_id.id, 'picking_type_id': sfp_move.picking_type_id.id, 'picking_id': sfp_move.picking_id.id, 'picking_type_id': sfp_move.picking_type_id.id,
'production_id': False}) 'production_id': False})
productions.write({'programming_no': self.programming_no, 'is_remanufacture': True}) productions.write({'programming_no': self.programming_no, 'is_remanufacture': True})
productions.procurement_group_id.mrp_production_ids.move_dest_ids.write( # productions.procurement_group_id.mrp_production_ids.move_dest_ids.write(
{'group_id': self.env['procurement.group'].search([('name', '=', sale_order.name)])}) # {'group_id': self.env['procurement.group'].search([('name', '=', sale_order.name)])})
stock_picking_remanufacture = self.env['stock.picking'].search([('origin', '=', productions.name)])
for pick in stock_picking_remanufacture:
if pick.name.startswith('WH/PC/') or pick.name.startswith('WH/INT/'):
if pick.move_ids:
product_type_id = pick.move_ids[0].product_id.categ_id
if product_type_id.name == '坯料':
location_id = self.env['stock.location'].search([('name', '=', '坯料存货区')])
if not location_id:
logging.info(f'没有搜索到【坯料存货区】: {location_id}')
break
if pick.picking_type_id.name == '内部调拨':
if pick.location_dest_id.product_type != product_type_id:
pick.location_dest_id = location_id.id
elif pick.picking_type_id.name == '生产发料':
if pick.location_id.product_type != product_type_id:
pick.location_id = location_id.id
scarp_process_parameter_workorder = self.env['mrp.workorder'].search( scarp_process_parameter_workorder = self.env['mrp.workorder'].search(
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', self.id), [('surface_technics_parameters_id', '!=', False), ('production_id', '=', self.id),
('is_subcontract', '=', True)]) ('is_subcontract', '=', True)])
if scarp_process_parameter_workorder: if scarp_process_parameter_workorder:
production_programming = self.env['mrp.production'].search( production_programming = self.env['mrp.production'].search(
[('programming_no', '=', self.programming_no)], order='name asc') [('programming_no', '=', self.programming_no), ('id', '!=', productions.id)], order='name asc')
production_list = [production.name for production in production_programming] production_list = [production.name for production in production_programming]
purchase_orders = self.env['purchase.order'].search([('origin', '=', ','.join(production_list))]) purchase_orders = self.env['purchase.order'].search([('origin', 'ilike', ','.join(production_list))])
for purchase_item in purchase_orders.order_line: for purchase_item in purchase_orders.order_line:
for process_item in scarp_process_parameter_workorder: for process_item in scarp_process_parameter_workorder:
if purchase_item.product_id.categ_type == '表面工艺': if purchase_item.product_id.categ_type == '表面工艺':
if purchase_item.product_id.server_product_process_parameters_id == process_item.surface_technics_parameters_id: if purchase_item.product_id.server_product_process_parameters_id == process_item.surface_technics_parameters_id:
print(purchase_orders.find(productions.name)) if purchase_orders.origin.find(productions.name) == -1:
if purchase_orders.find(productions.name) == -1: purchase_orders.origin += ',' + productions.name
purchase_orders.origin += productions.name
if item['is_reprogramming'] is False: if item['is_reprogramming'] is False:
productions._create_workorder(item) productions._create_workorder(item)
productions.programming_state = '已编程' productions.programming_state = '已编程'
for production_item in productions:
process_parameter_workorder = self.env['mrp.workorder'].search(
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', production_item.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_item)
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_item)
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
production_item)
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_item)
if is_pick is False and m == 0:
if len(sorted_workorders) == 1:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production_item)
else:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i],
production_item)
else: else:
productions.programming_state = '编程中' productions.programming_state = '编程中'
return productions return productions

View File

@@ -21,7 +21,7 @@ class ResMrpRoutingWorkcenter(models.Model):
workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_route', required=True) workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_route', required=True)
bom_id = fields.Many2one('mrp.bom', required=False) bom_id = fields.Many2one('mrp.bom', required=False)
surface_technics_id = fields.Many2one('sf.production.process', string="表面工艺") surface_technics_id = fields.Many2one('sf.production.process', string="表面工艺")
reserved_duration = fields.Float('预留时长', default=30, tracking=True)
def get_no(self): def get_no(self):
international_standards = self.search( international_standards = self.search(
[('code', '!=', ''), ('active', 'in', [True, False])], [('code', '!=', ''), ('active', 'in', [True, False])],

View File

@@ -41,6 +41,7 @@ class ResWorkcenter(models.Model):
oee_target = fields.Float( oee_target = fields.Float(
string='OEE Target', help="Overall Effective Efficiency Target in percentage", default=90, tracking=True) string='OEE Target', help="Overall Effective Efficiency Target in percentage", default=90, tracking=True)
oee = fields.Float(compute='_compute_oee', help='Overall Equipment Effectiveness, based on the last month', store=True)
time_start = fields.Float('Setup Time', tracking=True) time_start = fields.Float('Setup Time', tracking=True)
time_stop = fields.Float('Cleanup Time', tracking=True) time_stop = fields.Float('Cleanup Time', tracking=True)

View File

@@ -58,9 +58,15 @@ class ResMrpWorkOrder(models.Model):
('cancel', '取消')], string='Status', ('cancel', '取消')], string='Status',
compute='_compute_state', store=True, compute='_compute_state', store=True,
default='pending', copy=False, readonly=True, recursive=True, index=True, tracking=True) default='pending', copy=False, readonly=True, recursive=True, index=True, tracking=True)
# state = fields.Selection(selection_add=[('to be detected', "待检测"), ('rework', '返工')], tracking=True) # state = fields.Selection(selection_add=[('to be detected', "待检测"), ('rework', '返工')], tracking=True)
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True) @api.depends('production_id.manual_quotation')
def _compute_manual_quotation(self):
for item in self:
item.manual_quotation = item.production_id.manual_quotation
manual_quotation = fields.Boolean('人工编程', default=False, compute=_compute_manual_quotation, store=True)
def _compute_working_users(self): def _compute_working_users(self):
super()._compute_working_users() super()._compute_working_users()
@@ -178,17 +184,12 @@ class ResMrpWorkOrder(models.Model):
if order.routing_type == '表面工艺': if order.routing_type == '表面工艺':
production_programming = self.env['mrp.production'].search( production_programming = self.env['mrp.production'].search(
[('programming_no', '=', order.production_id.programming_no)], order='name asc') [('programming_no', '=', order.production_id.programming_no)], order='name asc')
production_no_remanufacture = production_programming.filtered(lambda a: a.is_remanufacture is False)
production_list = [production.name for production in production_programming] production_list = [production.name for production in production_programming]
purchase = self.env['purchase.order'].search([('origin', '=', ','.join(production_list))]) purchase = self.env['purchase.order'].search([('origin', '=', ','.join(production_list))])
for line in purchase.order_line: for line in purchase.order_line:
if line.product_id.server_product_process_parameters_id == order.surface_technics_parameters_id and line.product_qty == len( if line.product_id.server_product_process_parameters_id == order.surface_technics_parameters_id and line.product_qty == len(
production_programming): production_no_remanufacture):
# server_product = self.env['product.template'].search(
# [('server_product_process_parameters_id', '=', pp.id),
# ('detailed_type', '=', 'service')])
# purchase_order_line = self.env['purchase.order.line'].search(
# [('product_id', '=', server_product.id), ('product_qty', '=', len(production_programming))])
# if purchase_order_line:
order.surface_technics_purchase_count = len(purchase) order.surface_technics_purchase_count = len(purchase)
else: else:
order.surface_technics_purchase_count = 0 order.surface_technics_purchase_count = 0
@@ -237,6 +238,7 @@ class ResMrpWorkOrder(models.Model):
tool_state = fields.Selection([('0', '正常'), ('1', '缺刀'), ('2', '无效刀')], string='功能刀具状态', default='0', tool_state = fields.Selection([('0', '正常'), ('1', '缺刀'), ('2', '无效刀')], string='功能刀具状态', default='0',
store=True, compute='_compute_tool_state') store=True, compute='_compute_tool_state')
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)
reserved_duration = fields.Float('预留时长', default=30, tracking=True)
@api.depends('cnc_ids.tool_state') @api.depends('cnc_ids.tool_state')
def _compute_tool_state_remark(self): def _compute_tool_state_remark(self):
@@ -646,29 +648,36 @@ class ResMrpWorkOrder(models.Model):
# 拼接工单对象属性值 # 拼接工单对象属性值
def json_workorder_str(self, k, production, route, item): def json_workorder_str(self, k, production, route, item):
# 计算预计时长duration_expected # 计算预计时长duration_expected
if route.routing_type == '切割': routing_types = ['切割', '装夹预调', 'CNC加工', '解除装夹']
duration_expected = self.env['mrp.routing.workcenter'].sudo().search( if route.routing_type in routing_types:
[('name', '=', '切割')]).time_cycle routing_workcenter = self.env['mrp.routing.workcenter'].sudo().search(
# elif route.routing_type == '获取CNC加工程序': [('name', '=', route.routing_type)])
duration_expected = routing_workcenter.time_cycle
reserved_duration = routing_workcenter.reserved_duration
# if route.routing_type == '切割':
# duration_expected = self.env['mrp.routing.workcenter'].sudo().search( # duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# [('name', '=', '获取CNC加工程序')]).time_cycle # [('name', '=', '切割')]).time_cycle
elif route.routing_type == '装夹预调': # # elif route.routing_type == '获取CNC加工程序':
duration_expected = self.env['mrp.routing.workcenter'].sudo().search( # # duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
[('name', '=', '装夹预调')]).time_cycle # # [('name', '=', '获取CNC加工程序')]).time_cycle
# elif route.routing_type == '前置三元定位检测': # elif route.routing_type == '装夹预调':
# duration_expected = self.env['mrp.routing.workcenter'].sudo().search( # duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# [('name', '=', '前置三元定位检测')]).time_cycle # [('name', '=', '装夹预调')]).time_cycle
elif route.routing_type == 'CNC加工': # # elif route.routing_type == '前置三元定位检测':
duration_expected = self.env['mrp.routing.workcenter'].sudo().search( # # duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
[('name', '=', 'CNC加工')]).time_cycle # # [('name', '=', '前置三元定位检测')]).time_cycle
# elif route.routing_type == '后置三元质量检测': # elif route.routing_type == 'CNC加工':
# duration_expected = self.env['mrp.routing.workcenter'].sudo().search( # duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# [('name', '=', '后置三元质量检测')]).time_cycle # [('name', '=', 'CNC加工')]).time_cycle
elif route.routing_type == '解除装夹': # # elif route.routing_type == '后置三元质量检测':
duration_expected = self.env['mrp.routing.workcenter'].sudo().search( # # duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
[('name', '=', '解除装夹')]).time_cycle # # [('name', '=', '后置三元质量检测')]).time_cycle
# elif route.routing_type == '解除装夹':
# duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# [('name', '=', '解除装夹')]).time_cycle
else: else:
duration_expected = 60 duration_expected = 60
reserved_duration = 30
workorders_values_str = [0, '', { workorders_values_str = [0, '', {
'product_uom_id': production.product_uom_id.id, 'product_uom_id': production.product_uom_id.id,
'qty_producing': 0, 'qty_producing': 0,
@@ -692,6 +701,7 @@ class ResMrpWorkOrder(models.Model):
item), item),
# 'workpiece_delivery_ids': False if not route.routing_type == '装夹预调' else self._json_workpiece_delivery_list( # 'workpiece_delivery_ids': False if not route.routing_type == '装夹预调' else self._json_workpiece_delivery_list(
# production) # production)
'reserved_duration': reserved_duration,
}] }]
return workorders_values_str return workorders_values_str
@@ -956,12 +966,14 @@ class ResMrpWorkOrder(models.Model):
else: else:
production_programming = self.env['mrp.production'].search( production_programming = self.env['mrp.production'].search(
[('programming_no', '=', self.production_id.programming_no)], order='name asc') [('programming_no', '=', self.production_id.programming_no)], order='name asc')
production_no_remanufacture = production_programming.filtered(
lambda a: a.is_remanufacture is False)
production_list = [production.name for production in production_programming] production_list = [production.name for production in production_programming]
purchase_orders = self.env['purchase.order'].search( purchase_orders = self.env['purchase.order'].search(
[('origin', '=', ','.join(production_list))]) [('origin', 'ilike', ','.join(production_list))])
for line in purchase_orders.order_line: for line in purchase_orders.order_line:
if line.product_id.server_product_process_parameters_id == workorder.surface_technics_parameters_id and line.product_qty == len( if line.product_id.server_product_process_parameters_id == workorder.surface_technics_parameters_id and line.product_qty == len(
production_programming): production_no_remanufacture):
if purchase_orders.state == 'purchase': if purchase_orders.state == 'purchase':
workorder.state = 'ready' workorder.state = 'ready'
else: else:
@@ -1088,7 +1100,7 @@ class ResMrpWorkOrder(models.Model):
[('barcode', 'ilike', 'VL-SPOC')]).id), [('barcode', 'ilike', 'VL-SPOC')]).id),
('origin', '=', self.production_id.name)]) ('origin', '=', self.production_id.name)])
if move_out.state != 'done': if move_out.state != 'done':
move_out.write({'state': 'assigned'}) move_out.write({'state': 'assigned', 'production_id': False})
self.env['stock.move.line'].create(move_out.get_move_line(self.production_id, self)) self.env['stock.move.line'].create(move_out.get_move_line(self.production_id, self))
# move_out._action_assign() # move_out._action_assign()
@@ -1154,10 +1166,11 @@ class ResMrpWorkOrder(models.Model):
def button_finish(self): def button_finish(self):
for record in self: for record in self:
if record.routing_type == '装夹预调': if record.routing_type == '装夹预调':
if not record.material_center_point and record.X_deviation_angle > 0:
raise UserError("请对前置三元检测定位参数进行计算定位")
if not record.rfid_code and record.is_rework is False: if not record.rfid_code and record.is_rework is False:
raise UserError("请扫RFID码进行绑定") raise UserError("请扫RFID码进行绑定")
if record.is_rework is False:
if not record.material_center_point and record.X_deviation_angle > 0:
raise UserError("坯料中心点为空或X偏差角度小于等于0")
record.process_state = '待加工' record.process_state = '待加工'
# record.write({'process_state': '待加工'}) # record.write({'process_state': '待加工'})
record.production_id.process_state = '待加工' record.production_id.process_state = '待加工'
@@ -1257,15 +1270,14 @@ class ResMrpWorkOrder(models.Model):
# 解绑托盘 # 解绑托盘
def unbind_tray(self): def unbind_tray(self):
self.write({ self.production_id.workorder_ids.write({
'rfid_code': False, 'rfid_code': False,
'tray_serial_number': False, 'tray_serial_number': False,
'tray_product_id': False, 'tray_product_id': False,
'tray_brand_id': False, 'tray_brand_id': False,
'tray_type_id': False, 'tray_type_id': False,
'tray_model_id': False, 'tray_model_id': False,
'is_trayed': False 'is_trayed': False})
})
# 将FTP的检测报告文件下载到临时目录 # 将FTP的检测报告文件下载到临时目录
def download_reportfile_tmp(self, workorder, reportpath): def download_reportfile_tmp(self, workorder, reportpath):

View File

@@ -0,0 +1,22 @@
from odoo import models, fields, api
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
is_agv_task_dispatch = fields.Boolean('是否下发AGV任务', default=False)
@api.model
def get_values(self):
values = super(ResConfigSettings, self).get_values()
config = self.env['ir.config_parameter'].sudo()
is_agv_task_dispatch = config.get_param('is_agv_task_dispatch')
values.update(
is_agv_task_dispatch=is_agv_task_dispatch,
)
return values
def set_values(self):
super(ResConfigSettings, self).set_values()
config = self.env['ir.config_parameter'].sudo()
config.set_param("is_agv_task_dispatch", self.is_agv_task_dispatch or False)

View File

@@ -169,7 +169,6 @@ class StockRule(models.Model):
else: else:
forecasted_qties_by_loc[rule.location_src_id][procurement.product_id.id] -= qty_needed forecasted_qties_by_loc[rule.location_src_id][procurement.product_id.id] -= qty_needed
procure_method = 'make_to_stock' procure_method = 'make_to_stock'
move_values = rule._get_stock_move_values(*procurement) move_values = rule._get_stock_move_values(*procurement)
move_values['procure_method'] = procure_method move_values['procure_method'] = procure_method
moves_values_by_company[procurement.company_id.id].append(move_values) moves_values_by_company[procurement.company_id.id].append(move_values)
@@ -177,13 +176,10 @@ class StockRule(models.Model):
for company_id, moves_values in moves_values_by_company.items(): for company_id, moves_values in moves_values_by_company.items():
# create the move as SUPERUSER because the current user may not have the rights to do it (mto product # create the move as SUPERUSER because the current user may not have the rights to do it (mto product
# launched by a sale for example) # launched by a sale for example)
logging.info(moves_values)
moves = self.env['stock.move'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create( moves = self.env['stock.move'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create(
moves_values) moves_values)
logging.info(moves)
# Since action_confirm launch following procurement_group we should activate it. # Since action_confirm launch following procurement_group we should activate it.
moves._action_confirm() moves._action_confirm()
return True return True
@api.model @api.model
@@ -292,7 +288,12 @@ class StockRule(models.Model):
# 为同一个product_id创建一个生产订单名称列表 # 为同一个product_id创建一个生产订单名称列表
product_id_to_production_names[product_id] = [production.name for production in all_production] product_id_to_production_names[product_id] = [production.name for production in all_production]
for production_item in productions: for production_item in productions:
production_programming = self.env['mrp.production'].search(
[('product_id.id', '=', production_item.product_id.id),
('origin', '=', production_item.origin)],
limit=1, order='id asc')
if production_item.product_id.id in product_id_to_production_names: if production_item.product_id.id in product_id_to_production_names:
if not production_programming.programming_no:
if production_item.product_id.model_process_parameters_ids: if production_item.product_id.model_process_parameters_ids:
is_purchase = False is_purchase = False
sorted_process_parameters = sorted(production_item.product_id.model_process_parameters_ids, sorted_process_parameters = sorted(production_item.product_id.model_process_parameters_ids,
@@ -351,10 +352,6 @@ class StockRule(models.Model):
# # 同一个产品多个制造订单对应一个编程单和模型库 # # 同一个产品多个制造订单对应一个编程单和模型库
# # 只调用一次fetchCNC并将所有生产订单的名称作为字符串传递 # # 只调用一次fetchCNC并将所有生产订单的名称作为字符串传递
if not production_item.programming_no: if not production_item.programming_no:
production_programming = self.env['mrp.production'].search(
[('product_id.id', '=', production_item.product_id.id),
('origin', '=', production_item.origin)],
limit=1, order='id asc')
if not production_programming.programming_no: if not production_programming.programming_no:
production_item.fetchCNC( production_item.fetchCNC(
', '.join(product_id_to_production_names[production_item.product_id.id])) ', '.join(product_id_to_production_names[production_item.product_id.id]))
@@ -444,7 +441,7 @@ class ProductionLot(models.Model):
if product.tracking == "serial": if product.tracking == "serial":
last_serial = self.env['stock.lot'].search( last_serial = self.env['stock.lot'].search(
[('company_id', '=', company.id), ('product_id', '=', product.id)], [('company_id', '=', company.id), ('product_id', '=', product.id)],
limit=1, order='id DESC') limit=1, order='name desc')
if last_serial: if last_serial:
if product.categ_id.name == '刀具': if product.categ_id.name == '刀具':
return self.env['stock.lot'].get_tool_generate_lot_names1(company, product) return self.env['stock.lot'].get_tool_generate_lot_names1(company, product)
@@ -548,7 +545,7 @@ class StockPicking(models.Model):
# 设置外协出入单的名称 # 设置外协出入单的名称
def _get_name_Res(self, rescode): def _get_name_Res(self, rescode):
last_picking = self.sudo().search([('name', 'like', rescode)], order='create_date desc,id desc', limit=1) last_picking = self.sudo().search([('name', 'ilike', rescode)], order='create_date desc,id desc', limit=1)
if not last_picking: if not last_picking:
num = "%04d" % 1 num = "%04d" % 1
else: else:
@@ -577,6 +574,7 @@ class StockPicking(models.Model):
('origin', '=', self.origin), ('picking_id', '=', self.id)]) ('origin', '=', self.origin), ('picking_id', '=', self.id)])
if self.location_id == move_in.location_id and self.location_dest_id == move_in.location_dest_id: if self.location_id == move_in.location_id and self.location_dest_id == move_in.location_dest_id:
if move_out.origin == move_in.origin: if move_out.origin == move_in.origin:
move_in.write({'production_id': False})
if move_out.picking_id.state != 'done': if move_out.picking_id.state != 'done':
raise UserError( raise UserError(
_('该入库单对应的单号为%s的出库单还未完成,不能进行验证操作!' % move_out.picking_id.name)) _('该入库单对应的单号为%s的出库单还未完成,不能进行验证操作!' % move_out.picking_id.name))
@@ -662,6 +660,11 @@ class ReStockMove(models.Model):
return move_values return move_values
def _get_new_picking_values_Res(self, item, sorted_workorders, rescode): def _get_new_picking_values_Res(self, item, sorted_workorders, rescode):
picking_type_id = self.mapped('picking_type_id').id
if rescode == 'WH/OCOUT/':
picking_type_id = self.env.ref('sf_manufacturing.outcontract_picking_out').id
elif rescode == 'WH/OCIN/':
picking_type_id = self.env.ref('sf_manufacturing.outcontract_picking_in').id
return { return {
'name': self.env['stock.picking']._get_name_Res(rescode), 'name': self.env['stock.picking']._get_name_Res(rescode),
'origin': item.name, 'origin': item.name,
@@ -670,7 +673,7 @@ class ReStockMove(models.Model):
'user_id': False, 'user_id': False,
'move_type': self.mapped('group_id').move_type or 'direct', 'move_type': self.mapped('group_id').move_type or 'direct',
'partner_id': sorted_workorders.supplier_id.id, 'partner_id': sorted_workorders.supplier_id.id,
'picking_type_id': self.mapped('picking_type_id').id, 'picking_type_id': picking_type_id,
'location_id': self.mapped('location_id').id, 'location_id': self.mapped('location_id').id,
'location_dest_id': self.mapped('location_dest_id').id, 'location_dest_id': self.mapped('location_dest_id').id,
'state': 'confirmed', 'state': 'confirmed',

View File

@@ -3,13 +3,34 @@ $(document).off('keydown')
$(document).on('keydown', 'body.o_web_client', function (e) { $(document).on('keydown', 'body.o_web_client', function (e) {
setTimeout(() => { setTimeout(() => {
RFID = '' RFID = ''
}, 200)
}, 200)
if(e.key == 'Enter' && e.keyCode == 13 || e.key == 'Tab' && e.keyCode == 9){ if(e.key == 'Enter' && e.keyCode == 13 || e.key == 'Tab' && e.keyCode == 9){
let fieldValue1 = $('[name="routing_type"]');
console.log('字段值:', fieldValue1.text());
console.log(RFID) console.log(RFID)
if(!RFID || RFID.length <= 3) return; let fieldValue2 = $('[name="rfid_code"]');
$('[name="button_start"]').trigger('click') console.log('字段值2:', fieldValue2.text());
RFID = '' // if(!RFID || RFID.length <= 3) return;
// $('[name="button_start"]').trigger('click')
// setTimeout(() => {
// $('.o_dialog .modal-footer .btn-primary').trigger('click')
// }, 50)
// RFID = ''
// return;
// fieldValue2.val() === '')
// 检查字段值是否等于“装夹预调”
if (fieldValue1.text() === '装夹预调') {
if (!RFID || RFID.length <= 3) return;
$('[name="button_start"]').trigger('click');
setTimeout(() => {
$('.o_dialog .modal-footer .btn-primary').trigger('click');
}, 100);
}
RFID = '';
return; return;
} }
RFID += e.key RFID += e.key

View File

@@ -127,7 +127,7 @@
confirm="是否确认更新程序" confirm="是否确认更新程序"
attrs="{'invisible': ['|',('state', '!=', 'rework'),('programming_state', '!=', '已编程未下发')]}"/> attrs="{'invisible': ['|',('state', '!=', 'rework'),('programming_state', '!=', '已编程未下发')]}"/>
<button name="button_rework" string="返工" type="object" groups="sf_base.group_sf_mrp_user" <button name="button_rework" string="返工" type="object" groups="sf_base.group_sf_mrp_user"
attrs="{'invisible': ['|',('state', '!=', 'rework') ,('programming_state', '!=', '已编程')]}"/> attrs="{'invisible': ['|','|',('state', '!=', 'rework') ,('programming_state', '!=', '已编程'),('is_rework', '=', True)]}"/>
<button name="button_scrap_new" string="报废" type="object" <button name="button_scrap_new" string="报废" type="object"
groups="sf_base.group_sf_mrp_user" groups="sf_base.group_sf_mrp_user"
attrs="{'invisible': ['|',('is_scrap', '=', False),('state','=','cancel')]}"/> attrs="{'invisible': ['|',('is_scrap', '=', False),('state','=','cancel')]}"/>
@@ -201,6 +201,19 @@
data-hotkey="l"/> data-hotkey="l"/>
</xpath> </xpath>
<xpath expr="//button[@name='action_view_mo_delivery']" position="before">
<button class="oe_stat_button" name="action_view_remanufacture_productions" type="object"
icon="fa-wrench" attrs="{'invisible': [('remanufacture_count', '=', 0)]}"
groups="mrp.group_mrp_user">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
<field name="remanufacture_count"/>
</span>
<span class="o_stat_text">新的制造</span>
</div>
</button>
</xpath>
<xpath expr="//header//button[@name='action_toggle_is_locked']" position="replace"> <xpath expr="//header//button[@name='action_toggle_is_locked']" position="replace">
<button name="action_toggle_is_locked" <button name="action_toggle_is_locked"
attrs="{'invisible': ['|', ('show_lock', '=', False), ('is_locked', '=', True)]}" attrs="{'invisible': ['|', ('show_lock', '=', False), ('is_locked', '=', True)]}"
@@ -424,6 +437,12 @@
<xpath expr="//header//button[@name='action_cancel']" position="replace"> <xpath expr="//header//button[@name='action_cancel']" position="replace">
<button name="action_cancel" type="object" string="取消" groups="sf_base.group_sf_mrp_user"/> <button name="action_cancel" type="object" string="取消" groups="sf_base.group_sf_mrp_user"/>
</xpath> </xpath>
<xpath expr="//field[@name='state']" position="replace">
<field name="state" decoration-success="state in ('done', 'to_close')"
decoration-warning="state == 'progress'" decoration-info="state == 'confirmed'"
decoration-danger="state in ('cancel','rework','scrap')" decoration-muted="state == 'draft'"
optional="show" widget="badge" class="text-dark"/>
</xpath>
<xpath expr="//field[@name='state']" position="after"> <xpath expr="//field[@name='state']" position="after">
<field name="tool_state" invisible="1"/> <field name="tool_state" invisible="1"/>
</xpath> </xpath>

View File

@@ -17,6 +17,7 @@
<field name="bom_product_template_attribute_value_ids" position="after"> <field name="bom_product_template_attribute_value_ids" position="after">
<field name="routing_type" required="1"/> <field name="routing_type" required="1"/>
<field name="is_repeat"/> <field name="is_repeat"/>
<field name="reserved_duration"/>
</field> </field>
</field> </field>
</record> </record>

View File

@@ -10,7 +10,7 @@
<field name="name" decoration-success="is_subcontract" decoration-bf="is_subcontract"/> <field name="name" decoration-success="is_subcontract" decoration-bf="is_subcontract"/>
</field> </field>
<field name="name" position="before"> <field name="name" position="before">
<field name="sequence"/> <field name="sequence" string="序号"/>
<field name='user_permissions' invisible="1"/> <field name='user_permissions' invisible="1"/>
</field> </field>
<field name="name" position="after"> <field name="name" position="after">
@@ -36,6 +36,9 @@
<xpath expr="//field[@name='date_planned_start']" position="replace"> <xpath expr="//field[@name='date_planned_start']" position="replace">
<field name="date_planned_start" string="计划开始日期" optional="show"/> <field name="date_planned_start" string="计划开始日期" optional="show"/>
</xpath> </xpath>
<xpath expr="//field[@name='date_planned_start']" position="before">
<field name="reserved_duration" string="计划预留时间" optional="show"/>
</xpath>
<xpath expr="//field[@name='date_planned_finished']" position="replace"> <xpath expr="//field[@name='date_planned_finished']" position="replace">
<field name="date_planned_finished" string="计划结束日期" optional="hide"/> <field name="date_planned_finished" string="计划结束日期" optional="hide"/>
</xpath> </xpath>
@@ -195,7 +198,7 @@
attrs="{'invisible': ['|','|',('routing_type','!=','装夹预调'),('state','!=','progress'),('is_rework','=',True)]}"/> attrs="{'invisible': ['|','|',('routing_type','!=','装夹预调'),('state','!=','progress'),('is_rework','=',True)]}"/>
<button name="unbind_tray" type="object" string="解绑托盘" <button name="unbind_tray" type="object" string="解绑托盘"
class="btn-primary" class="btn-primary"
attrs="{'invisible': ['|', '|', ('routing_type','!=','装夹预调'),('state','!=','progress'), ('is_trayed', '=', False)]}"/> attrs="{'invisible': ['|', '|', '|', ('routing_type','!=','装夹预调'),('state','!=','progress'), ('is_trayed', '=', False), ('state', 'in', ('done'))]}"/>
<button name="print_method" type="object" string="打印二维码" class="btn-primary" <button name="print_method" type="object" string="打印二维码" class="btn-primary"
attrs="{'invisible': ['|',('routing_type','!=','解除装夹'),('state','!=','done')]}"/> attrs="{'invisible': ['|',('routing_type','!=','解除装夹'),('state','!=','done')]}"/>
</xpath> </xpath>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="res_config_settings_view_form_sf_sync" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.sf_sync</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="base_setup.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@id='agv_config']/div" position="after">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="is_agv_task_dispatch"/>
</div>
<div class="o_setting_right_pane">
<div class="text-muted">
<label for="is_agv_task_dispatch"/>
</div>
</div>
</div>
</xpath>
</field>
</record>
</data>
</odoo>

View File

@@ -13,9 +13,10 @@ class ProductionWizard(models.TransientModel):
_description = '制造订单向导' _description = '制造订单向导'
production_id = fields.Many2one('mrp.production', string='制造订单号') production_id = fields.Many2one('mrp.production', string='制造订单号')
reprogramming_num = fields.Integer('重新编程次数', default=0)
is_reprogramming = fields.Boolean(string='申请重新编程', default=False) is_reprogramming = fields.Boolean(string='申请重新编程', default=False)
is_remanufacture = fields.Boolean(string='重新生成制造订单', default=True) is_remanufacture = fields.Boolean(string='重新生成制造订单', default=True)
programming_state = fields.Selection( programming_states = fields.Selection(
[('待编程', '待编程'), ('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'), [('待编程', '待编程'), ('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'),
('已下发', '已下发')], ('已下发', '已下发')],
string='编程状态') string='编程状态')
@@ -26,18 +27,21 @@ class ProductionWizard(models.TransientModel):
self.is_reprogramming = False self.is_reprogramming = False
def confirm(self): def confirm(self):
self.production_id.action_cancel()
self.production_id.detection_result_ids.write({'handle_result': '已处理'}) self.production_id.detection_result_ids.write({'handle_result': '已处理'})
self.production_id.write({'state': 'cancel', 'scrap_ids': [(0, 0, { self.production_id.write({'state': 'cancel', 'scrap_ids': [(0, 0, {
'name': self.env['ir.sequence'].next_by_code('stock.scrap') or _('New'), 'name': self.env['ir.sequence'].next_by_code('stock.scrap') or _('New'),
'product_id': self.production_id.product_id.id, 'product_id': self.production_id.product_id.id,
'scrap_qty': 1, 'scrap_qty': 1,
'lot_id': self.production_id.move_line_raw_ids.lot_id.id, 'origin': self.production_id.origin,
'date_done': fields.datetime.now(),
'lot_id': self.env['stock.move.line'].search(
[('move_id', '=', self.production_id.move_raw_ids[0].id)]).lot_id.id,
'location_id': self.production_id.move_raw_ids.filtered(lambda x: x.state not in ( 'location_id': self.production_id.move_raw_ids.filtered(lambda x: x.state not in (
'done', 'done',
'cancel')) and self.production_id.location_src_id.id or self.production_id.location_dest_id.id, 'cancel')) and self.production_id.location_src_id.id or self.production_id.location_dest_id.id,
'scrap_location_id': self.env['stock.scrap']._get_default_scrap_location_id(), 'scrap_location_id': self.env['stock.scrap']._get_default_scrap_location_id(),
'state': 'done'})]}) 'state': 'done'})]})
self.production_id.action_cancel()
if self.is_remanufacture is True: if self.is_remanufacture is True:
ret = {'programming_list': [], 'is_reprogramming': self.is_reprogramming} ret = {'programming_list': [], 'is_reprogramming': self.is_reprogramming}
if self.is_reprogramming is True: if self.is_reprogramming is True:
@@ -78,6 +82,7 @@ class ProductionWizard(models.TransientModel):
ret['programming_list'].append(vals) ret['programming_list'].append(vals)
new_production = self.production_id.recreateManufacturing(ret) new_production = self.production_id.recreateManufacturing(ret)
self.production_id.write({'remanufacture_production_id': new_production.id})
if self.is_reprogramming is False: if self.is_reprogramming is False:
for panel in new_production.product_id.model_processing_panel.split(','): for panel in new_production.product_id.model_processing_panel.split(','):
scrap_cnc_workorder = max( scrap_cnc_workorder = max(

View File

@@ -7,15 +7,26 @@
<form> <form>
<sheet> <sheet>
<field name="production_id" invisible="1"/> <field name="production_id" invisible="1"/>
<field name="programming_state" invisible="1"/> <field name="programming_states" invisible="1"/>
<div> <div>
重新生成制造订单 重新生成制造订单
<field name="is_remanufacture" force_save="1"/> <field name="is_remanufacture" force_save="1"/>
</div> </div>
<div attrs='{"invisible": [("reprogramming_num","=",0)]}'>
注意: 该制造订单产品已申请重新编程次数为<field
name="reprogramming_num" string=""
readonly="1"
style='color:red;'/>,且当前编程状态为
<field name="programming_states" string=""
decoration-info="programming_states == '待编程'"
decoration-success="programming_states == '已下发'"
decoration-warning="programming_states =='编程中'"
decoration-danger="programming_states =='已编程'" readonly="1"/>
</div>
<div attrs='{"invisible": [("is_remanufacture","=",False)]}'> <div attrs='{"invisible": [("is_remanufacture","=",False)]}'>
<span style='font-weight:bold;'>申请重新编程 <span style='font-weight:bold;'>申请重新编程
<field name="is_reprogramming" force_save="1" <field name="is_reprogramming" force_save="1"
attrs='{"readonly": [("programming_state","not in",["已下发"])]}'/> attrs='{"readonly": [("programming_states","not in",["已下发"])]}'/>
</span> </span>
</div> </div>
<footer> <footer>

View File

@@ -25,6 +25,7 @@ class ReworkWizard(models.TransientModel):
processing_panel_id = fields.Many2many('sf.processing.panel', string="加工面") processing_panel_id = fields.Many2many('sf.processing.panel', string="加工面")
is_reprogramming = fields.Boolean(string='申请重新编程', default=False) is_reprogramming = fields.Boolean(string='申请重新编程', default=False)
is_reprogramming_readonly = fields.Boolean(string='申请重新编程(只读)', default=False) is_reprogramming_readonly = fields.Boolean(string='申请重新编程(只读)', default=False)
is_clamp_measure = fields.Boolean(string='保留装夹测量数据', default=True)
reprogramming_num = fields.Integer('重新编程次数', default=0) reprogramming_num = fields.Integer('重新编程次数', default=0)
programming_state = fields.Selection( programming_state = fields.Selection(
[('待编程', '待编程'), ('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'), [('待编程', '待编程'), ('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'),
@@ -35,6 +36,7 @@ class ReworkWizard(models.TransientModel):
def confirm(self): def confirm(self):
if self.routing_type in ['装夹预调', 'CNC加工']: if self.routing_type in ['装夹预调', 'CNC加工']:
self.is_clamp_measure = False
self.workorder_id.is_rework = True self.workorder_id.is_rework = True
self.production_id.write({'detection_result_ids': [(0, 0, { self.production_id.write({'detection_result_ids': [(0, 0, {
'rework_reason': self.rework_reason, 'rework_reason': self.rework_reason,
@@ -58,19 +60,15 @@ class ReworkWizard(models.TransientModel):
if processing_panels_missing: if processing_panels_missing:
processing_panels_str = ','.join(processing_panels_missing) processing_panels_str = ','.join(processing_panels_missing)
raise UserError('您还有待处理的检测结果中为%s的加工面未选择' % processing_panels_str) raise UserError('您还有待处理的检测结果中为%s的加工面未选择' % processing_panels_str)
# processing_panels = set()
# for handle_item in handle_result:
# for dr_panel in self.processing_panel_id:
# if dr_panel.name == handle_item.processing_panel:
# processing_panels.add(dr_panel.name)
# if len(processing_panels) != len(handle_result):
# processing_panels_str = ','.join(processing_panels)
# return UserError(f'您还有待处理的检测结果中为{processing_panels_str}的加工面未选择')
for panel in self.processing_panel_id: for panel in self.processing_panel_id:
panel_workorder = self.production_id.workorder_ids.filtered( panel_workorder = self.production_id.workorder_ids.filtered(
lambda ap: ap.processing_panel == panel.name and ap.state != 'rework') lambda ap: ap.processing_panel == panel.name and ap.state != 'rework')
if panel_workorder: if panel_workorder:
panel_workorder.write({'state': 'rework'}) panel_workorder.write({'state': 'rework'})
rework_clamp_workorder = max(panel_workorder.filtered(
lambda
rp: rp.processing_panel == panel.name and rp.routing_type == '装夹预调' and rp.state in [
'done', 'rework']))
# panel_workorder.filtered( # panel_workorder.filtered(
# lambda wo: wo.routing_type == '装夹预调').workpiece_delivery_ids.filtered( # lambda wo: wo.routing_type == '装夹预调').workpiece_delivery_ids.filtered(
# lambda wd: wd.status == '待下发').write({'status': '已取消'}) # lambda wd: wd.status == '待下发').write({'status': '已取消'})
@@ -93,6 +91,43 @@ class ReworkWizard(models.TransientModel):
self.production_id.detection_result_ids.filtered( self.production_id.detection_result_ids.filtered(
lambda ap1: ap1.processing_panel == panel.name and ap1.handle_result == '待处理').write( lambda ap1: ap1.processing_panel == panel.name and ap1.handle_result == '待处理').write(
{'handle_result': '已处理'}) {'handle_result': '已处理'})
new_pre_workorder = self.production_id.workorder_ids.filtered(lambda
p: p.routing_type == '装夹预调' and p.processing_panel == panel.name and p.state not in (
'rework', 'done'))
if new_pre_workorder and rework_clamp_workorder and self.is_clamp_measure is True:
new_pre_workorder.write(
{'X1_axis': rework_clamp_workorder.X1_axis, 'Y1_axis': rework_clamp_workorder.Y1_axis
, 'Z1_axis': rework_clamp_workorder.Z1_axis,
'X2_axis': rework_clamp_workorder.X2_axis
, 'Y2_axis': rework_clamp_workorder.Y2_axis,
'Z2_axis': rework_clamp_workorder.Z2_axis
, 'X3_axis': rework_clamp_workorder.X3_axis,
'Y3_axis': rework_clamp_workorder.Y3_axis
, 'Z3_axis': rework_clamp_workorder.Z3_axis,
'X4_axis': rework_clamp_workorder.X4_axis
, 'Y4_axis': rework_clamp_workorder.Y4_axis,
'Z4_axis': rework_clamp_workorder.Z4_axis
, 'X5_axis': rework_clamp_workorder.X5_axis,
'Y5_axis': rework_clamp_workorder.Y5_axis
, 'Z5_axis': rework_clamp_workorder.Z5_axis,
'X6_axis': rework_clamp_workorder.X6_axis
, 'Y6_axis': rework_clamp_workorder.Y6_axis,
'Z6_axis': rework_clamp_workorder.Z6_axis
, 'X7_axis': rework_clamp_workorder.X7_axis,
'Y7_axis': rework_clamp_workorder.Y7_axis
, 'Z7_axis': rework_clamp_workorder.Z7_axis,
'X8_axis': rework_clamp_workorder.X8_axis
, 'Y8_axis': rework_clamp_workorder.Y8_axis,
'Z8_axis': rework_clamp_workorder.Z8_axis
, 'X9_axis': rework_clamp_workorder.X9_axis,
'Y9_axis': rework_clamp_workorder.Y9_axis
, 'Z9_axis': rework_clamp_workorder.Z9_axis,
'X10_axis': rework_clamp_workorder.X10_axis
, 'Y10_axis': rework_clamp_workorder.Y10_axis,
'Z10_axis': rework_clamp_workorder.Z10_axis
, 'X_deviation_angle': rework_clamp_workorder.X_deviation_angle,
'material_center_point': rework_clamp_workorder.material_center_point
})
if self.is_reprogramming is False: if self.is_reprogramming is False:
if self.programming_state in ['已编程', '已下发']: if self.programming_state in ['已编程', '已下发']:
if self.reprogramming_num >= 1 and self.programming_state == '已编程': if self.reprogramming_num >= 1 and self.programming_state == '已编程':
@@ -149,9 +184,7 @@ class ReworkWizard(models.TransientModel):
'cmm_ids': new_cnc_workorder.cmm_ids.sudo()._json_cmm_program(panel.name, 'cmm_ids': new_cnc_workorder.cmm_ids.sudo()._json_cmm_program(panel.name,
ret), ret),
'cnc_worksheet': cnc_rework.cnc_worksheet}) 'cnc_worksheet': cnc_rework.cnc_worksheet})
new_pre_workorder = self.production_id.workorder_ids.filtered(lambda
p: p.routing_type == '装夹预调' and p.processing_panel == panel.name and p.state not in (
'rework', 'done'))
if new_pre_workorder: if new_pre_workorder:
pre_rework = max(self.production_id.workorder_ids.filtered( pre_rework = max(self.production_id.workorder_ids.filtered(
lambda pr: pr.processing_panel == panel.name and pr.state in ( lambda pr: pr.processing_panel == panel.name and pr.state in (

View File

@@ -14,8 +14,15 @@
<group> <group>
<field name="processing_panel_id" options="{'no_create': True}" <field name="processing_panel_id" options="{'no_create': True}"
attrs='{"invisible": [("routing_type","=","装夹预调")]}' widget="many2many_tags"/> attrs='{"invisible": [("routing_type","=","装夹预调")]}' widget="many2many_tags"/>
</group> </group>
<div attrs='{"invisible": [("routing_type","=","装夹预调")]}'>
<span style='font-weight:bold;'>保留装夹测量数据
<field name="is_clamp_measure" force_save="1"/>
</span>
</div>
<div attrs='{"invisible": [("reprogramming_num","=",0)]}'> <div attrs='{"invisible": [("reprogramming_num","=",0)]}'>
<span style='font-weight:bold;'>
注意: 该制造订单产品已申请重新编程次数为<field 注意: 该制造订单产品已申请重新编程次数为<field
name="reprogramming_num" string="" name="reprogramming_num" string=""
readonly="1" readonly="1"
@@ -25,6 +32,7 @@
decoration-success="programming_state == '已下发'" decoration-success="programming_state == '已下发'"
decoration-warning="programming_state =='编程中'" decoration-warning="programming_state =='编程中'"
decoration-danger="programming_state =='已编程'" readonly="1"/> decoration-danger="programming_state =='已编程'" readonly="1"/>
</span>
</div> </div>
<div attrs='{"invisible": ["|",("routing_type","in",["装夹预调","CNC加工"]),("programming_state","not in",["已下发"])],"readonly": [("tool_state", "=", "2")]}'> <div attrs='{"invisible": ["|",("routing_type","in",["装夹预调","CNC加工"]),("programming_state","not in",["已下发"])],"readonly": [("tool_state", "=", "2")]}'>
<span style='font-weight:bold;'>申请重新编程 <span style='font-weight:bold;'>申请重新编程

View File

@@ -109,11 +109,19 @@ class WorkpieceDeliveryWizard(models.TransientModel):
) )
# 如果关联了工件配送单,则修改状态为已下发 # 如果关联了工件配送单,则修改状态为已下发
if self.delivery_ids: if self.delivery_ids:
self.delivery_ids.write({ val = {
'status': '已下发', 'status': '已下发',
'agv_scheduling_id': scheduling.id, 'agv_scheduling_id': scheduling.id,
'feeder_station_start_id': scheduling.start_site_id.id, 'feeder_station_start_id': scheduling.start_site_id.id,
}
# 如果agv任务已经下发则修改工件配送单信息
if scheduling.state == '配送中':
val.update({
'feeder_station_destination_id': scheduling.end_site_id.id,
'route_id': scheduling.agv_route_id.id,
'task_delivery_time': fields.Datetime.now()
}) })
self.delivery_ids.write(val)
# 如果是解除装夹工单,则需要处理工单逻辑 # 如果是解除装夹工单,则需要处理工单逻辑
for item in self.workorder_ids: for item in self.workorder_ids:

View File

@@ -24,11 +24,13 @@ class Sf_Mrs_Connect(http.Controller):
ret = json.loads(datas) ret = json.loads(datas)
ret = json.loads(ret['result']) ret = json.loads(ret['result'])
logging.info('下发编程单:%s' % ret) logging.info('下发编程单:%s' % ret)
domain = [('programming_no', '=', ret['programming_no'])]
if ret['manufacturing_type'] == 'scrap':
domain += [('state', 'not in', ['done', 'scrap', 'cancel'])]
productions = request.env['mrp.production'].with_user( productions = request.env['mrp.production'].with_user(
request.env.ref("base.user_admin")).search( request.env.ref("base.user_admin")).search(domain)
[('programming_no', '=', ret['programming_no'])])
if productions: if productions:
# # 拉取所有加工面的程序文件 # 拉取所有加工面的程序文件
for r in ret['processing_panel'].split(','): for r in ret['processing_panel'].split(','):
program_path_tmp_r = os.path.join('/tmp', ret['folder_name'], 'return', r) program_path_tmp_r = os.path.join('/tmp', ret['folder_name'], 'return', r)
if os.path.exists(program_path_tmp_r): if os.path.exists(program_path_tmp_r):
@@ -48,48 +50,31 @@ class Sf_Mrs_Connect(http.Controller):
if not production.workorder_ids: if not production.workorder_ids:
production.product_id.model_processing_panel = ret['processing_panel'] production.product_id.model_processing_panel = ret['processing_panel']
production._create_workorder(ret) production._create_workorder(ret)
# else: productions.process_range_time()
# for panel in ret['processing_panel'].split(','): else:
# # 查询状态为进行中且工序类型为CNC加工的工单 for panel in ret['processing_panel'].split(','):
# cnc_workorder = production.workorder_ids.filtered( # 查询状态为进行中且工序类型为CNC加工的工单
# lambda ac: ac.routing_type == 'CNC加工' and ac.state not in ['progress', 'done', cnc_workorder_has = production.workorder_ids.filtered(
# 'cancel'] and ac.processing_panel == panel) lambda ach: ach.routing_type == 'CNC加工' and ach.state not in ['progress', 'done',
# if cnc_workorder: 'rework',
# if cnc_workorder.cnc_ids: 'cancel'] and ach.processing_panel == panel)
# cnc_workorder.cmm_ids.sudo().unlink() if cnc_workorder_has:
# cnc_workorder.cnc_ids.sudo().unlink() if cnc_workorder_has.cnc_ids:
# request.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan( cnc_workorder_has.cmm_ids.sudo().unlink()
# production) cnc_workorder_has.cnc_ids.sudo().unlink()
# # program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test', request.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan(
# # panel) production)
# program_path_tmp_panel = os.path.join('/tmp', ret['folder_name'], 'return', panel) cnc_workorder_has.write(
# logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel) {'cnc_ids': cnc_workorder_has.cnc_ids.sudo()._json_cnc_processing(panel, ret),
# files_panel = os.listdir(program_path_tmp_panel) 'cmm_ids': cnc_workorder_has.cmm_ids.sudo()._json_cmm_program(panel, ret)})
# if files_panel:
# for file in files_panel:
# file_extension = os.path.splitext(file)[1]
# logging.info('file_extension:%s' % file_extension)
# if file_extension.lower() == '.pdf':
# panel_file_path = os.path.join(program_path_tmp_panel, file)
# logging.info('panel_file_path:%s' % panel_file_path)
# cnc_workorder.write(
# {'cnc_ids': cnc_workorder.cnc_ids.sudo()._json_cnc_processing(panel, ret),
# 'cmm_ids': cnc_workorder.cmm_ids.sudo()._json_cmm_program(panel, ret),
# 'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
# pre_workorder = production.workorder_ids.filtered(
# lambda ap: ap.routing_type == '装夹预调' and ap.state not in ['done',
# 'cancel'] and ap.processing_panel == panel)
# if pre_workorder:
# pre_workorder.write(
# {'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
for panel in ret['processing_panel'].split(','): for panel in ret['processing_panel'].split(','):
# 查询状态为进行中且工序类型为CNC加工的工单 # 查询状态为进行中且工序类型为CNC加工的工单
cnc_workorder = productions.workorder_ids.filtered( cnc_workorder = productions.workorder_ids.filtered(
lambda ac: ac.routing_type == 'CNC加工' and ac.state not in ['progress', 'done', lambda ac: ac.routing_type == 'CNC加工' and ac.state not in ['progress', 'done', 'rework'
'cancel'] and ac.processing_panel == panel) 'cancel'] and ac.processing_panel == panel)
if cnc_workorder: if cnc_workorder:
program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test', # program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test',
panel) # panel)
program_path_tmp_panel = os.path.join('/tmp', ret['folder_name'], 'return', panel) program_path_tmp_panel = os.path.join('/tmp', ret['folder_name'], 'return', panel)
logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel) logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel)
files_panel = os.listdir(program_path_tmp_panel) files_panel = os.listdir(program_path_tmp_panel)
@@ -102,18 +87,12 @@ class Sf_Mrs_Connect(http.Controller):
logging.info('panel_file_path:%s' % panel_file_path) logging.info('panel_file_path:%s' % panel_file_path)
cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())}) cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
pre_workorder = productions.workorder_ids.filtered( pre_workorder = productions.workorder_ids.filtered(
lambda ap: ap.routing_type == '装夹预调' and ap.state not in ['done', lambda ap: ap.routing_type == '装夹预调' and ap.state not in ['done', 'rework'
'cancel'] and ap.processing_panel == panel) 'cancel'] and ap.processing_panel == panel)
if pre_workorder: if pre_workorder:
pre_workorder.write( pre_workorder.write(
{'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())}) {'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
productions.write({'programming_state': '已编程', 'work_state': '已编程'}) productions.write({'programming_state': '已编程', 'work_state': '已编程'})
cnc_program_ids = [item.id for item in productions]
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
[('production_id', 'in', cnc_program_ids)])
if workpiece_delivery:
workpiece_delivery.write(
{'is_cnc_program_down': True, 'production_line_id': productions.production_line_id.id})
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
else: else:
res = {'status': 0, 'message': '该制造订单暂未开始'} res = {'status': 0, 'message': '该制造订单暂未开始'}

View File

@@ -76,7 +76,7 @@
</div> </div>
<div> <div>
<h2>AGV参数配置</h2> <h2>AGV参数配置</h2>
<div class="row mt16 o_settings_container"> <div class="row mt16 o_settings_container" id="agv_config">
<div class="col-12 col-lg-6 o_setting_box"> <div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane"/> <div class="o_setting_left_pane"/>
<div class="o_setting_right_pane"> <div class="o_setting_right_pane">

View File

@@ -16,6 +16,8 @@ class Action_Plan_All_Wizard(models.TransientModel):
# 选择生产线 # 选择生产线
production_line_id = fields.Many2one('sf.production.line', string=u'生产线', required=True) production_line_id = fields.Many2one('sf.production.line', string=u'生产线', required=True)
date_planned_start = fields.Datetime(string='计划开始时间', index=True, copy=False,
default=fields.Datetime.now)
# 接收传递过来的计划ID # 接收传递过来的计划ID
plan_ids = fields.Many2many('sf.production.plan', string=u'计划ID') plan_ids = fields.Many2many('sf.production.plan', string=u'计划ID')
@@ -33,6 +35,7 @@ class Action_Plan_All_Wizard(models.TransientModel):
# 拿到计划对象 # 拿到计划对象
plan_obj = self.env['sf.production.plan'].browse(plan.id) plan_obj = self.env['sf.production.plan'].browse(plan.id)
plan_obj.production_line_id = self.production_line_id.id plan_obj.production_line_id = self.production_line_id.id
plan.date_planned_start = self.date_planned_start
plan_obj.do_production_schedule() plan_obj.do_production_schedule()
# plan_obj.state = 'done' # plan_obj.state = 'done'
print('处理计划:', plan.id, '完成') print('处理计划:', plan.id, '完成')

View File

@@ -7,6 +7,7 @@
<form> <form>
<group> <group>
<field name="production_line_id" domain="[('name', 'ilike', 'CNC')]"/> <field name="production_line_id" domain="[('name', 'ilike', 'CNC')]"/>
<field name="date_planned_start"/>
</group> </group>
<footer> <footer>
<button string="确认排程" name="action_plan_all" type="object" class="btn-primary"/> <button string="确认排程" name="action_plan_all" type="object" class="btn-primary"/>

View File

@@ -48565,3 +48565,16 @@ msgstr ""
#: model:ir.model.fields.selection,name:sf_maintenance.selection__maintenance_equipment__heightened_way__chilunjia #: model:ir.model.fields.selection,name:sf_maintenance.selection__maintenance_equipment__heightened_way__chilunjia
msgid "齿轮架驱动" msgid "齿轮架驱动"
msgstr "" msgstr ""
#. module: sf_manufacturing
#. odoo-python
#: code:addons/sf_manufacturing/models/mrp_production.py:0
#: model:ir.actions.act_window,name:sf_manufacturing.action_sf_production_wizard
#: model:ir.model.fields.selection,name:sf_manufacturing.selection__mrp_production__state__scrap
#: model:ir.model.fields.selection,name:sf_manufacturing.selection__mrp_workorder__test_results__报废
#: model:ir.model.fields.selection,name:sf_manufacturing.selection__sf_detection_result__test_results__报废
#: model_terms:ir.ui.view,arch_db:sf_manufacturing.custom_mrp_production_form_view
#: model_terms:ir.ui.view,arch_db:sf_manufacturing.custom_view_mrp_production_filter
#, python-format
msgid "报废"
msgstr "报废"

View File

@@ -60,7 +60,7 @@ class ReSaleOrder(models.Model):
deadline_of_delivery, payments_way, pay_way): deadline_of_delivery, payments_way, pay_way):
now_time = datetime.datetime.now() now_time = datetime.datetime.now()
partner = self.get_customer() partner = self.get_customer()
data ={ data = {
'company_id': company_id.id, 'company_id': company_id.id,
'date_order': now_time, 'date_order': now_time,
'name': self.env['ir.sequence'].next_by_code('sale.order', sequence_date=now_time), 'name': self.env['ir.sequence'].next_by_code('sale.order', sequence_date=now_time),
@@ -79,7 +79,7 @@ class ReSaleOrder(models.Model):
if not isinstance(deadline_of_delivery, str): if not isinstance(deadline_of_delivery, str):
data.update({'deadline_of_delivery': deadline_of_delivery}) data.update({'deadline_of_delivery': deadline_of_delivery})
else: else:
if deadline_of_delivery!="False": if deadline_of_delivery != "False":
data.update({'deadline_of_delivery': deadline_of_delivery}) data.update({'deadline_of_delivery': deadline_of_delivery})
order_id = self.env['sale.order'].sudo().create(data) order_id = self.env['sale.order'].sudo().create(data)
@@ -225,45 +225,42 @@ class RePurchaseOrder(models.Model):
raise UserError('请对【产品】中的【税】进行选择') raise UserError('请对【产品】中的【税】进行选择')
def get_purchase_order(self, consecutive_process_parameters, production, product_id_to_production_names): def get_purchase_order(self, consecutive_process_parameters, production, product_id_to_production_names):
is_exist = True
server_product_process = [] server_product_process = []
production_process = product_id_to_production_names.get( production_process = product_id_to_production_names.get(
production.product_id.id) production.product_id.id)
for pp in consecutive_process_parameters: for pp in consecutive_process_parameters:
if pp.gain_way == '外协': if pp.gain_way == '外协':
server_product = self.env['product.template'].search( server_template = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', pp.id), [('server_product_process_parameters_id', '=', pp.id),
('detailed_type', '=', 'service')]) ('detailed_type', '=', 'service')])
purchase_order_line = self.env['purchase.order.line'].search( purchase_order_line = self.env['purchase.order.line'].search(
[('product_id', '=', server_product.id), ('product_qty', '=', len(production_process))]) [('product_id', '=', server_template.product_variant_id.id),
('product_qty', '=', len(production_process))], limit=1, order='id desc')
if not purchase_order_line: if not purchase_order_line:
is_exist = False
server_product_process.append((0, 0, { server_product_process.append((0, 0, {
'product_id': server_product.product_variant_id.id, 'product_id': server_template.product_variant_id.id,
'product_qty': len(production_process), 'product_qty': len(production_process),
'product_uom': server_product.uom_id.id 'product_uom': server_template.uom_id.id
})) }))
else: else:
for item in purchase_order_line: for item in purchase_order_line:
if production.name in production_process:
purchase_order = self.env['purchase.order'].search( purchase_order = self.env['purchase.order'].search(
[('state', '=', 'draft'), ('origin', 'ilike', production.name), [('state', '=', 'draft'), ('origin', '=', ','.join(production_process)),
('id', '=', item.order_id.id)]) ('id', '=', item.order_id.id)])
if not purchase_order: if not purchase_order:
is_exist = False
server_product_process.append((0, 0, { server_product_process.append((0, 0, {
'product_id': server_product.product_variant_id.id, 'product_id': server_template.product_variant_id.id,
'product_qty': len(production_process), 'product_qty': len(production_process),
'product_uom': server_product.uom_id.id 'product_uom': server_template.uom_id.id
})) }))
if is_exist is False: if server_product_process:
purchase_order = self.env['purchase.order'].search(
[('state', '=', 'draft'), ('origin', '=', ','.join(production_process))])
if not purchase_order:
self.env['purchase.order'].sudo().create({ self.env['purchase.order'].sudo().create({
'partner_id': server_product.seller_ids.partner_id.id, 'partner_id': server_template.seller_ids.partner_id.id,
'origin': ','.join(production_process), 'origin': ','.join(production_process),
'state': 'draft', 'state': 'draft',
'order_line': server_product_process}) 'order_line': server_product_process})
# self.env.cr.commit()
@api.onchange('order_line') @api.onchange('order_line')
def _onchange_order_line(self): def _onchange_order_line(self):

View File

@@ -50,6 +50,7 @@
('check_status', '!=', 'approved'),('state', 'in', ['draft','cancel']),'&amp;','&amp;',('check_status', ('check_status', '!=', 'approved'),('state', 'in', ['draft','cancel']),'&amp;','&amp;',('check_status',
'=', 'approved'),('state', 'in', ['sale','cancel']),('delivery_status', '!=', False)]} '=', 'approved'),('state', 'in', ['sale','cancel']),('delivery_status', '!=', False)]}
</attribute> </attribute>
<attribute name="string">拒绝接单</attribute>
</xpath> </xpath>
<xpath expr="//form/header/button[@name='action_draft']" position="attributes"> <xpath expr="//form/header/button[@name='action_draft']" position="attributes">
<attribute name="invisible">1</attribute> <attribute name="invisible">1</attribute>

View File

@@ -842,21 +842,21 @@
<group string="刀柄" attrs="{'invisible': [('handle_product_id', '=', False)]}" <group string="刀柄" attrs="{'invisible': [('handle_product_id', '=', False)]}"
col="1"> col="1">
<group attrs="{'invisible': [('dismantle_cause', 'not in', ['寿命到期报废','崩刀报废'])]}"> <group attrs="{'invisible': [('dismantle_cause', 'not in', ['寿命到期报废','崩刀报废'])]}">
<group col="3"> <!-- <group col="3">-->
<group> <group>
<field name="scrap_boolean" string="是否报废"/> <field name="scrap_boolean" string="是否报废" readonly="0"/>
</group>
<group></group>
<group>
<button string="报废" name="tool_scrap" type="object"
class="btn-primary" confirm="是否确认报废刀柄"
attrs="{'invisible': [('scrap_boolean', '=', True)]}"/>
<button string="取消" name="tool_no_scrap" type="object"
class="btn-primary" confirm="是否取消报废刀柄"
attrs="{'invisible': [('scrap_boolean', '=', False)]}"/>
<group></group>
</group>
</group> </group>
<!-- <group></group>-->
<!-- <group>-->
<!-- <button string="报废" name="tool_scrap" type="object"-->
<!-- class="btn-primary" confirm="是否确认报废刀柄"-->
<!-- attrs="{'invisible': [('scrap_boolean', '=', True)]}"/>-->
<!-- <button string="取消" name="tool_no_scrap" type="object"-->
<!-- class="btn-primary" confirm="是否取消报废刀柄"-->
<!-- attrs="{'invisible': [('scrap_boolean', '=', False)]}"/>-->
<!-- <group></group>-->
<!-- </group>-->
<!-- </group>-->
</group> </group>
<group> <group>
<field name="handle_rfid" string="Rfid"/> <field name="handle_rfid" string="Rfid"/>

View File

@@ -387,9 +387,9 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
lot_ids = self.env['stock.lot'].sudo().search([('rfid', '=', barcode)]) lot_ids = self.env['stock.lot'].sudo().search([('rfid', '=', barcode)])
if lot_ids: if lot_ids:
for lot_id in lot_ids: for lot_id in lot_ids:
if lot_id.quant_ids[-1].location_id.name in '刀具房': if lot_id.tool_material_status == '可用':
record.handle_code_id = lot_id.id record.handle_code_id = lot_id.id
elif lot_id.quant_ids[-1].location_id.name == '刀具组装位置': elif lot_id.quant_ids[-1].location_id.name in ['刀具组装位置']:
raise ValidationError('该刀柄已使用,请重新扫描!!!') raise ValidationError('该刀柄已使用,请重新扫描!!!')
else: else:
raise ValidationError('该刀柄未入库,请重新扫描!!!') raise ValidationError('该刀柄未入库,请重新扫描!!!')