From cf8c14e7380eaec81b7800c58242ec8e5ffaa46b Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Wed, 9 Apr 2025 15:22:41 +0800
Subject: [PATCH 01/22] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=9F=A5=E8=AF=A2?=
=?UTF-8?q?=E8=8C=83=E5=9B=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
quality_control/views/quality_views.xml | 2 +-
sf_machine_connect/controllers/controllers.py | 25 +++++++++++++------
2 files changed, 19 insertions(+), 8 deletions(-)
diff --git a/quality_control/views/quality_views.xml b/quality_control/views/quality_views.xml
index 38de9e4e..528003e3 100644
--- a/quality_control/views/quality_views.xml
+++ b/quality_control/views/quality_views.xml
@@ -267,7 +267,7 @@
-
+
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index 4385937c..0017e2ae 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -567,7 +567,7 @@ class Sf_Dashboard_Connect(http.Controller):
"""
res = {'status': 1, 'message': '成功', 'data': {}}
# 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'])
begin_time_str = kw['begin_time'].strip('"')
end_time_str = kw['end_time'].strip('"')
@@ -617,11 +617,19 @@ class Sf_Dashboard_Connect(http.Controller):
for time_interval in time_intervals:
start_time, end_time = time_interval
- orders = plan_obj.search([
- ('production_id.production_line_id.name', '=', line),
+ # orders = plan_obj.search([
+ # ('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']),
(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'))
])
# 使用小时和分钟作为键,确保每个小时的数据有独立的键
@@ -638,18 +646,21 @@ class Sf_Dashboard_Connect(http.Controller):
for date in date_list:
next_day = date + timedelta(days=1)
- orders = plan_obj.search([('production_id.production_line_id.name', '=', line), ('state', 'in', ['done']),
+ orders = request.env['mrp.workorder'].sudo().search([('production_id.production_line_id.name', '=', line), ('state', 'in', ['done']),
+ ('routing_type', '=', 'CNC加工'),
(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 = plan_obj.search(
+ rework_orders = request.env['mrp.workorder'].sudo().search(
[('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, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
])
- not_passed_orders = plan_obj.search(
+ not_passed_orders = request.env['mrp.workorder'].sudo().search(
[('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, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
])
From ddb0c304b9957630f3aa486ae85dc71bc864fffd Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Wed, 9 Apr 2025 15:57:13 +0800
Subject: [PATCH 02/22] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=AD=97=E6=AE=B5?=
=?UTF-8?q?=E8=B7=AF=E5=BE=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_machine_connect/controllers/controllers.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index 0017e2ae..9fb69303 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -414,7 +414,7 @@ class Sf_Dashboard_Connect(http.Controller):
# 工单计划量切换为CNC工单
plan_data_total_counts = work_order_obj.search_count(
- [('production_id.production_line_id.name', '=', line),
+ [('production_line_id.name', '=', line), ('id', '!=', 8061),
('state', 'in', ['ready', 'progress', 'done']), ('routing_type', '=', 'CNC加工')])
# # 工单完成量
@@ -423,13 +423,13 @@ class Sf_Dashboard_Connect(http.Controller):
# 工单完成量切换为CNC工单
plan_data_finish_counts = work_order_obj.search_count(
- [('production_id.production_line_id.name', '=', line),
+ [('production_line_id.name', '=', line),
('state', 'in', ['done']), ('routing_type', '=', 'CNC加工')])
# 超期完成量
# 搜索所有已经完成的工单
plan_data_overtime = work_order_obj.search([
- ('production_id.production_line_id.name', '=', line),
+ ('production_line_id.name', '=', line),
('state', 'in', ['done']),
('routing_type', '=', 'CNC加工')
])
From 6c926bf08103a85af45fafab6d7203a4040ac7e6 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Wed, 9 Apr 2025 16:39:21 +0800
Subject: [PATCH 03/22] =?UTF-8?q?=E8=B0=83=E6=95=B4=E8=B4=A8=E6=A3=80?=
=?UTF-8?q?=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 | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index 9fb69303..ad958485 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -448,9 +448,14 @@ class Sf_Dashboard_Connect(http.Controller):
])
# 过滤出那些检测结果状态为 '返工' 或 '报废' 的记录
- faulty_plans = plan_data.filtered(lambda p: any(
- result.test_results in ['返工', '报废'] for result in p.production_id.detection_result_ids
- ))
+ # faulty_plans = plan_data.filtered(lambda p: any(
+ # 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(
From bdf4696c089fa49db3f30a9538bfb67e363defa7 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Wed, 9 Apr 2025 16:56:53 +0800
Subject: [PATCH 04/22] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=BE=85=E5=AE=8C?=
=?UTF-8?q?=E6=88=90=E5=B7=A5=E5=8D=95=E6=98=8E=E7=BB=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_machine_connect/controllers/controllers.py | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index ad958485..dcfa3ea2 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -767,11 +767,14 @@ 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_id.state', 'not in', ['cancel', 'done']), ('active', '=', True)
+ # not_done_orders = plan_obj.search(
+ # [('production_line_id.name', '=', line), ('state', 'not in', ['finished']),
+ # ('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小时前的时间
@@ -823,16 +826,18 @@ class Sf_Dashboard_Connect(http.Controller):
'draft': '待排程',
'done': '已排程',
'processing': '生产中',
- 'finished': '已完成'
+ 'finished': '已完成',
+ 'ready': '待加工',
+ 'progress': '生产中',
}
line_dict = {
'sequence': id_to_sequence[order.id],
- 'workorder_name': order.name,
+ 'workorder_name': order.production_id.name,
'blank_name': blank_name,
'material': material,
'dimensions': dimensions,
- 'order_qty': order.product_qty,
+ 'order_qty': 1,
'state': state_dict[order.state],
}
From 87786dbd8000cd3e14edb7e1f7f145aad3707a25 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Fri, 11 Apr 2025 14:16:18 +0800
Subject: [PATCH 05/22] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=95=85=E9=9A=9C?=
=?UTF-8?q?=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 | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index dcfa3ea2..19d0f17e 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -926,7 +926,10 @@ class Sf_Dashboard_Connect(http.Controller):
for row in result2:
alarm_count.append(row[1])
if row[0]:
- total_alarm_time += abs(float(row[0]))
+ if float(row[0]) >= 28800:
+ continue
+ # total_alarm_time += abs(float(row[0]))
+ total_alarm_time += float(row[0])
else:
total_alarm_time += 0.0
if len(list(set(alarm_count))) == 1:
@@ -1353,7 +1356,7 @@ class Sf_Dashboard_Connect(http.Controller):
for result in results:
alarm_last_24_nums.append(result[1])
if result[0]:
- if float(result[0]) >= 1000:
+ if float(result[0]) >= 28800:
continue
alarm_last_24_time += float(result[0])
else:
@@ -1371,7 +1374,7 @@ class Sf_Dashboard_Connect(http.Controller):
for result in results:
alarm_all_nums.append(result[1])
if result[0]:
- if float(result[0]) >= 1000:
+ if float(result[0]) >= 28800:
continue
alarm_all_time += float(result[0])
else:
From 026697f363bbd5892b4eb830f3627bba1bf3a052 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?=
Date: Fri, 11 Apr 2025 16:12:58 +0800
Subject: [PATCH 06/22] =?UTF-8?q?=E4=BF=AE=E6=94=B9sf=E5=AE=9A=E6=97=B6?=
=?UTF-8?q?=E4=BB=BB=E5=8A=A1=E4=B8=8D=E5=9C=A8=E9=83=A8=E7=BD=B2=E6=97=B6?=
=?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=85=8D=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../models/jikimo_workorder_exception.py | 4 ++--
sf_mrs_connect/data/ir_cron_data.xml | 23 ++++++++++---------
2 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/jikimo_workorder_exception_notify/models/jikimo_workorder_exception.py b/jikimo_workorder_exception_notify/models/jikimo_workorder_exception.py
index 0ab0d6ae..f4a49c1f 100644
--- a/jikimo_workorder_exception_notify/models/jikimo_workorder_exception.py
+++ b/jikimo_workorder_exception_notify/models/jikimo_workorder_exception.py
@@ -52,10 +52,10 @@ class JikimoWorkorderException(models.Model):
def _get_message(self, message_queue_ids):
contents, _ = super(JikimoWorkorderException, self)._get_message(message_queue_ids)
- url = self.env['ir.config_parameter'].get_param('web.base.url')
+ base_url = self.env['ir.config_parameter'].get_param('web.base.url')
action_id = self.env.ref('mrp.mrp_production_action').id
for index, content in enumerate(contents):
exception_id = self.env['jikimo.workorder.exception'].browse(message_queue_ids[index].res_id)
- url = url + '/web#id=%s&view_type=form&action=%s' % (exception_id.workorder_id.production_id.id, action_id)
+ url = base_url + '/web#id=%s&view_type=form&action=%s' % (exception_id.workorder_id.production_id.id, action_id)
contents[index] = content.replace('{{url}}', url)
return contents, message_queue_ids
diff --git a/sf_mrs_connect/data/ir_cron_data.xml b/sf_mrs_connect/data/ir_cron_data.xml
index 7cc18c8e..a2d43891 100644
--- a/sf_mrs_connect/data/ir_cron_data.xml
+++ b/sf_mrs_connect/data/ir_cron_data.xml
@@ -1,16 +1,16 @@
-
-
- 制造-配置:每日定时同步cloud的静态资源库
-
- code
- model.sf_all_sync()
- 1
- days
- -1
-
-
+
+
+ 制造-配置:每日定时同步cloud的静态资源库
+
+ code
+ model.sf_all_sync()
+ 1
+ days
+ -1
+
+
@@ -220,4 +220,5 @@
+
\ No newline at end of file
From a8961086389c4b433bac551d9896cf6d4811bca0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?=
Date: Fri, 11 Apr 2025 16:49:18 +0800
Subject: [PATCH 07/22] =?UTF-8?q?=E4=BA=A7=E5=93=81=E4=B8=BB=E6=95=B0?=
=?UTF-8?q?=E6=8D=AE=E3=80=90=E6=9D=A1=E7=A0=81=E3=80=91=E5=AD=98=E5=82=A8?=
=?UTF-8?q?=E6=A8=A1=E5=9E=8BID?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_manufacturing/__manifest__.py | 1 +
sf_manufacturing/views/product_template_views.xml | 15 +++++++++++++++
2 files changed, 16 insertions(+)
create mode 100644 sf_manufacturing/views/product_template_views.xml
diff --git a/sf_manufacturing/__manifest__.py b/sf_manufacturing/__manifest__.py
index ba0c6751..a19f5542 100644
--- a/sf_manufacturing/__manifest__.py
+++ b/sf_manufacturing/__manifest__.py
@@ -44,6 +44,7 @@
'views/sale_order_views.xml',
'views/mrp_workorder_batch_replan.xml',
'views/purchase_order_view.xml',
+ 'views/product_template_views.xml',
],
'assets': {
diff --git a/sf_manufacturing/views/product_template_views.xml b/sf_manufacturing/views/product_template_views.xml
new file mode 100644
index 00000000..a99a332a
--- /dev/null
+++ b/sf_manufacturing/views/product_template_views.xml
@@ -0,0 +1,15 @@
+
+
+
+
+ product.template.product.form.inherit.sf_manufacture
+ product.template
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From 91d79008e107ea88b68821951be1a76a12c8400b Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Mon, 14 Apr 2025 11:21:24 +0800
Subject: [PATCH 08/22] =?UTF-8?q?=E6=95=85=E9=9A=9C=E6=97=B6=E9=95=BF?=
=?UTF-8?q?=E6=8F=90=E5=8F=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_machine_connect/controllers/controllers.py | 58 ++++++++++++++++---
.../data/insepection_report_template.xml | 4 ++
2 files changed, 54 insertions(+), 8 deletions(-)
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index 19d0f17e..d5c8c7cb 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -18,6 +18,11 @@ db_config = {
"host": "172.16.10.131"
}
+# 基础数据
+TotalAlarmTime = 0
+TodayAlarmTime = 0
+MonthAlarmTime = 0
+
def convert_to_seconds(time_str):
# 修改正则表达式,使 H、M、S 部分可选
@@ -455,7 +460,7 @@ class Sf_Dashboard_Connect(http.Controller):
faulty_plans = request.env['quality.check'].sudo().search([
('operation_id.name', '=', 'CNC加工'),
('quality_state', 'in', ['fail'])
- ])
+ ])
# 查找制造订单取消与归档的数量
cancel_order_count = production_obj.search_count(
@@ -651,11 +656,12 @@ class Sf_Dashboard_Connect(http.Controller):
for date in date_list:
next_day = date + timedelta(days=1)
- orders = request.env['mrp.workorder'].sudo().search([('production_id.production_line_id.name', '=', line), ('state', 'in', ['done']),
- ('routing_type', '=', 'CNC加工'),
- (date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
- (date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
- ])
+ orders = request.env['mrp.workorder'].sudo().search(
+ [('production_id.production_line_id.name', '=', line), ('state', 'in', ['done']),
+ ('routing_type', '=', 'CNC加工'),
+ (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(
[('production_id.production_line_id.name', '=', line), ('state', 'in', ['rework']),
@@ -892,6 +898,8 @@ class Sf_Dashboard_Connect(http.Controller):
# 获取请求的机床数据
machine_list = ast.literal_eval(kw['machine_list'])
total_alarm_time = 0
+ today_alarm_time = 0
+ month_alarm_time = 0
alarm_count_num = 0
for item in machine_list:
sql = '''
@@ -904,6 +912,11 @@ class Sf_Dashboard_Connect(http.Controller):
) subquery;
'''
+ # 计算时间范围
+ 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)
+
sql2 = '''
SELECT DISTINCT ON (alarm_start_time) alarm_time, alarm_start_time
FROM device_data
@@ -918,8 +931,16 @@ class Sf_Dashboard_Connect(http.Controller):
cur.execute(sql2, (item,))
result2 = cur.fetchall()
- # print('result2========', result2)
- #
+ today_data = []
+ month_data = []
+
+ for record in result2:
+ 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 row in result:
res['data'][item] = {'idle_count': row[0]}
alarm_count = []
@@ -939,7 +960,28 @@ class Sf_Dashboard_Connect(http.Controller):
alarm_count_num = 1
else:
alarm_count_num = len(list(set(alarm_count)))
+
+ 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
+
+ TotalAlarmTime = total_alarm_time / 3600
+ TodayAlarmTime = today_alarm_time / 3600
+ MonthAlarmTime = month_alarm_time / 3600
+ logging.info('=====AlarmTime===== %s, %s, %s' % (TotalAlarmTime, TodayAlarmTime, MonthAlarmTime))
res['data'][item]['total_alarm_time'] = total_alarm_time / 3600
+ logging.info('=======================')
res['data'][item]['alarm_count_num'] = alarm_count_num
# 返回统计结果
diff --git a/sf_quality/data/insepection_report_template.xml b/sf_quality/data/insepection_report_template.xml
index d989137c..14a42065 100644
--- a/sf_quality/data/insepection_report_template.xml
+++ b/sf_quality/data/insepection_report_template.xml
@@ -78,6 +78,10 @@
公司邮箱:
+
+
+ 第1 页/共 1页
+
From c93553e78ec56a76da3c9bc6448fbbf46c4f45a8 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Mon, 14 Apr 2025 16:42:33 +0800
Subject: [PATCH 09/22] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=A8=BC=E5=8A=A8?=
=?UTF-8?q?=E7=8E=87=E6=8E=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_machine_connect/controllers/controllers.py | 241 +++++++++++++++---
1 file changed, 204 insertions(+), 37 deletions(-)
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index d5c8c7cb..00ba6223 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -5,7 +5,7 @@ import json
import base64
import logging
import psycopg2
-from datetime import datetime, timedelta
+from datetime import datetime, timedelta, timezone
from odoo import http, fields
from odoo.http import request
@@ -898,8 +898,6 @@ class Sf_Dashboard_Connect(http.Controller):
# 获取请求的机床数据
machine_list = ast.literal_eval(kw['machine_list'])
total_alarm_time = 0
- today_alarm_time = 0
- month_alarm_time = 0
alarm_count_num = 0
for item in machine_list:
sql = '''
@@ -912,11 +910,6 @@ class Sf_Dashboard_Connect(http.Controller):
) subquery;
'''
- # 计算时间范围
- 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)
-
sql2 = '''
SELECT DISTINCT ON (alarm_start_time) alarm_time, alarm_start_time
FROM device_data
@@ -931,15 +924,6 @@ class Sf_Dashboard_Connect(http.Controller):
cur.execute(sql2, (item,))
result2 = cur.fetchall()
- today_data = []
- month_data = []
-
- for record in result2:
- 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 row in result:
res['data'][item] = {'idle_count': row[0]}
@@ -961,27 +945,7 @@ class Sf_Dashboard_Connect(http.Controller):
else:
alarm_count_num = len(list(set(alarm_count)))
- 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
-
- TotalAlarmTime = total_alarm_time / 3600
- TodayAlarmTime = today_alarm_time / 3600
- MonthAlarmTime = month_alarm_time / 3600
- logging.info('=====AlarmTime===== %s, %s, %s' % (TotalAlarmTime, TodayAlarmTime, MonthAlarmTime))
res['data'][item]['total_alarm_time'] = total_alarm_time / 3600
- logging.info('=======================')
res['data'][item]['alarm_count_num'] = alarm_count_num
# 返回统计结果
@@ -1451,3 +1415,206 @@ class Sf_Dashboard_Connect(http.Controller):
conn.close()
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 = total_power_on_time - convert_to_seconds(results[1][0])
+ else:
+ month_power_on_time = 0
+ if len(results) >= 3:
+ today_power_on_time = total_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('报警时间=============', TotalAlarmTime, MonthAlarmTime, TodayAlarmTime)
+ logging.info("报警时间=%s" % TotalAlarmTime)
+ logging.info("月报警时间=%s" % MonthAlarmTime)
+ logging.info("日报警时间=%s" % TodayAlarmTime)
+
+ # 计算时间开动率(累计、月、日)
+ if total_power_on_time and TotalAlarmTime:
+ total_power_on_rate = (total_power_on_time - TotalAlarmTime) / total_power_on_time
+ else:
+ total_power_on_rate = 0
+ if month_power_on_time and MonthAlarmTime:
+ month_power_on_rate = (month_power_on_time - MonthAlarmTime) / month_power_on_time
+ else:
+ month_power_on_rate = 0
+ if today_power_on_time and TodayAlarmTime:
+ today_power_on_rate = (today_power_on_time - TodayAlarmTime) / 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)
+
+ # 计算性能开动率(累计、月、日)
+ total_performance_rate = len(orders) * 30 * 60 / (total_power_on_time - TotalAlarmTime)
+ month_performance_rate = len(month_data) * 30 * 60 / (month_power_on_time - MonthAlarmTime)
+ today_performance_rate = len(today_data) * 30 * 60 / (today_power_on_time - TodayAlarmTime) 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)
+
+
+
+
+
From 07336326ce223e206c0c6a04f895c8719571cb4f Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Mon, 14 Apr 2025 17:16:38 +0800
Subject: [PATCH 10/22] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=A8=BC=E5=8A=A8?=
=?UTF-8?q?=E7=8E=87=E6=8E=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_machine_connect/controllers/controllers.py | 41 ++++++++++---------
1 file changed, 21 insertions(+), 20 deletions(-)
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index 00ba6223..7bed4d3f 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -1508,18 +1508,18 @@ class Sf_Dashboard_Connect(http.Controller):
results = cur.fetchall()
print(results)
if len(results) >= 1:
- total_power_on_time = convert_to_seconds(results[0][0])
+ total_power_on_time += convert_to_seconds(results[0][0])
else:
- total_power_on_time = 0
+ total_power_on_time += 0
if len(results) >= 2:
- month_power_on_time = total_power_on_time - convert_to_seconds(results[1][0])
+ month_power_on_time += convert_to_seconds(results[1][0])
else:
- month_power_on_time = 0
+ month_power_on_time += 0
if len(results) >= 3:
- today_power_on_time = total_power_on_time - convert_to_seconds(results[2][0])
+ 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
+ today_power_on_time += 0
print(total_power_on_time, month_power_on_time, today_power_on_time)
with conn.cursor() as cur:
@@ -1562,22 +1562,21 @@ class Sf_Dashboard_Connect(http.Controller):
conn.close()
- print('报警时间=============', TotalAlarmTime, MonthAlarmTime, TodayAlarmTime)
- logging.info("报警时间=%s" % TotalAlarmTime)
- logging.info("月报警时间=%s" % MonthAlarmTime)
- logging.info("日报警时间=%s" % TodayAlarmTime)
-
+ 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 and TotalAlarmTime:
- total_power_on_rate = (total_power_on_time - TotalAlarmTime) / total_power_on_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 and MonthAlarmTime:
- month_power_on_rate = (month_power_on_time - MonthAlarmTime) / month_power_on_time
+ 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 and TodayAlarmTime:
- today_power_on_rate = (today_power_on_time - TodayAlarmTime) / today_power_on_time
+ 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)
@@ -1585,9 +1584,11 @@ class Sf_Dashboard_Connect(http.Controller):
print("日开动率: %s" % today_power_on_rate)
# 计算性能开动率(累计、月、日)
- total_performance_rate = len(orders) * 30 * 60 / (total_power_on_time - TotalAlarmTime)
- month_performance_rate = len(month_data) * 30 * 60 / (month_power_on_time - MonthAlarmTime)
- today_performance_rate = len(today_data) * 30 * 60 / (today_power_on_time - TodayAlarmTime) if today_power_on_time != 0 else 0
+ 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)
From 197ae6bc010a9c5d0c11666400eb6dc8a5ba8134 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Mon, 14 Apr 2025 17:22:27 +0800
Subject: [PATCH 11/22] =?UTF-8?q?=E5=8E=BB=E6=8E=89=E5=9F=BA=E7=A1=80?=
=?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 | 5 -----
1 file changed, 5 deletions(-)
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index 7bed4d3f..13394ddb 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -18,11 +18,6 @@ db_config = {
"host": "172.16.10.131"
}
-# 基础数据
-TotalAlarmTime = 0
-TodayAlarmTime = 0
-MonthAlarmTime = 0
-
def convert_to_seconds(time_str):
# 修改正则表达式,使 H、M、S 部分可选
From ba88070faddaff2f68cb609940104a6273242282 Mon Sep 17 00:00:00 2001
From: hyyy <123@qq.com>
Date: Tue, 15 Apr 2025 09:31:42 +0800
Subject: [PATCH 12/22] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=88=80=E7=89=87?=
=?UTF-8?q?=E5=88=80=E6=9D=86=E6=A0=B7=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_tool_management/static/src/change.scss | 12 +++++++++---
sf_tool_management/views/tool_base_views.xml | 18 +++++++++---------
2 files changed, 18 insertions(+), 12 deletions(-)
diff --git a/sf_tool_management/static/src/change.scss b/sf_tool_management/static/src/change.scss
index 20298bdf..9cf26b0a 100644
--- a/sf_tool_management/static/src/change.scss
+++ b/sf_tool_management/static/src/change.scss
@@ -22,15 +22,21 @@
color: #999;
}
}
+
+}
+.custom_group:has(.text-success){
+ position: relative;
&::after{
content: '';
display: block;
- width: 18px;
- height: 18px;
+ 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;
diff --git a/sf_tool_management/views/tool_base_views.xml b/sf_tool_management/views/tool_base_views.xml
index be0b5a3f..0ca78b31 100644
--- a/sf_tool_management/views/tool_base_views.xml
+++ b/sf_tool_management/views/tool_base_views.xml
@@ -531,7 +531,7 @@
-
+
@@ -554,7 +554,7 @@
-
+
@@ -582,8 +582,8 @@
-
-
+
+
@@ -607,8 +607,8 @@
-
-
+
+
@@ -631,8 +631,8 @@
-
-
+
+
@@ -911,7 +911,7 @@
-
+
From 33a5fc0ff420f20409af4aa84423875697779fe2 Mon Sep 17 00:00:00 2001
From: hyyy <123@qq.com>
Date: Tue, 15 Apr 2025 11:24:45 +0800
Subject: [PATCH 13/22] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99?=
=?UTF-8?q?=E5=AD=97=E6=AE=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_tool_management/views/tool_base_views.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sf_tool_management/views/tool_base_views.xml b/sf_tool_management/views/tool_base_views.xml
index 0ca78b31..fd05b4ef 100644
--- a/sf_tool_management/views/tool_base_views.xml
+++ b/sf_tool_management/views/tool_base_views.xml
@@ -911,7 +911,7 @@
-
+
From d672f3f4d7495238c9eeded5eb951205511e0408 Mon Sep 17 00:00:00 2001
From: yuxianghui <3437689193@qq.com>
Date: Tue, 15 Apr 2025 15:05:23 +0800
Subject: [PATCH 14/22] =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=88=80=E5=85=B7?=
=?UTF-8?q?=E6=8B=86=E8=A7=A3=E5=8D=95=E6=B7=BB=E5=8A=A0=E5=8F=AF=E4=BB=A5?=
=?UTF-8?q?=E9=80=89=E6=8B=A9=E7=BA=BF=E8=BE=B9=E5=88=80=E5=BA=93=E7=9A=84?=
=?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=88=80=E5=85=B7=E8=BF=9B=E8=A1=8C=E6=8B=86?=
=?UTF-8?q?=E8=A7=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_tool_management/models/base.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sf_tool_management/models/base.py b/sf_tool_management/models/base.py
index 3d9ec5ee..3cbaf974 100644
--- a/sf_tool_management/models/base.py
+++ b/sf_tool_management/models/base.py
@@ -1242,7 +1242,7 @@ class FunctionalToolDismantle(models.Model):
functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', required=True, tracking=True,
domain=[('functional_tool_status', '!=', '已拆除'),
- ('current_location', '=', '刀具房')])
+ ('current_location', 'in', ['刀具房', '线边刀库'])])
@api.onchange('functional_tool_id')
def _onchange_functional_tool_id(self):
From 085e6359ce76714b51ed9fdf458f6754b784f947 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?=
Date: Tue, 15 Apr 2025 15:47:35 +0800
Subject: [PATCH 15/22] =?UTF-8?q?=E7=BC=96=E7=A8=8B=E5=8D=95=E5=A2=9E?=
=?UTF-8?q?=E5=8A=A0=E4=BA=8C=E7=BB=B4=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_mrs_connect/controllers/controllers.py | 88 +++++++++++++++++++++--
1 file changed, 81 insertions(+), 7 deletions(-)
diff --git a/sf_mrs_connect/controllers/controllers.py b/sf_mrs_connect/controllers/controllers.py
index 7599ed6c..5404f313 100644
--- a/sf_mrs_connect/controllers/controllers.py
+++ b/sf_mrs_connect/controllers/controllers.py
@@ -3,9 +3,13 @@ import logging
import os
import json
import base64
+from qrcode.main import QRCode
+import PyPDF2
from odoo import http, fields, models
from odoo.http import request
from odoo.addons.sf_base.controllers.controllers import MultiInheritController
+import qrcode
+
class Sf_Mrs_Connect(http.Controller, MultiInheritController):
@@ -22,6 +26,7 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
try:
res = {'status': 1, 'message': '成功'}
datas = request.httprequest.data
+ model_id = None
ret = json.loads(datas)
ret = json.loads(ret['result'])
logging.info('下发编程单:%s' % ret)
@@ -57,6 +62,7 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
res['message'] = '编程单号为%s的CNC程序文件从FTP拉取失败' % (ret['programming_no'])
return json.JSONEncoder().encode(res)
for production in productions:
+ model_id = production.product_id.model_id # 一个编程单的制造订单对应同一个模型
production.write({'programming_state': '已编程', 'work_state': '已编程', 'is_rework': False})
for panel in ret['processing_panel'].split(','):
# 查询状态为进行中且工序类型为CNC加工的工单
@@ -83,19 +89,22 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
# panel)
program_path_tmp_panel = os.path.join('/tmp', ret['folder_name'], 'return', panel)
files_panel = os.listdir(program_path_tmp_panel)
+ panel_file_path = ''
if files_panel:
for file in files_panel:
file_extension = os.path.splitext(file)[1]
if file_extension.lower() == '.pdf':
panel_file_path = os.path.join(program_path_tmp_panel, file)
logging.info('panel_file_path:%s' % panel_file_path)
- cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
- pre_workorder = productions.workorder_ids.filtered(
- lambda ap: ap.routing_type in ['装夹预调', '人工线下加工'] and ap.state not in ['done', 'rework'
- 'cancel'] and ap.processing_panel == panel)
- if pre_workorder:
- pre_workorder.write(
- {'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
+
+ cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
+ pre_workorder = productions.workorder_ids.filtered(
+ lambda ap: ap.routing_type in ['装夹预调', '人工线下加工'] and ap.state not in ['done', 'rework'
+ 'cancel'] and ap.processing_panel == panel)
+ if pre_workorder:
+ self._add_qr_code_to_pdf(panel_file_path, model_id)
+ pre_workorder.write(
+ {'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
productions.write({'programming_state': '已编程', 'work_state': '已编程'})
productions.filtered(lambda p: p.production_type == '人工线下加工').write({'manual_quotation': True})
logging.info('已更新制造订单编程状态:%s' % productions.ids)
@@ -268,3 +277,68 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
request.cr.rollback()
logging.info('get_cnc_processing_create error:%s' % e)
return json.JSONEncoder().encode(res)
+
+
+ def _add_qr_code_to_pdf(self, panel_file_path, model_id):
+ if not os.path.exists(panel_file_path):
+ logging.warning(f'文件不存在: {panel_file_path}')
+ return False
+
+ # 生成二维码
+ qr = qrcode.QRCode(version=1, box_size=10, border=5)
+ qr.add_data(str(model_id))
+ qr.make(fit=True)
+ qr_img = qr.make_image(fill_color="black", back_color="white")
+
+ # 保存二维码为临时文件
+ qr_temp_path = '/tmp/qr_temp.png'
+ qr_img.save(qr_temp_path)
+
+ # 创建一个临时PDF文件路径
+ output_temp_path = '/tmp/output_temp.pdf'
+
+ # 使用reportlab创建一个新的PDF
+ from reportlab.pdfgen import canvas
+ from reportlab.lib.units import inch
+ from PyPDF2 import PdfFileReader, PdfFileWriter
+
+ # 读取原始PDF
+ existing_pdf = PdfFileReader(open(panel_file_path, "rb"))
+ output = PdfFileWriter()
+
+ # 处理第一页
+ page = existing_pdf.getPage(0)
+ # 获取页面尺寸
+ page_width = float(page.mediaBox.getWidth())
+ page_height = float(page.mediaBox.getHeight())
+
+ # 创建一个新的PDF页面用于放置二维码
+ c = canvas.Canvas(output_temp_path, pagesize=(page_width, page_height))
+ # 在右下角绘制二维码,预留边距
+ qr_size = 1 * inch # 二维码大小为1英寸
+ margin = 0.4 * inch # 边距为0.4英寸
+ c.drawImage(qr_temp_path, page_width - qr_size - margin, margin, width=qr_size, height=qr_size)
+ c.save()
+
+ # 读取带有二维码的临时PDF
+ qr_pdf = PdfFileReader(open(output_temp_path, "rb"))
+ qr_page = qr_pdf.getPage(0)
+
+ # 合并原始页面和二维码页面
+ page.mergePage(qr_page)
+ output.addPage(page)
+
+ # 添加剩余的页面
+ for i in range(1, existing_pdf.getNumPages()):
+ output.addPage(existing_pdf.getPage(i))
+
+ # 保存最终的PDF
+ with open(panel_file_path, "wb") as output_file:
+ output.write(output_file)
+
+ # 清理临时文件
+ qr_pdf.close()
+ os.remove(qr_temp_path)
+ os.remove(output_temp_path)
+
+ return True
\ No newline at end of file
From bc85c457ad9c10a3a011ed2bb0d1d1a00e003625 Mon Sep 17 00:00:00 2001
From: yuxianghui <3437689193@qq.com>
Date: Tue, 15 Apr 2025 16:46:49 +0800
Subject: [PATCH 16/22] =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=88=80=E5=85=B7?=
=?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=8A=B6=E6=80=81=E5=8F=98=E6=9B=B4=E8=B7=9F?=
=?UTF-8?q?=E8=B8=AA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_tool_management/models/functional_tool.py | 5 +++--
sf_tool_management/views/functional_tool_views.xml | 4 ++++
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/sf_tool_management/models/functional_tool.py b/sf_tool_management/models/functional_tool.py
index 63a31b82..a9dddc0e 100644
--- a/sf_tool_management/models/functional_tool.py
+++ b/sf_tool_management/models/functional_tool.py
@@ -10,6 +10,7 @@ from odoo.exceptions import ValidationError
class FunctionalCuttingToolEntity(models.Model):
_name = 'sf.functional.cutting.tool.entity'
+ _inherit = ['mail.thread']
_description = '功能刀具列表'
_order = 'functional_tool_status'
@@ -41,7 +42,7 @@ class FunctionalCuttingToolEntity(models.Model):
max_lifetime_value = fields.Integer(string='最大寿命值(min)', readonly=True)
alarm_value = fields.Integer(string='报警值(min)', readonly=True)
used_value = fields.Integer(string='已使用值(min)', readonly=True)
- functional_tool_status = fields.Selection([('正常', '正常'), ('报警', '报警'), ('已拆除', '已拆除')],
+ functional_tool_status = fields.Selection([('正常', '正常'), ('报警', '报警'), ('已拆除', '已拆除')], tracking=True,
string='状态', store=True, default='正常')
current_location_id = fields.Many2one('stock.location', string='当前位置', compute='_compute_current_location_id',
store=True)
@@ -263,7 +264,7 @@ class FunctionalCuttingToolEntity(models.Model):
functional_tool_model_ids.append(functional_tool_model.id)
return [(6, 0, functional_tool_model_ids)]
- dismantle_num = fields.Integer('拆解单数量', compute='_compute_dismantle_num', store=True)
+ dismantle_num = fields.Integer('拆解单数量', compute='_compute_dismantle_num', tracking=True, store=True)
dismantle_ids = fields.One2many('sf.functional.tool.dismantle', 'functional_tool_id', '拆解单')
@api.depends('dismantle_ids')
diff --git a/sf_tool_management/views/functional_tool_views.xml b/sf_tool_management/views/functional_tool_views.xml
index 2b872053..a306d6dd 100644
--- a/sf_tool_management/views/functional_tool_views.xml
+++ b/sf_tool_management/views/functional_tool_views.xml
@@ -192,6 +192,10 @@
+
+
+
+
From 6ff83959167df60a6dee937919409fc5495c07fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?=
Date: Tue, 15 Apr 2025 16:57:32 +0800
Subject: [PATCH 17/22] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=90=91pdf=E4=B8=AD?=
=?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=8C=E7=BB=B4=E7=A0=81=E7=9A=84=E5=8A=9F?=
=?UTF-8?q?=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_base/commons/common.py | 126 ++++++++++++++++++++++
sf_mrs_connect/controllers/controllers.py | 70 +-----------
2 files changed, 128 insertions(+), 68 deletions(-)
diff --git a/sf_base/commons/common.py b/sf_base/commons/common.py
index 9f359c9c..27b56038 100644
--- a/sf_base/commons/common.py
+++ b/sf_base/commons/common.py
@@ -2,7 +2,16 @@
import time, datetime
import hashlib
from odoo import models
+from typing import Optional
import socket
+import os
+import logging
+import qrcode
+from reportlab.pdfgen import canvas
+from reportlab.lib.units import inch
+from PyPDF2 import PdfFileReader, PdfFileWriter
+from reportlab.pdfbase import pdfmetrics
+from reportlab.pdfbase.ttfonts import TTFont
class Common(models.Model):
_name = 'sf.sync.common'
@@ -92,3 +101,120 @@ class PrintingUtils(models.AbstractModel):
# host = "192.168.50.110" # 可以作为参数传入,或者在此配置
# port = 9100 # 可以作为参数传入,或者在此配置
self.send_to_printer(host, port, zpl_code)
+
+
+ def add_qr_code_to_pdf(self, pdf_path:str, content:str, buttom_text:Optional[str]=False):
+ """
+ 在PDF文件中添加二维码
+ :param pdf_path: PDF文件路径
+ :param content: 二维码内容
+ :param buttom_text: 二维码下方文字
+ :return: 是否成功
+ """
+ if not os.path.exists(pdf_path):
+ logging.warning(f'PDF文件不存在: {pdf_path}')
+ return False
+
+ # 生成二维码
+ qr = qrcode.QRCode(version=1, box_size=10, border=5)
+ qr.add_data(str(content))
+ qr.make(fit=True)
+ qr_img = qr.make_image(fill_color="black", back_color="white")
+
+ # 保存二维码为临时文件
+ qr_temp_path = '/tmp/qr_temp.png'
+ qr_img.save(qr_temp_path)
+
+ # 创建一个临时PDF文件路径
+ output_temp_path = '/tmp/output_temp.pdf'
+
+ try:
+ # 使用reportlab创建一个新的PDF
+
+
+ # 注册中文字体
+ font_paths = [
+ "/usr/share/fonts/windows/simsun.ttc", # Windows系统宋体
+ "c:/windows/fonts/simsun.ttc", # Windows系统宋体另一个位置
+ "/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf", # Linux Droid字体
+ "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", # 文泉驿正黑
+ "/usr/share/fonts/chinese/TrueType/simsun.ttc", # 某些Linux发行版位置
+ ]
+
+ font_found = False
+ for font_path in font_paths:
+ if os.path.exists(font_path):
+ try:
+ pdfmetrics.registerFont(TTFont('SimSun', font_path))
+ font_found = True
+ break
+ except:
+ continue
+
+ # 读取原始PDF
+ with open(pdf_path, "rb") as original_file:
+ existing_pdf = PdfFileReader(original_file)
+ output = PdfFileWriter()
+
+ # 处理第一页
+ page = existing_pdf.getPage(0)
+ # 获取页面尺寸
+ page_width = float(page.mediaBox.getWidth())
+ page_height = float(page.mediaBox.getHeight())
+
+ # 创建一个新的PDF页面用于放置二维码
+ c = canvas.Canvas(output_temp_path, pagesize=(page_width, page_height))
+
+ # 设置字体
+ if font_found:
+ c.setFont('SimSun', 14) # 增大字体大小到14pt
+ else:
+ # 如果没有找到中文字体,使用默认字体
+ c.setFont('Helvetica', 14)
+ logging.warning("未找到中文字体,将使用默认字体")
+
+ # 在右下角绘制二维码,预留边距
+ qr_size = 1.5 * inch # 二维码大小为2英寸
+ margin = 0.1 * inch # 边距为0.4英寸
+ qr_y = margin + 20 # 将二维码向上移动一点,为文字留出空间
+ c.drawImage(qr_temp_path, page_width - qr_size - margin, qr_y, width=qr_size, height=qr_size)
+
+ if buttom_text:
+ # 在二维码下方绘制文字
+ text = buttom_text
+ text_width = c.stringWidth(text, "SimSun" if font_found else "Helvetica", 14) # 准确计算文字宽度
+ text_x = page_width - qr_size - margin + (qr_size - text_width) / 2 # 文字居中对齐
+ text_y = margin + 20 # 文字位置靠近底部
+ c.drawString(text_x, text_y, text)
+
+ c.save()
+
+ # 读取带有二维码的临时PDF
+ with open(output_temp_path, "rb") as qr_file:
+ qr_pdf = PdfFileReader(qr_file)
+ qr_page = qr_pdf.getPage(0)
+
+ # 合并原始页面和二维码页面
+ page.mergePage(qr_page)
+ output.addPage(page)
+
+ # 添加剩余的页面
+ for i in range(1, existing_pdf.getNumPages()):
+ output.addPage(existing_pdf.getPage(i))
+
+ # 保存最终的PDF到一个临时文件
+ final_temp_path = pdf_path + '.tmp'
+ with open(final_temp_path, "wb") as output_file:
+ output.write(output_file)
+
+ # 替换原始文件
+ os.replace(final_temp_path, pdf_path)
+
+ return True
+
+ finally:
+ # 清理临时文件
+ if os.path.exists(qr_temp_path):
+ os.remove(qr_temp_path)
+ if os.path.exists(output_temp_path):
+ os.remove(output_temp_path)
\ No newline at end of file
diff --git a/sf_mrs_connect/controllers/controllers.py b/sf_mrs_connect/controllers/controllers.py
index 5404f313..61499cea 100644
--- a/sf_mrs_connect/controllers/controllers.py
+++ b/sf_mrs_connect/controllers/controllers.py
@@ -3,12 +3,9 @@ import logging
import os
import json
import base64
-from qrcode.main import QRCode
-import PyPDF2
from odoo import http, fields, models
from odoo.http import request
from odoo.addons.sf_base.controllers.controllers import MultiInheritController
-import qrcode
@@ -96,13 +93,12 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
if file_extension.lower() == '.pdf':
panel_file_path = os.path.join(program_path_tmp_panel, file)
logging.info('panel_file_path:%s' % panel_file_path)
-
+ request.env['printing.utils'].add_qr_code_to_pdf(panel_file_path, model_id, "扫码获取工单")
cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
pre_workorder = productions.workorder_ids.filtered(
lambda ap: ap.routing_type in ['装夹预调', '人工线下加工'] and ap.state not in ['done', 'rework'
'cancel'] and ap.processing_panel == panel)
if pre_workorder:
- self._add_qr_code_to_pdf(panel_file_path, model_id)
pre_workorder.write(
{'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
productions.write({'programming_state': '已编程', 'work_state': '已编程'})
@@ -279,66 +275,4 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
return json.JSONEncoder().encode(res)
- def _add_qr_code_to_pdf(self, panel_file_path, model_id):
- if not os.path.exists(panel_file_path):
- logging.warning(f'文件不存在: {panel_file_path}')
- return False
-
- # 生成二维码
- qr = qrcode.QRCode(version=1, box_size=10, border=5)
- qr.add_data(str(model_id))
- qr.make(fit=True)
- qr_img = qr.make_image(fill_color="black", back_color="white")
-
- # 保存二维码为临时文件
- qr_temp_path = '/tmp/qr_temp.png'
- qr_img.save(qr_temp_path)
-
- # 创建一个临时PDF文件路径
- output_temp_path = '/tmp/output_temp.pdf'
-
- # 使用reportlab创建一个新的PDF
- from reportlab.pdfgen import canvas
- from reportlab.lib.units import inch
- from PyPDF2 import PdfFileReader, PdfFileWriter
-
- # 读取原始PDF
- existing_pdf = PdfFileReader(open(panel_file_path, "rb"))
- output = PdfFileWriter()
-
- # 处理第一页
- page = existing_pdf.getPage(0)
- # 获取页面尺寸
- page_width = float(page.mediaBox.getWidth())
- page_height = float(page.mediaBox.getHeight())
-
- # 创建一个新的PDF页面用于放置二维码
- c = canvas.Canvas(output_temp_path, pagesize=(page_width, page_height))
- # 在右下角绘制二维码,预留边距
- qr_size = 1 * inch # 二维码大小为1英寸
- margin = 0.4 * inch # 边距为0.4英寸
- c.drawImage(qr_temp_path, page_width - qr_size - margin, margin, width=qr_size, height=qr_size)
- c.save()
-
- # 读取带有二维码的临时PDF
- qr_pdf = PdfFileReader(open(output_temp_path, "rb"))
- qr_page = qr_pdf.getPage(0)
-
- # 合并原始页面和二维码页面
- page.mergePage(qr_page)
- output.addPage(page)
-
- # 添加剩余的页面
- for i in range(1, existing_pdf.getNumPages()):
- output.addPage(existing_pdf.getPage(i))
-
- # 保存最终的PDF
- with open(panel_file_path, "wb") as output_file:
- output.write(output_file)
-
- # 清理临时文件
- qr_pdf.close()
- os.remove(qr_temp_path)
- os.remove(output_temp_path)
-
- return True
\ No newline at end of file
+
\ No newline at end of file
From 2100ee95907e43666c86b7e5c540016b29b477a9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?=
Date: Tue, 15 Apr 2025 17:08:17 +0800
Subject: [PATCH 18/22] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A4=87=E6=B3=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_mrs_connect/controllers/controllers.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/sf_mrs_connect/controllers/controllers.py b/sf_mrs_connect/controllers/controllers.py
index 61499cea..c30d3d53 100644
--- a/sf_mrs_connect/controllers/controllers.py
+++ b/sf_mrs_connect/controllers/controllers.py
@@ -93,6 +93,8 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
if file_extension.lower() == '.pdf':
panel_file_path = os.path.join(program_path_tmp_panel, file)
logging.info('panel_file_path:%s' % panel_file_path)
+
+ # 向编程单中添加二维码
request.env['printing.utils'].add_qr_code_to_pdf(panel_file_path, model_id, "扫码获取工单")
cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
pre_workorder = productions.workorder_ids.filtered(
From 20722c12b846b9b2dc044024122ad454b9ea8f64 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?=
Date: Tue, 15 Apr 2025 17:08:43 +0800
Subject: [PATCH 19/22] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=B4=A8=E6=A3=80?=
=?UTF-8?q?=E5=8D=95=E7=9A=84=E8=B4=A8=E6=A3=80=E5=91=98=E5=AD=97=E6=AE=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
quality_control/models/quality.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/quality_control/models/quality.py b/quality_control/models/quality.py
index 47b21dc7..9be466f5 100644
--- a/quality_control/models/quality.py
+++ b/quality_control/models/quality.py
@@ -206,7 +206,7 @@ class QualityCheck(models.Model):
('NG', 'NG')
], string='出厂检验报告结果', default='OK')
measure_operator = fields.Many2one('res.users', string='操机员')
- quality_manager = fields.Many2one('res.users', string='质检员', compute='_compute_quality_manager', store=True)
+ quality_manager = fields.Many2one('res.users', string='质检员', compute='_compute_quality_manager')
@api.depends('measure_line_ids')
def _compute_quality_manager(self):
From 2de0e9f02f9cc591ae133cd5d4ec189465ed03e5 Mon Sep 17 00:00:00 2001
From: yuxianghui <3437689193@qq.com>
Date: Tue, 15 Apr 2025 17:20:16 +0800
Subject: [PATCH 20/22] =?UTF-8?q?=E5=A4=84=E7=90=86=E5=8A=9F=E8=83=BD?=
=?UTF-8?q?=E5=88=80=E5=85=B7=E9=A2=84=E8=AD=A6=E6=B2=A1=E6=9C=89=E8=87=AA?=
=?UTF-8?q?=E5=8A=A8=E5=88=9B=E5=BB=BA=E9=A2=84=E8=AD=A6=E8=AE=B0=E5=BD=95?=
=?UTF-8?q?=E5=92=8C=E6=8B=86=E8=A7=A3=E5=8D=95=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_tool_management/models/functional_tool.py | 31 +++++++++++++------
.../models/maintenance_equipment.py | 16 +++++++---
.../views/functional_tool_views.xml | 1 +
3 files changed, 33 insertions(+), 15 deletions(-)
diff --git a/sf_tool_management/models/functional_tool.py b/sf_tool_management/models/functional_tool.py
index a9dddc0e..498d9436 100644
--- a/sf_tool_management/models/functional_tool.py
+++ b/sf_tool_management/models/functional_tool.py
@@ -63,16 +63,27 @@ class FunctionalCuttingToolEntity(models.Model):
for item in self:
if item:
if item.functional_tool_status == '报警':
- # 创建报警刀具拆解单
- 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]
- })
+ self.create_tool_dismantle()
+
+ def set_functional_tool_status(self):
+ # self.write({
+ # 'functional_tool_status': '报警'
+ # })
+ self.functional_tool_status = '报警'
+ self.create_tool_dismantle()
+
+ 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',
'current_shelf_location_id', 'stock_num')
diff --git a/sf_tool_management/models/maintenance_equipment.py b/sf_tool_management/models/maintenance_equipment.py
index 12c7f8f3..a3375075 100644
--- a/sf_tool_management/models/maintenance_equipment.py
+++ b/sf_tool_management/models/maintenance_equipment.py
@@ -107,11 +107,17 @@ class SfMaintenanceEquipment(models.Model):
if functional_tool_id.current_location != '机内刀库':
# 对功能刀具进行移动到生产线
functional_tool_id.tool_inventory_displacement_out()
- functional_tool_id.write({
- 'max_lifetime_value': data['MaxLife'],
- 'used_value': data['UseLife'],
- 'functional_tool_status': tool_install_time.get(data['State'])
- })
+ data_tool = {
+ 'max_lifetime_value': data['MaxLife'],
+ 'used_value': data['UseLife'],
+ '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:
logging.info('获取的【%s】设备不存在!!!' % data['DeviceId'])
else:
diff --git a/sf_tool_management/views/functional_tool_views.xml b/sf_tool_management/views/functional_tool_views.xml
index a306d6dd..25b9346a 100644
--- a/sf_tool_management/views/functional_tool_views.xml
+++ b/sf_tool_management/views/functional_tool_views.xml
@@ -42,6 +42,7 @@
From c67944f6895b10aa30d9be24df3211dd7911ec89 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?=
Date: Thu, 17 Apr 2025 13:15:48 +0800
Subject: [PATCH 22/22] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=B7=A5=E5=8D=95?=
=?UTF-8?q?=E4=B8=8B=E5=8F=91=E6=97=B6=E6=89=93=E5=8D=B0=E7=BC=96=E7=A8=8B?=
=?UTF-8?q?=E6=96=87=E4=BB=B6pdf=E7=9A=84=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
jikimo_work_reporting_api/controllers/main.py | 4 +-
sf_base/commons/Printer.py | 1 +
sf_base/views/Printer.xml | 2 +
sf_maintenance/models/__init__.py | 2 +-
...code_printer.py => maintenance_printer.py} | 2 +-
sf_manufacturing/models/__init__.py | 3 +-
sf_manufacturing/models/workorder_printer.py | 134 ++++++++++++++++++
7 files changed, 143 insertions(+), 5 deletions(-)
rename sf_maintenance/models/{maintenance_qrcode_printer.py => maintenance_printer.py} (98%)
create mode 100644 sf_manufacturing/models/workorder_printer.py
diff --git a/jikimo_work_reporting_api/controllers/main.py b/jikimo_work_reporting_api/controllers/main.py
index c2594875..76b55dc9 100644
--- a/jikimo_work_reporting_api/controllers/main.py
+++ b/jikimo_work_reporting_api/controllers/main.py
@@ -6,9 +6,9 @@ from odoo.addons.sf_machine_connect.models.ftp_operate import transfer_nc_files
class MainController(http.Controller):
@http.route('/api/manual_download_program', type='json', methods=['POST'], auth='wechat_token', cors='*')
- def work_reporting(self, **kwargs):
+ def manual_download_program(self):
"""
-
+ 人工线下加工传输编程文件
"""
data = json.loads(request.httprequest.data)
maintenance_equipment_name = data.get('maintenance_equipment_name')
diff --git a/sf_base/commons/Printer.py b/sf_base/commons/Printer.py
index 563d9dea..e9d31282 100644
--- a/sf_base/commons/Printer.py
+++ b/sf_base/commons/Printer.py
@@ -8,6 +8,7 @@ class Printer(models.Model):
name = fields.Char(string='名称', required=True)
ip_address = fields.Char(string='IP 地址', required=True)
port = fields.Integer(string='端口', default=9100)
+ type = fields.Selection([('zpl', 'ZPL'), ('normal', '普通')], string='类型', default='zpl')
class TableStyle(models.Model):
diff --git a/sf_base/views/Printer.xml b/sf_base/views/Printer.xml
index 0e199b32..157121c9 100644
--- a/sf_base/views/Printer.xml
+++ b/sf_base/views/Printer.xml
@@ -9,6 +9,7 @@
+
@@ -24,6 +25,7 @@
+
diff --git a/sf_maintenance/models/__init__.py b/sf_maintenance/models/__init__.py
index b07aa1eb..4e925ec6 100644
--- a/sf_maintenance/models/__init__.py
+++ b/sf_maintenance/models/__init__.py
@@ -4,4 +4,4 @@ from . import sf_maintenance_oee
from . import sf_maintenance_logs
from . import sf_equipment_maintenance_standards
from . import sf_maintenance_requests
-from . import maintenance_qrcode_printer
+from . import maintenance_printer
diff --git a/sf_maintenance/models/maintenance_qrcode_printer.py b/sf_maintenance/models/maintenance_printer.py
similarity index 98%
rename from sf_maintenance/models/maintenance_qrcode_printer.py
rename to sf_maintenance/models/maintenance_printer.py
index 7d1950e7..b2c251aa 100644
--- a/sf_maintenance/models/maintenance_qrcode_printer.py
+++ b/sf_maintenance/models/maintenance_printer.py
@@ -42,7 +42,7 @@ class MaintenanceEquipment(models.Model):
# port = 9100 # 可以根据实际情况修改
# 获取默认打印机配置
- printer_config = self.env['printer.configuration'].sudo().search([('model', '=', '机床二维码')], limit=1)
+ printer_config = self.env['printer.configuration'].sudo().search([('model', '=', self._name)], limit=1)
if not printer_config:
raise UserError('请先配置打印机')
host = printer_config.printer_id.ip_address
diff --git a/sf_manufacturing/models/__init__.py b/sf_manufacturing/models/__init__.py
index e6845317..4a0a2a33 100644
--- a/sf_manufacturing/models/__init__.py
+++ b/sf_manufacturing/models/__init__.py
@@ -16,4 +16,5 @@ from . import sf_production_common
from . import sale_order
from . import quick_easy_order
from . import purchase_order
-from . import quality_check
\ No newline at end of file
+from . import quality_check
+from . import workorder_printer
diff --git a/sf_manufacturing/models/workorder_printer.py b/sf_manufacturing/models/workorder_printer.py
new file mode 100644
index 00000000..46eb3b8b
--- /dev/null
+++ b/sf_manufacturing/models/workorder_printer.py
@@ -0,0 +1,134 @@
+import qrcode
+import base64
+import logging
+import tempfile
+import os
+import platform
+import socket
+import subprocess
+from io import BytesIO
+from odoo import models, fields, api
+from odoo.exceptions import UserError
+
+_logger = logging.getLogger(__name__)
+
+class MrpWorkorder(models.Model):
+ _name = 'mrp.workorder'
+ _inherit = ['mrp.workorder', 'printing.utils']
+
+ def print_pdf(self, printer_config, pdf_data):
+ """跨平台打印函数,支持网络打印机(IP:端口)"""
+ # 将PDF数据保存到临时文件
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as temp_file:
+ pdf_binary = base64.b64decode(pdf_data)
+ temp_file.write(pdf_binary)
+ temp_file_path = temp_file.name
+
+ _logger.info(f"开始打印PDF文件: {temp_file_path}")
+
+ try:
+ # 获取打印机名称或IP地址
+ printer_name = printer_config.printer_id.name
+ if not printer_name:
+ raise UserError('打印机名称未配置')
+
+ # 使用打印机配置中的IP地址和端口
+ printer_ip = printer_config.printer_id.ip_address
+ printer_port = printer_config.printer_id.port
+ _logger.info(f"使用网络打印机: IP={printer_ip}, 端口={printer_port}")
+
+ if platform.system() == 'Windows':
+ _logger.info(f"Windows环境不支持网络打印机")
+ else: # Linux环境
+ # try:
+ # import cups
+
+ # # 处理网络打印机情况
+ # _logger.info(f"Linux环境下连接网络打印机: {printer_ip}:{printer_port}")
+
+ # # 创建连接
+ # conn = cups.Connection()
+
+ # # 检查打印机是否已经添加到系统
+ # printers = conn.getPrinters()
+ # _logger.info(f"可用打印机列表: {list(printers.keys())}")
+
+ # network_printer_name = f"IP_{printer_ip}_{printer_port}"
+
+ # # 如果打印机不存在,尝试添加
+ # if network_printer_name not in printers:
+ # _logger.info(f"添加网络打印机: {network_printer_name}")
+ # conn.addPrinter(
+ # network_printer_name,
+ # device=f"socket://{printer_ip}:{printer_port}",
+ # info=f"Network Printer {printer_ip}:{printer_port}",
+ # location="Network"
+ # )
+ # # 设置打印机为启用状态
+ # conn.enablePrinter(network_printer_name)
+ # _logger.info(f"网络打印机添加成功: {network_printer_name}")
+
+ # # 打印文件
+ # _logger.info(f"开始打印到网络打印机: {network_printer_name}")
+ # job_id = conn.printFile(network_printer_name, temp_file_path, "工单打印", {})
+ # _logger.info(f"打印作业ID: {job_id}")
+
+
+ # except ImportError as ie:
+ # _logger.error(f"导入CUPS库失败: {str(ie)}")
+
+ # 尝试使用lp命令打印
+ try:
+ _logger.info("尝试使用lp命令打印...")
+
+ # 使用socket设置打印
+ cmd = f"lp -h {printer_ip}:{printer_port} -d {printer_name} {temp_file_path}"
+
+ _logger.info(f"执行lp打印命令: {cmd}")
+ result = subprocess.run(cmd, shell=True, check=True, capture_output=True)
+ _logger.info(f"lp打印结果: {result.stdout.decode()}")
+ except Exception as e:
+ _logger.error(f"lp命令打印失败: {str(e)}")
+ raise UserError(f'打印失败,请安装cups打印库: pip install pycups 或确保lp命令可用')
+
+ return True
+ except Exception as e:
+ _logger.error(f"打印失败详细信息: {str(e)}")
+ raise UserError(f'打印失败: {str(e)}')
+ finally:
+ # 清理临时文件
+ if os.path.exists(temp_file_path):
+ try:
+ os.unlink(temp_file_path)
+ _logger.info(f"临时文件已清理: {temp_file_path}")
+ except Exception as e:
+ _logger.error(f"清理临时文件失败: {str(e)}")
+
+ def _compute_state(self):
+ super(MrpWorkorder, self)._compute_state()
+ for workorder in self:
+ work_ids = workorder.production_id.workorder_ids.filtered(lambda w: w.routing_type == '装夹预调' or w.routing_type == '人工线下加工')
+ for wo in work_ids:
+ if wo.state == 'ready' and not wo.production_id.product_id.is_print_program:
+ # 触发打印程序
+ pdf_data = self.processing_drawing
+ try:
+ if pdf_data:
+ # 获取默认打印机配置
+ printer_config = self.env['printer.configuration'].sudo().search([('model', '=', self._name), ('printer_id.type', '=', 'normal')], limit=1)
+ if not printer_config:
+ raise UserError('请先配置打印机')
+
+ # 执行打印
+ if self.print_pdf(printer_config, pdf_data):
+ wo.production_id.product_id.is_print_program = True
+ _logger.info(f"工单 {wo.name} 的PDF已成功打印")
+
+ except Exception as e:
+ _logger.error(f'打印配置错误: {str(e)}')
+
+class ProductTemplate(models.Model):
+ _inherit = 'product.template'
+
+ is_print_program = fields.Boolean(string='是否打印程序', default=False)
+