Compare commits

...

51 Commits

Author SHA1 Message Date
mgw
6a920be6d1 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-04-25 11:34:51 +08:00
mgw
3811079a7f 调整alarm_start_time为时间戳 2025-04-25 11:34:34 +08:00
mgw
ad8e0b6af0 调整当日故障时长取值 2025-04-25 11:25:38 +08:00
廖丹龙
04cb910803 Accept Merge Request #2060: (feature/tool_standard_library_process -> develop)
Merge Request: 【sf.t-计划】在排程单详情页选择时间段进行排程时未校验日历有效工作时间

Created By: @廖丹龙
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2060
2025-04-25 11:18:43 +08:00
liaodanlong
42292818af Merge branch 'refs/heads/develop' into feature/tool_standard_library_process
# Conflicts:
#	sf_plan/models/custom_plan.py
2025-04-25 11:17:48 +08:00
liaodanlong
bcafd9cf38 【sf.t-计划】在排程单详情页选择时间段进行排程时未校验日历有效工作时间 2025-04-25 11:16:23 +08:00
mgw
12ebd87f1d Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-04-25 10:14:00 +08:00
mgw
bdef852b98 调整当日在线时长取值为今天之前最后一次开机时长 2025-04-25 10:13:42 +08:00
胡尧
1d5fb747d4 Accept Merge Request #2059: (feature/6609 -> develop)
Merge Request: 解决更新出现字段未找到的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2059?initial=true
2025-04-25 10:09:21 +08:00
胡尧
8116e4f97d 解决更新出现字段未找到的问题 2025-04-25 10:08:03 +08:00
mgw
e3e5fcc378 调整当日故障时长 2025-04-25 09:42:45 +08:00
胡尧
879b5492db Accept Merge Request #2058: (feature/6609 -> develop)
Merge Request: 产品详情增加只有成品类型才显示模型ID

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2058
2025-04-25 09:19:17 +08:00
胡尧
27b9a4f982 解决冲突 2025-04-25 09:19:00 +08:00
胡尧
94007bae2b 产品详情增加只有成品类型才显示模型ID 2025-04-25 09:16:40 +08:00
mgw
bf92028027 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-04-24 17:27:40 +08:00
mgw
2b47e566d3 调整周期字段为当日0时间 2025-04-24 17:27:20 +08:00
胡尧
5aa2f1aa18 Accept Merge Request #2057: (feature/6694 -> develop)
Merge Request: 解决排程报错的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2057?initial=true
2025-04-24 17:08:10 +08:00
胡尧
b7128ba81a 解决排程报错的问题 2025-04-24 17:07:30 +08:00
胡尧
49546f9d08 Accept Merge Request #2056: (feature/6694 -> develop)
Merge Request: 修改参数类型判断

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2056?initial=true
2025-04-24 15:13:40 +08:00
胡尧
6959bd9a09 修改参数类型判断 2025-04-24 15:13:21 +08:00
禹翔辉
3a2babf2d5 Accept Merge Request #2055: (feature/工单排序_1 -> develop)
Merge Request: Merge branch 'feature/工单排序' into feature/工单排序_1

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2055
2025-04-24 15:11:23 +08:00
yuxianghui
d7d094c84d Merge branch 'feature/工单排序' into feature/工单排序_1 2025-04-24 15:05:57 +08:00
yuxianghui
a06e24583d 添加工单先按工序排序,再按创建时间排序 2025-04-24 15:04:04 +08:00
胡尧
0cbd830901 Accept Merge Request #2053: (feature/6694 -> develop)
Merge Request: 修改模型id为模型ID,增加机台判断

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2053?initial=true
2025-04-24 14:59:12 +08:00
胡尧
4b29def105 修改模型id为模型ID,增加机台判断 2025-04-24 14:58:50 +08:00
胡尧
582abb3f2e Accept Merge Request #2052: (feature/6694 -> develop)
Merge Request: 去掉产品form重复的字段

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2052?initial=true
2025-04-24 14:38:07 +08:00
胡尧
40137ba69c 去掉产品form重复的字段 2025-04-24 14:31:22 +08:00
胡尧
804f6a82b4 Accept Merge Request #2051: (feature/6694 -> develop)
Merge Request: 销售订单,制造订单视图中加入模型id

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2051?initial=true
2025-04-24 14:15:58 +08:00
胡尧
d16d47dfbe 销售订单,制造订单视图中加入模型id 2025-04-24 14:13:10 +08:00
胡尧
41cf9d5474 Accept Merge Request #2050: (feature/6694 -> develop)
Merge Request: 修改扫码报错信息

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2050?initial=true
2025-04-24 11:42:52 +08:00
胡尧
59aa6b4f10 修改扫码报错信息 2025-04-24 11:42:16 +08:00
胡尧
a759106fdc Accept Merge Request #2049: (feature/6694 -> develop)
Merge Request: 调整打印代码结构

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2049?initial=true
2025-04-24 11:08:45 +08:00
胡尧
8bb101c6b2 调整打印代码结构 2025-04-24 11:08:02 +08:00
胡尧
f02044b513 Accept Merge Request #2048: (feature/6694 -> develop)
Merge Request: 调整机台二维码使用A4打印机

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2048?initial=true
2025-04-24 09:59:29 +08:00
胡尧
3d937b85c9 调整机台二维码使用A4打印机 2025-04-24 09:59:05 +08:00
胡尧
5a61b3b459 调整机台二维码使用A4打印机 2025-04-24 09:58:27 +08:00
胡尧
afccb5ee6a Accept Merge Request #2047: (feature/6694 -> develop)
Merge Request: 修改机台二维码为ID

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2047?initial=true
2025-04-24 09:19:13 +08:00
胡尧
2b0648d9bc 修改机台二维码为ID 2025-04-24 09:18:49 +08:00
胡尧
8ea3487044 Accept Merge Request #2046: (feature/6694 -> develop)
Merge Request: 增加import

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2046?initial=true
2025-04-24 08:59:38 +08:00
胡尧
b24ed5fe4c 增加import 2025-04-24 08:59:07 +08:00
胡尧
b801b265c3 Accept Merge Request #2045: (feature/6694 -> develop)
Merge Request: 增加日志界面

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2045?initial=true
2025-04-23 17:51:41 +08:00
胡尧
27a67167fe 增加日志界面 2025-04-23 17:51:09 +08:00
胡尧
8fa9534b4e 增加接口请求日志,修改报工接口获取机台id 2025-04-23 17:39:42 +08:00
胡尧
db745e46b6 Accept Merge Request #2044: (feature/6609 -> develop)
Merge Request: 解决从制造订单跳转到产品无法显示模型id的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2044?initial=true
2025-04-23 16:59:51 +08:00
胡尧
f598b6c71c 解决从制造订单跳转到产品无法显示模型id的问题 2025-04-23 16:58:59 +08:00
胡尧
dccb0b3fb0 Accept Merge Request #2043: (feature/6763 -> develop)
Merge Request: 修改机台二维码为机台ID

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2043?initial=true
2025-04-23 16:16:50 +08:00
胡尧
83feb78f43 修改机台二维码为机台ID 2025-04-23 16:16:25 +08:00
胡尧
ac09794b10 Accept Merge Request #2042: (feature/6763 -> develop)
Merge Request: 解决模块更新报错问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2042?initial=true
2025-04-23 16:00:13 +08:00
胡尧
3f88b11a18 解决模块更新报错问题 2025-04-23 15:59:37 +08:00
胡尧
b97acfb181 Accept Merge Request #2041: (feature/6684 -> develop)
Merge Request: 取消线上解决bug的改动

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2041?initial=true
2025-04-23 14:24:00 +08:00
胡尧
bff0ff9401 增加日志 2025-04-23 10:18:49 +08:00
34 changed files with 385 additions and 135 deletions

