Compare commits
2 Commits
feature/页面
...
release/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9719a2b5b | ||
|
|
4b5c3b383b |
@@ -206,7 +206,7 @@ class QualityCheck(models.Model):
|
|||||||
('NG', 'NG')
|
('NG', 'NG')
|
||||||
], string='出厂检验报告结果', default='OK')
|
], string='出厂检验报告结果', default='OK')
|
||||||
measure_operator = fields.Many2one('res.users', string='操机员')
|
measure_operator = fields.Many2one('res.users', string='操机员')
|
||||||
quality_manager = fields.Many2one('res.users', string='质检员', compute='_compute_quality_manager')
|
quality_manager = fields.Many2one('res.users', string='质检员', compute='_compute_quality_manager', store=True)
|
||||||
|
|
||||||
@api.depends('measure_line_ids')
|
@api.depends('measure_line_ids')
|
||||||
def _compute_quality_manager(self):
|
def _compute_quality_manager(self):
|
||||||
|
|||||||
@@ -267,7 +267,7 @@
|
|||||||
<field name="company_id" invisible="1"/>
|
<field name="company_id" invisible="1"/>
|
||||||
<field name="categ_type" invisible="1"/>
|
<field name="categ_type" invisible="1"/>
|
||||||
<field name="product_id" attrs="{'invisible' : [('measure_on', '=', 'operation')]}"/>
|
<field name="product_id" attrs="{'invisible' : [('measure_on', '=', 'operation')]}"/>
|
||||||
<field name="part_name" attrs="{'invisible': [('categ_type', '!=', '成品')], 'readonly': [('publish_status', '=', 'published')]}"/>
|
<field name="part_name" attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
|
||||||
<field name="part_number" attrs="{'invisible': [('categ_type', '!=', '成品')], 'readonly': [('publish_status', '=', 'published')]}"/>
|
<field name="part_number" attrs="{'invisible': [('categ_type', '!=', '成品')], 'readonly': [('publish_status', '=', 'published')]}"/>
|
||||||
<field name="material_name" attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
|
<field name="material_name" attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
|
||||||
<field name="total_qty" attrs="{'invisible': ['|', ('measure_on', '!=', 'product'), ('is_out_check', '=', False)]}"/>
|
<field name="total_qty" attrs="{'invisible': ['|', ('measure_on', '!=', 'product'), ('is_out_check', '=', False)]}"/>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import json
|
|||||||
import base64
|
import base64
|
||||||
import logging
|
import logging
|
||||||
import psycopg2
|
import psycopg2
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta
|
||||||
from odoo import http, fields
|
from odoo import http, fields
|
||||||
from odoo.http import request
|
from odoo.http import request
|
||||||
|
|
||||||
@@ -414,7 +414,7 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
|
|
||||||
# 工单计划量切换为CNC工单
|
# 工单计划量切换为CNC工单
|
||||||
plan_data_total_counts = work_order_obj.search_count(
|
plan_data_total_counts = work_order_obj.search_count(
|
||||||
[('production_line_id.name', '=', line), ('id', '!=', 8061),
|
[('production_id.production_line_id.name', '=', line),
|
||||||
('state', 'in', ['ready', 'progress', 'done']), ('routing_type', '=', 'CNC加工')])
|
('state', 'in', ['ready', 'progress', 'done']), ('routing_type', '=', 'CNC加工')])
|
||||||
|
|
||||||
# # 工单完成量
|
# # 工单完成量
|
||||||
@@ -423,13 +423,13 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
|
|
||||||
# 工单完成量切换为CNC工单
|
# 工单完成量切换为CNC工单
|
||||||
plan_data_finish_counts = work_order_obj.search_count(
|
plan_data_finish_counts = work_order_obj.search_count(
|
||||||
[('production_line_id.name', '=', line),
|
[('production_id.production_line_id.name', '=', line),
|
||||||
('state', 'in', ['done']), ('routing_type', '=', 'CNC加工')])
|
('state', 'in', ['done']), ('routing_type', '=', 'CNC加工')])
|
||||||
|
|
||||||
# 超期完成量
|
# 超期完成量
|
||||||
# 搜索所有已经完成的工单
|
# 搜索所有已经完成的工单
|
||||||
plan_data_overtime = work_order_obj.search([
|
plan_data_overtime = work_order_obj.search([
|
||||||
('production_line_id.name', '=', line),
|
('production_id.production_line_id.name', '=', line),
|
||||||
('state', 'in', ['done']),
|
('state', 'in', ['done']),
|
||||||
('routing_type', '=', 'CNC加工')
|
('routing_type', '=', 'CNC加工')
|
||||||
])
|
])
|
||||||
@@ -448,14 +448,9 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
])
|
])
|
||||||
|
|
||||||
# 过滤出那些检测结果状态为 '返工' 或 '报废' 的记录
|
# 过滤出那些检测结果状态为 '返工' 或 '报废' 的记录
|
||||||
# faulty_plans = plan_data.filtered(lambda p: any(
|
faulty_plans = plan_data.filtered(lambda p: any(
|
||||||
# result.test_results in ['返工', '报废'] for result in p.production_id.detection_result_ids
|
result.test_results in ['返工', '报废'] for result in p.production_id.detection_result_ids
|
||||||
# ))
|
))
|
||||||
|
|
||||||
faulty_plans = request.env['quality.check'].sudo().search([
|
|
||||||
('operation_id.name', '=', 'CNC加工'),
|
|
||||||
('quality_state', 'in', ['fail'])
|
|
||||||
])
|
|
||||||
|
|
||||||
# 查找制造订单取消与归档的数量
|
# 查找制造订单取消与归档的数量
|
||||||
cancel_order_count = production_obj.search_count(
|
cancel_order_count = production_obj.search_count(
|
||||||
@@ -572,7 +567,7 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
"""
|
"""
|
||||||
res = {'status': 1, 'message': '成功', 'data': {}}
|
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||||
# plan_obj = request.env['sf.production.plan'].sudo()
|
# plan_obj = request.env['sf.production.plan'].sudo()
|
||||||
# plan_obj = request.env['mrp.workorder'].sudo().search([('routing_type', '=', 'CNC加工')])
|
plan_obj = request.env['mrp.workorder'].sudo().search([('routing_type', '=', 'CNC加工')])
|
||||||
line_list = ast.literal_eval(kw['line_list'])
|
line_list = ast.literal_eval(kw['line_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('"')
|
||||||
@@ -622,19 +617,11 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
for time_interval in time_intervals:
|
for time_interval in time_intervals:
|
||||||
start_time, end_time = time_interval
|
start_time, end_time = time_interval
|
||||||
|
|
||||||
# orders = plan_obj.search([
|
orders = plan_obj.search([
|
||||||
# ('production_line_id.name', '=', line),
|
('production_id.production_line_id.name', '=', line),
|
||||||
# ('state', 'in', ['done']),
|
|
||||||
# (date_field_name, '>=', start_time.strftime('%Y-%m-%d %H:%M:%S')),
|
|
||||||
# (date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')) # 包括结束时间
|
|
||||||
# ])
|
|
||||||
|
|
||||||
orders = request.env['mrp.workorder'].sudo().search([
|
|
||||||
('routing_type', '=', 'CNC加工'), # 将第一个条件合并进来
|
|
||||||
('production_line_id.name', '=', line),
|
|
||||||
('state', 'in', ['done']),
|
('state', 'in', ['done']),
|
||||||
(date_field_name, '>=', start_time.strftime('%Y-%m-%d %H:%M:%S')),
|
(date_field_name, '>=', start_time.strftime('%Y-%m-%d %H:%M:%S')),
|
||||||
(date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S'))
|
(date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')) # 包括结束时间
|
||||||
])
|
])
|
||||||
|
|
||||||
# 使用小时和分钟作为键,确保每个小时的数据有独立的键
|
# 使用小时和分钟作为键,确保每个小时的数据有独立的键
|
||||||
@@ -651,22 +638,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 = request.env['mrp.workorder'].sudo().search(
|
orders = plan_obj.search([('production_id.production_line_id.name', '=', line), ('state', 'in', ['done']),
|
||||||
[('production_id.production_line_id.name', '=', line), ('state', 'in', ['done']),
|
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
|
||||||
('routing_type', '=', 'CNC加工'),
|
(date_field_name, '<', next_day.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'))
|
|
||||||
])
|
|
||||||
|
|
||||||
rework_orders = request.env['mrp.workorder'].sudo().search(
|
rework_orders = plan_obj.search(
|
||||||
[('production_id.production_line_id.name', '=', line), ('state', 'in', ['rework']),
|
[('production_id.production_line_id.name', '=', line), ('state', 'in', ['rework']),
|
||||||
('routing_type', '=', 'CNC加工'),
|
|
||||||
(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 = request.env['mrp.workorder'].sudo().search(
|
not_passed_orders = plan_obj.search(
|
||||||
[('production_id.production_line_id.name', '=', line), ('state', 'in', ['scrap', 'cancel']),
|
[('production_id.production_line_id.name', '=', line), ('state', 'in', ['scrap', 'cancel']),
|
||||||
('routing_type', '=', 'CNC加工'),
|
|
||||||
(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'))
|
||||||
])
|
])
|
||||||
@@ -768,14 +751,11 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
|
|
||||||
for line in line_list:
|
for line in line_list:
|
||||||
# 未完成订单
|
# 未完成订单
|
||||||
# not_done_orders = plan_obj.search(
|
not_done_orders = plan_obj.search(
|
||||||
# [('production_line_id.name', '=', line), ('state', 'not in', ['finished']),
|
[('production_line_id.name', '=', line), ('state', 'not in', ['finished']),
|
||||||
# ('production_id.state', 'not in', ['cancel', 'done']), ('active', '=', True)
|
('production_id.state', 'not in', ['cancel', 'done']), ('active', '=', True)
|
||||||
# ])
|
|
||||||
not_done_orders = request.env['mrp.workorder'].sudo().search(
|
|
||||||
[('production_line_id.name', '=', line), ('state', 'in', ['ready', 'progress']),
|
|
||||||
('routing_type', '=', 'CNC加工')
|
|
||||||
])
|
])
|
||||||
|
# print(not_done_orders)
|
||||||
|
|
||||||
# 完成订单
|
# 完成订单
|
||||||
# 获取当前时间,并计算24小时前的时间
|
# 获取当前时间,并计算24小时前的时间
|
||||||
@@ -827,18 +807,16 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
'draft': '待排程',
|
'draft': '待排程',
|
||||||
'done': '已排程',
|
'done': '已排程',
|
||||||
'processing': '生产中',
|
'processing': '生产中',
|
||||||
'finished': '已完成',
|
'finished': '已完成'
|
||||||
'ready': '待加工',
|
|
||||||
'progress': '生产中',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
line_dict = {
|
line_dict = {
|
||||||
'sequence': id_to_sequence[order.id],
|
'sequence': id_to_sequence[order.id],
|
||||||
'workorder_name': order.production_id.name,
|
'workorder_name': order.name,
|
||||||
'blank_name': blank_name,
|
'blank_name': blank_name,
|
||||||
'material': material,
|
'material': material,
|
||||||
'dimensions': dimensions,
|
'dimensions': dimensions,
|
||||||
'order_qty': 1,
|
'order_qty': order.product_qty,
|
||||||
'state': state_dict[order.state],
|
'state': state_dict[order.state],
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -919,17 +897,15 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
|
|
||||||
cur.execute(sql2, (item,))
|
cur.execute(sql2, (item,))
|
||||||
result2 = cur.fetchall()
|
result2 = cur.fetchall()
|
||||||
|
# print('result2========', result2)
|
||||||
|
#
|
||||||
for row in result:
|
for row in result:
|
||||||
res['data'][item] = {'idle_count': row[0]}
|
res['data'][item] = {'idle_count': row[0]}
|
||||||
alarm_count = []
|
alarm_count = []
|
||||||
for row in result2:
|
for row in result2:
|
||||||
alarm_count.append(row[1])
|
alarm_count.append(row[1])
|
||||||
if row[0]:
|
if row[0]:
|
||||||
if float(row[0]) >= 28800:
|
total_alarm_time += abs(float(row[0]))
|
||||||
continue
|
|
||||||
# total_alarm_time += abs(float(row[0]))
|
|
||||||
total_alarm_time += float(row[0])
|
|
||||||
else:
|
else:
|
||||||
total_alarm_time += 0.0
|
total_alarm_time += 0.0
|
||||||
if len(list(set(alarm_count))) == 1:
|
if len(list(set(alarm_count))) == 1:
|
||||||
@@ -939,7 +915,6 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
alarm_count_num = 1
|
alarm_count_num = 1
|
||||||
else:
|
else:
|
||||||
alarm_count_num = len(list(set(alarm_count)))
|
alarm_count_num = len(list(set(alarm_count)))
|
||||||
|
|
||||||
res['data'][item]['total_alarm_time'] = total_alarm_time / 3600
|
res['data'][item]['total_alarm_time'] = total_alarm_time / 3600
|
||||||
res['data'][item]['alarm_count_num'] = alarm_count_num
|
res['data'][item]['alarm_count_num'] = alarm_count_num
|
||||||
|
|
||||||
@@ -1357,7 +1332,7 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
for result in results:
|
for result in results:
|
||||||
alarm_last_24_nums.append(result[1])
|
alarm_last_24_nums.append(result[1])
|
||||||
if result[0]:
|
if result[0]:
|
||||||
if float(result[0]) >= 28800:
|
if float(result[0]) >= 1000:
|
||||||
continue
|
continue
|
||||||
alarm_last_24_time += float(result[0])
|
alarm_last_24_time += float(result[0])
|
||||||
else:
|
else:
|
||||||
@@ -1375,7 +1350,7 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
for result in results:
|
for result in results:
|
||||||
alarm_all_nums.append(result[1])
|
alarm_all_nums.append(result[1])
|
||||||
if result[0]:
|
if result[0]:
|
||||||
if float(result[0]) >= 28800:
|
if float(result[0]) >= 1000:
|
||||||
continue
|
continue
|
||||||
alarm_all_time += float(result[0])
|
alarm_all_time += float(result[0])
|
||||||
else:
|
else:
|
||||||
@@ -1410,207 +1385,3 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
return json.dumps(res)
|
return json.dumps(res)
|
||||||
|
|
||||||
@http.route('/api/utilization/rate', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
|
||||||
def UtilizationRate(self, **kw):
|
|
||||||
"""
|
|
||||||
获取稼动率
|
|
||||||
"""
|
|
||||||
logging.info("kw=:%s" % kw)
|
|
||||||
res = {'status': 1, 'message': '成功', 'data': {}}
|
|
||||||
# 获取请求的机床数据
|
|
||||||
machine_list = ast.literal_eval(kw['machine_list'])
|
|
||||||
line = kw['line']
|
|
||||||
orders = request.env['mrp.workorder'].sudo().search([
|
|
||||||
('routing_type', '=', 'CNC加工'), # 将第一个条件合并进来
|
|
||||||
('production_line_id.name', '=', line),
|
|
||||||
('state', 'in', ['done'])
|
|
||||||
])
|
|
||||||
|
|
||||||
faulty_plans = request.env['quality.check'].sudo().search([
|
|
||||||
('operation_id.name', '=', 'CNC加工'),
|
|
||||||
('quality_state', 'in', ['fail'])
|
|
||||||
])
|
|
||||||
|
|
||||||
# 计算时间范围
|
|
||||||
now = datetime.now()
|
|
||||||
today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
|
||||||
month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
|
|
||||||
|
|
||||||
total_power_on_time = 0
|
|
||||||
month_power_on_time = 0
|
|
||||||
today_power_on_time = 0
|
|
||||||
today_power_on_dict = {}
|
|
||||||
|
|
||||||
today_data = []
|
|
||||||
month_data = []
|
|
||||||
today_check_ng = []
|
|
||||||
month_check_ng = []
|
|
||||||
|
|
||||||
total_alarm_time = 0
|
|
||||||
today_alarm_time = 0
|
|
||||||
month_alarm_time = 0
|
|
||||||
|
|
||||||
for order in orders:
|
|
||||||
time = datetime.strptime(order.date_finished, "%Y-%m-%d %H:%M:%S")
|
|
||||||
if time >= today_start:
|
|
||||||
today_data.append(order)
|
|
||||||
if time >= month_start:
|
|
||||||
month_data.append(order)
|
|
||||||
|
|
||||||
for faulty_plan in faulty_plans:
|
|
||||||
time = faulty_plan.write_date
|
|
||||||
if time >= today_start:
|
|
||||||
today_check_ng.append(faulty_plan)
|
|
||||||
if time >= month_start:
|
|
||||||
month_check_ng.append(faulty_plan)
|
|
||||||
|
|
||||||
# 连接数据库
|
|
||||||
conn = psycopg2.connect(**db_config)
|
|
||||||
for item in machine_list:
|
|
||||||
with conn.cursor() as cur:
|
|
||||||
cur.execute("""
|
|
||||||
(
|
|
||||||
SELECT power_on_time, 'latest' AS record_type
|
|
||||||
FROM device_data
|
|
||||||
WHERE device_name = %s
|
|
||||||
AND power_on_time IS NOT NULL
|
|
||||||
ORDER BY time DESC
|
|
||||||
LIMIT 1
|
|
||||||
)
|
|
||||||
UNION ALL
|
|
||||||
(
|
|
||||||
SELECT power_on_time, 'month_first' AS record_type
|
|
||||||
FROM device_data
|
|
||||||
WHERE device_name = %s
|
|
||||||
AND power_on_time IS NOT NULL
|
|
||||||
AND time >= date_trunc('month', CURRENT_DATE) -- ✅ 修复日期函数
|
|
||||||
AND time < (date_trunc('month', CURRENT_DATE) + INTERVAL '1 month')::date
|
|
||||||
ORDER BY time ASC
|
|
||||||
LIMIT 1
|
|
||||||
)
|
|
||||||
UNION ALL
|
|
||||||
(
|
|
||||||
SELECT power_on_time, 'day_first' AS record_type
|
|
||||||
FROM device_data
|
|
||||||
WHERE device_name = %s
|
|
||||||
AND power_on_time IS NOT NULL
|
|
||||||
AND time::date = CURRENT_DATE -- ✅ 更高效的写法
|
|
||||||
ORDER BY time ASC
|
|
||||||
LIMIT 1
|
|
||||||
);
|
|
||||||
""", (item, item, item))
|
|
||||||
results = cur.fetchall()
|
|
||||||
print(results)
|
|
||||||
if len(results) >= 1:
|
|
||||||
total_power_on_time += convert_to_seconds(results[0][0])
|
|
||||||
else:
|
|
||||||
total_power_on_time += 0
|
|
||||||
if len(results) >= 2:
|
|
||||||
month_power_on_time += convert_to_seconds(results[1][0])
|
|
||||||
else:
|
|
||||||
month_power_on_time += 0
|
|
||||||
if len(results) >= 3:
|
|
||||||
today_power_on_time += convert_to_seconds(results[2][0])
|
|
||||||
today_power_on_dict[item] = today_power_on_time
|
|
||||||
else:
|
|
||||||
today_power_on_time += 0
|
|
||||||
print(total_power_on_time, month_power_on_time, today_power_on_time)
|
|
||||||
|
|
||||||
with conn.cursor() as cur:
|
|
||||||
cur.execute("""
|
|
||||||
SELECT DISTINCT ON (alarm_start_time) alarm_time, alarm_start_time
|
|
||||||
FROM device_data
|
|
||||||
WHERE device_name = %s AND alarm_start_time IS NOT NULL
|
|
||||||
ORDER BY alarm_start_time, time;
|
|
||||||
""", (item,))
|
|
||||||
results = cur.fetchall()
|
|
||||||
today_data = []
|
|
||||||
month_data = []
|
|
||||||
|
|
||||||
for record in results:
|
|
||||||
if record[0]:
|
|
||||||
if float(record[0]) >= 28800:
|
|
||||||
continue
|
|
||||||
total_alarm_time += float(record[0])
|
|
||||||
else:
|
|
||||||
total_alarm_time += 0.0
|
|
||||||
alarm_start = datetime.strptime(record[1], "%Y-%m-%d %H:%M:%S")
|
|
||||||
if alarm_start >= today_start:
|
|
||||||
today_data.append(record)
|
|
||||||
if alarm_start >= month_start:
|
|
||||||
month_data.append(record)
|
|
||||||
for today in today_data:
|
|
||||||
if today[0]:
|
|
||||||
if float(today[0]) >= 28800:
|
|
||||||
continue
|
|
||||||
today_alarm_time += float(today[0])
|
|
||||||
else:
|
|
||||||
today_alarm_time += 0.0
|
|
||||||
for month in month_data:
|
|
||||||
if month[0]:
|
|
||||||
if float(month[0]) >= 28800:
|
|
||||||
continue
|
|
||||||
month_alarm_time += float(month[0])
|
|
||||||
else:
|
|
||||||
month_alarm_time += 0.0
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
print('报警时间=============', total_alarm_time, month_alarm_time, today_alarm_time)
|
|
||||||
logging.info("报警时间=%s" % total_alarm_time)
|
|
||||||
logging.info("报警时间=%s" % month_alarm_time)
|
|
||||||
logging.info("报警时间=%s" % today_alarm_time)
|
|
||||||
# 计算时间开动率(累计、月、日)
|
|
||||||
if total_power_on_time:
|
|
||||||
total_power_on_rate = (total_power_on_time - total_alarm_time) / total_power_on_time
|
|
||||||
else:
|
|
||||||
total_power_on_rate = 0
|
|
||||||
if month_power_on_time:
|
|
||||||
month_power_on_rate = (total_power_on_time - month_power_on_time - month_alarm_time) / month_power_on_time
|
|
||||||
else:
|
|
||||||
month_power_on_rate = 0
|
|
||||||
if today_power_on_time:
|
|
||||||
today_power_on_rate = (total_power_on_time - today_power_on_time - today_alarm_time) / today_power_on_time
|
|
||||||
else:
|
|
||||||
today_power_on_rate = 0
|
|
||||||
print("总开动率: %s" % total_power_on_rate)
|
|
||||||
print("月开动率: %s" % month_power_on_rate)
|
|
||||||
print("日开动率: %s" % today_power_on_rate)
|
|
||||||
|
|
||||||
# 计算性能开动率(累计、月、日)
|
|
||||||
print('===========',orders)
|
|
||||||
print(len(orders))
|
|
||||||
total_performance_rate = len(orders) * 30 * 60 / (total_power_on_time - total_alarm_time)
|
|
||||||
month_performance_rate = len(month_data) * 30 * 60 / (month_power_on_time - month_alarm_time)
|
|
||||||
today_performance_rate = len(today_data) * 30 * 60 / (today_power_on_time - today_alarm_time) if today_power_on_time != 0 else 0
|
|
||||||
print("总性能率: %s" % total_performance_rate)
|
|
||||||
print("月性能率: %s" % month_performance_rate)
|
|
||||||
print("日性能率: %s" % today_performance_rate)
|
|
||||||
|
|
||||||
# 计算累计合格率
|
|
||||||
total_pass_rate = (len(orders) - len(today_check_ng)) / len(orders) if len(orders) != 0 else 0
|
|
||||||
month_pass_rate = (len(month_data) - len(month_check_ng)) / len(month_data) if len(month_data) != 0 else 0
|
|
||||||
today_pass_rate = (len(today_data) - len(today_check_ng)) / len(today_data) if len(today_data) != 0 else 0
|
|
||||||
print("总合格率: %s" % total_pass_rate)
|
|
||||||
print("月合格率: %s" % month_pass_rate)
|
|
||||||
print("日合格率: %s" % today_pass_rate)
|
|
||||||
|
|
||||||
# # 返回数据
|
|
||||||
# res['data'][item] = {
|
|
||||||
# 'total_utilization_rate': total_power_on_rate * total_performance_rate * total_pass_rate,
|
|
||||||
# 'month_utilization_rate': month_power_on_rate * month_performance_rate * month_pass_rate,
|
|
||||||
# 'today_utilization_rate': today_power_on_rate * today_performance_rate * today_pass_rate,
|
|
||||||
# }
|
|
||||||
res['data'] = {
|
|
||||||
'total_utilization_rate': total_power_on_rate * total_performance_rate * total_pass_rate,
|
|
||||||
'month_utilization_rate': month_power_on_rate * month_performance_rate * month_pass_rate,
|
|
||||||
'today_utilization_rate': today_power_on_rate * today_performance_rate * today_pass_rate,
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.dumps(res)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="view_delivery_record_form_inherit_sf" model="ir.ui.view">
|
|
||||||
<field name="name">delivery.record.form.inherit.sf</field>
|
|
||||||
<field name="model">mrp.workorder</field>
|
|
||||||
<field name="inherit_id" ref="mrp.mrp_production_workorder_form_view_inherit"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<xpath expr="//page[last()-3]" position="before">
|
|
||||||
<!-- <page string="下发记录" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>-->
|
|
||||||
<page string="下发记录" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "HDR")]}'>
|
|
||||||
<field name="delivery_records">
|
|
||||||
<tree create="false">
|
|
||||||
<field name="delivery_type"/>
|
|
||||||
<field name="delivery_time"/>
|
|
||||||
<field name="influence_record"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
|
|
||||||
|
|
||||||
</page>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
<!-- <record id="seqence_b_purchase_order" model='ir.sequence'> -->
|
<!-- <record id="seqence_b_purchase_order" model='ir.sequence'> -->
|
||||||
<!-- <field name='name'>Purchase Order</field> -->
|
<!-- <field name='name'>Purchase Order</field> -->
|
||||||
<!-- <field name='code'>sf_machine_connect.delivery.record</field> -->
|
<!-- <field name='code'>sf_machine_connect.delivery.record</field> -->
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
<record id="view_machine_info_form_inherit_sf" model="ir.ui.view">
|
<record id="view_machine_info_form_inherit_sf" model="ir.ui.view">
|
||||||
<field name="name">machine.info.form.inherit.sf</field>
|
<field name="name">machine.info.form.inherit.sf</field>
|
||||||
<field name="model">mrp.workorder</field>
|
<field name="model">mrp.workorder</field>
|
||||||
<field name="inherit_id" ref="mrp.mrp_production_workorder_form_view_inherit"/>
|
<field name="inherit_id" ref="sf_manufacturing.view_mrp_production_workorder_tray_form_inherit_sf"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//page[last()-3]" position="before">
|
<xpath expr="//page[@name='CMR']" position="after">
|
||||||
<page string="机床信息" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "MTI")]}'>
|
<page string="机床信息" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "MTI")]}'>
|
||||||
<group string="机床信息">
|
<group string="机床信息">
|
||||||
<group>
|
<group>
|
||||||
@@ -33,6 +33,15 @@
|
|||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
</page>
|
</page>
|
||||||
|
<page string="下发记录" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "HDR")]}'>
|
||||||
|
<field name="delivery_records">
|
||||||
|
<tree create="false">
|
||||||
|
<field name="delivery_type"/>
|
||||||
|
<field name="delivery_time"/>
|
||||||
|
<field name="influence_record"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
@@ -1,57 +1,72 @@
|
|||||||
<odoo>
|
<odoo>
|
||||||
<data noupdate="1">
|
<data noupdate="0">
|
||||||
<record model="sf.work.individuation.page" id="sf_work_individuation_page_1">
|
|
||||||
<field name="code">PTD</field>
|
|
||||||
<field name="name">后置三元检测</field>
|
|
||||||
</record>
|
|
||||||
<record model="sf.work.individuation.page" id="sf_work_individuation_page_2">
|
<record model="sf.work.individuation.page" id="sf_work_individuation_page_2">
|
||||||
<field name="code">WCP</field>
|
<field name="code">WCP</field>
|
||||||
<field name="name">工件装夹</field>
|
<field name="name">工件装夹</field>
|
||||||
|
<field name="sequence">10</field>
|
||||||
</record>
|
</record>
|
||||||
<record model="sf.work.individuation.page" id="sf_work_individuation_page_3">
|
<record model="sf.work.individuation.page" id="sf_work_individuation_page_3">
|
||||||
<field name="code">ITD_PP</field>
|
<field name="code">ITD_PP</field>
|
||||||
<field name="name">前置三元检测定位参数</field>
|
<field name="name">前置三元检测定位参数</field>
|
||||||
</record>
|
<field name="sequence">20</field>
|
||||||
<record model="sf.work.individuation.page" id="sf_work_individuation_page_4">
|
|
||||||
<field name="code">2D_MD</field>
|
|
||||||
<field name="name">2D加工图纸</field>
|
|
||||||
</record>
|
|
||||||
<record model="sf.work.individuation.page" id="sf_work_individuation_page_5">
|
|
||||||
<field name="code">QIS</field>
|
|
||||||
<field name="name">质检标准</field>
|
|
||||||
</record>
|
</record>
|
||||||
<record model="sf.work.individuation.page" id="sf_work_individuation_page_6">
|
<record model="sf.work.individuation.page" id="sf_work_individuation_page_6">
|
||||||
<field name="code">WD</field>
|
<field name="code">WD</field>
|
||||||
<field name="name">工件配送</field>
|
<field name="name">工件配送</field>
|
||||||
|
<field name="sequence">30</field>
|
||||||
</record>
|
</record>
|
||||||
<record model="sf.work.individuation.page" id="sf_work_individuation_page_9">
|
<record model="sf.work.individuation.page" id="sf_work_individuation_page_9">
|
||||||
<field name="code">CNC_P</field>
|
<field name="code">CNC_P</field>
|
||||||
<field name="name">CNC程序</field>
|
<field name="name">CNC程序</field>
|
||||||
|
<field name="sequence">40</field>
|
||||||
</record>
|
</record>
|
||||||
<record model="sf.work.individuation.page" id="sf_work_individuation_page_10">
|
<record model="sf.work.individuation.page" id="sf_work_individuation_page_10">
|
||||||
<field name="code">CMM_P</field>
|
<field name="code">CMM_P</field>
|
||||||
<field name="name">CMM程序</field>
|
<field name="name">CMM程序</field>
|
||||||
|
<field name="sequence">50</field>
|
||||||
</record>
|
</record>
|
||||||
<record model="sf.work.individuation.page" id="sf_work_individuation_page_11">
|
<record model="sf.work.individuation.page" id="sf_work_individuation_page_1">
|
||||||
<field name="code">MTI</field>
|
<field name="code">PTD</field>
|
||||||
<field name="name">机床信息</field>
|
<field name="name">后置三元检测</field>
|
||||||
</record>
|
<field name="sequence">60</field>
|
||||||
<record model="sf.work.individuation.page" id="sf_work_individuation_page_12">
|
|
||||||
<field name="code">HDR</field>
|
|
||||||
<field name="name">下发记录</field>
|
|
||||||
</record>
|
|
||||||
<record model="sf.work.individuation.page" id="sf_work_individuation_page_13">
|
|
||||||
<field name="code">ER</field>
|
|
||||||
<field name="name">异常记录</field>
|
|
||||||
</record>
|
</record>
|
||||||
<record model="sf.work.individuation.page" id="sf_work_individuation_page_14">
|
<record model="sf.work.individuation.page" id="sf_work_individuation_page_14">
|
||||||
<field name="code">DCP</field>
|
<field name="code">DCP</field>
|
||||||
<field name="name">解除装夹</field>
|
<field name="name">解除装夹</field>
|
||||||
|
<field name="sequence">70</field>
|
||||||
|
</record>
|
||||||
|
<record model="sf.work.individuation.page" id="sf_work_individuation_page_4">
|
||||||
|
<field name="code">2D_MD</field>
|
||||||
|
<field name="name">2D加工图纸</field>
|
||||||
|
<field name="sequence">80</field>
|
||||||
|
</record>
|
||||||
|
<record model="sf.work.individuation.page" id="sf_work_individuation_page_5">
|
||||||
|
<field name="code">QIS</field>
|
||||||
|
<field name="name">质检标准</field>
|
||||||
|
<field name="sequence">90</field>
|
||||||
</record>
|
</record>
|
||||||
<record model="sf.work.individuation.page" id="sf_work_individuation_page_15">
|
<record model="sf.work.individuation.page" id="sf_work_individuation_page_15">
|
||||||
<field name="code">CMR</field>
|
<field name="code">CMR</field>
|
||||||
<field name="name">开料要求</field>
|
<field name="name">开料要求</field>
|
||||||
|
<field name="sequence">100</field>
|
||||||
</record>
|
</record>
|
||||||
|
<record model="sf.work.individuation.page" id="sf_work_individuation_page_11">
|
||||||
|
<field name="code">MTI</field>
|
||||||
|
<field name="name">机床信息</field>
|
||||||
|
<field name="sequence">110</field>
|
||||||
|
</record>
|
||||||
|
<record model="sf.work.individuation.page" id="sf_work_individuation_page_12">
|
||||||
|
<field name="code">HDR</field>
|
||||||
|
<field name="name">下发记录</field>
|
||||||
|
<field name="sequence">120</field>
|
||||||
|
</record>
|
||||||
|
<record model="sf.work.individuation.page" id="sf_work_individuation_page_13">
|
||||||
|
<field name="code">ER</field>
|
||||||
|
<field name="name">异常记录</field>
|
||||||
|
<field name="sequence">130</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- 原生页签先不进行配置 -->
|
<!-- 原生页签先不进行配置 -->
|
||||||
<!-- <record model="sf.work.individuation.page" id="sf_work_individuation_page_7">-->
|
<!-- <record model="sf.work.individuation.page" id="sf_work_individuation_page_7">-->
|
||||||
|
|||||||
@@ -87,12 +87,11 @@ class AgvScheduling(models.Model):
|
|||||||
agv_route_type: AGV任务类型
|
agv_route_type: AGV任务类型
|
||||||
workorders: 工单
|
workorders: 工单
|
||||||
"""
|
"""
|
||||||
scheduling = None
|
|
||||||
_logger.info('创建AGV调度任务\r\n起点为【%s】,任务类型为【%s】,工单为【%s】' % (agv_start_site_name, agv_route_type, workorders))
|
_logger.info('创建AGV调度任务\r\n起点为【%s】,任务类型为【%s】,工单为【%s】' % (agv_start_site_name, agv_route_type, workorders))
|
||||||
if not workorders:
|
if not workorders:
|
||||||
raise UserError(_('工单不能为空'))
|
raise UserError(_('工单不能为空'))
|
||||||
agv_start_sites = self.env['sf.agv.site'].sudo().search([('name', '=', agv_start_site_name)])
|
agv_start_site = self.env['sf.agv.site'].sudo().search([('name', '=', agv_start_site_name)], limit=1)
|
||||||
if not agv_start_sites:
|
if not agv_start_site:
|
||||||
raise UserError(_('不存在名称为【%s】的接驳站,请先创建!' % agv_start_site_name))
|
raise UserError(_('不存在名称为【%s】的接驳站,请先创建!' % agv_start_site_name))
|
||||||
# 如果存在相同任务类型工单的AGV调度任务,则提示错误
|
# 如果存在相同任务类型工单的AGV调度任务,则提示错误
|
||||||
agv_scheduling = self.sudo().search([
|
agv_scheduling = self.sudo().search([
|
||||||
@@ -108,32 +107,24 @@ class AgvScheduling(models.Model):
|
|||||||
(','.join(repetitive_workorders.mapped('production_id.name')), agv_scheduling.name)
|
(','.join(repetitive_workorders.mapped('production_id.name')), agv_scheduling.name)
|
||||||
)
|
)
|
||||||
|
|
||||||
# 如果只有唯一任务路线,则自动赋予终点接驳站跟任务名称
|
|
||||||
agv_routes = self.env['sf.agv.task.route'].sudo().search([
|
|
||||||
('route_type', '=', agv_route_type),
|
|
||||||
('start_site_id', 'in', agv_start_sites.ids)
|
|
||||||
])
|
|
||||||
vals = {
|
vals = {
|
||||||
|
'start_site_id': agv_start_site.id,
|
||||||
'agv_route_type': agv_route_type,
|
'agv_route_type': agv_route_type,
|
||||||
'workorder_ids': workorders.ids,
|
'workorder_ids': workorders.ids,
|
||||||
# 'workpiece_delivery_ids': deliveries.mapped('id') if deliveries else [],
|
# 'workpiece_delivery_ids': deliveries.mapped('id') if deliveries else [],
|
||||||
'task_create_time': fields.Datetime.now()
|
'task_create_time': fields.Datetime.now()
|
||||||
}
|
}
|
||||||
|
# 如果只有唯一任务路线,则自动赋予终点接驳站跟任务名称
|
||||||
|
agv_routes = self.env['sf.agv.task.route'].sudo().search([
|
||||||
|
('route_type', '=', agv_route_type),
|
||||||
|
('start_site_id', '=', agv_start_site.id)
|
||||||
|
])
|
||||||
if not agv_routes:
|
if not agv_routes:
|
||||||
raise UserError(_('不存在起点为【%s】的【%s】任务路线,请先创建!' % (agv_start_site_name, agv_route_type)))
|
raise UserError(_('不存在起点为【%s】的【%s】任务路线,请先创建!' % (agv_start_site_name, agv_route_type)))
|
||||||
# 如果路线中包含起点与终点相同的接驳站,则不创建AGV调度任务
|
|
||||||
if agv_routes.filtered(lambda r: r.start_site_id.name == r.end_site_id.name):
|
|
||||||
return True
|
|
||||||
# 配送类型相同的接驳站为同一个,取第一个即可
|
|
||||||
vals.update({
|
|
||||||
'start_site_id': agv_routes[0].start_site_id.id,
|
|
||||||
})
|
|
||||||
idle_route = None
|
idle_route = None
|
||||||
if len(agv_routes) == 1:
|
if len(agv_routes) == 1:
|
||||||
idle_route = agv_routes[0]
|
idle_route = agv_routes[0]
|
||||||
vals.update({
|
vals.update({'end_site_id': idle_route.end_site_id.id, 'agv_route_id': idle_route.id})
|
||||||
'end_site_id': idle_route.end_site_id.id, 'agv_route_id': idle_route.id
|
|
||||||
})
|
|
||||||
else:
|
else:
|
||||||
# 判断终点接驳站是否为空闲
|
# 判断终点接驳站是否为空闲
|
||||||
idle_routes = agv_routes.filtered(lambda r: r.end_site_id.state == '空闲')
|
idle_routes = agv_routes.filtered(lambda r: r.end_site_id.state == '空闲')
|
||||||
@@ -141,10 +132,7 @@ class AgvScheduling(models.Model):
|
|||||||
# 将空闲的路线按照终点接驳站名称排序
|
# 将空闲的路线按照终点接驳站名称排序
|
||||||
idle_routes = sorted(idle_routes, key=lambda r: r.end_site_id.name)
|
idle_routes = sorted(idle_routes, key=lambda r: r.end_site_id.name)
|
||||||
idle_route = idle_routes[0]
|
idle_route = idle_routes[0]
|
||||||
vals.update({
|
vals.update({'end_site_id': idle_route.end_site_id.id, 'agv_route_id': idle_route.id})
|
||||||
'end_site_id': idle_route.end_site_id.id, 'agv_route_id': idle_route.id
|
|
||||||
})
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
scheduling = self.env['sf.agv.scheduling'].sudo().create(vals)
|
scheduling = self.env['sf.agv.scheduling'].sudo().create(vals)
|
||||||
# 触发空闲接驳站状态更新,触发新任务下发
|
# 触发空闲接驳站状态更新,触发新任务下发
|
||||||
@@ -154,7 +142,7 @@ class AgvScheduling(models.Model):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
_logger.error('添加AGV调度任务失败: %s', e)
|
_logger.error('添加AGV调度任务失败: %s', e)
|
||||||
raise UserError(_('添加AGV调度任务失败: %s', e))
|
raise UserError(_('添加AGV调度任务失败: %s', e))
|
||||||
|
|
||||||
return scheduling
|
return scheduling
|
||||||
|
|
||||||
def on_site_state_change(self, agv_site_id, agv_site_state):
|
def on_site_state_change(self, agv_site_id, agv_site_state):
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class AgvSetting(models.Model):
|
|||||||
|
|
||||||
# name必须唯一
|
# name必须唯一
|
||||||
_sql_constraints = [
|
_sql_constraints = [
|
||||||
('name_uniq', 'unique (name, workcenter_id)', '同一工作中心的站点编号必须唯一!'),
|
('name_uniq', 'unique (name)', '站点编号必须唯一!'),
|
||||||
]
|
]
|
||||||
|
|
||||||
# def update_site_state(self):
|
# def update_site_state(self):
|
||||||
@@ -68,12 +68,11 @@ class AgvSetting(models.Model):
|
|||||||
"""
|
"""
|
||||||
if isinstance(agv_site_state_arr, dict):
|
if isinstance(agv_site_state_arr, dict):
|
||||||
for agv_site_name, is_occupy in agv_site_state_arr.items():
|
for agv_site_name, is_occupy in agv_site_state_arr.items():
|
||||||
agv_sites = self.env['sf.agv.site'].sudo().search([('name', '=', agv_site_name)])
|
agv_site = self.env['sf.agv.site'].sudo().search([('name', '=', agv_site_name)])
|
||||||
if agv_sites:
|
if agv_site:
|
||||||
agv_sites.state = is_occupy
|
agv_site.state = is_occupy
|
||||||
if notify:
|
if notify:
|
||||||
for agv_site in agv_sites:
|
self.env['sf.agv.scheduling'].on_site_state_change(agv_site.id, agv_site.state)
|
||||||
self.env['sf.agv.scheduling'].on_site_state_change(agv_site.id, agv_site.state)
|
|
||||||
else:
|
else:
|
||||||
_logger.error("更新失败:接驳站站点错误!%s" % agv_site_name)
|
_logger.error("更新失败:接驳站站点错误!%s" % agv_site_name)
|
||||||
raise UserError("更新失败:接驳站站点错误!")
|
raise UserError("更新失败:接驳站站点错误!")
|
||||||
|
|||||||
@@ -107,6 +107,8 @@ class ResMrpRoutingWorkcenter(models.Model):
|
|||||||
|
|
||||||
class WorkIndividuationPage(models.Model):
|
class WorkIndividuationPage(models.Model):
|
||||||
_name = 'sf.work.individuation.page'
|
_name = 'sf.work.individuation.page'
|
||||||
|
_order = 'sequence'
|
||||||
|
|
||||||
code = fields.Char('编号')
|
code = fields.Char('编号')
|
||||||
name = fields.Char('名称')
|
name = fields.Char('名称')
|
||||||
|
sequence = fields.Integer('序号')
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from odoo.addons.sf_mrs_connect.models.ftp_operate import FtpController
|
|||||||
|
|
||||||
class ResMrpWorkOrder(models.Model):
|
class ResMrpWorkOrder(models.Model):
|
||||||
_inherit = 'mrp.workorder'
|
_inherit = 'mrp.workorder'
|
||||||
|
_order = 'sequence asc'
|
||||||
_description = '工单'
|
_description = '工单'
|
||||||
|
|
||||||
product_tmpl_name = fields.Char('坯料产品名称', related='production_bom_id.bom_line_ids.product_id.name')
|
product_tmpl_name = fields.Char('坯料产品名称', related='production_bom_id.bom_line_ids.product_id.name')
|
||||||
@@ -1749,23 +1750,6 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
self.check_ids.filtered(lambda ch: ch.is_inspect is True and ch.quality_state == 'waiting').write(
|
self.check_ids.filtered(lambda ch: ch.is_inspect is True and ch.quality_state == 'waiting').write(
|
||||||
{'quality_state': 'none'})
|
{'quality_state': 'none'})
|
||||||
|
|
||||||
@api.model
|
|
||||||
def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
|
|
||||||
aggregate_field = 'create_date:max'
|
|
||||||
if aggregate_field not in fields:
|
|
||||||
fields.append(aggregate_field)
|
|
||||||
orderby = "create_date desc"
|
|
||||||
|
|
||||||
return super(ResMrpWorkOrder, self).read_group(
|
|
||||||
domain,
|
|
||||||
fields,
|
|
||||||
groupby,
|
|
||||||
offset=offset,
|
|
||||||
limit=limit,
|
|
||||||
orderby=orderby,
|
|
||||||
lazy=lazy
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CNCprocessing(models.Model):
|
class CNCprocessing(models.Model):
|
||||||
_name = 'sf.cnc.processing'
|
_name = 'sf.cnc.processing'
|
||||||
|
|||||||
@@ -755,24 +755,6 @@ class StockPicking(models.Model):
|
|||||||
if move_id.product_id.tracking in ['serial', 'lot'] and not move_id.move_line_nosuggest_ids:
|
if move_id.product_id.tracking in ['serial', 'lot'] and not move_id.move_line_nosuggest_ids:
|
||||||
move_id.action_show_details()
|
move_id.action_show_details()
|
||||||
|
|
||||||
@api.model
|
|
||||||
def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
|
|
||||||
aggregate_field = 'create_date:max'
|
|
||||||
if aggregate_field not in fields:
|
|
||||||
fields.append(aggregate_field)
|
|
||||||
|
|
||||||
orderby = "create_date desc"
|
|
||||||
|
|
||||||
return super(StockPicking, self).read_group(
|
|
||||||
domain,
|
|
||||||
fields,
|
|
||||||
groupby,
|
|
||||||
offset=offset,
|
|
||||||
limit=limit,
|
|
||||||
orderby=orderby,
|
|
||||||
lazy=lazy
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ReStockMove(models.Model):
|
class ReStockMove(models.Model):
|
||||||
_inherit = 'stock.move'
|
_inherit = 'stock.move'
|
||||||
|
|||||||
@@ -15,23 +15,6 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="view_agv_site_form" model="ir.ui.view">
|
|
||||||
<field name="name">agv.site.form</field>
|
|
||||||
<field name="model">sf.agv.site</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<form create="false" edit="false">
|
|
||||||
<sheet>
|
|
||||||
<group>
|
|
||||||
<field name="name" readonly="1" required="1"/>
|
|
||||||
<field name="workcenter_id" readonly="1" required="1" options="{'no_create': True}"/>
|
|
||||||
<field name="state" readonly="1" required="1"/>
|
|
||||||
<field name="divide_the_work" readonly="1" required="1"/>
|
|
||||||
</group>
|
|
||||||
</sheet>
|
|
||||||
</form>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="action_agv_site_form" model="ir.actions.act_window">
|
<record id="action_agv_site_form" model="ir.actions.act_window">
|
||||||
<field name="name">AGV站点</field>
|
<field name="name">AGV站点</field>
|
||||||
<field name="res_model">sf.agv.site</field>
|
<field name="res_model">sf.agv.site</field>
|
||||||
@@ -56,8 +39,7 @@
|
|||||||
<field name="route_type" string="类型" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
|
<field name="route_type" string="类型" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
|
||||||
<field name="start_site_id" required="1" options="{'no_create': True}" string="起点接驳站"
|
<field name="start_site_id" required="1" options="{'no_create': True}" string="起点接驳站"
|
||||||
attrs="{'readonly': [('id', '!=', False)]}"/>
|
attrs="{'readonly': [('id', '!=', False)]}"/>
|
||||||
<field name="end_site_id" required="1" options="{'no_create': True}" string="终点接驳站"
|
<field name="end_site_id" required="1" options="{'no_create': True}" string="终点接驳站"/>
|
||||||
attrs="{'readonly': [('id', '!=', False)]}"/>
|
|
||||||
<!-- <field name="destination_production_line_id" required="1" options="{'no_create': True}"-->
|
<!-- <field name="destination_production_line_id" required="1" options="{'no_create': True}"-->
|
||||||
<!-- attrs="{'readonly': [('id', '!=', False)]}"/>-->
|
<!-- attrs="{'readonly': [('id', '!=', False)]}"/>-->
|
||||||
<field name="workcenter_id"/>
|
<field name="workcenter_id"/>
|
||||||
|
|||||||
@@ -455,7 +455,7 @@
|
|||||||
<field name="inherit_id" ref="mrp.mrp_production_workorder_tree_editable_view"/>
|
<field name="inherit_id" ref="mrp.mrp_production_workorder_tree_editable_view"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//tree" position="attributes">
|
<xpath expr="//tree" position="attributes">
|
||||||
<attribute name="default_order">create_date desc</attribute>
|
<attribute name="default_order">sequence</attribute>
|
||||||
<attribute name="decoration-warning">delivery_warning == 'warning'</attribute>
|
<attribute name="decoration-warning">delivery_warning == 'warning'</attribute>
|
||||||
<attribute name="decoration-danger">delivery_warning == 'overdue'</attribute>
|
<attribute name="decoration-danger">delivery_warning == 'overdue'</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|||||||
@@ -226,22 +226,6 @@
|
|||||||
<!-- string="返工"-->
|
<!-- string="返工"-->
|
||||||
<!-- attrs='{"invisible": [("rework_flag","=",True)]}' confirm="是否返工"/>-->
|
<!-- attrs='{"invisible": [("rework_flag","=",True)]}' confirm="是否返工"/>-->
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//page[1]" position="before">
|
|
||||||
<page string="开料要求" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "CMR")]}'>
|
|
||||||
<group>
|
|
||||||
<group>
|
|
||||||
<field name="product_tmpl_id_materials_id" widget="many2one"/>
|
|
||||||
<field name="product_tmpl_id_materials_type_id" widget="many2one"/>
|
|
||||||
|
|
||||||
</group>
|
|
||||||
<group>
|
|
||||||
<field name="product_tmpl_id_length"/>
|
|
||||||
<field name="product_tmpl_id_width"/>
|
|
||||||
<field name="product_tmpl_id_height"/>
|
|
||||||
</group>
|
|
||||||
</group>
|
|
||||||
</page>
|
|
||||||
</xpath>
|
|
||||||
<xpath expr="//label[1]" position="before">
|
<xpath expr="//label[1]" position="before">
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<field name="production_id" invisible="0"/>
|
<field name="production_id" invisible="0"/>
|
||||||
@@ -325,6 +309,8 @@
|
|||||||
|
|
||||||
|
|
||||||
<xpath expr="//page[1]" position="before">
|
<xpath expr="//page[1]" position="before">
|
||||||
|
<field name="results" invisible="1"/>
|
||||||
|
<field name="individuation_page_list" invisible="1"/>
|
||||||
<page string="工件装夹" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "WCP")]}'>
|
<page string="工件装夹" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "WCP")]}'>
|
||||||
<group>
|
<group>
|
||||||
<!-- <field name="_barcode_scanned" widget="barcode_handler"/> -->
|
<!-- <field name="_barcode_scanned" widget="barcode_handler"/> -->
|
||||||
@@ -503,7 +489,6 @@
|
|||||||
<field name='X_deviation_angle' readonly="1"/>
|
<field name='X_deviation_angle' readonly="1"/>
|
||||||
</group>
|
</group>
|
||||||
</page>
|
</page>
|
||||||
|
|
||||||
<page string="工件配送" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "WD")]}'>
|
<page string="工件配送" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "WD")]}'>
|
||||||
<field name="workpiece_delivery_ids">
|
<field name="workpiece_delivery_ids">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
@@ -523,58 +508,6 @@
|
|||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</page>
|
</page>
|
||||||
|
|
||||||
</xpath>
|
|
||||||
|
|
||||||
<xpath expr="//form//header" position="inside">
|
|
||||||
<button type="object" class="oe_highlight jikimo_button_confirm" name="get_three_check_datas"
|
|
||||||
string="获取数据"
|
|
||||||
attrs='{"invisible": ["|", ("state","!=","progress"), ("routing_type","!=","装夹预调")]}'/>
|
|
||||||
</xpath>
|
|
||||||
|
|
||||||
<!-- =====原生页签,暂时不进行配置===== -->
|
|
||||||
<!-- <xpath expr="//page[@name='components']" position="attributes">-->
|
|
||||||
<!-- <attribute name="attrs">{"invisible": ["!", ("individuation_page_list", "ilike", "ML")]}</attribute>-->
|
|
||||||
<!-- </xpath>-->
|
|
||||||
<!-- <xpath expr="//page[@name='time_tracking']" position="attributes">-->
|
|
||||||
<!-- <attribute name="attrs">{"invisible": ["!", ("individuation_page_list", "ilike", "TT")]}</attribute>-->
|
|
||||||
<!-- </xpath>-->
|
|
||||||
<!-- ============================= -->
|
|
||||||
|
|
||||||
<xpath expr="//page[1]" position="before">
|
|
||||||
<field name="results" invisible="1"/>
|
|
||||||
<field name="individuation_page_list" invisible="1"/>
|
|
||||||
<page string="后置三元检测" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "PTD")]}'>
|
|
||||||
<group>
|
|
||||||
<field name="test_results"
|
|
||||||
attrs='{"readonly":["&","|",("state","!=","to be detected"), "|",("routing_type","=","CNC加工"),("is_inspect", "=", True),("state","in",["done","rework"])],
|
|
||||||
"invisible":[("results","!=",False)]}'/>
|
|
||||||
<!-- <field name="is_remanufacture" attrs='{"invisible":[("test_results","!=","报废")]}'/>-->
|
|
||||||
<!-- <field name="is_fetchcnc"-->
|
|
||||||
<!-- attrs='{"invisible":["|",("test_results","=","合格"),("is_remanufacture","=",False)]}'/>-->
|
|
||||||
<field name="reason"
|
|
||||||
attrs='{"required":[("test_results","!=","合格")],"invisible":[("test_results","=","合格")],"readonly":[("state","in",("done", "rework"))]}'/>
|
|
||||||
<field name="detailed_reason"
|
|
||||||
attrs='{"required":[("test_results","!=","合格")],"invisible":[("test_results","=","合格")],"readonly":[("state","in",("done", "rework"))]}'/>
|
|
||||||
<!-- <field name="results" readonly="1" attrs='{"invisible":[("results","!=","合格")]}'/>-->
|
|
||||||
<field name="detection_report" attrs='{"invisible":[("results","!=",False)]}'
|
|
||||||
widget="pdf_viewer" readonly="1"/>
|
|
||||||
</group>
|
|
||||||
<!-- <div class="col-12 col-lg-6 o_setting_box">-->
|
|
||||||
<!-- <button type="object" class="oe_highlight" name="recreateManufacturingOrWorkerOrder"-->
|
|
||||||
<!-- string="检测确认"-->
|
|
||||||
<!-- attrs='{"invisible": ["|","|",("state","!=","progress"),("user_permissions","=",False),("results","=","合格")]}'/>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
</page>
|
|
||||||
<page string="2D加工图纸" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "2D_MD")]}'>
|
|
||||||
<field name="machining_drawings" widget="adaptive_viewer"/>
|
|
||||||
</page>
|
|
||||||
|
|
||||||
<page string="质检标准" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "QIS")]}'>
|
|
||||||
<field name="quality_standard" widget="adaptive_viewer"/>
|
|
||||||
</page>
|
|
||||||
</xpath>
|
|
||||||
<xpath expr="//page[1]" position="before">
|
|
||||||
<page string="CNC程序" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "CNC_P")]}'>
|
<page string="CNC程序" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "CNC_P")]}'>
|
||||||
<field name="cnc_ids" widget="one2many" string="工作程序" default_order="sequence_number,id"
|
<field name="cnc_ids" widget="one2many" string="工作程序" default_order="sequence_number,id"
|
||||||
readonly="0">
|
readonly="0">
|
||||||
@@ -610,8 +543,28 @@
|
|||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</page>
|
</page>
|
||||||
</xpath>
|
<page string="后置三元检测" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "PTD")]}'>
|
||||||
<xpath expr="//page[1]" position="before">
|
<group>
|
||||||
|
<field name="test_results"
|
||||||
|
attrs='{"readonly":["&","|",("state","!=","to be detected"), "|",("routing_type","=","CNC加工"),("is_inspect", "=", True),("state","in",["done","rework"])],
|
||||||
|
"invisible":[("results","!=",False)]}'/>
|
||||||
|
<!-- <field name="is_remanufacture" attrs='{"invisible":[("test_results","!=","报废")]}'/>-->
|
||||||
|
<!-- <field name="is_fetchcnc"-->
|
||||||
|
<!-- attrs='{"invisible":["|",("test_results","=","合格"),("is_remanufacture","=",False)]}'/>-->
|
||||||
|
<field name="reason"
|
||||||
|
attrs='{"required":[("test_results","!=","合格")],"invisible":[("test_results","=","合格")],"readonly":[("state","in",("done", "rework"))]}'/>
|
||||||
|
<field name="detailed_reason"
|
||||||
|
attrs='{"required":[("test_results","!=","合格")],"invisible":[("test_results","=","合格")],"readonly":[("state","in",("done", "rework"))]}'/>
|
||||||
|
<!-- <field name="results" readonly="1" attrs='{"invisible":[("results","!=","合格")]}'/>-->
|
||||||
|
<field name="detection_report" attrs='{"invisible":[("results","!=",False)]}'
|
||||||
|
widget="pdf_viewer" readonly="1"/>
|
||||||
|
</group>
|
||||||
|
<!-- <div class="col-12 col-lg-6 o_setting_box">-->
|
||||||
|
<!-- <button type="object" class="oe_highlight" name="recreateManufacturingOrWorkerOrder"-->
|
||||||
|
<!-- string="检测确认"-->
|
||||||
|
<!-- attrs='{"invisible": ["|","|",("state","!=","progress"),("user_permissions","=",False),("results","=","合格")]}'/>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
</page>
|
||||||
<page string="解除装夹" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "DCP")]}'>
|
<page string="解除装夹" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "DCP")]}'>
|
||||||
<!-- <field name="tray_id" readonly="1"/>-->
|
<!-- <field name="tray_id" readonly="1"/>-->
|
||||||
<!-- <div class="col-12 col-lg-6 o_setting_box">-->
|
<!-- <div class="col-12 col-lg-6 o_setting_box">-->
|
||||||
@@ -625,7 +578,45 @@
|
|||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
|
|
||||||
</page>
|
</page>
|
||||||
|
<page string="2D加工图纸" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "2D_MD")]}'>
|
||||||
|
<field name="machining_drawings" widget="adaptive_viewer"/>
|
||||||
|
</page>
|
||||||
|
<page string="质检标准" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "QIS")]}'>
|
||||||
|
<field name="quality_standard" widget="adaptive_viewer"/>
|
||||||
|
</page>
|
||||||
|
<page name="CMR" string="开料要求" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "CMR")]}'>
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="product_tmpl_id_materials_id" widget="many2one"/>
|
||||||
|
<field name="product_tmpl_id_materials_type_id" widget="many2one"/>
|
||||||
|
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="product_tmpl_id_length"/>
|
||||||
|
<field name="product_tmpl_id_width"/>
|
||||||
|
<field name="product_tmpl_id_height"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
|
<xpath expr="//form//header" position="inside">
|
||||||
|
<button type="object" class="oe_highlight jikimo_button_confirm" name="get_three_check_datas"
|
||||||
|
string="获取数据"
|
||||||
|
attrs='{"invisible": ["|", ("state","!=","progress"), ("routing_type","!=","装夹预调")]}'/>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
|
<!-- =====原生页签,暂时不进行配置===== -->
|
||||||
|
<!-- <xpath expr="//page[@name='components']" position="attributes">-->
|
||||||
|
<!-- <attribute name="attrs">{"invisible": ["!", ("individuation_page_list", "ilike", "ML")]}</attribute>-->
|
||||||
|
<!-- </xpath>-->
|
||||||
|
<!-- <xpath expr="//page[@name='time_tracking']" position="attributes">-->
|
||||||
|
<!-- <attribute name="attrs">{"invisible": ["!", ("individuation_page_list", "ilike", "TT")]}</attribute>-->
|
||||||
|
<!-- </xpath>-->
|
||||||
|
<!-- ============================= -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- <xpath expr="//form//sheet//group//group//div[1]" position="after">-->
|
<!-- <xpath expr="//form//sheet//group//group//div[1]" position="after">-->
|
||||||
<!-- <label for="date_start" string="实际加工时间"/>-->
|
<!-- <label for="date_start" string="实际加工时间"/>-->
|
||||||
<!-- <div class="oe_inline">-->
|
<!-- <div class="oe_inline">-->
|
||||||
|
|||||||
@@ -101,7 +101,7 @@
|
|||||||
action="action_quotations_supply_method"
|
action="action_quotations_supply_method"
|
||||||
parent="sale.sale_order_menu"
|
parent="sale.sale_order_menu"
|
||||||
groups="sf_base.group_production_engineer"
|
groups="sf_base.group_production_engineer"
|
||||||
sequence="20"/>
|
sequence="2"/>
|
||||||
|
|
||||||
<record id="sale.menu_sale_order" model="ir.ui.menu">
|
<record id="sale.menu_sale_order" model="ir.ui.menu">
|
||||||
<field name="groups_id" eval="[(4, ref('sf_base.group_production_engineer'))]"/>
|
<field name="groups_id" eval="[(4, ref('sf_base.group_production_engineer'))]"/>
|
||||||
|
|||||||
@@ -50,9 +50,6 @@
|
|||||||
<xpath expr="//field[@name='origin']" position="after">
|
<xpath expr="//field[@name='origin']" position="after">
|
||||||
<field name="retrospect_ref"/>
|
<field name="retrospect_ref"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//tree" position="attributes">
|
|
||||||
<attribute name="default_order">create_date desc</attribute>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|||||||
@@ -117,30 +117,17 @@ class WorkpieceDeliveryWizard(models.TransientModel):
|
|||||||
item.button_finish()
|
item.button_finish()
|
||||||
|
|
||||||
# return scheduling.read()[0]
|
# return scheduling.read()[0]
|
||||||
if isinstance(scheduling, bool) and scheduling is True:
|
return {
|
||||||
return{
|
'type': 'ir.actions.client',
|
||||||
'type': 'ir.actions.client',
|
'tag': 'display_notification',
|
||||||
'tag': 'display_notification',
|
'target': 'new',
|
||||||
'target': 'new',
|
'params': {
|
||||||
'params': {
|
'message': f'任务下发成功!AGV任务调度编号为【{scheduling.name}】',
|
||||||
'message': f'解除装夹成功',
|
'type': 'success',
|
||||||
'type': 'success',
|
'sticky': False,
|
||||||
'sticky': False,
|
'next': {'type': 'ir.actions.act_window_close'},
|
||||||
'next': {'type': 'ir.actions.act_window_close'},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
return {
|
|
||||||
'type': 'ir.actions.client',
|
|
||||||
'tag': 'display_notification',
|
|
||||||
'target': 'new',
|
|
||||||
'params': {
|
|
||||||
'message': f'任务下发成功!AGV任务调度编号为【{scheduling.name}】',
|
|
||||||
'type': 'success',
|
|
||||||
'sticky': False,
|
|
||||||
'next': {'type': 'ir.actions.act_window_close'},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.info('%s任务下发失败:%s', self.delivery_type, e)
|
logging.info('%s任务下发失败:%s', self.delivery_type, e)
|
||||||
raise UserError(f'{self.delivery_type}任务下发失败:{e}') from e
|
raise UserError(f'{self.delivery_type}任务下发失败:{e}') from e
|
||||||
|
|||||||
@@ -61,29 +61,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 定义页脚模板无页码 -->
|
|
||||||
<template id="html_report_quality_footer">
|
|
||||||
<div class="footer">
|
|
||||||
<div style="border-top: 3px solid black;"></div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-6">
|
|
||||||
<p>售后服务: <span t-field="o.company_id.phone"/></p>
|
|
||||||
<p>公司名称: <span t-field="o.company_id.name"/></p>
|
|
||||||
<p>加工工厂: <span t-field="o.company_id.factory_name"/></p>
|
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
|
||||||
<p>公司网址: <span t-field="o.company_id.website"/></p>
|
|
||||||
<p>公司邮箱: <span t-field="o.company_id.email"/></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- <div style="border-top: 2px solid black;"></div> -->
|
|
||||||
<div class="text-center">
|
|
||||||
<span>第<span>1</span> 页/共 <span>1</span>页</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template id="report_quality_inspection">
|
<template id="report_quality_inspection">
|
||||||
<t t-call="web.html_container">
|
<t t-call="web.html_container">
|
||||||
<t t-foreach="docs" t-as="o">
|
<t t-foreach="docs" t-as="o">
|
||||||
@@ -96,7 +73,7 @@
|
|||||||
<table class="table table-sm o_main_table mt-4" style="border: 1px solid black;">
|
<table class="table table-sm o_main_table mt-4" style="border: 1px solid black;">
|
||||||
<tr>
|
<tr>
|
||||||
<td style="width: 15%; border: 1px solid black;"><strong>产品名称:</strong></td>
|
<td style="width: 15%; border: 1px solid black;"><strong>产品名称:</strong></td>
|
||||||
<td style="width: 35%; border: 1px solid black;"><span t-field="o.part_name"/></td>
|
<td style="width: 35%; border: 1px solid black;"><span t-field="o.product_id.name"/></td>
|
||||||
<td style="width: 15%; border: 1px solid black;"><strong>材料:</strong></td>
|
<td style="width: 15%; border: 1px solid black;"><strong>材料:</strong></td>
|
||||||
<td style="width: 35%; border: 1px solid black;"><span t-field="o.material_name"/></td>
|
<td style="width: 35%; border: 1px solid black;"><span t-field="o.material_name"/></td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -210,131 +187,4 @@
|
|||||||
</t>
|
</t>
|
||||||
</t>
|
</t>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template id="html_report_quality_inspection">
|
|
||||||
<t t-call="web.html_container">
|
|
||||||
<t t-foreach="docs" t-as="o">
|
|
||||||
<t t-call="web.basic_layout">
|
|
||||||
<t t-call="sf_quality.report_quality_header"/>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="page" style="min-height: 800px; position: relative; padding-bottom: 250px;">
|
|
||||||
|
|
||||||
<table class="table table-sm o_main_table mt-4" style="border: 1px solid black;">
|
|
||||||
<tr>
|
|
||||||
<td style="width: 15%; border: 1px solid black;"><strong>产品名称:</strong></td>
|
|
||||||
<td style="width: 35%; border: 1px solid black;"><span t-field="o.part_name"/></td>
|
|
||||||
<td style="width: 15%; border: 1px solid black;"><strong>材料:</strong></td>
|
|
||||||
<td style="width: 35%; border: 1px solid black;"><span t-field="o.material_name"/></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="border: 1px solid black;"><strong>图号:</strong></td>
|
|
||||||
<td style="border: 1px solid black;"><span t-field="o.part_number"/></td>
|
|
||||||
<td style="border: 1px solid black;"><strong>日期:</strong></td>
|
|
||||||
<td style="border: 1px solid black;"><span t-field="o.write_date"/></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="border: 1px solid black;"><strong>总数量:</strong></td>
|
|
||||||
<td style="border: 1px solid black;"><span t-field="o.total_qty"/></td>
|
|
||||||
<td style="border: 1px solid black;"><strong>检验数量:</strong></td>
|
|
||||||
<td style="border: 1px solid black;"><span t-field="o.check_qty"/></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h4 class="text-center mt-4">检验结果</h4>
|
|
||||||
<div class="" style="position: relative;">
|
|
||||||
<table class="table table-sm mt-2" style="border: 1px solid black;">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th style="border: 1px solid black;" class="text-center" rowspan="2">检测项目<br/>(图示尺寸)</th>
|
|
||||||
<th style="border: 1px solid black;" t-att-colspan="o.column_nums" class="text-center">测量值</th>
|
|
||||||
<th style="border: 1px solid black; vertical-align: middle;" class="text-center" rowspan="2">判定</th>
|
|
||||||
<th style="border: 1px solid black; vertical-align: middle;" class="text-center" rowspan="2">备注</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<!-- <th style="border: 1px solid black;"></th> -->
|
|
||||||
<th style="border: 1px solid black;" t-if="o.column_nums >= 1" class="text-center">1</th>
|
|
||||||
<th style="border: 1px solid black;" t-if="o.column_nums >= 2" class="text-center">2</th>
|
|
||||||
<th style="border: 1px solid black;" t-if="o.column_nums >= 3" class="text-center">3</th>
|
|
||||||
<th style="border: 1px solid black;" t-if="o.column_nums >= 4" class="text-center">4</th>
|
|
||||||
<th style="border: 1px solid black;" t-if="o.column_nums >= 5" class="text-center">5</th>
|
|
||||||
<!-- <th style="border: 1px solid black;"></th>
|
|
||||||
<th style="border: 1px solid black;"></th> -->
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr t-foreach="o.measure_line_ids" t-as="line">
|
|
||||||
<td style="border: 1px solid black;" class="text-center"><span t-field="line.measure_item"/></td>
|
|
||||||
<td style="border: 1px solid black;" t-if="o.column_nums >= 1" class="text-center"><span t-field="line.measure_value1"/></td>
|
|
||||||
<td style="border: 1px solid black;" t-if="o.column_nums >= 2" class="text-center"><span t-field="line.measure_value2"/></td>
|
|
||||||
<td style="border: 1px solid black;" t-if="o.column_nums >= 3" class="text-center"><span t-field="line.measure_value3"/></td>
|
|
||||||
<td style="border: 1px solid black;" t-if="o.column_nums >= 4" class="text-center"><span t-field="line.measure_value4"/></td>
|
|
||||||
<td style="border: 1px solid black;" t-if="o.column_nums >= 5" class="text-center"><span t-field="line.measure_value5"/></td>
|
|
||||||
<td style="border: 1px solid black;" class="text-center"><span t-field="line.measure_result"/></td>
|
|
||||||
<td style="border: 1px solid black;" class="text-center"><span t-field="line.remark"/></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<img src="/sf_quality/static/img/pass.png" style="width: 200px; height: 200px;position: absolute; bottom: 20px; right: 20%;"/>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div style="clear: both; margin-top: 30px; padding-top: 10px;">
|
|
||||||
<div style="display: inline-block;">
|
|
||||||
<span style="font-size: 18px; font-weight: bold;">检验结论: </span>
|
|
||||||
<span t-if="o.report_result == 'OK'" style="margin-left: 30px; display: inline-block;">
|
|
||||||
<svg width="20" height="20" style="vertical-align: middle;">
|
|
||||||
<rect x="1" y="1" width="18" height="18" fill="none" stroke="black" stroke-width="1.5"/>
|
|
||||||
<path d="M4 10 L9 15 L16 6" stroke="black" stroke-width="2" fill="none"/>
|
|
||||||
</svg>
|
|
||||||
<span style="margin-left: 5px;">合格</span>
|
|
||||||
</span>
|
|
||||||
<span t-else="" style="margin-left: 30px; display: inline-block;">
|
|
||||||
<svg width="20" height="20" style="vertical-align: middle;">
|
|
||||||
<rect x="1" y="1" width="18" height="18" fill="none" stroke="black" stroke-width="1.5"/>
|
|
||||||
</svg>
|
|
||||||
<span style="margin-left: 5px;">合格</span>
|
|
||||||
</span>
|
|
||||||
<span t-if="o.report_result == 'NG'" style="margin-left: 50px; display: inline-block;">
|
|
||||||
<svg width="20" height="20" style="vertical-align: middle;">
|
|
||||||
<rect x="1" y="1" width="18" height="18" fill="none" stroke="black" stroke-width="1.5"/>
|
|
||||||
<path d="M4 10 L9 15 L16 6" stroke="black" stroke-width="2" fill="none"/>
|
|
||||||
</svg>
|
|
||||||
<span style="margin-left: 5px;">不合格</span>
|
|
||||||
</span>
|
|
||||||
<span t-else="" style="margin-left: 50px; display: inline-block;">
|
|
||||||
<svg width="20" height="20" style="vertical-align: middle;">
|
|
||||||
<rect x="1" y="1" width="18" height="18" fill="none" stroke="black" stroke-width="1.5"/>
|
|
||||||
</svg>
|
|
||||||
<span style="margin-left: 5px;">不合格</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mt-4">
|
|
||||||
<div class="col-6">
|
|
||||||
<p><strong>操作员: </strong> <span t-field="o.measure_operator"/></p>
|
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
|
||||||
<p><strong>质检员: </strong> <span t-field="o.quality_manager"/></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="border-top: 3px solid black;"></div>
|
|
||||||
|
|
||||||
<!-- 添加合格标签 -->
|
|
||||||
|
|
||||||
<!-- <div class="row mt-5">
|
|
||||||
<div class="col-12 text-center">
|
|
||||||
<p></p>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<!-- 页脚固定在底部 -->
|
|
||||||
<!-- <div style="position: absolute; bottom: 0; left: 0; right: 0;"> -->
|
|
||||||
<t t-call="sf_quality.html_report_quality_footer"/>
|
|
||||||
<!-- </div> -->
|
|
||||||
</div>
|
|
||||||
</t>
|
|
||||||
</t>
|
|
||||||
</t>
|
|
||||||
</template>
|
|
||||||
</odoo>
|
</odoo>
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
<field name="name">预览检验报告</field>
|
<field name="name">预览检验报告</field>
|
||||||
<field name="model">quality.check</field>
|
<field name="model">quality.check</field>
|
||||||
<field name="report_type">qweb-html</field>
|
<field name="report_type">qweb-html</field>
|
||||||
<field name="report_name">sf_quality.html_report_quality_inspection</field>
|
<field name="report_name">sf_quality.report_quality_inspection</field>
|
||||||
<field name="binding_type">report</field>
|
<field name="binding_type">report</field>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
@@ -323,7 +323,7 @@ class RePurchaseOrder(models.Model):
|
|||||||
contract_summary = fields.Text(string='合同概况')
|
contract_summary = fields.Text(string='合同概况')
|
||||||
|
|
||||||
# 选择是否为紧急采购
|
# 选择是否为紧急采购
|
||||||
urgent_purchase = fields.Selection([('no', '否'), ('yes', '是')], string='紧急采购', default='yes')
|
urgent_purchase = fields.Selection([('no', '否'), ('yes', '是')], string='紧急采购', default='no')
|
||||||
|
|
||||||
@api.depends('origin')
|
@api.depends('origin')
|
||||||
def _compute_purchase_type(self):
|
def _compute_purchase_type(self):
|
||||||
|
|||||||
@@ -298,10 +298,6 @@
|
|||||||
<xpath expr="//field[@name='name']" position="attributes">
|
<xpath expr="//field[@name='name']" position="attributes">
|
||||||
<attribute name="class">purchase_order_list_name</attribute>
|
<attribute name="class">purchase_order_list_name</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<!-- 修改 tree 视图的排序规则 -->
|
|
||||||
<xpath expr="//tree" position="attributes">
|
|
||||||
<attribute name="default_order">date_approve desc</attribute>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|||||||
@@ -14,14 +14,16 @@
|
|||||||
<!-- name="Orders"-->
|
<!-- name="Orders"-->
|
||||||
<!-- sequence="10">-->
|
<!-- sequence="10">-->
|
||||||
|
|
||||||
|
<!-- <menuitem id="menu_sale_quotations"-->
|
||||||
|
<!-- action="action_quotations_with_onboarding"-->
|
||||||
|
<!-- groups="sales_team.group_sale_salesman"-->
|
||||||
|
<!-- sequence="10"/>-->
|
||||||
|
|
||||||
<menuitem id="sale.menu_sale_quotations"
|
<!-- <menuitem id="menu_sale_order"-->
|
||||||
parent="sale.sale_order_menu"
|
<!-- name="Orders"-->
|
||||||
sequence="20"/>
|
<!-- action="action_orders"-->
|
||||||
|
<!-- groups="sales_team.group_sale_salesman"-->
|
||||||
<menuitem id="sale.menu_sale_order"
|
<!-- sequence="20"/>-->
|
||||||
parent="sale.sale_order_menu"
|
|
||||||
sequence="10"/>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- <menuitem id="report_sales_team"-->
|
<!-- <menuitem id="report_sales_team"-->
|
||||||
|
|||||||
@@ -38,7 +38,6 @@
|
|||||||
'web.assets_qweb': [
|
'web.assets_qweb': [
|
||||||
],
|
],
|
||||||
'web.assets_backend': [
|
'web.assets_backend': [
|
||||||
'sf_tool_management/static/src/change.scss'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -387,7 +387,7 @@ class FunctionalToolAssembly(models.Model):
|
|||||||
else:
|
else:
|
||||||
raise ValidationError('刀柄选择错误,请重新确认!!!')
|
raise ValidationError('刀柄选择错误,请重新确认!!!')
|
||||||
else:
|
else:
|
||||||
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', barcode.upper())])
|
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', barcode)])
|
||||||
if location:
|
if location:
|
||||||
if location == record.integral_freight_barcode_id:
|
if location == record.integral_freight_barcode_id:
|
||||||
tool_assembly_id.integral_verify = True
|
tool_assembly_id.integral_verify = True
|
||||||
@@ -782,48 +782,47 @@ class FunctionalToolAssembly(models.Model):
|
|||||||
"""根据BOM对刀具物料进行初始配置"""
|
"""根据BOM对刀具物料进行初始配置"""
|
||||||
options = bom.get('options')
|
options = bom.get('options')
|
||||||
# 配置刀柄信息
|
# 配置刀柄信息
|
||||||
handle_ids = self._get_old_tool_material_lot(bom.get('handle_ids'))
|
for handle_id in bom.get('handle_ids'):
|
||||||
for handle_id in handle_ids:
|
|
||||||
if handle_id:
|
if handle_id:
|
||||||
if not self.handle_product_id:
|
if not self.handle_product_id:
|
||||||
self.handle_product_id = handle_id.product_id.id
|
self.handle_product_id = handle_id.id
|
||||||
break
|
break
|
||||||
|
|
||||||
# 刀柄之外的物料配置
|
# 刀柄之外的物料配置
|
||||||
if options == '刀柄+整体式刀具':
|
if options == '刀柄+整体式刀具':
|
||||||
# 配置整体式刀具
|
# 配置整体式刀具
|
||||||
integra_lot_id = self._get_old_tool_material_lot(bom.get('integral_ids'))
|
integra_lot_ids = self._get_old_tool_material_lot(bom.get('integral_ids'))
|
||||||
integra_location_lot_id = self._get_shelf_location_lot(integra_lot_id)
|
integra_location_lot_id = self._get_shelf_location_lot(integra_lot_ids)
|
||||||
self.integral_freight_barcode_id = integra_location_lot_id.shelf_location_id.id
|
self.integral_freight_barcode_id = integra_location_lot_id.shelf_location_id.id
|
||||||
self.integral_lot_id = integra_location_lot_id.lot_id.id
|
self.integral_lot_id = integra_location_lot_id.lot_id.id
|
||||||
else:
|
else:
|
||||||
# 配置刀片
|
# 配置刀片
|
||||||
blade_lot_id = self._get_old_tool_material_lot(bom.get('blade_ids'))
|
blade_lot_ids = self._get_old_tool_material_lot(bom.get('blade_ids'))
|
||||||
blade_location_lot_id = self._get_shelf_location_lot(blade_lot_id)
|
blade_location_lot_id = self._get_shelf_location_lot(blade_lot_ids)
|
||||||
self.blade_freight_barcode_id = blade_location_lot_id.shelf_location_id.id
|
self.blade_freight_barcode_id = blade_location_lot_id.shelf_location_id.id
|
||||||
self.blade_lot_id = blade_location_lot_id.lot_id.id
|
self.blade_lot_id = blade_location_lot_id.lot_id.id
|
||||||
|
|
||||||
if options == '刀柄+刀杆+刀片':
|
if options == '刀柄+刀杆+刀片':
|
||||||
# 配置刀杆
|
# 配置刀杆
|
||||||
bar_lot_id = self._get_old_tool_material_lot(bom.get('bar_ids'))
|
bar_lot_ids = self._get_old_tool_material_lot(bom.get('bar_ids'))
|
||||||
bar_location_lot_id = self._get_shelf_location_lot(bar_lot_id)
|
bar_location_lot_id = self._get_shelf_location_lot(bar_lot_ids)
|
||||||
self.bar_freight_barcode_id = bar_location_lot_id.shelf_location_id.id
|
self.bar_freight_barcode_id = bar_location_lot_id.shelf_location_id.id
|
||||||
self.bar_lot_id = bar_location_lot_id.lot_id.id
|
self.bar_lot_id = bar_location_lot_id.lot_id.id
|
||||||
elif options == '刀柄+刀盘+刀片':
|
elif options == '刀柄+刀盘+刀片':
|
||||||
# 配置刀盘
|
# 配置刀盘
|
||||||
pad_lot_id = self._get_old_tool_material_lot(bom.get('pad_ids'))
|
pad_lot_ids = self._get_old_tool_material_lot(bom.get('pad_ids'))
|
||||||
pad_location_lot_id = self._get_shelf_location_lot(pad_lot_id)
|
pad_location_lot_id = self._get_shelf_location_lot(pad_lot_ids)
|
||||||
self.pad_freight_barcode_id = pad_location_lot_id.shelf_location_id.id
|
self.pad_freight_barcode_id = pad_location_lot_id.shelf_location_id.id
|
||||||
self.pad_lot_id = pad_location_lot_id.lot_id.id
|
self.pad_lot_id = pad_location_lot_id.lot_id.id
|
||||||
|
|
||||||
def _get_old_tool_material_lot(self, material_ids):
|
def _get_old_tool_material_lot(self, material_ids):
|
||||||
""" 根据先进先出原则选择物料批次 """
|
""" 根据先进先出原则选择物料批次 """
|
||||||
location_id = self.env['stock.location'].search([('name', '=', '刀具房')])
|
location_id = self.env['stock.location'].search([('name', '=', '刀具房')])
|
||||||
stock_quant = self.env['stock.quant'].sudo().search(
|
stock_quant_ids = self.env['stock.quant'].sudo().search(
|
||||||
[('location_id', '=', location_id.id), ('product_id', 'in', material_ids.ids), ('quantity', '>', '0')],
|
[('location_id', '=', location_id.id), ('product_id', 'in', material_ids.ids), ('quantity', '>', '0')],
|
||||||
order='lot_id')
|
order='lot_id')
|
||||||
if stock_quant:
|
if stock_quant_ids:
|
||||||
return [quant.lot_id for quant in stock_quant]
|
return [stock_quant_id.lot_id for stock_quant_id in stock_quant_ids]
|
||||||
else:
|
else:
|
||||||
raise ValidationError(f'【{material_ids[0].cutting_tool_material_id.name}】物料库存不足,请先进行盘点或采购')
|
raise ValidationError(f'【{material_ids[0].cutting_tool_material_id.name}】物料库存不足,请先进行盘点或采购')
|
||||||
|
|
||||||
@@ -833,8 +832,9 @@ class FunctionalToolAssembly(models.Model):
|
|||||||
location_lots = self.env['sf.shelf.location.lot'].sudo().search([('lot_id', '=', lot_id.id)])
|
location_lots = self.env['sf.shelf.location.lot'].sudo().search([('lot_id', '=', lot_id.id)])
|
||||||
if location_lots:
|
if location_lots:
|
||||||
return location_lots[0]
|
return location_lots[0]
|
||||||
raise ValidationError(f'【{lot_ids[0].product_id.cutting_tool_material_id.name}】物料在货位库存不足,请先进行盘点入库')
|
if lot_ids:
|
||||||
|
raise ValidationError(
|
||||||
|
f'【{lot_ids[0].product_id.cutting_tool_material_id.name}】物料在货位上库存不足,请先进行盘点或采购!')
|
||||||
|
|
||||||
def _get_inventory_bom(self, inventory_id):
|
def _get_inventory_bom(self, inventory_id):
|
||||||
"""获取BOM的刀具物料产品信息"""
|
"""获取BOM的刀具物料产品信息"""
|
||||||
@@ -1242,7 +1242,7 @@ class FunctionalToolDismantle(models.Model):
|
|||||||
|
|
||||||
functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', required=True, tracking=True,
|
functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', required=True, tracking=True,
|
||||||
domain=[('functional_tool_status', '!=', '已拆除'),
|
domain=[('functional_tool_status', '!=', '已拆除'),
|
||||||
('current_location', 'in', ['刀具房', '线边刀库'])])
|
('current_location', '=', '刀具房')])
|
||||||
|
|
||||||
@api.onchange('functional_tool_id')
|
@api.onchange('functional_tool_id')
|
||||||
def _onchange_functional_tool_id(self):
|
def _onchange_functional_tool_id(self):
|
||||||
@@ -1439,26 +1439,14 @@ class FunctionalToolDismantle(models.Model):
|
|||||||
def confirmation_disassembly(self):
|
def confirmation_disassembly(self):
|
||||||
logging.info('%s刀具确认开始拆解' % self.dismantle_cause)
|
logging.info('%s刀具确认开始拆解' % self.dismantle_cause)
|
||||||
code = self.code
|
code = self.code
|
||||||
context = self.env.context
|
|
||||||
if self.functional_tool_id.functional_tool_status == '已拆除':
|
if self.functional_tool_id.functional_tool_status == '已拆除':
|
||||||
raise ValidationError('Rfid为【%s】名称为【%s】的功能刀具已经拆解,请勿重复操作!' % (
|
raise ValidationError('Rfid为【%s】名称为【%s】的功能刀具已经拆解,请勿重复操作!' % (
|
||||||
self.functional_tool_id.rfid_dismantle, self.name))
|
self.functional_tool_id.rfid_dismantle, self.name))
|
||||||
# 对拆解的功能刀具进行校验,只有在刀具房的功能刀具才能拆解
|
# 对拆解的功能刀具进行校验,只有在刀具房的功能刀具才能拆解
|
||||||
elif self.functional_tool_id.functional_tool_status != '报警':
|
elif self.functional_tool_id.functional_tool_status != '报警':
|
||||||
if self.functional_tool_id.current_location == '机内刀库':
|
if self.functional_tool_id.tool_room_num == 0:
|
||||||
raise ValidationError('Rfid为【%s】的功能刀具当前位置为【%s】,不能进行拆解!' % (
|
raise ValidationError('Rfid为【%s】的功能刀具当前位置为【%s】,不能进行拆解!' % (
|
||||||
self.rfid, self.functional_tool_id.current_location))
|
self.rfid, self.functional_tool_id.current_location))
|
||||||
elif not context.get('TRUE_DISASSEMBLE') and self.functional_tool_id.current_location == '线边刀库':
|
|
||||||
return {
|
|
||||||
'type': 'ir.actions.act_window',
|
|
||||||
'res_model': 'sf.functional.tool.dismantle.wiard',
|
|
||||||
'name': '刀具寿命未到期',
|
|
||||||
'view_mode': 'form',
|
|
||||||
'target': 'new',
|
|
||||||
'context': {
|
|
||||||
'default_functional_tool_dismantle_id': self.id,
|
|
||||||
'TRUE_DISASSEMBLE': True}
|
|
||||||
}
|
|
||||||
# 目标重复校验
|
# 目标重复校验
|
||||||
self.location_duplicate_check()
|
self.location_duplicate_check()
|
||||||
datas = {'scrap': [], 'picking': []}
|
datas = {'scrap': [], 'picking': []}
|
||||||
@@ -1535,18 +1523,11 @@ class FunctionalToolDismantle(models.Model):
|
|||||||
'functional_tool_name': self.functional_tool_id.name,
|
'functional_tool_name': self.functional_tool_id.name,
|
||||||
'handle_code_id': self.handle_lot_id.id,
|
'handle_code_id': self.handle_lot_id.id,
|
||||||
'handle_product_id': self.handle_product_id.id,
|
'handle_product_id': self.handle_product_id.id,
|
||||||
'functional_tool_diameter': self.functional_tool_id.functional_tool_diameter,
|
|
||||||
'knife_tip_r_angle': self.functional_tool_id.knife_tip_r_angle,
|
|
||||||
'tool_loading_length': self.functional_tool_id.tool_loading_length,
|
|
||||||
'functional_tool_length': self.functional_tool_id.functional_tool_length,
|
|
||||||
'loading_task_source': '3',
|
'loading_task_source': '3',
|
||||||
'use_tool_time': fields.Datetime.now() + timedelta(hours=4),
|
'use_tool_time': fields.Datetime.now() + timedelta(hours=4),
|
||||||
'reason_for_applying': '刀具寿命到期'
|
'reason_for_applying': '刀具寿命到期'
|
||||||
})
|
})
|
||||||
|
|
||||||
# 将新的组装单更新到对应的功能刀具安全库存的组装单列表中
|
|
||||||
self.functional_tool_id.safe_inventory_id.sudo().sf_functional_tool_assembly_ids = [(4, assembly_id.id)]
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'type': 'ir.actions.act_window',
|
'type': 'ir.actions.act_window',
|
||||||
'res_model': 'sf.functional.tool.assembly',
|
'res_model': 'sf.functional.tool.assembly',
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ from odoo.exceptions import ValidationError
|
|||||||
|
|
||||||
class FunctionalCuttingToolEntity(models.Model):
|
class FunctionalCuttingToolEntity(models.Model):
|
||||||
_name = 'sf.functional.cutting.tool.entity'
|
_name = 'sf.functional.cutting.tool.entity'
|
||||||
_inherit = ['mail.thread']
|
|
||||||
_description = '功能刀具列表'
|
_description = '功能刀具列表'
|
||||||
_order = 'functional_tool_status'
|
_order = 'functional_tool_status'
|
||||||
|
|
||||||
@@ -42,7 +41,7 @@ class FunctionalCuttingToolEntity(models.Model):
|
|||||||
max_lifetime_value = fields.Integer(string='最大寿命值(min)', readonly=True)
|
max_lifetime_value = fields.Integer(string='最大寿命值(min)', readonly=True)
|
||||||
alarm_value = fields.Integer(string='报警值(min)', readonly=True)
|
alarm_value = fields.Integer(string='报警值(min)', readonly=True)
|
||||||
used_value = fields.Integer(string='已使用值(min)', readonly=True)
|
used_value = fields.Integer(string='已使用值(min)', readonly=True)
|
||||||
functional_tool_status = fields.Selection([('正常', '正常'), ('报警', '报警'), ('已拆除', '已拆除')], tracking=True,
|
functional_tool_status = fields.Selection([('正常', '正常'), ('报警', '报警'), ('已拆除', '已拆除')],
|
||||||
string='状态', store=True, default='正常')
|
string='状态', store=True, default='正常')
|
||||||
current_location_id = fields.Many2one('stock.location', string='当前位置', compute='_compute_current_location_id',
|
current_location_id = fields.Many2one('stock.location', string='当前位置', compute='_compute_current_location_id',
|
||||||
store=True)
|
store=True)
|
||||||
@@ -63,27 +62,16 @@ class FunctionalCuttingToolEntity(models.Model):
|
|||||||
for item in self:
|
for item in self:
|
||||||
if item:
|
if item:
|
||||||
if item.functional_tool_status == '报警':
|
if item.functional_tool_status == '报警':
|
||||||
self.create_tool_dismantle()
|
# 创建报警刀具拆解单
|
||||||
|
self.env['sf.functional.tool.dismantle'].sudo().create({
|
||||||
def set_functional_tool_status(self):
|
'functional_tool_id': item.ids[0],
|
||||||
# self.write({
|
'dismantle_cause': '寿命到期报废'
|
||||||
# 'functional_tool_status': '报警'
|
})
|
||||||
# })
|
# 创建刀具报警记录
|
||||||
self.functional_tool_status = '报警'
|
self.env['sf.functional.tool.warning'].sudo().create({
|
||||||
self.create_tool_dismantle()
|
'rfid': item.rfid,
|
||||||
|
'functional_tool_id': item.ids[0]
|
||||||
def create_tool_dismantle(self):
|
})
|
||||||
for item in self:
|
|
||||||
# 创建报警刀具拆解单
|
|
||||||
self.env['sf.functional.tool.dismantle'].sudo().create({
|
|
||||||
'functional_tool_id': item.ids[0],
|
|
||||||
'dismantle_cause': '寿命到期报废'
|
|
||||||
})
|
|
||||||
# 创建刀具报警记录
|
|
||||||
self.env['sf.functional.tool.warning'].sudo().create({
|
|
||||||
'rfid': item.rfid,
|
|
||||||
'functional_tool_id': item.ids[0]
|
|
||||||
})
|
|
||||||
|
|
||||||
@api.depends('barcode_id.quant_ids', 'barcode_id.quant_ids.location_id', 'functional_tool_status',
|
@api.depends('barcode_id.quant_ids', 'barcode_id.quant_ids.location_id', 'functional_tool_status',
|
||||||
'current_shelf_location_id', 'stock_num')
|
'current_shelf_location_id', 'stock_num')
|
||||||
@@ -275,7 +263,7 @@ class FunctionalCuttingToolEntity(models.Model):
|
|||||||
functional_tool_model_ids.append(functional_tool_model.id)
|
functional_tool_model_ids.append(functional_tool_model.id)
|
||||||
return [(6, 0, functional_tool_model_ids)]
|
return [(6, 0, functional_tool_model_ids)]
|
||||||
|
|
||||||
dismantle_num = fields.Integer('拆解单数量', compute='_compute_dismantle_num', tracking=True, store=True)
|
dismantle_num = fields.Integer('拆解单数量', compute='_compute_dismantle_num', store=True)
|
||||||
dismantle_ids = fields.One2many('sf.functional.tool.dismantle', 'functional_tool_id', '拆解单')
|
dismantle_ids = fields.One2many('sf.functional.tool.dismantle', 'functional_tool_id', '拆解单')
|
||||||
|
|
||||||
@api.depends('dismantle_ids')
|
@api.depends('dismantle_ids')
|
||||||
|
|||||||
@@ -107,17 +107,11 @@ class SfMaintenanceEquipment(models.Model):
|
|||||||
if functional_tool_id.current_location != '机内刀库':
|
if functional_tool_id.current_location != '机内刀库':
|
||||||
# 对功能刀具进行移动到生产线
|
# 对功能刀具进行移动到生产线
|
||||||
functional_tool_id.tool_inventory_displacement_out()
|
functional_tool_id.tool_inventory_displacement_out()
|
||||||
data_tool = {
|
functional_tool_id.write({
|
||||||
'max_lifetime_value': data['MaxLife'],
|
'max_lifetime_value': data['MaxLife'],
|
||||||
'used_value': data['UseLife'],
|
'used_value': data['UseLife'],
|
||||||
'functional_tool_status': tool_install_time.get(data['State'])
|
'functional_tool_status': tool_install_time.get(data['State'])
|
||||||
}
|
})
|
||||||
if (functional_tool_id.functional_tool_status != '报警'
|
|
||||||
and tool_install_time.get(data['State']) == '报警'):
|
|
||||||
functional_tool_id.write(data_tool)
|
|
||||||
functional_tool_id.create_tool_dismantle()
|
|
||||||
else:
|
|
||||||
functional_tool_id.write(data_tool)
|
|
||||||
else:
|
else:
|
||||||
logging.info('获取的【%s】设备不存在!!!' % data['DeviceId'])
|
logging.info('获取的【%s】设备不存在!!!' % data['DeviceId'])
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -40,7 +40,4 @@ access_sf_functional_tool_dismantle_group_sf_tool_user,sf.functional.tool.disman
|
|||||||
access_sf_functional_tool_dismantle_group_plan_dispatch,sf.functional.tool.dismantle_group_plan_dispatch,model_sf_functional_tool_dismantle,sf_base.group_plan_dispatch,1,0,0,0
|
access_sf_functional_tool_dismantle_group_plan_dispatch,sf.functional.tool.dismantle_group_plan_dispatch,model_sf_functional_tool_dismantle,sf_base.group_plan_dispatch,1,0,0,0
|
||||||
|
|
||||||
access_jikimo_bom,jikimo.bom,model_jikimo_bom,base.group_user,1,1,1,1
|
access_jikimo_bom,jikimo.bom,model_jikimo_bom,base.group_user,1,1,1,1
|
||||||
access_jikimo_bom_wizard,jikimo.bom.wizard,model_jikimo_bom_wizard,base.group_user,1,1,1,1
|
access_jikimo_bom_wizard,jikimo.bom.wizard,model_jikimo_bom_wizard,base.group_user,1,1,1,1
|
||||||
|
|
||||||
access_sf_functional_tool_dismantle_wiard,sf.functional.tool.dismantle.wiard,model_sf_functional_tool_dismantle_wiard,sf_base.group_sf_tool_user,1,1,1,0
|
|
||||||
access_sf_functional_tool_dismantle_wiard_group_plan_dispatch,sf.functional.tool.dismantle.wiard,model_sf_functional_tool_dismantle_wiard,sf_base.group_plan_dispatch,1,0,0,0
|
|
||||||
|
Binary file not shown.
|
Before Width: | Height: | Size: 785 B |
@@ -1,47 +1,17 @@
|
|||||||
// .modal-content .o_cp_buttons {
|
.modal-content .o_cp_buttons {
|
||||||
// display:none
|
display:none
|
||||||
// }
|
}
|
||||||
|
|
||||||
// .modal-content .o_control_panel {
|
.modal-content .o_control_panel {
|
||||||
// display:none
|
display:none
|
||||||
// }
|
}
|
||||||
|
|
||||||
// .modal-content .o_list_button {
|
.modal-content .o_list_button {
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .o_form_view .o_field_widget .o_list_renderer {
|
|
||||||
// width: 100%!important;
|
|
||||||
// margin:0 auto;
|
|
||||||
// overflow: auto;
|
|
||||||
// }
|
|
||||||
.o_field_widget.o_readonly_modifier.o_field_char.text-success[name=handle_freight_rfid],
|
|
||||||
.o_field_widget.o_readonly_modifier.o_field_many2one.text-success[name=integral_freight_barcode_id] {
|
|
||||||
a.text-success{
|
|
||||||
span {
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
.custom_group:has(.text-success){
|
|
||||||
position: relative;
|
.o_form_view .o_field_widget .o_list_renderer {
|
||||||
&::after{
|
width: 100%!important;
|
||||||
content: '';
|
margin:0 auto;
|
||||||
display: block;
|
overflow: auto;
|
||||||
width: 72px;
|
|
||||||
height: 72px;
|
|
||||||
background: url('/sf_tool_management/static/images/replaceIcon.png') no-repeat center center;
|
|
||||||
background-size: 100%;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 20px;
|
|
||||||
left: 300px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.o_field_widget.o_readonly_modifier.o_field_char.text-success[name=handle_freight_rfid] {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
> span {
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -42,7 +42,6 @@
|
|||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form create="0" edit="0" delete="0">
|
<form create="0" edit="0" delete="0">
|
||||||
<header>
|
<header>
|
||||||
<button name="set_functional_tool_status" string="报警" type="object" invisible="1"/>
|
|
||||||
<!-- <button name="enroll_functional_tool_entity" string="功能刀具注册" type="object"-->
|
<!-- <button name="enroll_functional_tool_entity" string="功能刀具注册" type="object"-->
|
||||||
<!-- class="btn-primary"/>-->
|
<!-- class="btn-primary"/>-->
|
||||||
<field name="functional_tool_status" widget="statusbar" statusbar_visible="正常,报警,已拆除"/>
|
<field name="functional_tool_status" widget="statusbar" statusbar_visible="正常,报警,已拆除"/>
|
||||||
@@ -193,10 +192,6 @@
|
|||||||
</page>
|
</page>
|
||||||
</notebook>
|
</notebook>
|
||||||
</sheet>
|
</sheet>
|
||||||
<div class="oe_chatter">
|
|
||||||
<field name="message_follower_ids"/>
|
|
||||||
<field name="message_ids"/>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
@@ -432,7 +432,7 @@
|
|||||||
<field name="name">功能刀具组装</field>
|
<field name="name">功能刀具组装</field>
|
||||||
<field name="model">sf.functional.tool.assembly</field>
|
<field name="model">sf.functional.tool.assembly</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree create="0" delete="0" default_order="create_date desc">
|
<tree create="0" delete="0">
|
||||||
<field name="assembly_order_code"/>
|
<field name="assembly_order_code"/>
|
||||||
<field name="barcode_id" optional="hide"/>
|
<field name="barcode_id" optional="hide"/>
|
||||||
<field name="code" optional="hide"/>
|
<field name="code" optional="hide"/>
|
||||||
@@ -531,10 +531,10 @@
|
|||||||
<div>
|
<div>
|
||||||
<separator string="刀柄:" style="font-size: 13px;"/>
|
<separator string="刀柄:" style="font-size: 13px;"/>
|
||||||
</div>
|
</div>
|
||||||
<group class="custom_group" >
|
<group>
|
||||||
<field name="handle_code_id" string="序列号" placeholder="请选择"
|
<field name="handle_code_id" string="序列号" placeholder="请选择"
|
||||||
options="{'no_create': True, 'no_quick_create': True}"/>
|
options="{'no_create': True, 'no_quick_create': True}"/>
|
||||||
<field name="handle_freight_rfid" string="Rfid" decoration-success="handle_freight_rfid"/>
|
<field name="handle_freight_rfid" string="Rfid"/>
|
||||||
<field name="handle_product_id" string="名称"/>
|
<field name="handle_product_id" string="名称"/>
|
||||||
<field name="cutting_tool_cutterhandle_model_id" string="型号"/>
|
<field name="cutting_tool_cutterhandle_model_id" string="型号"/>
|
||||||
<field name="handle_specification_id" string="规格"/>
|
<field name="handle_specification_id" string="规格"/>
|
||||||
@@ -554,15 +554,15 @@
|
|||||||
<separator string="整体式刀具:" style="font-size: 13px;"/>
|
<separator string="整体式刀具:" style="font-size: 13px;"/>
|
||||||
</div>
|
</div>
|
||||||
<group>
|
<group>
|
||||||
<group class="custom_group">
|
<group>
|
||||||
<field name="integral_freight_barcode_id" string="货位" decoration-success="integral_verify == True"/>
|
<field name="integral_freight_barcode_id" string="货位"/>
|
||||||
<field name="integral_lot_id" string="批次"/>
|
<field name="integral_lot_id" string="批次"/>
|
||||||
<field name="integral_product_id" string="名称"/>
|
<field name="integral_product_id" string="名称"/>
|
||||||
<field name="cutting_tool_integral_model_id" string="型号"/>
|
<field name="cutting_tool_integral_model_id" string="型号"/>
|
||||||
<field name="integral_specification_id" string="规格"/>
|
<field name="integral_specification_id" string="规格"/>
|
||||||
<field name="sf_tool_brand_id_1" string="品牌"/>
|
<field name="sf_tool_brand_id_1" string="品牌"/>
|
||||||
</group>
|
</group>
|
||||||
<group invisible="1">
|
<group>
|
||||||
<field name="integral_verify" string="" readonly="1"/>
|
<field name="integral_verify" string="" readonly="1"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
@@ -582,8 +582,8 @@
|
|||||||
<separator string="刀片:" style="font-size: 13px;"/>
|
<separator string="刀片:" style="font-size: 13px;"/>
|
||||||
</div>
|
</div>
|
||||||
<group>
|
<group>
|
||||||
<group class="custom_group">
|
<group>
|
||||||
<field name="blade_freight_barcode_id" string="货位" decoration-success="blade_verify == True"/>
|
<field name="blade_freight_barcode_id" string="货位"/>
|
||||||
<field name="blade_lot_id" string="批次"/>
|
<field name="blade_lot_id" string="批次"/>
|
||||||
<field name="blade_product_id" string="名称"/>
|
<field name="blade_product_id" string="名称"/>
|
||||||
<field name="cutting_tool_blade_model_id" string="型号"/>
|
<field name="cutting_tool_blade_model_id" string="型号"/>
|
||||||
@@ -607,8 +607,8 @@
|
|||||||
<separator string="刀杆:" style="font-size: 13px;"/>
|
<separator string="刀杆:" style="font-size: 13px;"/>
|
||||||
</div>
|
</div>
|
||||||
<group>
|
<group>
|
||||||
<group class="custom_group">
|
<group>
|
||||||
<field name="bar_freight_barcode_id" string="货位" decoration-success="bar_verify == True"/>
|
<field name="bar_freight_barcode_id" string="货位"/>
|
||||||
<field name="bar_lot_id" string="批次"/>
|
<field name="bar_lot_id" string="批次"/>
|
||||||
<field name="bar_product_id" string="名称"/>
|
<field name="bar_product_id" string="名称"/>
|
||||||
<field name="cutting_tool_cutterbar_model_id" string="型号"/>
|
<field name="cutting_tool_cutterbar_model_id" string="型号"/>
|
||||||
@@ -631,8 +631,8 @@
|
|||||||
<separator string="刀盘:" style="font-size: 13px;"/>
|
<separator string="刀盘:" style="font-size: 13px;"/>
|
||||||
</div>
|
</div>
|
||||||
<group>
|
<group>
|
||||||
<group class="custom_group">
|
<group>
|
||||||
<field name="pad_freight_barcode_id" string="货位" decoration-success="pad_verify == True"/>
|
<field name="pad_freight_barcode_id" string="货位"/>
|
||||||
<field name="pad_lot_id" string="批次"/>
|
<field name="pad_lot_id" string="批次"/>
|
||||||
<field name="pad_product_id" string="名称"/>
|
<field name="pad_product_id" string="名称"/>
|
||||||
<field name="cutting_tool_cutterpad_model_id" string="型号"/>
|
<field name="cutting_tool_cutterpad_model_id" string="型号"/>
|
||||||
|
|||||||
@@ -770,12 +770,3 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
|
|||||||
# }
|
# }
|
||||||
|
|
||||||
|
|
||||||
class FunctionalToolDismantle(models.TransientModel):
|
|
||||||
_name = 'sf.functional.tool.dismantle.wiard'
|
|
||||||
_description = '功能刀具拆解二次确认'
|
|
||||||
|
|
||||||
functional_tool_dismantle_id = fields.Many2one('sf.functional.tool.dismantle', '拆解单')
|
|
||||||
|
|
||||||
def confirm(self):
|
|
||||||
self.functional_tool_dismantle_id.confirmation_disassembly()
|
|
||||||
return True
|
|
||||||
|
|||||||
@@ -444,19 +444,4 @@
|
|||||||
<field name="view_id" ref="sf_functional_tool_assembly_order_form"/>
|
<field name="view_id" ref="sf_functional_tool_assembly_order_form"/>
|
||||||
<field name="target">new</field>
|
<field name="target">new</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|
||||||
<record id="sf_functional_tool_dismantle_wiard_form" model="ir.ui.view">
|
|
||||||
<field name="name">刀具拆解</field>
|
|
||||||
<field name="model">sf.functional.tool.dismantle.wiard</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<form>
|
|
||||||
<div>刀具寿命未到期,是否继续拆解?</div>
|
|
||||||
<footer>
|
|
||||||
<button string="确定" name="confirm" type="object" class="btn-primary"/>
|
|
||||||
<button string="取消" class="btn-secondary" special="cancel"/>
|
|
||||||
</footer>
|
|
||||||
</form>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
</odoo>
|
</odoo>
|
||||||
@@ -150,4 +150,4 @@ access_sf_shelf_lot_group_user,sf.shelf.location.lot.group_user,model_sf_shelf_l
|
|||||||
|
|
||||||
|
|
||||||
access_ir_model_group_sf_stock_user,ir_model_group_sf_stock_user,base.model_ir_model,sf_base.group_sf_stock_user,1,1,0,0
|
access_ir_model_group_sf_stock_user,ir_model_group_sf_stock_user,base.model_ir_model,sf_base.group_sf_stock_user,1,1,0,0
|
||||||
access_mrp_workorder_group_sf_stock_user,mrp_workorder_group_sf_stock_user,mrp.model_mrp_workorder,sf_base.group_sf_stock_user,1,1,0,0
|
access_mrp_workorder_group_sf_stock_user,mrp_workorder_group_sf_stock_user,mrp.model_mrp_workorder,sf_base.group_sf_stock_user,1,0,0,0
|
||||||
|
|||||||
|
@@ -6,7 +6,10 @@ import win32gui
|
|||||||
import win32con
|
import win32con
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
import re
|
|
||||||
|
# 配置日志记录
|
||||||
|
logging.basicConfig(filename='service.log', level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(levelname)s - %(message)s')
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
@@ -16,11 +19,11 @@ class FileUploadRequest(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
# FTP 服务器配置信息
|
# FTP 服务器配置信息
|
||||||
ftp_host = '47.119.33.43'
|
ftp_host = '110.52.114.162'
|
||||||
ftp_port = 21
|
ftp_port = 10021
|
||||||
ftp_user = 'ftpuser'
|
ftp_user = 'ftpuser'
|
||||||
ftp_password = 'FTPftp123'
|
ftp_password = '123456'
|
||||||
ftp_directory = 'ThreeTest/XT/Before/'
|
ftp_directory = '/home/ftp/ftp_root/ThreeTest/XT/Before/'
|
||||||
|
|
||||||
|
|
||||||
def find_child_window(parent_hwnd, class_name):
|
def find_child_window(parent_hwnd, class_name):
|
||||||
@@ -60,95 +63,25 @@ def find_child_window_by_title(parent_hwnd, title):
|
|||||||
return child_hwnds
|
return child_hwnds
|
||||||
|
|
||||||
|
|
||||||
# 获取 ComboBox 的句柄
|
|
||||||
def get_combobox_handle(parent_handle):
|
|
||||||
combo_handle = win32gui.FindWindowEx(parent_handle, 0, "ComboBox", None)
|
|
||||||
if combo_handle == 0:
|
|
||||||
raise Exception("ComboBox not found")
|
|
||||||
return combo_handle
|
|
||||||
|
|
||||||
|
|
||||||
# 获取 ComboBox 中的所有选项
|
|
||||||
def get_combobox_items(combo_handle):
|
|
||||||
count = win32gui.SendMessage(combo_handle, win32con.CB_GETCOUNT, 0, 0)
|
|
||||||
items = []
|
|
||||||
for i in range(count):
|
|
||||||
length = win32gui.SendMessage(combo_handle, win32con.CB_GETLBTEXTLEN, i, 0)
|
|
||||||
buffer = win32gui.PyMakeBuffer(length + 1)
|
|
||||||
win32gui.SendMessage(combo_handle, win32con.CB_GETLBTEXT, i, buffer)
|
|
||||||
byte_data = buffer.tobytes()
|
|
||||||
|
|
||||||
# 尝试多种编码方式
|
|
||||||
text = None
|
|
||||||
# 尝试多种编码方式,包括utf-16le
|
|
||||||
for encoding in ['utf-16le', 'utf-8', 'gbk', 'latin-1']:
|
|
||||||
try:
|
|
||||||
decoded_text = byte_data.decode(encoding).rstrip('\x00')
|
|
||||||
# 如果解码后的文本看起来是合理的,就接受它
|
|
||||||
if any(char.isprintable() for char in decoded_text):
|
|
||||||
text = decoded_text
|
|
||||||
break
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 如果所有解码方式都失败,或者内容仍有大量乱码,显示为十六进制字符串
|
|
||||||
if text is None or not all(char.isprintable() or char.isspace() for char in text):
|
|
||||||
text = byte_data.hex()
|
|
||||||
|
|
||||||
items.append(text)
|
|
||||||
return items
|
|
||||||
|
|
||||||
|
|
||||||
# 获取当前选定项
|
|
||||||
def get_combobox_selected(combo_handle):
|
|
||||||
index = win32gui.SendMessage(combo_handle, win32con.CB_GETCURSEL, 0, 0)
|
|
||||||
if index == -1:
|
|
||||||
return None
|
|
||||||
length = win32gui.SendMessage(combo_handle, win32con.CB_GETLBTEXTLEN, index, 0)
|
|
||||||
buffer = win32gui.PyMakeBuffer(1024) # 调整缓冲区大小
|
|
||||||
win32gui.SendMessage(combo_handle, win32con.CB_GETLBTEXT, index, buffer)
|
|
||||||
|
|
||||||
# 尝试多种编码方式进行解码
|
|
||||||
for encoding in ['utf-16le', 'utf-8', 'latin-1']:
|
|
||||||
try:
|
|
||||||
# 解码
|
|
||||||
raw_text = buffer.tobytes().decode(encoding, errors='ignore').rstrip('\x00')
|
|
||||||
# 使用正则表达式查找 "数字 + 月" 格式的部分
|
|
||||||
match = re.search(r'\d+月', raw_text)
|
|
||||||
if match:
|
|
||||||
return match.group()
|
|
||||||
# 使用正则表达式提取有效字符(中文、数字、字母和常见标点)
|
|
||||||
filtered_text = re.findall(r'[\w\u4e00-\u9fa5]+', raw_text)
|
|
||||||
# 返回匹配到的第一个有效部分
|
|
||||||
if filtered_text:
|
|
||||||
return filtered_text[0].strip()
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
continue # 尝试下一个编码方式
|
|
||||||
|
|
||||||
# 如果所有解码方式都失败,返回 None
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
# 设置 ComboBox 的值
|
|
||||||
def set_combobox_value(combo_handle, value):
|
|
||||||
items = get_combobox_items(combo_handle)
|
|
||||||
try:
|
|
||||||
index = items.index(value)
|
|
||||||
win32gui.SendMessage(combo_handle, win32con.CB_SETCURSEL, index, 0)
|
|
||||||
except ValueError:
|
|
||||||
raise Exception("Value not found in ComboBox")
|
|
||||||
|
|
||||||
|
|
||||||
def set_path_and_save(filename):
|
def set_path_and_save(filename):
|
||||||
parent_hwnd = win32gui.FindWindow(None, '保存Excel文件')
|
parent_hwnd = win32gui.FindWindow(None, '另存为')
|
||||||
|
|
||||||
if parent_hwnd == 0:
|
if parent_hwnd == 0:
|
||||||
raise HTTPException(status_code=404, detail="没有找到保存报告的窗口,请检查!")
|
raise HTTPException(status_code=404, detail="没有找到保存报告的窗口,请检查!")
|
||||||
|
|
||||||
combo_handle = get_combobox_handle(parent_hwnd)
|
# 这里假设“地址:”是你需要的部分标题
|
||||||
#logging.info(f"ComboBox Items: {get_combobox_items(combo_handle)}")
|
address_hwnds = find_child_window_by_partial_title(parent_hwnd, "地址:")
|
||||||
logging.info(f"Current Selected: {get_combobox_selected(combo_handle)}")
|
|
||||||
local_file_path = "C:\\RationalDMIS64\\Output\\" + get_combobox_selected(combo_handle) + "\\" + filename
|
# 确保找到的窗口句柄有效
|
||||||
|
if not address_hwnds:
|
||||||
|
raise HTTPException(status_code=404, detail="未找到地址框,请联系管理员!")
|
||||||
|
|
||||||
|
# 假设找到的第一个窗口是目标组件
|
||||||
|
address_hwnd = address_hwnds[0]
|
||||||
|
logging.info(f"找到地址框地址: {win32gui.GetWindowText(address_hwnd)}")
|
||||||
|
|
||||||
|
# 设置路径
|
||||||
|
local_file_path = os.path.join(win32gui.GetWindowText(address_hwnd).split(' ')[1], filename)
|
||||||
logging.info(f"设置路径: {local_file_path}")
|
logging.info(f"设置路径: {local_file_path}")
|
||||||
|
|
||||||
path_hwnds = find_child_window(parent_hwnd, 'Edit')
|
path_hwnds = find_child_window(parent_hwnd, 'Edit')
|
||||||
@@ -182,6 +115,7 @@ def wait_for_file_to_save(filepath, timeout=30):
|
|||||||
|
|
||||||
|
|
||||||
def upload_file_to_ftp(local_file):
|
def upload_file_to_ftp(local_file):
|
||||||
|
|
||||||
if not os.path.isfile(local_file):
|
if not os.path.isfile(local_file):
|
||||||
raise HTTPException(status_code=204, detail="文件未找到")
|
raise HTTPException(status_code=204, detail="文件未找到")
|
||||||
|
|
||||||
@@ -223,4 +157,4 @@ async def upload_file(request: FileUploadRequest):
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|
||||||
uvicorn.run(app, host="0.0.0.0", port=8999)
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||||
|
|||||||
Reference in New Issue
Block a user