Merge remote-tracking branch 'origin/develop' into feature/临时分支

# Conflicts:
#	sf_message/models/sf_message_stock_picking.py
This commit is contained in:
hujiaying
2024-10-10 15:54:07 +08:00
17 changed files with 259 additions and 41 deletions

View File

@@ -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

View File

@@ -16,7 +16,7 @@
<!-- hide 登录页面 powerd by odoo 及管理数据库 -->
<template id="login_page_layout" inherit_id="web.login_layout" name="Login Page Layout">
<xpath expr="//div[@class='card-body']/div[last()]" position="replace"></xpath>
<!-- <xpath expr="//div[@class='card-body']/div[last()]" position="replace"></xpath> -->
</template>
<!-- 隐藏odoo版本信息 -->

View File

@@ -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',

View File

@@ -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': [
# 定义权限组放在最上面
# 权限组

View File

@@ -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,

View File

@@ -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,12 +124,19 @@ class SfMaintenanceEquipmentOEE(models.Model):
real_dict = result_time['data'][self.equipment_code]
print('=', result_time)
if result_time['status'] == 1:
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.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(
@@ -140,6 +149,12 @@ class SfMaintenanceEquipmentOEE(models.Model):
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)
if response.status_code == 200:
result = response.json()

View File

@@ -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 = '待加工'
@@ -1827,6 +1827,11 @@ class WorkPieceDelivery(models.Model):
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):
config = self.env['res.config.settings'].get_values()
@@ -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})

View File

@@ -797,7 +797,7 @@
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
<!-- <field name="type" readonly="1"/>-->
<field name="feeder_station_destination_id" readonly="1" force_save="1"/>
<button name="button_delivery" type="object" string="配送" class="oe_highlight"/>
<button name="delivery_avg" type="object" string="配送" class="oe_highlight"/>
<button name="action_delivery_history" type="object" class="btn btn-link text-info" icon="fa-history"
string="历史"/>
</tree>

View File

@@ -66,8 +66,8 @@
<record id="template_transfer_inventory_remind" model="jikimo.message.template">
<field name="menu_id" ref="stock.menu_stock_root"/>
<field name="action_id" ref="stock.action_picking_tree_ready"/>
<!-- <field name="menu_id" ref="stock.menu_stock_root"/>-->
<!-- <field name="action_id" ref="stock.action_picking_tree_ready"/>-->
<field name="name">调拨入库</field>
<field name="model_id" ref="stock.model_stock_picking"/>
<field name="model">stock.picking</field>
@@ -75,13 +75,13 @@
<field name="msgtype">markdown</field>
<field name="urgency">normal</field>
<field name="content">### 调拨入库通知:
单号:调拨入库单[{{name}}]({{request_url}})
单号:调拨入库单[{{name}}]({{transfer_inventory_special_url}})
事项:完成刀具物料上架入库</field>
</record>
<record id="template_tool_expired_remind" model="jikimo.message.template">
<field name="menu_id" ref="mrp.menu_mrp_root"/>
<field name="action_id" ref="sf_tool_management.sf_functional_tool_dismantle_view_act"/>
<!-- <field name="menu_id" ref="mrp.menu_mrp_root"/>-->
<!-- <field name="action_id" ref="sf_tool_management.sf_functional_tool_dismantle_view_act"/>-->
<field name="name">功能刀具寿命到期</field>
<field name="model_id" ref="sf_tool_management.model_sf_functional_tool_dismantle"/>
<field name="model">sf.functional.tool.dismantle</field>
@@ -89,13 +89,13 @@
<field name="msgtype">markdown</field>
<field name="urgency">normal</field>
<field name="content">### 功能刀具寿命到期提醒:
单号:拆解单[{{code}}]({{request_url}})
单号:拆解单[{{code}}]({{tool_expired_remind_special_url}})
事项:{{functional_tool_id.tool_name_id.name}}寿命已到期,需拆解</field>
</record>
<record id="template_tool_assembly_remind" model="jikimo.message.template">
<field name="menu_id" ref="mrp.menu_mrp_root"/>
<field name="action_id" ref="sf_tool_management.sf_functional_tool_assembly_view_act"/>
<!-- <field name="menu_id" ref="mrp.menu_mrp_root"/>-->
<!-- <field name="action_id" ref="sf_tool_management.sf_functional_tool_assembly_view_act"/>-->
<field name="name">功能刀具组装</field>
<field name="model_id" ref="sf_tool_management.model_sf_functional_tool_assembly"/>
<field name="model">sf.functional.tool.assembly</field>
@@ -103,7 +103,7 @@
<field name="msgtype">markdown</field>
<field name="urgency">normal</field>
<field name="content">### 功能刀具组装通知:
单号:组装任务单[{{name}}]({{request_url}})
单号:组装任务单[{{name}}]({{tool_assembly_special_url}})
事项:{{use_tool_time}}前完成组装</field>
</record>
<record id="template_production_completed_remind" model="jikimo.message.template">

View File

@@ -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)

View File

@@ -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)

View File

@@ -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')

View File

@@ -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"
/>
<!-- <menuitem -->

View File

@@ -6,7 +6,7 @@
'summary': '智能工厂刀具管理',
'sequence': 1,
'description': """
在本模块,定义了主要的角色、菜单、基础业务对象
在本模块,定义了刀具相关的模型和视图,以及相关的业务逻辑。
""",
'category': 'sf',
'website': 'https://www.sf.jikimo.com',

View File

@@ -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)

View File

@@ -0,0 +1,5 @@
app.py是主程序文件主要功能是监听装夹自动保存文件并将其上传到FTP服务器。
需要讲app.py做成一个exe文件可以用pyinstaller工具。
然后在windows的计划任务中执行此exe文件即可。