View File

@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import models

View File

@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
{
'name': '机企猫 打印模块',
'version': '1.0',
'summary': """ 包含机台二维码,程序单打印等 """,
'author': '机企猫',
'website': 'https://www.jikimo.com',
'category': '机企猫',
'depends': ['sf_manufacturing', 'sf_maintenance', 'base_report_to_printer'],
'data': [
'views/maintenance_views.xml',
],
'application': True,
'installable': True,
'auto_install': False,
'license': 'LGPL-3',
}

View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
from . import jikimo_printing
from . import maintenance_printing
from . import workorder_printing

View File

@@ -0,0 +1,56 @@
from io import BytesIO
import qrcode
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from PIL import Image
from reportlab.lib.utils import ImageReader
from odoo import models, fields, api
class JikimoPrinting(models.AbstractModel):
_name = 'jikimo.printing'
def print_qr_code(self, data):
"""
打印二维码
"""
# 生成二维码
qr = qrcode.QRCode(version=1, box_size=10, border=5)
qr.add_data(data)
qr.make(fit=True)
qr_image = qr.make_image(fill_color="black", back_color="white")
# 将PIL Image转换为reportlab可用的格式
temp_image = BytesIO()
qr_image.save(temp_image, format="PNG")
temp_image.seek(0)
# 创建PDF
pdf_buffer = BytesIO()
c = canvas.Canvas(pdf_buffer, pagesize=A4)
# 计算位置
a4_width, a4_height = A4
qr_width = 200
qr_height = 200
x = (a4_width - qr_width) / 2
y = (a4_height - qr_height) / 2
# 直接从BytesIO绘制图片
c.drawImage(ImageReader(Image.open(temp_image)), x, y, width=qr_width, height=qr_height)
c.save()
# 获取PDF内容并打印
pdf_content = pdf_buffer.getvalue()
printer = self.env['printing.printer'].get_default()
printer.print_document(report=None, content=pdf_content, doc_format='pdf')
# 清理资源
pdf_buffer.close()
temp_image.close()
def print_pdf(self, pdf_data):
"""
打印PDF
"""
printer = self.env['printing.printer'].get_default()
printer.print_document(report=None, content = pdf_data, doc_format='pdf')

View File

