@@ -634,6 +574,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From f6a9c97e7f7ef8dc50b9127df4070225aedcc867 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Wed, 11 Sep 2024 08:51:01 +0800
Subject: [PATCH 09/40] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BA=A7=E9=87=8F?=
=?UTF-8?q?=E9=83=A8=E5=88=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_machine_connect/controllers/controllers.py | 29 +++++++++++--------
sf_plan/models/custom_plan.py | 25 ++++++++++++++--
2 files changed, 40 insertions(+), 14 deletions(-)
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index 6e661f46..6f692bde 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -301,21 +301,21 @@ class Sf_Dashboard_Connect(http.Controller):
try:
plan_obj = request.env['sf.production.plan'].sudo()
+ production_obj = request.env['mrp.production'].sudo()
line_list = ast.literal_eval(kw['line_list'])
print('line_list: %s' % line_list)
for line in line_list:
- plan_data = plan_obj.search([('production_line_id.name', '=', line)])
- # 工单总量
- plan_data_total_counts = plan_obj.search_count([('production_line_id.name', '=', line)])
+
+ # 工单计划量
+ plan_data_total_counts = production_obj.search_count(
+ [('production_line_id.name', '=', line), ('state', 'not in', ['cancel']),
+ ('active', '=', True)])
# 工单完成量
plan_data_finish_counts = plan_obj.search_count(
[('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
- # 工单计划量
- plan_data_plan_counts = plan_obj.search_count(
- [('production_line_id.name', '=', line), ('state', 'not in', ['finished'])])
- # # 工单不良累计
- # plan_data_fault_counts = plan_obj.search_count(
- # [('production_line_id.name', '=', line), ('production_id.detection_result_ids.state', 'in', ['返工', '报废'])])
+ # # 工单计划量
+ # plan_data_plan_counts = plan_obj.search_count(
+ # [('production_line_id.name', '=', line), ('state', 'not in', ['finished'])])
# 查找符合条件的生产计划记录
plan_data = plan_obj.search([
@@ -327,8 +327,13 @@ class Sf_Dashboard_Connect(http.Controller):
result.test_results in ['返工', '报废'] for result in p.production_id.detection_result_ids
))
+ # 查找制造订单取消与归档的数量
+ cancel_order_count = production_obj.search_count(
+ [('production_line_id.name', '=', line), ('state', 'in', ['cancel']),
+ ('active', '=', False)])
+
# 计算符合条件的记录数量
- plan_data_fault_counts = len(faulty_plans)
+ plan_data_fault_counts = len(faulty_plans) + cancel_order_count
# 工单返工数量
@@ -340,13 +345,13 @@ class Sf_Dashboard_Connect(http.Controller):
(plan_data_finish_counts / plan_data_total_counts if plan_data_total_counts > 0 else 0), 3)
# 工单进度偏差
- plan_data_progress_deviation = plan_data_finish_counts - plan_data_plan_counts
+ plan_data_progress_deviation = plan_data_total_counts - plan_data_finish_counts - plan_data_fault_counts
if plan_data:
data = {
'plan_data_total_counts': plan_data_total_counts,
'plan_data_finish_counts': plan_data_finish_counts,
- 'plan_data_plan_counts': plan_data_plan_counts,
+ 'plan_data_plan_counts': plan_data_total_counts,
'plan_data_fault_counts': plan_data_fault_counts,
'finishe_rate': finishe_rate,
'plan_data_progress_deviation': plan_data_progress_deviation,
diff --git a/sf_plan/models/custom_plan.py b/sf_plan/models/custom_plan.py
index c4043d33..b3bd6ff6 100644
--- a/sf_plan/models/custom_plan.py
+++ b/sf_plan/models/custom_plan.py
@@ -18,7 +18,8 @@ class sf_production_plan(models.Model):
('draft', '待排程'),
('done', '已排程'),
('processing', '加工中'),
- ('finished', '已完成')
+ ('finished', '已完成'),
+ ('cancel', '已取消')
], string='状态', tracking=True)
state_order = fields.Integer(compute='_compute_state_order', store=True)
@@ -37,7 +38,7 @@ class sf_production_plan(models.Model):
_order = 'state_order asc, write_date desc'
name = fields.Char(string='制造订单')
- # active = fields.Boolean(string='已归档', default=True)
+ active = fields.Boolean(string='已归档', default=True)
# selected = fields.Boolean(default=False)
# order_number = fields.Char(string='订单号')
order_deadline = fields.Datetime(string='订单交期')
@@ -385,3 +386,23 @@ class machine_work_schedule(models.Model):
_description = '机台作业计划'
name = fields.Char(string='机台名')
+
+
+class MrpProductionInheritForPlan(models.Model):
+ _inherit = 'mrp.production'
+
+ def button_cancel(self):
+ # 调用父类的取消操作
+ res = super(MrpProductionInheritForPlan, self).button_cancel()
+ # 更新 sf.production.plan 模型的 state 为 'cancel'
+ self.env['sf.production.plan'].search([('production_id', '=', self.id)]).write({'state': 'cancel'})
+ return res
+
+ def toggle_active(self):
+ # 调用父类方法切换 active 状态
+ res = super(MrpProductionInheritForPlan, self).toggle_active()
+ self.env['sf.production.plan'].search(
+ [('production_id', '=', self.id), '|', ('active', '=', False), ('active', '=', True)]).write(
+ {'active': self.active})
+
+ return res
From 4394dd023ed62d37db4a60942e25983321f9d8da Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Wed, 11 Sep 2024 10:11:22 +0800
Subject: [PATCH 10/40] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=A4=E4=BB=98?=
=?UTF-8?q?=E5=87=86=E6=97=B6=E7=8E=87?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_machine_connect/controllers/controllers.py | 40 +++++++++++++++++--
1 file changed, 36 insertions(+), 4 deletions(-)
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index 6f692bde..9702b6e4 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -6,7 +6,7 @@ import base64
import logging
import psycopg2
from datetime import datetime, timedelta
-from odoo import http
+from odoo import http, fields
from odoo.http import request
# 数据库连接配置
@@ -347,6 +347,32 @@ class Sf_Dashboard_Connect(http.Controller):
# 工单进度偏差
plan_data_progress_deviation = plan_data_total_counts - plan_data_finish_counts - plan_data_fault_counts
+ # 交付准时率
+ plan_data_finish_orders = plan_obj.search(
+ [('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
+
+ delay_num = 0
+ for plan in plan_data_finish_orders:
+ sale_obj = request.env['sale.order'].sudo().search([('name', '=', plan.origin)])
+ if sale_obj:
+ if sale_obj.deadline_of_delivery and plan.actual_end_time:
+ # 将 deadline_of_delivery 转换为字符串
+ date_as_string = sale_obj.deadline_of_delivery.strftime('%Y-%m-%d')
+ # 然后使用 strptime 将字符串转换为 datetime 对象
+ date_as_datetime = datetime.strptime(date_as_string, '%Y-%m-%d')
+
+ # 将 actual_end_time 转换为 datetime 对象
+ datetime_value = fields.Datetime.from_string(plan.actual_end_time)
+
+ # 给 datetime_value 加1天
+ new_datetime_value = datetime_value + timedelta(days=1)
+
+ # 比较 new_datetime_value 和 date_as_datetime 的大小
+ if new_datetime_value.date() > date_as_datetime.date():
+ delay_num += 1
+ delay_rate = round((delay_num / plan_data_finish_counts if plan_data_finish_counts > 0 else 0), 3)
+ on_time_rate = 1 - delay_rate
+
if plan_data:
data = {
'plan_data_total_counts': plan_data_total_counts,
@@ -355,7 +381,8 @@ class Sf_Dashboard_Connect(http.Controller):
'plan_data_fault_counts': plan_data_fault_counts,
'finishe_rate': finishe_rate,
'plan_data_progress_deviation': plan_data_progress_deviation,
- 'plan_data_rework_counts': plan_data_rework_counts
+ 'plan_data_rework_counts': plan_data_rework_counts,
+ 'on_time_rate': on_time_rate
}
res['data'][line] = data
@@ -514,10 +541,15 @@ class Sf_Dashboard_Connect(http.Controller):
for line in line_list:
# 未完成订单
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']), ('active', '=', True)
+ ])
print(not_done_orders)
# 完成订单
- finish_orders = plan_obj.search([('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
+ finish_orders = plan_obj.search([
+ ('production_line_id.name', '=', line), ('state', 'in', ['finished']),
+ ('production_id.state', 'not in', ['cancel']), ('active', '=', True)
+ ])
print(finish_orders)
# 获取所有未完成订单的ID列表
From 436adc5ff60abf5a33533c4146cb65742f087a4a Mon Sep 17 00:00:00 2001
From: liaodanlong
Date: Wed, 11 Sep 2024 10:39:27 +0800
Subject: [PATCH 11/40] =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=88=80=E5=85=B7?=
=?UTF-8?q?=E6=B8=85=E5=8D=95=20bom=E6=B7=BB=E5=8A=A0=E6=9F=A5=E8=AF=A2?=
=?UTF-8?q?=E6=8E=92=E5=BA=8F=E4=B8=8E=E6=B8=85=E5=8D=95=E6=98=8E=E7=BB=86?=
=?UTF-8?q?=E8=A1=8C=E5=88=A0=E9=99=A4=E6=A0=A1=E9=AA=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_tool_management/models/jikimo_bom.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/sf_tool_management/models/jikimo_bom.py b/sf_tool_management/models/jikimo_bom.py
index 29ef1605..73f63173 100644
--- a/sf_tool_management/models/jikimo_bom.py
+++ b/sf_tool_management/models/jikimo_bom.py
@@ -47,6 +47,7 @@ class jikimo_bom(models.Model):
return True
else:
raise UserError('每种物料最少要有一个')
+ return True
return super(jikimo_bom, self).write(vals)
def bom_product_domains(self, assembly_options):
From d5c82e4a28bbb80e7f5524aa5e82b654b27e8ed8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?=
Date: Wed, 11 Sep 2024 10:40:26 +0800
Subject: [PATCH 12/40] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BB=A3=E7=A0=81?=
=?UTF-8?q?=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_manufacturing/models/agv_scheduling.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/sf_manufacturing/models/agv_scheduling.py b/sf_manufacturing/models/agv_scheduling.py
index f758abd9..a18dc5ef 100644
--- a/sf_manufacturing/models/agv_scheduling.py
+++ b/sf_manufacturing/models/agv_scheduling.py
@@ -1,10 +1,10 @@
+
+import logging
import requests
from odoo import models, fields, api, _
from odoo.exceptions import UserError
-import logging
-
_logger = logging.getLogger(__name__)
@@ -54,7 +54,7 @@ class AgvScheduling(models.Model):
def web_search_read(self, domain=None, fields=None, offset=0, limit=None, order=None, count_limit=None):
domain = domain or []
new_domain = []
- for index, item in enumerate(domain):
+ for item in domain:
if isinstance(item, list):
if item[0] == 'delivery_workpieces':
new_domain.append('&')
@@ -63,7 +63,7 @@ class AgvScheduling(models.Model):
continue
new_domain.append(item)
- return super(AgvScheduling, self).web_search_read(new_domain, fields, limit=limit, offset=offset)
+ return super(AgvScheduling, self).web_search_read(new_domain, fields, offset, limit, order, count_limit)
@api.depends('task_completion_time', 'task_delivery_time')
def _compute_task_duration(self):
From 96eeb1cab74584063062032c61414f0d9ee353bc Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Wed, 11 Sep 2024 11:45:14 +0800
Subject: [PATCH 13/40] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=A3=80=E6=B5=8B?=
=?UTF-8?q?=E9=87=8F=E3=80=81=E5=90=88=E6=A0=BC=E9=87=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_machine_connect/controllers/controllers.py | 35 +++++++++++++++++--
1 file changed, 32 insertions(+), 3 deletions(-)
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index 9702b6e4..8802a5be 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -333,7 +333,8 @@ class Sf_Dashboard_Connect(http.Controller):
('active', '=', False)])
# 计算符合条件的记录数量
- plan_data_fault_counts = len(faulty_plans) + cancel_order_count
+ # plan_data_fault_counts = len(faulty_plans) + cancel_order_count
+ plan_data_fault_counts = len(faulty_plans)
# 工单返工数量
@@ -347,10 +348,36 @@ class Sf_Dashboard_Connect(http.Controller):
# 工单进度偏差
plan_data_progress_deviation = plan_data_total_counts - plan_data_finish_counts - plan_data_fault_counts
- # 交付准时率
+ # 完成记录
plan_data_finish_orders = plan_obj.search(
[('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
+ # # 检测量
+ # detection_nums = 0
+ # for i in plan_data_finish_orders:
+ # print('i: %s' % i)
+ # if i.production_id.detection_result_ids:
+ # detection_nums += 1
+ #
+ # print('detection_nums: %s' % detection_nums)
+
+ # 检测量
+ detection_data = len(plan_data_finish_orders.mapped('production_id.detection_result_ids').filtered(lambda r: r))
+
+ # 检测合格量
+ pass_nums = plan_data_finish_orders.filtered(lambda p: any(
+ result.test_results in ['合格'] for result in p.production_id.detection_result_ids
+ ))
+
+ # 质量合格率
+ pass_rate = 1
+ if pass_nums:
+ pass_rate = round((len(pass_nums) / len(plan_data_finish_orders) if len(plan_data_finish_orders) > 0 else 0), 3)
+
+ # 返工率
+ rework_rate = round((plan_data_rework_counts / plan_data_finish_counts if plan_data_finish_counts > 0 else 0), 3)
+
+ # 交付准时率
delay_num = 0
for plan in plan_data_finish_orders:
sale_obj = request.env['sale.order'].sudo().search([('name', '=', plan.origin)])
@@ -382,7 +409,9 @@ class Sf_Dashboard_Connect(http.Controller):
'finishe_rate': finishe_rate,
'plan_data_progress_deviation': plan_data_progress_deviation,
'plan_data_rework_counts': plan_data_rework_counts,
- 'on_time_rate': on_time_rate
+ 'on_time_rate': on_time_rate,
+ 'detection_data': detection_data,
+ 'pass_rate': pass_rate
}
res['data'][line] = data
From 763d64a1cad789d096083933818d766b391e88e3 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Wed, 11 Sep 2024 14:57:42 +0800
Subject: [PATCH 14/40] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=AE=BE=E5=A4=87?=
=?UTF-8?q?=E8=BF=90=E8=A1=8C=E6=97=B6=E9=95=BF=E6=95=88=E7=8E=87=E6=8E=A5?=
=?UTF-8?q?=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_machine_connect/controllers/controllers.py | 176 +++++++++++++++++-
1 file changed, 173 insertions(+), 3 deletions(-)
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index 8802a5be..e4496553 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -22,7 +22,11 @@ db_config = {
def convert_to_seconds(time_str):
# 修改正则表达式,使 H、M、S 部分可选
+ if time_str is None:
+ return 0
+
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
+
match = re.match(pattern, time_str)
if match:
@@ -362,7 +366,8 @@ class Sf_Dashboard_Connect(http.Controller):
# print('detection_nums: %s' % detection_nums)
# 检测量
- detection_data = len(plan_data_finish_orders.mapped('production_id.detection_result_ids').filtered(lambda r: r))
+ detection_data = len(
+ plan_data_finish_orders.mapped('production_id.detection_result_ids').filtered(lambda r: r))
# 检测合格量
pass_nums = plan_data_finish_orders.filtered(lambda p: any(
@@ -372,10 +377,12 @@ class Sf_Dashboard_Connect(http.Controller):
# 质量合格率
pass_rate = 1
if pass_nums:
- pass_rate = round((len(pass_nums) / len(plan_data_finish_orders) if len(plan_data_finish_orders) > 0 else 0), 3)
+ pass_rate = round(
+ (len(pass_nums) / len(plan_data_finish_orders) if len(plan_data_finish_orders) > 0 else 0), 3)
# 返工率
- rework_rate = round((plan_data_rework_counts / plan_data_finish_counts if plan_data_finish_counts > 0 else 0), 3)
+ rework_rate = round(
+ (plan_data_rework_counts / plan_data_finish_counts if plan_data_finish_counts > 0 else 0), 3)
# 交付准时率
delay_num = 0
@@ -928,3 +935,166 @@ class Sf_Dashboard_Connect(http.Controller):
else:
# 如果没有图片数据,返回404
return request.not_found()
+
+ # 设备运行时长
+ @http.route('/api/RunningTime', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
+ def RunningTime(self, **kw):
+ """
+ 获取设备运行时长
+ """
+ res = {'status': 1, 'message': '成功', 'data': {}}
+ # 连接数据库
+ conn = psycopg2.connect(**db_config)
+ # 获取请求的机床数据
+ machine_list = ast.literal_eval(kw['machine_list'])
+
+ def fetch_result_as_dict(cursor):
+ """辅助函数:将查询结果转为字典"""
+ columns = [desc[0] for desc in cursor.description]
+ return dict(zip(columns, cursor.fetchone())) if cursor.rowcount != 0 else None
+
+ # 初始化当天、当月和有记录以来的总时长
+ day_total_running_time = 0
+ day_total_process_time = 0
+ day_work_rate = 0
+ month_total_running_time = 0
+ month_total_process_time = 0
+ month_work_rate = 0
+ all_time_total_running_time = 0
+ all_time_total_process_time = 0
+ all_time_work_rate = 0
+
+ for item in machine_list:
+ # 获取当天第一条记录(排除device_state等于‘离线’的记录)
+ with conn.cursor() as cur:
+ cur.execute("""
+ SELECT * FROM device_data
+ WHERE device_name = %s
+ AND time::date = CURRENT_DATE
+ AND device_state != '离线'
+ ORDER BY time ASC
+ LIMIT 1;
+ """, (item,))
+ first_today = fetch_result_as_dict(cur)
+ print("当天第一条记录(非离线):", first_today)
+
+ # 获取当天最新一条记录(排除device_state等于‘离线’的记录)
+ with conn.cursor() as cur:
+ cur.execute("""
+ SELECT * FROM device_data
+ WHERE device_name = %s
+ AND time::date = CURRENT_DATE
+ AND device_state != '离线'
+ ORDER BY time DESC
+ LIMIT 1;
+ """, (item,))
+ last_today = fetch_result_as_dict(cur)
+ print("当天最新一条记录(非离线):", last_today)
+
+ # 计算当天运行时长
+ if first_today and last_today:
+ running_time = convert_to_seconds(last_today['run_time']) - convert_to_seconds(first_today['run_time'])
+ process_time = convert_to_seconds(last_today['process_time']) - convert_to_seconds(
+ first_today['process_time'])
+ day_total_running_time += abs(running_time)
+ day_total_process_time += abs(process_time)
+
+ # 获取当月第一条记录(排除device_state等于‘离线’的记录)
+ with conn.cursor() as cur:
+ cur.execute("""
+ SELECT * FROM device_data
+ WHERE device_name = %s
+ AND EXTRACT(YEAR FROM time) = EXTRACT(YEAR FROM CURRENT_DATE)
+ AND EXTRACT(MONTH FROM time) = EXTRACT(MONTH FROM CURRENT_DATE)
+ AND device_state != '离线'
+ ORDER BY time ASC
+ LIMIT 1;
+ """, (item,))
+ first_month = fetch_result_as_dict(cur)
+ print("当月第一条记录(非离线):", first_month)
+
+ # 获取当月最新一条记录(排除device_state等于‘离线’的记录)
+ with conn.cursor() as cur:
+ cur.execute("""
+ SELECT * FROM device_data
+ WHERE device_name = %s
+ AND EXTRACT(YEAR FROM time) = EXTRACT(YEAR FROM CURRENT_DATE)
+ AND EXTRACT(MONTH FROM time) = EXTRACT(MONTH FROM CURRENT_DATE)
+ AND device_state != '离线'
+ ORDER BY time DESC
+ LIMIT 1;
+ """, (item,))
+ last_month = fetch_result_as_dict(cur)
+ print("当月最新一条记录(非离线):", last_month)
+
+ # 计算当月运行时长
+ if first_month and last_month:
+ month_running_time = convert_to_seconds(last_month['run_time']) - convert_to_seconds(
+ first_month['run_time'])
+ month_process_time = convert_to_seconds(last_month['process_time']) - convert_to_seconds(
+ first_month['process_time'])
+ month_total_running_time += abs(month_running_time)
+ month_total_process_time += abs(month_process_time)
+
+ # 获取有记录以来的第一条记录(排除device_state等于‘离线’的记录)
+ with conn.cursor() as cur:
+ cur.execute("""
+ SELECT * FROM device_data
+ WHERE device_name = %s
+ AND device_state != '离线'
+ ORDER BY time ASC
+ LIMIT 1;
+ """, (item,))
+ first_all_time = fetch_result_as_dict(cur)
+ print("有记录以来的第一条记录(非离线):", first_all_time)
+
+ # 获取有记录以来的最新一条记录(排除device_state等于‘离线’的记录)
+ with conn.cursor() as cur:
+ cur.execute("""
+ SELECT * FROM device_data
+ WHERE device_name = %s
+ AND device_state != '离线'
+ ORDER BY time DESC
+ LIMIT 1;
+ """, (item,))
+ last_all_time = fetch_result_as_dict(cur)
+ print("有记录以来的最新一条记录(非离线):", last_all_time)
+
+ # 计算有记录以来的运行时长
+ if first_all_time and last_all_time:
+ all_time_running_time = convert_to_seconds(last_all_time['run_time']) - convert_to_seconds(
+ first_all_time['run_time'])
+ all_time_process_time = convert_to_seconds(last_all_time['process_time']) - convert_to_seconds(
+ first_all_time['process_time'])
+ all_time_total_running_time += abs(all_time_running_time)
+ all_time_total_process_time += abs(all_time_process_time)
+
+ # 计算当天工作效率
+ if day_total_running_time > day_total_process_time:
+ day_work_rate = day_total_process_time / day_total_running_time if day_total_running_time != 0 else 0
+ else:
+ day_work_rate = day_total_running_time / day_total_process_time if day_total_process_time != 0 else 0
+ print("当天工作效率: %s" % day_work_rate)
+
+ # 计算当月工作效率
+ if month_total_running_time > month_total_process_time:
+ month_work_rate = month_total_process_time / month_total_running_time if month_total_running_time != 0 else 0
+ else:
+ month_work_rate = month_total_running_time / month_total_process_time if month_total_process_time != 0 else 0
+ print("当月工作效率: %s" % month_work_rate)
+
+ # 计算有记录以来的工作效率
+ if all_time_total_running_time > all_time_total_process_time:
+ all_time_work_rate = all_time_total_process_time / all_time_total_running_time if all_time_total_running_time != 0 else 0
+ else:
+ all_time_work_rate = all_time_total_running_time / all_time_total_process_time if all_time_total_process_time != 0 else 0
+ print("有记录以来的工作效率: %s" % all_time_work_rate)
+
+ conn.close()
+
+ # 返回数据
+ res['data']['day_work_rate'] = day_work_rate
+ res['data']['month_work_rate'] = month_work_rate
+ res['data']['all_time_work_rate'] = all_time_work_rate
+
+ return json.dumps(res)
From da5e322229c4110981f8a2d50d66072971e6f68c Mon Sep 17 00:00:00 2001
From: yuxianghui <3437689193@qq.com>
Date: Wed, 11 Sep 2024 17:49:43 +0800
Subject: [PATCH 15/40] =?UTF-8?q?1=E3=80=81=E7=BB=84=E8=A3=85=E5=8D=95?=
=?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BA=94=E7=A7=8D=E7=89=A9=E6=96=99=E7=9A=84?=
=?UTF-8?q?=E3=80=90=E6=9B=B4=E5=A4=9A=E3=80=91=E6=8C=89=E9=92=AE=EF=BC=8C?=
=?UTF-8?q?=E7=82=B9=E5=87=BB=E6=8C=89=E9=92=AE=E8=B7=B3=E8=BD=AC=E8=87=B3?=
=?UTF-8?q?=E6=96=B0=E7=9A=84=E5=BC=B9=E7=AA=97=E7=95=8C=E9=9D=A2=EF=BC=8C?=
=?UTF-8?q?=E6=A0=B9=E6=8D=AE=E5=8A=9F=E8=83=BD=E5=88=80=E5=85=B7=E6=B8=85?=
=?UTF-8?q?=E5=8D=95BOM=E7=9A=84=E9=85=8D=E7=BD=AE=EF=BC=8C=E6=90=9C?=
=?UTF-8?q?=E7=B4=A2=E5=87=BA=E5=85=A8=E9=83=A8=E6=BB=A1=E8=B6=B3=E6=9D=A1?=
=?UTF-8?q?=E4=BB=B6=E7=9A=84=E5=88=80=E6=9F=84=E4=BA=A7=E5=93=81=E6=88=96?=
=?UTF-8?q?=E8=80=85=E5=85=B6=E4=BB=96=E5=88=80=E5=85=B7=E7=89=A9=E6=96=99?=
=?UTF-8?q?=E5=AF=B9=E5=BA=94=E7=9A=84=E6=89=B9=E6=AC=A1=E3=80=81=E8=B4=A7?=
=?UTF-8?q?=E4=BD=8D=EF=BC=8C=E4=BD=BF=E7=94=A8tree=E8=A7=86=E5=9B=BE?=
=?UTF-8?q?=E6=8F=90=E4=BE=9B=E9=80=89=E6=8B=A9=E3=80=822=E3=80=81?=
=?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BA=A7=E5=93=81tree=E3=80=81search?=
=?UTF-8?q?=E8=A7=86=E5=9B=BE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_tool_management/models/base.py | 48 +++++++++++++++++++-
sf_tool_management/models/stock.py | 6 +++
sf_tool_management/views/tool_base_views.xml | 21 +++++++++
3 files changed, 74 insertions(+), 1 deletion(-)
diff --git a/sf_tool_management/models/base.py b/sf_tool_management/models/base.py
index 4d7821ac..518c4c0f 100644
--- a/sf_tool_management/models/base.py
+++ b/sf_tool_management/models/base.py
@@ -925,7 +925,53 @@ class FunctionalToolAssembly(models.Model):
else:
raise ValidationError(f'功能刀具清单BOM的组装方式错误:【{options}】')
- # def set_
+ def set_tool_lot(self):
+ # 获取BOM的刀具物料产品信息
+ tool_data = self._get_inventory_bom(self.tool_inventory_id)
+
+ # 获取刀具类型
+ tool_type = self.env.context.get('tool_type')
+
+ if tool_type == '刀柄':
+ tool_material_ids = tool_data.get('handle_ids')
+ elif tool_type == '整体式刀具':
+ tool_material_ids = self._get_all_material_location_lot(tool_data.get('integral_ids'))
+ elif tool_type == '刀片':
+ tool_material_ids = self._get_all_material_location_lot(tool_data.get('handle_ids'))
+ elif tool_type == '刀杆':
+ tool_material_ids = self._get_all_material_location_lot(tool_data.get('bar_ids'))
+ else:
+ tool_material_ids = self._get_all_material_location_lot(tool_data.get('pad_ids'))
+
+ if tool_type == '刀柄':
+ return {
+ "type": "ir.actions.act_window",
+ "res_model": "product.product",
+ "views": [[self.env.ref('sf_tool_management.view_tool_product_tree').id, "tree"],
+ [self.env.ref('sf_tool_management.view_tool_product_search').id, "search"]],
+ "target": "new",
+ "domain": [('id', 'in', tool_material_ids.ids)]
+ }
+ elif tool_type in ['整体式刀具', '刀片', '刀杆', '刀盘']:
+ return {
+ "type": "ir.actions.act_window",
+ "res_model": "sf.shelf.location.lot",
+ "views": [[False, "tree"]],
+ "target": "new",
+ "domain": [('id', 'in', tool_material_ids.ids)],
+ "context": {'create': False}
+ }
+
+ def _get_all_material_location_lot(self, material_ids):
+ """ 获取所有满足条件 """
+ location_id = self.env['stock.location'].search([('name', '=', '刀具房')])
+ stock_quant_ids = self.env['stock.quant'].sudo().search(
+ [('location_id', '=', location_id.id), ('product_id', 'in', material_ids.ids), ('quantity', '>', '0')])
+ lot_ids = []
+ for stock_quant_id in stock_quant_ids:
+ lot_ids.append(stock_quant_id.lot_id.id)
+ location_lots = self.env['sf.shelf.location.lot'].sudo().search([('lot_id', 'in', lot_ids)])
+ return location_lots
def functional_tool_assembly(self):
"""
diff --git a/sf_tool_management/models/stock.py b/sf_tool_management/models/stock.py
index 22a7fa72..50218e65 100644
--- a/sf_tool_management/models/stock.py
+++ b/sf_tool_management/models/stock.py
@@ -247,3 +247,9 @@ class ProductProduct(models.Model):
m = int(stock_lot_id.name[-3:]) + 1
num = "%03d" % m
return '%s-%s' % (code, num)
+
+
+class SfShelfLocationLot(models.Model):
+ _inherit = 'sf.shelf.location.lot'
+
+
diff --git a/sf_tool_management/views/tool_base_views.xml b/sf_tool_management/views/tool_base_views.xml
index bd4d949c..0c492721 100644
--- a/sf_tool_management/views/tool_base_views.xml
+++ b/sf_tool_management/views/tool_base_views.xml
@@ -524,6 +524,7 @@
+
+
+
+
@@ -552,6 +557,10 @@
+
+
+
@@ -572,6 +581,10 @@
+
+
+
@@ -592,6 +605,10 @@
+
+
+
@@ -610,6 +627,10 @@
+
+
+
From 908e4a3df619081bc0949b485fde01746ef7443d Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Wed, 11 Sep 2024 18:07:21 +0800
Subject: [PATCH 16/40] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=80=9A=E8=BF=87?=
=?UTF-8?q?=E6=95=B0=E9=87=8F=E5=8F=96=E5=80=BC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_machine_connect/controllers/controllers.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index e4496553..92410fbd 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -378,7 +378,7 @@ class Sf_Dashboard_Connect(http.Controller):
pass_rate = 1
if pass_nums:
pass_rate = round(
- (len(pass_nums) / len(plan_data_finish_orders) if len(plan_data_finish_orders) > 0 else 0), 3)
+ (len(pass_nums) / detection_data if len(plan_data_finish_orders) > 0 else 0), 3)
# 返工率
rework_rate = round(
@@ -413,6 +413,7 @@ class Sf_Dashboard_Connect(http.Controller):
'plan_data_finish_counts': plan_data_finish_counts,
'plan_data_plan_counts': plan_data_total_counts,
'plan_data_fault_counts': plan_data_fault_counts,
+ 'nopass_orders_counts': detection_data - len(pass_nums),
'finishe_rate': finishe_rate,
'plan_data_progress_deviation': plan_data_progress_deviation,
'plan_data_rework_counts': plan_data_rework_counts,
From 95e49c7b0fad4f90c120ec4d0b6531a0330d9d90 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Thu, 12 Sep 2024 09:17:52 +0800
Subject: [PATCH 17/40] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=89=93=E5=8D=B0?=
=?UTF-8?q?=E8=BE=93=E5=87=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_machine_connect/controllers/controllers.py | 38 ++++++++++---------
1 file changed, 20 insertions(+), 18 deletions(-)
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index 92410fbd..e8c5c226 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -70,7 +70,7 @@ class Sf_Dashboard_Connect(http.Controller):
# 获取当前时间的时间戳
current_timestamp = datetime.now().timestamp()
- print(current_timestamp)
+ # print(current_timestamp)
# tem_list = [
# "XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-1", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-3",
@@ -199,7 +199,7 @@ class Sf_Dashboard_Connect(http.Controller):
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
- print('begin_time: %s' % begin_time)
+ # print('begin_time: %s' % begin_time)
for item in machine_list:
sql = '''
@@ -280,7 +280,7 @@ class Sf_Dashboard_Connect(http.Controller):
res = {'Succeed': True}
line_list_obj = request.env['sf.production.line'].sudo().search([('name', 'ilike', 'CNC')])
line_list = list(map(lambda x: x.name, line_list_obj))
- print('line_list: %s' % line_list)
+ # print('line_list: %s' % line_list)
res['LineList'] = line_list
except Exception as e:
@@ -307,7 +307,7 @@ class Sf_Dashboard_Connect(http.Controller):
plan_obj = request.env['sf.production.plan'].sudo()
production_obj = request.env['mrp.production'].sudo()
line_list = ast.literal_eval(kw['line_list'])
- print('line_list: %s' % line_list)
+ # print('line_list: %s' % line_list)
for line in line_list:
# 工单计划量
@@ -446,7 +446,7 @@ class Sf_Dashboard_Connect(http.Controller):
end_time_str = kw['end_time'].strip('"')
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
- print('line_list: %s' % line_list)
+ # print('line_list: %s' % line_list)
def get_date_list(start_date, end_date):
date_list = []
@@ -483,7 +483,9 @@ class Sf_Dashboard_Connect(http.Controller):
'date': date.strftime('%Y-%m-%d'),
'order_count': len(orders),
'rework_orders': len(rework_orders),
- 'not_passed_orders': len(not_passed_orders)
+ 'not_passed_orders': len(not_passed_orders),
+ 'finish_order_nums': len(orders),
+ 'plan_order_nums': 20,
})
# 外面包一层,没什么是包一层不能解决的,包一层就能区分了,类似于包一层div
# 外面包一层的好处是,可以把多个数据结构打包在一起,方便前端处理
@@ -570,7 +572,7 @@ class Sf_Dashboard_Connect(http.Controller):
end_time_str = kw['end_time'].strip('"')
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
- print('line_list: %s' % line_list)
+ # print('line_list: %s' % line_list)
not_done_data = []
done_data = []
final_data = {}
@@ -581,13 +583,13 @@ class Sf_Dashboard_Connect(http.Controller):
[('production_line_id.name', '=', line), ('state', 'not in', ['finished']),
('production_id.state', 'not in', ['cancel']), ('active', '=', True)
])
- print(not_done_orders)
+ # print(not_done_orders)
# 完成订单
finish_orders = plan_obj.search([
('production_line_id.name', '=', line), ('state', 'in', ['finished']),
('production_id.state', 'not in', ['cancel']), ('active', '=', True)
])
- print(finish_orders)
+ # print(finish_orders)
# 获取所有未完成订单的ID列表
order_ids = [order.id for order in not_done_orders]
@@ -710,11 +712,11 @@ class Sf_Dashboard_Connect(http.Controller):
# 执行SQL命令
cur.execute(sql, (item,))
result = cur.fetchall()
- print('result========', result)
+ # print('result========', result)
cur.execute(sql2, (item,))
result2 = cur.fetchall()
- print('result2========', result2)
+ # print('result2========', result2)
#
for row in result:
res['data'][item] = {'idle_count': row[0]}
@@ -770,7 +772,7 @@ class Sf_Dashboard_Connect(http.Controller):
# 执行SQL命令
cur.execute(sql)
result = cur.fetchall()
- print('result', result)
+ # print('result', result)
# 将查询结果转换为字典列表
data = []
@@ -977,7 +979,7 @@ class Sf_Dashboard_Connect(http.Controller):
LIMIT 1;
""", (item,))
first_today = fetch_result_as_dict(cur)
- print("当天第一条记录(非离线):", first_today)
+ # print("当天第一条记录(非离线):", first_today)
# 获取当天最新一条记录(排除device_state等于‘离线’的记录)
with conn.cursor() as cur:
@@ -990,7 +992,7 @@ class Sf_Dashboard_Connect(http.Controller):
LIMIT 1;
""", (item,))
last_today = fetch_result_as_dict(cur)
- print("当天最新一条记录(非离线):", last_today)
+ # print("当天最新一条记录(非离线):", last_today)
# 计算当天运行时长
if first_today and last_today:
@@ -1012,7 +1014,7 @@ class Sf_Dashboard_Connect(http.Controller):
LIMIT 1;
""", (item,))
first_month = fetch_result_as_dict(cur)
- print("当月第一条记录(非离线):", first_month)
+ # print("当月第一条记录(非离线):", first_month)
# 获取当月最新一条记录(排除device_state等于‘离线’的记录)
with conn.cursor() as cur:
@@ -1026,7 +1028,7 @@ class Sf_Dashboard_Connect(http.Controller):
LIMIT 1;
""", (item,))
last_month = fetch_result_as_dict(cur)
- print("当月最新一条记录(非离线):", last_month)
+ # print("当月最新一条记录(非离线):", last_month)
# 计算当月运行时长
if first_month and last_month:
@@ -1047,7 +1049,7 @@ class Sf_Dashboard_Connect(http.Controller):
LIMIT 1;
""", (item,))
first_all_time = fetch_result_as_dict(cur)
- print("有记录以来的第一条记录(非离线):", first_all_time)
+ # print("有记录以来的第一条记录(非离线):", first_all_time)
# 获取有记录以来的最新一条记录(排除device_state等于‘离线’的记录)
with conn.cursor() as cur:
@@ -1059,7 +1061,7 @@ class Sf_Dashboard_Connect(http.Controller):
LIMIT 1;
""", (item,))
last_all_time = fetch_result_as_dict(cur)
- print("有记录以来的最新一条记录(非离线):", last_all_time)
+ # print("有记录以来的最新一条记录(非离线):", last_all_time)
# 计算有记录以来的运行时长
if first_all_time and last_all_time:
From e0bad6ed40731a8b0cd9092989f1985806cb10e7 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Thu, 12 Sep 2024 11:08:24 +0800
Subject: [PATCH 18/40] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=BD=93=E7=8F=AD?=
=?UTF-8?q?=E8=AE=A1=E5=88=92=E9=87=8F=EF=BC=8C=E5=AE=8C=E6=88=90=E9=87=8F?=
=?UTF-8?q?=E6=95=B0=E6=8D=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_machine_connect/controllers/controllers.py | 52 +++++++++++++++++--
1 file changed, 47 insertions(+), 5 deletions(-)
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index e8c5c226..4f99f319 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -447,6 +447,27 @@ class Sf_Dashboard_Connect(http.Controller):
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
# print('line_list: %s' % line_list)
+ print('kw', kw)
+ time_unit = kw.get('time_unit', 'day').strip('"') # 默认单位为天
+ print('time_unit: %s' % time_unit)
+
+ # 日期或小时循环生成器,根据time_unit决定是按天还是按小时
+ def get_time_intervals(start_time, end_time, time_unit):
+ intervals = []
+ current_time = start_time
+ if time_unit == 'hour':
+ delta = timedelta(hours=1)
+ else:
+ delta = timedelta(days=1)
+
+ while current_time < end_time:
+ next_time = current_time + delta
+ # 确保最后一个时间段不会超出end_time
+ if next_time > end_time:
+ next_time = end_time
+ intervals.append((current_time, next_time))
+ current_time = next_time
+ return intervals
def get_date_list(start_date, end_date):
date_list = []
@@ -457,10 +478,33 @@ class Sf_Dashboard_Connect(http.Controller):
return date_list
for line in line_list:
- date_list = get_date_list(begin_time, end_time)
+ date_field_name = 'actual_end_time' # 替换为你模型中的实际字段名
order_counts = []
- date_field_name = 'actual_end_time' # 替换为你模型中的实际字段名
+ if time_unit == 'hour':
+ time_intervals = get_time_intervals(begin_time, end_time, time_unit)
+ print('============================= %s' % time_intervals)
+
+ time_count_dict = {}
+
+ for time_interval in time_intervals:
+ start_time, end_time = time_interval
+ # print(start_time, end_time)
+ orders = plan_obj.search([('production_line_id.name', '=', line), ('state', 'in', ['finished']),
+ (date_field_name, '>=', start_time.strftime('%Y-%m-%d 00:00:00')),
+ (date_field_name, '<', end_time.strftime('%Y-%m-%d 00:00:00'))
+ ])
+ # 使用小时和分钟作为键,确保每个小时的数据有独立的键
+ key = start_time.strftime('%H:%M:%S') # 只取小时:分钟:秒作为键
+ time_count_dict[key] = len(orders)
+ order_counts.append({
+ 'finish_order_nums': time_count_dict,
+ 'plan_order_nums': 28
+ })
+ res['data'][line] = order_counts
+ return json.dumps(res)
+
+ date_list = get_date_list(begin_time, end_time)
for date in date_list:
next_day = date + timedelta(days=1)
@@ -483,9 +527,7 @@ class Sf_Dashboard_Connect(http.Controller):
'date': date.strftime('%Y-%m-%d'),
'order_count': len(orders),
'rework_orders': len(rework_orders),
- 'not_passed_orders': len(not_passed_orders),
- 'finish_order_nums': len(orders),
- 'plan_order_nums': 20,
+ 'not_passed_orders': len(not_passed_orders)
})
# 外面包一层,没什么是包一层不能解决的,包一层就能区分了,类似于包一层div
# 外面包一层的好处是,可以把多个数据结构打包在一起,方便前端处理
From 2808005ce9da2a2bc517cd65b5c4b3bc41e12d8f Mon Sep 17 00:00:00 2001
From: liaodanlong
Date: Thu, 12 Sep 2024 15:17:09 +0800
Subject: [PATCH 19/40] =?UTF-8?q?=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_bf_connect/models/process_status.py | 12 ++++++++----
sf_manufacturing/models/mrp_production.py | 6 ++++--
2 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/sf_bf_connect/models/process_status.py b/sf_bf_connect/models/process_status.py
index 111254c7..3bbdb5fc 100644
--- a/sf_bf_connect/models/process_status.py
+++ b/sf_bf_connect/models/process_status.py
@@ -1,3 +1,4 @@
+import traceback
from datetime import datetime
import logging
import requests
@@ -53,11 +54,14 @@ class StatusChange(models.Model):
if not ret.get('error'):
logging.info('接口已经执行=============')
else:
- logging.error('工厂加工同步订单状态失败 {}'.format(ret))
- raise UserError('工厂加工同步订单状态失败')
+ traceback_error = traceback.format_exc()
+ logging.error("bfm订单状态同步失败:%s request info %s" % traceback_error)
+ logging.error('/api/get/state/get_order 请求失败{}'.format(ret))
+ raise UserError('工厂加工同步订单状态到bfm失败')
except UserError as e:
- logging.error('工厂加工同步订单状态失败 {}'.format(e))
- raise UserError('工厂加工同步订单状态失败')
+ traceback_error = traceback.format_exc()
+ logging.error("工厂加工同步订单状态失败:%s " % traceback_error)
+ raise UserError(e)
return res
def action_cancel(self):
diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py
index 405baf38..0ab2be6b 100644
--- a/sf_manufacturing/models/mrp_production.py
+++ b/sf_manufacturing/models/mrp_production.py
@@ -318,8 +318,10 @@ class MrpProduction(models.Model):
# cnc程序获取
def fetchCNC(self, production_names):
cnc = self.env['mrp.production'].search([('id', '=', self.id)])
- quick_order = self.env['quick.easy.order'].search(
- [('name', '=', cnc.product_id.default_code.rsplit('-', 1)[0])])
+ quick_order = False
+ if cnc.product_id.default_code:
+ quick_order = self.env['quick.easy.order'].search(
+ [('name', '=', cnc.product_id.default_code.rsplit('-', 1)[0])])
programme_way = False
if cnc.manual_quotation is True:
programme_way = 'manual operation'
From 01c57a86912a5d811862a51ad771157a133c7c7c Mon Sep 17 00:00:00 2001
From: liaodanlong
Date: Thu, 12 Sep 2024 15:17:47 +0800
Subject: [PATCH 20/40] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E6=97=A0=E7=94=A8?=
=?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=B7=BB=E5=8A=A0=E6=8F=8F=E8=BF=B0=E4=BF=A1?=
=?UTF-8?q?=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_tool_management/models/jikimo_bom.py | 42 +++++++++++++------
sf_tool_management/models/tool_inventory.py | 6 +--
.../wizard/jikimo_bom_wizard.py | 1 -
3 files changed, 31 insertions(+), 18 deletions(-)
diff --git a/sf_tool_management/models/jikimo_bom.py b/sf_tool_management/models/jikimo_bom.py
index 73f63173..8453eb48 100644
--- a/sf_tool_management/models/jikimo_bom.py
+++ b/sf_tool_management/models/jikimo_bom.py
@@ -30,9 +30,23 @@ class jikimo_bom(models.Model):
return result
def check_types_in_list(self):
- # 统计每个元素的类型
- type_counts = Counter(item.cutting_tool_material_id.name for item in self.product_ids)
- return all(count > 0 for count in type_counts.values()) and len(type_counts) == self.options.split('+')
+ """
+ 检查产品列表中的元素是否包含了所有指定的类型,并且每种类型至少出现一次。
+ :return: 如果条件满足返回True,否则返回False
+ """
+ if not self.product_ids:
+ return False
+ try:
+ # 统计每个类型的出现次数
+ type_counts = Counter(item.cutting_tool_material_id.name for item in self.product_ids)
+
+ # 检查是否每种类型的出现次数都大于0,并且类型的数量与选项字符串中的数量相等
+ return all(count > 0 for count in type_counts.values()) and len(type_counts) == len(self.options.split('+'))
+ except AttributeError:
+ # 如果出现属性错误,说明产品列表中的元素可能缺少必要的属性
+ return False
+ # type_counts = Counter(item.cutting_tool_material_id.name for item in self.product_ids)
+ # return all(count > 0 for count in type_counts.values()) and len(type_counts) == self.options.split('+')
def write(self, vals):
# 在更新模型时记录旧的 Many2many ID 列表
@@ -51,6 +65,11 @@ class jikimo_bom(models.Model):
return super(jikimo_bom, self).write(vals)
def bom_product_domains(self, assembly_options):
+ """
+ 根据装配选项生成产品域列表
+ :param assembly_options: 装配选项字符串,各选项以'+'分隔
+ :return: 动态生成的产品搜索条件
+ """
self.options = assembly_options
cutting_tool_materials = self.env['sf.cutting.tool.material'].search(
[('name', 'in', assembly_options.split('+'))])
@@ -83,23 +102,22 @@ class jikimo_bom(models.Model):
domains = domains + domain
if index != 0:
domains = ['|'] + domains
- # wqwqwe = self.env['product.product'].search(ddd)
- # product = self.env['product.product'].search(domain)
- # if product:
- # products = products + product
domains = domains + [('stock_move_count', '>', 0)]
return domains
def generate_bill_materials(self, assembly_options):
+ """
+ 生成物料清单
+
+ 根据装配选项生成物料清单,首先获取产品领域,然后搜索相关产品,并设置产品ID。
+
+ :param assembly_options: 组装方式
+ :type assembly_options: 装配选项字符串,各选项以'+'分隔
+ """
domains = self.bom_product_domains(assembly_options)
products = self.env['product.product'].search(domains)
if products:
self.product_ids = [Command.set(products.ids)]
- # if option.name == '刀盘':
- # hilt = self.env['product.product'].search(
- # [('cutting_tool_blade_diameter', '=', self.tool_inventory_id.diameter),
- # ('cutting_tool_material_id', '=', option.id)])
- # self.product_ids = [Command.set(hilt.ids)]k
class jikimo_bom_line(models.Model):
diff --git a/sf_tool_management/models/tool_inventory.py b/sf_tool_management/models/tool_inventory.py
index 5b27c9aa..7eb83aff 100644
--- a/sf_tool_management/models/tool_inventory.py
+++ b/sf_tool_management/models/tool_inventory.py
@@ -14,10 +14,7 @@ class ToolInventory(models.Model):
self._bom_mainfest()
return self.bom_mainfest()
request.session['jikimo_bom_product'] = {'bom_id': int(self.jikimo_bom_ids)}
- # context = dict(self.env.context)
- # context.update({'jikimo_bom_product': self.jikimo_bom_ids.options})
- # if self.functional_cutting_tool_model_id.cutting_tool_type_ids:
- # context.update({'jikimo_bom_product_cutting_tool_type': self.functional_cutting_tool_model_id.cutting_tool_type_ids.ids})
+
return {
'type': 'ir.actions.act_window',
'name': '刀具组装清单',
@@ -26,7 +23,6 @@ class ToolInventory(models.Model):
'view_id': self.env.ref('sf_tool_management.view_jikimo_bom_form').id,
'res_id': int(self.jikimo_bom_ids),
'target': 'current', # Use 'new' to open in a new window/tab
- # {'jikimo_bom_product': self.jikimo_bom_ids.options}
}
# 创建bom单
diff --git a/sf_tool_management/wizard/jikimo_bom_wizard.py b/sf_tool_management/wizard/jikimo_bom_wizard.py
index f86a7a09..ed6fe790 100644
--- a/sf_tool_management/wizard/jikimo_bom_wizard.py
+++ b/sf_tool_management/wizard/jikimo_bom_wizard.py
@@ -15,7 +15,6 @@ class JikimoBomWizard(models.TransientModel):
('刀柄+刀杆+刀片', '刀柄+刀杆+刀片'),
('刀柄+刀盘+刀片', '刀柄+刀盘+刀片')
], string='组装方式', required=True)
- # assembly_options_ids = fields.Many2many('sf.cutting.tool.material', string="组装方式")
is_ok = fields.Boolean('确认上述信息正确无误。')
def submit(self):
From eae902ee92871c00c2e5bb1848daffb3de8b1d6b Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Thu, 12 Sep 2024 15:36:30 +0800
Subject: [PATCH 21/40] =?UTF-8?q?=E8=B0=83=E6=95=B4=E8=AE=BE=E5=A4=87?=
=?UTF-8?q?=E8=BF=90=E8=A1=8C=E6=97=B6=E9=95=BF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_machine_connect/controllers/controllers.py | 47 +++++++++++++++++--
1 file changed, 43 insertions(+), 4 deletions(-)
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index 4f99f319..faf68f6e 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -497,11 +497,11 @@ class Sf_Dashboard_Connect(http.Controller):
# 使用小时和分钟作为键,确保每个小时的数据有独立的键
key = start_time.strftime('%H:%M:%S') # 只取小时:分钟:秒作为键
time_count_dict[key] = len(orders)
- order_counts.append({
+ # order_counts.append()
+ res['data'][line] = {
'finish_order_nums': time_count_dict,
'plan_order_nums': 28
- })
- res['data'][line] = order_counts
+ }
return json.dumps(res)
date_list = get_date_list(begin_time, end_time)
@@ -981,7 +981,7 @@ class Sf_Dashboard_Connect(http.Controller):
# 如果没有图片数据,返回404
return request.not_found()
- # 设备运行时长
+ # 设备运行率
@http.route('/api/RunningTime', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
def RunningTime(self, **kw):
"""
@@ -1143,3 +1143,42 @@ class Sf_Dashboard_Connect(http.Controller):
res['data']['all_time_work_rate'] = all_time_work_rate
return json.dumps(res)
+
+ # 设备运行时长
+ @http.route('/api/RunningTimeDetail', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
+ def RunningTimeDetail(self, **kw):
+ """
+ 获取
+ """
+ res = {'status': 1, 'message': '成功', 'data': {}}
+ # 连接数据库
+ conn = psycopg2.connect(**db_config)
+ # 获取请求的机床数据
+ machine_list = ast.literal_eval(kw['machine_list'])
+
+ def fetch_result_as_dict(cursor):
+ """辅助函数:将查询结果转为字典"""
+ columns = [desc[0] for desc in cursor.description]
+ return dict(zip(columns, cursor.fetchone())) if cursor.rowcount != 0 else None
+
+ for item in machine_list:
+ with conn.cursor() as cur:
+ cur.execute("""
+ SELECT * FROM device_data
+ WHERE device_name = %s
+ AND device_state != '离线'
+ ORDER BY time DESC
+ LIMIT 1;
+ """, (item,))
+ last_all_time = fetch_result_as_dict(cur)
+ # 返回数据
+ res['data'][item] = {
+ 'wait_time': last_all_time['run_time'] if last_all_time['run_time'] is not None else 0,
+ 'cut_time': last_all_time['process_time'] if last_all_time['process_time'] is not None else 0,
+ 'run_time': last_all_time['run_time'] if last_all_time['run_time'] is not None else 0 + last_all_time['process_time'] if last_all_time['process_time'] is not None else 0,
+ 'power_on_time': last_all_time['power_on_time'] if last_all_time['power_on_time'] is not None else 0
+ }
+
+ conn.close()
+
+ return json.dumps(res)
From 1719e0394e3c470336b0a87980e96deb3a3feeb7 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Thu, 12 Sep 2024 16:33:55 +0800
Subject: [PATCH 22/40] =?UTF-8?q?=E5=8E=BB=E6=8E=89run=5Ftime=E8=BF=94?=
=?UTF-8?q?=E5=9B=9E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_machine_connect/controllers/controllers.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index faf68f6e..e301154b 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -1175,7 +1175,6 @@ class Sf_Dashboard_Connect(http.Controller):
res['data'][item] = {
'wait_time': last_all_time['run_time'] if last_all_time['run_time'] is not None else 0,
'cut_time': last_all_time['process_time'] if last_all_time['process_time'] is not None else 0,
- 'run_time': last_all_time['run_time'] if last_all_time['run_time'] is not None else 0 + last_all_time['process_time'] if last_all_time['process_time'] is not None else 0,
'power_on_time': last_all_time['power_on_time'] if last_all_time['power_on_time'] is not None else 0
}
From 2e5694cd1a311df553d407be8e55dc33e6045c02 Mon Sep 17 00:00:00 2001
From: yuxianghui <3437689193@qq.com>
Date: Thu, 12 Sep 2024 17:20:58 +0800
Subject: [PATCH 23/40] =?UTF-8?q?1=E3=80=81=E4=BC=98=E5=8C=96=E7=BB=84?=
=?UTF-8?q?=E8=A3=85=E5=8D=95=E6=89=AB=E6=8F=8F=E9=AA=8C=E8=AF=81=E7=89=A9?=
=?UTF-8?q?=E6=96=99=E6=96=B9=E6=B3=95=EF=BC=9B2=E3=80=81=E5=AF=B9?=
=?UTF-8?q?=E7=BB=84=E8=A3=85=E5=8D=95=E7=89=A9=E6=96=99=E6=89=B9=E6=AC=A1?=
=?UTF-8?q?=E8=BF=9B=E8=A1=8C=E8=B0=83=E6=95=B4=EF=BC=8C=E5=88=A0=E9=99=A4?=
=?UTF-8?q?=E9=83=A8=E5=88=86=E5=AD=97=E6=AE=B5=E5=92=8C=E6=96=B9=E6=B3=95?=
=?UTF-8?q?=EF=BC=9B=E4=BC=98=E5=8C=96=E8=8E=B7=E5=8F=96BOM=E5=B9=B6?=
=?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=88=9D=E5=A7=8B=E5=8C=96=E7=89=A9=E6=96=99?=
=?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=96=B9=E6=B3=95=EF=BC=9B3=E3=80=81?=
=?UTF-8?q?=E8=B4=A7=E4=BD=8D=E6=89=B9=E6=AC=A1=E6=A8=A1=E5=9E=8B=E6=96=B0?=
=?UTF-8?q?=E5=A2=9E=E7=89=A9=E6=96=99=E5=AD=97=E6=AE=B5=EF=BC=8C=E6=96=B0?=
=?UTF-8?q?=E5=A2=9Etree=E8=A7=86=E5=9B=BE=EF=BC=8C=E5=B9=B6=E4=B8=94?=
=?UTF-8?q?=E9=99=A4=E5=88=80=E6=9F=84=E5=A4=96=E7=89=A9=E6=96=99=E7=82=B9?=
=?UTF-8?q?=E5=87=BB=E3=80=90=E6=9B=B4=E5=A4=9A=E3=80=91=E6=97=B6=E8=B7=B3?=
=?UTF-8?q?=E8=BD=AC=E8=87=B3=E4=B8=8D=E5=90=8C=E7=9A=84=E8=B4=A7=E4=BD=8D?=
=?UTF-8?q?=E6=89=B9=E6=AC=A1=E6=A8=A1=E5=9E=8Btree=E8=A7=86=E5=9B=BE?=
=?UTF-8?q?=EF=BC=9B4=E3=80=81=E6=B7=BB=E5=8A=A0=E5=88=80=E6=9F=84?=
=?UTF-8?q?=E5=92=8C=E5=85=B6=E4=BB=96=E7=89=A9=E6=96=99=E5=9C=A8=E7=82=B9?=
=?UTF-8?q?=E5=87=BB=E3=80=90=E6=9B=B4=E5=A4=9A=E3=80=91=E6=8C=89=E9=92=AE?=
=?UTF-8?q?=E5=90=8E=E9=80=89=E5=8F=96=E6=96=B0=E7=89=A9=E6=96=99=E7=82=B9?=
=?UTF-8?q?=E5=87=BB=E3=80=90=E7=A1=AE=E8=AE=A4=E3=80=91=E5=90=8E=E5=9B=9E?=
=?UTF-8?q?=E5=A1=AB=E7=89=A9=E6=96=99=E4=BF=A1=E6=81=AF=E5=88=B0=E7=BB=84?=
=?UTF-8?q?=E8=A3=85=E5=8D=95=E5=B9=B6=E7=BB=99=E5=87=BA=E6=8F=90=E7=A4=BA?=
=?UTF-8?q?=E4=BF=A1=E6=81=AF=EF=BC=9B5=E3=80=81=E6=B7=BB=E5=8A=A0?=
=?UTF-8?q?=E7=BB=84=E8=A3=85=E7=89=A9=E6=96=99=E4=BF=A1=E6=81=AF=E6=A0=A1?=
=?UTF-8?q?=E9=AA=8C=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=88=9D=E5=A7=8B=E5=8C=96?=
=?UTF-8?q?=E6=88=96=E9=87=8D=E6=96=B0=E9=80=89=E5=8F=96=E7=89=A9=E6=96=99?=
=?UTF-8?q?=E5=90=8E=E9=9C=80=E8=BF=9B=E8=A1=8C=E9=AA=8C=E8=AF=81=E6=89=8D?=
=?UTF-8?q?=E8=83=BD=E8=BF=9B=E8=A1=8C=E7=BB=84=E8=A3=85=EF=BC=9B6?=
=?UTF-8?q?=E3=80=81=E7=BB=84=E8=A3=85=E5=8D=95form=E8=A7=86=E5=9B=BE?=
=?UTF-8?q?=E5=B8=83=E5=B1=80=E8=B0=83=E6=95=B4=EF=BC=9B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_tool_management/controllers/controllers.py | 3 +-
sf_tool_management/models/base.py | 189 ++++++------------
sf_tool_management/models/stock.py | 106 +++++++++-
sf_tool_management/views/stock.xml | 127 ++++++++++++
4 files changed, 295 insertions(+), 130 deletions(-)
diff --git a/sf_tool_management/controllers/controllers.py b/sf_tool_management/controllers/controllers.py
index 4b0909dc..b3ca0224 100644
--- a/sf_tool_management/controllers/controllers.py
+++ b/sf_tool_management/controllers/controllers.py
@@ -122,7 +122,8 @@ class Manufacturing_Connect(http.Controller):
tool_assembly.write({
'after_assembly_tool_loading_length': float(data_list[1] or "0"), # 高度(总长度)
'after_assembly_functional_tool_diameter': float(data_list[2] or "0") * 2, # 直径
- 'after_assembly_knife_tip_r_angle': float(data_list[3] or "0") # R角
+ 'after_assembly_knife_tip_r_angle': float(data_list[3] or "0"), # R角
+ 'bool_preset_parameter': True
})
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
diff --git a/sf_tool_management/models/base.py b/sf_tool_management/models/base.py
index 518c4c0f..76fb20ff 100644
--- a/sf_tool_management/models/base.py
+++ b/sf_tool_management/models/base.py
@@ -358,6 +358,7 @@ class FunctionalToolAssembly(models.Model):
智能工厂组装单处扫码校验刀具物料
"""
for record in self:
+ tool_assembly_id = self.env['sf.functional.tool.assembly'].browse(self.ids)
lot_ids = self.env['stock.lot'].sudo().search([('rfid', '=', barcode)])
if lot_ids:
for lot_id in lot_ids:
@@ -365,26 +366,31 @@ class FunctionalToolAssembly(models.Model):
raise ValidationError(f'Rfid为【{barcode}】的刀柄已被占用,请重新扫描!!')
if lot_id.product_id == record.handle_product_id:
record.handle_code_id = lot_id.id
- record.handle_verify = True
+ tool_assembly_id.handle_code_id = lot_id.id
else:
- raise ValidationError('刀具选择错误,请重新确认!!!')
+ raise ValidationError('刀柄选择错误,请重新确认!!!')
else:
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', barcode)])
if location:
if location == record.integral_freight_barcode_id:
+ tool_assembly_id.integral_verify = True
record.integral_verify = True
elif location == record.blade_freight_barcode_id:
+ tool_assembly_id.blade_verify = True
record.blade_verify = True
elif location == record.bar_freight_barcode_id:
+ tool_assembly_id.bar_verify = True
record.bar_verify = True
elif location == record.pad_freight_barcode_id:
+ tool_assembly_id.pad_verify = True
record.pad_verify = True
elif location == record.chuck_freight_barcode_id:
+ tool_assembly_id.chuck_verify = True
record.chuck_verify = True
else:
raise ValidationError('刀具选择错误,请重新确认!')
else:
- raise ValidationError(f'扫描为【{barcode}】的刀具不存在,请重新扫描!')
+ raise ValidationError(f'扫描为【{barcode}】的货位不存在,请重新扫描!')
@api.depends('functional_tool_name')
def _compute_name(self):
@@ -472,12 +478,9 @@ class FunctionalToolAssembly(models.Model):
# 刀具物料信息
# ==============整体式刀具型号=============
- integral_freight_barcode_id = fields.Many2one('sf.shelf.location', string='整体式刀具货位',
+ integral_freight_barcode_id = fields.Many2one('sf.shelf.location', string='整体式刀具货位', readonly=True,
domain="[('product_id.cutting_tool_material_id.name', '=', '整体式刀具'),('product_num', '>', 0)]")
- integral_freight_lot_id = fields.Many2one('sf.shelf.location.lot', string='整体式刀具批次',
- domain="[('shelf_location_id', '=', integral_freight_barcode_id)]")
- integral_lot_id = fields.Many2one('stock.lot', string='整体式刀具批次', compute='_compute_integral_lot_id',
- store=True)
+ integral_lot_id = fields.Many2one('stock.lot', string='整体式刀具批次', readonly=True)
integral_product_id = fields.Many2one('product.product', string='整体式刀具名称',
compute='_compute_integral_product_id', store=True)
cutting_tool_integral_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='整体式刀具型号',
@@ -488,19 +491,6 @@ class FunctionalToolAssembly(models.Model):
related='integral_product_id.brand_id')
integral_verify = fields.Boolean('整体刀校验', default=False)
- @api.onchange('integral_freight_barcode_id')
- def _onchange_integral_freight_barcode_id(self):
- for item in self:
- item.integral_freight_lot_id = False
-
- @api.depends('integral_freight_lot_id', 'integral_freight_barcode_id')
- def _compute_integral_lot_id(self):
- for item in self:
- if item.integral_freight_lot_id:
- item.integral_lot_id = item.integral_freight_lot_id.lot_id.id
- elif not item.integral_freight_barcode_id:
- item.integral_lot_id = False
-
@api.depends('integral_lot_id')
def _compute_integral_product_id(self):
for item in self:
@@ -510,11 +500,9 @@ class FunctionalToolAssembly(models.Model):
item.integral_product_id = False
# =================刀片型号=============
- blade_freight_barcode_id = fields.Many2one('sf.shelf.location', string='刀片货位',
+ blade_freight_barcode_id = fields.Many2one('sf.shelf.location', string='刀片货位', readonly=True,
domain="[('product_id.cutting_tool_material_id.name', '=', '刀片'),('product_num', '>', 0)]")
- blade_freight_lot_id = fields.Many2one('sf.shelf.location.lot', string='刀片批次',
- domain="[('shelf_location_id', '=', blade_freight_barcode_id)]")
- blade_lot_id = fields.Many2one('stock.lot', string='刀片批次', compute='blade_freight_lot_id.lot_id', store=True)
+ blade_lot_id = fields.Many2one('stock.lot', string='刀片批次', readonly=True)
blade_product_id = fields.Many2one('product.product', string='刀片名称', compute='_compute_blade_product_id',
store=True)
cutting_tool_blade_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀片型号',
@@ -524,19 +512,6 @@ class FunctionalToolAssembly(models.Model):
sf_tool_brand_id_2 = fields.Many2one('sf.machine.brand', '刀片品牌', related='blade_product_id.brand_id')
blade_verify = fields.Boolean('刀片校验', default=False)
- @api.onchange('blade_freight_barcode_id')
- def _onchange_blade_freight_barcode_id(self):
- for item in self:
- item.blade_freight_lot_id = False
-
- @api.depends('blade_freight_lot_id', 'blade_freight_barcode_id')
- def _compute_blade_lot_id(self):
- for item in self:
- if item.blade_freight_lot_id:
- item.blade_lot_id = item.blade_freight_lot_id.lot_id.id
- elif not item.blade_freight_barcode_id:
- item.blade_lot_id = False
-
@api.depends('blade_lot_id')
def _compute_blade_product_id(self):
for item in self:
@@ -546,11 +521,9 @@ class FunctionalToolAssembly(models.Model):
item.blade_product_id = False
# ==============刀杆型号================
- bar_freight_barcode_id = fields.Many2one('sf.shelf.location', string='刀杆货位',
+ bar_freight_barcode_id = fields.Many2one('sf.shelf.location', string='刀杆货位', readonly=True,
domain="[('product_id.cutting_tool_material_id.name', '=', '刀杆'),('product_num', '>', 0)]")
- bar_freight_lot_id = fields.Many2one('sf.shelf.location.lot', string='刀杆批次',
- domain="[('shelf_location_id', '=', bar_freight_barcode_id)]")
- bar_lot_id = fields.Many2one('stock.lot', string='刀杆批次', compute='_compute_bar_lot_id', store=True)
+ bar_lot_id = fields.Many2one('stock.lot', string='刀杆批次', readonly=True)
bar_product_id = fields.Many2one('product.product', string='刀杆名称', compute='_compute_bar_product_id',
store=True)
cutting_tool_cutterbar_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀杆型号',
@@ -558,20 +531,7 @@ class FunctionalToolAssembly(models.Model):
bar_specification_id = fields.Many2one('sf.tool.materials.basic.parameters', string='刀杆规格',
related='bar_product_id.specification_id')
sf_tool_brand_id_3 = fields.Many2one('sf.machine.brand', '刀杆品牌', related='bar_product_id.brand_id')
- brand_verify = fields.Boolean('刀杆校验', default=False)
-
- @api.onchange('bar_freight_barcode_id')
- def _onchange_bar_freight_barcode_id(self):
- for item in self:
- item.bar_freight_lot_id = False
-
- @api.depends('bar_freight_lot_id', 'bar_freight_barcode_id')
- def _compute_bar_lot_id(self):
- for item in self:
- if item.bar_freight_lot_id:
- item.bar_lot_id = item.bar_freight_lot_id.lot_id.id
- elif not item.bar_freight_barcode_id:
- item.bar_lot_id = False
+ bar_verify = fields.Boolean('刀杆校验', default=False)
@api.depends('bar_lot_id')
def _compute_bar_product_id(self):
@@ -582,11 +542,9 @@ class FunctionalToolAssembly(models.Model):
item.bar_product_id = False
# =============刀盘型号================
- pad_freight_barcode_id = fields.Many2one('sf.shelf.location', string='刀盘货位',
+ pad_freight_barcode_id = fields.Many2one('sf.shelf.location', string='刀盘货位', readonly=True,
domain="[('product_id.cutting_tool_material_id.name', '=', '刀盘'),('product_num', '>', 0)]")
- pad_freight_lot_id = fields.Many2one('sf.shelf.location.lot', string='刀盘批次',
- domain="[('shelf_location_id', '=', pad_freight_barcode_id)]")
- pad_lot_id = fields.Many2one('stock.lot', string='刀盘批次', compute='_compute_pad_lot_id', store=True)
+ pad_lot_id = fields.Many2one('stock.lot', string='刀盘批次', readonly=True)
pad_product_id = fields.Many2one('product.product', string='刀盘名称', compute='_compute_pad_product_id',
store=True)
cutting_tool_cutterpad_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀盘型号',
@@ -596,19 +554,6 @@ class FunctionalToolAssembly(models.Model):
sf_tool_brand_id_4 = fields.Many2one('sf.machine.brand', '刀盘品牌', related='pad_product_id.brand_id')
pad_verify = fields.Boolean('刀盘校验', default=False)
- @api.onchange('pad_freight_barcode_id')
- def _onchange_pad_freight_barcode_id(self):
- for item in self:
- item.pad_freight_lot_id = False
-
- @api.depends('pad_freight_lot_id', 'pad_freight_barcode_id')
- def _compute_pad_lot_id(self):
- for item in self:
- if item.pad_freight_lot_id:
- item.pad_lot_id = item.pad_freight_lot_id.lot_id.id
- elif not item.pad_freight_barcode_id:
- item.pad_lot_id = False
-
@api.depends('pad_lot_id')
def _compute_pad_product_id(self):
for item in self:
@@ -618,12 +563,10 @@ class FunctionalToolAssembly(models.Model):
item.pad_product_id = False
# ==============刀柄型号==============
- handle_freight_rfid = fields.Char('刀柄Rfid', compute='_compute_handle_product_id', store=True)
- handle_code_id = fields.Many2one('stock.lot', '刀柄序列号',
- domain=[('product_id.cutting_tool_material_id.name', '=', '刀柄'),
- ('tool_material_status', '=', '可用')])
- handle_product_id = fields.Many2one('product.product', string='刀柄名称', compute='_compute_handle_product_id',
- store=True)
+ handle_code_id = fields.Many2one('stock.lot', '刀柄序列号', readonly=True,
+ domain="[('product_id', '=', handle_product_id)]")
+ handle_freight_rfid = fields.Char('刀柄Rfid', compute='_compute_handle_rfid', store=True)
+ handle_product_id = fields.Many2one('product.product', string='刀柄名称', readonly=True)
cutting_tool_cutterhandle_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀柄型号',
related='handle_product_id.cutting_tool_model_id')
handle_specification_id = fields.Many2one('sf.tool.materials.basic.parameters', string='刀柄规格',
@@ -632,23 +575,19 @@ class FunctionalToolAssembly(models.Model):
handle_verify = fields.Boolean('刀柄校验', default=False)
@api.depends('handle_code_id')
- def _compute_handle_product_id(self):
+ def _compute_handle_rfid(self):
for item in self:
if item.handle_code_id:
- item.handle_product_id = item.handle_code_id.product_id.id
item.handle_freight_rfid = item.handle_code_id.rfid
item.rfid = item.handle_freight_rfid
else:
- item.handle_product_id = False
item.handle_freight_rfid = False
item.rfid = False
# ==============夹头型号==============
- chuck_freight_barcode_id = fields.Many2one('sf.shelf.location', string='夹头货位',
+ chuck_freight_barcode_id = fields.Many2one('sf.shelf.location', string='夹头货位', readonly=True,
domain="[('product_id.cutting_tool_material_id.name', '=', '夹头'),('product_num', '>', 0)]")
- chuck_freight_lot_id = fields.Many2one('sf.shelf.location.lot', string='夹头批次',
- domain="[('shelf_location_id', '=', chuck_freight_barcode_id)]")
- chuck_lot_id = fields.Many2one('stock.lot', string='夹头批次', compute='_compute_chuck_lot_id', store=True)
+ chuck_lot_id = fields.Many2one('stock.lot', string='夹头批次', readonly=True)
chuck_product_id = fields.Many2one('product.product', string='夹头名称', compute='_compute_chuck_product_id',
store=True)
cutting_tool_cutterhead_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='夹头型号',
@@ -658,19 +597,6 @@ class FunctionalToolAssembly(models.Model):
sf_tool_brand_id_6 = fields.Many2one('sf.machine.brand', '夹头品牌', related='chuck_product_id.brand_id')
chuck_verify = fields.Boolean('夹头校验', default=False)
- @api.onchange('chuck_freight_barcode_id')
- def _onchange_chuck_freight_barcode_id(self):
- for item in self:
- item.chuck_freight_lot_id = False
-
- @api.depends('chuck_freight_lot_id', 'chuck_freight_barcode_id')
- def _compute_chuck_lot_id(self):
- for item in self:
- if item.chuck_freight_lot_id:
- item.chuck_lot_id = item.chuck_freight_lot_id.lot_id.id
- elif not item.chuck_freight_barcode_id:
- item.chuck_lot_id = False
-
@api.depends('chuck_lot_id')
def _compute_chuck_product_id(self):
for item in self:
@@ -841,7 +767,8 @@ class FunctionalToolAssembly(models.Model):
# 配置刀柄信息
for handle_id in bom.get('handle_ids'):
if handle_id:
- self.handle_product_id = handle_id.id
+ if not self.handle_product_id:
+ self.handle_product_id = handle_id.id
break
# 刀柄之外的物料配置
@@ -850,26 +777,26 @@ class FunctionalToolAssembly(models.Model):
integra_lot_id = self._get_old_tool_material_lot(bom.get('integral_ids'))
integra_location_lot_id = self._get_shelf_location_lot(integra_lot_id)
self.integral_freight_barcode_id = integra_location_lot_id.shelf_location_id.id
- self.integral_freight_lot_id = integra_location_lot_id.id
+ self.integral_lot_id = integra_location_lot_id.lot_id.id
else:
# 配置刀片
blade_lot_id = self._get_old_tool_material_lot(bom.get('blade_ids'))
blade_location_lot_id = self._get_shelf_location_lot(blade_lot_id)
self.blade_freight_barcode_id = blade_location_lot_id.shelf_location_id.id
- self.blade_freight_lot_id = blade_location_lot_id.id
+ self.blade_lot_id = blade_location_lot_id.lot_id.id
if options == '刀柄+刀杆+刀片':
# 配置刀杆
bar_lot_id = self._get_old_tool_material_lot(bom.get('bar_ids'))
bar_location_lot_id = self._get_shelf_location_lot(bar_lot_id)
self.bar_freight_barcode_id = bar_location_lot_id.shelf_location_id.id
- self.bar_freight_lot_id = bar_location_lot_id.id
+ self.bar_lot_id = bar_location_lot_id.lot_id.id
elif options == '刀柄+刀盘+刀片':
# 配置刀盘
pad_lot_id = self._get_old_tool_material_lot(bom.get('pad_ids'))
pad_location_lot_id = self._get_shelf_location_lot(pad_lot_id)
self.pad_freight_barcode_id = pad_location_lot_id.shelf_location_id.id
- self.pad_freight_lot_id = pad_location_lot_id.id
+ self.pad_lot_id = pad_location_lot_id.lot_id.id
def _get_old_tool_material_lot(self, material_ids):
""" 根据先进先出原则选择物料批次 """
@@ -896,7 +823,7 @@ class FunctionalToolAssembly(models.Model):
options = inventory_id.jikimo_bom_ids.options # BOM产品组装类型
if not product_ids or not options:
- raise ValidationError('功能刀具清单的BOM未进行配置,请先配置BOM再开始组装!')
+ raise ValidationError('功能刀具清单的BOM未进行配置,请先配置BOM信息!')
handle_ids = product_ids.filtered(lambda a: a.cutting_tool_material_id.name == '刀柄')
integral_ids = product_ids.filtered(lambda a: a.cutting_tool_material_id.name == '整体式刀具')
blade_ids = product_ids.filtered(lambda a: a.cutting_tool_material_id.name == '刀片')
@@ -934,39 +861,46 @@ class FunctionalToolAssembly(models.Model):
if tool_type == '刀柄':
tool_material_ids = tool_data.get('handle_ids')
+ tool_material_tree_id = self.env.ref('sf_tool_management.view_tool_product_tree')
elif tool_type == '整体式刀具':
tool_material_ids = self._get_all_material_location_lot(tool_data.get('integral_ids'))
+ tool_material_tree_id = self.env.ref('sf_tool_management.view_shelf_location_lot_tree_1')
elif tool_type == '刀片':
- tool_material_ids = self._get_all_material_location_lot(tool_data.get('handle_ids'))
+ tool_material_ids = self._get_all_material_location_lot(tool_data.get('blade_ids'))
+ tool_material_tree_id = self.env.ref('sf_tool_management.view_shelf_location_lot_tree_2')
elif tool_type == '刀杆':
tool_material_ids = self._get_all_material_location_lot(tool_data.get('bar_ids'))
+ tool_material_tree_id = self.env.ref('sf_tool_management.view_shelf_location_lot_tree_3')
else:
tool_material_ids = self._get_all_material_location_lot(tool_data.get('pad_ids'))
+ tool_material_tree_id = self.env.ref('sf_tool_management.view_shelf_location_lot_tree_4')
if tool_type == '刀柄':
return {
"type": "ir.actions.act_window",
"res_model": "product.product",
- "views": [[self.env.ref('sf_tool_management.view_tool_product_tree').id, "tree"],
+ "views": [[tool_material_tree_id.id, "tree"],
[self.env.ref('sf_tool_management.view_tool_product_search').id, "search"]],
"target": "new",
- "domain": [('id', 'in', tool_material_ids.ids)]
+ "domain": [('id', 'in', tool_material_ids.ids)],
+ "context": {'tool_id': self.id}
}
elif tool_type in ['整体式刀具', '刀片', '刀杆', '刀盘']:
return {
"type": "ir.actions.act_window",
"res_model": "sf.shelf.location.lot",
- "views": [[False, "tree"]],
+ "views": [[tool_material_tree_id.id, "tree"]],
"target": "new",
"domain": [('id', 'in', tool_material_ids.ids)],
- "context": {'create': False}
+ "context": {'tool_id': self.id}
}
def _get_all_material_location_lot(self, material_ids):
""" 获取所有满足条件 """
location_id = self.env['stock.location'].search([('name', '=', '刀具房')])
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 if material_ids else []),
+ ('quantity', '>', '0')])
lot_ids = []
for stock_quant_id in stock_quant_ids:
lot_ids.append(stock_quant_id.lot_id.id)
@@ -1033,22 +967,21 @@ class FunctionalToolAssembly(models.Model):
# 物料必填校验
if not self.handle_code_id:
raise ValidationError('缺少【刀柄】物料信息!')
- if not self.integral_product_id and not self.blade_product_id:
- raise ValidationError('【整体式刀具】和【刀片】必须填写一个!')
- if self.blade_product_id:
- if not self.bar_product_id and not self.pad_product_id:
- raise ValidationError('【刀盘】和【刀杆】必须填写一个!')
+ if self.integral_lot_id:
+ if not self.integral_verify:
+ raise ValidationError('【整体式刀具】未进行验证!')
+ elif self.blade_lot_id:
+ if not self.blade_verify:
+ raise ValidationError('【刀片】未进行验证!')
+ if self.bar_lot_id:
+ if not self.bar_verify:
+ raise ValidationError('【刀杆】未进行验证!')
+ elif self.pad_lot_id:
+ if not self.pad_verify:
+ raise ValidationError('【刀盘】未进行验证!')
# 组装参数必填校验
- if self.after_assembly_functional_tool_length == 0:
- raise ValidationError('组装参数信息【伸出长】不能为0!')
if self.after_assembly_max_lifetime_value == 0:
raise ValidationError('组装参数信息【最大寿命值】不能为0!')
- # if self.after_assembly_alarm_value == 0:
- # raise ValidationError('组装参数信息【报警值】不能为0!')
- # if self.after_assembly_effective_length == 0:
- # raise ValidationError('组装参数信息【有效长】不能为0!!!')
- # if self.hiding_length == 0:
- # raise ValidationError('组装参数信息【避空长】不能为0!!!')
if self.after_assembly_functional_tool_diameter <= 0:
raise ValidationError('组装参数信息【刀具直径】不能小于等于0!')
if self.after_assembly_tool_loading_length == 0:
@@ -1172,6 +1105,13 @@ class FunctionalToolAssembly(models.Model):
return functional_tool
return False
+ bool_preset_parameter = fields.Boolean('', default=False)
+
+ def get_tool_preset_parameter(self):
+ if not self.bool_preset_parameter:
+ raise ValidationError('没有获取到测量数据, 请确认刀具预调仪操作是否正确!')
+ return True
+
def assemble_single_print(self):
"""
todo 组装单打印
@@ -1563,6 +1503,7 @@ class FunctionalToolDismantle(models.Model):
assembly_id = self.env['sf.functional.tool.assembly'].sudo().create({
'functional_tool_name': self.functional_tool_id.name,
'handle_code_id': self.handle_lot_id.id,
+ 'handle_product_id': self.handle_product_id.id,
'loading_task_source': '3',
'reason_for_applying': '刀具寿命到期'
})
diff --git a/sf_tool_management/models/stock.py b/sf_tool_management/models/stock.py
index 50218e65..32719520 100644
--- a/sf_tool_management/models/stock.py
+++ b/sf_tool_management/models/stock.py
@@ -115,6 +115,13 @@ class StockPicking(models.Model):
num = "%03d" % m
return name + str(num)
+ def tool_location_num(self, freight_barcode_id, lot_id):
+ location_lot = self.env['sf.shelf.location.lot'].sudo().search([('lot_id', '=', lot_id.id), (
+ 'shelf_location_id', '=', freight_barcode_id.id)])
+ if not location_lot:
+ raise ValidationError(
+ f'[{freight_barcode_id.barcode}]货位的[{lot_id.name}]批次物料已用完,请重新选择!')
+
def create_tool_stocking_picking1(self, obj):
"""
创建刀具物料出库单
@@ -133,25 +140,32 @@ class StockPicking(models.Model):
stock_move_id = self.env['stock.move']
datas = {'data': [], 'picking_id': picking_id}
if obj.handle_code_id:
+ if obj.handle_code_id.tool_material_status == '在用':
+ raise ValidationError(f'Rfid为{obj.handle_code_id.rfid}的刀柄已被使用,请重新选择!')
# 修改刀柄序列号状态为【在用】
obj.handle_code_id.sudo().write({'tool_material_status': '在用'})
datas['data'].append(
{'current_location_id': self.env['sf.shelf.location'], 'lot_id': obj.handle_code_id})
if obj.integral_product_id:
+ self.tool_location_num(obj.integral_freight_barcode_id, obj.integral_lot_id)
datas['data'].append(
- {'current_location_id': obj.integral_freight_barcode_id, 'lot_id': obj.integral_freight_lot_id.lot_id})
+ {'current_location_id': obj.integral_freight_barcode_id, 'lot_id': obj.integral_lot_id})
if obj.blade_product_id:
+ self.tool_location_num(obj.blade_freight_barcode_id, obj.blade_lot_id)
datas['data'].append(
- {'current_location_id': obj.blade_freight_barcode_id, 'lot_id': obj.blade_freight_lot_id.lot_id})
+ {'current_location_id': obj.blade_freight_barcode_id, 'lot_id': obj.blade_lot_id})
if obj.bar_product_id:
+ self.tool_location_num(obj.bar_freight_barcode_id, obj.bar_lot_id)
datas['data'].append(
- {'current_location_id': obj.bar_freight_barcode_id, 'lot_id': obj.bar_freight_lot_id.lot_id})
+ {'current_location_id': obj.bar_freight_barcode_id, 'lot_id': obj.bar_lot_id})
if obj.pad_product_id:
+ self.tool_location_num(obj.pad_freight_barcode_id, obj.pad_lot_id)
datas['data'].append(
- {'current_location_id': obj.pad_freight_barcode_id, 'lot_id': obj.pad_freight_lot_id.lot_id})
+ {'current_location_id': obj.pad_freight_barcode_id, 'lot_id': obj.pad_lot_id})
if obj.chuck_product_id:
+ self.tool_location_num(obj.chuck_freight_barcode_id, obj.chuck_lot_id)
datas['data'].append(
- {'current_location_id': obj.chuck_freight_barcode_id, 'lot_id': obj.chuck_freight_lot_id.lot_id})
+ {'current_location_id': obj.chuck_freight_barcode_id, 'lot_id': obj.chuck_lot_id})
# 创建刀具物料出库库存移动记录
stock_move_id.create_tool_material_stock_moves(datas)
# 将刀具物料出库库单的状态更改为就绪
@@ -187,6 +201,9 @@ class StockMove(models.Model):
stock_move_ids = []
for res in data:
if res:
+ if res['lot_id'].product_qty <= 0:
+ raise ValidationError(
+ f'[{res["lot_id"].product_id.name}产品的{res["lot_id"].name}]批次/序列号库存不足!')
# 创建库存移动记录
stock_move_id = self.env['stock.move'].sudo().create({
'name': picking_id.name,
@@ -248,8 +265,87 @@ class ProductProduct(models.Model):
num = "%03d" % m
return '%s-%s' % (code, num)
+ def set_tool_material(self):
+ tool_id = self.env.context.get('tool_id')
+ tool_assembly_id = self.env['sf.functional.tool.assembly'].sudo().search([('id', '=', tool_id)])
+ if len(self) > 1:
+ raise ValidationError('请不要多选')
+ else:
+ tool_assembly_id.handle_product_id = self.id
+ tool_assembly_id.handle_code_id = False
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'params': {
+ 'message': '刀柄信息更改成功',
+ 'type': 'success',
+ 'next': {'type': 'ir.actions.act_window_close'}
+ }
+ }
+
class SfShelfLocationLot(models.Model):
_inherit = 'sf.shelf.location.lot'
+ product_id = fields.Many2one('product.product', '产品', compute='_compute_product_id', store=True)
+ cutting_tool_type = fields.Char(string="刀具物料类型", compute='_compute_product_id', store=True)
+ cutting_tool_type_id = fields.Many2one('sf.cutting.tool.type', string='类型',
+ related='product_id.cutting_tool_type_id')
+ cutting_tool_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='型号名称',
+ related='product_id.cutting_tool_model_id')
+ specification_id = fields.Many2one('sf.tool.materials.basic.parameters', string='物料号',
+ related='product_id.specification_id')
+ brand_id = fields.Many2one('sf.machine.brand', '品牌', related='product_id.brand_id')
+ cutting_tool_blade_diameter = fields.Float('刃部直径(mm)', related='product_id.cutting_tool_blade_diameter')
+ cutting_tool_blade_tip_working_size = fields.Char('刀尖R角(mm)',
+ related='product_id.cutting_tool_blade_tip_working_size')
+ cutting_tool_blade_radius = fields.Char('刀尖圆弧半径(mm)',
+ related='product_id.cutting_tool_blade_tip_circular_arc_radius')
+ cutting_tool_cutter_arbor_diameter = fields.Float('刀杆直径(mm)',
+ related='product_id.cutting_tool_cutter_arbor_diameter')
+ cutting_tool_cutter_head_diameter = fields.Float('刀盘直径(mm)',
+ related='product_id.cutting_tool_cutter_head_diameter')
+
+ fit_blade_shape_id = fields.Many2one('maintenance.equipment.image', '适配刀片形状',
+ related='product_id.fit_blade_shape_id')
+
+ @api.depends('lot_id')
+ def _compute_product_id(self):
+ for item in self:
+ if item.lot_id:
+ item.product_id = item.lot_id.product_id.id
+ item.cutting_tool_type = item.lot_id.product_id.cutting_tool_type
+
+ def set_tool_material(self):
+ tool_type = self.env.context.get('tool_type')
+ tool_id = self.env.context.get('tool_id')
+ tool_assembly_id = self.env['sf.functional.tool.assembly'].sudo().search([('id', '=', tool_id)])
+ if len(self) > 1:
+ raise ValidationError('请不要多选')
+ if tool_type == '整体式刀具':
+ tool_assembly_id.integral_freight_barcode_id = self.shelf_location_id.id
+ tool_assembly_id.integral_lot_id = self.lot_id.id
+ tool_assembly_id.integral_verify = False
+ elif tool_type == '刀片':
+ tool_assembly_id.blade_freight_barcode_id = self.shelf_location_id.id
+ tool_assembly_id.blade_lot_id = self.lot_id.id
+ tool_assembly_id.blade_verify = False
+ elif tool_type == '刀杆':
+ tool_assembly_id.bar_freight_barcode_id = self.shelf_location_id.id
+ tool_assembly_id.bar_lot_id = self.lot_id.id
+ tool_assembly_id.bar_verify = False
+ elif tool_type == '刀盘':
+ tool_assembly_id.pad_freight_barcode_id = self.shelf_location_id.id
+ tool_assembly_id.pad_lot_id = self.lot_id.id
+ tool_assembly_id.pad_verify = False
+
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'params': {
+ 'message': f'[{tool_type}]物料信息更改成功',
+ 'type': 'success',
+ 'next': {'type': 'ir.actions.act_window_close'}
+ }
+ }
diff --git a/sf_tool_management/views/stock.xml b/sf_tool_management/views/stock.xml
index 81783631..c0942282 100644
--- a/sf_tool_management/views/stock.xml
+++ b/sf_tool_management/views/stock.xml
@@ -11,4 +11,131 @@
+
+
+ 刀柄
+ product.product
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ product.product
+
+
+
+
+
+
+
+
+ sf.shelf.location.lot.tree
+ sf.shelf.location.lot
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sf.shelf.location.lot.tree
+ sf.shelf.location.lot
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sf.shelf.location.lot.tree
+ sf.shelf.location.lot
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sf.shelf.location.lot.tree
+ sf.shelf.location.lot
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From 43a49242c5c37845a96c0c33b5f456e3d462d861 Mon Sep 17 00:00:00 2001
From: yuxianghui <3437689193@qq.com>
Date: Thu, 12 Sep 2024 17:21:37 +0800
Subject: [PATCH 24/40] =?UTF-8?q?1=E3=80=81=E7=BB=84=E8=A3=85=E5=8D=95?=
=?UTF-8?q?=E5=B8=83=E5=B1=80=E8=B0=83=E6=95=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_tool_management/views/tool_base_views.xml | 180 +++++++++----------
1 file changed, 87 insertions(+), 93 deletions(-)
diff --git a/sf_tool_management/views/tool_base_views.xml b/sf_tool_management/views/tool_base_views.xml
index 0c492721..ab4912ce 100644
--- a/sf_tool_management/views/tool_base_views.xml
+++ b/sf_tool_management/views/tool_base_views.xml
@@ -479,6 +479,10 @@
+
@@ -524,11 +528,9 @@
-
+ options="{'no_create': True, 'no_quick_create': True}"/>
@@ -537,53 +539,8 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ class="btn-primary" context="{'tool_type': '刀柄'}"
+ attrs="{'invisible': [('assemble_status', 'not in', ['0','01'])]}"/>
@@ -594,65 +551,102 @@