diff --git a/jikimo_account_process/__manifest__.py b/jikimo_account_process/__manifest__.py
index f701deb7..0c1b521c 100644
--- a/jikimo_account_process/__manifest__.py
+++ b/jikimo_account_process/__manifest__.py
@@ -3,8 +3,8 @@
'name': "jikimo_account_process",
'summary': """
- Short (1 phrase/line) summary of the module's purpose, used as
- subtitle on modules listing or apps.openerp.com""",
+ 处理会计凭证生成重复名称报错问题
+ """,
'description': """
Long description of module's purpose
diff --git a/jikimo_frontend/views/bye_odoo.xml b/jikimo_frontend/views/bye_odoo.xml
index 14fd211b..d75ddd06 100644
--- a/jikimo_frontend/views/bye_odoo.xml
+++ b/jikimo_frontend/views/bye_odoo.xml
@@ -16,7 +16,7 @@
-
+
diff --git a/sf_dlm/__manifest__.py b/sf_dlm/__manifest__.py
index 7878467b..bcd052f0 100644
--- a/sf_dlm/__manifest__.py
+++ b/sf_dlm/__manifest__.py
@@ -10,7 +10,7 @@
""",
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
- 'depends': ['sf_base', 'web_widget_model_viewer', 'mrp_subcontracting', 'purchase_stock', 'uom', ],
+ 'depends': ['sf_base', 'mrp_subcontracting', 'purchase_stock', 'uom'],
'data': [
'data/product_data.xml',
'data/uom_data.xml',
diff --git a/sf_machine_connect/__manifest__.py b/sf_machine_connect/__manifest__.py
index db7ae467..48857914 100644
--- a/sf_machine_connect/__manifest__.py
+++ b/sf_machine_connect/__manifest__.py
@@ -12,7 +12,7 @@
'category': 'sf',
'author': 'jikimo',
'website': 'https://sf.cs.jikimo.com',
- 'depends': ['web', 'mail', 'sf_base', 'sf_manufacturing', 'barcodes', ],
+ 'depends': ['web', 'sf_manufacturing', 'barcodes'],
'data': [
# 定义权限组放在最上面
# 权限组
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
index b8c31cb6..a65e97ef 100644
--- a/sf_machine_connect/controllers/controllers.py
+++ b/sf_machine_connect/controllers/controllers.py
@@ -15,7 +15,7 @@ db_config = {
"user": "postgres",
"password": "postgres",
"port": "5432",
- "host": "172.16.10.113"
+ "host": "172.16.10.131"
}
@@ -24,6 +24,8 @@ def convert_to_seconds(time_str):
if time_str is None:
return 0
+ if time_str == 0:
+ return 0
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
@@ -1306,9 +1308,9 @@ 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,
- 'cut_24_time': last_24_time['process_time'] if last_24_time['process_time'] is not None else 0,
+ 'cut_24_time': last_24_time['process_time'] if last_24_time else 0,
'power_on_time': last_all_time['power_on_time'] if last_all_time['power_on_time'] is not None else 0,
- 'power_on_24_time': last_24_time['power_on_time'] if last_24_time['power_on_time'] is not None else 0,
+ 'power_on_24_time': last_24_time['power_on_time'] if last_24_time else 0,
'alarm_last_24_time': alarm_last_24_time,
'alarm_last_24_nums': len(list(set(alarm_last_24_nums))),
'idle_count': idle_count,
diff --git a/sf_maintenance/models/sf_maintenance_oee.py b/sf_maintenance/models/sf_maintenance_oee.py
index 0c75ba53..a88bc27e 100644
--- a/sf_maintenance/models/sf_maintenance_oee.py
+++ b/sf_maintenance/models/sf_maintenance_oee.py
@@ -12,6 +12,8 @@ def convert_to_seconds(time_str):
if time_str is None:
return 0
+ if time_str == 0:
+ return 0
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
@@ -122,24 +124,37 @@ class SfMaintenanceEquipmentOEE(models.Model):
real_dict = result_time['data'][self.equipment_code]
print('=', result_time)
if result_time['status'] == 1:
- self.online_time = round((convert_to_seconds(real_dict['power_on_time']) - convert_to_seconds(
- real_dict['power_on_24_time'])) / 3600, 2)
- self.work_time = round(
- (convert_to_seconds(real_dict['cut_time']) - convert_to_seconds(real_dict['cut_24_time'])) / 3600,
- 2)
+ if real_dict['power_on_24_time'] == 0:
+ self.online_time = 0
+ self.idle_time = 0
+ self.idle_rate = 0
+ self.work_rate = 0
+ self.fault_time = 0
+ self.fault_rate = 0
+ self.fault_nums = 0
+ self.idle_nums = 0
+ self.work_time = 0
+ else:
+ self.online_time = round((convert_to_seconds(real_dict['power_on_time']) - convert_to_seconds(
+ real_dict['power_on_24_time'])) / 3600, 2)
+ self.idle_time = float(self.online_time) - float(
+ self.work_time) if self.online_time and self.work_time else 0
+ self.idle_rate = round(
+ float(self.idle_time) / (float(self.online_time) if self.online_time else 1) * 100, 2)
+ self.work_rate = round(
+ float(self.work_time) / (float(self.online_time) if self.online_time else 1) * 100, 2)
+ self.fault_time = (float(real_dict['alarm_last_24_time']) if real_dict[
+ 'alarm_last_24_time'] else 0) / 3600
+ self.fault_rate = round(
+ float(self.fault_time) / (float(self.online_time) if self.online_time else 1) * 100, 2)
+ self.fault_nums = real_dict['alarm_last_24_nums']
+ self.idle_nums = real_dict['idle_count']
+ self.work_time = round(
+ (convert_to_seconds(real_dict['cut_time']) - convert_to_seconds(real_dict['cut_24_time'])) / 3600,
+ 2)
self.offline_time = 24 - (float(self.online_time) if self.online_time else 0)
- self.idle_time = float(self.online_time) - float(
- self.work_time) if self.online_time and self.work_time else 0
- self.idle_rate = round(
- float(self.idle_time) / (float(self.online_time) if self.online_time else 1) * 100, 2)
- self.work_rate = round(
- float(self.work_time) / (float(self.online_time) if self.online_time else 1) * 100, 2)
- self.fault_time = (float(real_dict['alarm_last_24_time']) if real_dict[
- 'alarm_last_24_time'] else 0) / 3600
- self.fault_rate = round(
- float(self.fault_time) / (float(self.online_time) if self.online_time else 1) * 100, 2)
- self.fault_nums = real_dict['alarm_last_24_nums']
- self.idle_nums = real_dict['idle_count']
+
+
if response.status_code == 200:
result = response.json()
diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py
index 7c3ec50f..eb613c75 100644
--- a/sf_manufacturing/models/mrp_workorder.py
+++ b/sf_manufacturing/models/mrp_workorder.py
@@ -1197,8 +1197,8 @@ class ResMrpWorkOrder(models.Model):
if record.is_rework is False:
if not record.material_center_point:
raise UserError("坯料中心点为空,请检查")
- if record.X_deviation_angle <= 0:
- raise UserError("X偏差角度小于等于0,请检查!本次计算的X偏差角度为:%s" % record.X_deviation_angle)
+ # if record.X_deviation_angle <= 0:
+ # raise UserError("X偏差角度小于等于0,请检查!本次计算的X偏差角度为:%s" % record.X_deviation_angle)
record.process_state = '待加工'
# record.write({'process_state': '待加工'})
record.production_id.process_state = '待加工'
@@ -1826,6 +1826,11 @@ class WorkPieceDelivery(models.Model):
return is_free
else:
raise UserError("接驳站暂未反馈站点实时状态,请稍后再试")
+
+ def delivery_avg(self):
+ is_agv_task_dispatch = self.env['ir.config_parameter'].sudo().get_param('is_agv_task_dispatch')
+ if is_agv_task_dispatch:
+ self._delivery_avg()
# 配送至avg小车
def _delivery_avg(self):
@@ -1886,7 +1891,7 @@ class WorkPieceDelivery(models.Model):
logging.info('delivery_item-name:%s' % delivery_item.name)
delivery_item.write({
'task_delivery_time': fields.Datetime.now(),
- 'status': '待配送'
+ 'status': '已下发'
})
if delivery_item.type == "上产线":
delivery_item.workorder_id.write({'is_delivery': True})
diff --git a/sf_manufacturing/views/mrp_workorder_view.xml b/sf_manufacturing/views/mrp_workorder_view.xml
index c3e05739..d0577fbc 100644
--- a/sf_manufacturing/views/mrp_workorder_view.xml
+++ b/sf_manufacturing/views/mrp_workorder_view.xml
@@ -797,7 +797,7 @@
-
+
diff --git a/sf_message/data/template_data.xml b/sf_message/data/template_data.xml
index b3b453de..f4e342c4 100644
--- a/sf_message/data/template_data.xml
+++ b/sf_message/data/template_data.xml
@@ -66,8 +66,8 @@
-
-
+
+
调拨入库
stock.picking
@@ -75,13 +75,13 @@
markdown
normal
### 调拨入库通知:
-单号:调拨入库单[{{name}}]({{request_url}})
+单号:调拨入库单[{{name}}]({{transfer_inventory_special_url}})
事项:完成刀具物料上架入库
-
-
+
+
功能刀具寿命到期
sf.functional.tool.dismantle
@@ -89,13 +89,13 @@
markdown
normal
### 功能刀具寿命到期提醒:
-单号:拆解单[{{code}}]({{request_url}})
+单号:拆解单[{{code}}]({{tool_expired_remind_special_url}})
事项:{{functional_tool_id.tool_name_id.name}}寿命已到期,需拆解
-
-
+
+
功能刀具组装
sf.functional.tool.assembly
@@ -103,7 +103,7 @@
markdown
normal
### 功能刀具组装通知:
-单号:组装任务单[{{name}}]({{request_url}})
+单号:组装任务单[{{name}}]({{tool_assembly_special_url}})
事项:{{use_tool_time}}前完成组装
diff --git a/sf_message/models/sf_message_functional_tool_assembly.py b/sf_message/models/sf_message_functional_tool_assembly.py
index bf745b53..49ddc957 100644
--- a/sf_message/models/sf_message_functional_tool_assembly.py
+++ b/sf_message/models/sf_message_functional_tool_assembly.py
@@ -13,3 +13,14 @@ class SFMessagefunctionalToolAssembly(models.Model):
if obj.loading_task_source == '0' and obj.assemble_status == '0':
obj.add_queue('功能刀具组装')
return result
+
+
+ def get_special_url(self,id,tmplate_name,special_name,model_id):
+ menu_id = 0
+ action_id = 0
+ if tmplate_name=='调拨入库' and special_name== 'tool_assembly_special_url':
+ menu_id = self.env.ref('mrp.menu_mrp_root').id
+ action_id = self.env.ref('sf_tool_management.sf_functional_tool_assembly_view_act').id
+ return super(SFMessagefunctionalToolAssembly, self).get_url(id, menu_id, action_id,model_id)
+ else:
+ return super(SFMessagefunctionalToolAssembly, self).get_special_url(id, tmplate_name, special_name, model_id)
\ No newline at end of file
diff --git a/sf_message/models/sf_message_functional_tool_dismantle.py b/sf_message/models/sf_message_functional_tool_dismantle.py
index f6edb995..5f844064 100644
--- a/sf_message/models/sf_message_functional_tool_dismantle.py
+++ b/sf_message/models/sf_message_functional_tool_dismantle.py
@@ -17,3 +17,13 @@ class SFMessagefunctionalToolDismantle(models.Model):
if obj.dismantle_cause in ['寿命到期报废', '崩刀报废'] and obj.state == '待拆解':
obj.add_queue('功能刀具寿命到期')
return result
+
+ def get_special_url(self,id,tmplate_name,special_name,model_id):
+ menu_id = 0
+ action_id = 0
+ if tmplate_name=='调拨入库' and special_name== 'tool_expired_remind_special_url':
+ menu_id = self.env.ref('mrp.menu_mrp_root').id
+ action_id = self.env.ref('sf_tool_management.sf_functional_tool_dismantle_view_act').id
+ return super(SFMessagefunctionalToolDismantle, self).get_url(id, menu_id, action_id,model_id)
+ else:
+ return super(SFMessagefunctionalToolDismantle, self).get_special_url(id, tmplate_name, special_name, model_id)
\ No newline at end of file
diff --git a/sf_message/models/sf_message_stock_picking.py b/sf_message/models/sf_message_stock_picking.py
index 0b47de36..95786d93 100644
--- a/sf_message/models/sf_message_stock_picking.py
+++ b/sf_message/models/sf_message_stock_picking.py
@@ -73,7 +73,17 @@ class SFMessageStockPicking(models.Model):
contents.append(content)
return contents
else:
- return self.deal_stock_picking_sfp(message_queue_ids)
+ res = super(SFMessageStockPicking, self)._get_message(message_queue_id)
+ return res
+ def get_special_url(self,id,tmplate_name,special_name,model_id):
+ menu_id = 0
+ action_id = 0
+ if tmplate_name=='调拨入库' and special_name== 'transfer_inventory_special_url':
+ menu_id = self.env.ref('stock.menu_stock_root').id
+ action_id = self.env.ref('stock.action_picking_tree_ready').id
+ return super(SFMessageStockPicking, self).get_url(id, menu_id, action_id,model_id)
+ else:
+ return super(SFMessageStockPicking, self).get_special_url(id, tmplate_name, special_name, model_id)
def request_url(self):
url = self.env['ir.config_parameter'].get_param('web.base.url')
diff --git a/sf_plan/views/view.xml b/sf_plan/views/view.xml
index bfb584b8..f30cbb49 100644
--- a/sf_plan/views/view.xml
+++ b/sf_plan/views/view.xml
@@ -339,7 +339,7 @@
name="空料架配送"
sequence="11"
action="sf_manufacturing.sf_workpiece_delivery_empty_racks_act"
- groups="base.group_system"
+ groups="sf_base.group_sf_order_user,sf_base.group_sf_mrp_manager,sf_base.group_sf_equipment_user"
parent="mrp.menu_mrp_manufacturing"
/>
diff --git a/sf_tool_management/__manifest__.py b/sf_tool_management/__manifest__.py
index 97c26e40..8baa540f 100644
--- a/sf_tool_management/__manifest__.py
+++ b/sf_tool_management/__manifest__.py
@@ -6,7 +6,7 @@
'summary': '智能工厂刀具管理',
'sequence': 1,
'description': """
-在本模块,定义了主要的角色、菜单、基础业务对象
+在本模块,定义了刀具相关的模型和视图,以及相关的业务逻辑。
""",
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
diff --git a/tool_service/装夹自动保存检测文件服务/app.py b/tool_service/装夹自动保存检测文件服务/app.py
new file mode 100644
index 00000000..ba41f666
--- /dev/null
+++ b/tool_service/装夹自动保存检测文件服务/app.py
@@ -0,0 +1,160 @@
+from fastapi import FastAPI, HTTPException
+from pydantic import BaseModel
+import os
+from ftplib import FTP
+import win32gui
+import win32con
+import logging
+import time
+
+# 配置日志记录
+logging.basicConfig(filename='service.log', level=logging.INFO,
+ format='%(asctime)s - %(levelname)s - %(message)s')
+
+app = FastAPI()
+
+
+class FileUploadRequest(BaseModel):
+ filename: str
+
+
+# FTP 服务器配置信息
+ftp_host = '110.52.114.162'
+ftp_port = 10021
+ftp_user = 'ftpuser'
+ftp_password = '123456'
+ftp_directory = '/home/ftp/ftp_root/ThreeTest/XT/Before/'
+
+
+def find_child_window(parent_hwnd, class_name):
+ def enum_child_windows(hwnd, lparam):
+ class_name = win32gui.GetClassName(hwnd)
+ if class_name == lparam:
+ child_hwnds.append(hwnd)
+ return True
+
+ child_hwnds = []
+ win32gui.EnumChildWindows(parent_hwnd, enum_child_windows, class_name)
+ return child_hwnds
+
+
+def find_child_window_by_partial_title(parent_hwnd, partial_title):
+ def enum_child_windows(hwnd, lparam):
+ # 获取窗口的标题
+ title = win32gui.GetWindowText(hwnd)
+ # 检查标题是否包含指定的部分标题
+ if partial_title in title:
+ child_hwnds.append(hwnd)
+ return True
+
+ child_hwnds = []
+ win32gui.EnumChildWindows(parent_hwnd, enum_child_windows, None)
+ return child_hwnds
+
+
+def find_child_window_by_title(parent_hwnd, title):
+ def enum_child_windows(hwnd, lparam):
+ if win32gui.GetWindowText(hwnd) == lparam:
+ child_hwnds.append(hwnd)
+ return True
+
+ child_hwnds = []
+ win32gui.EnumChildWindows(parent_hwnd, enum_child_windows, title)
+ return child_hwnds
+
+
+def set_path_and_save(filename):
+ parent_hwnd = win32gui.FindWindow(None, '另存为')
+
+ if parent_hwnd == 0:
+ raise HTTPException(status_code=404, detail="没有找到保存报告的窗口,请检查!")
+
+ # 这里假设“地址:”是你需要的部分标题
+ address_hwnds = find_child_window_by_partial_title(parent_hwnd, "地址:")
+
+ # 确保找到的窗口句柄有效
+ if not address_hwnds:
+ raise HTTPException(status_code=404, detail="未找到地址框,请联系管理员!")
+
+ # 假设找到的第一个窗口是目标组件
+ address_hwnd = address_hwnds[0]
+ logging.info(f"找到地址框地址: {win32gui.GetWindowText(address_hwnd)}")
+
+ # 设置路径
+ local_file_path = os.path.join(win32gui.GetWindowText(address_hwnd).split(' ')[1], filename)
+ logging.info(f"设置路径: {local_file_path}")
+
+ path_hwnds = find_child_window(parent_hwnd, 'Edit')
+
+ if not path_hwnds:
+ raise HTTPException(status_code=404, detail="未找到路径框")
+
+ path_hwnd = path_hwnds[0]
+ win32gui.SendMessage(path_hwnd, win32con.WM_SETTEXT, 0, filename)
+
+ button_hwnds = find_child_window_by_title(parent_hwnd, '保存(&S)')
+
+ if not button_hwnds:
+ raise HTTPException(status_code=404, detail="未找到保存按钮")
+
+ save_button_hwnd = button_hwnds[0]
+ win32gui.PostMessage(save_button_hwnd, win32con.BM_CLICK, 0, 0)
+
+ return local_file_path
+
+
+def wait_for_file_to_save(filepath, timeout=30):
+ start_time = time.time()
+
+ while time.time() - start_time < timeout:
+ if os.path.isfile(filepath):
+ return True
+ time.sleep(0.1)
+
+ return False
+
+
+def upload_file_to_ftp(local_file):
+
+ if not os.path.isfile(local_file):
+ raise HTTPException(status_code=204, detail="文件未找到")
+
+ ftp = FTP()
+ try:
+ ftp.connect(ftp_host, ftp_port)
+ ftp.login(ftp_user, ftp_password)
+ ftp.cwd(ftp_directory)
+ with open(local_file, 'rb') as file:
+ ftp.storbinary(f'STOR {os.path.basename(local_file)}', file)
+ return True
+ except Exception as e:
+ print(f"文件上传失败: {e}")
+ return False
+ finally:
+ ftp.quit()
+
+
+@app.post("/get/check/report")
+async def upload_file(request: FileUploadRequest):
+ # 设置路径框并点击保存
+ local_file_path = set_path_and_save(request.filename)
+ logging.info(f"文件上传请求: {request.filename}")
+ logging.info(f"文件保存路径: {local_file_path}")
+
+ # 等待文件保存完成
+ if not wait_for_file_to_save(local_file_path):
+ raise HTTPException(status_code=500, detail="文件保存超时")
+
+ # 上传文件到 FTP
+ success = upload_file_to_ftp(local_file_path)
+ if success:
+ ftp_file_path = os.path.join(ftp_directory, request.filename)
+ return {"ftp_file_path": ftp_file_path}
+ else:
+ raise HTTPException(status_code=500, detail="文件上传失败")
+
+
+if __name__ == "__main__":
+ import uvicorn
+
+ uvicorn.run(app, host="0.0.0.0", port=8000)
diff --git a/tool_service/装夹自动保存检测文件服务/readme.md b/tool_service/装夹自动保存检测文件服务/readme.md
new file mode 100644
index 00000000..674b567e
--- /dev/null
+++ b/tool_service/装夹自动保存检测文件服务/readme.md
@@ -0,0 +1,5 @@
+app.py是主程序文件,主要功能是监听装夹自动保存文件,并将其上传到FTP服务器。
+
+需要讲app.py做成一个exe文件,可以用pyinstaller工具。
+
+然后在windows的计划任务中执行此exe文件即可。
\ No newline at end of file
diff --git a/tool_service/装夹自动保存检测文件服务/装夹检测文件自动保存操作流程.docx b/tool_service/装夹自动保存检测文件服务/装夹检测文件自动保存操作流程.docx
new file mode 100644
index 00000000..fef85556
Binary files /dev/null and b/tool_service/装夹自动保存检测文件服务/装夹检测文件自动保存操作流程.docx differ