@@ -0,0 +1,69 @@
from odoo import models, fields, api
class MaintenancePrinting(models.Model):
_inherit = 'maintenance.equipment'
def print_single_method(self):
print('self.name========== %s' % self.name)
self.ensure_one()
# maintenance_equipment_id = self.id
# # host = "192.168.50.110" # 可以根据实际情况修改
# # port = 9100 # 可以根据实际情况修改
# # 获取默认打印机配置
# 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
# port = printer_config.printer_id.port
# self.print_qr_code(maintenance_equipment_id, host, port)
# 切换成A4打印机
try:
self.env['jikimo.printing'].print_qr_code(self.id)
except Exception as e:
raise UserError(f"打印失败: {str(e)}")
# def generate_zpl_code(self, code):
# """生成ZPL代码用于打印二维码标签
# Args:
# code: 需要编码的内容
# Returns:
# str: ZPL指令字符串
# """
# zpl_code = "^XA\n" # 开始ZPL格式
# # 设置打印参数
# zpl_code += "^LH0,0\n" # 设置标签起始位置
# zpl_code += "^CI28\n" # 设置中文编码
# zpl_code += "^PW400\n" # 设置打印宽度为400点
# zpl_code += "^LL300\n" # 设置标签长度为300点
# # 打印标题
# zpl_code += "^FO10,20\n" # 设置标题位置
# zpl_code += "^A0N,30,30\n" # 设置字体大小
# zpl_code += "^FD机床二维码^FS\n" # 打印标题文本
# # 打印二维码
# zpl_code += "^FO50,60\n" # 设置二维码位置
# zpl_code += f"^BQN,2,8\n" # 设置二维码参数:模式2,放大倍数8
# zpl_code += f"^FDLA,{code}^FS\n" # 二维码内容
# # 打印编码文本
# zpl_code += "^FO50,220\n" # 设置编码文本位置
# zpl_code += "^A0N,25,25\n" # 设置字体大小
# zpl_code += f"^FD编码: {code}^FS\n" # 打印编码文本
# # 打印日期
# zpl_code += "^FO50,260\n"
# zpl_code += "^A0N,20,20\n"
# zpl_code += f"^FD打印日期: {fields.Date.today()}^FS\n"
# zpl_code += "^PQ1\n" # 打印1份
# zpl_code += "^XZ\n" # 结束ZPL格式
# return zpl_code

View File

@@ -1,8 +1,6 @@
import logging import logging
from io import BytesIO
from odoo import models, fields, api from odoo import models, fields, api
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@@ -21,9 +19,7 @@ class MrpWorkorder(models.Model):
if pdf_data: if pdf_data:
try: try:
# 执行打印 # 执行打印
printer = self.env['printing.printer'].get_default() self.env['jikimo.printing'].print_pdf(pdf_data)
printer.print_document(report=None, content = pdf_data, doc_format='pdf')
wo.production_id.product_id.is_print_program = True wo.production_id.product_id.is_print_program = True
_logger.info(f"工单 {wo.name} 的PDF已成功打印") _logger.info(f"工单 {wo.name} 的PDF已成功打印")

View File

@@ -0,0 +1,19 @@
<?xml version="1.0"?>
<odoo>
<record id="sf_maintenance_equipment_view_form_qrcode_print" model="ir.ui.view">
<field name="name">sf_equipment.form</field>
<field name="model">maintenance.equipment</field>
<field name="inherit_id" ref="sf_maintenance.sf_hr_equipment_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='qr_code_image']" position="after">
<label for="print_single_method"/>
<div class="col-12 col-lg-6 o_setting_box" style="white-space: nowrap">
<button type="object" class="oe_highlight" name='print_single_method' string="打印机床二维码"
attrs="{'invisible': [('equipment_type', '!=', '机床')]}"/>
</div>
</xpath>
</field>
</record>
</odoo>

View File

@@ -8,7 +8,6 @@
'category': 'sf', 'category': 'sf',
'depends': ['base', 'sf_maintenance', 'jikimo_mini_program'], 'depends': ['base', 'sf_maintenance', 'jikimo_mini_program'],
'data': [ 'data': [
], ],
'application': True, 'application': True,

View File

@@ -2,20 +2,30 @@ import json
from odoo import http from odoo import http
from odoo.http import request from odoo.http import request
from odoo.addons.sf_machine_connect.models.ftp_operate import transfer_nc_files from odoo.addons.sf_machine_connect.models.ftp_operate import transfer_nc_files
from odoo.addons.sf_base.decorators.api_log import api_log
class MainController(http.Controller): class MainController(http.Controller):
@http.route('/api/manual_download_program', type='json', methods=['POST'], auth='public', cors='*') @http.route('/api/manual_download_program', type='json', methods=['POST'], auth='public', cors='*')
@api_log('人工线下加工编程文件传输', requester='报工系统')
def manual_download_program(self): def manual_download_program(self):
""" """
人工线下加工传输编程文件 人工线下加工传输编程文件
""" """
data = json.loads(request.httprequest.data) data = json.loads(request.httprequest.data)
maintenance_equipment_name = data.get('maintenance_equipment_name') maintenance_equipment_id = data.get('maintenance_equipment_id')
model_id = data.get('model_id') model_id = data.get('model_id')
if not maintenance_equipment_name or not model_id: if not maintenance_equipment_id or not model_id:
return {'code': 400, 'message': '参数错误'} return {'code': 400, 'message': '参数错误'}
maintenance_equipment = request.env['maintenance.equipment'].sudo().search([('name', '=', maintenance_equipment_name)], limit=1) try:
maintenance_equipment_id = int(maintenance_equipment_id)
model_id = int(model_id)
except Exception as e:
return {'code': 400, 'message': '参数类型错误'}
maintenance_equipment = request.env['maintenance.equipment'].sudo().search(
[('id', '=', maintenance_equipment_id), ('category_id.equipment_type', '=', '机床')],
limit=1
)
if not maintenance_equipment: if not maintenance_equipment:
return {'code': 400, 'message': '机台不存在,请扫描正确的机台二维码'} return {'code': 400, 'message': '机台不存在,请扫描正确的机台二维码'}
product = request.env['product.template'].sudo().search([('model_id', '=', model_id)], limit=1) product = request.env['product.template'].sudo().search([('model_id', '=', model_id)], limit=1)

View File

@@ -1,3 +1,4 @@
from . import models from . import models
from . import commons from . import commons
from . import controllers from . import controllers
from . import decorators

View File

@@ -25,7 +25,7 @@
'views/menu_fixture_view.xml', 'views/menu_fixture_view.xml',
'views/change_base_view.xml', 'views/change_base_view.xml',
'views/Printer.xml', 'views/Printer.xml',
'views/api_log_views.xml',
], ],
'demo': [ 'demo': [
], ],

View File

@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import logging import logging
import json import json
import base64 import logging
from odoo import http from odoo import http
from odoo.http import request from odoo.http import request
_logger = logging.getLogger(__name__)
class Manufacturing_Connect(http.Controller): class Manufacturing_Connect(http.Controller):

View File

@@ -0,0 +1 @@
from . import api_log

View File

@@ -0,0 +1,59 @@
import functools
import json
import logging
from datetime import datetime
from odoo.http import request
_logger = logging.getLogger(__name__)
def api_log(name=None, requester=None):
"""记录API请求日志的装饰器"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = datetime.now()
# 获取请求信息
try:
# 获取请求数据
request_data = json.loads(request.httprequest.data) if request.httprequest.data else {}
# 获取请求路径
path = request.httprequest.path
# 获取请求方法
method = request.httprequest.method
# 获取客户端IP
remote_addr = request.httprequest.remote_addr
# 执行原始函数
result = func(*args, **kwargs)
# 计算响应时间
end_time = datetime.now()
response_time = (end_time - start_time).total_seconds()
# 创建日志记录
log_vals = {
'name': name or func.__name__,
'path': path,
'method': method,
'request_data': json.dumps(request_data, ensure_ascii=False),
'response_data': json.dumps(result, ensure_ascii=False),
'remote_addr': remote_addr,
'response_time': response_time,
'status': result.get('code', 500),
'requester': requester,
'responser': '智能工厂'
}
# 异步创建日志记录
request.env['api.request.log'].sudo().with_context(tracking_disable=True).create(log_vals)
return result
except Exception as e:
_logger.error(f"API日志记录失败: {str(e)}")
# 即使日志记录失败,也要返回原始结果
return func(*args, **kwargs)
return wrapper
return decorator

View File

@@ -6,3 +6,4 @@ from . import functional_fixture
from . import tool_other_features from . import tool_other_features
from . import basic_parameters_fixture from . import basic_parameters_fixture
from . import ir_sequence from . import ir_sequence
from . import api_log

18
sf_base/models/api_log.py Normal file
View File

@@ -0,0 +1,18 @@
from odoo import models, fields, api
class ApiRequestLog(models.Model):
_name = 'api.request.log'
_description = '接口请求日志'
_order = 'id desc'
name = fields.Char('接口名称')
path = fields.Char('请求路径')
method = fields.Char('请求方法')
request_data = fields.Text('请求数据')
response_data = fields.Text('响应数据')
remote_addr = fields.Char('客户端IP')
response_time = fields.Float('响应时间(秒)', digits=(16, 6))
status = fields.Integer('状态码')
requester = fields.Char('请求方')
responser = fields.Char('响应方')

View File

@@ -254,3 +254,6 @@ access_sf_machining_accuracy_admin,sf_machining_accuracy_admin,model_sf_machinin
access_sf_embryo_redundancy,sf_embryo_redundancy,model_sf_embryo_redundancy,base.group_user,1,0,0,0 access_sf_embryo_redundancy,sf_embryo_redundancy,model_sf_embryo_redundancy,base.group_user,1,0,0,0
access_sf_embryo_redundancy_admin,sf_embryo_redundancy_admin,model_sf_embryo_redundancy,base.group_system,1,0,0,0 access_sf_embryo_redundancy_admin,sf_embryo_redundancy_admin,model_sf_embryo_redundancy,base.group_system,1,0,0,0
access_api_request_log_user,api.request.log.user,model_api_request_log,base.group_user,1,0,0,0
access_api_request_log_admin,api.request.log.admin,model_api_request_log,base.group_system,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
254
255
256
257
258
259

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_api_request_log_tree" model="ir.ui.view">
<field name="name">api.request.log.tree</field>
<field name="model">api.request.log</field>
<field name="arch" type="xml">
<tree>
<field name="create_date"/>
<field name="name"/>
<field name="path"/>
<field name="method"/>
<field name="remote_addr"/>
<field name="response_time"/>
<field name="status"/>
</tree>
</field>
</record>
<record id="view_api_request_log_form" model="ir.ui.view">
<field name="name">api.request.log.form</field>
<field name="model">api.request.log</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<group>
<field name="name"/>
<field name="path"/>
<field name="method"/>
<field name="remote_addr"/>
</group>
<group>
<field name="response_time"/>
<field name="status"/>
<field name="create_date" string="请求时间"/>
</group>
</group>
<notebook>
<page string="请求数据">
<field name="request_data"/>
</page>
<page string="响应数据">
<field name="response_data"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="action_api_request_log" model="ir.actions.act_window">
<field name="name">API请求日志</field>
<field name="res_model">api.request.log</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="menu_api_request_log"
name="API请求日志"
parent="base.next_id_9"
action="action_api_request_log"
sequence="100"/>
</odoo>

View File

@@ -1298,7 +1298,7 @@ class Sf_Dashboard_Connect(http.Controller):
conn = psycopg2.connect(**db_config) conn = psycopg2.connect(**db_config)
# 获取请求的机床数据 # 获取请求的机床数据
machine_list = ast.literal_eval(kw['machine_list']) machine_list = ast.literal_eval(kw['machine_list'])
time_threshold = datetime.now() - timedelta(days=1) time_threshold = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
alarm_last_24_time = 0.0 alarm_last_24_time = 0.0
alarm_all_time = 0.0 alarm_all_time = 0.0
@@ -1327,8 +1327,8 @@ class Sf_Dashboard_Connect(http.Controller):
cur.execute(""" cur.execute("""
SELECT * FROM device_data SELECT * FROM device_data
WHERE device_name = %s WHERE device_name = %s
AND device_state in ('待机', '警告', '运行中') AND time >= %s AND process_time IS NOT NULL AND device_state in ('待机', '警告', '运行中') AND time <= %s AND process_time IS NOT NULL
ORDER BY time ASC ORDER BY time DESC
LIMIT 1; LIMIT 1;
""", (item, time_threshold)) """, (item, time_threshold))
last_24_time = fetch_result_as_dict(cur) last_24_time = fetch_result_as_dict(cur)
@@ -1348,20 +1348,21 @@ class Sf_Dashboard_Connect(http.Controller):
alarm_last_24_nums = [] alarm_last_24_nums = []
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute(""" cur.execute("""
SELECT DISTINCT ON (alarm_start_time) alarm_time, alarm_start_time SELECT DISTINCT ON (alarm_start_time, alarm_time) alarm_time, alarm_start_time
FROM device_data FROM device_data
WHERE device_name = %s WHERE device_name = %s
AND alarm_start_time IS NOT NULL AND time >= %s; AND alarm_start_time IS NOT NULL AND alarm_start_time::timestamp >= %s;
""", (item, time_threshold)) """, (item, time_threshold))
results = cur.fetchall() results = cur.fetchall()
logging.info("results============:%s" % results)
for result in results: for result in results:
alarm_last_24_nums.append(result[1]) alarm_last_24_nums.append(result[1])
if result[0]: if result[0]:
if float(result[0]) >= 28800: if float(result[0]) >= 28800:
continue continue
alarm_last_24_time += float(result[0]) alarm_last_24_time = float(result[0])
else: else:
alarm_last_24_time += 0.0 alarm_last_24_time = 0.0
alarm_all_nums = [] alarm_all_nums = []
with conn.cursor() as cur: with conn.cursor() as cur:

View File

@@ -4,4 +4,3 @@ from . import sf_maintenance_oee
from . import sf_maintenance_logs from . import sf_maintenance_logs
from . import sf_equipment_maintenance_standards from . import sf_equipment_maintenance_standards
from . import sf_maintenance_requests from . import sf_maintenance_requests
from . import maintenance_printer

View File

@@ -1,92 +0,0 @@
import qrcode
import base64
from io import BytesIO
from odoo import models, fields, api
class MaintenanceEquipment(models.Model):
_name = 'maintenance.equipment'
_inherit = ['maintenance.equipment', 'printing.utils']
qr_code_image = fields.Binary(string='二维码', compute='_generate_qr_code')
@api.depends('name')
def _generate_qr_code(self):
for record in self:
# Generate QR code
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10,
border=4,
)
qr.add_data(record.name)
qr.make(fit=True)
qr_image = qr.make_image(fill_color="black", back_color="white")
# Encode the image data in base64
image_stream = BytesIO()
qr_image.save(image_stream, format="PNG")
encoded_image = base64.b64encode(image_stream.getvalue())
record.qr_code_image = encoded_image
def print_single_method(self):
print('self.name========== %s' % self.name)
self.ensure_one()
qr_code_data = self.qr_code_image
if not qr_code_data:
raise UserError("没有找到二维码数据。")
maintenance_equipment_name = self.name
# host = "192.168.50.110" # 可以根据实际情况修改
# port = 9100 # 可以根据实际情况修改
# 获取默认打印机配置
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
port = printer_config.printer_id.port
self.print_qr_code(maintenance_equipment_name, host, port)
def generate_zpl_code(self, code):
"""生成ZPL代码用于打印二维码标签
Args:
code: 需要编码的内容
Returns:
str: ZPL指令字符串
"""
zpl_code = "^XA\n" # 开始ZPL格式
# 设置打印参数
zpl_code += "^LH0,0\n" # 设置标签起始位置
zpl_code += "^CI28\n" # 设置中文编码
zpl_code += "^PW400\n" # 设置打印宽度为400点
zpl_code += "^LL300\n" # 设置标签长度为300点
# 打印标题
zpl_code += "^FO10,20\n" # 设置标题位置
zpl_code += "^A0N,30,30\n" # 设置字体大小
zpl_code += "^FD机床二维码^FS\n" # 打印标题文本
# 打印二维码
zpl_code += "^FO50,60\n" # 设置二维码位置
zpl_code += f"^BQN,2,8\n" # 设置二维码参数:模式2,放大倍数8
zpl_code += f"^FDLA,{code}^FS\n" # 二维码内容
# 打印编码文本
zpl_code += "^FO50,220\n" # 设置编码文本位置
zpl_code += "^A0N,25,25\n" # 设置字体大小
zpl_code += f"^FD编码: {code}^FS\n" # 打印编码文本
# 打印日期
zpl_code += "^FO50,260\n"
zpl_code += "^A0N,20,20\n"
zpl_code += f"^FD打印日期: {fields.Date.today()}^FS\n"
zpl_code += "^PQ1\n" # 打印1份
zpl_code += "^XZ\n" # 结束ZPL格式
return zpl_code

View File

@@ -2,6 +2,8 @@
import json import json
import base64 import base64
import logging import logging
import qrcode
from io import BytesIO
from datetime import timedelta from datetime import timedelta
import requests import requests
from odoo.addons.sf_base.commons.common import Common from odoo.addons.sf_base.commons.common import Common
@@ -831,6 +833,29 @@ class SfMaintenanceEquipment(models.Model):
ftp_username = fields.Char('FTP 用户名') ftp_username = fields.Char('FTP 用户名')
ftp_password = fields.Char('FTP 密码') ftp_password = fields.Char('FTP 密码')
qr_code_image = fields.Binary(string='二维码', compute='_generate_qr_code')
@api.depends('name')
def _generate_qr_code(self):
for record in self:
# Generate QR code
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10,
border=4,
)
qr.add_data(record.id)
qr.make(fit=True)
qr_image = qr.make_image(fill_color="black", back_color="white")
# Encode the image data in base64
image_stream = BytesIO()
qr_image.save(image_stream, format="PNG")
encoded_image = base64.b64encode(image_stream.getvalue())
record.qr_code_image = encoded_image
class SfRobotAxisNum(models.Model): class SfRobotAxisNum(models.Model):
_name = 'sf.robot.axis.num' _name = 'sf.robot.axis.num'

View File

@@ -1055,11 +1055,6 @@
<xpath expr="//group/field[@name='location']" position="after"> <xpath expr="//group/field[@name='location']" position="after">
<field name="qr_code_image" widget="image" readonly="1" attrs="{'invisible': [('equipment_type', '!=', '机床')]}" /> <field name="qr_code_image" widget="image" readonly="1" attrs="{'invisible': [('equipment_type', '!=', '机床')]}" />
<label for="print_single_method"/>
<div class="col-12 col-lg-6 o_setting_box" style="white-space: nowrap">
<button type="object" class="oe_highlight" name='print_single_method' string="打印机床二维码"
attrs="{'invisible': [('equipment_type', '!=', '机床')]}"/>
</div>
</xpath> </xpath>
<xpath expr="//page[@name='maintenance']" position="after"> <xpath expr="//page[@name='maintenance']" position="after">
<page name="network_config" string="网络配置" attrs="{'invisible': [('equipment_type', '!=', '机床')]}" > <page name="network_config" string="网络配置" attrs="{'invisible': [('equipment_type', '!=', '机床')]}" >

View File

@@ -18,4 +18,3 @@ from . import quick_easy_order
from . import purchase_order from . import purchase_order
from . import quality_check from . import quality_check
from . import purchase_request_line from . import purchase_request_line
from . import workorder_printer

View File

@@ -1821,7 +1821,7 @@ class MrpProduction(models.Model):
logging.info('update_programming_state error:%s' % e) logging.info('update_programming_state error:%s' % e)
raise UserError("更新编程单状态失败,请联系管理员") raise UserError("更新编程单状态失败,请联系管理员")
model_id = fields.Char('模型id', related='product_id.model_id') model_id = fields.Char('模型ID', related='product_id.model_id')
# 编程记录 # 编程记录

View File

@@ -1814,7 +1814,7 @@ class ResMrpWorkOrder(models.Model):
lazy=lazy lazy=lazy
) )
model_id = fields.Char('模型id', related='production_id.model_id') model_id = fields.Char('模型ID', related='production_id.model_id')
class CNCprocessing(models.Model): class CNCprocessing(models.Model):

View File

@@ -787,7 +787,7 @@ class ResProductMo(models.Model):
glb_url = fields.Char('glb文件地址') glb_url = fields.Char('glb文件地址')
area = fields.Float('表面积(m²)') area = fields.Float('表面积(m²)')
auto_machining = fields.Boolean('自动化加工(模型识别)', default=False) auto_machining = fields.Boolean('自动化加工(模型识别)', default=False)
model_id = fields.Char('模型id') model_id = fields.Char('模型ID')
@api.depends('name') @api.depends('name')

View File

@@ -110,12 +110,12 @@
</xpath> </xpath>
<xpath expr="//sheet//group//group[2]//label" position="before"> <xpath expr="//sheet//group//group[2]//label" position="before">
<!-- <field name="process_state"/> --> <!-- <field name="process_state"/> -->
<field name="production_type" readonly="1"/>
<field name="state" readonly="1"/> <field name="state" readonly="1"/>
<!-- <field name="process_state"/> --> <!-- <field name="process_state"/> -->
</xpath> </xpath>
<xpath expr="//sheet//group//group//div[3]" position="after"> <xpath expr="//sheet//group//group//div[3]" position="after">
<field name="production_type" readonly="1"/>
<field name="production_product_type" invisible="1"/> <field name="production_product_type" invisible="1"/>
<field name="manual_quotation" readonly="1" <field name="manual_quotation" readonly="1"
attrs="{'invisible': ['|', ('production_type', 'not in', ['自动化产线加工', '人工线下加工']), ('production_product_type', '!=', '成品')]}"/> attrs="{'invisible': ['|', ('production_type', 'not in', ['自动化产线加工', '人工线下加工']), ('production_product_type', '!=', '成品')]}"/>
@@ -455,7 +455,7 @@
<field name="inherit_id" ref="mrp.mrp_production_workorder_tree_editable_view"/> <field name="inherit_id" ref="mrp.mrp_production_workorder_tree_editable_view"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//tree" position="attributes"> <xpath expr="//tree" position="attributes">
<attribute name="default_order">create_date desc</attribute> <attribute name="default_order">sequence,create_date desc</attribute>
<attribute name="decoration-warning">delivery_warning == 'warning'</attribute> <attribute name="decoration-warning">delivery_warning == 'warning'</attribute>
<attribute name="decoration-danger">delivery_warning == 'overdue'</attribute> <attribute name="decoration-danger">delivery_warning == 'overdue'</attribute>
</xpath> </xpath>

View File

@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<!-- 工作中心看板 --> <!-- 工作中心看板 -->
<record id="mrp_production_view_form_inherit_maintenance" model="ir.ui.view"> <!-- <record id="mrp_production_view_form_inherit_maintenance" model="ir.ui.view">
<field name="name">mrp.production.view.form.inherit.maintenance</field> <field name="name">mrp.production.view.form.inherit.maintenance</field>
<field name="model">mrp.production</field> <field name="model">mrp.production</field>
<field name="inherit_id" ref="mrp.mrp_production_form_view"/> <field name="inherit_id" ref="mrp.mrp_production_form_view"/>
<field name="arch" type="xml"> <field name="arch" type="xml"> -->
<!-- <button name="action_cancel" position="before"> --> <!-- <button name="action_cancel" position="before"> -->
<!-- <button name="button_maintenance_req" type="object" string="维修请求"/> --> <!-- <button name="button_maintenance_req" type="object" string="维修请求"/> -->
<!-- </button> --> <!-- </button> -->
<div name="button_box" position="inside"> <!-- <div name="button_box" position="inside">
<button name="open_maintenance_request_mo" type="object" class="oe_stat_button" icon="fa-wrench" <button name="open_maintenance_request_mo" type="object" class="oe_stat_button" icon="fa-wrench"
attrs="{'invisible': [('maintenance_count', '=', 0)]}" attrs="{'invisible': [('maintenance_count', '=', 0)]}"
context="{'search_default_production_id': active_id}"> context="{'search_default_production_id': active_id}">
@@ -22,7 +22,7 @@
</button> </button>
</div> </div>
</field> </field>
</record> </record> -->
<record id="custom_model_form_view_inherit" model="ir.ui.view"> <record id="custom_model_form_view_inherit" model="ir.ui.view">
<field name="name">custom.model.form.view.inherit</field> <field name="name">custom.model.form.view.inherit</field>
@@ -451,6 +451,7 @@
</div> </div>
<field name="product_id" position="after"> <field name="product_id" position="after">
<field name="model_file" string="产品模型" readonly="1" widget="Viewer3D" attrs="{'invisible': [('model_file', '=', False)]}"/> <field name="model_file" string="产品模型" readonly="1" widget="Viewer3D" attrs="{'invisible': [('model_file', '=', False)]}"/>
<field name="model_id" readonly="1"/>
<field name="glb_url" widget="Viewer3D" string="模型" readonly="1" force_save="1" <field name="glb_url" widget="Viewer3D" string="模型" readonly="1" force_save="1"
attrs="{'invisible': [('glb_url', '=', False)]}"/> attrs="{'invisible': [('glb_url', '=', False)]}"/>
</field> </field>

View File

@@ -4,10 +4,11 @@
<record model="ir.ui.view" id="view_product_template_form_inherit_sf_manufacturing"> <record model="ir.ui.view" id="view_product_template_form_inherit_sf_manufacturing">
<field name="name">product.template.product.form.inherit.sf_manufacture</field> <field name="name">product.template.product.form.inherit.sf_manufacture</field>
<field name="model">product.template</field> <field name="model">product.template</field>
<field name="inherit_id" ref="sf_dlm_management.view_product_template_only_form_inherit_sf"/> <field name="inherit_id" ref="sf_sale.view_product_template_form_inherit_sf"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//page[@name='general_information']/group/group[@name='group_standard_price']/field[@name='barcode']" position="after"> <xpath expr="//page[@name='general_information']/group/group[@name='group_standard_price']/field[@name='product_tag_ids']" position="after">
<field name="model_id" readonly="1"/> <field name="categ_type" invisible="1"/>
<field name="model_id" readonly="1" attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
</xpath> </xpath>
</field> </field>
</record> </record>

View File

@@ -224,11 +224,11 @@ class sf_production_plan(models.Model):
return num return num
def do_production_schedule(self): def do_production_schedule(self,date_planned_start):
""" """
排程方法 排程方法
""" """
self.deal_processing_schedule(self.date_planned_start) self.deal_processing_schedule(date_planned_start)
for record in self: for record in self:
if not record.production_line_id: if not record.production_line_id:
raise ValidationError("未选择生产线") raise ValidationError("未选择生产线")

View File

@@ -40,5 +40,5 @@ class Action_Plan_All_Wizard(models.TransientModel):
self.plan_ids.date_planned_start = self.date_planned_start self.plan_ids.date_planned_start = self.date_planned_start
# 在这里添加您的逻辑来处理这些ID # 在这里添加您的逻辑来处理这些ID
# 判断能否排成 # 判断能否排成
self.plan_ids.do_production_schedule() self.plan_ids.do_production_schedule(self.date_planned_start)
_logger.info('处理计划: %s 完成', self.plan_ids.ids) _logger.info('处理计划: %s 完成', self.plan_ids.ids)

View File

@@ -271,7 +271,7 @@ class ResaleOrderLine(models.Model):
embryo_redundancy_id = fields.Many2one('sf.embryo.redundancy', '坯料冗余') embryo_redundancy_id = fields.Many2one('sf.embryo.redundancy', '坯料冗余')
manual_quotation = fields.Boolean('人工编程', default=False) manual_quotation = fields.Boolean('人工编程', default=False)
model_url = fields.Char('模型文件地址') model_url = fields.Char('模型文件地址')
model_id = fields.Char('模型id') model_id = fields.Char('模型ID')
delivery_end_date = fields.Date('交货截止日期') delivery_end_date = fields.Date('交货截止日期')

View File

@@ -107,6 +107,7 @@
<field name="glb_url" widget="Viewer3D" optional="show" <field name="glb_url" widget="Viewer3D" optional="show"
string="模型文件" readonly="1" attrs="{'column_invisible': [('parent.model_display_version', '!=', 'v2')], 'isInList': True}"/> string="模型文件" readonly="1" attrs="{'column_invisible': [('parent.model_display_version', '!=', 'v2')], 'isInList': True}"/>
<field name="part_name" optional="show"/> <field name="part_name" optional="show"/>
<field name="model_id" optional="hide"/>
</xpath> </xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='price_subtotal']" position="after"> <xpath expr="//field[@name='order_line']/tree/field[@name='price_subtotal']" position="after">
<field name="remark"/> <field name="remark"/>
@@ -359,10 +360,10 @@
<field name="categ_id" position="replace"> <field name="categ_id" position="replace">
<field name='categ_id' invisible="1"/> <field name='categ_id' invisible="1"/>
</field> </field>
<field name="product_tag_ids" position="after"> <!-- <field name="product_tag_ids" position="after">
<field name="default_code" attrs="{'invisible': [('product_variant_count', '&gt;', 1)]}"/> <field name="default_code" attrs="{'invisible': [('product_variant_count', '&gt;', 1)]}"/>
<field name="barcode" attrs="{'invisible': [('product_variant_count', '&gt;', 1)]}"/> <field name="barcode" attrs="{'invisible': [('product_variant_count', '&gt;', 1)]}"/>
</field> </field> -->
</field> </field>
</record> </record>
<record id="sale.product_template_action" model="ir.actions.act_window"> <record id="sale.product_template_action" model="ir.actions.act_window">