master-sf1.0
This commit is contained in:
2
sf_base/__init__.py
Normal file
2
sf_base/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from . import models
|
||||||
|
from . import commons
|
||||||
29
sf_base/__manifest__.py
Normal file
29
sf_base/__manifest__.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
{
|
||||||
|
'name': '机企猫智能工厂 基础配置',
|
||||||
|
'version': '1.0',
|
||||||
|
'summary': '智能工厂基础模块',
|
||||||
|
'sequence': 1,
|
||||||
|
'description': """
|
||||||
|
在本模块,定义了主要的角色、菜单、基础业务对象
|
||||||
|
""",
|
||||||
|
'category': 'sf',
|
||||||
|
'website': 'https://www.sf.jikimo.com',
|
||||||
|
'depends': ['account', 'base', 'mrp'],
|
||||||
|
'data': [
|
||||||
|
'security/group_security.xml',
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
'views/base_view.xml',
|
||||||
|
'views/common_view.xml',
|
||||||
|
"views/menu_view.xml"
|
||||||
|
|
||||||
|
],
|
||||||
|
'demo': [
|
||||||
|
],
|
||||||
|
'qweb': [
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'application': False,
|
||||||
|
'auto_install': False,
|
||||||
|
}
|
||||||
1
sf_base/commons/__init__.py
Normal file
1
sf_base/commons/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from. import common
|
||||||
28
sf_base/commons/common.py
Normal file
28
sf_base/commons/common.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import fields, models, api
|
||||||
|
import time
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Common(models.Model):
|
||||||
|
_name = 'sf.sync.common'
|
||||||
|
_description = u'公用类'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_headers(self,token, secret_key):
|
||||||
|
'''
|
||||||
|
获取requests中的heardes参数
|
||||||
|
'''
|
||||||
|
timestamp = int(time.time())
|
||||||
|
check_str = '%s%s%s' % (token, timestamp, secret_key)
|
||||||
|
check_sf_str = hashlib.sha1(check_str.encode('utf-8')).hexdigest()
|
||||||
|
headers = {'TOKEN': token,
|
||||||
|
'TIMESTAMP': str(timestamp),
|
||||||
|
'checkstr': check_sf_str}
|
||||||
|
return headers
|
||||||
|
|
||||||
|
|
||||||
4
sf_base/models/__init__.py
Normal file
4
sf_base/models/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from . import base
|
||||||
|
from . import common
|
||||||
|
|
||||||
|
|
||||||
264
sf_base/models/base.py
Normal file
264
sf_base/models/base.py
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
from odoo import fields, models, api
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
from odoo.http import request
|
||||||
|
from odoo.addons.sf_base.commons.common import Common
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class MachineBrandTags(models.Model):
|
||||||
|
_name = 'sf.machine.brand.tags'
|
||||||
|
_description = '标签'
|
||||||
|
name = fields.Char('名称', size=50)
|
||||||
|
color = fields.Integer('颜色', default=0)
|
||||||
|
|
||||||
|
|
||||||
|
class MachineControlSystem(models.Model):
|
||||||
|
_name = 'sf.machine.control_system'
|
||||||
|
_description = '控制系统'
|
||||||
|
|
||||||
|
code = fields.Char('编码', size=10)
|
||||||
|
name = fields.Char('名称', size=10)
|
||||||
|
brand_id = fields.Many2one('sf.machine.brand', '品牌')
|
||||||
|
active = fields.Boolean('有效', default=True)
|
||||||
|
|
||||||
|
|
||||||
|
# 品牌标签
|
||||||
|
class MachineBrand(models.Model):
|
||||||
|
_name = 'sf.machine.brand'
|
||||||
|
_description = '品牌'
|
||||||
|
|
||||||
|
name = fields.Char('名称')
|
||||||
|
tag_ids = fields.Many2many('sf.machine.brand.tags', 'rel_machine_brand_tags', string='类别')
|
||||||
|
image_brand = fields.Image("品牌图片")
|
||||||
|
active = fields.Boolean('有效', default=True)
|
||||||
|
code = fields.Char('编码')
|
||||||
|
|
||||||
|
|
||||||
|
# 机床
|
||||||
|
class MachineTool(models.Model):
|
||||||
|
_name = 'sf.machine_tool'
|
||||||
|
_description = '机床'
|
||||||
|
|
||||||
|
def get_no(self):
|
||||||
|
partner = self.env['sf.machine_tool'].sudo().search(
|
||||||
|
[('MTcode', '!=', '')],
|
||||||
|
limit=1,
|
||||||
|
order="id desc")
|
||||||
|
if not partner:
|
||||||
|
num = "%04d" % 1
|
||||||
|
|
||||||
|
else:
|
||||||
|
m = int(partner.MTcode) + 1
|
||||||
|
num = "%04d" % m
|
||||||
|
return num
|
||||||
|
|
||||||
|
MTcode = fields.Char("编码", default=get_no)
|
||||||
|
code = fields.Char('行业编码')
|
||||||
|
name = fields.Char('名称')
|
||||||
|
knife_type = fields.Selection(
|
||||||
|
[("BT40", "BT40"), ("BT30", "BT30")],
|
||||||
|
default="", string="刀把类型")
|
||||||
|
number_of_knife_library = fields.Integer('刀库数量')
|
||||||
|
rotate_speed = fields.Integer('转速')
|
||||||
|
number_of_axles = fields.Selection(
|
||||||
|
[("三轴", "三轴"), ("四轴", "四轴"), ("五轴", "五轴")],
|
||||||
|
default="", string="轴数")
|
||||||
|
# 加工进程
|
||||||
|
x_axis = fields.Integer('X轴')
|
||||||
|
y_axis = fields.Integer('Y轴')
|
||||||
|
z_axis = fields.Integer('Z轴')
|
||||||
|
b_axis = fields.Integer('B轴')
|
||||||
|
c_axis = fields.Integer('C轴')
|
||||||
|
remark = fields.Text('备注')
|
||||||
|
is_binding = fields.Boolean('是否绑定机床', default=False)
|
||||||
|
precision = fields.Float('加工精度')
|
||||||
|
control_system_id = fields.Many2one('sf.machine.control_system',
|
||||||
|
string="控制系统")
|
||||||
|
# 多个机床型号对应一个机床
|
||||||
|
type_id = fields.Many2one('sf.machine_tool.type', '型号')
|
||||||
|
brand_id = fields.Many2one('sf.machine.brand', string='品牌')
|
||||||
|
state = fields.Selection(
|
||||||
|
[("正常", "正常"), ("故障", "故障"), ("不可用", "不可用")],
|
||||||
|
default='正常', string="机床状态")
|
||||||
|
|
||||||
|
# 一个机床对应一個加工工厂,一个加工工厂对应多个机床
|
||||||
|
factory_id = fields.Many2one('res.partner', string='所属工厂',
|
||||||
|
domain="[('is_factory', '=', True)]")
|
||||||
|
# 一个机床对应一个供应商,一个供应商对应多个机床
|
||||||
|
supplier_id = fields.Many2one('res.partner', string='制造商',
|
||||||
|
domain="[('is_vendor', '=', True)]")
|
||||||
|
registration_date = fields.Date('注册日期')
|
||||||
|
state_zc = fields.Selection([("已注册", "已注册"), ("未注册", "未注册")], string="注册状态", default='未注册', tracking=True)
|
||||||
|
active = fields.Boolean('有效', default=True)
|
||||||
|
|
||||||
|
@api.constrains('rotate_speed')
|
||||||
|
def _check_rotate_speed(self):
|
||||||
|
if self.rotate_speed <= 0:
|
||||||
|
raise ValidationError("转速不能为0")
|
||||||
|
|
||||||
|
@api.constrains('precision')
|
||||||
|
def _check_precision(self):
|
||||||
|
if self.precision <= 0.00:
|
||||||
|
raise ValidationError("加工精度不能为0")
|
||||||
|
|
||||||
|
@api.constrains('number_of_knife_library')
|
||||||
|
def _check_number_of_knife_library(self):
|
||||||
|
if self.number_of_knife_library <= 0:
|
||||||
|
raise ValidationError("刀库数量不能为0")
|
||||||
|
|
||||||
|
@api.constrains('x_axis')
|
||||||
|
def _check_x_axis(self):
|
||||||
|
if self.x_axis <= 0:
|
||||||
|
raise ValidationError("加工行程里x轴不能为0")
|
||||||
|
|
||||||
|
@api.constrains('y_axis')
|
||||||
|
def _check_y_axis(self):
|
||||||
|
if self.y_axis <= 0:
|
||||||
|
raise ValidationError("加工行程里y轴不能为0")
|
||||||
|
|
||||||
|
@api.constrains('z_axis')
|
||||||
|
def _check_z_axis(self):
|
||||||
|
if self.z_axis <= 0:
|
||||||
|
raise ValidationError("加工行程里z轴不能为0")
|
||||||
|
|
||||||
|
@api.constrains('b_axis')
|
||||||
|
def _check_b_axis(self):
|
||||||
|
if self.number_of_axles == '四轴':
|
||||||
|
print(self.number_of_axles)
|
||||||
|
if self.b_axis <= 0:
|
||||||
|
raise ValidationError("加工行程里b轴不能为0")
|
||||||
|
|
||||||
|
@api.constrains('c_axis')
|
||||||
|
def _check_c_axis(self):
|
||||||
|
if self.number_of_axles == '五轴':
|
||||||
|
if self.c_axis <= 0:
|
||||||
|
raise ValidationError("加工行程里c轴不能为0")
|
||||||
|
|
||||||
|
@api.onchange('type_id')
|
||||||
|
def get_type_info(self):
|
||||||
|
for item in self:
|
||||||
|
item.knife_type = item.type_id.knife_type
|
||||||
|
item.number_of_knife_library = item.type_id.number_of_knife_library
|
||||||
|
item.number_of_axles = item.type_id.number_of_axles
|
||||||
|
item.rotate_speed = item.type_id.rotate_speed
|
||||||
|
item.precision = item.type_id.precision
|
||||||
|
item.control_system_id = item.type_id.control_system_id
|
||||||
|
item.x_axis = item.type_id.x_axis
|
||||||
|
item.y_axis = item.type_id.y_axis
|
||||||
|
item.z_axis = item.type_id.z_axis
|
||||||
|
item.b_axis = item.type_id.b_axis
|
||||||
|
item.c_axis = item.type_id.c_axis
|
||||||
|
|
||||||
|
# 注册同步机床
|
||||||
|
def enroll_machine_tool(self):
|
||||||
|
sf_sync_config = self.env['res.config.settings'].get_values()
|
||||||
|
token = sf_sync_config['token']
|
||||||
|
sf_secret_key = sf_sync_config['sf_secret_key']
|
||||||
|
headers = Common.get_headers(self, token, sf_secret_key)
|
||||||
|
strurl = sf_sync_config['sf_url'] + self.crea_url
|
||||||
|
objs_all = self.env['sf.machine_tool'].search([('MTcode', '=', self.MTcode)])
|
||||||
|
machine_tool_list = []
|
||||||
|
if objs_all:
|
||||||
|
for item in objs_all:
|
||||||
|
val = {
|
||||||
|
'MTcode': item.MTcode,
|
||||||
|
'factory_token': token,
|
||||||
|
'id': item.id,
|
||||||
|
'name': item.name,
|
||||||
|
'code': item.code,
|
||||||
|
'precision': item.precision,
|
||||||
|
'knife_type': item.knife_type,
|
||||||
|
'number_of_knife_library': item.number_of_knife_library,
|
||||||
|
'rotate_speed': item.rotate_speed,
|
||||||
|
'number_of_axles': item.number_of_axles,
|
||||||
|
'control_system_id': self.env['sf.machine.control_system'].search(
|
||||||
|
[('id', '=', item.control_system_id.id)]).code,
|
||||||
|
'type_id': self.env['sf.machine_tool.type'].search([('id', '=', item.type_id.id)]).code,
|
||||||
|
'brand_id': self.env['sf.machine.brand'].search([('id', '=', item.brand_id.id)]).code,
|
||||||
|
'supplier_id': item.supplier_id.id,
|
||||||
|
'x_axis': item.x_axis,
|
||||||
|
'y_axis': item.y_axis,
|
||||||
|
'z_axis': item.z_axis,
|
||||||
|
'b_axis': item.b_axis,
|
||||||
|
'c_axis': item.c_axis,
|
||||||
|
'state': item.state,
|
||||||
|
'active': item.active,
|
||||||
|
|
||||||
|
}
|
||||||
|
machine_tool_list.append(val)
|
||||||
|
# kw = machine_tool_list
|
||||||
|
kw = json.dumps(machine_tool_list, ensure_ascii=False)
|
||||||
|
r = requests.post(strurl, json={}, data={'kw': kw, 'token': token}, headers=headers)
|
||||||
|
ret = r.json()
|
||||||
|
self.code = ret['message']
|
||||||
|
self.state_zc = "已注册"
|
||||||
|
if r == 200:
|
||||||
|
return "机床注册成功"
|
||||||
|
else:
|
||||||
|
raise ValidationError("没有注册机床信息")
|
||||||
|
|
||||||
|
|
||||||
|
class MachineToolType(models.Model):
|
||||||
|
_name = 'sf.machine_tool.type'
|
||||||
|
_description = '机床型号'
|
||||||
|
# _order = 'priority desc, code, name, id'
|
||||||
|
|
||||||
|
name = fields.Char('名称')
|
||||||
|
brand_id = fields.Many2one('sf.machine.brand', string='品牌')
|
||||||
|
knife_type = fields.Selection(
|
||||||
|
[("BT40", "BT40"), ("BT30", "BT30")],
|
||||||
|
default="", string="刀把类型")
|
||||||
|
number_of_knife_library = fields.Integer('刀库数量')
|
||||||
|
rotate_speed = fields.Integer('转速')
|
||||||
|
# 多个型号对应一个机床
|
||||||
|
machine_tool_id = fields.Many2one('sf.machine_tool', '机床')
|
||||||
|
number_of_axles = fields.Selection(
|
||||||
|
[("三轴", "三轴"), ("四轴", "四轴"), ("五轴", "五轴")],
|
||||||
|
default="", string="轴数")
|
||||||
|
# 加工进程
|
||||||
|
x_axis = fields.Integer('X轴')
|
||||||
|
y_axis = fields.Integer('Y轴')
|
||||||
|
z_axis = fields.Integer('Z轴')
|
||||||
|
b_axis = fields.Integer('B轴')
|
||||||
|
c_axis = fields.Integer('C轴')
|
||||||
|
remark = fields.Text('备注')
|
||||||
|
precision = fields.Float('加工精度')
|
||||||
|
control_system_id = fields.Many2one('sf.machine.control_system',
|
||||||
|
string="控制系统")
|
||||||
|
active = fields.Boolean('有效', default=True)
|
||||||
|
code = fields.Char('编码')
|
||||||
|
|
||||||
|
|
||||||
|
# 刀具
|
||||||
|
class CuttingTool(models.Model):
|
||||||
|
_name = 'sf.cutting_tool.category'
|
||||||
|
_description = '刀具类别'
|
||||||
|
code = fields.Char('编码')
|
||||||
|
name = fields.Char('名称')
|
||||||
|
remark = fields.Text('备注')
|
||||||
|
active = fields.Boolean('有效', default=True)
|
||||||
|
|
||||||
|
|
||||||
|
class CuttingToolType(models.Model):
|
||||||
|
_name = 'sf.cutting_tool.type'
|
||||||
|
_description = '刀具型号'
|
||||||
|
code = fields.Char('编码')
|
||||||
|
name = fields.Char('名称')
|
||||||
|
diameter = fields.Integer('直径')
|
||||||
|
long_blade = fields.Integer('避空长/刃长')
|
||||||
|
cone_angle_pitch = fields.Integer('锥角/节距')
|
||||||
|
shank_diameter = fields.Integer('柄径')
|
||||||
|
taper_shank_length = fields.Integer('锥柄长')
|
||||||
|
tool_length = fields.Integer('刀具总长')
|
||||||
|
blade_number = fields.Integer('刃数')
|
||||||
|
category_id = fields.Many2one('sf.cutting_tool.category', string='刀具类别')
|
||||||
|
brand_id = fields.Many2one('sf.machine.brand', string='品牌')
|
||||||
|
remark = fields.Text('备注')
|
||||||
|
active = fields.Boolean('有效', default=True)
|
||||||
177
sf_base/models/common.py
Normal file
177
sf_base/models/common.py
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import logging
|
||||||
|
from odoo import fields, models
|
||||||
|
import requests
|
||||||
|
from odoo.addons.sf_base.commons.common import Common
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# 材料
|
||||||
|
class MrsProductionMaterials(models.Model):
|
||||||
|
_name = 'sf.production.materials'
|
||||||
|
_description = '材料'
|
||||||
|
|
||||||
|
materials_no = fields.Char("编码")
|
||||||
|
name = fields.Char('名称')
|
||||||
|
partner_ids = fields.Many2many('res.partner', 'materials_ids', '加工工厂')
|
||||||
|
materials_model_ids = fields.One2many('sf.materials.model', 'materials_id', '材料型号')
|
||||||
|
remark = fields.Text("备注")
|
||||||
|
active = fields.Boolean('有效', default=True)
|
||||||
|
|
||||||
|
# def open_url_action(self):
|
||||||
|
# base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||||
|
# # url = 'http://192.168.50.202:8080/api'
|
||||||
|
# url = 'https://bfw.jikimo.com/'
|
||||||
|
# # data = {
|
||||||
|
# # 'key' : 'value'
|
||||||
|
# # }
|
||||||
|
#
|
||||||
|
# response = requests.get(url)
|
||||||
|
# response.raise_for_status()
|
||||||
|
# return {
|
||||||
|
# 'type': 'ir.actions.act_url',
|
||||||
|
# 'url': response.url,
|
||||||
|
# 'target': 'new'
|
||||||
|
# }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def open_url_action(self):
|
||||||
|
# sf_sync_config = self.env['res.config.settings'].get_values()
|
||||||
|
# token = sf_sync_config['token']
|
||||||
|
# sf_secret_key = sf_sync_config['sf_secret_key']
|
||||||
|
# headers = Common.get_headers(self, token, sf_secret_key)
|
||||||
|
url = 'http://192.168.50.127:8081'
|
||||||
|
params = {
|
||||||
|
'user_id': self._uid
|
||||||
|
}
|
||||||
|
url_params = urlencode(params)
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_url',
|
||||||
|
'url': url + '?' + url_params,
|
||||||
|
# 'url': url,
|
||||||
|
'target': 'new'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 材料型号
|
||||||
|
class MrsMaterialModel(models.Model):
|
||||||
|
_name = 'sf.materials.model'
|
||||||
|
_description = '材料型号'
|
||||||
|
|
||||||
|
materials_no = fields.Char("编码")
|
||||||
|
materials_num = fields.Char("编码号")
|
||||||
|
name = fields.Char('型号名')
|
||||||
|
need_h = fields.Boolean("热处理", default="false")
|
||||||
|
mf_materia_post = fields.Char("热处理后密度")
|
||||||
|
density = fields.Float("密度(kg/m³)")
|
||||||
|
materials_id = fields.Many2one('sf.production.materials', "材料名")
|
||||||
|
remark = fields.Text("备注")
|
||||||
|
gain_way = fields.Selection(
|
||||||
|
[("自加工", "自加工"), ("外协", "外协"), ("采购", "采购")],
|
||||||
|
default="", string="获取方式")
|
||||||
|
supplier_ids = fields.One2many('sf.supplier.sort', 'materials_model_id', string='供应商')
|
||||||
|
active = fields.Boolean('有效', default=True)
|
||||||
|
|
||||||
|
|
||||||
|
class MrsProductionProcessCategory(models.Model):
|
||||||
|
_name = 'sf.production.process.category'
|
||||||
|
_description = '表面工艺类别'
|
||||||
|
order = 'id desc'
|
||||||
|
|
||||||
|
name = fields.Char('名称')
|
||||||
|
code = fields.Char("编码")
|
||||||
|
sequence = fields.Integer('排序')
|
||||||
|
production_process_ids = fields.One2many('sf.production.process', 'category_id', string="表面工艺")
|
||||||
|
active = fields.Boolean('有效', default=True)
|
||||||
|
|
||||||
|
|
||||||
|
# 工艺 编码,名称,备注
|
||||||
|
class MrsProductionProcess(models.Model):
|
||||||
|
_name = 'sf.production.process'
|
||||||
|
_description = '表面工艺'
|
||||||
|
|
||||||
|
process_encode = fields.Char("编码")
|
||||||
|
name = fields.Char('名称')
|
||||||
|
remark = fields.Text("备注")
|
||||||
|
processing_order_ids = fields.One2many('sf.processing.order', 'production_process_id', string='工序')
|
||||||
|
partner_process_ids = fields.Many2many('res.partner', 'process_ids', '加工工厂')
|
||||||
|
active = fields.Boolean('有效', default=True)
|
||||||
|
parameter_ids = fields.One2many('sf.production.process.parameter', 'process_id', string='可选参数')
|
||||||
|
category_id = fields.Many2one('sf.production.process.category')
|
||||||
|
# workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_process', required=True)
|
||||||
|
|
||||||
|
|
||||||
|
class MrsProcessingTechnology(models.Model):
|
||||||
|
_name = 'sf.processing.technology'
|
||||||
|
_description = '加工工艺'
|
||||||
|
|
||||||
|
name = fields.Char('名称', index=True)
|
||||||
|
remark = fields.Text('备注', index=True)
|
||||||
|
process_encode = fields.Char("编码")
|
||||||
|
processing_order_ids = fields.Many2many('sf.processing.order', 'sf_associated_processes',
|
||||||
|
index=True, string='工序')
|
||||||
|
active = fields.Boolean('有效', default=True)
|
||||||
|
|
||||||
|
|
||||||
|
class MrsProcessingOrder(models.Model):
|
||||||
|
_name = 'sf.processing.order'
|
||||||
|
_description = '工序'
|
||||||
|
sequence = fields.Integer('Sequence')
|
||||||
|
processing_technology_ids = fields.Many2many('sf.processing.technology', 'sf_associated_processes',
|
||||||
|
index=True, string='加工工艺')
|
||||||
|
production_process_id = fields.Many2one('sf.production.process', string="表面工艺")
|
||||||
|
|
||||||
|
|
||||||
|
class Tray(models.Model):
|
||||||
|
_name = 'sf.tray'
|
||||||
|
_description = '托盘'
|
||||||
|
|
||||||
|
code = fields.Char('编码', copy=False)
|
||||||
|
name = fields.Char('名称')
|
||||||
|
state = fields.Selection(
|
||||||
|
[("空闲", "空闲"), ("占用", "占用"), ("报损", "报损")],
|
||||||
|
default="空闲", string="状态")
|
||||||
|
active = fields.Boolean('有效', default=True)
|
||||||
|
|
||||||
|
|
||||||
|
class SupplierSort(models.Model):
|
||||||
|
_name = 'sf.supplier.sort'
|
||||||
|
_description = '供应商排序'
|
||||||
|
|
||||||
|
sequence = fields.Integer('Sequence')
|
||||||
|
partner_id = fields.Many2one('res.partner', domain="[('is_company', '=', True),('supplier_rank', '!=', 0)]")
|
||||||
|
materials_model_id = fields.Many2one('sf.materials.model')
|
||||||
|
|
||||||
|
_sql_constraints = [
|
||||||
|
('supplier_sort_uniq', 'unique (partner_id,materials_model_id)', '排序不能重复!')
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class MrsProductionProcessParameter(models.Model):
|
||||||
|
_name = 'sf.production.process.parameter'
|
||||||
|
_description = '可选参数'
|
||||||
|
# _display_name = 'name'
|
||||||
|
|
||||||
|
code = fields.Char("编码")
|
||||||
|
name = fields.Char('名称')
|
||||||
|
gain_way = fields.Selection([("自加工", "自加工"), ("外协", "外协")], default="", string="获取方式")
|
||||||
|
is_check = fields.Boolean(default=False)
|
||||||
|
# price = fields.Float('单价')
|
||||||
|
process_id = fields.Many2one('sf.production.process', string='表面工艺')
|
||||||
|
materials_model_ids = fields.Many2many('sf.materials.model', 'applicable_material', string='适用材料')
|
||||||
|
active = fields.Boolean('有效', default=True)
|
||||||
|
|
||||||
|
def name_get(self):
|
||||||
|
result = []
|
||||||
|
for parameter in self:
|
||||||
|
if parameter.process_id:
|
||||||
|
name = parameter.process_id.name + '-' + parameter.name
|
||||||
|
result.append((parameter.id, name))
|
||||||
|
return result
|
||||||
|
|
||||||
|
# 获取表面工艺的获取方式
|
||||||
|
def get_gain_way(self, item):
|
||||||
|
process_parameter = self.env['sf.production.process.parameter'].search([('id', '=', item.id)])
|
||||||
|
return process_parameter
|
||||||
4
sf_base/security/group_security.xml
Normal file
4
sf_base/security/group_security.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
23
sf_base/security/ir.model.access.csv
Normal file
23
sf_base/security/ir.model.access.csv
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_sf_machine_tool,sf_machine_tool,model_sf_machine_tool,base.group_user,1,1,1,1
|
||||||
|
access_sf_cutting_tool_category,sf_cutting_tool_category,model_sf_cutting_tool_category,base.group_user,1,1,1,1
|
||||||
|
access_sf_machine_tool_type,sf_machine_tool_type,model_sf_machine_tool_type,base.group_user,1,1,1,1
|
||||||
|
access_sf_cutting_tool_type,sf_cutting_tool_type,model_sf_cutting_tool_type,base.group_user,1,1,1,1
|
||||||
|
access_sf_machine_brand,sf_machine_brand,model_sf_machine_brand,base.group_user,1,1,1,1
|
||||||
|
access_sf_machine_brand_tags,sf_machine_brand_tags,model_sf_machine_brand_tags,base.group_user,1,1,1,1
|
||||||
|
access_sf_machine_control_system,sf_machine_control_system,model_sf_machine_control_system,base.group_user,1,1,1,1
|
||||||
|
access_sf_processing_order,sf_processing_order,model_sf_processing_order,base.group_user,1,1,1,1
|
||||||
|
access_sf_production_process,sf_production_process,model_sf_production_process,base.group_user,1,1,1,1
|
||||||
|
access_sf_production_materials,sf_production_materials,model_sf_production_materials,base.group_user,1,1,1,1
|
||||||
|
access_sf_materials_model,sf_materials_model,model_sf_materials_model,base.group_user,1,1,1,1
|
||||||
|
access_sf_processing_technology,sf_processing_technology,model_sf_processing_technology,base.group_user,1,1,1,1
|
||||||
|
access_sf_tray,sf_tray,model_sf_tray,base.group_user,1,1,1,1
|
||||||
|
access_sf_supplier_sort,sf_supplier_sort,model_sf_supplier_sort,base.group_user,1,1,1,1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
503
sf_base/views/base_view.xml
Normal file
503
sf_base/views/base_view.xml
Normal file
@@ -0,0 +1,503 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<odoo>
|
||||||
|
#------------------品牌------------------
|
||||||
|
<record model="ir.ui.view" id="search_sf_machine_brand_view">
|
||||||
|
<field name="name">search.sf.machine.brand</field>
|
||||||
|
<field name="model">sf.machine.brand</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="品牌">
|
||||||
|
<!-- <field name="name" string="名称"-->
|
||||||
|
<!-- filter_domain="[('name', 'ilike', self)]"/>-->
|
||||||
|
<field name="name" string="模糊搜索"
|
||||||
|
filter_domain="['|',('name', 'ilike', self),('code', 'ilike', self)]"/>
|
||||||
|
<group string="分组">
|
||||||
|
<filter name="tag_ids" string="标签" domain="[]" context="{'group_by': 'tag_ids'}"/>
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="tree_sf_machine_brand_view">
|
||||||
|
<field name="name">tree.sf.machine.brand</field>
|
||||||
|
<field name="model">sf.machine.brand</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="品牌">
|
||||||
|
<field name="code"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}" optional="hide"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="form_sf_machine_brand">
|
||||||
|
<field name="name">form.sf.machine.brand</field>
|
||||||
|
<field name="model">sf.machine.brand</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="品牌">
|
||||||
|
<!-- <widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>-->
|
||||||
|
<field name="image_brand" widget='image' class="oe_avatar" options="{'preview_image': 'image_128'}"/>
|
||||||
|
<div class="oe_title">
|
||||||
|
<label for="code" string="编码"/>
|
||||||
|
<h2 class="d-flex">
|
||||||
|
<field name="code" readonly="True"/>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="name" required="True"/>
|
||||||
|
<field name="tag_ids"
|
||||||
|
widget="many2many_tags"
|
||||||
|
options="{'color_field': 'color', 'no_create_edit': True}"
|
||||||
|
required="True"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_sf_machine_brand" model="ir.actions.act_window">
|
||||||
|
<field name="name">品牌</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">sf.machine.brand</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
[品牌] 还没有哦!点左上角的[创建]按钮,沙发归你了!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
#------------------机床型号------------------
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="search_sf_machine_tool_type_view">
|
||||||
|
<field name="name">search.sf.machine_tool.type</field>
|
||||||
|
<field name="model">sf.machine_tool.type</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="机床型号">
|
||||||
|
<field name="name" string="模糊搜索"
|
||||||
|
filter_domain="['|',('name', 'ilike', self),('remark', 'ilike', self)]"/>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="tree_sf_machine_tool_type_view">
|
||||||
|
<field name="name">tree.sf.machine_tool.type</field>
|
||||||
|
<field name="model">sf.machine_tool.type</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="机床型号">
|
||||||
|
<field name="code"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="brand_id"/>
|
||||||
|
<field name="remark"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="form_sf_machine_tool_type">
|
||||||
|
<field name="name">form.sf.machine_tool.type</field>
|
||||||
|
<field name="model">sf.machine_tool.type</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="机床型号">
|
||||||
|
<group string="基本信息" name="base_info">
|
||||||
|
<group>
|
||||||
|
<field name="code" force_save="1" readonly="1"/>
|
||||||
|
<field name="name" required="1"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="brand_id" required="1"
|
||||||
|
domain="[('tag_ids', '=', '机床')]"
|
||||||
|
options="{'no_create': True}"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group string="参数">
|
||||||
|
<group>
|
||||||
|
<field name="knife_type" required="1"/>
|
||||||
|
<field name="number_of_knife_library" required="1" options="{'format': false}"/>
|
||||||
|
<field name="number_of_axles" required="1" widget="radio" options="{'horizontal': true}"/>
|
||||||
|
<label for="x_axis" string="加工行程(mm)"
|
||||||
|
attrs="{'invisible': [('number_of_axles', '=', False)]}"/>
|
||||||
|
<div class="o_address_format"
|
||||||
|
attrs="{'invisible': [('number_of_axles', '=', False)]}">
|
||||||
|
<label for="x_axis" string="x"/>
|
||||||
|
<field name="x_axis" class="o_address_city" required="1" options="{'format': false}"/>
|
||||||
|
<label for="y_axis" string="y"/>
|
||||||
|
<field name="y_axis" class="o_address_zip" required="1" options="{'format': false}"/>
|
||||||
|
<label for="z_axis" string="z"/>
|
||||||
|
<field name="z_axis" class="o_address_zip" required="1" options="{'format': false}"/>
|
||||||
|
<label for="b_axis" string="b"
|
||||||
|
attrs="{'invisible': [('number_of_axles', '=', '三轴')]}"/>
|
||||||
|
<field name="b_axis" class="o_address_city" required="1"
|
||||||
|
attrs="{'invisible': [('number_of_axles', '=', '三轴')]}"
|
||||||
|
options="{'format': false}"/>
|
||||||
|
<label for="c_axis" string="c"
|
||||||
|
attrs="{'invisible': [('number_of_axles', 'in', ['三轴','四轴'])]}"/>
|
||||||
|
<field name="c_axis" class="o_address_zip" required="1"
|
||||||
|
attrs="{'invisible': [('number_of_axles', 'in', ['三轴','四轴'])]}"
|
||||||
|
options="{'format': false}"/>
|
||||||
|
</div>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="rotate_speed" string="转速(min)" required="1" options="{'format': false}"/>
|
||||||
|
<field name="precision" required="1" string="加工精度(mm)"/>
|
||||||
|
<field name="control_system_id" required="1" options="{'no_create': True}"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
<group string="其它">
|
||||||
|
<field name="remark"/>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_sf_machine_tool_type" model="ir.actions.act_window">
|
||||||
|
<field name="name">机床型号</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">sf.machine_tool.type</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
[机床型号] 还没有哦!点左上角的[创建]按钮,沙发归你了!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
#------------------刀具型号------------------
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="search_sf_cutting_tool_type_view">
|
||||||
|
<field name="name">search.sf.cutting_tool.type</field>
|
||||||
|
<field name="model">sf.cutting_tool.type</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="刀具型号">
|
||||||
|
<field name="name" string="模糊搜索"
|
||||||
|
filter_domain="['|',('name', 'ilike', self),('code', 'ilike', self)]"/>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="tree_cutting_tool_type_view">
|
||||||
|
<field name="name">tree.sf.cutting_tool.type</field>
|
||||||
|
<field name="model">sf.cutting_tool.type</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="刀具型号">
|
||||||
|
<field name="code"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="remark"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="form_sf_cutting_tool_type">
|
||||||
|
<field name="name">form.sf.cutting_tool.type</field>
|
||||||
|
<field name="model">sf.cutting_tool.type</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="刀具型号">
|
||||||
|
<group string="基本信息">
|
||||||
|
<group>
|
||||||
|
<field name="code" force_save="1" readonly="1"/>
|
||||||
|
<field name="category_id" string="类别" required="1"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="name" required="1"/>
|
||||||
|
<field name="brand_id" required="1"
|
||||||
|
domain="[('tag_ids', '=', '机床')]"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group string="刀具参数">
|
||||||
|
<group>
|
||||||
|
<field name="taper_shank_length" required="1" options="{'format': false}"/>
|
||||||
|
<field name="long_blade" required="1" options="{'format': false}"/>
|
||||||
|
<field name="tool_length" required="1" options="{'format': false}"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="diameter" required="1" options="{'format': false}"/>
|
||||||
|
<field name="shank_diameter" required="1" options="{'format': false}"/>
|
||||||
|
<field name="cone_angle_pitch" required="1" options="{'format': false}"/>
|
||||||
|
<field name="blade_number" required="1" options="{'format': false}"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group string="其它">
|
||||||
|
<field name="remark"/>
|
||||||
|
</group>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_sf_cutting_tool_type" model="ir.actions.act_window">
|
||||||
|
<field name="name">刀具型号</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">sf.cutting_tool.type</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
[刀具型号] 还没有哦!点左上角的[创建]按钮,沙发归你了!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
#------------------刀具类别------------------
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="search_sf_cutting_tool_category_view">
|
||||||
|
<field name="name">search.sf.cutting_tool.category</field>
|
||||||
|
<field name="model">sf.cutting_tool.category</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="刀具类别">
|
||||||
|
<field name="name" string="模糊搜索"
|
||||||
|
filter_domain="['|',('name', 'ilike', self),('remark', 'ilike', self)]"/>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="tree_sf_cutting_tool_category_view">
|
||||||
|
<field name="name">tree.sf.cutting_tool.category</field>
|
||||||
|
<field name="model">sf.cutting_tool.category</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="刀具类别">
|
||||||
|
<field name="code"/>
|
||||||
|
<field name="name"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="sf_cutting_tool_category_form">
|
||||||
|
<field name="name">form.sf.cutting_tool.category</field>
|
||||||
|
<field name="model">sf.cutting_tool.category</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="刀具类别">
|
||||||
|
<group string="基本信息">
|
||||||
|
<group>
|
||||||
|
<field name="code" readonly="1"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="name" required="1"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<!-- <group string="参数">-->
|
||||||
|
<!-- <field name="type_ids" string="刀具型号">-->
|
||||||
|
<!-- <tree editable="bottom">-->
|
||||||
|
<!-- <field name="category_id" invisible="True"/>-->
|
||||||
|
<!-- <field name="code" string="编码" required="True"/>-->
|
||||||
|
<!-- <field name="name" string="名称" required="True"/>-->
|
||||||
|
<!-- <field name="diameter" string="直径" required="True"/>-->
|
||||||
|
<!-- <field name="long_blade" string="避空长/刃长" required="True"/>-->
|
||||||
|
<!-- <field name="cone_angle_pitch" string="锥角/节距" required="True"/>-->
|
||||||
|
<!-- <field name="shank_diameter" string="柄径" required="True"/>-->
|
||||||
|
<!-- <field name="taper_shank_length" string="锥柄" required="True"/>-->
|
||||||
|
<!-- <field name="tool_length" string="刀具总长" required="True"/>-->
|
||||||
|
<!-- <field name="blade_number" string="刃数" required="True"/>-->
|
||||||
|
<!-- <field name="remark" string="备注"/>-->
|
||||||
|
<!-- <field name="active" string="有效"/>-->
|
||||||
|
<!-- </tree>-->
|
||||||
|
<!-- </field>-->
|
||||||
|
<!-- </group>-->
|
||||||
|
<group string="其它">
|
||||||
|
<field name="remark"/>
|
||||||
|
</group>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_sf_cutting_tool_category" model="ir.actions.act_window">
|
||||||
|
<field name="name">刀具类别</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">sf.cutting_tool.category</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
[刀具类别] 还没有哦!点左上角的[创建]按钮,沙发归你了!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
#------------------机床注册------------------
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="search_sf_machine_tool_view">
|
||||||
|
<field name="name">search.sf.machine_tool</field>
|
||||||
|
<field name="model">sf.machine_tool</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="机床">
|
||||||
|
<field name="name" string="名称" filter_domain="[('name', 'ilike', self)]"/>
|
||||||
|
<field name="type_id" string="型号" filter_domain="[('type_id', 'ilike', self)]"/>
|
||||||
|
<searchpanel>
|
||||||
|
<field name="brand_id" icon="fa-building" enable_counters="1"/>
|
||||||
|
<!-- <field name="type_id" icon="fa-users" enable_counters="1"/>-->
|
||||||
|
</searchpanel>
|
||||||
|
</search>
|
||||||
|
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="tree_sf_machine_tool_view">
|
||||||
|
<field name="name">tree.sf.machine_tool</field>
|
||||||
|
<field name="model">sf.machine_tool</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="机床">
|
||||||
|
<field name="code"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="brand_id"/>
|
||||||
|
<field name="type_id"/>
|
||||||
|
<field name="state_zc"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="sf_machine_tool_form">
|
||||||
|
<field name="name">form.sf.machine_tool</field>
|
||||||
|
<field name="model">sf.machine_tool</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="机床">
|
||||||
|
<header>
|
||||||
|
<button type="object" class="oe_highlight" name='enroll_machine_tool' string="机床注册" attrs="{'invisible': [('code','!=',False)]}"/>
|
||||||
|
</header>
|
||||||
|
<group string="基本信息">
|
||||||
|
<group>
|
||||||
|
<field name="MTcode" string="编码"/>
|
||||||
|
|
||||||
|
|
||||||
|
<field name="brand_id"
|
||||||
|
required="1"
|
||||||
|
domain="[('tag_ids', '=', '机床')]"
|
||||||
|
options="{'no_create': True}"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="name" required="1"/>
|
||||||
|
<field name="type_id" required="1" options="{'no_create': True}"
|
||||||
|
domain="[('brand_id', '=', brand_id)]" attrs="{'invisible': [('brand_id','=',False)]}"/>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
<group>
|
||||||
|
<field name="code" readonly="1" string="行业编码" force_save="1"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<notebook>
|
||||||
|
<page string="参数">
|
||||||
|
<group string="参数">
|
||||||
|
<group>
|
||||||
|
<field name="knife_type" required="1"/>
|
||||||
|
<field name="number_of_knife_library" required="1" options="{'format': false}"/>
|
||||||
|
<field name="number_of_axles" widget="radio" options="{'horizontal': true}"/>
|
||||||
|
<label for="x_axis" string="加工行程(mm)"
|
||||||
|
attrs="{'invisible': [('number_of_axles', '=', False)]}"/>
|
||||||
|
<div class="o_address_format"
|
||||||
|
attrs="{'invisible': [('number_of_axles', '=', False)]}">
|
||||||
|
<label for="x_axis" string="x"/>
|
||||||
|
<field name="x_axis" class="o_address_city" required="1" options="{'format': false}"/>
|
||||||
|
<label for="y_axis" string="y"/>
|
||||||
|
<field name="y_axis" class="o_address_zip" required="1" options="{'format': false}"/>
|
||||||
|
<label for="z_axis" string="z"/>
|
||||||
|
<field name="z_axis" class="o_address_zip" required="1" options="{'format': false}"/>
|
||||||
|
<label for="b_axis" string="b"
|
||||||
|
attrs="{'invisible': [('number_of_axles', '=', '三轴')]}"/>
|
||||||
|
<field name="b_axis" class="o_address_city" required="1"
|
||||||
|
attrs="{'invisible': [('number_of_axles', '=', '三轴')]}"
|
||||||
|
options="{'format': false}"/>
|
||||||
|
<label for="c_axis" string="c"
|
||||||
|
attrs="{'invisible': [('number_of_axles', 'in', ['三轴','四轴'])]}"/>
|
||||||
|
<field name="c_axis" class="o_address_zip" required="1"
|
||||||
|
attrs="{'invisible': [('number_of_axles', 'in', ['三轴','四轴'])]}"
|
||||||
|
options="{'format': false}"/>
|
||||||
|
</div>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="rotate_speed" required="1" string="转速(min)" options="{'format': false}"/>
|
||||||
|
<field name="precision" required="1" string="加工精度(mm)"/>
|
||||||
|
<field name="control_system_id" required="1" options="{'no_create': True}"/>
|
||||||
|
<field name="state" widget="selection"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
<page string="其他">
|
||||||
|
<group string="其他">
|
||||||
|
<field name="remark"/>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_sf_machine_tool" model="ir.actions.act_window">
|
||||||
|
<field name="name">机床</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">sf.machine_tool</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<!-- <field name="search_view_id" ref="view_okr_filter"/>-->
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
[机床] 还没有哦!点左上角的[创建]按钮,沙发归你了!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
#------------------控制系统------------------
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="search_sf_machine_control_system_view">
|
||||||
|
<field name="name">search.sf.machine.control_system</field>
|
||||||
|
<field name="model">sf.machine.control_system</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="控制系统">
|
||||||
|
<field name="name" string="模糊搜索"
|
||||||
|
filter_domain="['|',('name', 'ilike', self),('code', 'ilike', self)]"/>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="tree_sf_machine_control_system_view">
|
||||||
|
<field name="name">tree.sf.machine.control_system</field>
|
||||||
|
<field name="model">sf.machine.control_system</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="控制系统">
|
||||||
|
<field name="code"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="brand_id"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="form_sf_machine_control_system">
|
||||||
|
<field name="name">form.sf.machine.control_system</field>
|
||||||
|
<field name="model">sf.machine.control_system</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="控制系统">
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="code" force_save="1" readonly="1"/>
|
||||||
|
<field name="brand_id" required="1"
|
||||||
|
domain="[('tag_ids', '=', '控制系统')]"
|
||||||
|
options="{'no_create': True}"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="name" required="1"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_sf_machine_control_system" model="ir.actions.act_window">
|
||||||
|
<field name="name">控制系统</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">sf.machine.control_system</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
[控制系统] 还没有哦!点左上角的[创建]按钮,沙发归你了!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
332
sf_base/views/common_view.xml
Normal file
332
sf_base/views/common_view.xml
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!--加工工艺-->
|
||||||
|
<record model="ir.ui.view" id="sf_processing_technology_form">
|
||||||
|
<field name="model">sf.processing.technology</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="加工工艺">
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="process_encode" readonly="1"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="name" required="1"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="remark"/>
|
||||||
|
</group>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.ui.view" id="sf_processing_technology_tree">
|
||||||
|
<field name="model">sf.processing.technology</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="加工工艺">
|
||||||
|
<field name="process_encode"/>
|
||||||
|
<field name="name"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.ui.view" id="search_sf_processing_technology_view">
|
||||||
|
<field name="name">search.sf.processing.technology.type</field>
|
||||||
|
<field name="model">sf.processing.technology</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search>
|
||||||
|
<field name="name" string="名称搜索" filter_domain="[('name','ilike',self)]"/>
|
||||||
|
<field name="process_encode" string="编码搜索" filter_domain="[('process_encode','ilike',self)]"/>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<!--表面工艺-->
|
||||||
|
<record model="ir.ui.view" id="sf_production_process_tree">
|
||||||
|
<field name="model">sf.production.process</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="表面工艺">
|
||||||
|
<field name="process_encode" readonly="1"/>
|
||||||
|
<field name="name" readonly="1"/>
|
||||||
|
<field name="remark"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="sf_production_process_form">
|
||||||
|
<field name="model">sf.production.process</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="表面工艺">
|
||||||
|
<group>
|
||||||
|
<field name="process_encode" readonly="1"/>
|
||||||
|
<field name="name" required="1"/>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
<notebook>
|
||||||
|
<page string="工序">
|
||||||
|
<field name='processing_order_ids' options="{'no_create':True}" widget="one2many">
|
||||||
|
<tree editable='bottom'>
|
||||||
|
<field name="sequence" widget="handle"/>
|
||||||
|
<field name="processing_technology_ids" widget="many2many_tags">
|
||||||
|
</field>
|
||||||
|
</tree>
|
||||||
|
<form>
|
||||||
|
<field name="processing_technology_ids" widget="many2many">
|
||||||
|
</field>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
|
<group>
|
||||||
|
<field name="remark"/>
|
||||||
|
</group>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.ui.view" id="search_sf_production_process_view">
|
||||||
|
<field name="name">search.sf.production.process.type</field>
|
||||||
|
<field name="model">sf.production.process</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search>
|
||||||
|
<field name="name" string="名称搜索" filter_domain="[('name','ilike',self)]"/>
|
||||||
|
<field name="process_encode" string="编码搜索" filter_domain="[('process_encode','ilike',self)]"/>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<!--材料型号-->
|
||||||
|
<record model="ir.ui.view" id="sf_materials_model_form">
|
||||||
|
<field name="model">sf.materials.model</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="材料型号">
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="materials_no" readonly="1"/>
|
||||||
|
<field name="name" required="1"/>
|
||||||
|
<field name="gain_way" required="1"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="density" required="1"/>
|
||||||
|
<field name="need_h" default="false"/>
|
||||||
|
<field name="mf_materia_post" attrs="{'invisible':[('need_h','=',False)]} "/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name='materials_id' default="default" invisible="1"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name='supplier_ids'>
|
||||||
|
<tree editable='bottom'>
|
||||||
|
<field name="sequence" widget="handle" string="序号"/>
|
||||||
|
<field name="partner_id" string="名称"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="remark"/>
|
||||||
|
</group>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.ui.view" id="sf_materials_model_tree">
|
||||||
|
<field name="model">sf.materials.model</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="材料型号">
|
||||||
|
<field name="materials_no"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="need_h"/>
|
||||||
|
<field name="density"/>
|
||||||
|
<field name='materials_id' invisible="1"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.ui.view" id="search_sf_materials_model_type_view">
|
||||||
|
<field name="name">search.sf.materials.model.type</field>
|
||||||
|
<field name="model">sf.materials.model</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search>
|
||||||
|
<field name="name" string="型号名搜索" filter_domain="[('name','ilike',self)]"/>
|
||||||
|
<field name="materials_no" string="编码搜索" filter_domain="[('materials_no','ilike',self)]"/>
|
||||||
|
<searchpanel class="account_root">
|
||||||
|
<field name="materials_id" icon="fa-filter"/>
|
||||||
|
</searchpanel>
|
||||||
|
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<!--材料-->
|
||||||
|
<record model="ir.ui.view" id="sf_production_materials_form">
|
||||||
|
<field name="model">sf.production.materials</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="材料">
|
||||||
|
|
||||||
|
<group string="详情">
|
||||||
|
<group>
|
||||||
|
<field name="materials_no" readonly="1" default="编码"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="name" required="1"/>
|
||||||
|
</group>
|
||||||
|
<field name="materials_model_ids" widget="ony2many">
|
||||||
|
<tree string="材料型号">
|
||||||
|
<field name="materials_no"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="need_h"/>
|
||||||
|
<field name="mf_materia_post"/>
|
||||||
|
<field name="density"/>
|
||||||
|
<field name='materials_id' default="default" invisible="1"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="remark"/>
|
||||||
|
</group>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.ui.view" id="sf_production_materials_tree">
|
||||||
|
<field name="model">sf.production.materials</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="材料">
|
||||||
|
<field name="materials_no" readonly="1"/>
|
||||||
|
<field name="name" readonly="1"/>
|
||||||
|
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.ui.view" id="search_sf_production_materials_view">
|
||||||
|
<field name="name">search.sf.production.materials.type</field>
|
||||||
|
<field name="model">sf.production.materials</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search>
|
||||||
|
<field name="name" string="名称搜索" filter_domain="[('name','ilike',self)]"/>
|
||||||
|
<field name="materials_no" string="编码搜索" filter_domain="[('materials_no','ilike',self)]"/>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- <record model="ir.ui.view" id="sf_production_materials_form">-->
|
||||||
|
<!-- <field name="model">sf.production.materials</field>-->
|
||||||
|
<!-- <field name="arch" type="xml">-->
|
||||||
|
<!-- <form string="材料">-->
|
||||||
|
|
||||||
|
<!-- <group string="详情">-->
|
||||||
|
<!-- <group>-->
|
||||||
|
<!-- <field name="materials_no" required="1" default="编码"/>-->
|
||||||
|
<!-- </group>-->
|
||||||
|
<!-- <group>-->
|
||||||
|
<!-- <field name="name" required="1"/>-->
|
||||||
|
<!-- </group>-->
|
||||||
|
<!-- <field name="materials_model_ids">-->
|
||||||
|
|
||||||
|
<!-- </field>-->
|
||||||
|
<!-- </group>-->
|
||||||
|
<!-- </form>-->
|
||||||
|
<!-- </field>-->
|
||||||
|
<!-- </record>-->
|
||||||
|
|
||||||
|
<record id="sf_production_materials" model="ir.actions.act_window">
|
||||||
|
<field name="name">材料</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">sf.production.materials</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
材料!
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="sf_production_process" model="ir.actions.act_window">
|
||||||
|
<field name="name">表面工艺</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">sf.production.process</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
表面工艺!
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="sf_materials_model" model="ir.actions.act_window">
|
||||||
|
<field name="name">材料型号</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">sf.materials.model</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
材料型号!
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="sf_processing_technology" model="ir.actions.act_window">
|
||||||
|
<field name="name">加工工艺</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">sf.processing.technology</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
加工工艺!
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
#------------------托盘------------------
|
||||||
|
<record id="action_sf_tray" model="ir.actions.act_window">
|
||||||
|
<field name="name">托盘</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">sf.tray</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
创建托盘吧
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_sf_tray_search" model="ir.ui.view">
|
||||||
|
<field name="name">sf.tray.search</field>
|
||||||
|
<field name="model">sf.tray</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="托盘">
|
||||||
|
<field name="name" string="名称" filter_domain="[('name','ilike',self)]"/>
|
||||||
|
<field name="code" string="编码" filter_domain="[('code','ilike',self)]"/>
|
||||||
|
<group string="分组">
|
||||||
|
<filter name="state" string="状态" domain="[]" context="{'group_by': 'state'}"/>
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
|
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="tree_sf_tray_view">
|
||||||
|
<field name="name">sf.tray.tree</field>
|
||||||
|
<field name="model">sf.tray</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="托盘">
|
||||||
|
<field name="code"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="state"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="sf_tray_form">
|
||||||
|
<field name="name">sf.tray.form</field>
|
||||||
|
<field name="model">sf.tray</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="托盘">
|
||||||
|
<header>
|
||||||
|
<field name='state' widget="radio" options="{'horizontal': True}"/>
|
||||||
|
</header>
|
||||||
|
<group string="基本信息" name="group1">
|
||||||
|
<group>
|
||||||
|
<field name="code" required="1"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="name" required="1"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
145
sf_base/views/menu_view.xml
Normal file
145
sf_base/views/menu_view.xml
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<!-- <menuitem action="mrp_production_action"-->
|
||||||
|
<!-- id="menu_mrp_production_action"-->
|
||||||
|
<!-- parent="menu_mrp_manufacturing"-->
|
||||||
|
<!-- sequence="1"/>-->
|
||||||
|
<!-- name="Manufacturing-->
|
||||||
|
<!-- menu_mrp_configuration-->
|
||||||
|
<!--parent="menu_mrp_configuration"-->
|
||||||
|
<!-- <menuitem id="menu_mrp_config"
|
||||||
|
name="Settings"
|
||||||
|
parent="menu_mrp_configuration"
|
||||||
|
sequence="0"
|
||||||
|
action="action_mrp_configuration"
|
||||||
|
groups="base.group_system"/>-->
|
||||||
|
<menuitem
|
||||||
|
id="menu_sf_machine_tool"
|
||||||
|
parent="mrp.menu_mrp_configuration"
|
||||||
|
name="机床"
|
||||||
|
sequence="1"
|
||||||
|
action="action_sf_machine_tool"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="menu_sf_tray"
|
||||||
|
parent="mrp.menu_mrp_configuration"
|
||||||
|
name="托盘"
|
||||||
|
sequence="9"
|
||||||
|
action="action_sf_tray"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
sequence="2"
|
||||||
|
name="基础数据"
|
||||||
|
id="menu_sf_base"
|
||||||
|
action="sf_production_materials"
|
||||||
|
parent="mrp.menu_mrp_configuration"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="menu_sf_production_materials_1"
|
||||||
|
name="原材料"
|
||||||
|
parent="mrp.menu_mrp_configuration"
|
||||||
|
sequence="3"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="menu_sf_production_process_1"
|
||||||
|
name="工艺"
|
||||||
|
parent="mrp.menu_mrp_configuration"
|
||||||
|
sequence="4"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="menu_sf_production_process"
|
||||||
|
name="表面工艺"
|
||||||
|
parent="menu_sf_production_process_1"
|
||||||
|
sequence="1"
|
||||||
|
action="sf_production_process"
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="menu_sf_production_materials"
|
||||||
|
name="材料"
|
||||||
|
parent="menu_sf_production_materials_1"
|
||||||
|
sequence="1"
|
||||||
|
action="sf_production_materials"
|
||||||
|
|
||||||
|
|
||||||
|
/>
|
||||||
|
<menuitem
|
||||||
|
id="menu_sf_materials_model"
|
||||||
|
name="材料型号"
|
||||||
|
parent="menu_sf_production_materials_1"
|
||||||
|
sequence="1"
|
||||||
|
action="sf_materials_model"
|
||||||
|
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="menu_sf_processing_technology"
|
||||||
|
name="加工工艺"
|
||||||
|
parent="menu_sf_production_process_1"
|
||||||
|
sequence="1"
|
||||||
|
action="sf_processing_technology"/>
|
||||||
|
|
||||||
|
<!-- <menuitem-->
|
||||||
|
<!-- id="menu_sf_partner_views"-->
|
||||||
|
<!-- name="工厂token"-->
|
||||||
|
<!-- parent="menu_sf_base"-->
|
||||||
|
<!-- sequence="1"-->
|
||||||
|
<!-- action="token_factory_view"-->
|
||||||
|
<!-- />-->
|
||||||
|
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="menu_sf_machine_brand"
|
||||||
|
parent="menu_sf_base"
|
||||||
|
name="品牌"
|
||||||
|
sequence="1"
|
||||||
|
action="action_sf_machine_brand"/>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="menu_sf_machine_tool_type"
|
||||||
|
parent="menu_sf_base"
|
||||||
|
name="机床型号"
|
||||||
|
sequence="1"
|
||||||
|
action="action_sf_machine_tool_type"/>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="menu_sf_cutting_tool"
|
||||||
|
parent="mrp.menu_mrp_configuration"
|
||||||
|
name="刀具"
|
||||||
|
sequence="5"/>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="menu_sf_cutting_tool_category"
|
||||||
|
parent="menu_sf_cutting_tool"
|
||||||
|
name="刀具类别"
|
||||||
|
sequence="1"
|
||||||
|
action="action_sf_cutting_tool_category"/>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="menu_sf_cutting_tool_type"
|
||||||
|
parent="menu_sf_cutting_tool"
|
||||||
|
name="刀具型号"
|
||||||
|
sequence="1"
|
||||||
|
action="action_sf_cutting_tool_type"/>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="menu_sf_machine_control_system"
|
||||||
|
parent="menu_sf_base"
|
||||||
|
name="控制系统"
|
||||||
|
sequence="1"
|
||||||
|
action="action_sf_machine_control_system"/>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
4
sf_bf_connect/__init__.py
Normal file
4
sf_bf_connect/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# -*-coding:utf-8-*-
|
||||||
|
from . import controllers
|
||||||
|
from . import models
|
||||||
|
|
||||||
24
sf_bf_connect/__manifest__.py
Normal file
24
sf_bf_connect/__manifest__.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
{
|
||||||
|
'name': '机企猫智能工厂 对接业务平台',
|
||||||
|
'version': '1.0',
|
||||||
|
'summary': '连接业务平台模块',
|
||||||
|
'sequence': 1,
|
||||||
|
'description': """
|
||||||
|
在本模块,接收业务平台的模块
|
||||||
|
""",
|
||||||
|
'category': 'sf',
|
||||||
|
'website': 'https://www.sf.cs.jikimo.com',
|
||||||
|
'depends': ['sf_base', 'sf_sale', 'sf_dlm'],
|
||||||
|
'data': [
|
||||||
|
'views/res_partner_view.xml'
|
||||||
|
],
|
||||||
|
'demo': [
|
||||||
|
],
|
||||||
|
'qweb': [
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'application': False,
|
||||||
|
'auto_install': False,
|
||||||
|
}
|
||||||
2
sf_bf_connect/controllers/__init__.py
Normal file
2
sf_bf_connect/controllers/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from .import controllers
|
||||||
|
|
||||||
115
sf_bf_connect/controllers/controllers.py
Normal file
115
sf_bf_connect/controllers/controllers.py
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from odoo import http
|
||||||
|
from odoo.http import request
|
||||||
|
|
||||||
|
|
||||||
|
class Sf_Bf_Connect(http.Controller):
|
||||||
|
|
||||||
|
@http.route('/api/bfm_process_order/list', type='http', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
||||||
|
cors="*")
|
||||||
|
def get_bfm_process_order_list(self, **kw):
|
||||||
|
"""
|
||||||
|
获取业务平台传送来的订单
|
||||||
|
:param kw:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
res = {'status': 1, 'factory_order_no': ''}
|
||||||
|
logging.info('get_bfm_process_order_list:%s' % kw)
|
||||||
|
try:
|
||||||
|
product_id = request.env.ref('sf_dlm.product_template_sf').sudo()
|
||||||
|
self_machining_id = request.env.ref('sf_dlm.product_embryo_sf_self_machining').sudo()
|
||||||
|
outsource_id = request.env.ref('sf_dlm.product_embryo_sf_outsource').sudo()
|
||||||
|
purchase_id = request.env.ref('sf_dlm.product_embryo_sf_purchase').sudo()
|
||||||
|
company_id = request.env.ref('base.main_company').sudo()
|
||||||
|
# user_id = request.env.ref('base.user_admin').sudo()
|
||||||
|
bfm_process_order_list = json.loads(kw['bfm_process_order_list'])
|
||||||
|
order_id = request.env['sale.order'].with_user(request.env.ref("base.user_admin")).sale_order_create(
|
||||||
|
company_id, kw['delivery_name'], kw['delivery_telephone'], kw['delivery_address'],
|
||||||
|
kw['delivery_end_date'])
|
||||||
|
i = 1
|
||||||
|
# 给sale_order的default_code字段赋值
|
||||||
|
aa = request.env['sale.order'].sudo().search([('name', '=', order_id.name)])
|
||||||
|
logging.info('get_bfm_process_or===================================:%s' % order_id.name)
|
||||||
|
aa.default_code = kw['order_number']
|
||||||
|
logging.info('get_bfm_process_order_listaaaaaaaaaaaaaaaaaaaaaaaaaaaa================:%s' % aa.default_code)
|
||||||
|
for item in bfm_process_order_list:
|
||||||
|
product = request.env['product.template'].sudo().product_create(product_id, item, order_id,
|
||||||
|
kw['order_number'], i)
|
||||||
|
bom_data = request.env['mrp.bom'].with_user(request.env.ref("base.user_admin")).get_bom(product)
|
||||||
|
logging.info('bom_data:%s' % bom_data)
|
||||||
|
if bom_data:
|
||||||
|
bom = request.env['mrp.bom'].with_user(request.env.ref("base.user_admin")).bom_create(product,
|
||||||
|
'normal',
|
||||||
|
False)
|
||||||
|
bom.with_user(request.env.ref("base.user_admin")).bom_create_line_has(bom_data)
|
||||||
|
else:
|
||||||
|
if product.materials_type_id.gain_way == '自加工':
|
||||||
|
# 创建胚料
|
||||||
|
self_machining_embryo = request.env['product.template'].sudo().no_bom_product_create(
|
||||||
|
self_machining_id,
|
||||||
|
item,
|
||||||
|
order_id, 'self_machining', i)
|
||||||
|
# 创建胚料的bom
|
||||||
|
self_machining_bom = request.env['mrp.bom'].with_user(
|
||||||
|
request.env.ref("base.user_admin")).bom_create(
|
||||||
|
self_machining_embryo, 'normal', False)
|
||||||
|
# 创建胚料里bom的组件
|
||||||
|
self_machining_bom_line = self_machining_bom.with_user(
|
||||||
|
request.env.ref("base.user_admin")).bom_create_line(
|
||||||
|
self_machining_embryo)
|
||||||
|
if self_machining_bom_line == False:
|
||||||
|
res['status'] = 2
|
||||||
|
res['message'] = '该订单模型的材料型号在您分配的工厂里暂未有原材料,请先配置再进行分配'
|
||||||
|
request.cr.rollback()
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
|
# 产品配置bom
|
||||||
|
product_bom_self_machining = request.env['mrp.bom'].with_user(
|
||||||
|
request.env.ref("base.user_admin")).bom_create(
|
||||||
|
product, 'normal', False)
|
||||||
|
product_bom_self_machining.with_user(request.env.ref("base.user_admin")).bom_create_line_has(
|
||||||
|
self_machining_embryo)
|
||||||
|
elif product.materials_type_id.gain_way == '外协':
|
||||||
|
# 创建胚料
|
||||||
|
outsource_embryo = request.env['product.template'].sudo().no_bom_product_create(outsource_id,
|
||||||
|
item,
|
||||||
|
order_id,
|
||||||
|
'subcontract',
|
||||||
|
i)
|
||||||
|
# 创建胚料的bom
|
||||||
|
outsource_bom = request.env['mrp.bom'].with_user(request.env.ref("base.user_admin")).bom_create(
|
||||||
|
outsource_embryo,
|
||||||
|
'subcontract', True)
|
||||||
|
# 创建胚料的bom的组件
|
||||||
|
outsource_bom_line = outsource_bom.with_user(
|
||||||
|
request.env.ref("base.user_admin")).bom_create_line(outsource_embryo)
|
||||||
|
if outsource_bom_line == False:
|
||||||
|
res['status'] = 2
|
||||||
|
res['message'] = '该订单模型的材料型号在您分配的工厂里暂未有原材料,请先配置再进行分配'
|
||||||
|
request.cr.rollback()
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
|
# 产品配置bom
|
||||||
|
product_bom_outsource = request.env['mrp.bom'].with_user(
|
||||||
|
request.env.ref("base.user_admin")).bom_create(product, 'normal', False)
|
||||||
|
product_bom_outsource.with_user(request.env.ref("base.user_admin")).bom_create_line_has(
|
||||||
|
outsource_embryo)
|
||||||
|
elif product.materials_type_id.gain_way == '采购':
|
||||||
|
purchase_embryo = request.env['product.template'].sudo().no_bom_product_create(purchase_id,
|
||||||
|
item,
|
||||||
|
order_id,
|
||||||
|
'purchase', i)
|
||||||
|
# 产品配置bom
|
||||||
|
product_bom_purchase = request.env['mrp.bom'].with_user(
|
||||||
|
request.env.ref("base.user_admin")).bom_create(product, 'normal', False)
|
||||||
|
product_bom_purchase.with_user(request.env.ref("base.user_admin")).bom_create_line_has(
|
||||||
|
purchase_embryo)
|
||||||
|
order_id.with_user(request.env.ref("base.user_admin")).sale_order_create_line(product, item)
|
||||||
|
i += 1
|
||||||
|
res['factory_order_no'] = order_id.name
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
|
except Exception as e:
|
||||||
|
logging.info('get_bfm_process_order_list error:%s' % e)
|
||||||
|
res['status'] = -1
|
||||||
|
res['message'] = '工厂创建销售订单和产品失败,请联系管理员'
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
4
sf_bf_connect/models/__init__.py
Normal file
4
sf_bf_connect/models/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from . import http
|
||||||
|
from . import models
|
||||||
|
from . import process_status
|
||||||
|
|
||||||
47
sf_bf_connect/models/http.py
Normal file
47
sf_bf_connect/models/http.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import logging
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
import hashlib
|
||||||
|
from odoo import models
|
||||||
|
from odoo.http import request
|
||||||
|
|
||||||
|
__author__ = 'jinling.yang'
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class AuthenticationError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class AuthenticationError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Http(models.AbstractModel):
|
||||||
|
_inherit = 'ir.http'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _auth_method_sf_token(cls):
|
||||||
|
# 从headers.environ中获取对方传过来的token,timestamp,加密的校验字符串
|
||||||
|
datas = request.httprequest.headers.environ
|
||||||
|
if 'HTTP_TOKEN' in datas:
|
||||||
|
_logger.info('token:%s' % datas['HTTP_TOKEN'])
|
||||||
|
# 查询密钥
|
||||||
|
factory_secret = request.env['res.partner'].sudo().search(
|
||||||
|
[('sf_token', '=', datas['HTTP_TOKEN'])], limit=1)
|
||||||
|
logging.info('factory_secret:%s' % factory_secret)
|
||||||
|
if not factory_secret:
|
||||||
|
raise AuthenticationError('无效的token')
|
||||||
|
timestamp_str = int(time.time())
|
||||||
|
# 设置API接口请求时间,不能超过5秒
|
||||||
|
deltime = datetime.timedelta(seconds=30)
|
||||||
|
#if abs(int(datas['HTTP_TIMESTAMP'])-timestamp_str) > deltime.seconds:
|
||||||
|
#raise AuthenticationError('请求已过期')
|
||||||
|
# 获得sha1_str加密字符串
|
||||||
|
post_time = int(datas['HTTP_TIMESTAMP'])
|
||||||
|
check_str = '%s%s%s' % (datas['HTTP_TOKEN'], post_time, factory_secret.sf_secret_key)
|
||||||
|
check_sf_str = hashlib.sha1(check_str.encode('utf-8')).hexdigest()
|
||||||
|
if check_sf_str != datas['HTTP_CHECKSTR']:
|
||||||
|
raise AuthenticationError('数据校验不通过')
|
||||||
|
else:
|
||||||
|
raise AuthenticationError('请求参数中无token')
|
||||||
28
sf_bf_connect/models/models.py
Normal file
28
sf_bf_connect/models/models.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import logging
|
||||||
|
import uuid
|
||||||
|
import string
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
__author__ = 'jinling.yang'
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ResPartner(models.Model):
|
||||||
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
|
# 获取token,token自动生成且唯一
|
||||||
|
def get_token(self):
|
||||||
|
return uuid.uuid1()
|
||||||
|
|
||||||
|
# 获取密钥(大小字母+数字随机)
|
||||||
|
def get_secret(self):
|
||||||
|
ran_str = ''.join(random.sample(string.ascii_letters + string.digits, 16))
|
||||||
|
return ran_str
|
||||||
|
|
||||||
|
sf_token = fields.Char(u'Token', default=get_token)
|
||||||
|
sf_secret_key = fields.Char(u'密钥', default=get_secret)
|
||||||
110
sf_bf_connect/models/process_status.py
Normal file
110
sf_bf_connect/models/process_status.py
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
from odoo import api, fields, models, SUPERUSER_ID, _
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
from datetime import datetime
|
||||||
|
import logging
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
import requests
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class StatusChange(models.Model):
|
||||||
|
_inherit = 'sale.order'
|
||||||
|
|
||||||
|
default_code = fields.Char(string='内部编码')
|
||||||
|
|
||||||
|
def action_confirm(self):
|
||||||
|
logging.info('函数已经执行=============')
|
||||||
|
if self._get_forbidden_state_confirm() & set(self.mapped('state')):
|
||||||
|
raise UserError(_(
|
||||||
|
"It is not allowed to confirm an order in the following states: %s",
|
||||||
|
", ".join(self._get_forbidden_state_confirm()),
|
||||||
|
))
|
||||||
|
logging.info('函数已经执行=============1')
|
||||||
|
for order in self:
|
||||||
|
if order.partner_id in order.message_partner_ids:
|
||||||
|
logging.info('函数已经执行=============2')
|
||||||
|
continue
|
||||||
|
order.message_subscribe([order.partner_id.id])
|
||||||
|
logging.info('函数已经执行=============3')
|
||||||
|
self.write(self._prepare_confirmation_values())
|
||||||
|
|
||||||
|
# Context key 'default_name' is sometimes propagated up to here.
|
||||||
|
# We don't need it and it creates issues in the creation of linked records.
|
||||||
|
context = self._context.copy()
|
||||||
|
context.pop('default_name', None)
|
||||||
|
logging.info('函数已经执行=============4')
|
||||||
|
|
||||||
|
self.with_context(context)._action_confirm()
|
||||||
|
if self.env.user.has_group('sale.group_auto_done_setting'):
|
||||||
|
logging.info('函数已经执行=============5')
|
||||||
|
self.action_done()
|
||||||
|
process_start_time = str(datetime.now())
|
||||||
|
json1 = {
|
||||||
|
'params': {
|
||||||
|
'model_name': 'jikimo.process.order',
|
||||||
|
'field_name': 'name',
|
||||||
|
# 'default_code': 'PO-2022-1214-0022',
|
||||||
|
'default_code': self.default_code,
|
||||||
|
# 'default_code': self.name,
|
||||||
|
'state': '加工中',
|
||||||
|
'process_start_time': process_start_time,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
url1 = 'https://bfm.cs.jikimo.com/api/get/state/get_order'
|
||||||
|
requests.post(url1, json=json1, data=None)
|
||||||
|
logging.info('接口已经执行=============')
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def action_cancel(self):
|
||||||
|
""" Cancel SO after showing the cancel wizard when needed. (cfr `_show_cancel_wizard`)
|
||||||
|
|
||||||
|
For post-cancel operations, please only override `_action_cancel`.
|
||||||
|
|
||||||
|
note: self.ensure_one() if the wizard is shown.
|
||||||
|
"""
|
||||||
|
logging.info('函数已经执行=============')
|
||||||
|
cancel_warning = self._show_cancel_wizard()
|
||||||
|
logging.info('函数已经执行=============2')
|
||||||
|
json1 = {
|
||||||
|
'params': {
|
||||||
|
'model_name': 'jikimo.process.order',
|
||||||
|
'field_name': 'name',
|
||||||
|
'default_code': self.default_code,
|
||||||
|
# 'default_code': self.name,
|
||||||
|
'state': '待派单',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
url1 = 'https://bfm.cs.jikimo.com/api/get/state/cancel_order'
|
||||||
|
requests.post(url1, json=json1, data=None)
|
||||||
|
if cancel_warning:
|
||||||
|
logging.info('函数已经执行=============3')
|
||||||
|
self.ensure_one()
|
||||||
|
logging.info('函数已经执行=============4')
|
||||||
|
template_id = self.env['ir.model.data']._xmlid_to_res_id(
|
||||||
|
'sale.mail_template_sale_cancellation', raise_if_not_found=False
|
||||||
|
)
|
||||||
|
lang = self.env.context.get('lang')
|
||||||
|
template = self.env['mail.template'].browse(template_id)
|
||||||
|
if template.lang:
|
||||||
|
lang = template._render_lang(self.ids)[self.id]
|
||||||
|
ctx = {
|
||||||
|
'default_use_template': bool(template_id),
|
||||||
|
'default_template_id': template_id,
|
||||||
|
'default_order_id': self.id,
|
||||||
|
'mark_so_as_canceled': True,
|
||||||
|
'default_email_layout_xmlid': "mail.mail_notification_layout_with_responsible_signature",
|
||||||
|
'model_description': self.with_context(lang=lang).type_name,
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
'name': _('Cancel %s', self.type_name),
|
||||||
|
'view_mode': 'form',
|
||||||
|
'res_model': 'sale.order.cancel',
|
||||||
|
'view_id': self.env.ref('sale.sale_order_cancel_view_form').id,
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'context': ctx,
|
||||||
|
'target': 'new'
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return self._action_cancel()
|
||||||
|
|
||||||
20
sf_bf_connect/views/res_partner_view.xml
Normal file
20
sf_bf_connect/views/res_partner_view.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="view_partner_form_sf" model="ir.ui.view">
|
||||||
|
<field name="name">view_partner_form</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//page[last()]" position="before">
|
||||||
|
<page string="token和密钥">
|
||||||
|
<group>
|
||||||
|
<field name="sf_token"/>
|
||||||
|
<field name="sf_secret_key"/>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
1
sf_dlm/__init__.py
Normal file
1
sf_dlm/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import models
|
||||||
26
sf_dlm/__manifest__.py
Normal file
26
sf_dlm/__manifest__.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
{
|
||||||
|
'name': '机企猫智能工厂 产品管理',
|
||||||
|
'version': '1.0',
|
||||||
|
'summary': '智能工厂产品模块',
|
||||||
|
'sequence': 1,
|
||||||
|
'description': """
|
||||||
|
在本模块,为业务平台传过来的订单信息
|
||||||
|
""",
|
||||||
|
'category': 'sf',
|
||||||
|
'website': 'https://www.sf.jikimo.com',
|
||||||
|
'depends': ['mrp', 'base', 'sf_manufacturing'],
|
||||||
|
'data': [
|
||||||
|
'data/product_data.xml',
|
||||||
|
'views/product_template_view.xml',
|
||||||
|
'views/product_workorder.xml'
|
||||||
|
],
|
||||||
|
'demo': [
|
||||||
|
],
|
||||||
|
'qweb': [
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'application': False,
|
||||||
|
'auto_install': False,
|
||||||
|
}
|
||||||
79
sf_dlm/data/product_data.xml
Normal file
79
sf_dlm/data/product_data.xml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record id="product_category_embryo_sf" model="product.category">
|
||||||
|
<field name="name">胚料</field>
|
||||||
|
<field name="type">胚料</field>
|
||||||
|
</record>
|
||||||
|
<record id="product_category_finished_sf" model="product.category">
|
||||||
|
<field name="name">成品</field>
|
||||||
|
<field name="type">成品</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_category_raw_sf" model="product.category">
|
||||||
|
<field name="name">原材料</field>
|
||||||
|
<field name="type">原材料</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_template_sf" model="product.product">
|
||||||
|
<field name="name">CNC加工产品模板</field>
|
||||||
|
<field name="active" eval="False"/>
|
||||||
|
<field name="categ_id" ref="product_category_finished_sf"/>
|
||||||
|
<field name="route_ids"
|
||||||
|
eval="[ref('stock.route_warehouse0_mto'), ref('mrp.route_warehouse0_manufacture')]"/>
|
||||||
|
<field name="invoice_policy">delivery</field>
|
||||||
|
<field name="detailed_type">product</field>
|
||||||
|
<field name="purchase_ok">false</field>
|
||||||
|
<field name="uom_id" ref="uom.product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="uom.product_uom_unit"/>
|
||||||
|
<field name="company_id" ref="base.main_company"/>
|
||||||
|
<field name="single_manufacturing">true</field>
|
||||||
|
<field name="tracking">serial</field>
|
||||||
|
|
||||||
|
</record>
|
||||||
|
<record id="product_embryo_sf_self_machining" model="product.product">
|
||||||
|
<field name="name">胚料自加工模板</field>
|
||||||
|
<field name="active" eval="False"/>
|
||||||
|
<field name="categ_id" ref="product_category_embryo_sf"/>
|
||||||
|
<field name="route_ids"
|
||||||
|
eval="[ref('stock.route_warehouse0_mto'), ref('mrp.route_warehouse0_manufacture')]"/>
|
||||||
|
<field name="invoice_policy">delivery</field>
|
||||||
|
<field name="detailed_type">product</field>
|
||||||
|
<field name="purchase_ok">false</field>
|
||||||
|
<field name="uom_id" ref="uom.product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="uom.product_uom_unit"/>
|
||||||
|
<field name="company_id" ref="base.main_company"/>
|
||||||
|
<field name="single_manufacturing">true</field>
|
||||||
|
<field name="tracking">serial</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_embryo_sf_outsource" model="product.product">
|
||||||
|
<field name="name">胚料外协加工模板</field>
|
||||||
|
<field name="active" eval="False"/>
|
||||||
|
<field name="categ_id" ref="product_category_embryo_sf"/>
|
||||||
|
<field name="route_ids"
|
||||||
|
eval="[ref('stock.route_warehouse0_mto'), ref('purchase_stock.route_warehouse0_buy'),ref('mrp_subcontracting.route_resupply_subcontractor_mto')]"/>
|
||||||
|
<field name="invoice_policy">delivery</field>
|
||||||
|
<field name="detailed_type">product</field>
|
||||||
|
<field name="purchase_ok">true</field>
|
||||||
|
<field name="uom_id" ref="uom.product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="uom.product_uom_unit"/>
|
||||||
|
<field name="company_id" ref="base.main_company"/>
|
||||||
|
<field name="tracking">serial</field>
|
||||||
|
</record>
|
||||||
|
<record id="product_embryo_sf_purchase" model="product.product">
|
||||||
|
<field name="name">胚料采购模板</field>
|
||||||
|
<field name="active" eval="False"/>
|
||||||
|
<field name="categ_id" ref="product_category_embryo_sf"/>
|
||||||
|
<field name="route_ids"
|
||||||
|
eval="[ref('stock.route_warehouse0_mto'), ref('purchase_stock.route_warehouse0_buy')]"/>
|
||||||
|
<field name="invoice_policy">delivery</field>
|
||||||
|
<field name="detailed_type">product</field>
|
||||||
|
<field name="purchase_ok">true</field>
|
||||||
|
<field name="uom_id" ref="uom.product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="uom.product_uom_unit"/>
|
||||||
|
<field name="company_id" ref="base.main_company"/>
|
||||||
|
<field name="tracking">serial</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
11
sf_dlm/data/uom_data.xml
Normal file
11
sf_dlm/data/uom_data.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo noupdate="1">
|
||||||
|
<!-- UOM.UOM -->
|
||||||
|
<!-- VOLUME -->
|
||||||
|
<record id="product_uom_cubic_millimeter" model="uom.uom">
|
||||||
|
<field name="name">立方毫米</field>
|
||||||
|
<field name="category_id" ref="uom.product_uom_categ_vol"/>
|
||||||
|
<field name="factor_inv">1000</field>
|
||||||
|
<field name="uom_type">bigger</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
6
sf_dlm/models/__init__.py
Normal file
6
sf_dlm/models/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from. import product_template
|
||||||
|
from. import product_supplierinfo
|
||||||
|
from. import product_workorder
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
12
sf_dlm/models/product_supplierinfo.py
Normal file
12
sf_dlm/models/product_supplierinfo.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from odoo import models
|
||||||
|
|
||||||
|
|
||||||
|
class ResSupplierInfo(models.Model):
|
||||||
|
_inherit = 'product.supplierinfo'
|
||||||
|
|
||||||
|
def _compute_is_subcontractor(self):
|
||||||
|
for supplier in self:
|
||||||
|
boms = supplier.product_id.variant_bom_ids
|
||||||
|
boms |= supplier.product_tmpl_id.bom_ids.filtered(lambda b: not b.product_id or b.product_id in (
|
||||||
|
supplier.product_id or supplier.product_tmpl_id.product_variant_ids))
|
||||||
|
supplier.is_subcontractor = supplier.partner_id in boms.subcontractor_id
|
||||||
292
sf_dlm/models/product_template.py
Normal file
292
sf_dlm/models/product_template.py
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
from odoo import models, fields
|
||||||
|
import logging
|
||||||
|
import base64
|
||||||
|
|
||||||
|
|
||||||
|
class ResProductTemplate(models.Model):
|
||||||
|
_inherit = 'product.template'
|
||||||
|
|
||||||
|
# 模型的长,宽,高,体积,精度,材料
|
||||||
|
model_name = fields.Char('模型名称')
|
||||||
|
categ_type = fields.Selection(
|
||||||
|
[("成品", "成品"), ("胚料", "胚料"), ("原材料", "原材料")], string='产品的类别', related='categ_id.type', store=True)
|
||||||
|
model_long = fields.Float('模型长[mm]', digits=(16, 3))
|
||||||
|
model_width = fields.Float('模型宽[mm]', digits=(16, 3))
|
||||||
|
model_height = fields.Float('模型高[mm]', digits=(16, 3))
|
||||||
|
model_volume = fields.Float('模型体积[m³]')
|
||||||
|
model_machining_precision = fields.Selection([
|
||||||
|
('0.10', '±0.10mm'),
|
||||||
|
('0.05', '±0.05mm'),
|
||||||
|
('0.03', '±0.03mm'),
|
||||||
|
('0.02', '±0.02mm'),
|
||||||
|
('0.01', '±0.01mm')], string='加工精度')
|
||||||
|
product_model_type_id = fields.Many2one('sf.model.type', string='产品模型类型')
|
||||||
|
embryo_model_type_id = fields.Many2one('sf.model.type', string='胚料模型类型')
|
||||||
|
model_processing_panel = fields.Char('模型加工面板')
|
||||||
|
model_surface_process_id = fields.Many2one('sf.production.process', string='表面工艺')
|
||||||
|
model_process_parameters_id = fields.Many2one('sf.processing.technology', string='工艺参数')
|
||||||
|
# model_price = fields.Float('模型单价', digits=(16, 3))
|
||||||
|
model_remark = fields.Char('模型备注说明')
|
||||||
|
length = fields.Float('长[mm]', digits=(16, 3))
|
||||||
|
width = fields.Float('宽[mm]', digits=(16, 3))
|
||||||
|
height = fields.Float('高[mm]', digits=(16, 3))
|
||||||
|
materials_id = fields.Many2one('sf.production.materials', string='材料')
|
||||||
|
materials_type_id = fields.Many2one('sf.materials.model', string='材料型号')
|
||||||
|
single_manufacturing = fields.Boolean(string="单个制造")
|
||||||
|
upload_model_file = fields.Many2many('ir.attachment', 'upload_model_file_attachment_ref', string='上传模型文件')
|
||||||
|
model_code = fields.Char('模型编码')
|
||||||
|
is_bfm = fields.Boolean('业务平台是否自动创建', default=False)
|
||||||
|
|
||||||
|
def _get_volume_uom_id_from_ir_config_parameter(self):
|
||||||
|
product_length_in_feet_param = self.env['ir.config_parameter'].sudo().get_param('product.volume_in_cubic_feet')
|
||||||
|
if product_length_in_feet_param == '1':
|
||||||
|
return self.env.ref('uom.product_uom_cubic_foot')
|
||||||
|
else:
|
||||||
|
return self.env.ref('sf_dlm.product_uom_cubic_millimeter')
|
||||||
|
|
||||||
|
# model_file = fields.Binary('模型文件')
|
||||||
|
|
||||||
|
# 胚料的库存路线设置
|
||||||
|
# def _get_routes(self, route_type):
|
||||||
|
# route_manufacture = self.env.ref('mrp.route_warehouse0_manufacture', raise_if_not_found=False).sudo()
|
||||||
|
# route_mto = self.env.ref('stock.route_warehouse0_mto', raise_if_not_found=False).sudo()
|
||||||
|
# route_purchase = self.env.ref('purchase_stock.route_warehouse0_buy', raise_if_not_found=False).sudo()
|
||||||
|
# if route_manufacture and route_mto:
|
||||||
|
# # 外协
|
||||||
|
# if route_type == 'subcontract':
|
||||||
|
# route_subcontract = self.env.ref('mrp_subcontracting.route_resupply_subcontractor_mto',
|
||||||
|
# raise_if_not_found=False).sudo()
|
||||||
|
# return [route_mto.id, route_purchase.id, route_subcontract.id]
|
||||||
|
# elif route_type == 'purchase':
|
||||||
|
# # 采购
|
||||||
|
# return [route_mto.id, route_purchase.id]
|
||||||
|
# else:
|
||||||
|
# return [route_mto.id, route_manufacture.id]
|
||||||
|
# return []
|
||||||
|
|
||||||
|
# route_ids = fields.Many2many(default=lambda self: self._get_route())
|
||||||
|
|
||||||
|
# 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品
|
||||||
|
def product_create(self, product_id, item, order_id, order_number, i):
|
||||||
|
copy_product_id = product_id.with_user(self.env.ref("base.user_admin")).copy()
|
||||||
|
copy_product_id.product_tmpl_id.active = True
|
||||||
|
model_type = self.env['sf.model.type'].search([], limit=1)
|
||||||
|
attachment = self.attachment_create(item['model_name'], item['model_data'])
|
||||||
|
vals = {
|
||||||
|
'name': '%s-%s-%s' % ('P', order_id.name, i),
|
||||||
|
'model_long': item['model_long'] + model_type.embryo_tolerance,
|
||||||
|
'model_width': item['model_width'] + model_type.embryo_tolerance,
|
||||||
|
'model_height': item['model_height'] + model_type.embryo_tolerance,
|
||||||
|
'model_volume': (item['model_long'] + model_type.embryo_tolerance) * (
|
||||||
|
item['model_width'] + model_type.embryo_tolerance) * (
|
||||||
|
item['model_height'] + model_type.embryo_tolerance),
|
||||||
|
'product_model_type_id': model_type.id,
|
||||||
|
'model_processing_panel': 'R',
|
||||||
|
'model_machining_precision': item['model_machining_precision'],
|
||||||
|
'model_code': item['barcode'],
|
||||||
|
'length': item['model_long'],
|
||||||
|
'width': item['model_width'],
|
||||||
|
'height': item['model_height'],
|
||||||
|
'volume': item['model_long'] * item['model_width'] * item['model_height'],
|
||||||
|
'model_file': '' if not item['model_file'] else base64.b64decode(item['model_file']),
|
||||||
|
'model_name': attachment.name,
|
||||||
|
'upload_model_file': [(6, 0, [attachment.id])],
|
||||||
|
# 'single_manufacturing': True,
|
||||||
|
# 'tracking': 'serial',
|
||||||
|
'list_price': item['price'],
|
||||||
|
# 'categ_id': self.env.ref('sf_dlm.product_category_finished_sf').id,
|
||||||
|
'materials_id': self.env['sf.production.materials'].search(
|
||||||
|
[('materials_no', '=', item['texture_code'])]).id,
|
||||||
|
'materials_type_id': self.env['sf.materials.model'].search(
|
||||||
|
[('materials_no', '=', item['texture_type_code'])]).id,
|
||||||
|
'model_surface_process_id': self.env['sf.production.process'].search(
|
||||||
|
[('process_encode', '=', item['surface_process_code'])]).id,
|
||||||
|
# 'model_process_parameters_id': self.env['sf.processing.technology'].search(
|
||||||
|
# [('process_encode', '=', item['process_parameters_code'])]).id,
|
||||||
|
'model_remark': item['remark'],
|
||||||
|
'default_code': '%s-%s' % (order_number, i),
|
||||||
|
# 'barcode': item['barcode'],
|
||||||
|
'active': True,
|
||||||
|
# 'route_ids': self._get_routes('')
|
||||||
|
}
|
||||||
|
copy_product_id.sudo().write(vals)
|
||||||
|
# product_id.product_tmpl_id.active = False
|
||||||
|
return copy_product_id
|
||||||
|
|
||||||
|
def attachment_create(self, name, data):
|
||||||
|
attachment = self.env['ir.attachment'].create({
|
||||||
|
'datas': base64.b64decode(data),
|
||||||
|
'type': 'binary',
|
||||||
|
'public': True,
|
||||||
|
'description': '模型文件',
|
||||||
|
'name': name
|
||||||
|
})
|
||||||
|
return attachment
|
||||||
|
|
||||||
|
# 创建胚料
|
||||||
|
def no_bom_product_create(self, product_id, item, order_id, route_type, i):
|
||||||
|
no_bom_copy_product_id = product_id.with_user(self.env.ref("base.user_admin")).copy()
|
||||||
|
no_bom_copy_product_id.product_tmpl_id.active = True
|
||||||
|
materials_id = self.env['sf.production.materials'].search(
|
||||||
|
[('materials_no', '=', item['texture_code'])])
|
||||||
|
materials_type_id = self.env['sf.materials.model'].search(
|
||||||
|
[('materials_no', '=', item['texture_type_code'])])
|
||||||
|
model_type = self.env['sf.model.type'].search([], limit=1)
|
||||||
|
supplier = self.env['mrp.bom'].get_supplier(materials_type_id)
|
||||||
|
logging.info('no_bom_copy_product_supplier-vals:%s' % supplier)
|
||||||
|
vals = {
|
||||||
|
'name': '%s-%s-%s [%s %s-%s * %s * %s]' % ('R',
|
||||||
|
order_id.name, i, materials_id.name, materials_type_id.name,
|
||||||
|
item['model_long'] + model_type.embryo_tolerance,
|
||||||
|
item['model_width'] + model_type.embryo_tolerance,
|
||||||
|
item['model_height'] + model_type.embryo_tolerance),
|
||||||
|
'length': item['model_long'] + model_type.embryo_tolerance,
|
||||||
|
'width': item['model_width'] + model_type.embryo_tolerance,
|
||||||
|
'height': item['model_height'] + model_type.embryo_tolerance,
|
||||||
|
'volume': (item['model_long'] + model_type.embryo_tolerance) * (
|
||||||
|
item['model_width'] + model_type.embryo_tolerance) * (
|
||||||
|
item['model_height'] + model_type.embryo_tolerance),
|
||||||
|
'embryo_model_type_id': model_type.id,
|
||||||
|
'list_price': item['price'],
|
||||||
|
'materials_id': materials_id.id,
|
||||||
|
'materials_type_id': materials_type_id.id,
|
||||||
|
'is_bfm': True,
|
||||||
|
# 'route_ids': self._get_routes(route_type),
|
||||||
|
# 'categ_id': self.env.ref('sf_dlm.product_category_embryo_sf').id,
|
||||||
|
# 'model_surface_process_id': self.env['sf.production.process'].search(
|
||||||
|
# [('process_encode', '=', item['surface_process_code'])]).id,
|
||||||
|
# 'model_process_parameters_id': self.env['sf.processing.technology'].search(
|
||||||
|
# [('process_encode', '=', item['process_parameters_code'])]).id,
|
||||||
|
'active': True
|
||||||
|
}
|
||||||
|
# 外协和采购生成的胚料需要根据材料型号绑定供应商
|
||||||
|
if route_type == 'subcontract' or route_type == 'purchase':
|
||||||
|
no_bom_copy_product_id.purchase_ok = True
|
||||||
|
no_bom_copy_product_id.seller_ids = [
|
||||||
|
(0, 0, {'partner_id': supplier.partner_id.id, 'delay': 1.0})]
|
||||||
|
if route_type == 'subcontract':
|
||||||
|
partner = self.env['res.partner'].search([('id', '=', supplier.partner_id.id)])
|
||||||
|
partner.is_subcontractor = True
|
||||||
|
no_bom_copy_product_id.write(vals)
|
||||||
|
logging.info('no_bom_copy_product_id-vals:%s' % vals)
|
||||||
|
# product_id.product_tmpl_id.active = False
|
||||||
|
return no_bom_copy_product_id
|
||||||
|
|
||||||
|
# @api.onchange('upload_model_file')
|
||||||
|
# def onchange_model_file(self):
|
||||||
|
# for item in self:
|
||||||
|
# if len(item.upload_model_file) > 1:
|
||||||
|
# raise ValidationError('只允许上传一个文件')
|
||||||
|
# if item.upload_model_file:
|
||||||
|
# file_attachment_id = item.upload_model_file[0]
|
||||||
|
# item.model_name = file_attachment_id.name
|
||||||
|
# # 附件路径
|
||||||
|
# report_path = file_attachment_id._full_path(file_attachment_id.store_fname)
|
||||||
|
# shapes = read_step_file(report_path)
|
||||||
|
# output_file = get_resource_path('sf_dlm', 'static/file', 'out.stl')
|
||||||
|
# write_stl_file(shapes, output_file, 'binary', 0.03, 0.5)
|
||||||
|
# # 转化为glb
|
||||||
|
# output_glb_file = get_resource_path('sf_dlm', 'static/file', 'out.glb')
|
||||||
|
# util_path = get_resource_path('sf_dlm', 'static/util')
|
||||||
|
# cmd = 'python %s/stl2gltf.py %s %s -b' % (util_path, output_file, output_glb_file)
|
||||||
|
# os.system(cmd)
|
||||||
|
# # 转base64
|
||||||
|
# with open(output_glb_file, 'rb') as fileObj:
|
||||||
|
# image_data = fileObj.read()
|
||||||
|
# base64_data = base64.b64encode(image_data)
|
||||||
|
# item.model_file = base64_data
|
||||||
|
|
||||||
|
|
||||||
|
class ResMrpBom(models.Model):
|
||||||
|
_inherit = 'mrp.bom'
|
||||||
|
|
||||||
|
subcontractor_id = fields.Many2one('res.partner', string='外包商')
|
||||||
|
|
||||||
|
def bom_create_line_has(self, embryo):
|
||||||
|
vals = {
|
||||||
|
'bom_id': self.id,
|
||||||
|
'product_id': embryo.id,
|
||||||
|
'product_tmpl_id': embryo.product_tmpl_id.id,
|
||||||
|
'product_qty': 1,
|
||||||
|
'product_uom_id': 1
|
||||||
|
}
|
||||||
|
return self.env['mrp.bom.line'].create(vals)
|
||||||
|
|
||||||
|
# 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品后再次进行创建bom
|
||||||
|
def bom_create(self, product, bom_type, product_type):
|
||||||
|
bom_id = self.env['mrp.bom'].create({
|
||||||
|
'product_tmpl_id': product.product_tmpl_id.id,
|
||||||
|
'type': bom_type,
|
||||||
|
# 'subcontractor_id': '' or subcontract.partner_id.id,
|
||||||
|
'product_qty': 1,
|
||||||
|
'product_uom_id': 1
|
||||||
|
})
|
||||||
|
if bom_type == 'subcontract' and product_type is not False:
|
||||||
|
subcontract = self.get_supplier(product.materials_type_id)
|
||||||
|
bom_id.subcontractor_id = subcontract.partner_id.id
|
||||||
|
return bom_id
|
||||||
|
|
||||||
|
# 胚料BOM组件:选取当前胚料原材料,
|
||||||
|
# 然后根据当前的胚料的体积得出需要的原材料重量(立方米m³) *材料密度 * 1000 = 所需原材料重量KG(公斤)
|
||||||
|
# 胚料所需原材料公式:当前的胚料的体积(立方米m³) *材料密度 * 1000 = 所需原材料重量KG(公斤)
|
||||||
|
def bom_create_line(self, embryo):
|
||||||
|
# 选取当前胚料原材料
|
||||||
|
raw_bom_line = self.get_raw_bom(embryo)
|
||||||
|
if raw_bom_line:
|
||||||
|
bom_line = self.env['mrp.bom.line'].create({
|
||||||
|
'bom_id': self.id,
|
||||||
|
'product_id': raw_bom_line.id,
|
||||||
|
'product_tmpl_id': raw_bom_line.product_tmpl_id.id,
|
||||||
|
'product_qty': round(embryo.volume * raw_bom_line.materials_type_id.density / 1000000),
|
||||||
|
'product_uom_id': raw_bom_line.uom_id.id,
|
||||||
|
})
|
||||||
|
return bom_line
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 查询材料型号默认排第一的供应商
|
||||||
|
def get_supplier(self, materials_type):
|
||||||
|
seller_id = self.env['sf.supplier.sort'].search(
|
||||||
|
[('materials_model_id', '=', materials_type.id)],
|
||||||
|
limit=1,
|
||||||
|
order='sequence asc')
|
||||||
|
return seller_id
|
||||||
|
|
||||||
|
# 匹配bom
|
||||||
|
def get_bom(self, product):
|
||||||
|
embryo_has = self.env['product.product'].search(
|
||||||
|
[('categ_id.type', '=', '胚料'), ('materials_type_id', '=', product.materials_type_id.id),
|
||||||
|
('length', '>', product.length), ('width', '>', product.width),
|
||||||
|
('height', '>', product.height), ('is_bfm', '=', False)
|
||||||
|
],
|
||||||
|
limit=1,
|
||||||
|
order='volume desc'
|
||||||
|
)
|
||||||
|
logging.info('get_bom-vals:%s' % embryo_has)
|
||||||
|
if embryo_has:
|
||||||
|
rate_of_waste = ((embryo_has.volume - product.model_volume) % embryo_has.volume) * 100
|
||||||
|
if rate_of_waste <= 20:
|
||||||
|
return embryo_has
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 查bom的原材料
|
||||||
|
def get_raw_bom(self, product):
|
||||||
|
raw_bom = self.env['product.product'].search(
|
||||||
|
[('categ_id.type', '=', '原材料'), ('materials_type_id', '=', product.materials_type_id.id)])
|
||||||
|
return raw_bom
|
||||||
|
|
||||||
|
|
||||||
|
class ResProductCategory(models.Model):
|
||||||
|
_inherit = "product.category"
|
||||||
|
|
||||||
|
type = fields.Selection(
|
||||||
|
[("成品", "成品"), ("胚料", "胚料"), ("原材料", "原材料")],
|
||||||
|
default="", string="类型")
|
||||||
|
|
||||||
|
# @api.constrains('type')
|
||||||
|
# def _check_type(self):
|
||||||
|
# category = self.env['product.category'].search(
|
||||||
|
# [('type', '=', self.type)])
|
||||||
|
# if category:
|
||||||
|
# raise ValidationError("该类别已存在,请选择其他类别")
|
||||||
13
sf_dlm/models/product_workorder.py
Normal file
13
sf_dlm/models/product_workorder.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ResMrpWorkOrder(models.Model):
|
||||||
|
_inherit = 'mrp.workorder'
|
||||||
|
_order = 'sequence'
|
||||||
|
|
||||||
|
product_tmpl_id_length = fields.Float(related='production_id.product_tmpl_id.length', readonly=True, store=True, check_company=True, string="胚料长度(mm)")
|
||||||
|
product_tmpl_id_width = fields.Float(related='production_id.product_tmpl_id.width', readonly=True, store=True, check_company=True, string="胚料宽度(mm)")
|
||||||
|
product_tmpl_id_height = fields.Float(related='production_id.product_tmpl_id.height', readonly=True, store=True, check_company=True, string="胚料高度(mm)")
|
||||||
|
product_tmpl_id_materials_id = fields.Many2one(related='production_id.product_tmpl_id.materials_id', readonly=True, store=True, check_company=True, string="材料")
|
||||||
|
product_tmpl_id_materials_type_id = fields.Many2one(related='production_id.product_tmpl_id.materials_type_id', readonly=True, store=True, check_company=True, string="型号")
|
||||||
BIN
sf_dlm/static/file/out.glb
Normal file
BIN
sf_dlm/static/file/out.glb
Normal file
Binary file not shown.
BIN
sf_dlm/static/file/out.stl
Normal file
BIN
sf_dlm/static/file/out.stl
Normal file
Binary file not shown.
277
sf_dlm/static/util/stl2gltf.py
Normal file
277
sf_dlm/static/util/stl2gltf.py
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
def stl_to_gltf(binary_stl_path, out_path, is_binary):
|
||||||
|
import struct
|
||||||
|
|
||||||
|
gltf2 = '''
|
||||||
|
{
|
||||||
|
"scenes" : [
|
||||||
|
{
|
||||||
|
"nodes" : [ 0 ]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nodes" : [
|
||||||
|
{
|
||||||
|
"mesh" : 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meshes" : [
|
||||||
|
{
|
||||||
|
"primitives" : [ {
|
||||||
|
"attributes" : {
|
||||||
|
"POSITION" : 1
|
||||||
|
},
|
||||||
|
"indices" : 0
|
||||||
|
} ]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buffers" : [
|
||||||
|
{
|
||||||
|
%s
|
||||||
|
"byteLength" : %d
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bufferViews" : [
|
||||||
|
{
|
||||||
|
"buffer" : 0,
|
||||||
|
"byteOffset" : 0,
|
||||||
|
"byteLength" : %d,
|
||||||
|
"target" : 34963
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer" : 0,
|
||||||
|
"byteOffset" : %d,
|
||||||
|
"byteLength" : %d,
|
||||||
|
"target" : 34962
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessors" : [
|
||||||
|
{
|
||||||
|
"bufferView" : 0,
|
||||||
|
"byteOffset" : 0,
|
||||||
|
"componentType" : 5125,
|
||||||
|
"count" : %d,
|
||||||
|
"type" : "SCALAR",
|
||||||
|
"max" : [ %d ],
|
||||||
|
"min" : [ 0 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView" : 1,
|
||||||
|
"byteOffset" : 0,
|
||||||
|
"componentType" : 5126,
|
||||||
|
"count" : %d,
|
||||||
|
"type" : "VEC3",
|
||||||
|
"min" : [%f, %f, %f],
|
||||||
|
"max" : [%f, %f, %f]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"asset" : {
|
||||||
|
"version" : "2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
header_bytes = 80
|
||||||
|
unsigned_long_int_bytes = 4
|
||||||
|
float_bytes = 4
|
||||||
|
vec3_bytes = 4 * 3
|
||||||
|
spacer_bytes = 2
|
||||||
|
num_vertices_in_face = 3
|
||||||
|
|
||||||
|
vertices = {}
|
||||||
|
indices = []
|
||||||
|
|
||||||
|
if not is_binary:
|
||||||
|
out_bin = os.path.join(out_path, "out.bin")
|
||||||
|
out_gltf = os.path.join(out_path, "out.gltf")
|
||||||
|
else:
|
||||||
|
out_bin = out_path
|
||||||
|
|
||||||
|
unpack_face = struct.Struct("<12fH").unpack
|
||||||
|
face_bytes = float_bytes*12 + 2
|
||||||
|
|
||||||
|
with open(path_to_stl, "rb") as f:
|
||||||
|
f.seek(header_bytes) # skip 80 bytes headers
|
||||||
|
|
||||||
|
num_faces_bytes = f.read(unsigned_long_int_bytes)
|
||||||
|
number_faces = struct.unpack("<I", num_faces_bytes)[0]
|
||||||
|
|
||||||
|
# the vec3_bytes is for normal
|
||||||
|
stl_assume_bytes = header_bytes + unsigned_long_int_bytes + number_faces * (vec3_bytes*3 + spacer_bytes + vec3_bytes)
|
||||||
|
assert stl_assume_bytes == os.path.getsize(path_to_stl), "stl is not binary or ill formatted"
|
||||||
|
|
||||||
|
minx, maxx = [9999999, -9999999]
|
||||||
|
miny, maxy = [9999999, -9999999]
|
||||||
|
minz, maxz = [9999999, -9999999]
|
||||||
|
|
||||||
|
vertices_length_counter = 0
|
||||||
|
|
||||||
|
data = struct.unpack("<" + "12fH"*number_faces, f.read())
|
||||||
|
len_data = len(data)
|
||||||
|
|
||||||
|
for i in range(0, len_data, 13):
|
||||||
|
for j in range(3, 12, 3):
|
||||||
|
x, y, z = data[i+j:i+j+3]
|
||||||
|
|
||||||
|
x = int(x*100000)/100000
|
||||||
|
y = int(y*100000)/100000
|
||||||
|
z = int(z*100000)/100000
|
||||||
|
|
||||||
|
tuple_xyz = (x, y, z);
|
||||||
|
|
||||||
|
try:
|
||||||
|
indices.append(vertices[tuple_xyz])
|
||||||
|
except KeyError:
|
||||||
|
vertices[tuple_xyz] = vertices_length_counter
|
||||||
|
vertices_length_counter += 1
|
||||||
|
indices.append(vertices[tuple_xyz])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if x < minx: minx = x
|
||||||
|
if x > maxx: maxx = x
|
||||||
|
if y < miny: miny = y
|
||||||
|
if y > maxy: maxy = y
|
||||||
|
if z < minz: minz = z
|
||||||
|
if z > maxz: maxz = z
|
||||||
|
|
||||||
|
# f.seek(spacer_bytes, 1) # skip the spacer
|
||||||
|
|
||||||
|
number_vertices = len(vertices)
|
||||||
|
vertices_bytelength = number_vertices * vec3_bytes # each vec3 has 3 floats, each float is 4 bytes
|
||||||
|
unpadded_indices_bytelength = number_vertices * unsigned_long_int_bytes
|
||||||
|
|
||||||
|
out_number_vertices = len(vertices)
|
||||||
|
out_number_indices = len(indices)
|
||||||
|
|
||||||
|
unpadded_indices_bytelength = out_number_indices * unsigned_long_int_bytes
|
||||||
|
indices_bytelength = (unpadded_indices_bytelength + 3) & ~3
|
||||||
|
|
||||||
|
out_bin_bytelength = vertices_bytelength + indices_bytelength
|
||||||
|
|
||||||
|
if is_binary:
|
||||||
|
out_bin_uir = ""
|
||||||
|
else:
|
||||||
|
out_bin_uir = '"uri": "out.bin",'
|
||||||
|
|
||||||
|
gltf2 = gltf2 % ( out_bin_uir,
|
||||||
|
#buffer
|
||||||
|
out_bin_bytelength,
|
||||||
|
|
||||||
|
# bufferViews[0]
|
||||||
|
indices_bytelength,
|
||||||
|
|
||||||
|
# bufferViews[1]
|
||||||
|
indices_bytelength,
|
||||||
|
vertices_bytelength,
|
||||||
|
|
||||||
|
# accessors[0]
|
||||||
|
out_number_indices,
|
||||||
|
out_number_vertices - 1,
|
||||||
|
|
||||||
|
# accessors[1]
|
||||||
|
out_number_vertices,
|
||||||
|
minx, miny, minz,
|
||||||
|
maxx, maxy, maxz
|
||||||
|
)
|
||||||
|
|
||||||
|
glb_out = bytearray()
|
||||||
|
if is_binary:
|
||||||
|
gltf2 = gltf2.replace(" ", "")
|
||||||
|
gltf2 = gltf2.replace("\n", "")
|
||||||
|
|
||||||
|
scene = bytearray(gltf2.encode())
|
||||||
|
|
||||||
|
scene_len = len(scene)
|
||||||
|
padded_scene_len = (scene_len + 3) & ~3
|
||||||
|
body_offset = padded_scene_len + 12 + 8
|
||||||
|
|
||||||
|
file_len = body_offset + out_bin_bytelength + 8
|
||||||
|
|
||||||
|
# 12-byte header
|
||||||
|
glb_out.extend(struct.pack('<I', 0x46546C67)) # magic number for glTF
|
||||||
|
glb_out.extend(struct.pack('<I', 2))
|
||||||
|
glb_out.extend(struct.pack('<I', file_len))
|
||||||
|
|
||||||
|
# chunk 0
|
||||||
|
glb_out.extend(struct.pack('<I', padded_scene_len))
|
||||||
|
glb_out.extend(struct.pack('<I', 0x4E4F534A)) # magic number for JSON
|
||||||
|
glb_out.extend(scene)
|
||||||
|
|
||||||
|
while len(glb_out) < body_offset:
|
||||||
|
glb_out.extend(b' ')
|
||||||
|
|
||||||
|
# chunk 1
|
||||||
|
glb_out.extend(struct.pack('<I', out_bin_bytelength))
|
||||||
|
glb_out.extend(struct.pack('<I', 0x004E4942)) # magin number for BIN
|
||||||
|
|
||||||
|
# print('<%dI' % len(indices))
|
||||||
|
# print(struct.pack('<%dI' % len(indices), *indices))
|
||||||
|
glb_out.extend(struct.pack('<%dI' % len(indices), *indices))
|
||||||
|
|
||||||
|
for i in range(indices_bytelength - unpadded_indices_bytelength):
|
||||||
|
glb_out.extend(b' ')
|
||||||
|
|
||||||
|
vertices = dict((v, k) for k,v in vertices.items())
|
||||||
|
|
||||||
|
# glb_out.extend(struct.pack('f',
|
||||||
|
# print([each_v for vertices[v_counter] for v_counter in range(number_vertices)]) # magin number for BIN
|
||||||
|
vertices = [vertices[i] for i in range(number_vertices)]
|
||||||
|
flatten = lambda l: [item for sublist in l for item in sublist]
|
||||||
|
|
||||||
|
# for v_counter in :
|
||||||
|
# v_3f = vertices[v_counter]
|
||||||
|
# all_floats_in_vertices.append(v_3f[0])
|
||||||
|
# all_floats_in_vertices.append(v_3f[1])
|
||||||
|
# all_floats_in_vertices.append(v_3f[2])
|
||||||
|
|
||||||
|
# for v_counter in range(number_vertices):
|
||||||
|
glb_out.extend(struct.pack('%df' % number_vertices*3, *flatten(vertices))) # magin number for BIN
|
||||||
|
|
||||||
|
# for v_counter in range(number_vertices):
|
||||||
|
# glb_out.extend(struct.pack('3f', *vertices[v_counter])) # magin number for BIN
|
||||||
|
|
||||||
|
# for (v_x, v_y, v_z), _ in sorted(vertices.items(), key=lambda x: x[1]):
|
||||||
|
# glb_out.extend(struct.pack('3f', v_x, v_y, v_z)) # magin number for BIN
|
||||||
|
# # glb_out.extend(struct.pack('f', v_y)) # magin number for BIN
|
||||||
|
# # glb_out.extend(struct.pack('f', v_z)) # magin number for BIN
|
||||||
|
|
||||||
|
with open(out_bin, "wb") as out:
|
||||||
|
out.write(glb_out)
|
||||||
|
|
||||||
|
if not is_binary:
|
||||||
|
with open(out_gltf, "w") as out:
|
||||||
|
out.write(gltf2)
|
||||||
|
|
||||||
|
print("Done! Exported to %s" %out_path)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if len(sys.argv) < 3:
|
||||||
|
print("use it like python3 stl_to_gltf.py /path/to/stl /path/to/gltf/folder")
|
||||||
|
print("or python3 stl_to_gltf.py /path/to/stl /path/to/glb/file -b")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
path_to_stl = sys.argv[1]
|
||||||
|
out_path = sys.argv[2]
|
||||||
|
if len(sys.argv) > 3:
|
||||||
|
is_binary = True
|
||||||
|
else:
|
||||||
|
is_binary = False
|
||||||
|
|
||||||
|
if out_path.lower().endswith(".glb"):
|
||||||
|
print("Use binary mode since output file has glb extension")
|
||||||
|
is_binary = True
|
||||||
|
else:
|
||||||
|
if is_binary:
|
||||||
|
print("output file should have glb extension but not %s", out_path)
|
||||||
|
|
||||||
|
if not os.path.exists(path_to_stl):
|
||||||
|
print("stl file does not exists %s" % path_to_stl)
|
||||||
|
|
||||||
|
if not is_binary:
|
||||||
|
if not os.path.isdir(out_path):
|
||||||
|
os.mkdir(out_path)
|
||||||
|
|
||||||
|
stl_to_gltf(path_to_stl, out_path, is_binary)
|
||||||
144
sf_dlm/views/product_template_view.xml
Normal file
144
sf_dlm/views/product_template_view.xml
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record model="ir.ui.view" id="view_product_template_form_inherit_sf">
|
||||||
|
<field name="name">product.template.form.inherit.sf</field>
|
||||||
|
<field name="model">product.template</field>
|
||||||
|
<field name="inherit_id" ref="product.product_template_only_form_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<!-- <field name="image_1920" position="replace">-->
|
||||||
|
<!-- <field name="upload_model_file" required="True"-->
|
||||||
|
<!-- widget='many2many_binary'/>-->
|
||||||
|
<!-- </field>-->
|
||||||
|
|
||||||
|
<field name="invoice_policy" position="after">
|
||||||
|
<!-- <field name="model_file" widget="model_viewer"-->
|
||||||
|
<!-- attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>-->
|
||||||
|
<!-- <field name="embryo_model_type_id" string="模型类型"-->
|
||||||
|
<!-- attrs="{'invisible': [('categ_type', '=', '胚料')]}"/>-->
|
||||||
|
<field name="materials_id" string="材料"/>
|
||||||
|
<field name="materials_type_id" string="型号"
|
||||||
|
domain="[('materials_id', '=', materials_id)]"/>
|
||||||
|
</field>
|
||||||
|
|
||||||
|
<xpath expr="//label[@for='volume']" position="before">
|
||||||
|
<label for="length" string="尺寸"
|
||||||
|
attrs="{'invisible':[('product_variant_count', '>', 1), ('is_product_variant', '=', False)]}"/>
|
||||||
|
<div class="o_address_format"
|
||||||
|
attrs="{'invisible':[('product_variant_count', '>', 1), ('is_product_variant', '=', False)]}">
|
||||||
|
<label for="length" string="长"/>
|
||||||
|
<field name="length" class="o_address_zip"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="width" string="宽"/>
|
||||||
|
<field name="width" class="o_address_zip"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="height" string="高"/>
|
||||||
|
<field name="height" class="o_address_zip"/>
|
||||||
|
</div>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
|
<xpath expr="//page[last()]" position="after">
|
||||||
|
<page string="加工参数">
|
||||||
|
<group>
|
||||||
|
<group string="模型">
|
||||||
|
<label for="model_long" string="尺寸[mm]"/>
|
||||||
|
<div class="o_address_format">
|
||||||
|
<label for="model_long" string="长"/>
|
||||||
|
<field name="model_long" class="o_address_zip"/>
|
||||||
|
<!-- <span>&nbsp;</span>-->
|
||||||
|
<label for="model_width" string="宽"/>
|
||||||
|
<field name="model_width" class="o_address_zip"/>
|
||||||
|
<!-- <span>&nbsp;</span>-->
|
||||||
|
<label for="model_height" string="高"/>
|
||||||
|
<field name="model_height" class="o_address_zip"/>
|
||||||
|
</div>
|
||||||
|
<!-- <field name="model_long" string="长[mm]"/>-->
|
||||||
|
<!-- <field name="model_width" string="宽[mm]"/>-->
|
||||||
|
<!-- <field name="model_height" string="高[mm]"/>-->
|
||||||
|
<field name="model_volume" string="体积[mm³]"/>
|
||||||
|
<field name="product_model_type_id" string="模型类型"/>
|
||||||
|
<field name="model_processing_panel" placeholder="例如R,U" string="加工面板"/>
|
||||||
|
<field name="model_machining_precision"/>
|
||||||
|
<field name="model_surface_process_id" string="表面工艺"/>
|
||||||
|
<field name="model_process_parameters_id" string="工艺参数"
|
||||||
|
domain="[('processing_order_ids', '=', model_surface_process_id)]"/>
|
||||||
|
<field name="model_remark" string="备注说明"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_product_category_form_inherit_sf" model="ir.ui.view">
|
||||||
|
<field name="name">product.category.form.inherit.sf</field>
|
||||||
|
<field name="model">product.category</field>
|
||||||
|
<field name="inherit_id" ref="product.product_category_form_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="parent_id" position="before">
|
||||||
|
<field name="type"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_template_property_form" model="ir.ui.view">
|
||||||
|
<field name="name">product.template.stock.property.form.inherit</field>
|
||||||
|
<field name="model">product.template</field>
|
||||||
|
<field name="inherit_id" ref="product.product_template_form_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//group[@name='group_lots_and_weight']" position="after">
|
||||||
|
<group string="订单规则">
|
||||||
|
<field name="single_manufacturing" string="单个制造" widget="checkbox"/>
|
||||||
|
</group>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
|
<page name="inventory" position="inside">
|
||||||
|
<group>
|
||||||
|
<group string="Description for Receipts">
|
||||||
|
<field name="description_pickingin" nolabel="1"
|
||||||
|
placeholder="This note is added to receipt orders (e.g. where to store the product in the warehouse)."/>
|
||||||
|
</group>
|
||||||
|
<group string="Description for Delivery Orders">
|
||||||
|
<field name="description_pickingout" nolabel="1"
|
||||||
|
placeholder="This note is added to delivery orders."/>
|
||||||
|
</group>
|
||||||
|
<group string="Description for Internal Transfers" groups="stock.group_stock_multi_locations">
|
||||||
|
<field name="description_picking"
|
||||||
|
placeholder="This note is added to internal transfer orders (e.g. where to pick the product in the warehouse)."
|
||||||
|
nolabel="1"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
<page name="inventory" position="attributes">
|
||||||
|
<attribute name="groups">stock.group_stock_user,product.group_stock_packaging</attribute>
|
||||||
|
</page>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_mrp_bom_form_inherit_sf" model="ir.ui.view">
|
||||||
|
<field name="name">mrp.bom.form.inherit.sf</field>
|
||||||
|
<field name="model">mrp.bom</field>
|
||||||
|
<field name="inherit_id" ref="mrp.mrp_bom_form_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="subcontractor_ids" position="replace">
|
||||||
|
<field name="subcontractor_id"
|
||||||
|
attrs="{'invisible': [('type', '!=', 'subcontract')], 'required': [('type', '=', 'subcontract')]}"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_template_search_view_sf" model="ir.ui.view">
|
||||||
|
<field name="name">product.template.search</field>
|
||||||
|
<field name="model">product.template</field>
|
||||||
|
<field name="inherit_id" ref="product.product_template_search_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//filter[@name='consumable']" position="after">
|
||||||
|
<separator/>
|
||||||
|
<filter name="finish_product" string="成品" domain="[('categ_id.type','=','成品')]"/>
|
||||||
|
<filter name="embryo" string="胚料" domain="[('categ_id.type','=','胚料')]"/>
|
||||||
|
<filter name="raw_bom" string="原材料" domain="[('categ_id.type','=','原材料')]"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
27
sf_dlm/views/product_workorder.xml
Normal file
27
sf_dlm/views/product_workorder.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_mrp_production_workorder_tray_form_inherit_sf1" model="ir.ui.view">
|
||||||
|
<field name="name">production.workorder.dlm</field>
|
||||||
|
<field name="model">mrp.workorder</field>
|
||||||
|
<field name="inherit_id" ref="sf_manufacturing.view_mrp_production_workorder_tray_form_inherit_sf"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//page[1]" position="before">
|
||||||
|
<page string="开料要求" attrs='{"invisible": [("routing_type","!=","切割")]}'>
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="product_tmpl_id_materials_id" widget="many2one"/>
|
||||||
|
<field name="product_tmpl_id_materials_type_id" widget="many2one"/>
|
||||||
|
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="product_tmpl_id_length"/>
|
||||||
|
<field name="product_tmpl_id_width"/>
|
||||||
|
<field name="product_tmpl_id_height"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
2
sf_machine_connect/__init__.py
Normal file
2
sf_machine_connect/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from . import models
|
||||||
|
from . import wizard
|
||||||
42
sf_machine_connect/__manifest__.py
Normal file
42
sf_machine_connect/__manifest__.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
{
|
||||||
|
'name': '机企猫智能工厂 对接机床',
|
||||||
|
'version': '1.0',
|
||||||
|
'summary': 'sf_machine_connect',
|
||||||
|
'sequence': 10,
|
||||||
|
'description': """
|
||||||
|
这是一个连接机床的模块
|
||||||
|
====================
|
||||||
|
""",
|
||||||
|
'category': 'sf',
|
||||||
|
'author': 'jikimo',
|
||||||
|
'website': 'https://sf.cs.jikimo.com',
|
||||||
|
'depends': ['sf_base', 'sf_manufacturing', 'barcodes', ],
|
||||||
|
'data': [
|
||||||
|
# 定义权限组放在最上面
|
||||||
|
# 权限组
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
'wizard/up_select.xml',
|
||||||
|
'views/default_delivery.xml',
|
||||||
|
'views/ftp_button.xml',
|
||||||
|
|
||||||
|
'views/compensation.xml',
|
||||||
|
|
||||||
|
'views/SfWorkOrderBarcodes.xml',
|
||||||
|
'views/WorkCenterBarcodes.xml',
|
||||||
|
'views/Stock_picking_Barcodes.xml',
|
||||||
|
'views/machine_monitor.xml',
|
||||||
|
'views/machine_info_present.xml',
|
||||||
|
'views/delivery_record.xml',
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
'assets': {
|
||||||
|
},
|
||||||
|
|
||||||
|
'installable': True,
|
||||||
|
'application': True,
|
||||||
|
# 'auto_install': False,
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
}
|
||||||
3
sf_machine_connect/models/__init__.py
Normal file
3
sf_machine_connect/models/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from . import ftp_client
|
||||||
|
from . import ftp_operate
|
||||||
|
from . import py2opcua
|
||||||
487
sf_machine_connect/models/ftp_client.py
Normal file
487
sf_machine_connect/models/ftp_client.py
Normal file
@@ -0,0 +1,487 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import base64
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import hashlib
|
||||||
|
import time
|
||||||
|
import requests
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
from odoo import fields, models, api, _
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
from odoo.addons.sf_machine_connect.models import py2opcua, ftp_operate
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# Models for client
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
|
class FtpButton(models.Model):
|
||||||
|
_inherit = 'sf.cnc.processing'
|
||||||
|
|
||||||
|
user_permissions = fields.Boolean(string='权限', related='workorder_id.user_permissions')
|
||||||
|
state = fields.Selection(string='状态', related='workorder_id.state')
|
||||||
|
|
||||||
|
@api.onchange('users_ids')
|
||||||
|
def get_user_permissions(self):
|
||||||
|
uid = self.env.uid
|
||||||
|
for workorder in self:
|
||||||
|
if workorder.users_ids:
|
||||||
|
list_user_id = []
|
||||||
|
for item in workorder.users_ids:
|
||||||
|
list_user_id.append(item.id)
|
||||||
|
if uid in list_user_id:
|
||||||
|
workorder.user_permissions = True
|
||||||
|
else:
|
||||||
|
workorder.user_permissions = False
|
||||||
|
else:
|
||||||
|
workorder.user_permissions = False
|
||||||
|
|
||||||
|
def up(self):
|
||||||
|
"""
|
||||||
|
此函数用于将NC代码下发到机床
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# 点击下发按钮自动补偿三元检测偏差值
|
||||||
|
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
if self.sequence_number == '1':
|
||||||
|
self.check_compensation_before_up()
|
||||||
|
except Exception:
|
||||||
|
raise UserError("补偿值写入执行超时,请检查机床状态或者写入状态")
|
||||||
|
|
||||||
|
# host="192.168.2.158", port=8080, username="MITSUBISHI", password="CNC"
|
||||||
|
host = self.workorder_id.workcenter_id.machine_tool_id.ftp_host
|
||||||
|
port = self.workorder_id.workcenter_id.machine_tool_id.ftp_port
|
||||||
|
username = self.workorder_id.workcenter_id.machine_tool_id.ftp_num
|
||||||
|
pwd = self.workorder_id.workcenter_id.machine_tool_id.ftp_pwd
|
||||||
|
remote_path = self.workorder_id.workcenter_id.machine_tool_id.ftp_remote_path
|
||||||
|
print(host, port, username, pwd, remote_path)
|
||||||
|
ftp = ftp_operate.FtpController(host, port, username, pwd)
|
||||||
|
# ftp.delAllfile('C://Users//马广威//Desktop//ftp')
|
||||||
|
a = self.cnc_id
|
||||||
|
datas = base64.standard_b64decode(a.datas)
|
||||||
|
|
||||||
|
# file_path = '{}\{}\{}'.format(a._filestore(), a.store_fname.split('/'[0]), a.display_name)
|
||||||
|
file_path_local = '{}/{}'.format('/nc2machine', a.display_name)
|
||||||
|
# file_path_remote = '{}\{}'.format('//M80(192,168,2,142)//DS', a.display_name)
|
||||||
|
file_path_remote = '{}\{}'.format(remote_path, a.display_name)
|
||||||
|
|
||||||
|
with open(file_path_local, mode='wb+') as file:
|
||||||
|
file.write(datas)
|
||||||
|
# 存在本地的文件下发到机床
|
||||||
|
ftp.upload_file(remotepath=file_path_remote, localpath=file_path_local)
|
||||||
|
_logger.info("======%s下发成功======" % a.display_name)
|
||||||
|
ftp.close_ftp()
|
||||||
|
|
||||||
|
up_time = str(datetime.now() + timedelta(hours=8))
|
||||||
|
self.env['delivery.record'].create({
|
||||||
|
'workorder_id': self.workorder_id.id,
|
||||||
|
'delivery_type': '单个下发',
|
||||||
|
'delivery_time': up_time,
|
||||||
|
'influence_record': self.sequence_number,
|
||||||
|
})
|
||||||
|
# self.workorder_id.delivery_records.sudo().create({
|
||||||
|
# 'delivery_type': '单个下发',
|
||||||
|
# 'delivery_time': 'up_time',
|
||||||
|
# 'influence_record': 'self.sequence_number',
|
||||||
|
# })
|
||||||
|
except Exception as e:
|
||||||
|
_logger.info("=====================================", e)
|
||||||
|
raise UserError('NC下发执行超时, 请检查下发状态')
|
||||||
|
|
||||||
|
# 补偿下发都执行完毕后,按钮标志位变为true
|
||||||
|
self.button_state = True
|
||||||
|
|
||||||
|
def check_compensation_before_up(self):
|
||||||
|
"""
|
||||||
|
下发NC代码前自动补偿三元检测偏差值
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
hongbianliang550 = self.workorder_id.workcenter_id.machine_tool_id.x_compensation_node
|
||||||
|
hongbianliang551 = self.workorder_id.workcenter_id.machine_tool_id.y_compensation_node
|
||||||
|
try:
|
||||||
|
temp_dict = {}
|
||||||
|
temp_dict[hongbianliang550] = self.workorder_id.compensation_value_x
|
||||||
|
temp_dict[hongbianliang551] = self.workorder_id.compensation_value_y
|
||||||
|
temp = py2opcua.Py2opcua()
|
||||||
|
temp.write(temp_dict)
|
||||||
|
except Exception as e:
|
||||||
|
_logger.info("=====================================", e)
|
||||||
|
raise UserError('补偿值获取失败,或机床未连接,请检查')
|
||||||
|
|
||||||
|
|
||||||
|
class Machine_ftp(models.Model):
|
||||||
|
"""
|
||||||
|
数据采集类
|
||||||
|
"""
|
||||||
|
_inherit = 'sf.machine_tool'
|
||||||
|
|
||||||
|
workorder_ids = fields.One2many('mrp.workorder', 'machine_tool_id', string='工单')
|
||||||
|
|
||||||
|
# 机床配置项目
|
||||||
|
# ftp相关
|
||||||
|
ftp_num = fields.Char('ftp账号')
|
||||||
|
ftp_pwd = fields.Char('ftp密码')
|
||||||
|
ftp_host = fields.Char('ftp地址')
|
||||||
|
ftp_port = fields.Integer('ftp端口')
|
||||||
|
ftp_remote_path = fields.Char('机床ftp路径')
|
||||||
|
# 补偿值写入相关
|
||||||
|
x_compensation_node = fields.Char('x补偿值节点')
|
||||||
|
y_compensation_node = fields.Char('y补偿值节点')
|
||||||
|
# 数采配置相关
|
||||||
|
machine_ip = fields.Char('机床IP')
|
||||||
|
machine_signed = fields.Char('机床刷新间隔')
|
||||||
|
machine_status = fields.Char('机床在线状态')
|
||||||
|
machine_cnc_type = fields.Char('机床CNC型号')
|
||||||
|
machine_axis_count = fields.Char('机床轴总数')
|
||||||
|
machine_run_status = fields.Char('机床运行状态')
|
||||||
|
machine_emg_status = fields.Char('机床急停状态')
|
||||||
|
machine_cut_status = fields.Char('机床当前切削状态')
|
||||||
|
machine_mode = fields.Char('机床当前操作模式')
|
||||||
|
machine_spindle_load = fields.Char('机床主轴负载')
|
||||||
|
machine_x_mach = fields.Char('机床X轴机械坐标')
|
||||||
|
machine_x_abs_mach = fields.Char('机床X轴当前位置')
|
||||||
|
machine_x_rel_mach = fields.Char('机床X轴相对工件坐标')
|
||||||
|
machine_x_dis_mach = fields.Char('机床X轴目标距离')
|
||||||
|
machine_x_axis_load = fields.Char('机床X轴伺服轴负载')
|
||||||
|
machine_y_mach = fields.Char('机床Y轴机械坐标')
|
||||||
|
machine_y_abs_mach = fields.Char('机床Y轴当前位置')
|
||||||
|
machine_y_rel_mach = fields.Char('机床Y轴相对工件坐标')
|
||||||
|
machine_y_dis_mach = fields.Char('机床Y轴目标距离')
|
||||||
|
machine_y_axis_load = fields.Char('机床Y轴伺服轴负载')
|
||||||
|
machine_z_mach = fields.Char('机床Z轴机械坐标')
|
||||||
|
machine_z_abs_mach = fields.Char('机床Z轴当前位置')
|
||||||
|
machine_z_rel_mach = fields.Char('机床Z轴相对工件坐标')
|
||||||
|
machine_z_dis_mach = fields.Char('机床Z轴目标距离')
|
||||||
|
machine_z_axis_load = fields.Char('机床Z轴伺服轴负载')
|
||||||
|
machine_tool_num = fields.Char('机床当前刀位号')
|
||||||
|
machine_program = fields.Char('机床主程序名称')
|
||||||
|
machine_current_prg = fields.Char('机床当前执行指令')
|
||||||
|
machine_prg_seq = fields.Char('机床当前执行语句号')
|
||||||
|
machine_spindle_speed_set = fields.Char('机床设定主轴速度')
|
||||||
|
machine_act_spindle_speed = fields.Char('机床实际主轴转速')
|
||||||
|
machine_feed_speed_set = fields.Char('机床设定进给速度')
|
||||||
|
machine_act_feed_speed = fields.Char('机床实际进给速度')
|
||||||
|
machine_spindle_feed = fields.Char('机床主轴倍率')
|
||||||
|
machine_feed_rate = fields.Char('机床进给倍率')
|
||||||
|
machine_rapid_feed = fields.Char('机床快速移动倍率')
|
||||||
|
machine_run_time = fields.Char('机床运行时间')
|
||||||
|
machine_cut_time = fields.Char('机床切削时间')
|
||||||
|
machine_keep_alive_time = fields.Char('机床上电时间')
|
||||||
|
machine_circle_time = fields.Char('机床循环时间')
|
||||||
|
machine_product_counts = fields.Char('机床加工件数')
|
||||||
|
machine_system_date = fields.Char('机床系统日期')
|
||||||
|
machine_system_time = fields.Char('机床系统时间')
|
||||||
|
machine_alarm_msg = fields.Char('机床系统报警')
|
||||||
|
|
||||||
|
# 机床采集项目
|
||||||
|
timestamp = fields.Datetime('时间戳', readonly=True)
|
||||||
|
signed = fields.Integer('刷新间隔', readonly=True)
|
||||||
|
status = fields.Boolean('在线状态', readonly=True)
|
||||||
|
time_on = fields.Char('总在线时长', readonly=True)
|
||||||
|
time_on_now = fields.Char('本次在线时长', readonly=True)
|
||||||
|
tool_num = fields.Integer('当前刀具', readonly=True)
|
||||||
|
program = fields.Char('当前程序', readonly=True)
|
||||||
|
run_status = fields.Selection([('0', '空闲中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')], string='运行状态',
|
||||||
|
readonly=True, default='0')
|
||||||
|
run_time = fields.Char('总运行时长', readonly=True)
|
||||||
|
cut_time = fields.Char('总切削时长', readonly=True)
|
||||||
|
cut_status = fields.Selection([('0', '未切削'), ('1', '切削中'), ('2', '切削中'), ('3', '切削中')], string='切削状态',
|
||||||
|
readonly=True, default='0')
|
||||||
|
spindle_speed = fields.Char('主轴转速', readonly=True)
|
||||||
|
|
||||||
|
tool_num_process_time1 = fields.Char('刀位1', readonly=True, default='0')
|
||||||
|
tool_num_process_time2 = fields.Char('刀位2', readonly=True, default='0')
|
||||||
|
tool_num_process_time3 = fields.Char('刀位3', readonly=True, default='0')
|
||||||
|
tool_num_process_time4 = fields.Char('刀位4', readonly=True, default='0')
|
||||||
|
tool_num_process_time5 = fields.Char('刀位5', readonly=True, default='0')
|
||||||
|
tool_num_process_time6 = fields.Char('刀位6', readonly=True, default='0')
|
||||||
|
tool_num_process_time7 = fields.Char('刀位7', readonly=True, default='0')
|
||||||
|
tool_num_process_time8 = fields.Char('刀位8', readonly=True, default='0')
|
||||||
|
tool_num_process_time9 = fields.Char('刀位9', readonly=True, default='0')
|
||||||
|
tool_num_process_time10 = fields.Char('刀位10', readonly=True, default='0')
|
||||||
|
tool_num_process_time11 = fields.Char('刀位11', readonly=True, default='0')
|
||||||
|
tool_num_process_time12 = fields.Char('刀位12', readonly=True, default='0')
|
||||||
|
tool_num_process_time13 = fields.Char('刀位13', readonly=True, default='0')
|
||||||
|
tool_num_process_time14 = fields.Char('刀位14', readonly=True, default='0')
|
||||||
|
tool_num_process_time15 = fields.Char('刀位15', readonly=True, default='0')
|
||||||
|
tool_num_process_time16 = fields.Char('刀位16', readonly=True, default='0')
|
||||||
|
tool_num_process_time17 = fields.Char('刀位17', readonly=True, default='0')
|
||||||
|
tool_num_process_time18 = fields.Char('刀位18', readonly=True, default='0')
|
||||||
|
tool_num_process_time19 = fields.Char('刀位19', readonly=True, default='0')
|
||||||
|
tool_num_process_time20 = fields.Char('刀位20', readonly=True, default='0')
|
||||||
|
tool_num_process_time21 = fields.Char('刀位21', readonly=True, default='0')
|
||||||
|
tool_num_process_time22 = fields.Char('刀位22', readonly=True, default='0')
|
||||||
|
tool_num_process_time23 = fields.Char('刀位23', readonly=True, default='0')
|
||||||
|
tool_num_process_time24 = fields.Char('刀位24', readonly=True, default='0')
|
||||||
|
|
||||||
|
|
||||||
|
class WorkCenterBarcode(models.Model):
|
||||||
|
"""
|
||||||
|
扫码托盘码可查到制造订单,由制造订单查工单
|
||||||
|
"""
|
||||||
|
_inherit = "mrp.workorder"
|
||||||
|
|
||||||
|
compensation_value_x = fields.Float(string='X轴补偿值')
|
||||||
|
compensation_value_y = fields.Float(string='Y轴补偿值')
|
||||||
|
button_compensation_state = fields.Boolean(string='是否已经补偿', readonly=True)
|
||||||
|
button_up_all_state = fields.Boolean(string='是否已经全部下发', readonly=True)
|
||||||
|
machine_tool_id = fields.Many2one('sf.machine_tool', string='机床')
|
||||||
|
machine_tool_name = fields.Char(string='机床名称', default='未知机床', compute='_run_info', readonly=True)
|
||||||
|
machine_tool_type_id = fields.Char(string='机床型号', default='未知型号', compute='_run_info', readonly=True)
|
||||||
|
machine_tool_status = fields.Boolean(string='在线状态', compute='_run_info', readonly=True)
|
||||||
|
machine_tool_run_status = fields.Selection([('0', '空闲中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')],
|
||||||
|
string='运行状态', compute='_run_info', readonly=True, default='0')
|
||||||
|
machine_tool_timestamp = fields.Datetime('时间戳', compute='_run_info', readonly=True)
|
||||||
|
machine_tool_time_on = fields.Char('总在线时长', compute='_run_info', readonly=True)
|
||||||
|
machine_tool_time_on_now = fields.Char('本次在线时长', compute='_run_info', readonly=True)
|
||||||
|
machine_tool_tool_num = fields.Integer('当前刀具', compute='_run_info', readonly=True)
|
||||||
|
machine_tool_program = fields.Char('当前程序', compute='_run_info', readonly=True)
|
||||||
|
machine_tool_machine_ip = fields.Char('机床IP', compute='_run_info', readonly=True)
|
||||||
|
machine_tool_cut_status = fields.Selection([('0', '未切削'), ('1', '切削中'), ('2', '切削中'), ('3', '切削中')],
|
||||||
|
string='切削状态', compute='_run_info', readonly=True, default='0')
|
||||||
|
machine_tool_compensation_value_x = fields.Char('x补偿值', compute='_run_info', readonly=True)
|
||||||
|
machine_tool_compensation_value_y = fields.Char('y补偿值', compute='_run_info', readonly=True)
|
||||||
|
delivery_records = fields.One2many('delivery.record', 'workorder_id', string="下发记录")
|
||||||
|
|
||||||
|
@api.depends('workcenter_id.machine_tool_id.timestamp')
|
||||||
|
def _run_info(self):
|
||||||
|
# self.machine_tool_name = '1号机床'
|
||||||
|
self.machine_tool_name = self.workcenter_id.machine_tool_id.name
|
||||||
|
self.machine_tool_type_id = self.workcenter_id.machine_tool_id.type_id.name
|
||||||
|
self.machine_tool_status = self.workcenter_id.machine_tool_id.status
|
||||||
|
self.machine_tool_run_status = self.workcenter_id.machine_tool_id.run_status
|
||||||
|
self.machine_tool_timestamp = self.workcenter_id.machine_tool_id.timestamp
|
||||||
|
self.machine_tool_time_on = self.workcenter_id.machine_tool_id.time_on
|
||||||
|
self.machine_tool_time_on_now = self.workcenter_id.machine_tool_id.time_on_now
|
||||||
|
self.machine_tool_tool_num = self.workcenter_id.machine_tool_id.tool_num
|
||||||
|
self.machine_tool_program = self.workcenter_id.machine_tool_id.program
|
||||||
|
self.machine_tool_machine_ip = self.workcenter_id.machine_tool_id.machine_ip
|
||||||
|
self.machine_tool_cut_status = self.workcenter_id.machine_tool_id.cut_status
|
||||||
|
self.machine_tool_compensation_value_x = self.compensation_value_x
|
||||||
|
self.machine_tool_compensation_value_y = self.compensation_value_y
|
||||||
|
|
||||||
|
def compensation(self):
|
||||||
|
|
||||||
|
|
||||||
|
hongbianliang550 = self.workcenter_id.machine_tool_id.x_compensation_node
|
||||||
|
hongbianliang551 = self.workcenter_id.machine_tool_id.y_compensation_node
|
||||||
|
try:
|
||||||
|
temp_dict = {}
|
||||||
|
temp_dict[hongbianliang550] = self.compensation_value_x
|
||||||
|
temp_dict[hongbianliang551] = self.compensation_value_y
|
||||||
|
|
||||||
|
print(temp_dict)
|
||||||
|
temp = py2opcua.Py2opcua()
|
||||||
|
_logger.info("====================2===============%s" % temp_dict)
|
||||||
|
temp.write(temp_dict)
|
||||||
|
_logger.info("================补偿完成==============")
|
||||||
|
except Exception as e:
|
||||||
|
_logger.info("=====================================", e)
|
||||||
|
raise UserError('补偿值获取失败,或机床未连接,请检查')
|
||||||
|
# 补偿执行完毕后,按钮标志位变为true
|
||||||
|
self.button_compensation_state = True
|
||||||
|
|
||||||
|
def up_all(self):
|
||||||
|
"""
|
||||||
|
此函数用于将NC代码全部下发到机床
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# 一键下发前删除机床上的全部程序
|
||||||
|
try:
|
||||||
|
filepath = '/nc2machine'
|
||||||
|
del_list = os.listdir(filepath)
|
||||||
|
_logger.info("=====================================%s" % del_list)
|
||||||
|
for f in del_list:
|
||||||
|
file_path = os.path.join(filepath, f)
|
||||||
|
if os.path.isfile(file_path):
|
||||||
|
os.remove(file_path)
|
||||||
|
except Exception as e:
|
||||||
|
_logger.info("=====================================", e)
|
||||||
|
raise UserError('程序删除失败,请重试')
|
||||||
|
# host="192.168.2.158", port=8080, username="MITSUBISHI", password="CNC"
|
||||||
|
try:
|
||||||
|
self.compensation()
|
||||||
|
except Exception:
|
||||||
|
raise UserError("补偿值写入执行超时,请检查机床状态或者写入状态")
|
||||||
|
sequence_collect = []
|
||||||
|
host = self.workcenter_id.machine_tool_id.ftp_host
|
||||||
|
port = self.workcenter_id.machine_tool_id.ftp_port
|
||||||
|
username = self.workcenter_id.machine_tool_id.ftp_num
|
||||||
|
pwd = self.workcenter_id.machine_tool_id.ftp_pwd
|
||||||
|
remote_path = self.workcenter_id.machine_tool_id.ftp_remote_path
|
||||||
|
_logger.info("=====================1666666661111================%s,%s,%s,%s" % (host, port, username, pwd))
|
||||||
|
ftp = ftp_operate.FtpController(host, port, username, pwd)
|
||||||
|
_logger.info("=====================1777777777111================")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
for item in self.cnc_ids:
|
||||||
|
a = item.cnc_id
|
||||||
|
sequence_collect.append(item.sequence_number)
|
||||||
|
datas = base64.standard_b64decode(a.datas)
|
||||||
|
# file_path = '{}\{}\{}'.format(a._filestore(), a.store_fname.split('/'[0]), a.display_name)
|
||||||
|
file_path_local = '{}/{}'.format('/nc2machine', a.display_name)
|
||||||
|
# 本地测试一键下发
|
||||||
|
# file_path_local = '{}/{}'.format('D:\\jikimo', a.display_name)
|
||||||
|
# file_path_remote = '{}\{}'.format('//M80(192,168,2,142)//DS', a.display_name)
|
||||||
|
file_path_remote = '{}\{}'.format(remote_path, a.display_name)
|
||||||
|
|
||||||
|
with open(file_path_local, mode='wb+') as file:
|
||||||
|
file.write(datas)
|
||||||
|
# 存在本地的文件下发到机床
|
||||||
|
ftp.upload_file(remotepath=file_path_remote, localpath=file_path_local)
|
||||||
|
_logger.info("======%s下发成功======" % a.display_name)
|
||||||
|
item.button_state = True
|
||||||
|
ftp.close_ftp()
|
||||||
|
# 增加一键下发记录
|
||||||
|
up_time = str(datetime.now() + timedelta(hours=8))
|
||||||
|
sequence_str = ','.join(sequence_collect)
|
||||||
|
self.delivery_records.create({
|
||||||
|
'delivery_type': '一键下发',
|
||||||
|
'delivery_time': up_time,
|
||||||
|
'influence_record': sequence_str,
|
||||||
|
})
|
||||||
|
_logger.info("=====================22================")
|
||||||
|
except Exception as e:
|
||||||
|
_logger.info("=====================================", e)
|
||||||
|
raise UserError('NC下发执行超时, 请检查下发状态')
|
||||||
|
|
||||||
|
def up_merge_all(self):
|
||||||
|
"""
|
||||||
|
此函数用于将NC代码一键合并下发到机床
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# 一键合并下发前删除机床上的全部程序
|
||||||
|
try:
|
||||||
|
filepath = '/nc2machine'
|
||||||
|
del_list = os.listdir(filepath)
|
||||||
|
_logger.info("=====================================%s" % del_list)
|
||||||
|
for f in del_list:
|
||||||
|
file_path = os.path.join(filepath, f)
|
||||||
|
if os.path.isfile(file_path):
|
||||||
|
os.remove(file_path)
|
||||||
|
except Exception as e:
|
||||||
|
_logger.info("=====================================", e)
|
||||||
|
raise UserError('程序删除失败,请重试')
|
||||||
|
# host="192.168.2.158", port=8080, username="MITSUBISHI", password="CNC"
|
||||||
|
try:
|
||||||
|
self.compensation()
|
||||||
|
except Exception:
|
||||||
|
raise UserError("补偿值写入执行超时,请检查机床状态或者写入状态")
|
||||||
|
sequence_collect = []
|
||||||
|
host = self.workcenter_id.machine_tool_id.ftp_host
|
||||||
|
port = self.workcenter_id.machine_tool_id.ftp_port
|
||||||
|
username = self.workcenter_id.machine_tool_id.ftp_num
|
||||||
|
pwd = self.workcenter_id.machine_tool_id.ftp_pwd
|
||||||
|
remote_path = self.workcenter_id.machine_tool_id.ftp_remote_path
|
||||||
|
_logger.info("=====================1666666661111================%s,%s,%s,%s" % (host, port, username, pwd))
|
||||||
|
ftp = ftp_operate.FtpController(host, port, username, pwd)
|
||||||
|
_logger.info("=====================1777777777111================")
|
||||||
|
file_path_local = ''
|
||||||
|
file_path_remote = ''
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 给文件名
|
||||||
|
_logger.info('启动')
|
||||||
|
begin_name1 = self.cnc_ids[0].cnc_id.display_name.split('-')
|
||||||
|
_logger.info(begin_name1)
|
||||||
|
temp_name1 = begin_name1[-1].split('.')
|
||||||
|
final_name = '%s-ALL-MERGE.%s' % (begin_name1[0], temp_name1[-1])
|
||||||
|
_logger.info(final_name)
|
||||||
|
for item in self.cnc_ids:
|
||||||
|
a = item.cnc_id
|
||||||
|
sequence_collect.append(item.sequence_number)
|
||||||
|
datas = base64.standard_b64decode(a.datas)
|
||||||
|
# file_path = '{}\{}\{}'.format(a._filestore(), a.store_fname.split('/'[0]), a.display_name)
|
||||||
|
file_path_local = '{}/{}'.format('/nc2machine', final_name)
|
||||||
|
# 本地测试一键下发
|
||||||
|
# file_path_local = '{}/{}'.format('D:\\jikimo', a.display_name)
|
||||||
|
# file_path_remote = '{}\{}'.format('//M80(192,168,2,142)//DS', a.display_name)
|
||||||
|
file_path_remote = '{}\{}'.format(remote_path, final_name)
|
||||||
|
# 合并文件
|
||||||
|
with open(file_path_local, mode='ab+') as file:
|
||||||
|
file.write(datas)
|
||||||
|
item.button_state = True
|
||||||
|
_logger.info('========一键下发前合并成功===============')
|
||||||
|
|
||||||
|
# 去除合并文件中间部分的头尾
|
||||||
|
with open(file_path_local, mode='rb+') as f:
|
||||||
|
# _logger.info(f.read())
|
||||||
|
# content = f.read()
|
||||||
|
# _logger.info(content)
|
||||||
|
new_content = f.read().replace(b'\r\nM30\r\n%\r\n%\r\n', b'\r\n')
|
||||||
|
# _logger.info(new_content)
|
||||||
|
f.seek(0)
|
||||||
|
f.truncate()
|
||||||
|
f.write(new_content)
|
||||||
|
_logger.info('========二次合并成功===============')
|
||||||
|
|
||||||
|
# 存在本地的文件下发到机床
|
||||||
|
ftp.upload_file(remotepath=file_path_remote, localpath=file_path_local)
|
||||||
|
_logger.info("======%s一键合并下发成功======" % a.display_name)
|
||||||
|
ftp.close_ftp()
|
||||||
|
# 增加一键合并下发记录
|
||||||
|
up_time = str(datetime.now() + timedelta(hours=8))
|
||||||
|
sequence_str = ','.join(sequence_collect)
|
||||||
|
self.delivery_records.create({
|
||||||
|
'delivery_type': '一键合并下发',
|
||||||
|
'delivery_time': up_time,
|
||||||
|
'influence_record': sequence_str,
|
||||||
|
})
|
||||||
|
_logger.info("=====================22================")
|
||||||
|
except Exception as e:
|
||||||
|
for item in self.cnc_ids:
|
||||||
|
item.button_state = False
|
||||||
|
_logger.info("=====================================", e)
|
||||||
|
raise UserError('NC下发执行超时, 请检查下发状态')
|
||||||
|
|
||||||
|
def get__state(self):
|
||||||
|
pay_time = str(datetime.now())
|
||||||
|
json = {
|
||||||
|
'params': {
|
||||||
|
'model_name': 'jikimo.process.order',
|
||||||
|
'field_name': 'name',
|
||||||
|
'default_code': 'PO-2022-1207-0020',
|
||||||
|
'state': '待付款',
|
||||||
|
# 'pay_time': pay_time,
|
||||||
|
# 'get_order_sf': 21,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
# res_str = json.dumps(vals)
|
||||||
|
url = 'https://bfm.cs.jikimo.com/api/get/state'
|
||||||
|
requests.post(url, json=json, data=None)
|
||||||
|
|
||||||
|
|
||||||
|
class DeliveryRecord(models.Model):
|
||||||
|
"""
|
||||||
|
扫码托盘码可查到制造订单,由制造订单查工单
|
||||||
|
"""
|
||||||
|
_name = "delivery.record"
|
||||||
|
_description = 'Delivery Record'
|
||||||
|
|
||||||
|
delivery_type = fields.Char(string='下发方式', readonly=True)
|
||||||
|
delivery_time = fields.Char(string='下发时间', readonly=True)
|
||||||
|
influence_record = fields.Char(string='影响记录', readonly=True)
|
||||||
|
workorder_id = fields.Many2one('mrp.workorder', string='工单')
|
||||||
|
sf_cnc_processing_id = fields.Many2one('sf.cnc.processing', string='NC')
|
||||||
|
|
||||||
|
# @api.model
|
||||||
|
# def create(self, values):
|
||||||
|
# try:
|
||||||
|
# values['order_code'] = self.env['ir.sequence'].next_by_code('sf_machine_connect.delivery.record')
|
||||||
|
# stock = self.env['sunrise.u.stock']
|
||||||
|
# if 'order_items' in values.keys():
|
||||||
|
# for item in values['order_items']:
|
||||||
|
# stock_commodity = stock.search([('commodity', '=', item[2]['commodity'])])
|
||||||
|
# stock_commodity.amount += item[2]['amount']
|
||||||
|
# return super().create(values)
|
||||||
|
# except:
|
||||||
|
# raise Exception('b_purchase_order.py:create()')
|
||||||
134
sf_machine_connect/models/ftp_operate.py
Normal file
134
sf_machine_connect/models/ftp_operate.py
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
from ftplib import FTP
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class FTP_P(FTP):
|
||||||
|
"""
|
||||||
|
重写FTP类,重写dirs方法
|
||||||
|
"""
|
||||||
|
|
||||||
|
def dirs(self, *args):
|
||||||
|
"""List a directory in long form.
|
||||||
|
By default list current directory to stdout.
|
||||||
|
Optional last argument is callback function; all
|
||||||
|
non-empty arguments before it are concatenated to the
|
||||||
|
LIST command. (This *should* only be used for a pathname.)"""
|
||||||
|
cmd = 'LIST'
|
||||||
|
templist = []
|
||||||
|
tempdic = {}
|
||||||
|
func = None
|
||||||
|
if args[-1:] and type(args[-1]) != type(''):
|
||||||
|
args, func = args[:-1], args[-1]
|
||||||
|
for arg in args:
|
||||||
|
if arg:
|
||||||
|
cmd = cmd + (' ' + arg)
|
||||||
|
self.retrlines(cmd, templist.append)
|
||||||
|
# 处理返回结果,只需要目录名称
|
||||||
|
r_files = [file.split(" ")[-1] for file in templist]
|
||||||
|
tempdic['name'] = [file for file in r_files if file != "." and file != ".."]
|
||||||
|
# 去除. ..
|
||||||
|
return tempdic
|
||||||
|
# return [file for file in r_files if file != "." and file != ".."]
|
||||||
|
|
||||||
|
|
||||||
|
# FTP接口类
|
||||||
|
class FtpController:
|
||||||
|
"""
|
||||||
|
这是ftp接口类,在类初始化的时候就连接了ftp服务器,能否成功连接有反馈。
|
||||||
|
类中定义了两个接口:上传接口和删除接口
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 三菱机床连接
|
||||||
|
def __init__(self, host="192.168.2.158", port=8080, username="MITSUBISHI", password="CNC"):
|
||||||
|
_logger.info("===================ftppppp==================")
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.username = username
|
||||||
|
self.password = password
|
||||||
|
# _logger.info("===================ftppppp==================%s,%s,%s,%s" % self.host, self.port, self.username, self.password)
|
||||||
|
# 测试
|
||||||
|
print("==============================================")
|
||||||
|
print(self.username, self.port, self.host, self.password)
|
||||||
|
ftp = FTP_P()
|
||||||
|
_logger.info("===================connect==================")
|
||||||
|
# self.ftp.set_debuglevel(2) #打开调试级别2,显示详细信息
|
||||||
|
ftp.set_pasv(0) # 0主动模式 1 #被动模式
|
||||||
|
try:
|
||||||
|
ftp.connect(self.host, self.port)
|
||||||
|
ftp.login(self.username, self.password)
|
||||||
|
_logger.info("=================连接成功==================")
|
||||||
|
print("连接成功")
|
||||||
|
self.ftp = ftp
|
||||||
|
except Exception as e:
|
||||||
|
print("连接失败" + str(e))
|
||||||
|
|
||||||
|
# 试验接口
|
||||||
|
def prin(self):
|
||||||
|
print("这是试验接口")
|
||||||
|
|
||||||
|
# 三菱代码下发
|
||||||
|
def upload_file(self, remotepath='/(192,168,199,2)/DS/Z4.5.NC', localpath='D:/ftp/up/Z4.5.NC'):
|
||||||
|
"""
|
||||||
|
第一个是要上传到ftp服务器路径下的文件,第二个是本地要上传的的路径文件
|
||||||
|
:param remotepath: 上传和下载都需要设置工作目录,注意只能使用文件名,不能有路径中的冒号
|
||||||
|
:param localpath:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
bufsize = 8192
|
||||||
|
# fp = open(localpath, 'rb')
|
||||||
|
# self.ftp.storbinary('STOR ' + remotepath, fp, bufsize)
|
||||||
|
# fp.close()
|
||||||
|
with open(localpath, mode='rb') as file:
|
||||||
|
self.ftp.storbinary('STOR ' + remotepath, file, bufsize)
|
||||||
|
|
||||||
|
# 关闭连接
|
||||||
|
def close_ftp(self):
|
||||||
|
"""
|
||||||
|
下发完成后关闭ftp连接,减少资源损耗
|
||||||
|
"""
|
||||||
|
self.ftp.close()
|
||||||
|
|
||||||
|
|
||||||
|
def delAllfile(self, ftppath):
|
||||||
|
"""
|
||||||
|
删除ftp服务器端全部文件
|
||||||
|
:param ftppath:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
dir_res = []
|
||||||
|
try:
|
||||||
|
print(ftppath)
|
||||||
|
try:
|
||||||
|
self.ftp.cwd(ftppath)
|
||||||
|
except Exception as e:
|
||||||
|
print("进入ftp目录失败" + str(e))
|
||||||
|
self.ftp.dir('.', dir_res.append) # 对当前目录进行dir(),将结果放入列表
|
||||||
|
print(dir_res)
|
||||||
|
# for i in dir_res:
|
||||||
|
# if i.startswith("d"):
|
||||||
|
# dirName = i.split(" ")[-1]
|
||||||
|
# print("开始删除" + dirName + "文件夹")
|
||||||
|
# delAllfile(ftp, ftp.pwd() + "/" + dirName)
|
||||||
|
# ftp.cwd('..')
|
||||||
|
# print(ftppath + "/" + dirName)
|
||||||
|
# ftp.rmd(ftppath + '/' + dirName)
|
||||||
|
# else:
|
||||||
|
# filelist = ftp.getfiles(ftppath)
|
||||||
|
# for f in filelist:
|
||||||
|
# print("删除FTP目录:" + ftppath + "下存在文件:" + f)
|
||||||
|
# ftp.delete(f)
|
||||||
|
except Exception as e:
|
||||||
|
print("删除失败" + str(e))
|
||||||
|
|
||||||
|
# 出现550 not found file是路径不对
|
||||||
|
def del_file(self, delpath='/(192,168,199,2)/DS/Z4.5.NC'):
|
||||||
|
"""
|
||||||
|
删除ftp服务器端指定文件
|
||||||
|
:param delpath:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.ftp.delete(delpath)
|
||||||
46
sf_machine_connect/models/py2opcua.py
Normal file
46
sf_machine_connect/models/py2opcua.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
from opcua import ua, Client
|
||||||
|
|
||||||
|
|
||||||
|
class Py2opcua:
|
||||||
|
"""
|
||||||
|
将三元检测补偿值写入opcua服务器
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, url='opc.tcp://192.168.2.99:4840'):
|
||||||
|
self.client = Client(url, timeout=3600000)
|
||||||
|
|
||||||
|
# 连接客户端
|
||||||
|
# def connect(self):
|
||||||
|
# try:
|
||||||
|
# self.client.connect()
|
||||||
|
# print("opcua服务器连接成功,可以写入")
|
||||||
|
# return self.client
|
||||||
|
# except Exception as e:
|
||||||
|
# print("opcua服务器连接失败,请检查" + str(e))
|
||||||
|
|
||||||
|
def write(self, temp_dict):
|
||||||
|
"""
|
||||||
|
补偿值写入方法,参数是一个字典,键是节点名,值是补偿值
|
||||||
|
:param temp_dict:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# try:
|
||||||
|
self.client.connect()
|
||||||
|
# print("opcua服务器连接成功,可以写入")
|
||||||
|
# return self.client
|
||||||
|
# except Exception as e:
|
||||||
|
# print("opcua服务器连接失败,请检查" + str(e))
|
||||||
|
# temp_dict = temp_dict
|
||||||
|
temp_list = list(temp_dict.items())
|
||||||
|
for i in range(len(temp_list)):
|
||||||
|
# 寻找节点上的变量
|
||||||
|
var = self.client.get_node(temp_list[i][0])
|
||||||
|
# var.set_value(ua.Variant(1.234, ua.VariantType.Float))
|
||||||
|
# 通过set_value写值
|
||||||
|
var.set_value(ua.Variant(temp_list[i][1], ua.VariantType.Double))
|
||||||
|
# print("%s 已写入" % var.get_value())
|
||||||
|
self.client.disconnect()
|
||||||
|
|
||||||
|
# 断开连接
|
||||||
|
# def disconnect(self):
|
||||||
|
# self.client.disconnect()
|
||||||
20
sf_machine_connect/security/iot_security.xml
Normal file
20
sf_machine_connect/security/iot_security.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
|
||||||
|
<record id="iot_box_comp_rule" model="ir.rule">
|
||||||
|
<field name="name">IoT Box multi company rule</field>
|
||||||
|
<field name="model_id" ref="model_iot_box"/>
|
||||||
|
<field eval="True" name="global"/>
|
||||||
|
<field name="domain_force">['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="iot_device_comp_rule" model="ir.rule">
|
||||||
|
<field name="name">IoT Device multi company rule</field>
|
||||||
|
<field name="model_id" ref="model_iot_device"/>
|
||||||
|
<field eval="True" name="global"/>
|
||||||
|
<field name="domain_force">['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
5
sf_machine_connect/security/ir.model.access.csv
Normal file
5
sf_machine_connect/security/ir.model.access.csv
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
|
||||||
|
access_up_select_wizard,up.select.wizard,model_up_select_wizard,base.group_user,1,1,1,1
|
||||||
|
access_delivery_record,delivery.record,model_delivery_record,base.group_user,1,1,1,1
|
||||||
|
access_mrp_workorder,mrp.workorder,model_mrp_workorder,base.group_user,1,1,1,1
|
||||||
|
BIN
sf_machine_connect/static/description/icon.png
Normal file
BIN
sf_machine_connect/static/description/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.5 KiB |
30
sf_machine_connect/static/src/css/MyWidget.css
Normal file
30
sf_machine_connect/static/src/css/MyWidget.css
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
.o_int_colorpicker {
|
||||||
|
.o_color_pill {
|
||||||
|
display: inline-block;
|
||||||
|
height: 25px;
|
||||||
|
width: 25px;
|
||||||
|
margin: 4px;
|
||||||
|
border-radius: 25px;
|
||||||
|
position: relative;
|
||||||
|
@for $size from 1 through length($o-colors) {
|
||||||
|
&.o_color_#{$size - 1} {
|
||||||
|
background-color: nth($o-colors, $size);
|
||||||
|
&:not(.readonly):hover {
|
||||||
|
transform: scale(1.2);
|
||||||
|
transition: 0.3s;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
&.active:after{
|
||||||
|
content: "\f00c";
|
||||||
|
display: inline-block;
|
||||||
|
font: normal normal normal 14px/1 FontAwesome;
|
||||||
|
font-size: inherit;
|
||||||
|
color: #fff;
|
||||||
|
position: absolute;
|
||||||
|
padding: 4px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
sf_machine_connect/static/src/css/iot.css
Normal file
5
sf_machine_connect/static/src/css/iot.css
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.o_button_iot {
|
||||||
|
min-width: 120px;
|
||||||
|
min-height: 40px;
|
||||||
|
margin-left: 50px;
|
||||||
|
}
|
||||||
13
sf_machine_connect/static/src/css/many2one_field.scss
Normal file
13
sf_machine_connect/static/src/css/many2one_field.scss
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
.o_form_view:not(.o_field_highlight) {
|
||||||
|
.o_field_many2one_selection {
|
||||||
|
.o_external_button, .o_dropdown_button {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover, &:focus-within {
|
||||||
|
.o_external_button, .o_dropdown_button {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
82
sf_machine_connect/static/src/js/test.js
Normal file
82
sf_machine_connect/static/src/js/test.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
import { browser } from "@web/core/browser/browser";
|
||||||
|
import { Dialog } from "@web/core/dialog/dialog";
|
||||||
|
import { _lt } from "@web/core/l10n/translation";
|
||||||
|
import { useChildRef, useOwnedDialogs, useService } from "@web/core/utils/hooks";
|
||||||
|
import { sprintf } from "@web/core/utils/strings";
|
||||||
|
import { isMobileOS } from "@web/core/browser/feature_detection";
|
||||||
|
import * as BarcodeScanner from "@web/webclient/barcode/barcode_scanner";
|
||||||
|
|
||||||
|
const {xml, Component} = owl;
|
||||||
|
import { standardFieldProps } from "@web/views/fields/standard_field_props";
|
||||||
|
// Import the registry
|
||||||
|
import {registry} from "@web/core/registry";
|
||||||
|
|
||||||
|
|
||||||
|
export class CodeField extends Component {
|
||||||
|
setup() {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
async onBarcodeBtnClick() {
|
||||||
|
const barcode = await BarcodeScanner.scanBarcode();
|
||||||
|
if (barcode) {
|
||||||
|
await this.onBarcodeScanned(barcode);
|
||||||
|
if ("vibrate" in browser.navigator) {
|
||||||
|
browser.navigator.vibrate(100);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.notification.add(this.env._t("Please, scan again !"), {
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async search(barcode) {
|
||||||
|
const results = await this.orm.call("sf.tray", "name_search", [code], {
|
||||||
|
name: barcode,
|
||||||
|
args: this.getDomain(),
|
||||||
|
operator: "ilike",
|
||||||
|
limit: 2, // If one result we set directly and if more than one we use normal flow so no need to search more
|
||||||
|
context: this.context,
|
||||||
|
});
|
||||||
|
return results.map((result) => {
|
||||||
|
const [id, displayName] = result;
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name: displayName,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async onBarcodeScanned(barcode) {
|
||||||
|
const results = await this.search(barcode);
|
||||||
|
const records = results.filter((r) => !!r.id);
|
||||||
|
if (records.length === 1) {
|
||||||
|
this.update([{ id: records[0].id, name: records[0].name }]);
|
||||||
|
} else {
|
||||||
|
const searchInput = this.autocompleteContainerRef.el.querySelector("input");
|
||||||
|
searchInput.value = barcode;
|
||||||
|
searchInput.dispatchEvent(new Event("input"));
|
||||||
|
if (this.env.isSmall) {
|
||||||
|
searchInput.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeField.template = xml`
|
||||||
|
<button
|
||||||
|
t-on-click="onBarcodeBtnClick"
|
||||||
|
type="button"
|
||||||
|
class="btn ms-3 o_barcode"
|
||||||
|
tabindex="-1"
|
||||||
|
draggable="false"
|
||||||
|
aria-label="Scan barcode"
|
||||||
|
title="Scan barcode"
|
||||||
|
data-tooltip="Scan barcode"
|
||||||
|
/>
|
||||||
|
`;
|
||||||
|
// CodeField.template = 'sf_machine_connect.CodeField';
|
||||||
|
CodeField.props = standardFieldProps;
|
||||||
|
|
||||||
|
// Add the field to the correct category
|
||||||
|
registry.category("fields").add("code", CodeField);
|
||||||
94
sf_machine_connect/static/src/xml/Barcode_Scan_template.xml
Normal file
94
sf_machine_connect/static/src/xml/Barcode_Scan_template.xml
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
<t t-name="sf_machine_connect.Barcode_Scan_template" t-inherit="web.Legacy.ControlPanel" t-inherit-mode="extension" owl="1">
|
||||||
|
<xpath expr="//div[hasclass('o_cp_pager')]" position="inside">
|
||||||
|
<button
|
||||||
|
style="float:left"
|
||||||
|
t-on-click="onBarcodeScanned"
|
||||||
|
type="button"
|
||||||
|
class="btn ms-3 o_barcode"
|
||||||
|
tabindex="-1"
|
||||||
|
draggable="false"
|
||||||
|
aria-label="Scan barcode"
|
||||||
|
title="Scan barcode"
|
||||||
|
data-tooltip="Scan barcode"
|
||||||
|
>扫码</button>
|
||||||
|
|
||||||
|
</xpath>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<!-- <t t-name="web.Many2OneField.CreateConfirmationDialog" owl="1">-->
|
||||||
|
<!-- <Dialog title="title" size="'md'">-->
|
||||||
|
<!-- <div>-->
|
||||||
|
<!-- Create <strong t-esc="props.value"/> as a new <t t-esc="props.name"/>?-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <t t-set-slot="footer">-->
|
||||||
|
<!-- <button class="btn btn-primary" t-on-click="onCreate">Create</button>-->
|
||||||
|
<!-- <button class="btn" t-on-click="() => props.close()">Discard</button>-->
|
||||||
|
<!-- </t>-->
|
||||||
|
<!-- </Dialog>-->
|
||||||
|
<!-- </t>-->
|
||||||
|
|
||||||
|
<!-- <t t-name="web.Many2OneField" owl="1">-->
|
||||||
|
<!-- <t t-if="props.readonly">-->
|
||||||
|
<!-- <t t-if="!props.canOpen">-->
|
||||||
|
<!-- <span>-->
|
||||||
|
<!-- <span t-esc="displayName" />-->
|
||||||
|
<!-- <t t-foreach="extraLines" t-as="extraLine" t-key="extraLine_index">-->
|
||||||
|
<!-- <br />-->
|
||||||
|
<!-- <span t-esc="extraLine" />-->
|
||||||
|
<!-- </t>-->
|
||||||
|
<!-- </span>-->
|
||||||
|
<!-- </t>-->
|
||||||
|
<!-- <t t-else="">-->
|
||||||
|
<!-- <a-->
|
||||||
|
<!-- t-if="props.value"-->
|
||||||
|
<!-- class="o_form_uri"-->
|
||||||
|
<!-- t-att-href="props.value ? `#id=${props.value[0]}&model=${relation}` : '#'"-->
|
||||||
|
<!-- t-on-click.prevent="onClick"-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- <span t-esc="displayName" />-->
|
||||||
|
<!-- <t t-foreach="extraLines" t-as="extraLine" t-key="extraLine_index">-->
|
||||||
|
<!-- <br />-->
|
||||||
|
<!-- <span t-esc="extraLine" />-->
|
||||||
|
<!-- </t>-->
|
||||||
|
<!-- </a>-->
|
||||||
|
<!-- </t>-->
|
||||||
|
<!-- </t>-->
|
||||||
|
<!-- <t t-else="">-->
|
||||||
|
<!-- <div class="o_field_many2one_selection">-->
|
||||||
|
<!-- <Many2XAutocomplete t-props="Many2XAutocompleteProps"/>-->
|
||||||
|
<!-- <t t-if="hasExternalButton">-->
|
||||||
|
<!-- <button-->
|
||||||
|
<!-- type="button"-->
|
||||||
|
<!-- class="btn btn-secondary fa o_external_button"-->
|
||||||
|
<!-- t-att-class="props.openTarget === 'current' ? 'fa-arrow-right' : 'fa-external-link'"-->
|
||||||
|
<!-- tabindex="-1"-->
|
||||||
|
<!-- draggable="false"-->
|
||||||
|
<!-- aria-label="Internal link"-->
|
||||||
|
<!-- data-tooltip="Internal link"-->
|
||||||
|
<!-- t-on-click="onExternalBtnClick"-->
|
||||||
|
<!-- />-->
|
||||||
|
<!-- </t>-->
|
||||||
|
<!-- <button-->
|
||||||
|
<!-- t-if="hasBarcodeButton"-->
|
||||||
|
<!-- t-on-click="onBarcodeBtnClick"-->
|
||||||
|
<!-- type="button"-->
|
||||||
|
<!-- class="btn ms-3 o_barcode"-->
|
||||||
|
<!-- tabindex="-1"-->
|
||||||
|
<!-- draggable="false"-->
|
||||||
|
<!-- aria-label="Scan barcode"-->
|
||||||
|
<!-- title="Scan barcode"-->
|
||||||
|
<!-- data-tooltip="Scan barcode"-->
|
||||||
|
<!-- />-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <div class="o_field_many2one_extra">-->
|
||||||
|
<!-- <t t-foreach="extraLines" t-as="extraLine" t-key="extraLine_index">-->
|
||||||
|
<!-- <br t-if="!extraLine_first" />-->
|
||||||
|
<!-- <span t-esc="extraLine" />-->
|
||||||
|
<!-- </t>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </t>-->
|
||||||
|
<!-- </t>-->
|
||||||
|
|
||||||
|
</templates>
|
||||||
10
sf_machine_connect/static/src/xml/Flush.xml
Normal file
10
sf_machine_connect/static/src/xml/Flush.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
<t t-name="owl_demo.PartnerOrderSummary2" owl="1">
|
||||||
|
<div class="row" style="padding-top: 20px;">
|
||||||
|
<p>
|
||||||
|
显示累加字符串:aaaaaabbb
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
27
sf_machine_connect/static/src/xml/MyComponent.xml
Normal file
27
sf_machine_connect/static/src/xml/MyComponent.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
<t t-name="owl_demo.MyComponent1" owl="1">
|
||||||
|
<div class="container">
|
||||||
|
<div class="jumbotron">
|
||||||
|
<h1>欢迎登陆页面!</h1>
|
||||||
|
<p>这是一个超大屏幕(Jumbotron)的实例。</p>
|
||||||
|
<p><a class="btn btn-primary btn-lg" role="button">
|
||||||
|
学习更多</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="bg-info text-center p-2">-->
|
||||||
|
<!-- <i class="fa fa-arrow-left p-1 left"-->
|
||||||
|
<!-- style="cursor: pointer;"-->
|
||||||
|
<!-- t-on-click="onPrevious"> </i>-->
|
||||||
|
<!-- <b t-esc="messageList[Math.abs(-->
|
||||||
|
<!-- state.currentIndex%4)]"/>-->
|
||||||
|
<!-- <i class="fa fa-arrow-right p-1 right"-->
|
||||||
|
<!-- style="cursor: pointer;"-->
|
||||||
|
<!-- t-on-click="onNext"> </i>-->
|
||||||
|
<!-- <i class="fa fa-close p-1 float-right"-->
|
||||||
|
<!-- style="cursor: pointer;"-->
|
||||||
|
<!-- t-on-click="onRemove"> </i>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
4
sf_machine_connect/static/src/xml/MyWidget.xml
Normal file
4
sf_machine_connect/static/src/xml/MyWidget.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<templates>
|
||||||
|
|
||||||
|
</templates>
|
||||||
32
sf_machine_connect/static/src/xml/PartnerOrderSummary2.xml
Normal file
32
sf_machine_connect/static/src/xml/PartnerOrderSummary2.xml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
<t t-name="sf_machine_connect.PartnerOrderSummary2" owl="1">
|
||||||
|
<!-- <div class="row" style="padding-top: 20px;">-->
|
||||||
|
<div style="padding-top: 20px;">
|
||||||
|
|
||||||
|
<button
|
||||||
|
t-on-click="onBarcodeBtnClick"
|
||||||
|
type="button"
|
||||||
|
class="btn ms-3 o_barcode"
|
||||||
|
tabindex="-1"
|
||||||
|
draggable="false"
|
||||||
|
aria-label="Scan barcode"
|
||||||
|
title="Scan barcode"
|
||||||
|
data-tooltip="Scan barcode"
|
||||||
|
>扫码</button>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- <div class="o_barcode_mobile_container">-->
|
||||||
|
<!-- <a role="button" class="btn btn-primary o_mobile_barcode">-->
|
||||||
|
<!-- <i class="fa fa-camera fa-2x o_barcode_mobile_camera"/>-->
|
||||||
|
<!-- Tap to scan-->
|
||||||
|
<!-- </a>-->
|
||||||
|
<!--<!– <img src="/barcodes/static/img/barcode.png" alt="Barcode"/>–>-->
|
||||||
|
<!-- <span class="o_barcode_laser"/>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
15
sf_machine_connect/static/src/xml/barcodes.xml
Normal file
15
sf_machine_connect/static/src/xml/barcodes.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
|
||||||
|
<t t-name="mobile_barcode_template">
|
||||||
|
<div class="o_barcode_mobile_container">
|
||||||
|
<a role="button" class="btn btn-primary o_mobile_barcode">
|
||||||
|
<i class="fa fa-camera fa-2x o_barcode_mobile_camera"/>
|
||||||
|
Tap to scan
|
||||||
|
</a>
|
||||||
|
<img src="/barcodes/static/img/barcode.png" alt="Barcode"/>
|
||||||
|
<span class="o_barcode_laser"/>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</templates>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<templates>
|
||||||
|
<div t-name="iot.scan_progress_template">
|
||||||
|
<h4>Range(s) to scan</h4>
|
||||||
|
<ul class="scan_ranges list-group mb-2"/>
|
||||||
|
<div class="input-group mb-4">
|
||||||
|
<input type="text" name="add_scan_range_ip" class="add_scan_range_ip form-control" placeholder="Scan another range, e.g.: 10.1.1.*"/>
|
||||||
|
<a role="button" class="add_scan_range btn btn-primary" tabindex="-1">Add</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<h4 class="scan_network"></h4>
|
||||||
|
<h4 class="iot_box_found"></h4>
|
||||||
|
<ul class="found_devices list-group"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</templates>
|
||||||
20
sf_machine_connect/static/src/xml/many2one_field.xml
Normal file
20
sf_machine_connect/static/src/xml/many2one_field.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
|
||||||
|
<t t-name="sf_machine_connect.CodeField" owl="1">
|
||||||
|
<div class="o_field_many2one_selection">
|
||||||
|
<button
|
||||||
|
t-on-click="onBarcodeBtnClick"
|
||||||
|
type="button"
|
||||||
|
class="btn ms-3 o_barcode"
|
||||||
|
tabindex="-1"
|
||||||
|
draggable="false"
|
||||||
|
aria-label="Scan barcode"
|
||||||
|
title="Scan barcode"
|
||||||
|
data-tooltip="Scan barcode"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</templates>
|
||||||
17
sf_machine_connect/static/src/xml/stack.xml
Normal file
17
sf_machine_connect/static/src/xml/stack.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates id="template" >
|
||||||
|
<t t-extend="stock_barcode_lines_template">
|
||||||
|
<t t-jquery="div[class='o_barcode_line list-group-item d-flex flex-row flex-nowrap']"
|
||||||
|
t-operation="append">
|
||||||
|
<div class="o_barcode_pic position-relative text-center mt-2 mb-1">
|
||||||
|
<i class="fa fa-5x mx-auto fa-exclamation-triangle text-white d-none"/>
|
||||||
|
<img class="o_barcode_icon" src="/stock_barcode/static/img/barcode.svg" alt="Barcode" height="40px"/>
|
||||||
|
<!-- <t t-if='widget.mobileMethods.scanBarcode'> -->
|
||||||
|
|
||||||
|
<div class="o_stock_mobile_barcode"/> <!-- Used to open the device scanner -->
|
||||||
|
<span> Tap to scan</span>
|
||||||
|
<!-- </t> -->
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
15
sf_machine_connect/views/SfWorkOrderBarcodes.xml
Normal file
15
sf_machine_connect/views/SfWorkOrderBarcodes.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="sf_install_the_tray_workorder_form_view__scan_barcode" model="ir.ui.view">
|
||||||
|
<field name="name">sf.install.the.tray.workorder.form.scan.barcode</field>
|
||||||
|
<field name="model">mrp.workorder</field>
|
||||||
|
<field name="inherit_id" ref="sf_manufacturing.view_mrp_production_workorder_tray_form_inherit_sf"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//group//field[@name='processing_panel']" position="after">
|
||||||
|
<!-- invisible="1" -->
|
||||||
|
<field name="_barcode_scanned" widget="barcode_handler" string="扫码" attrs='{"invisible": [("routing_type","!=","装夹")]}'/>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
18
sf_machine_connect/views/Stock_picking_Barcodes.xml
Normal file
18
sf_machine_connect/views/Stock_picking_Barcodes.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="stock_picking_form_vieww_scan_barcode_search" model="ir.ui.view">
|
||||||
|
<field name="name">stock.picking.search</field>
|
||||||
|
<field name="model">stock.picking</field>
|
||||||
|
<field name="inherit_id" ref="stock.view_picking_internal_search"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='name']" position="before">
|
||||||
|
<!-- <field name="tray_code" filter_domain="[('production_id.tray_ids.code','=',self)]"/>-->
|
||||||
|
<field name="origin"/>
|
||||||
|
</xpath>
|
||||||
|
<!-- <xpath expr="//search//group//filter[@name='product']" position="before">-->
|
||||||
|
<!-- <filter string="Tray code" name="traycode" domain="[]" context="{'group_by': 'tray_code'}"/>-->
|
||||||
|
<!-- </xpath>-->
|
||||||
|
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
20
sf_machine_connect/views/WorkCenterBarcodes.xml
Normal file
20
sf_machine_connect/views/WorkCenterBarcodes.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="workcenter_form_vieww_scan_barcode_search" model="ir.ui.view">
|
||||||
|
<field name="name">work.order.search</field>
|
||||||
|
<!-- <field name="model">mrp.workcenter</field>-->
|
||||||
|
<field name="model">mrp.workorder</field>
|
||||||
|
<field name="inherit_id" ref="mrp.view_mrp_production_work_order_search"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='production_id']" position="before">
|
||||||
|
<!-- <field name="name" filter_domain="['|', '|', ('明确的字段内容', 'ilike', self), ('shortdesc', 'ilike', self), ('name', 'ilike', self)]" string="Theme"/>-->
|
||||||
|
<field name="tray_code" filter_domain="[('production_id.tray_ids.code','=',self)]"/>
|
||||||
|
<!-- <field name="production_id"/>-->
|
||||||
|
</xpath>
|
||||||
|
<!-- <xpath expr="//search//group//filter[@name='product']" position="before">-->
|
||||||
|
<!-- <filter string="Tray code" name="traycode" domain="[]" context="{'group_by': 'tray_code'}"/>-->
|
||||||
|
<!-- </xpath>-->
|
||||||
|
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
42
sf_machine_connect/views/compensation.xml
Normal file
42
sf_machine_connect/views/compensation.xml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="compensation_view" model="ir.ui.view">
|
||||||
|
<field name="name">compensation</field>
|
||||||
|
<field name="model">mrp.workorder</field>
|
||||||
|
<field name="inherit_id" ref="sf_manufacturing.view_mrp_production_workorder_tray_form_inherit_sf"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//page//field[@name='cnc_ids']" position="before">
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="compensation_value_x"/>
|
||||||
|
<field name="compensation_value_y"/>
|
||||||
|
</group>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<field name="button_compensation_state" attrs='{"invisible": ["|",
|
||||||
|
("state","!=","progress"),("user_permissions","=",False)]}'/>
|
||||||
|
<button string="一键补偿" name="compensation" type="object" confirm="是否确认下发补偿"
|
||||||
|
class="btn-primary" attrs='{"invisible": ["|",
|
||||||
|
("state","!=","progress"),("user_permissions","=",False)]}'/>
|
||||||
|
<span> </span>
|
||||||
|
<!-- <field name="button_up_all_state" attrs='{"invisible": ["|",-->
|
||||||
|
<!-- ("state","!=","progress"),("user_permissions","=",False)]}'/>-->
|
||||||
|
<button string="一键下发" name="up_all" type="object" style="text-align: right;" confirm="是否确认一键下发"
|
||||||
|
class="btn-primary" context="{'default_workorder_id': id}" attrs='{"invisible": ["|",
|
||||||
|
("state","!=","progress"),("user_permissions","=",False)]}'/>
|
||||||
|
<span> </span>
|
||||||
|
<button string="合并下发" id="action_up_select" name="%(sf_machine_connect.action_up_select)d"
|
||||||
|
type="action" class="btn-primary" context="{'default_workorder_id': id}" attrs='{"invisible": ["|",
|
||||||
|
("state","!=","progress"),("user_permissions","=",False)]}'/>
|
||||||
|
<span> </span>
|
||||||
|
<button string="一键合并下发" name="up_merge_all" type="object" style="text-align: right;" confirm="是否确认一键合并下发"
|
||||||
|
class="btn-primary" context="{'default_workorder_id': id}" attrs='{"invisible": ["|",
|
||||||
|
("state","!=","progress"),("user_permissions","=",False)]}'/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</group>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
51
sf_machine_connect/views/default_delivery.xml
Normal file
51
sf_machine_connect/views/default_delivery.xml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<!-- 缺省sf.cnc.processing视图 -->
|
||||||
|
<record id="sf_cnc_processing_tree" model="ir.ui.view">
|
||||||
|
<field name="name">sf.cnc.processing.tree</field>
|
||||||
|
<field name="model">sf.cnc.processing</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree>
|
||||||
|
<field name="sequence_number"/>
|
||||||
|
<field name="program_name"/>
|
||||||
|
<field name="cnc_id" string="文件"/>
|
||||||
|
<field name="cutting_tool_name"/>
|
||||||
|
<field name="cutting_tool_no"/>
|
||||||
|
<field name="processing_type"/>
|
||||||
|
<field name="margin_x_y"/>
|
||||||
|
<field name="margin_z"/>
|
||||||
|
<field name="depth_of_processing_z"/>
|
||||||
|
<field name="cutting_tool_extension_length"/>
|
||||||
|
<field name="cutting_tool_handle_type"/>
|
||||||
|
<field name="estimated_processing_time"/>
|
||||||
|
<field name="remark"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="delivery_tree" model="ir.ui.view">
|
||||||
|
<field name="name">delivery.record.tree</field>
|
||||||
|
<field name="model">delivery.record</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree>
|
||||||
|
<field name="delivery_type"/>
|
||||||
|
<field name="delivery_time"/>
|
||||||
|
<field name="influence_record"/>
|
||||||
|
</tree>
|
||||||
|
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="delivery_form" model="ir.ui.view">
|
||||||
|
<field name="name">delivery.record.form</field>
|
||||||
|
<field name="model">delivery.record</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<field name="delivery_type"/>
|
||||||
|
<field name="delivery_time"/>
|
||||||
|
<field name="influence_record"/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
32
sf_machine_connect/views/delivery_record.xml
Normal file
32
sf_machine_connect/views/delivery_record.xml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_delivery_record_form_inherit_sf" model="ir.ui.view">
|
||||||
|
<field name="name">delivery.record.form.inherit.sf</field>
|
||||||
|
<field name="model">mrp.workorder</field>
|
||||||
|
<field name="inherit_id" ref="mrp.mrp_production_workorder_form_view_inherit"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//page[last()-3]" position="before">
|
||||||
|
<!-- <page string="下发记录" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>-->
|
||||||
|
<page string="下发记录" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
|
||||||
|
<field name="delivery_records">
|
||||||
|
<tree create="false">
|
||||||
|
<field name="delivery_type"/>
|
||||||
|
<field name="delivery_time"/>
|
||||||
|
<field name="influence_record"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<!-- <record id="seqence_b_purchase_order" model='ir.sequence'> -->
|
||||||
|
<!-- <field name='name'>Purchase Order</field> -->
|
||||||
|
<!-- <field name='code'>sf_machine_connect.delivery.record</field> -->
|
||||||
|
<!-- <field name='prefix'>%(year)s%(month)s%(day)s</field> -->
|
||||||
|
<!-- <field name='padding'>5</field> -->
|
||||||
|
<!-- </record> -->
|
||||||
|
|
||||||
|
</odoo>
|
||||||
45
sf_machine_connect/views/ftp_button.xml
Normal file
45
sf_machine_connect/views/ftp_button.xml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="sf_install_the_tray_workorder_form_view_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">sale.order.inherit</field>
|
||||||
|
<field name="model">mrp.workorder</field>
|
||||||
|
<!-- <field name="model">cnc.processing</field>-->
|
||||||
|
<field name="inherit_id" ref="sf_manufacturing.view_mrp_production_workorder_tray_form_inherit_sf"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='remark']" position="after">
|
||||||
|
<!-- <button string="测试按钮" name="pri" type="object" class="btn-primary"/>-->
|
||||||
|
<!-- <field name="workorder_id" invisible="1"/>-->
|
||||||
|
<field name="user_permissions" invisible="1"/>
|
||||||
|
<field name="state" invisible="1"/>
|
||||||
|
<field name="button_state" attrs='{"invisible": ["|",("state","!=","progress"),
|
||||||
|
("user_permissions","!=",True)]}' optional="hide"/>
|
||||||
|
<!-- <button string="下发" name="up" type="object" class="btn-primary" confirm="是否确认下发此程序" -->
|
||||||
|
<!-- attrs='{"invisible": ["|",("state","!=","progress"), -->
|
||||||
|
<!-- ("user_permissions","!=",True)]}'/> -->
|
||||||
|
<!-- <button string="下发" name="up" type="object" class="btn-primary" confirm="是否确认下发此程序" context="{'default_cnc_ids': cnc_ids}"/> -->
|
||||||
|
<button string="下发" name="up" type="object" class="btn-primary" confirm="是否确认下发此程序" context="{'default_sf_cnc_processing_id': id}" attrs='{"invisible": ["|",
|
||||||
|
("state","!=","progress"),("user_permissions","=",False)]}'/>
|
||||||
|
<!-- <button string="下发" name="up" type="object" class="btn-primary" confirm="是否确认下发此程序"-->
|
||||||
|
<!-- />-->
|
||||||
|
|
||||||
|
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<!-- <record id="app_view_quotation_tree" model="ir.ui.view">-->
|
||||||
|
<!-- <field name="name">app.sale.order.form</field>-->
|
||||||
|
<!-- <field name="model">mrp.workorder</field>-->
|
||||||
|
<!-- <field name="inherit_id" ref="sf_manufacturing.view_mrp_production_workorder_tray_form_inherit_sf"/>-->
|
||||||
|
<!-- <field name="arch" type="xml">-->
|
||||||
|
<!-- <xpath expr="//page//field[@name='cnc_ids']//tree" position="attributes">-->
|
||||||
|
<!-- <attribute name="bg_colors">-->
|
||||||
|
<!--<!– Gold:state in ('sale');LightGreen:state in ('done')–>-->
|
||||||
|
<!-- LightGreen:button_state is ('False')-->
|
||||||
|
<!-- </attribute>-->
|
||||||
|
<!-- </xpath>-->
|
||||||
|
<!-- </field>-->
|
||||||
|
<!-- </record>-->
|
||||||
|
</odoo>
|
||||||
|
|
||||||
31
sf_machine_connect/views/machine_info_present.xml
Normal file
31
sf_machine_connect/views/machine_info_present.xml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_machine_info_form_inherit_sf" model="ir.ui.view">
|
||||||
|
<field name="name">machine.info.form.inherit.sf</field>
|
||||||
|
<field name="model">mrp.workorder</field>
|
||||||
|
<field name="inherit_id" ref="mrp.mrp_production_workorder_form_view_inherit"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//page[last()-3]" position="before">
|
||||||
|
<page string="机床信息" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
|
||||||
|
<group string="机床信息">
|
||||||
|
<field name="machine_tool_name"/>
|
||||||
|
<field name="machine_tool_type_id"/>
|
||||||
|
<field name="machine_tool_machine_ip"/>
|
||||||
|
</group>
|
||||||
|
<group string="运行信息">
|
||||||
|
<field name="machine_tool_timestamp"/>
|
||||||
|
<field name="machine_tool_status"/>
|
||||||
|
<field name="machine_tool_run_status"/>
|
||||||
|
<field name="machine_tool_time_on"/>
|
||||||
|
|
||||||
|
<field name="machine_tool_tool_num"/>
|
||||||
|
<field name="machine_tool_program"/>
|
||||||
|
<field name="machine_tool_cut_status"/>
|
||||||
|
<field name="machine_tool_compensation_value_x"/>
|
||||||
|
<field name="machine_tool_compensation_value_y"/>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
201
sf_machine_connect/views/machine_monitor.xml
Normal file
201
sf_machine_connect/views/machine_monitor.xml
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_tree_mrs_machine_tool_inherited" model="ir.ui.view">
|
||||||
|
<field name="name">sf_base_extension</field>
|
||||||
|
<field name="model">sf.machine_tool</field>
|
||||||
|
<field name="inherit_id" ref="sf_base.sf_machine_tool_form"/>
|
||||||
|
<field eval="20" name="priority"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<!-- <xpath expr="//field[@name='knife_type']" position="before">-->
|
||||||
|
<xpath expr="//form//notebook//page[1]" position="inside">
|
||||||
|
<!-- <xpath expr="//page[@name='other']" position="before">-->
|
||||||
|
<notebook>
|
||||||
|
<page string="机床监控">
|
||||||
|
<!-- <group string="监控">-->
|
||||||
|
<group col='12'>
|
||||||
|
<field name="timestamp"/>
|
||||||
|
<field name="signed"/>
|
||||||
|
<field name="status"/>
|
||||||
|
<field name="time_on"/>
|
||||||
|
<field name="time_on_now"/>
|
||||||
|
<field name="run_status"/>
|
||||||
|
<field name="run_time"/>
|
||||||
|
<field name="cut_status"/>
|
||||||
|
<!-- <field name="cut_time"/>-->
|
||||||
|
<field name="program"/>
|
||||||
|
<field name="tool_num"/>
|
||||||
|
<field name="spindle_speed"/>
|
||||||
|
|
||||||
|
</group>
|
||||||
|
<group string='刀位统计' col='8'>
|
||||||
|
<field name="tool_num_process_time1"/>
|
||||||
|
<field name="tool_num_process_time2"/>
|
||||||
|
<field name="tool_num_process_time3"/>
|
||||||
|
<field name="tool_num_process_time4"/>
|
||||||
|
<field name="tool_num_process_time5"/>
|
||||||
|
<field name="tool_num_process_time6"/>
|
||||||
|
<field name="tool_num_process_time7"/>
|
||||||
|
<field name="tool_num_process_time8"/>
|
||||||
|
<field name="tool_num_process_time9"/>
|
||||||
|
<field name="tool_num_process_time10"/>
|
||||||
|
<field name="tool_num_process_time11"/>
|
||||||
|
<field name="tool_num_process_time12"/>
|
||||||
|
<field name="tool_num_process_time13"/>
|
||||||
|
<field name="tool_num_process_time14"/>
|
||||||
|
<field name="tool_num_process_time15"/>
|
||||||
|
<field name="tool_num_process_time16"/>
|
||||||
|
<field name="tool_num_process_time17"/>
|
||||||
|
<field name="tool_num_process_time18"/>
|
||||||
|
<field name="tool_num_process_time19"/>
|
||||||
|
<field name="tool_num_process_time20"/>
|
||||||
|
<field name="tool_num_process_time21"/>
|
||||||
|
<field name="tool_num_process_time22"/>
|
||||||
|
<field name="tool_num_process_time23"/>
|
||||||
|
<field name="tool_num_process_time24"/>
|
||||||
|
|
||||||
|
<!-- <div class="o_address_format"> -->
|
||||||
|
<!-- <label for="tool_num_process_time1" string="刀位1"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time1" class="o_form_label"/> -->
|
||||||
|
<!-- <span>&nbsp;</span> -->
|
||||||
|
<!-- <label for="tool_num_process_time2" string="刀位2"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time2" class="o_form_label"/> -->
|
||||||
|
<!-- <span>&nbsp;</span> -->
|
||||||
|
<!-- <label for="tool_num_process_time3" string="刀位3"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time3" class="o_form_label"/> -->
|
||||||
|
<!-- <span>&nbsp;</span> -->
|
||||||
|
<!-- <label for="tool_num_process_time4" string="刀位4"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time4" class="o_form_label"/> -->
|
||||||
|
<!-- <div></div> -->
|
||||||
|
<!-- <label for="tool_num_process_time5" string="刀位5"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time5" class="o_form_label"/> -->
|
||||||
|
<!-- <span>&nbsp;</span> -->
|
||||||
|
<!-- <label for="tool_num_process_time6" string="刀位6"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time6" class="o_form_label"/> -->
|
||||||
|
<!-- <span>&nbsp;</span> -->
|
||||||
|
<!-- <label for="tool_num_process_time7" string="刀位7"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time7" class="o_form_label"/> -->
|
||||||
|
<!-- <span>&nbsp;</span> -->
|
||||||
|
<!-- <label for="tool_num_process_time8" string="刀位8"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time8" class="o_form_label"/> -->
|
||||||
|
<!-- <div></div> -->
|
||||||
|
<!-- <label for="tool_num_process_time9" string="刀位9"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time9" class="o_form_label"/> -->
|
||||||
|
<!-- <span>&nbsp;</span> -->
|
||||||
|
<!-- <label for="tool_num_process_time10" string="刀位10"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time10" class="o_form_label"/> -->
|
||||||
|
<!-- <span>&nbsp;</span> -->
|
||||||
|
<!-- <label for="tool_num_process_time11" string="刀位11"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time11" class="o_form_label"/> -->
|
||||||
|
<!-- <span>&nbsp;</span> -->
|
||||||
|
<!-- <label for="tool_num_process_time12" string="刀位12"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time12" class="o_form_label"/> -->
|
||||||
|
<!-- <div></div> -->
|
||||||
|
<!-- <label for="tool_num_process_time13" string="刀位13"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time13" class="o_form_label"/> -->
|
||||||
|
<!-- <span>&nbsp;</span> -->
|
||||||
|
<!-- <label for="tool_num_process_time14" string="刀位14"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time14" class="o_form_label"/> -->
|
||||||
|
<!-- <span>&nbsp;</span> -->
|
||||||
|
<!-- <label for="tool_num_process_time15" string="刀位15"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time15" class="o_form_label"/> -->
|
||||||
|
<!-- <span>&nbsp;</span> -->
|
||||||
|
<!-- <label for="tool_num_process_time16" string="刀位16"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time16" class="o_form_label"/> -->
|
||||||
|
<!-- <div></div> -->
|
||||||
|
<!-- <label for="tool_num_process_time17" string="刀位17"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time17" class="o_form_label"/> -->
|
||||||
|
<!-- <span>&nbsp;</span> -->
|
||||||
|
<!-- <label for="tool_num_process_time18" string="刀位18"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time18" class="o_form_label"/> -->
|
||||||
|
<!-- <span>&nbsp;</span> -->
|
||||||
|
<!-- <label for="tool_num_process_time19" string="刀位19"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time19" class="o_form_label"/> -->
|
||||||
|
<!-- <span>&nbsp;</span> -->
|
||||||
|
<!-- <label for="tool_num_process_time20" string="刀位20"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time20" class="o_form_label"/> -->
|
||||||
|
<!-- <div></div> -->
|
||||||
|
<!-- <label for="tool_num_process_time21" string="刀位21"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time21" class="o_form_label"/> -->
|
||||||
|
<!-- <span>&nbsp;</span> -->
|
||||||
|
<!-- <label for="tool_num_process_time22" string="刀位22"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time22" class="o_form_label"/> -->
|
||||||
|
<!-- <span>&nbsp;</span> -->
|
||||||
|
<!-- <label for="tool_num_process_time23" string="刀位23"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time23" class="o_form_label"/> -->
|
||||||
|
<!-- <span>&nbsp;</span> -->
|
||||||
|
<!-- <label for="tool_num_process_time24" string="刀位24"/> -->
|
||||||
|
<!-- <field name="tool_num_process_time24" class="o_form_label"/> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
<page string="机床配置">
|
||||||
|
<!-- <group string="机床配置">-->
|
||||||
|
<group>
|
||||||
|
<group string="ftp相关">
|
||||||
|
<field name="ftp_num"/>
|
||||||
|
<field name="ftp_pwd"/>
|
||||||
|
<field name="ftp_host"/>
|
||||||
|
<field name="ftp_port"/>
|
||||||
|
<field name="ftp_remote_path"/>
|
||||||
|
</group>
|
||||||
|
<group string="补偿值相关">
|
||||||
|
<field name="x_compensation_node"/>
|
||||||
|
<field name="y_compensation_node"/>
|
||||||
|
</group>
|
||||||
|
<group string="数采相关">
|
||||||
|
<field name="machine_ip"/>
|
||||||
|
<field name="machine_signed"/>
|
||||||
|
<field name="machine_status"/>
|
||||||
|
<field name="machine_cnc_type"/>
|
||||||
|
<field name="machine_axis_count"/>
|
||||||
|
<field name="machine_run_status"/>
|
||||||
|
<field name="machine_emg_status"/>
|
||||||
|
<field name="machine_cut_status"/>
|
||||||
|
<field name="machine_mode"/>
|
||||||
|
<field name="machine_spindle_load"/>
|
||||||
|
<field name="machine_x_mach"/>
|
||||||
|
<field name="machine_x_abs_mach"/>
|
||||||
|
<field name="machine_x_rel_mach"/>
|
||||||
|
<field name="machine_x_dis_mach"/>
|
||||||
|
<field name="machine_x_axis_load"/>
|
||||||
|
<field name="machine_y_mach"/>
|
||||||
|
<field name="machine_y_abs_mach"/>
|
||||||
|
<field name="machine_y_rel_mach"/>
|
||||||
|
<field name="machine_y_dis_mach"/>
|
||||||
|
<field name="machine_y_axis_load"/>
|
||||||
|
<field name="machine_z_mach"/>
|
||||||
|
<field name="machine_z_abs_mach"/>
|
||||||
|
<field name="machine_z_rel_mach"/>
|
||||||
|
<field name="machine_z_dis_mach"/>
|
||||||
|
<field name="machine_z_axis_load"/>
|
||||||
|
<field name="machine_tool_num"/>
|
||||||
|
<field name="machine_program"/>
|
||||||
|
<field name="machine_current_prg"/>
|
||||||
|
<field name="machine_prg_seq"/>
|
||||||
|
<field name="machine_spindle_speed_set"/>
|
||||||
|
<field name="machine_act_spindle_speed"/>
|
||||||
|
<field name="machine_feed_speed_set"/>
|
||||||
|
<field name="machine_act_feed_speed"/>
|
||||||
|
<field name="machine_spindle_feed"/>
|
||||||
|
<field name="machine_feed_rate"/>
|
||||||
|
<field name="machine_rapid_feed"/>
|
||||||
|
<field name="machine_run_time"/>
|
||||||
|
<field name="machine_cut_time"/>
|
||||||
|
<field name="machine_keep_alive_time"/>
|
||||||
|
<field name="machine_circle_time"/>
|
||||||
|
<field name="machine_product_counts"/>
|
||||||
|
<field name="machine_system_date"/>
|
||||||
|
<field name="machine_system_time"/>
|
||||||
|
<field name="machine_alarm_msg"/>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
13
sf_machine_connect/views/project_sequence.xml
Normal file
13
sf_machine_connect/views/project_sequence.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<odoo noupdate="1">
|
||||||
|
|
||||||
|
<record id="seq_test" model="ir.sequence">
|
||||||
|
<field name="name">Test</field>
|
||||||
|
<field name="code">sf_machine_connect.delivery.record</field>
|
||||||
|
<field name="prefix">T%(range_year)s</field>
|
||||||
|
<field name="padding">4</field>
|
||||||
|
<field name="company_id" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
1
sf_machine_connect/wizard/__init__.py
Normal file
1
sf_machine_connect/wizard/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import action_up
|
||||||
113
sf_machine_connect/wizard/action_up.py
Normal file
113
sf_machine_connect/wizard/action_up.py
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import base64
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import hashlib
|
||||||
|
import time
|
||||||
|
import requests
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from odoo import fields, models, api, _
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
from odoo.addons.sf_machine_connect.models import py2opcua, ftp_operate
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class UpSelectWizard(models.TransientModel):
|
||||||
|
_name = 'up.select.wizard'
|
||||||
|
_description = 'Up Select Wizard'
|
||||||
|
|
||||||
|
workorder_id = fields.Many2one('mrp.workorder', string='工单', readonly=True)
|
||||||
|
workcenter_id = fields.Many2one('mrp.workcenter', string='工作中心', related='workorder_id.workcenter_id',
|
||||||
|
readonly=True)
|
||||||
|
program_ids = fields.Many2many('sf.cnc.processing', string='程序列表')
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_up(self):
|
||||||
|
# 合并下发前删除机床上的全部程序
|
||||||
|
try:
|
||||||
|
filepath = '/nc2machine'
|
||||||
|
del_list = os.listdir(filepath)
|
||||||
|
_logger.info("=====================================%s" % del_list)
|
||||||
|
for f in del_list:
|
||||||
|
file_path = os.path.join(filepath, f)
|
||||||
|
if os.path.isfile(file_path):
|
||||||
|
os.remove(file_path)
|
||||||
|
except Exception as e:
|
||||||
|
_logger.info("=====================================", e)
|
||||||
|
raise UserError('程序删除失败,请重试')
|
||||||
|
host = self.workcenter_id.machine_tool_id.ftp_host
|
||||||
|
port = self.workcenter_id.machine_tool_id.ftp_port
|
||||||
|
username = self.workcenter_id.machine_tool_id.ftp_num
|
||||||
|
pwd = self.workcenter_id.machine_tool_id.ftp_pwd
|
||||||
|
remote_path = self.workcenter_id.machine_tool_id.ftp_remote_path
|
||||||
|
print(host, port, username, pwd, remote_path)
|
||||||
|
ftp = ftp_operate.FtpController(host, port, username, pwd)
|
||||||
|
sequence_collect = []
|
||||||
|
file_path_local = ''
|
||||||
|
file_path_remote = ''
|
||||||
|
try:
|
||||||
|
# 给文件名
|
||||||
|
begin_name1 = self.program_ids[0].cnc_id.display_name.split('-')
|
||||||
|
temp_name1 = begin_name1[-1].split('.')
|
||||||
|
final_name = '%s-MERGE.%s' % (begin_name1[0], temp_name1[-1])
|
||||||
|
print(final_name)
|
||||||
|
|
||||||
|
for item in self.program_ids:
|
||||||
|
# print(item.program_name)
|
||||||
|
# print(item.cnc_id)
|
||||||
|
sequence_collect.append(item.sequence_number)
|
||||||
|
a = item.cnc_id
|
||||||
|
print(a.display_name)
|
||||||
|
_logger.info(a.public)
|
||||||
|
_logger.info(a.display_name)
|
||||||
|
datas = base64.standard_b64decode(a.datas)
|
||||||
|
file_path_local = '{}/{}'.format('/nc2machine', final_name)
|
||||||
|
|
||||||
|
# 本地测试合并下发
|
||||||
|
# file_path_local = '{}/{}'.format('D:\\jikimo', a.display_name)
|
||||||
|
# file_path_remote = '{}\{}'.format('//M80(192,168,2,142)//DS', a.display_name)
|
||||||
|
file_path_remote = '{}\{}'.format(remote_path, final_name)
|
||||||
|
# 合并文件
|
||||||
|
with open(file_path_local, mode='ab+') as file:
|
||||||
|
file.write(datas)
|
||||||
|
# 下发成功标识
|
||||||
|
item.button_state = True
|
||||||
|
_logger.info('========初次合并成功===============')
|
||||||
|
_logger.info(file_path_local)
|
||||||
|
# 去除合并文件中间部分的头尾
|
||||||
|
with open(file_path_local, mode='rb+') as f:
|
||||||
|
# _logger.info(f.read())
|
||||||
|
# content = f.read()
|
||||||
|
# _logger.info(content)
|
||||||
|
new_content = f.read().replace(b'\r\nM30\r\n%\r\n%\r\n', b'\r\n')
|
||||||
|
# _logger.info(new_content)
|
||||||
|
f.seek(0)
|
||||||
|
_logger.info(111111111111111111111111111111111111111)
|
||||||
|
f.truncate()
|
||||||
|
_logger.info(2222222222222222222222222)
|
||||||
|
f.write(new_content)
|
||||||
|
_logger.info(3333333333333333333333333)
|
||||||
|
_logger.info('========二次合并成功===============')
|
||||||
|
# 存在本地的文件下发到机床
|
||||||
|
_logger.info("==========存在服务器成功,准备下发===========")
|
||||||
|
ftp.upload_file(remotepath=file_path_remote, localpath=file_path_local)
|
||||||
|
ftp.close_ftp()
|
||||||
|
|
||||||
|
up_time = str(datetime.now())
|
||||||
|
sequence_str = ','.join(sequence_collect)
|
||||||
|
self.workorder_id.delivery_records.create({
|
||||||
|
'delivery_type': '合并下发',
|
||||||
|
'delivery_time': up_time,
|
||||||
|
'influence_record': sequence_str,
|
||||||
|
})
|
||||||
|
_logger.info("==============合并下发成功==============")
|
||||||
|
except Exception as e:
|
||||||
|
for item in self.program_ids:
|
||||||
|
item.button_state = False
|
||||||
|
_logger.info("=====================================", e)
|
||||||
|
raise UserError('NC下发执行超时, 请检查下发状态')
|
||||||
|
|
||||||
34
sf_machine_connect/wizard/up_select.xml
Normal file
34
sf_machine_connect/wizard/up_select.xml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="action_up_select_form" model="ir.ui.view">
|
||||||
|
<field name="name">CNC工单</field>
|
||||||
|
<field name="model">up.select.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<group>
|
||||||
|
<field name="workcenter_id"/>
|
||||||
|
<field name="workorder_id"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="program_ids" widget='many2many_tags' domain="[('workorder_id', '=', workorder_id)]"/>
|
||||||
|
<!-- <field name="program_ids" domain="[('workorder_id', '=', workorder_id)]"/>-->
|
||||||
|
</group>
|
||||||
|
<footer>
|
||||||
|
<button string="合并下发" name="confirm_up" type="object" class="btn-primary" confirm="是否确认合并下发"/>
|
||||||
|
<button string="取消" class="btn-secondary" special="cancel"/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="action_up_select" model="ir.actions.act_window">
|
||||||
|
<field name="name">选择要下发的NC程序</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<!-- <field name="res_model">up.select.wizard</field>-->
|
||||||
|
<field name="res_model">up.select.wizard</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="view_id" ref="action_up_select_form"/>
|
||||||
|
<field name="target">new</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
BIN
sf_manufacturing/.DS_Store
vendored
Normal file
BIN
sf_manufacturing/.DS_Store
vendored
Normal file
Binary file not shown.
1
sf_manufacturing/__init__.py
Normal file
1
sf_manufacturing/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import models
|
||||||
33
sf_manufacturing/__manifest__.py
Normal file
33
sf_manufacturing/__manifest__.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
{
|
||||||
|
'name': '机企猫智能工厂 制造管理',
|
||||||
|
'version': '1.0',
|
||||||
|
'summary': '智能工厂制造模块',
|
||||||
|
'sequence': 1,
|
||||||
|
'description': """
|
||||||
|
|
||||||
|
""",
|
||||||
|
'category': 'sf',
|
||||||
|
'website': 'https://www.sf.jikimo.com',
|
||||||
|
'depends': ['mrp', 'sf_base', 'maintenance'],
|
||||||
|
'data': [
|
||||||
|
'security/group_security.xml',
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
'report/tray_report.xml',
|
||||||
|
'views/mrp_maintenance_views.xml',
|
||||||
|
'views/mrp_routing_workcenter_view.xml',
|
||||||
|
'views/mrp_workcenter_views.xml',
|
||||||
|
'views/mrp_workorder_view.xml',
|
||||||
|
'views/tray_view.xml',
|
||||||
|
'views/model_type_view.xml',
|
||||||
|
|
||||||
|
],
|
||||||
|
'demo': [
|
||||||
|
],
|
||||||
|
'qweb': [
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'application': False,
|
||||||
|
'auto_install': False,
|
||||||
|
}
|
||||||
14
sf_manufacturing/models/__init__.py
Normal file
14
sf_manufacturing/models/__init__.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from . import tray
|
||||||
|
from . import mrp_production
|
||||||
|
from . import mrp_workcenter
|
||||||
|
from . import mrp_maintenance
|
||||||
|
from . import mrp_routing_workcenter
|
||||||
|
from . import mrp_workorder
|
||||||
|
from . import model_type
|
||||||
|
from . import stock
|
||||||
|
from . import res_user
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
59
sf_manufacturing/models/model_type.py
Normal file
59
sf_manufacturing/models/model_type.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class ModelType(models.Model):
|
||||||
|
_name = 'sf.model.type'
|
||||||
|
_description = '模型类型'
|
||||||
|
|
||||||
|
name = fields.Char('名称')
|
||||||
|
embryo_tolerance = fields.Integer('胚料容余')
|
||||||
|
product_routing_tmpl_ids = fields.One2many('sf.product.model.type.routing.sort', 'product_model_type_id',
|
||||||
|
'成品工序模板')
|
||||||
|
embryo_routing_tmpl_ids = fields.One2many('sf.embryo.model.type.routing.sort', 'embryo_model_type_id',
|
||||||
|
'胚料工序模板')
|
||||||
|
|
||||||
|
|
||||||
|
class ProductModelTypeRoutingSort(models.Model):
|
||||||
|
_name = 'sf.product.model.type.routing.sort'
|
||||||
|
_description = '成品工序排序'
|
||||||
|
|
||||||
|
sequence = fields.Integer('Sequence')
|
||||||
|
route_workcenter_id = fields.Many2one('mrp.routing.workcenter')
|
||||||
|
is_repeat = fields.Boolean('重复', related='route_workcenter_id.is_repeat')
|
||||||
|
routing_type = fields.Selection([
|
||||||
|
('获取CNC加工程序', '获取CNC加工程序'),
|
||||||
|
('装夹', '装夹'),
|
||||||
|
('前置三元定位检测', '前置三元定位检测'),
|
||||||
|
('CNC加工', 'CNC加工'),
|
||||||
|
('后置三元质量检测', '后置三元质量检测'),
|
||||||
|
('解除装夹', '解除装夹'), ('切割', '切割')
|
||||||
|
], string="工序类型", related='route_workcenter_id.routing_type')
|
||||||
|
workcenter_ids = fields.Many2many('mrp.workcenter', required=False, related='route_workcenter_id.workcenter_ids')
|
||||||
|
product_model_type_id = fields.Many2one('sf.model.type')
|
||||||
|
|
||||||
|
_sql_constraints = [
|
||||||
|
('route_model_type_uniq', 'unique (route_workcenter_id,product_model_type_id)', '成品工序不能重复!')
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class EmbryoModelTypeRoutingSort(models.Model):
|
||||||
|
_name = 'sf.embryo.model.type.routing.sort'
|
||||||
|
_description = '胚料工序排序'
|
||||||
|
|
||||||
|
sequence = fields.Integer('Sequence')
|
||||||
|
route_workcenter_id = fields.Many2one('mrp.routing.workcenter')
|
||||||
|
is_repeat = fields.Boolean('重复', related='route_workcenter_id.is_repeat')
|
||||||
|
routing_type = fields.Selection([
|
||||||
|
('获取CNC加工程序', '获取CNC加工程序'),
|
||||||
|
('装夹', '装夹'),
|
||||||
|
('前置三元定位检测', '前置三元定位检测'),
|
||||||
|
('CNC加工', 'CNC加工'),
|
||||||
|
('后置三元质量检测', '后置三元质量检测'),
|
||||||
|
('解除装夹', '解除装夹'), ('切割', '切割')
|
||||||
|
], string="工序类型", related='route_workcenter_id.routing_type')
|
||||||
|
workcenter_ids = fields.Many2many('mrp.workcenter', required=False, related='route_workcenter_id.workcenter_ids')
|
||||||
|
embryo_model_type_id = fields.Many2one('sf.model.type')
|
||||||
|
|
||||||
|
_sql_constraints = [
|
||||||
|
('route_model_type_uniq', 'unique (route_workcenter_id,embryo_model_type_id)', '胚料工序不能重复!')
|
||||||
|
]
|
||||||
66
sf_manufacturing/models/mrp_maintenance.py
Normal file
66
sf_manufacturing/models/mrp_maintenance.py
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
|
|
||||||
|
class MaintenanceEquipment(models.Model):
|
||||||
|
_inherit = "maintenance.equipment"
|
||||||
|
_check_company_auto = True
|
||||||
|
|
||||||
|
expected_mtbf = fields.Integer(string='Expected MTBF', help='Expected Mean Time Between Failure')
|
||||||
|
mtbf = fields.Integer(compute='_compute_maintenance_request', string='MTBF',
|
||||||
|
help='Mean Time Between Failure, computed based on done corrective maintenances.')
|
||||||
|
mttr = fields.Integer(compute='_compute_maintenance_request', string='MTTR', help='Mean Time To Repair')
|
||||||
|
estimated_next_failure = fields.Date(compute='_compute_maintenance_request',
|
||||||
|
string='Estimated time before next failure (in days)',
|
||||||
|
help='Computed as Latest Failure Date + MTBF')
|
||||||
|
latest_failure_date = fields.Date(compute='_compute_maintenance_request', string='Latest Failure Date')
|
||||||
|
workcenter_id = fields.Many2one(
|
||||||
|
'mrp.workcenter', string='Work Center', check_company=True)
|
||||||
|
|
||||||
|
@api.depends('effective_date', 'maintenance_ids.stage_id', 'maintenance_ids.close_date',
|
||||||
|
'maintenance_ids.request_date')
|
||||||
|
def _compute_maintenance_request(self):
|
||||||
|
for equipment in self:
|
||||||
|
maintenance_requests = equipment.maintenance_ids.filtered(
|
||||||
|
lambda x: x.maintenance_type == 'corrective' and x.stage_id.done)
|
||||||
|
mttr_days = 0
|
||||||
|
for maintenance in maintenance_requests:
|
||||||
|
if maintenance.stage_id.done and maintenance.close_date:
|
||||||
|
mttr_days += (maintenance.close_date - maintenance.request_date).days
|
||||||
|
equipment.mttr = len(maintenance_requests) and (mttr_days / len(maintenance_requests)) or 0
|
||||||
|
maintenance = maintenance_requests.sorted(lambda x: x.request_date)
|
||||||
|
if len(maintenance) >= 1:
|
||||||
|
equipment.mtbf = (maintenance[-1].request_date - equipment.effective_date).days / len(maintenance)
|
||||||
|
equipment.latest_failure_date = maintenance and maintenance[-1].request_date or False
|
||||||
|
if equipment.mtbf:
|
||||||
|
equipment.estimated_next_failure = equipment.latest_failure_date + relativedelta(days=equipment.mtbf)
|
||||||
|
else:
|
||||||
|
equipment.estimated_next_failure = False
|
||||||
|
|
||||||
|
def button_mrp_workcenter(self):
|
||||||
|
self.ensure_one()
|
||||||
|
return {
|
||||||
|
'name': _('work centers'),
|
||||||
|
'view_mode': 'form',
|
||||||
|
'res_model': 'mrp.workcenter',
|
||||||
|
'view_id': self.env.ref('mrp.mrp_workcenter_view').id,
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'res_id': self.workcenter_id.id,
|
||||||
|
'context': {
|
||||||
|
'default_company_id': self.company_id.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MaintenanceRequest(models.Model):
|
||||||
|
_inherit = "maintenance.request"
|
||||||
|
_check_company_auto = True
|
||||||
|
|
||||||
|
production_id = fields.Many2one(
|
||||||
|
'mrp.production', string='Manufacturing Order', check_company=True)
|
||||||
|
workorder_id = fields.Many2one(
|
||||||
|
'mrp.workorder', string='Work Order', check_company=True)
|
||||||
|
production_company_id = fields.Many2one(string='Production Company', related='production_id.company_id')
|
||||||
|
company_id = fields.Many2one(domain="[('id', '=?', production_company_id)]")
|
||||||
237
sf_manufacturing/models/mrp_production.py
Normal file
237
sf_manufacturing/models/mrp_production.py
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
|
||||||
|
|
||||||
|
class resProduct(models.Model):
|
||||||
|
_inherit = 'product.template'
|
||||||
|
model_file = fields.Binary('模型文件')
|
||||||
|
|
||||||
|
|
||||||
|
class MrpProduction(models.Model):
|
||||||
|
_inherit = 'mrp.production'
|
||||||
|
_description = "制造订单"
|
||||||
|
|
||||||
|
tray_ids = fields.One2many('sf.tray', 'production_id', string="托盘")
|
||||||
|
maintenance_count = fields.Integer(compute='_compute_maintenance_count', string="Number of maintenance requests")
|
||||||
|
request_ids = fields.One2many('maintenance.request', 'production_id')
|
||||||
|
model_file = fields.Binary('模型文件', related='product_id.model_file')
|
||||||
|
|
||||||
|
@api.depends('request_ids')
|
||||||
|
def _compute_maintenance_count(self):
|
||||||
|
for production in self:
|
||||||
|
production.maintenance_count = len(production.request_ids)
|
||||||
|
|
||||||
|
def button_maintenance_req(self):
|
||||||
|
self.ensure_one()
|
||||||
|
return {
|
||||||
|
'name': _('New Maintenance Request'),
|
||||||
|
'view_mode': 'form',
|
||||||
|
'res_model': 'maintenance.request',
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'context': {
|
||||||
|
'default_company_id': self.company_id.id,
|
||||||
|
'default_production_id': self.id,
|
||||||
|
},
|
||||||
|
'domain': [('production_id', '=', self.id)],
|
||||||
|
}
|
||||||
|
|
||||||
|
def open_maintenance_request_mo(self):
|
||||||
|
self.ensure_one()
|
||||||
|
action = {
|
||||||
|
'name': _('Maintenance Requests'),
|
||||||
|
'view_mode': 'kanban,tree,form,pivot,graph,calendar',
|
||||||
|
'res_model': 'maintenance.request',
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'context': {
|
||||||
|
'default_company_id': self.company_id.id,
|
||||||
|
'default_production_id': self.id,
|
||||||
|
},
|
||||||
|
'domain': [('production_id', '=', self.id)],
|
||||||
|
}
|
||||||
|
if self.maintenance_count == 1:
|
||||||
|
production = self.env['maintenance.request'].search([('production_id', '=', self.id)])
|
||||||
|
action['view_mode'] = 'form'
|
||||||
|
action['res_id'] = production.id
|
||||||
|
return action
|
||||||
|
|
||||||
|
def action_generate_serial(self):
|
||||||
|
self.ensure_one()
|
||||||
|
self.lot_producing_id = self.env['stock.lot'].create({
|
||||||
|
'product_id': self.product_id.id,
|
||||||
|
'company_id': self.company_id.id,
|
||||||
|
'name': self.env['stock.lot']._get_next_serial(self.company_id, self.product_id) or self.env[
|
||||||
|
'ir.sequence'].next_by_code('stock.lot.serial'),
|
||||||
|
})
|
||||||
|
if self.move_finished_ids.filtered(lambda m: m.product_id == self.product_id).move_line_ids:
|
||||||
|
self.move_finished_ids.filtered(
|
||||||
|
lambda m: m.product_id == self.product_id).move_line_ids.lot_id = self.lot_producing_id
|
||||||
|
if self.product_id.tracking == 'serial':
|
||||||
|
self._set_qty_producing()
|
||||||
|
|
||||||
|
# 重载根据工序生成工单的程序:如果产品BOM中没有工序时,
|
||||||
|
# 根据产品对应的模板类型中工序,去生成工单;
|
||||||
|
# CNC加工工序的选取规则:
|
||||||
|
# 如果自动报价有带过来预分配的机床,
|
||||||
|
# 则根据设备找到工作中心;否则采用前面描述的工作中心分配机制;
|
||||||
|
# 其他规则限制: 默认只分配给工作中心状态为非故障的工作中心;
|
||||||
|
|
||||||
|
def _create_workorder3(self):
|
||||||
|
for production in self:
|
||||||
|
if not production.bom_id or not production.product_id:
|
||||||
|
continue
|
||||||
|
workorders_values = []
|
||||||
|
|
||||||
|
product_qty = production.product_uom_id._compute_quantity(production.product_qty,
|
||||||
|
production.bom_id.product_uom_id)
|
||||||
|
exploded_boms, dummy = production.bom_id.explode(production.product_id,
|
||||||
|
product_qty / production.bom_id.product_qty,
|
||||||
|
picking_type=production.bom_id.picking_type_id)
|
||||||
|
|
||||||
|
for bom, bom_data in exploded_boms:
|
||||||
|
# If the operations of the parent BoM and phantom BoM are the same, don't recreate work orders.
|
||||||
|
if not (bom.operation_ids and (not bom_data['parent_line'] or bom_data[
|
||||||
|
'parent_line'].bom_id.operation_ids != bom.operation_ids)):
|
||||||
|
continue
|
||||||
|
for operation in bom.operation_ids:
|
||||||
|
if operation._skip_operation_line(bom_data['product']):
|
||||||
|
continue
|
||||||
|
workorders_values += [{
|
||||||
|
'name': operation.name,
|
||||||
|
'production_id': production.id,
|
||||||
|
'workcenter_id': operation.workcenter_id.id,
|
||||||
|
'product_uom_id': production.product_uom_id.id,
|
||||||
|
'operation_id': operation.id,
|
||||||
|
'state': 'pending',
|
||||||
|
}]
|
||||||
|
if production.product_id.categ_id.type == '成品':
|
||||||
|
# 根据加工面板的面数及对应的工序模板生成工单
|
||||||
|
i = 0
|
||||||
|
processing_panel_len = len(production.product_id.model_processing_panel.split(','))
|
||||||
|
for k in (production.product_id.model_processing_panel.split(',')):
|
||||||
|
product_routing_workcenter = self.env['sf.product.model.type.routing.sort'].search(
|
||||||
|
[('product_model_type_id', '=', production.product_id.product_model_type_id.id)],
|
||||||
|
order='sequence asc'
|
||||||
|
)
|
||||||
|
i += 1
|
||||||
|
for route in product_routing_workcenter:
|
||||||
|
if i == 1 and route.routing_type == '获取CNC加工程序':
|
||||||
|
workorders_values.append(
|
||||||
|
self.env['mrp.workorder'].json_workorder_str('', production, route))
|
||||||
|
if route.is_repeat == True:
|
||||||
|
workorders_values.append(
|
||||||
|
self.env['mrp.workorder'].json_workorder_str(k, production, route))
|
||||||
|
if i == processing_panel_len and route.routing_type == '解除装夹':
|
||||||
|
workorders_values.append(
|
||||||
|
self.env['mrp.workorder'].json_workorder_str(k, production, route))
|
||||||
|
elif production.product_id.categ_id.type == '胚料':
|
||||||
|
embryo_routing_workcenter = self.env['sf.embryo.model.type.routing.sort'].search(
|
||||||
|
[('embryo_model_type_id', '=', production.product_id.embryo_model_type_id.id)],
|
||||||
|
order='sequence asc'
|
||||||
|
)
|
||||||
|
for route in embryo_routing_workcenter:
|
||||||
|
workorders_values.append(
|
||||||
|
self.env['mrp.workorder'].json_workorder_str('', production, route))
|
||||||
|
production.workorder_ids = workorders_values
|
||||||
|
for workorder in production.workorder_ids:
|
||||||
|
workorder.duration_expected = workorder._get_duration_expected()
|
||||||
|
|
||||||
|
def create_production1_values(self, production):
|
||||||
|
production_values_str = {'origin': production.origin,
|
||||||
|
'product_id': production.product_id.id,
|
||||||
|
'product_description_variants': production.product_description_variants,
|
||||||
|
'product_qty': production.product_qty,
|
||||||
|
'product_uom_id': production.product_uom_id.id,
|
||||||
|
'location_src_id': production.location_src_id.id,
|
||||||
|
'location_dest_id': production.location_dest_id.id,
|
||||||
|
'bom_id': production.bom_id.id,
|
||||||
|
'date_deadline': production.date_deadline,
|
||||||
|
'date_planned_start': production.date_planned_start,
|
||||||
|
'date_planned_finished': production.date_planned_finished,
|
||||||
|
'procurement_group_id': False,
|
||||||
|
'propagate_cancel': production.propagate_cancel,
|
||||||
|
'orderpoint_id': production.orderpoint_id.id,
|
||||||
|
'picking_type_id': production.picking_type_id.id,
|
||||||
|
'company_id': production.company_id.id,
|
||||||
|
'move_dest_ids': production.move_dest_ids.ids,
|
||||||
|
'user_id': production.user_id.id}
|
||||||
|
return production_values_str
|
||||||
|
|
||||||
|
def _reset_work_order_sequence1(self, k):
|
||||||
|
for rec in self:
|
||||||
|
current_sequence = 1
|
||||||
|
for work in rec.workorder_ids:
|
||||||
|
work.sequence = current_sequence
|
||||||
|
current_sequence += 1
|
||||||
|
sfa = rec
|
||||||
|
for a in sfa:
|
||||||
|
print(a)
|
||||||
|
|
||||||
|
def _create_workorder1(self, k):
|
||||||
|
for production in self:
|
||||||
|
if not production.bom_id or not production.product_id:
|
||||||
|
continue
|
||||||
|
workorders_values = []
|
||||||
|
|
||||||
|
product_qty = production.product_uom_id._compute_quantity(production.product_qty,
|
||||||
|
production.bom_id.product_uom_id)
|
||||||
|
exploded_boms, dummy = production.bom_id.explode(production.product_id,
|
||||||
|
product_qty / production.bom_id.product_qty,
|
||||||
|
picking_type=production.bom_id.picking_type_id)
|
||||||
|
|
||||||
|
for bom, bom_data in exploded_boms:
|
||||||
|
# If the operations of the parent BoM and phantom BoM are the same, don't recreate work orders.
|
||||||
|
if not (bom.operation_ids and (not bom_data['parent_line'] or bom_data[
|
||||||
|
'parent_line'].bom_id.operation_ids != bom.operation_ids)):
|
||||||
|
continue
|
||||||
|
for operation in bom.operation_ids:
|
||||||
|
if operation._skip_operation_line(bom_data['product']):
|
||||||
|
continue
|
||||||
|
workorders_values += [{
|
||||||
|
'name': operation.name,
|
||||||
|
'production_id': production.id,
|
||||||
|
'workcenter_id': operation.workcenter_id.id,
|
||||||
|
'product_uom_id': production.product_uom_id.id,
|
||||||
|
'operation_id': operation.id,
|
||||||
|
'state': 'pending',
|
||||||
|
}]
|
||||||
|
# 根据加工面板的面数及对应的成品工序模板生成工单
|
||||||
|
i = 0
|
||||||
|
production.product_id.model_processing_panel = k
|
||||||
|
for k in (production.product_id.model_processing_panel.split(',')):
|
||||||
|
routingworkcenter = self.env['sf.product.model.type.routing.sort'].search(
|
||||||
|
[('product_model_type_id', '=', production.product_id.product_model_type_id.id)],
|
||||||
|
order='sequence asc'
|
||||||
|
)
|
||||||
|
i += 1
|
||||||
|
for route in routingworkcenter:
|
||||||
|
|
||||||
|
if route.routing_type == '后置三元质量检测':
|
||||||
|
workorders_values.append(
|
||||||
|
self.env['mrp.workorder'].json_workorder_str1(k, production, route)
|
||||||
|
)
|
||||||
|
if route.routing_type == 'CNC加工':
|
||||||
|
workorders_values.append(
|
||||||
|
self.env['mrp.workorder'].json_workorder_str1(k, production, route))
|
||||||
|
|
||||||
|
production.workorder_ids = workorders_values
|
||||||
|
for workorder in production.workorder_ids:
|
||||||
|
workorder.duration_expected = workorder._get_duration_expected()
|
||||||
|
|
||||||
|
def _create_workorder2(self, k):
|
||||||
|
res = self._create_workorder1(k)
|
||||||
|
self._reset_work_order_sequence1(k)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _reset_work_order_sequence(self):
|
||||||
|
for rec in self:
|
||||||
|
current_sequence = 1
|
||||||
|
for work in rec.workorder_ids:
|
||||||
|
work.sequence = current_sequence
|
||||||
|
current_sequence += 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _create_workorder(self):
|
||||||
|
res = self._create_workorder3()
|
||||||
|
self._reset_work_order_sequence()
|
||||||
|
return res
|
||||||
41
sf_manufacturing/models/mrp_routing_workcenter.py
Normal file
41
sf_manufacturing/models/mrp_routing_workcenter.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class ResMrpRoutingWorkcenter(models.Model):
|
||||||
|
_inherit = 'mrp.routing.workcenter'
|
||||||
|
|
||||||
|
routing_type = fields.Selection([
|
||||||
|
('获取CNC加工程序', '获取CNC加工程序'),
|
||||||
|
('装夹', '装夹'),
|
||||||
|
('前置三元定位检测', '前置三元定位检测'),
|
||||||
|
('CNC加工', 'CNC加工'),
|
||||||
|
('后置三元质量检测', '后置三元质量检测'),
|
||||||
|
('解除装夹', '解除装夹'),
|
||||||
|
('切割', '切割')
|
||||||
|
], string="工序类型")
|
||||||
|
is_repeat = fields.Boolean('重复', default=False)
|
||||||
|
workcenter_id = fields.Many2one('mrp.workcenter', required=False)
|
||||||
|
workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_route', required=True)
|
||||||
|
bom_id = fields.Many2one('mrp.bom', required=False)
|
||||||
|
|
||||||
|
# 获得当前登陆者公司
|
||||||
|
def get_company_id(self):
|
||||||
|
self.company_id = self.env.user.company_id.id
|
||||||
|
|
||||||
|
company_id = fields.Many2one('res.company', compute="get_company_id", related=False)
|
||||||
|
|
||||||
|
# 工单对应的工作中心,根据工序中的工作中心去匹配,
|
||||||
|
# 如果只配置了一个工作中心,则默认采用该工作中心;
|
||||||
|
# 如果有多个工作中心,
|
||||||
|
# 则根据该工作中心的工单个数进行分配(优先分配给工单个数最少的);
|
||||||
|
def get_workcenter(self, workcenter_ids):
|
||||||
|
if workcenter_ids:
|
||||||
|
if len(workcenter_ids) == 1:
|
||||||
|
return workcenter_ids[0]
|
||||||
|
elif len(workcenter_ids) >= 2:
|
||||||
|
# workcenter_ids_str = ','.join([str(s) for s in workcenter_ids])
|
||||||
|
self.env.cr.execute("""
|
||||||
|
SELECT workcenter_id FROM mrp_workorder where workcenter_id
|
||||||
|
in %s group by workcenter_id
|
||||||
|
order by count(*),workcenter_id asc limit 1 """, [tuple(workcenter_ids)])
|
||||||
|
return self.env.cr.dictfetchall()[0].get('workcenter_id')
|
||||||
81
sf_manufacturing/models/mrp_workcenter.py
Normal file
81
sf_manufacturing/models/mrp_workcenter.py
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
from odoo import api, fields, models
|
||||||
|
from datetime import datetime
|
||||||
|
from collections import defaultdict
|
||||||
|
from odoo.addons.resource.models.resource import Intervals
|
||||||
|
|
||||||
|
|
||||||
|
class ResWorkcenter(models.Model):
|
||||||
|
_inherit = "mrp.workcenter"
|
||||||
|
machine_tool_id = fields.Many2one('sf.machine_tool', '机床')
|
||||||
|
|
||||||
|
users_ids = fields.Many2many("res.users", 'users_workcenter')
|
||||||
|
|
||||||
|
equipment_ids = fields.One2many(
|
||||||
|
'maintenance.equipment', 'workcenter_id', string="Maintenance Equipment",
|
||||||
|
check_company=True)
|
||||||
|
|
||||||
|
@api.onchange('machine_tool_id')
|
||||||
|
def update_machine_tool_is_binding(self):
|
||||||
|
machine_tool = self.env["sf.machine_tool"].search([('is_binding', '=', True)])
|
||||||
|
if machine_tool:
|
||||||
|
for item in machine_tool:
|
||||||
|
workcenter_machine_tool = self.env["mrp.workcenter"].search([('machine_tool_id', '=', item.id)])
|
||||||
|
if workcenter_machine_tool:
|
||||||
|
if self.machine_tool_id.id:
|
||||||
|
if workcenter_machine_tool.id != self.machine_tool_id.id:
|
||||||
|
self.machine_tool_id.is_binding = True
|
||||||
|
else:
|
||||||
|
self.machine_tool_id.is_binding = True
|
||||||
|
else:
|
||||||
|
self.machine_tool_id.is_binding = True
|
||||||
|
item.is_binding = False
|
||||||
|
else:
|
||||||
|
self.machine_tool_id.is_binding = True
|
||||||
|
|
||||||
|
def action_work_order(self):
|
||||||
|
if not self.env.context.get('desktop_list_view', False):
|
||||||
|
action = self.env["ir.actions.actions"]._for_xml_id("sf_manufacturing.mrp_workorder_action_tablet")
|
||||||
|
return action
|
||||||
|
else:
|
||||||
|
return super(ResWorkcenter, self).action_work_order()
|
||||||
|
|
||||||
|
def _get_unavailability_intervals(self, start_datetime, end_datetime):
|
||||||
|
res = super(ResWorkcenter, self)._get_unavailability_intervals(start_datetime, end_datetime)
|
||||||
|
if not self:
|
||||||
|
return res
|
||||||
|
sql = """
|
||||||
|
SELECT workcenter_id, ARRAY_AGG((schedule_date || '|' || schedule_date + INTERVAL '1h' * duration)) as date_intervals
|
||||||
|
FROM maintenance_request
|
||||||
|
LEFT JOIN maintenance_equipment
|
||||||
|
ON maintenance_request.equipment_id = maintenance_equipment.id
|
||||||
|
WHERE
|
||||||
|
schedule_date IS NOT NULL
|
||||||
|
AND duration IS NOT NULL
|
||||||
|
AND equipment_id IS NOT NULL
|
||||||
|
AND maintenance_equipment.workcenter_id IS NOT NULL
|
||||||
|
AND maintenance_equipment.workcenter_id IN %s
|
||||||
|
AND (schedule_date, schedule_date + INT ERVAL '1h' * duration) OVERLAPS (%s, %s)
|
||||||
|
GROUP BY maintenance_equipment.workcenter_id;
|
||||||
|
"""
|
||||||
|
self.env.cr.execute(sql, [tuple(self.ids), fields.Datetime.to_string(start_datetime.astimezone()),
|
||||||
|
fields.Datetime.to_string(end_datetime.astimezone())])
|
||||||
|
res_maintenance = defaultdict(list)
|
||||||
|
for wc_row in self.env.cr.dictfetchall():
|
||||||
|
res_maintenance[wc_row.get('workcenter_id')] = [
|
||||||
|
[fields.Datetime.to_datetime(i) for i in intervals.split('|')]
|
||||||
|
for intervals in wc_row.get('date_intervals')
|
||||||
|
]
|
||||||
|
|
||||||
|
for wc_id in self.ids:
|
||||||
|
intervals_previous_list = [(s.timestamp(), e.timestamp(), self.env['maintenance.request']) for s, e in
|
||||||
|
res[wc_id]]
|
||||||
|
intervals_maintenances_list = [(m[0].timestamp(), m[1].timestamp(), self.env['maintenance.request']) for m
|
||||||
|
in res_maintenance[wc_id]]
|
||||||
|
final_intervals_wc = Intervals(intervals_previous_list + intervals_maintenances_list)
|
||||||
|
res[wc_id] = [(datetime.fromtimestamp(s), datetime.fromtimestamp(e)) for s, e, _ in final_intervals_wc]
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
class ResWorkcenterProductivity(models.Model):
|
||||||
|
_inherit = 'mrp.workcenter.productivity'
|
||||||
|
workcenter_id = fields.Many2one('mrp.workcenter', required=False)
|
||||||
541
sf_manufacturing/models/mrp_workorder.py
Normal file
541
sf_manufacturing/models/mrp_workorder.py
Normal file
@@ -0,0 +1,541 @@
|
|||||||
|
import os
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import requests
|
||||||
|
import logging
|
||||||
|
import base64
|
||||||
|
# import subprocess
|
||||||
|
from datetime import datetime
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
from odoo import api, fields, models, SUPERUSER_ID, _
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
from odoo.addons.sf_base.commons.common import Common
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
from odoo.addons.sf_mrs_connect.models.ftp_operate import FtpController
|
||||||
|
|
||||||
|
|
||||||
|
class ResMrpWorkOrder(models.Model):
|
||||||
|
_inherit = 'mrp.workorder'
|
||||||
|
_order = 'sequence'
|
||||||
|
|
||||||
|
workcenter_id = fields.Many2one('mrp.workcenter', required=False)
|
||||||
|
users_ids = fields.Many2many("res.users", 'users_workorder', related="workcenter_id.users_ids")
|
||||||
|
processing_panel = fields.Char('加工面')
|
||||||
|
sequence = fields.Integer(string='工序')
|
||||||
|
routing_type = fields.Selection([
|
||||||
|
('获取CNC加工程序', '获取CNC加工程序'),
|
||||||
|
('装夹', '装夹'),
|
||||||
|
('前置三元定位检测', '前置三元定位检测'),
|
||||||
|
('CNC加工', 'CNC加工'),
|
||||||
|
('后置三元质量检测', '后置三元质量检测'),
|
||||||
|
('解除装夹', '解除装夹'),
|
||||||
|
('切割', '切割')
|
||||||
|
], string="工序类型")
|
||||||
|
results = fields.Char('检测结果')
|
||||||
|
|
||||||
|
@api.onchange('users_ids')
|
||||||
|
def get_user_permissions(self):
|
||||||
|
uid = self.env.uid
|
||||||
|
for workorder in self:
|
||||||
|
if workorder.users_ids:
|
||||||
|
list_user_id = []
|
||||||
|
for item in workorder.users_ids:
|
||||||
|
list_user_id.append(item.id)
|
||||||
|
if uid in list_user_id:
|
||||||
|
workorder.user_permissions = True
|
||||||
|
else:
|
||||||
|
workorder.user_permissions = False
|
||||||
|
else:
|
||||||
|
workorder.user_permissions = False
|
||||||
|
|
||||||
|
user_permissions = fields.Boolean('用户权限', compute='get_user_permissions')
|
||||||
|
programming_no = fields.Char('编程单号')
|
||||||
|
work_state = fields.Char('业务状态')
|
||||||
|
programming_state = fields.Char('编程状态')
|
||||||
|
cnc_worksheet = fields.Binary(
|
||||||
|
'工作指令', readonly=True)
|
||||||
|
material_center_point = fields.Char(string='胚料中心点')
|
||||||
|
X1_axis = fields.Float(default=0)
|
||||||
|
Y1_axis = fields.Float(default=0)
|
||||||
|
Z1_axis = fields.Float(default=0)
|
||||||
|
X2_axis = fields.Float(default=0)
|
||||||
|
Y2_axis = fields.Float(default=0)
|
||||||
|
Z2_axis = fields.Float(default=0)
|
||||||
|
X3_axis = fields.Float(default=0)
|
||||||
|
Y3_axis = fields.Float(default=0)
|
||||||
|
Z3_axis = fields.Float(default=0)
|
||||||
|
X4_axis = fields.Float(default=0)
|
||||||
|
Y4_axis = fields.Float(default=0)
|
||||||
|
Z4_axis = fields.Float(default=0)
|
||||||
|
X5_axis = fields.Float(default=0)
|
||||||
|
Y5_axis = fields.Float(default=0)
|
||||||
|
Z5_axis = fields.Float(default=0)
|
||||||
|
X6_axis = fields.Float(default=0)
|
||||||
|
Y6_axis = fields.Float(default=0)
|
||||||
|
Z6_axis = fields.Float(default=0)
|
||||||
|
X7_axis = fields.Float(default=0)
|
||||||
|
Y7_axis = fields.Float(default=0)
|
||||||
|
Z7_axis = fields.Float(default=0)
|
||||||
|
X8_axis = fields.Float(default=0)
|
||||||
|
Y8_axis = fields.Float(default=0)
|
||||||
|
Z8_axis = fields.Float(default=0)
|
||||||
|
X9_axis = fields.Float(default=0)
|
||||||
|
Y9_axis = fields.Float(default=0)
|
||||||
|
Z9_axis = fields.Float(default=0)
|
||||||
|
X10_axis = fields.Float(default=0)
|
||||||
|
Y10_axis = fields.Float(default=0)
|
||||||
|
Z10_axis = fields.Float(default=0)
|
||||||
|
X_deviation_angle = fields.Integer(string="X轴偏差度", default=0)
|
||||||
|
test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")], default='合格',
|
||||||
|
string="检测结果")
|
||||||
|
cnc_ids = fields.One2many("sf.cnc.processing", 'workorder_id', string="CNC加工")
|
||||||
|
tray_code = fields.Char(string="托盘")
|
||||||
|
|
||||||
|
# 计算配料中心点和与x轴倾斜度方法
|
||||||
|
def getcenter(self):
|
||||||
|
try:
|
||||||
|
x1 = self.X1_axis
|
||||||
|
x2 = self.X2_axis
|
||||||
|
x3 = self.X3_axis
|
||||||
|
x4 = self.X4_axis
|
||||||
|
x5 = self.X5_axis
|
||||||
|
x6 = self.X6_axis
|
||||||
|
x7 = self.X7_axis
|
||||||
|
x8 = self.X8_axis
|
||||||
|
y1 = self.Y1_axis
|
||||||
|
y2 = self.Y2_axis
|
||||||
|
y3 = self.Y3_axis
|
||||||
|
y4 = self.Y4_axis
|
||||||
|
y5 = self.Y5_axis
|
||||||
|
y6 = self.Y6_axis
|
||||||
|
y7 = self.Y7_axis
|
||||||
|
y8 = self.Y8_axis
|
||||||
|
z1 = self.Z9_axis
|
||||||
|
x0 = ((x3 - x4) * (x2 * y1 - x1 * y2) - (x1 - x2) * (x4 * y3 - x3 * y4)) / (
|
||||||
|
(x3 - x4) * (y1 - y2) - (x1 - x2) * (y3 - y4))
|
||||||
|
y0 = ((y3 - y4) * (y2 * x1 - y1 * x2) - (y1 - y2) * (y4 * x3 - y3 * x4)) / (
|
||||||
|
(y3 - y4) * (x1 - x2) - (y1 - y2) * (x3 - x4))
|
||||||
|
x1 = ((x7 - x8) * (x6 * y5 - x5 * y6) - (x5 - x6) * (x8 * y7 - x7 * y8)) / (
|
||||||
|
(x7 - x8) * (y5 - y6) - (x5 - x6) * (y7 - y8));
|
||||||
|
y1 = ((y7 - y8) * (y6 * x5 - y5 * x6) - (y5 - y6) * (y8 * x7 - y7 * x8)) / (
|
||||||
|
(y7 - y8) * (x5 - x6) - (y5 - y6) * (x7 - x8))
|
||||||
|
x = (x0 + x1) / 2
|
||||||
|
y = (y0 + y1) / 2
|
||||||
|
z = z1 / 2
|
||||||
|
|
||||||
|
jd = math.atan2((x5 - x6), (y5 - y6))
|
||||||
|
jdz = jd * 180 / math.pi
|
||||||
|
print("(%.2f,%.2f)" % (x, y))
|
||||||
|
self.material_center_point = ("(%.2f,%.2f,%.2f)" % (x, y, z))
|
||||||
|
self.X_deviation_angle = jdz
|
||||||
|
# 将补偿值写入CNC加工工单
|
||||||
|
workorder = self.env['mrp.workorder'].browse(self.ids)
|
||||||
|
work = workorder.production_id.workorder_ids
|
||||||
|
work.compensation_value_x = eval(self.material_center_point)[0]
|
||||||
|
work.compensation_value_y = eval(self.material_center_point)[1]
|
||||||
|
except:
|
||||||
|
raise UserError("参数计算有误")
|
||||||
|
|
||||||
|
def json_workorder_str(self, k, production, route):
|
||||||
|
workorders_values_str = [0, '', {
|
||||||
|
'product_uom_id': production.product_uom_id.id,
|
||||||
|
'qty_producing': 0,
|
||||||
|
'operation_id': False,
|
||||||
|
'name': route.route_workcenter_id.name,
|
||||||
|
'processing_panel': k,
|
||||||
|
'routing_type': route.routing_type,
|
||||||
|
'work_state': '' if not route.routing_type == '获取CNC加工程序' else '待发起',
|
||||||
|
'workcenter_id': self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids),
|
||||||
|
'date_planned_start': False,
|
||||||
|
'date_planned_finished': False,
|
||||||
|
'duration_expected': 60,
|
||||||
|
'duration': 0
|
||||||
|
}]
|
||||||
|
return workorders_values_str
|
||||||
|
|
||||||
|
# 工作中心看板按钮
|
||||||
|
def button_maintenance_req(self):
|
||||||
|
self.ensure_one()
|
||||||
|
return {
|
||||||
|
'name': _('New Maintenance Request'),
|
||||||
|
'view_mode': 'form',
|
||||||
|
'views': [(self.env.ref('mrp_maintenance.maintenance_request_view_form_inherit_mrp').id, 'form')],
|
||||||
|
'res_model': 'maintenance.request',
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'context': {
|
||||||
|
'default_company_id': self.company_id.id,
|
||||||
|
'default_workorder_id': self.id,
|
||||||
|
'default_production_id': self.production_id.id,
|
||||||
|
'discard_on_footer_button': True,
|
||||||
|
},
|
||||||
|
'target': 'new',
|
||||||
|
'domain': [('workorder_id', '=', self.id)]
|
||||||
|
}
|
||||||
|
|
||||||
|
tray_id = fields.Many2one('sf.tray', string="托盘信息", tracking=True)
|
||||||
|
|
||||||
|
# 扫码绑定托盘方法
|
||||||
|
|
||||||
|
def gettray(self):
|
||||||
|
if self.tray_code != False:
|
||||||
|
values = self.env['sf.tray'].search([("code", "=", self.tray_code)])
|
||||||
|
if values:
|
||||||
|
if values.state == "占用":
|
||||||
|
raise UserError('该托盘已占用')
|
||||||
|
if values.state == "报损":
|
||||||
|
raise UserError('该托盘已损坏')
|
||||||
|
else:
|
||||||
|
values.update({
|
||||||
|
'workorder_id': self,
|
||||||
|
'production_id': self.production_id,
|
||||||
|
'state': '占用',
|
||||||
|
})
|
||||||
|
self.work_state = "已绑定"
|
||||||
|
orders = self.env['mrp.workorder'].search([('production_id', '=', self.production_id.id)])
|
||||||
|
for a in orders:
|
||||||
|
a.tray_id = values
|
||||||
|
else:
|
||||||
|
raise UserError('该托盘编码已失效')
|
||||||
|
else:
|
||||||
|
raise UserError('托盘码不能为空')
|
||||||
|
|
||||||
|
def gettray_auto(self, barcode):
|
||||||
|
if barcode != False:
|
||||||
|
values = self.env['sf.tray'].search([("code", "=", barcode)])
|
||||||
|
if values:
|
||||||
|
if values.state == "占用":
|
||||||
|
raise UserError('该托盘已占用')
|
||||||
|
if values.state == "报损":
|
||||||
|
raise UserError('该托盘已损坏')
|
||||||
|
else:
|
||||||
|
values.update({
|
||||||
|
'workorder_id': self,
|
||||||
|
'production_id': self.production_id,
|
||||||
|
'state': '占用',
|
||||||
|
})
|
||||||
|
self.work_state = "已绑定"
|
||||||
|
orders = self.env['mrp.workorder'].search([('production_id', '=', self.production_id.id)])
|
||||||
|
for a in orders:
|
||||||
|
a.tray_id = values
|
||||||
|
return values
|
||||||
|
else:
|
||||||
|
raise UserError('该托盘编码已失效')
|
||||||
|
else:
|
||||||
|
raise UserError('托盘码不能为空')
|
||||||
|
|
||||||
|
# 解除托盘绑定
|
||||||
|
def unbindtray(self):
|
||||||
|
tray = self.env['sf.tray'].search([("production_id", "=", self.production_id.id)])
|
||||||
|
if tray:
|
||||||
|
tray.unclamp()
|
||||||
|
self.tray_id = False
|
||||||
|
|
||||||
|
# return {
|
||||||
|
# 'name': _('New Maintenance Request'),
|
||||||
|
# 'view_mode': 'form',
|
||||||
|
# 'res_model': 'maintenance.request',
|
||||||
|
# 'res_id':self.id,
|
||||||
|
# 'type': 'ir.actions.act_window',
|
||||||
|
# 'context': {
|
||||||
|
# 'default_company_id': self.company_id.id,
|
||||||
|
# 'default_production_id': self.id,
|
||||||
|
# },
|
||||||
|
# 'domain': [('production_id', '=', self.id)],
|
||||||
|
# 'target':'new'
|
||||||
|
# }
|
||||||
|
|
||||||
|
def recreateManufacturingOrWorkerOrder(self):
|
||||||
|
"""
|
||||||
|
重新生成制造订单或者重新生成工单
|
||||||
|
"""
|
||||||
|
if self.test_results == '报废':
|
||||||
|
values = self.env['mrp.production'].create_production1_values(self.production_id)
|
||||||
|
productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(
|
||||||
|
self.production_id.company_id).create(
|
||||||
|
values)
|
||||||
|
self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
|
||||||
|
self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
|
||||||
|
productions._create_workorder()
|
||||||
|
productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \
|
||||||
|
(
|
||||||
|
p.move_dest_ids.procure_method != 'make_to_order' and not p.move_raw_ids and not p.workorder_ids)).action_confirm()
|
||||||
|
|
||||||
|
for production in productions:
|
||||||
|
origin_production = production.move_dest_ids and production.move_dest_ids[
|
||||||
|
0].raw_material_production_id or False
|
||||||
|
orderpoint = production.orderpoint_id
|
||||||
|
if orderpoint and orderpoint.create_uid.id == SUPERUSER_ID and orderpoint.trigger == 'manual':
|
||||||
|
production.message_post(
|
||||||
|
body=_('This production order has been created from Replenishment Report.'),
|
||||||
|
message_type='comment',
|
||||||
|
subtype_xmlid='mail.mt_note')
|
||||||
|
elif orderpoint:
|
||||||
|
production.message_post_with_view(
|
||||||
|
'mail.message_origin_link',
|
||||||
|
values={'self': production, 'origin': orderpoint},
|
||||||
|
subtype_id=self.env.ref('mail.mt_note').id)
|
||||||
|
elif origin_production:
|
||||||
|
production.message_post_with_view(
|
||||||
|
'mail.message_origin_link',
|
||||||
|
values={'self': production, 'origin': origin_production},
|
||||||
|
subtype_id=self.env.ref('mail.mt_note').id)
|
||||||
|
if self.test_results == '返工':
|
||||||
|
productions = self.production_id
|
||||||
|
self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
|
||||||
|
self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
|
||||||
|
productions._create_workorder2(self.processing_panel)
|
||||||
|
else:
|
||||||
|
self.results = '合格'
|
||||||
|
|
||||||
|
# cnc程序获取
|
||||||
|
def fetchCNC(self):
|
||||||
|
try:
|
||||||
|
cnc = self.env['mrp.workorder'].search(
|
||||||
|
[('routing_type', '=', 'CNC加工'), ('production_id', '=', self.production_id.id)], limit=1)
|
||||||
|
logging.info('fetchCNC-cnc:%s' % cnc)
|
||||||
|
# if cnc.product_id.upload_model_file:
|
||||||
|
# logging.info('fetchCNC-upload_model_file:%s' % cnc.product_id.upload_model_file)
|
||||||
|
# attachments = cnc.product_id.upload_model_file[0]
|
||||||
|
# logging.info('fetchCNC-attachment1:%s' % attachments)
|
||||||
|
# logging.info('fetchCNC-attachment1:%s' % cnc.product_id.upload_model_file[0])
|
||||||
|
# logging.info('fetchCNC-attachment2:%s' % cnc.product_id.upload_model_file[0].datas)
|
||||||
|
# logging.info('fetchCNC-attachment:%s' % attachments.datas)
|
||||||
|
# base64_data = base64.b64encode(attachments.datas)
|
||||||
|
# logging.info('fetchCNC-attachment1:%s' % attachments)
|
||||||
|
# base64_datas = base64_data.decode('utf-8')
|
||||||
|
# model_code = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest()
|
||||||
|
# logging.info('fetchCNC-model_code:%s' % model_code)
|
||||||
|
logging.info('fetchCNC-model_code1:%s' % cnc.product_id.model_code)
|
||||||
|
res = {'model_code': '' if not cnc.product_id.model_code else cnc.product_id.model_code,
|
||||||
|
'production_no': self.production_id.name,
|
||||||
|
'machine_tool_code': cnc.workcenter_id.machine_tool_id.code,
|
||||||
|
'material_code': cnc.env['sf.production.materials'].search(
|
||||||
|
[('id', '=', cnc.product_id.materials_id.id)]).materials_no,
|
||||||
|
'material_type_code': cnc.env['sf.materials.model'].search(
|
||||||
|
[('id', '=', cnc.product_id.materials_type_id.id)]).materials_no,
|
||||||
|
'machining_processing_panel': cnc.product_id.model_processing_panel,
|
||||||
|
'machining_precision': cnc.product_id.model_machining_precision,
|
||||||
|
'embryo_long': cnc.product_id.bom_ids.bom_line_ids.product_id.length,
|
||||||
|
'embryo_height': cnc.product_id.bom_ids.bom_line_ids.product_id.height,
|
||||||
|
'embryo_width': cnc.product_id.bom_ids.bom_line_ids.product_id.width,
|
||||||
|
'order_no': cnc.production_id.origin,
|
||||||
|
'user': self.env.user.name,
|
||||||
|
'model_file': '' if not cnc.product_id.model_file else base64.b64encode(
|
||||||
|
cnc.product_id.model_file).decode('utf-8')
|
||||||
|
}
|
||||||
|
logging.info('res:%s' % res)
|
||||||
|
configsettings = self.env['res.config.settings'].get_values()
|
||||||
|
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
|
||||||
|
url = '/api/intelligent_programming/create'
|
||||||
|
config_url = configsettings['sf_url'] + url
|
||||||
|
# res_str = json.dumps(res)
|
||||||
|
ret = requests.post(config_url, json={}, data=res, headers=config_header)
|
||||||
|
ret = ret.json()
|
||||||
|
logging.info('fetchCNC-ret:%s' % ret)
|
||||||
|
if ret['status'] == 1:
|
||||||
|
self.write(
|
||||||
|
{'programming_no': ret['programming_no'], 'programming_state': '编程中', 'work_state': '编程中'})
|
||||||
|
else:
|
||||||
|
raise UserError(ret['message'])
|
||||||
|
except Exception as e:
|
||||||
|
logging.info('fetchCNC error:%s' % e)
|
||||||
|
raise UserError("cnc程序获取编程单失败,请联系管理员")
|
||||||
|
|
||||||
|
def json_workorder_str1(self, k, production, route):
|
||||||
|
workorders_values_str = [0, '', {
|
||||||
|
'product_uom_id': production.product_uom_id.id,
|
||||||
|
'qty_producing': 0,
|
||||||
|
'operation_id': False,
|
||||||
|
'name': route.route_workcenter_id.name,
|
||||||
|
'processing_panel': k,
|
||||||
|
'routing_type': route.routing_type,
|
||||||
|
'work_state': '' if not route.routing_type == '获取CNC加工程序' else '待发起',
|
||||||
|
'workcenter_id': self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids),
|
||||||
|
'date_planned_start': False,
|
||||||
|
'date_planned_finished': False,
|
||||||
|
'duration_expected': 60,
|
||||||
|
'duration': 0
|
||||||
|
}]
|
||||||
|
return workorders_values_str
|
||||||
|
|
||||||
|
# 重写工单开始按钮方法
|
||||||
|
def button_start(self):
|
||||||
|
if self.state == 'waiting' or self.state == 'ready' or self.state == 'progress':
|
||||||
|
self.ensure_one()
|
||||||
|
if any(not time.date_end for time in self.time_ids.filtered(lambda t: t.user_id.id == self.env.user.id)):
|
||||||
|
return True
|
||||||
|
# As button_start is automatically called in the new view
|
||||||
|
if self.state in ('done', 'cancel'):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if self.product_tracking == 'serial':
|
||||||
|
self.qty_producing = 1.0
|
||||||
|
else:
|
||||||
|
self.qty_producing = self.qty_remaining
|
||||||
|
|
||||||
|
self.env['mrp.workcenter.productivity'].create(
|
||||||
|
self._prepare_timeline_vals(self.duration, datetime.now())
|
||||||
|
)
|
||||||
|
if self.production_id.state != 'progress':
|
||||||
|
self.production_id.write({
|
||||||
|
'date_start': datetime.now(),
|
||||||
|
})
|
||||||
|
if self.state == 'progress':
|
||||||
|
return True
|
||||||
|
start_date = datetime.now()
|
||||||
|
vals = {
|
||||||
|
'state': 'progress',
|
||||||
|
'date_start': start_date,
|
||||||
|
}
|
||||||
|
if not self.leave_id:
|
||||||
|
leave = self.env['resource.calendar.leaves'].create({
|
||||||
|
'name': self.display_name,
|
||||||
|
'calendar_id': self.workcenter_id.resource_calendar_id.id,
|
||||||
|
'date_from': start_date,
|
||||||
|
'date_to': start_date + relativedelta(minutes=self.duration_expected),
|
||||||
|
'resource_id': self.workcenter_id.resource_id.id,
|
||||||
|
'time_type': 'other'
|
||||||
|
})
|
||||||
|
vals['leave_id'] = leave.id
|
||||||
|
return self.write(vals)
|
||||||
|
else:
|
||||||
|
if self.date_planned_start > start_date:
|
||||||
|
vals['date_planned_start'] = start_date
|
||||||
|
if self.date_planned_finished and self.date_planned_finished < start_date:
|
||||||
|
vals['date_planned_finished'] = start_date
|
||||||
|
return self.write(vals)
|
||||||
|
else:
|
||||||
|
raise UserError(_('请先完成上一步工单'))
|
||||||
|
|
||||||
|
|
||||||
|
class CNCprocessing(models.Model):
|
||||||
|
_name = 'sf.cnc.processing'
|
||||||
|
_description = "CNC加工"
|
||||||
|
_rec_name = 'program_name'
|
||||||
|
|
||||||
|
cnc_id = fields.Many2one('ir.attachment')
|
||||||
|
sequence_number = fields.Char('序号')
|
||||||
|
program_name = fields.Char('程序名')
|
||||||
|
cutting_tool_name = fields.Char('刀具名称')
|
||||||
|
cutting_tool_no = fields.Char('刀号')
|
||||||
|
processing_type = fields.Char('加工类型')
|
||||||
|
margin_x_y = fields.Char('余量_X/Y')
|
||||||
|
margin_z = fields.Char('余量_Z')
|
||||||
|
depth_of_processing_z = fields.Char('加工深度(Z)')
|
||||||
|
cutting_tool_extension_length = fields.Char('刀具伸出长度')
|
||||||
|
cutting_tool_handle_type = fields.Char('刀柄型号')
|
||||||
|
estimated_processing_time = fields.Char('预计加工时间')
|
||||||
|
remark = fields.Text('备注')
|
||||||
|
workorder_id = fields.Many2one('mrp.workorder', string="工单")
|
||||||
|
button_state = fields.Boolean(string='是否已经下发')
|
||||||
|
# mrs下发编程单创建CNC加工
|
||||||
|
def cnc_processing_create(self, cnc_workorder, ret):
|
||||||
|
logging.info('ret:%s' % ret)
|
||||||
|
for obj in ret['programming_list']:
|
||||||
|
workorder = self.env['mrp.workorder'].search([('production_id.name', '=', ret['production_order_no']),
|
||||||
|
('processing_panel', '=', obj['processing_panel']),
|
||||||
|
('routing_type', '=', 'CNC加工')])
|
||||||
|
cnc_processing = self.env['sf.cnc.processing'].create({
|
||||||
|
'workorder_id': workorder.id,
|
||||||
|
'sequence_number': obj['sequence_number'],
|
||||||
|
'program_name': obj['program_name'],
|
||||||
|
'cutting_tool_name': obj['cutting_tool_name'],
|
||||||
|
'cutting_tool_no': obj['cutting_tool_no'],
|
||||||
|
'processing_type': obj['processing_type'],
|
||||||
|
'margin_x_y': obj['margin_x_y'],
|
||||||
|
'margin_z': obj['margin_z'],
|
||||||
|
'depth_of_processing_z': obj['depth_of_processing_z'],
|
||||||
|
'cutting_tool_extension_length': obj['cutting_tool_extension_length'],
|
||||||
|
'cutting_tool_handle_type': obj['cutting_tool_handle_type'],
|
||||||
|
'estimated_processing_time': obj['estimated_processing_time'],
|
||||||
|
'remark': obj['remark']
|
||||||
|
})
|
||||||
|
self.get_cnc_processing_file(ret['folder_name'], cnc_processing, workorder.processing_panel)
|
||||||
|
cnc_workorder.state = 'done'
|
||||||
|
cnc_workorder.work_state = '已编程'
|
||||||
|
cnc_workorder.programming_state = '已编程'
|
||||||
|
cnc_workorder.time_ids.date_end = datetime.now()
|
||||||
|
cnc_workorder.button_finish()
|
||||||
|
|
||||||
|
def get_cnc_processing_file(self, folder_name, cnc_processing, processing_panel):
|
||||||
|
logging.info('folder_name:%s' % folder_name)
|
||||||
|
serverdir = os.path.join('/tmp', folder_name, 'return', processing_panel)
|
||||||
|
logging.info('serverdir:%s' % serverdir)
|
||||||
|
for root, dirs, files in os.walk(serverdir):
|
||||||
|
for f in files:
|
||||||
|
logging.info('f:%s' % f)
|
||||||
|
if os.path.splitext(f)[1] == ".pdf":
|
||||||
|
full_path = os.path.join(serverdir, root, f)
|
||||||
|
logging.info('pdf:%s' % full_path)
|
||||||
|
if full_path != False:
|
||||||
|
if not cnc_processing.workorder_id.cnc_worksheet:
|
||||||
|
cnc_processing.workorder_id.cnc_worksheet = base64.b64encode(
|
||||||
|
open(full_path, 'rb').read())
|
||||||
|
else:
|
||||||
|
if cnc_processing.program_name == f.split('.')[0]:
|
||||||
|
cnc_file_path = os.path.join(serverdir, root, f)
|
||||||
|
logging.info('cnc_file_path:%s' % cnc_file_path)
|
||||||
|
self.write_file(cnc_file_path, cnc_processing)
|
||||||
|
|
||||||
|
# 创建附件(nc文件)
|
||||||
|
def attachment_create(self, name, data):
|
||||||
|
attachment = self.env['ir.attachment'].create({
|
||||||
|
'datas': base64.b64encode(data),
|
||||||
|
'type': 'binary',
|
||||||
|
'public': True,
|
||||||
|
'description': '程序文件',
|
||||||
|
'name': name
|
||||||
|
})
|
||||||
|
return attachment
|
||||||
|
|
||||||
|
# 将FTP的nc文件下载到临时目录
|
||||||
|
def download_file_tmp(self, production_no, processing_panel):
|
||||||
|
remotepath = os.path.join('/', production_no, 'return', processing_panel)
|
||||||
|
serverdir = os.path.join('/tmp', production_no, 'return', processing_panel)
|
||||||
|
ftp_resconfig = self.env['res.config.settings'].get_values()
|
||||||
|
ftp = FtpController(str(ftp_resconfig['ftp_host']), int(ftp_resconfig['ftp_port']), ftp_resconfig['ftp_user'],
|
||||||
|
ftp_resconfig['ftp_password'])
|
||||||
|
download_state = ftp.download_file_tree(remotepath, serverdir)
|
||||||
|
return download_state
|
||||||
|
|
||||||
|
# 将nc文件存到attach的datas里
|
||||||
|
def write_file(self, nc_file_path, cnc):
|
||||||
|
if os.path.exists(nc_file_path):
|
||||||
|
with open(nc_file_path, 'rb') as file:
|
||||||
|
data_bytes = file.read()
|
||||||
|
attachment = self.attachment_create(cnc.program_name + '.NC', data_bytes)
|
||||||
|
cnc.write({'cnc_id': attachment.id})
|
||||||
|
file.close()
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class SfWorkOrderBarcodes(models.Model):
|
||||||
|
"""
|
||||||
|
智能工厂工单处扫码绑定托盘
|
||||||
|
"""
|
||||||
|
_name = "mrp.workorder"
|
||||||
|
_inherit = ["mrp.workorder", "barcodes.barcode_events_mixin"]
|
||||||
|
|
||||||
|
def on_barcode_scanned(self, barcode):
|
||||||
|
workorder = self.env['mrp.workorder'].browse(self.ids)
|
||||||
|
logging.info(111111111111111111111111111111111)
|
||||||
|
if "*" not in barcode:
|
||||||
|
logging.info(222222222222222222222222222222222222222)
|
||||||
|
if self.routing_type == '装夹':
|
||||||
|
tray_code = self.env['sf.tray'].search([('code', '=', barcode)])
|
||||||
|
self.tray_code = tray_code.code
|
||||||
|
self.tray_id = workorder.gettray_auto(barcode)
|
||||||
|
elif self.routing_type == '前置三元定位检测':
|
||||||
|
print('我是前置三元检测')
|
||||||
|
logging.info('我是前置三元检测')
|
||||||
|
elif self.routing_type == 'CNC加工':
|
||||||
|
if barcode == 'UP-ALL':
|
||||||
|
print("我是一键合并下发")
|
||||||
|
logging.info('我是一键合并下发')
|
||||||
|
self.up_merge_all()
|
||||||
|
else:
|
||||||
|
print('CNC加工')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
7
sf_manufacturing/models/res_user.py
Normal file
7
sf_manufacturing/models/res_user.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import SUPERUSER_ID, _, api, fields, models, registry
|
||||||
|
|
||||||
|
class Users(models.Model):
|
||||||
|
_inherit = 'res.users'
|
||||||
|
|
||||||
|
workcenter_ids = fields.Many2many("mrp.workcenter", 'users_workcenter')
|
||||||
229
sf_manufacturing/models/stock.py
Normal file
229
sf_manufacturing/models/stock.py
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import logging
|
||||||
|
from collections import defaultdict, namedtuple
|
||||||
|
from odoo.addons.stock.models.stock_rule import ProcurementException
|
||||||
|
from re import findall as regex_findall
|
||||||
|
from re import split as regex_split
|
||||||
|
from odoo import SUPERUSER_ID, _, api, fields, models, registry
|
||||||
|
from odoo.tools import float_compare, float_is_zero, html_escape
|
||||||
|
|
||||||
|
|
||||||
|
class StockRule(models.Model):
|
||||||
|
_inherit = 'stock.rule'
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _run_pull(self, procurements):
|
||||||
|
moves_values_by_company = defaultdict(list)
|
||||||
|
mtso_products_by_locations = defaultdict(list)
|
||||||
|
|
||||||
|
# To handle the `mts_else_mto` procure method, we do a preliminary loop to
|
||||||
|
# isolate the products we would need to read the forecasted quantity,
|
||||||
|
# in order to to batch the read. We also make a sanitary check on the
|
||||||
|
# `location_src_id` field.
|
||||||
|
|
||||||
|
# list1 = []
|
||||||
|
# for item in procurements:
|
||||||
|
# num = int(item[0].product_qty)
|
||||||
|
# if num > 1:
|
||||||
|
# for no in range(1, num+1):
|
||||||
|
#
|
||||||
|
# Procurement = namedtuple('Procurement', ['product_id', 'product_qty',
|
||||||
|
# 'product_uom', 'location_id', 'name', 'origin',
|
||||||
|
# 'company_id',
|
||||||
|
# 'values'])
|
||||||
|
# s = Procurement(product_id=item[0].product_id,product_qty=1.0,product_uom=item[0].product_uom,
|
||||||
|
# location_id=item[0].location_id,
|
||||||
|
# name=item[0].name,
|
||||||
|
# origin=item[0].origin,
|
||||||
|
# company_id=item[0].company_id,
|
||||||
|
# values=item[0].values,
|
||||||
|
# )
|
||||||
|
# item1 = list(item)
|
||||||
|
# item1[0]=s
|
||||||
|
#
|
||||||
|
# list1.append(tuple(item1))
|
||||||
|
# else:
|
||||||
|
# list1.append(item)
|
||||||
|
|
||||||
|
for procurement, rule in procurements:
|
||||||
|
if not rule.location_src_id:
|
||||||
|
msg = _('No source location defined on stock rule: %s!') % (rule.name,)
|
||||||
|
raise ProcurementException([(procurement, msg)])
|
||||||
|
|
||||||
|
if rule.procure_method == 'mts_else_mto':
|
||||||
|
mtso_products_by_locations[rule.location_src_id].append(procurement.product_id.id)
|
||||||
|
|
||||||
|
# Get the forecasted quantity for the `mts_else_mto` procurement.
|
||||||
|
forecasted_qties_by_loc = {}
|
||||||
|
for location, product_ids in mtso_products_by_locations.items():
|
||||||
|
products = self.env['product.product'].browse(product_ids).with_context(location=location.id)
|
||||||
|
forecasted_qties_by_loc[location] = {product.id: product.free_qty for product in products}
|
||||||
|
|
||||||
|
# Prepare the move values, adapt the `procure_method` if needed.
|
||||||
|
procurements = sorted(procurements, key=lambda proc: float_compare(proc[0].product_qty, 0.0,
|
||||||
|
precision_rounding=proc[
|
||||||
|
0].product_uom.rounding) > 0)
|
||||||
|
list2 = []
|
||||||
|
for item in procurements:
|
||||||
|
num = int(item[0].product_qty)
|
||||||
|
|
||||||
|
product = self.env['product.product'].search(
|
||||||
|
[("id", '=', item[0].product_id.id)])
|
||||||
|
product_tmpl = self.env['product.template'].search(
|
||||||
|
["&", ("id", '=', product.product_tmpl_id.id), ('single_manufacturing', "!=", False)])
|
||||||
|
if product_tmpl:
|
||||||
|
if num > 1:
|
||||||
|
for no in range(1, num + 1):
|
||||||
|
Procurement = namedtuple('Procurement', ['product_id', 'product_qty',
|
||||||
|
'product_uom', 'location_id', 'name', 'origin',
|
||||||
|
'company_id',
|
||||||
|
'values'])
|
||||||
|
s = Procurement(product_id=item[0].product_id, product_qty=1.0, product_uom=item[0].product_uom,
|
||||||
|
location_id=item[0].location_id,
|
||||||
|
name=item[0].name,
|
||||||
|
origin=item[0].origin,
|
||||||
|
company_id=item[0].company_id,
|
||||||
|
values=item[0].values,
|
||||||
|
)
|
||||||
|
item1 = list(item)
|
||||||
|
item1[0] = s
|
||||||
|
|
||||||
|
list2.append(tuple(item1))
|
||||||
|
else:
|
||||||
|
list2.append(item)
|
||||||
|
else:
|
||||||
|
list2.append(item)
|
||||||
|
|
||||||
|
for procurement, rule in list2:
|
||||||
|
procure_method = rule.procure_method
|
||||||
|
if rule.procure_method == 'mts_else_mto':
|
||||||
|
qty_needed = procurement.product_uom._compute_quantity(procurement.product_qty,
|
||||||
|
procurement.product_id.uom_id)
|
||||||
|
if float_compare(qty_needed, 0, precision_rounding=procurement.product_id.uom_id.rounding) <= 0:
|
||||||
|
procure_method = 'make_to_order'
|
||||||
|
for move in procurement.values.get('group_id', self.env['procurement.group']).stock_move_ids:
|
||||||
|
if move.rule_id == rule and float_compare(move.product_uom_qty, 0,
|
||||||
|
precision_rounding=move.product_uom.rounding) > 0:
|
||||||
|
procure_method = move.procure_method
|
||||||
|
break
|
||||||
|
forecasted_qties_by_loc[rule.location_src_id][procurement.product_id.id] -= qty_needed
|
||||||
|
elif float_compare(qty_needed, forecasted_qties_by_loc[rule.location_src_id][procurement.product_id.id],
|
||||||
|
precision_rounding=procurement.product_id.uom_id.rounding) > 0:
|
||||||
|
procure_method = 'make_to_order'
|
||||||
|
else:
|
||||||
|
forecasted_qties_by_loc[rule.location_src_id][procurement.product_id.id] -= qty_needed
|
||||||
|
procure_method = 'make_to_stock'
|
||||||
|
|
||||||
|
move_values = rule._get_stock_move_values(*procurement)
|
||||||
|
move_values['procure_method'] = procure_method
|
||||||
|
moves_values_by_company[procurement.company_id.id].append(move_values)
|
||||||
|
|
||||||
|
for company_id, moves_values in moves_values_by_company.items():
|
||||||
|
# create the move as SUPERUSER because the current user may not have the rights to do it (mto product launched by a sale for example)
|
||||||
|
moves = self.env['stock.move'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create(moves_values)
|
||||||
|
# Since action_confirm launch following procurement_group we should activate it.
|
||||||
|
moves._action_confirm()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _run_manufacture(self, procurements):
|
||||||
|
productions_values_by_company = defaultdict(list)
|
||||||
|
errors = []
|
||||||
|
for procurement, rule in procurements:
|
||||||
|
if float_compare(procurement.product_qty, 0, precision_rounding=procurement.product_uom.rounding) <= 0:
|
||||||
|
# If procurement contains negative quantity, don't create a MO that would be for a negative value.
|
||||||
|
continue
|
||||||
|
bom = rule._get_matching_bom(procurement.product_id, procurement.company_id, procurement.values)
|
||||||
|
|
||||||
|
productions_values_by_company[procurement.company_id.id].append(rule._prepare_mo_vals(*procurement, bom))
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
raise ProcurementException(errors)
|
||||||
|
|
||||||
|
for company_id, productions_values in productions_values_by_company.items():
|
||||||
|
# create the MO as SUPERUSER because the current user may not have the rights to do it (mto product launched by a sale for example)
|
||||||
|
'''创建制造订单'''
|
||||||
|
productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create(
|
||||||
|
productions_values)
|
||||||
|
self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
|
||||||
|
self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
|
||||||
|
'''
|
||||||
|
创建工单
|
||||||
|
'''
|
||||||
|
productions._create_workorder()
|
||||||
|
|
||||||
|
productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \
|
||||||
|
(
|
||||||
|
p.move_dest_ids.procure_method != 'make_to_order' and not p.move_raw_ids and not p.workorder_ids)).action_confirm()
|
||||||
|
|
||||||
|
for production in productions:
|
||||||
|
'''
|
||||||
|
创建制造订单时生成序列号
|
||||||
|
'''
|
||||||
|
production.action_generate_serial()
|
||||||
|
origin_production = production.move_dest_ids and production.move_dest_ids[
|
||||||
|
0].raw_material_production_id or False
|
||||||
|
orderpoint = production.orderpoint_id
|
||||||
|
if orderpoint and orderpoint.create_uid.id == SUPERUSER_ID and orderpoint.trigger == 'manual':
|
||||||
|
production.message_post(
|
||||||
|
body=_('This production order has been created from Replenishment Report.'),
|
||||||
|
message_type='comment',
|
||||||
|
subtype_xmlid='mail.mt_note')
|
||||||
|
elif orderpoint:
|
||||||
|
production.message_post_with_view(
|
||||||
|
'mail.message_origin_link',
|
||||||
|
values={'self': production, 'origin': orderpoint},
|
||||||
|
subtype_id=self.env.ref('mail.mt_note').id)
|
||||||
|
elif origin_production:
|
||||||
|
production.message_post_with_view(
|
||||||
|
'mail.message_origin_link',
|
||||||
|
values={'self': production, 'origin': origin_production},
|
||||||
|
subtype_id=self.env.ref('mail.mt_note').id)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class ProductionLot(models.Model):
|
||||||
|
_inherit = 'stock.lot'
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def generate_lot_names1(self, display_name, first_lot, count):
|
||||||
|
"""Generate `lot_names` from a string."""
|
||||||
|
if first_lot.__contains__(display_name):
|
||||||
|
first_lot = first_lot[(len(display_name) + 1):]
|
||||||
|
|
||||||
|
# We look if the first lot contains at least one digit.
|
||||||
|
caught_initial_number = regex_findall(r"\d+", first_lot)
|
||||||
|
if not caught_initial_number:
|
||||||
|
return self.generate_lot_names1(display_name, first_lot + "0", count)
|
||||||
|
# We base the series on the last number found in the base lot.
|
||||||
|
initial_number = caught_initial_number[-1]
|
||||||
|
padding = len(initial_number)
|
||||||
|
# We split the lot name to get the prefix and suffix.
|
||||||
|
splitted = regex_split(initial_number, first_lot)
|
||||||
|
# initial_number could appear several times, e.g. BAV023B00001S00001
|
||||||
|
prefix = initial_number.join(splitted[:-1])
|
||||||
|
suffix = splitted[-1]
|
||||||
|
initial_number = int(initial_number)
|
||||||
|
|
||||||
|
lot_names = []
|
||||||
|
for i in range(0, count):
|
||||||
|
lot_names.append('%s-%s%s%s' % (
|
||||||
|
display_name,
|
||||||
|
prefix,
|
||||||
|
str(initial_number + i).zfill(padding),
|
||||||
|
suffix
|
||||||
|
))
|
||||||
|
return lot_names
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _get_next_serial(self, company, product):
|
||||||
|
"""Return the next serial number to be attributed to the product."""
|
||||||
|
if product.tracking == "serial":
|
||||||
|
last_serial = self.env['stock.lot'].search(
|
||||||
|
[('company_id', '=', company.id), ('product_id', '=', product.id)],
|
||||||
|
limit=1, order='id DESC')
|
||||||
|
if last_serial:
|
||||||
|
return self.env['stock.lot'].generate_lot_names1(product.name, last_serial.name, 2)[
|
||||||
|
1]
|
||||||
|
return "%s-%03d" % (product.name, 1)
|
||||||
59
sf_manufacturing/models/tray.py
Normal file
59
sf_manufacturing/models/tray.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of SmartGo. See LICENSE file for full copyright and licensing details.
|
||||||
|
import base64
|
||||||
|
from io import BytesIO
|
||||||
|
from odoo import api, fields, models, SUPERUSER_ID, _
|
||||||
|
from pystrich.code128 import Code128Encoder
|
||||||
|
|
||||||
|
|
||||||
|
class Tray(models.Model):
|
||||||
|
_inherit = 'sf.tray'
|
||||||
|
_description = '托盘'
|
||||||
|
qr_image = fields.Binary(string="托盘二维码", compute='compute_qr_image')
|
||||||
|
production_id = fields.Many2one('mrp.production', string='制造订单',
|
||||||
|
related='workorder_id.production_id'
|
||||||
|
)
|
||||||
|
workorder_id = fields.Many2one('mrp.workorder', string="工单"
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.onchange('production_id')
|
||||||
|
def updateTrayState(self):
|
||||||
|
if self.workorder_id != False and self.create_date != False:
|
||||||
|
|
||||||
|
self.state = '占用'
|
||||||
|
else:
|
||||||
|
self.state = '空闲'
|
||||||
|
|
||||||
|
def unclamp(self):
|
||||||
|
self.workorder_id = False
|
||||||
|
self.production_id = False
|
||||||
|
self.state = '空闲'
|
||||||
|
|
||||||
|
@api.depends('code')
|
||||||
|
def compute_qr_image(self):
|
||||||
|
for item in self:
|
||||||
|
if not item.code:
|
||||||
|
item.qr_image = False
|
||||||
|
continue
|
||||||
|
# 根据code动态生成二维码图片
|
||||||
|
# qr = qrcode.QRCode(
|
||||||
|
# version=1,
|
||||||
|
# error_correction=qrcode.constants.ERROR_CORRECT_L,
|
||||||
|
# box_size=10,
|
||||||
|
# border=4,
|
||||||
|
# )
|
||||||
|
# qr.add_data(item.code)
|
||||||
|
# qr.make(fit=True)
|
||||||
|
# img = qr.make_image()
|
||||||
|
# 生成条形码文件
|
||||||
|
# bar = barcode.get("ean13", "123456789102", writer=ImageWriter())
|
||||||
|
# a = bar.get_fullcode()
|
||||||
|
# b = bar.save('occ')
|
||||||
|
# 生成条形码图片
|
||||||
|
partner_encoder = Code128Encoder(item.code)
|
||||||
|
# 转换bytes流
|
||||||
|
temp = BytesIO()
|
||||||
|
partner_encoder.save(temp)
|
||||||
|
# img.save(temp, format='PNG')
|
||||||
|
qr_image = base64.b64encode(temp.getvalue())
|
||||||
|
item.qr_image = qr_image
|
||||||
74
sf_manufacturing/report/tray_report.xml
Normal file
74
sf_manufacturing/report/tray_report.xml
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!-- 托盘码打印尺寸-->
|
||||||
|
<record id="sf_tray1" model="report.paperformat">
|
||||||
|
<field name="name">Dymo Label Sheet</field>
|
||||||
|
<field name="default" eval="True"/>
|
||||||
|
<field name="format">custom</field>
|
||||||
|
<field name="page_height">100</field>
|
||||||
|
<field name="page_width">60</field>
|
||||||
|
<field name="orientation">Landscape</field>
|
||||||
|
<field name="margin_top">0</field>
|
||||||
|
<field name="margin_bottom">0</field>
|
||||||
|
<field name="margin_left">0</field>
|
||||||
|
<field name="margin_right">0</field>
|
||||||
|
<field name="disable_shrinking" eval="True"/>
|
||||||
|
<field name="dpi">96</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- 托盘码打印动作-->
|
||||||
|
<record id="label_sf_tray_code" model="ir.actions.report">
|
||||||
|
<field name="name">打印条形码</field>
|
||||||
|
<field name="model">sf.tray</field>
|
||||||
|
<field name="report_type">qweb-pdf</field>
|
||||||
|
<field name="report_name">sf_manufacturing.sf_tray_template</field>
|
||||||
|
<field name="report_file">sf_manufacturing.sf_tray_template</field>
|
||||||
|
<field name="binding_model_id" ref="model_sf_tray"/>
|
||||||
|
<field name="binding_type">report</field>
|
||||||
|
<field name="paperformat_id" ref="sf_manufacturing.sf_tray1"/>
|
||||||
|
|
||||||
|
</record>
|
||||||
|
<!-- 托盘码打印模板-->
|
||||||
|
<template id="sf_tray_template">
|
||||||
|
<t t-call="web.html_container">
|
||||||
|
<t t-call="web.external_layout">
|
||||||
|
<t t-foreach="docs" t-as="o">
|
||||||
|
<div class="page">
|
||||||
|
<div t-field="o.code"
|
||||||
|
t-options="{'widget': 'barcode', 'width': 600, 'height': 100, 'img_style': 'width:350px;height:60px'}"/>
|
||||||
|
<div t-field="o.code" style="text-align: center"/>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 产品信息打印动作-->
|
||||||
|
<record id="label_sf_tray_code1" model="ir.actions.report">
|
||||||
|
<field name="name">打印产品信息</field>
|
||||||
|
<field name="model">mrp.workorder</field>
|
||||||
|
<field name="report_type">qweb-pdf</field>
|
||||||
|
<field name="report_name">sf_manufacturing.sf_tray_template1</field>
|
||||||
|
<field name="report_file">sf_manufacturing.sf_tray_template1</field>
|
||||||
|
<field name="binding_model_id" ref="model_mrp_workorder"/>
|
||||||
|
<field name="binding_type">report</field>
|
||||||
|
<field name="paperformat_id" ref="sf_manufacturing.sf_tray1"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- 产品信息打印模板-->
|
||||||
|
<template id="sf_tray_template1">
|
||||||
|
<t t-call="web.html_container">
|
||||||
|
<t t-call="web.external_layout">
|
||||||
|
<t t-foreach="docs" t-as="o">
|
||||||
|
<div class="page">
|
||||||
|
<div t-field="o.production_id.name"
|
||||||
|
t-options="{'widget': 'barcode', 'width': 600, 'height': 100, 'img_style': 'width:350px;height:60px'}"/>
|
||||||
|
<div t-field="o.production_id" style="text-align: center"/>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
27
sf_manufacturing/security/group_security.xml
Normal file
27
sf_manufacturing/security/group_security.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record model="ir.module.category" id="module_category_employee">
|
||||||
|
<field name="name">员工</field>
|
||||||
|
<field name="sequence">1</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.module.category" id="module_category_manage">
|
||||||
|
<field name="name">管理</field>
|
||||||
|
<field name="sequence">2</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- <!– 系统管理相关 –>-->
|
||||||
|
<!-- <record id="group_master_data_manager" model="res.groups">-->
|
||||||
|
<!-- <field name="name">基础设置</field>-->
|
||||||
|
<!-- <field name="category_id" ref="base.module_category_employee"/>-->
|
||||||
|
<!-- <field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>-->
|
||||||
|
<!-- </record>-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
10
sf_manufacturing/security/ir.model.access.csv
Normal file
10
sf_manufacturing/security/ir.model.access.csv
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_sf_cnc_processing,sf_cnc_processing,model_sf_cnc_processing,base.group_user,1,1,1,1
|
||||||
|
access_sf_model_type,sf_model_type,model_sf_model_type,base.group_user,1,1,1,1
|
||||||
|
access_sf_product_model_type_routing_sort,sf_product_model_type_routing_sort,model_sf_product_model_type_routing_sort,base.group_user,1,1,1,1
|
||||||
|
access_sf_embryo_model_type_routing_sort,sf_embryo_model_type_routing_sort,model_sf_embryo_model_type_routing_sort,base.group_user,1,1,1,1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
84
sf_manufacturing/views/model_type_view.xml
Normal file
84
sf_manufacturing/views/model_type_view.xml
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
#------------------模型类型------------------
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="search_sf_model_type_view">
|
||||||
|
<field name="name">search.sf.model.type</field>
|
||||||
|
<field name="model">sf.model.type</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="模型类型">
|
||||||
|
<field name="name" string="模糊搜索"
|
||||||
|
filter_domain="[('name', 'ilike', self)]"/>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="tree_sf_model_type_view">
|
||||||
|
<field name="name">tree.sf.model.type</field>
|
||||||
|
<field name="model">sf.model.type</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="模型类型">
|
||||||
|
<field name="name"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="form_sf_model_type">
|
||||||
|
<field name="name">form.sf.model.type</field>
|
||||||
|
<field name="model">sf.model.type</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="模型类型">
|
||||||
|
<group>
|
||||||
|
<field name="name" required="1"/>
|
||||||
|
<field name="embryo_tolerance" required="1" string="胚料容余(mm)"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name='product_routing_tmpl_ids'>
|
||||||
|
<tree editable='bottom'>
|
||||||
|
<field name="sequence" widget="handle" string="序号"/>
|
||||||
|
<field name="route_workcenter_id" string="工序"/>
|
||||||
|
<field name="routing_type" string="类型"/>
|
||||||
|
<field name="is_repeat" string="重复"/>
|
||||||
|
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name='embryo_routing_tmpl_ids'>
|
||||||
|
<tree editable='bottom'>
|
||||||
|
<field name="sequence" widget="handle" string="序号"/>
|
||||||
|
<field name="route_workcenter_id" string="工序"/>
|
||||||
|
<field name="routing_type" string="类型"/>
|
||||||
|
<field name="is_repeat" string="重复"/>
|
||||||
|
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</group>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_sf_model_type" model="ir.actions.act_window">
|
||||||
|
<field name="name">模型类型</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">sf.model.type</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
[模型类型] 还没有哦!点左上角的[创建]按钮,沙发归你了!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="menu_sf_model_type"
|
||||||
|
name="模型类型"
|
||||||
|
parent="mrp.menu_mrp_configuration"
|
||||||
|
sequence="10"
|
||||||
|
action="action_sf_model_type"
|
||||||
|
/>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
105
sf_manufacturing/views/mrp_maintenance_views.xml
Normal file
105
sf_manufacturing/views/mrp_maintenance_views.xml
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="maintenance_equipment_view_form_inherit_mrp" model="ir.ui.view">
|
||||||
|
<field name="name">maintenance.equipment.view.form.inherit.mrp</field>
|
||||||
|
<field name="model">maintenance.equipment</field>
|
||||||
|
<field name="inherit_id" ref="maintenance.hr_equipment_view_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//div[@name='button_box']" position="inside">
|
||||||
|
<button name="button_mrp_workcenter" type="object" class="oe_stat_button"
|
||||||
|
icon="fa-cogs" string="Work Center" attrs="{'invisible': [('workcenter_id', '=', False)]}"
|
||||||
|
groups="mrp.group_mrp_routings">
|
||||||
|
</button>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='location']" position="after">
|
||||||
|
<field name="workcenter_id" context="{'default_company_id':company_id}"
|
||||||
|
groups="mrp.group_mrp_routings"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//group[@name='maintenance']" position="after">
|
||||||
|
<group name="statistics">
|
||||||
|
<label for="expected_mtbf" string="Expected Mean Time Between Failure"/>
|
||||||
|
<div class="o_row">
|
||||||
|
<field name="expected_mtbf"/>
|
||||||
|
days
|
||||||
|
</div>
|
||||||
|
<label for="mtbf" string="Mean Time Between Failure"/>
|
||||||
|
<div class="o_row">
|
||||||
|
<field name="mtbf"/>
|
||||||
|
days
|
||||||
|
</div>
|
||||||
|
<label for="estimated_next_failure" string="Estimated Next Failure"/>
|
||||||
|
<div class="o_row">
|
||||||
|
<field name="estimated_next_failure"/>
|
||||||
|
</div>
|
||||||
|
<field name="latest_failure_date" string="Latest Failure"/>
|
||||||
|
<label for="mttr" string="Mean Time To Repair"/>
|
||||||
|
<div class="o_row">
|
||||||
|
<field name="mttr"/>
|
||||||
|
days
|
||||||
|
</div>
|
||||||
|
</group>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="maintenance_request_view_form_inherit_mrp" model="ir.ui.view">
|
||||||
|
<field name="name">maintenance.request.view.form.inherit.mrp</field>
|
||||||
|
<field name="model">maintenance.request</field>
|
||||||
|
<field name="inherit_id" ref="maintenance.hr_equipment_request_view_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='maintenance_type']" position="after">
|
||||||
|
<field name="production_company_id" invisible="1"/>
|
||||||
|
<field name="workorder_id" invisible="1"/>
|
||||||
|
<field name="production_id" options="{'no_create': True, 'no_open': True}"/>
|
||||||
|
<field name="workorder_id" attrs="{'invisible': [('production_id', '=', False)]}"
|
||||||
|
options="{'no_create': True, 'no_open': True}" domain="[('production_id', '=', production_id)]"
|
||||||
|
groups="mrp.group_mrp_routings"/>
|
||||||
|
<!-- <field name="repair_id"/> -->
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//div[hasclass('oe_chatter')]" position="after">
|
||||||
|
<div invisible="not context.get('discard_on_footer_button', False)">
|
||||||
|
<footer class="oe_edit_only">
|
||||||
|
<button special="save" data-hotkey="v" string="Save" class="oe_highlight"/>
|
||||||
|
<button string="Discard" special="cancel" data-hotkey="z"/>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</xpath>
|
||||||
|
<field name="equipment_id" position="attributes">
|
||||||
|
<attribute name="domain">['|', (not workorder_id and 1 or 0, '=', 1), '|', ('workcenter_id', '=',
|
||||||
|
False), ('workcenter_id.order_ids', 'in', workorder_id)]
|
||||||
|
</attribute>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="maintenance_request_view_search_inherit_mrp" model="ir.ui.view">
|
||||||
|
<field name="name">maintenence.request.view.search.inherit.mrp</field>
|
||||||
|
<field name="model">maintenance.request</field>
|
||||||
|
<field name="inherit_id" ref="maintenance.hr_equipment_request_view_search"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='maintenance_team_id']" position="after">
|
||||||
|
<field name="production_id" string="Operation"
|
||||||
|
filter_domain="['|', ('production_id', 'ilike', self), ('workorder_id', 'ilike', self)]"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="maintenance.menu_equipment_form"
|
||||||
|
name="Equipments"
|
||||||
|
parent="maintenance.menu_maintenance_title"
|
||||||
|
groups="maintenance.group_equipment_manager,base.group_user"
|
||||||
|
sequence="2"/>
|
||||||
|
<menuitem id="menu_workcenter_tree"
|
||||||
|
action="mrp.mrp_workcenter_action"
|
||||||
|
groups="mrp.group_mrp_routings"
|
||||||
|
parent="maintenance.menu_equipment_form"
|
||||||
|
sequence="1"/>
|
||||||
|
<menuitem
|
||||||
|
id="menu_equipment_dashboard"
|
||||||
|
name="Machines & Tools"
|
||||||
|
parent="maintenance.menu_equipment_form"
|
||||||
|
action="maintenance.hr_equipment_action"
|
||||||
|
sequence="2"/>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
19
sf_manufacturing/views/mrp_routing_workcenter_view.xml
Normal file
19
sf_manufacturing/views/mrp_routing_workcenter_view.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record model="ir.ui.view" id="view_mrp_routing_workcenter_form_inherit_sf">
|
||||||
|
<field name="name">mrp.routing.workcenter.form.inherit.sf</field>
|
||||||
|
<field name="model">mrp.routing.workcenter</field>
|
||||||
|
<field name="inherit_id" ref="mrp.mrp_routing_workcenter_form_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="workcenter_id" position="replace">
|
||||||
|
<field name="workcenter_ids" widget="many2many_tags" string="工作中心" required="0"/>
|
||||||
|
</field>
|
||||||
|
<field name="bom_product_template_attribute_value_ids" position="after">
|
||||||
|
<field name="routing_type" required="1"/>
|
||||||
|
<field name="is_repeat"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
153
sf_manufacturing/views/mrp_workcenter_views.xml
Normal file
153
sf_manufacturing/views/mrp_workcenter_views.xml
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<!-- 工作中心看板 -->
|
||||||
|
<record id="mrp_production_view_form_inherit_maintenance" model="ir.ui.view">
|
||||||
|
<field name="name">mrp.production.view.form.inherit.maintenance</field>
|
||||||
|
<field name="model">mrp.production</field>
|
||||||
|
<field name="inherit_id" ref="mrp.mrp_production_form_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<button name="action_cancel" position="before">
|
||||||
|
<button name="button_maintenance_req" type="object" string="维修请求"/>
|
||||||
|
</button>
|
||||||
|
<div name="button_box" position="inside">
|
||||||
|
<button name="open_maintenance_request_mo" type="object" class="oe_stat_button" icon="fa-wrench"
|
||||||
|
attrs="{'invisible': [('maintenance_count', '=', 0)]}"
|
||||||
|
context="{'search_default_production_id': active_id}">
|
||||||
|
<div class="o_field_widget o_stat_info">
|
||||||
|
<span class="o_stat_value">
|
||||||
|
<field name="maintenance_count"/>
|
||||||
|
</span>
|
||||||
|
<span class="o_stat_text">Maintenance</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- <record id="mrp_workcenter_view_kanban_inherit_workorder" model="ir.ui.view">-->
|
||||||
|
<!-- <field name="name">mrp.workcenter.view.kanban.inherit.mrp.workorder</field>-->
|
||||||
|
<!-- <field name="model">mrp.workcenter</field>-->
|
||||||
|
<!-- <field name="inherit_id" ref="mrp.mrp_workcenter_kanban"/>-->
|
||||||
|
<!-- <field name="arch" type="xml">-->
|
||||||
|
<!-- <!– Desktop view –>-->
|
||||||
|
<!-- <xpath expr="//div[@name='o_wo']" position="inside">-->
|
||||||
|
<!-- <button class="btn btn-secondary fa fa-desktop" name="action_work_order" type="object"-->
|
||||||
|
<!-- context="{'search_default_ready': 1, 'search_default_progress': 1, 'search_default_pending': 1, 'desktop_list_view': 1, 'search_default_workcenter_id': active_id}"-->
|
||||||
|
<!-- title="Work orders" aria-label="Work orders"/>-->
|
||||||
|
<!-- </xpath>-->
|
||||||
|
<!-- </field>-->
|
||||||
|
<!-- </record>-->
|
||||||
|
|
||||||
|
<!-- override to change the no content image -->
|
||||||
|
<record id="mrp.action_work_orders" model="ir.actions.act_window">
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_workorder">
|
||||||
|
没有工单要做!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
工作订单是作为制造订单的一部分执行的操作。
|
||||||
|
工序在物料清单中定义或直接添加到制造订单中。
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
使用工作台工作中心控制面板直接登记车间中的操作.
|
||||||
|
平板电脑为您的工人提供工作表,并允许他们报废产品,跟踪时间,
|
||||||
|
发起维护请求,执行质量测试等.
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="mrp_workcenter_kanban_action1" model="ir.actions.act_window">
|
||||||
|
<field name="name">工作中心看板</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">mrp.workcenter</field>
|
||||||
|
<field name="view_mode">kanban,form</field>
|
||||||
|
<field name="view_id" ref="mrp.mrp_workcenter_kanban"/>
|
||||||
|
<field name="search_view_id" ref="mrp.view_mrp_workcenter_search"/>
|
||||||
|
<field name="domain">[('users_ids','in',uid)]</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
创建新的工作中心
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
制造业务在工作中心处理。工作中心可以由
|
||||||
|
工人和/或机器,它们用于成本核算、调度、产能规划等。
|
||||||
|
它们可以通过配置菜单定义。
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="menu_mrp_dashboard"
|
||||||
|
name="工作中心看板"
|
||||||
|
action="mrp_workcenter_kanban_action1"
|
||||||
|
groups="mrp.group_mrp_routings"
|
||||||
|
parent="mrp.menu_mrp_root"
|
||||||
|
sequence="5"/>
|
||||||
|
|
||||||
|
<!-- MRP.WORKCENTER -->
|
||||||
|
<record model="ir.ui.view" id="view_mrp_workcenter_form_inherit_sf">
|
||||||
|
<field name="name">mrp.workcenter.form.inherit.sf</field>
|
||||||
|
<field name="model">mrp.workcenter</field>
|
||||||
|
<field name="inherit_id" ref="mrp.mrp_workcenter_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//notebook" position="inside">
|
||||||
|
<page string="Equipment" name="equipment">
|
||||||
|
<field name="equipment_ids" widget="many2many">
|
||||||
|
<tree string="Equipments">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="technician_user_id"/>
|
||||||
|
<field name="category_id"/>
|
||||||
|
<field name="mtbf"/>
|
||||||
|
<field name="mttr"/>
|
||||||
|
<field name="estimated_next_failure" string="Est. Next Failure"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='company_id']" position="after">
|
||||||
|
<field name="users_ids" widget="many2many_tags" string="可操作用户"/>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
|
<xpath expr="//field[@name='alternative_workcenter_ids']" position="after">
|
||||||
|
<field name="machine_tool_id" domain="[('is_binding','=',False)]"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="mrp_workcenter_view_kanban_inherit_maintenance" model="ir.ui.view">
|
||||||
|
<field name="name">mrp.workcenter.view.kanban.inherit.maintenance</field>
|
||||||
|
<field name="model">mrp.workcenter</field>
|
||||||
|
<field name="inherit_id" ref="mrp.mrp_workcenter_kanban"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//div[@name='plan_order']" position="after">
|
||||||
|
<div>
|
||||||
|
<a name="%(maintenance.hr_equipment_request_action)d" type="action">Maintenance</a>
|
||||||
|
</div>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- MRP.PRODUCTION -->
|
||||||
|
<record id="mrp_production_view_form_inherit_maintenance" model="ir.ui.view">
|
||||||
|
<field name="name">mrp.production.view.form.inherit.maintenance</field>
|
||||||
|
<field name="model">mrp.production</field>
|
||||||
|
<field name="inherit_id" ref="mrp.mrp_production_form_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<button name="action_cancel" position="before">
|
||||||
|
<button name="button_maintenance_req" type="object" string="维修请求"/>
|
||||||
|
</button>
|
||||||
|
<div name="button_box" position="inside">
|
||||||
|
<button name="open_maintenance_request_mo" type="object" class="oe_stat_button" icon="fa-wrench"
|
||||||
|
attrs="{'invisible': [('maintenance_count', '=', 0)]}"
|
||||||
|
context="{'search_default_production_id': active_id}">
|
||||||
|
<div class="o_field_widget o_stat_info">
|
||||||
|
<span class="o_stat_value">
|
||||||
|
<field name="maintenance_count"/>
|
||||||
|
</span>
|
||||||
|
<span class="o_stat_text">Maintenance</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
387
sf_manufacturing/views/mrp_workorder_view.xml
Normal file
387
sf_manufacturing/views/mrp_workorder_view.xml
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record model="ir.ui.view" id="view_mrp_production_workorder_tree_editable_inherit_sf">
|
||||||
|
<field name="name">mrp.production.workorder.tree.editable.inherit.sf</field>
|
||||||
|
<field name="model">mrp.workorder</field>
|
||||||
|
<field name="inherit_id" ref="mrp.mrp_production_workorder_tree_editable_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="name" position="before">
|
||||||
|
<field name="sequence"/>
|
||||||
|
<field name='user_permissions' invisible="1"/>
|
||||||
|
</field>
|
||||||
|
<field name="name" position="after">
|
||||||
|
<field name="processing_panel"/>
|
||||||
|
</field>
|
||||||
|
<field name="state" position="after">
|
||||||
|
<field name="work_state"/>
|
||||||
|
</field>
|
||||||
|
<xpath expr="//button[@name='button_start']" position="attributes">
|
||||||
|
<attribute name="attrs">{'invisible': ['|', '|', '|','|', ('production_state','in', ('draft', 'done',
|
||||||
|
'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),
|
||||||
|
('is_user_working', '!=', False),("user_permissions","=",False)]}
|
||||||
|
</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//button[@name='%(mrp.act_mrp_block_workcenter_wo)d']" position="attributes">
|
||||||
|
<attribute name="attrs">{'invisible': [("user_permissions","=",False)]}</attribute>
|
||||||
|
<attribute name="string">停工</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//button[@name='action_open_wizard']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
|
||||||
|
</xpath>
|
||||||
|
<!-- <button name="button_start" type="object" string="Start" class="btn-success"-->
|
||||||
|
<!-- attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')), ('is_user_working', '!=', False)]}"/>-->
|
||||||
|
<!-- <button name="button_pending" type="object" string="Pause" class="btn-warning"-->
|
||||||
|
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>-->
|
||||||
|
<!-- <button name="button_finish" type="object" string="Done" class="btn-success"-->
|
||||||
|
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>-->
|
||||||
|
<!-- <button name="%(mrp.act_mrp_block_workcenter_wo)d" type="action" string="Block" context="{'default_workcenter_id': workcenter_id}" class="btn-danger"-->
|
||||||
|
<!-- attrs="{'invisible': ['|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked')]}"/>-->
|
||||||
|
<!-- <button name="button_unblock" type="object" string="Unblock" context="{'default_workcenter_id': workcenter_id}" class="btn-danger"-->
|
||||||
|
<!-- attrs="{'invisible': ['|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked')]}"/>-->
|
||||||
|
<!-- <button name="action_open_wizard" type="object" icon="fa-external-link" class="oe_edit_only"-->
|
||||||
|
<!-- title="Open Work Order"/>-->
|
||||||
|
<!-- edit='false' create = 'false'-->
|
||||||
|
<tree position="attributes">
|
||||||
|
<attribute name="multi_edit"></attribute>
|
||||||
|
<attribute name="editable"></attribute>
|
||||||
|
<attribute name="create">false</attribute>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.actions.act_window" id="mrp_workorder_action_tablet">
|
||||||
|
<field name="name">工单</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">mrp.workorder</field>
|
||||||
|
<field name="view_mode">tree,kanban,form</field>
|
||||||
|
<field name="view_ids" eval="[(5, 0, 0),
|
||||||
|
(0, 0, {'view_mode': 'tree', 'view_id': ref('mrp.mrp_production_workorder_tree_editable_view')}),
|
||||||
|
(0, 0, {'view_mode': 'kanban', 'view_id': ref('mrp.workcenter_line_kanban')}) ]"/>
|
||||||
|
<!-- <field name="target">fullscreen</field>-->
|
||||||
|
<field name="target">current</field>
|
||||||
|
<field name="domain">[('state', '!=', 'cancel')]</field>
|
||||||
|
<field name="context">{'search_default_workcenter_id': active_id}</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_workorder">
|
||||||
|
没有工单要做!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
工作订单是作为制造订单的一部分执行的操作。
|
||||||
|
工序在物料清单中定义或直接添加到制造订单中。
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
使用工作台工作中心控制面板直接登记车间中的操作.
|
||||||
|
平板电脑为您的工人提供工作表,并允许他们报废产品,跟踪时间,
|
||||||
|
发起维护请求,执行质量测试等.
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- <record model="ir.ui.view" id="view_mrp_production_workorder_form_inherit_sf">-->
|
||||||
|
<!-- <field name="name">mrp.production.workorder.form.inherit.sf</field>-->
|
||||||
|
<!-- <field name="model">mrp.workorder</field>-->
|
||||||
|
<!-- <field name="inherit_id" ref="mrp.mrp_production_workorder_form_view_inherit"/>-->
|
||||||
|
<!-- <field name="arch" type="xml">-->
|
||||||
|
<!-- <field name="production_id" position="after">-->
|
||||||
|
<!-- <field name="processing_panel" readonly="1"/>-->
|
||||||
|
<!-- </field>-->
|
||||||
|
<!-- </field>-->
|
||||||
|
<!-- </record>-->
|
||||||
|
|
||||||
|
<record id="view_mrp_production_workorder_tray_form_inherit_sf" model="ir.ui.view">
|
||||||
|
<field name="name">mrp.production.workorder.tray.form.inherit.sf</field>
|
||||||
|
<field name="model">mrp.workorder</field>
|
||||||
|
<field name="inherit_id" ref="mrp.mrp_production_workorder_form_view_inherit"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='state']" position="before">
|
||||||
|
|
||||||
|
<field name='user_permissions' invisible="1"/>
|
||||||
|
<button name="button_start" type="object" string="开始" class="btn-success"
|
||||||
|
attrs="{'invisible': ['|', '|', '|','|', ('production_state','in', ('draft', 'done',
|
||||||
|
'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),
|
||||||
|
('is_user_working', '!=', False),('user_permissions','=',False)]}"/>
|
||||||
|
<button name="button_pending" type="object" string="暂停" class="btn-warning"
|
||||||
|
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>
|
||||||
|
<button name="button_finish" type="object" string="完成" class="btn-success"
|
||||||
|
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>
|
||||||
|
<button name="%(mrp.act_mrp_block_workcenter_wo)d" type="action" string="停工"
|
||||||
|
context="{'default_workcenter_id': workcenter_id}" class="btn-danger"
|
||||||
|
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'),('user_permissions','=',False)]}"/>
|
||||||
|
<button name="button_unblock" type="object" string="Unblock"
|
||||||
|
context="{'default_workcenter_id': workcenter_id}" class="btn-danger"
|
||||||
|
attrs="{'invisible': ['|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked')]}"/>
|
||||||
|
</xpath>
|
||||||
|
<!-- 隐藏物料清单-->
|
||||||
|
<xpath expr="//page[@name='components']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<!-- 隐藏物料清单-->
|
||||||
|
|
||||||
|
<field name="production_id" position="after" invisible="0">
|
||||||
|
<group>
|
||||||
|
<field name="date_planned_start" invisible="1"/>
|
||||||
|
<field name="date_planned_finished" invisible="1"/>
|
||||||
|
<!-- <field name="production_id" readonly="1"/>-->
|
||||||
|
<field name="duration" widget="mrp_timer"
|
||||||
|
attrs="{'invisible': [('production_state','=', 'draft')], 'readonly': [('is_user_working', '=', True)]}"
|
||||||
|
sum="real duration"/>
|
||||||
|
<field name="processing_panel" readonly="1" attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
<!-- <field name="processing_panel" readonly="1" attrs="{'invisible': [('routing_type', 'in', ('获取CNC加工程序','装夹','解除装夹',-->
|
||||||
|
<!-- '前置三元定位检测','后置三元质量检测','解除装夹'))]}"/>-->
|
||||||
|
</field>
|
||||||
|
|
||||||
|
<xpath expr="//page[1]" position="before">
|
||||||
|
<page string="获取CNC加工程序" attrs='{"invisible": [("routing_type","!=","获取CNC加工程序")]}'>
|
||||||
|
<group>
|
||||||
|
<field name="programming_no" readonly="1"
|
||||||
|
attrs='{"invisible": [("programming_no","=",False)]}'/>
|
||||||
|
<field name="programming_state" readonly="1"
|
||||||
|
attrs='{"invisible": [("programming_no","=",False)]}'/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<div class="col-12 col-lg-6 o_setting_box" style="white-space: nowrap">
|
||||||
|
<button type="object" class="oe_highlight" name="fetchCNC" string="获取CNC程序代码"
|
||||||
|
attrs='{"invisible": ["|", "|", ("state","!=","progress"),("user_permissions","=",False),("programming_no","!=",False)]}'/>
|
||||||
|
</div>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
|
||||||
|
</page>
|
||||||
|
|
||||||
|
</xpath>
|
||||||
|
<!-- <page string="Components" name="components">-->
|
||||||
|
<xpath expr="//page[1]" position="before">
|
||||||
|
<page string="装夹托盘" attrs='{"invisible": [("routing_type","!=","装夹")]}'>
|
||||||
|
<group>
|
||||||
|
<field name="routing_type" invisible="1"/>
|
||||||
|
<field name="tray_code"/>
|
||||||
|
<field name="tray_id" readonly="1"/>
|
||||||
|
|
||||||
|
|
||||||
|
</group>
|
||||||
|
<div class="col-12 col-lg-6 o_setting_box">
|
||||||
|
<button type="object" class="oe_highlight" name="gettray" string="绑定托盘"
|
||||||
|
attrs='{"invisible": ["|","|",("tray_id","!=",False),("state","!=","progress"),("production_id","=",False)]}'/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</page>
|
||||||
|
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//page[1]" position="before">
|
||||||
|
<page string="前置三元检测定位参数" attrs='{"invisible": [("routing_type","!=","前置三元定位检测")]}'>
|
||||||
|
|
||||||
|
<div>左面:</div>
|
||||||
|
<div class="o_address_format">
|
||||||
|
<label for="X1_axis" string="x"/>
|
||||||
|
<field name="X1_axis" class="o_form_label"
|
||||||
|
decoration-success="X1_axis > 0"
|
||||||
|
decoration-warning="X1_axis < 0"/>
|
||||||
|
<label for="Y1_axis" string="y"/>
|
||||||
|
<field name="Y1_axis" class="o_form_label"
|
||||||
|
decoration-success="Y1_axis > 0"
|
||||||
|
decoration-warning="Y1_axis < 0"/>
|
||||||
|
<label for="Z1_axis" string="z"/>
|
||||||
|
<field name="Z1_axis" class="o_form_label"
|
||||||
|
decoration-success="Z1_axis > 0"
|
||||||
|
decoration-warning="Z1_axis < 0"/>
|
||||||
|
<div></div>
|
||||||
|
<label for="X2_axis" string="x"/>
|
||||||
|
<field name="X2_axis" class="o_form_label"
|
||||||
|
decoration-success="X2_axis > 0"
|
||||||
|
decoration-warning="X2_axis < 0"/>
|
||||||
|
<label for="Y2_axis" string="y"/>
|
||||||
|
<field name="Y2_axis" class="o_form_label"
|
||||||
|
decoration-success="Y2_axis > 0"
|
||||||
|
decoration-warning="Y2_axis < 0"/>
|
||||||
|
<label for="Z2_axis" string="z"/>
|
||||||
|
<field name="Z2_axis" class="o_form_label"
|
||||||
|
decoration-success="Z2_axis > 0"
|
||||||
|
decoration-warning="Z2_axis < 0"/>
|
||||||
|
</div>
|
||||||
|
<div>前面:</div>
|
||||||
|
<div class="o_address_format">
|
||||||
|
<label for="X3_axis" string="x"/>
|
||||||
|
<field name="X3_axis" class="o_form_label"
|
||||||
|
decoration-success="X3_axis > 0"
|
||||||
|
decoration-warning="X3_axis < 0"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="Y3_axis" string="y"/>
|
||||||
|
<field name="Y3_axis" class="o_form_label"
|
||||||
|
decoration-success="Y3_axis > 0"
|
||||||
|
decoration-warning="Y3_axis < 0"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="Z3_axis" string="z"/>
|
||||||
|
<field name="Z3_axis" class="o_form_label"
|
||||||
|
decoration-success="Z3_axis > 0"
|
||||||
|
decoration-warning="Z3_axis < 0"/>
|
||||||
|
<div></div>
|
||||||
|
<label for="X4_axis" string="x"/>
|
||||||
|
<field name="X4_axis" class="o_form_label"
|
||||||
|
decoration-success="X4_axis > 0"
|
||||||
|
decoration-warning="X4_axis < 0"
|
||||||
|
/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="Y4_axis" string="y"/>
|
||||||
|
<field name="Y4_axis" class="o_form_label"
|
||||||
|
decoration-success="Y4_axis > 0"
|
||||||
|
decoration-warning="Y4_axis < 0"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="Z4_axis" string="z"/>
|
||||||
|
<field name="Z4_axis" class="o_form_label"
|
||||||
|
decoration-success="Z4_axis > 0"
|
||||||
|
decoration-warning="Z4_axis < 0"/>
|
||||||
|
</div>
|
||||||
|
<div>右面:</div>
|
||||||
|
<div class="o_address_format">
|
||||||
|
<label for="X5_axis" string="x"/>
|
||||||
|
<field name="X5_axis" class="o_form_label"
|
||||||
|
decoration-success="X5_axis > 0"
|
||||||
|
decoration-warning="X5_axis < 0"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="Y5_axis" string="y"/>
|
||||||
|
<field name="Y5_axis" class="o_form_label" decoration-success="Y5_axis > 0"
|
||||||
|
decoration-warning="Y5_axis < 0"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="Z5_axis" string="z"/>
|
||||||
|
<field name="Z5_axis" class="o_form_label" decoration-success="Z5_axis > 0"
|
||||||
|
decoration-warning="Z5_axis < 0"/>
|
||||||
|
<div></div>
|
||||||
|
<label for="X6_axis" string="x"/>
|
||||||
|
<field name="X6_axis" class="o_form_label" decoration-success="X6_axis > 0"
|
||||||
|
decoration-warning="X6_axis < 0"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="Y6_axis" string="y"/>
|
||||||
|
<field name="Y6_axis" class="o_form_label" decoration-success="Y6_axis > 0"
|
||||||
|
decoration-warning="Y6_axis < 0"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="Z6_axis" string="z"/>
|
||||||
|
<field name="Z6_axis" class="o_form_label" decoration-success="Z6_axis > 0"
|
||||||
|
decoration-warning="Z6_axis < 0"/>
|
||||||
|
</div>
|
||||||
|
<div>后面:</div>
|
||||||
|
<div class="o_address_format">
|
||||||
|
<label for="X7_axis" string="x"/>
|
||||||
|
<field name="X7_axis" class="o_form_label" decoration-success="X7_axis > 0"
|
||||||
|
decoration-warning="X7_axis < 0"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="Y7_axis" string="y"/>
|
||||||
|
<field name="Y7_axis" class="o_form_label" decoration-success="Y7_axis > 0"
|
||||||
|
decoration-warning="Y7_axis < 0"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="Z7_axis" string="z"/>
|
||||||
|
<field name="Z7_axis" class="o_form_label" decoration-success="Z7_axis > 0"
|
||||||
|
decoration-warning="Z7_axis < 0"/>
|
||||||
|
<div></div>
|
||||||
|
<label for="X8_axis" string="x"/>
|
||||||
|
<field name="X8_axis" class="o_form_label" decoration-success="X8_axis > 0"
|
||||||
|
decoration-warning="X8_axis < 0"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="Y8_axis" string="y"/>
|
||||||
|
<field name="Y8_axis" class="o_form_label" decoration-success="Y8_axis > 0"
|
||||||
|
decoration-warning="Y8_axis < 0"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="Z8_axis" string="z"/>
|
||||||
|
<field name="Z8_axis" class="o_form_label" decoration-success="Z8_axis > 0"
|
||||||
|
decoration-warning="Z8_axis < 0"/>
|
||||||
|
</div>
|
||||||
|
<div>上面:</div>
|
||||||
|
<div class="o_address_format">
|
||||||
|
<label for="X9_axis" string="x"/>
|
||||||
|
<field name="X9_axis" class="o_form_label" decoration-success="X9_axis > 0"
|
||||||
|
decoration-warning="X9_axis < 0"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="Y9_axis" string="y"/>
|
||||||
|
<field name="Y9_axis" class="o_form_label" decoration-success="Y9_axis > 0"
|
||||||
|
decoration-warning="Y9_axis < 0"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="Z9_axis" string="z"/>
|
||||||
|
<field name="Z9_axis" class="o_form_label" decoration-success="Z9_axis > 0"
|
||||||
|
decoration-warning="Z9_axis < 0"/>
|
||||||
|
<div></div>
|
||||||
|
<label for="X10_axis" string="x"/>
|
||||||
|
<field name="X10_axis" class="o_form_label" decoration-success="X10_axis > 0"
|
||||||
|
decoration-warning="X10_axis < 0"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="Y10_axis" string="y"/>
|
||||||
|
<field name="Y10_axis" class="o_form_label" decoration-success="Y10_axis > 0"
|
||||||
|
decoration-warning="Y10_axis < 0"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="Z10_axis" string="z"/>
|
||||||
|
<field name="Z10_axis" class="o_form_label" decoration-success="Z10_axis > 0"
|
||||||
|
decoration-warning="Z10_axis < 0"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-lg-6 o_setting_box">
|
||||||
|
<button type="object" class="oe_highlight" name="getcenter" string="计算定位"
|
||||||
|
attrs='{"invisible": ["|","|",("material_center_point","!=",False),("state","!=","progress"),("user_permissions","=",False)]}'/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<group>
|
||||||
|
<field name="material_center_point"/>
|
||||||
|
<field name='X_deviation_angle'/>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
|
<xpath expr="//page[1]" position="before">
|
||||||
|
<page string="CNC程序" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
|
||||||
|
<field name="cnc_ids" widget="one2many" string="工作程序">
|
||||||
|
<tree decoration-success="button_state" decoration-bf="button_state">
|
||||||
|
<field name="sequence_number"/>
|
||||||
|
<field name="program_name"/>
|
||||||
|
<field name="cnc_id" string="文件"/>
|
||||||
|
<field name="cutting_tool_name"/>
|
||||||
|
<field name="cutting_tool_no"/>
|
||||||
|
<field name="processing_type"/>
|
||||||
|
<field name="margin_x_y"/>
|
||||||
|
<field name="margin_z"/>
|
||||||
|
<field name="depth_of_processing_z"/>
|
||||||
|
<field name="cutting_tool_extension_length"/>
|
||||||
|
<field name="cutting_tool_handle_type"/>
|
||||||
|
<field name="estimated_processing_time"/>
|
||||||
|
<field name="remark"/>
|
||||||
|
<field name="button_state" invisible="1"/>
|
||||||
|
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
<group>
|
||||||
|
<field name="cnc_worksheet" string="工作指令" widget="pdf_viewer"/>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//page[1]" position="before">
|
||||||
|
<field name="results" invisible="1"/>
|
||||||
|
<page string="后置三元检测" attrs='{"invisible": [("routing_type","!=","后置三元质量检测")]}'>
|
||||||
|
<group>
|
||||||
|
<field name="test_results" widget="selection" attrs='{"invisible":[("results","!=",False)]}'/>
|
||||||
|
<field name="results" readonly="1" attrs='{"invisible":[("results","!=","合格")]}'/>
|
||||||
|
</group>
|
||||||
|
<div class="col-12 col-lg-6 o_setting_box">
|
||||||
|
<button type="object" class="oe_highlight" name="recreateManufacturingOrWorkerOrder"
|
||||||
|
string="检测确认"
|
||||||
|
attrs='{"invisible": ["|","|",("state","!=","progress"),("user_permissions","=",False),("results","=","合格")]}'/>
|
||||||
|
</div>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//page[1]" position="before">
|
||||||
|
<page string="解除装夹" attrs='{"invisible": [("routing_type","!=","解除装夹")]}'>
|
||||||
|
<field name="tray_id" readonly="1"/>
|
||||||
|
<div class="col-12 col-lg-6 o_setting_box">
|
||||||
|
<button type="object" class="oe_highlight" name="unbindtray" string="解除装夹"
|
||||||
|
attrs='{"invisible": ["|","|",("tray_id","=",False),("state","!=","progress"),("user_permissions","=",False)]}'/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-lg-6 o_setting_box">
|
||||||
|
<button type="action" class="oe_highlight" name="sf_manufacturing.label_sf_tray_code1"
|
||||||
|
string="打印标签"
|
||||||
|
attrs='{"invisible": ["|",("state","!=","progress"),("user_permissions","=",False)]}'/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
|
|
||||||
31
sf_manufacturing/views/tray_view.xml
Normal file
31
sf_manufacturing/views/tray_view.xml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="sf_tray_form_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">托盘条形码生成</field>
|
||||||
|
<field name="model">sf.tray</field>
|
||||||
|
<field name="inherit_id" ref="sf_base.sf_tray_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//group[@name='group1']" position="after">
|
||||||
|
<notebook>
|
||||||
|
<page string="生成条形码">
|
||||||
|
<field name='qr_image' widget="image"/>
|
||||||
|
<group>
|
||||||
|
<field name='production_id' readonly="1"
|
||||||
|
attrs='{"invisible": [("production_id","=",False)]}'/>
|
||||||
|
<field name="workorder_id"/>
|
||||||
|
</group>
|
||||||
|
<div class="col
|
||||||
|
-12 col-lg-6 o_setting_box">
|
||||||
|
<button type="object" class="oe_highlight" name="unclamp" string="解除装夹"
|
||||||
|
attrs='{"invisible": [("state","=","空闲")]}'/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</page>
|
||||||
|
|
||||||
|
</notebook>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
5856
sf_manufacturing_translation/translation_Po/zh_CN.po
Normal file
5856
sf_manufacturing_translation/translation_Po/zh_CN.po
Normal file
File diff suppressed because it is too large
Load Diff
3
sf_mrs_connect/__init__.py
Normal file
3
sf_mrs_connect/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from . import controllers
|
||||||
|
from . import models
|
||||||
|
|
||||||
25
sf_mrs_connect/__manifest__.py
Normal file
25
sf_mrs_connect/__manifest__.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
{
|
||||||
|
'name': '机企猫智能工厂 对接制造资源库 ',
|
||||||
|
'version': '1.0',
|
||||||
|
'summary': '智能工厂连接制造资源库模块',
|
||||||
|
'sequence': 1,
|
||||||
|
'description': """
|
||||||
|
在本模块,智能工厂连接制造资源库
|
||||||
|
""",
|
||||||
|
'category': 'sf',
|
||||||
|
'website': 'https://www.sf.cs.jikimo.com',
|
||||||
|
'depends': ['sf_base', 'base_setup'],
|
||||||
|
'data': [
|
||||||
|
'data/ir_cron_data.xml',
|
||||||
|
'views/res_config_settings_views.xml'
|
||||||
|
],
|
||||||
|
'demo': [
|
||||||
|
],
|
||||||
|
'qweb': [
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'application': False,
|
||||||
|
'auto_install': False,
|
||||||
|
}
|
||||||
1
sf_mrs_connect/controllers/__init__.py
Normal file
1
sf_mrs_connect/controllers/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import controllers
|
||||||
52
sf_mrs_connect/controllers/controllers.py
Normal file
52
sf_mrs_connect/controllers/controllers.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import json
|
||||||
|
import base64
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from datetime import date, timedelta
|
||||||
|
from odoo import http
|
||||||
|
from odoo.http import request
|
||||||
|
|
||||||
|
|
||||||
|
class Sf_Mrs_Connect(http.Controller):
|
||||||
|
|
||||||
|
@http.route('/api/cnc_processing/create', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
||||||
|
cors="*")
|
||||||
|
def get_cnc_processing_create(self, **kw):
|
||||||
|
"""
|
||||||
|
获取mrs下发的编程单
|
||||||
|
:param kw:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
logging.info('get_cnc_processing_create:%s' % kw)
|
||||||
|
try:
|
||||||
|
res = {'status': 1, 'message': '成功'}
|
||||||
|
datas = request.httprequest.data
|
||||||
|
ret = json.loads(datas)
|
||||||
|
ret = json.loads(ret['result'])
|
||||||
|
# 查询状态为进行中且类型为获取CNC加工程序的工单
|
||||||
|
cnc_workorder = request.env['mrp.workorder'].with_user(
|
||||||
|
request.env.ref("base.user_admin")).search([('production_id.name', '=', ret['production_order_no']),
|
||||||
|
('routing_type', '=', '获取CNC加工程序'),
|
||||||
|
('state', '=', 'progress')])
|
||||||
|
if cnc_workorder:
|
||||||
|
# 拉取所有加工面的程序文件
|
||||||
|
# i = 1
|
||||||
|
for r in ret['processing_panel']:
|
||||||
|
download_state = request.env['sf.cnc.processing'].with_user(
|
||||||
|
request.env.ref("base.user_admin")).download_file_tmp(
|
||||||
|
ret['folder_name'], r)
|
||||||
|
if download_state == False:
|
||||||
|
res['status'] = -2
|
||||||
|
res['message'] = '制造订单号为%s的CNC程序文件从FTP拉取失败' % (cnc_workorder.production_id.name)
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
|
request.env['sf.cnc.processing'].with_user(
|
||||||
|
request.env.ref("base.user_admin")).cnc_processing_create(cnc_workorder, ret)
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
|
else:
|
||||||
|
res = {'status': 0, 'message': '该制造订单暂未开始'}
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
|
except Exception as e:
|
||||||
|
res = {'status': -1, 'message': '系统解析失败'}
|
||||||
|
logging.info('get_cnc_processing_create error:%s' % e)
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
134
sf_mrs_connect/data/ir_cron_data.xml
Normal file
134
sf_mrs_connect/data/ir_cron_data.xml
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
<?xml version="1.0" encoding='UTF-8'?>
|
||||||
|
<odoo>
|
||||||
|
<record model="ir.cron" id="sf_cron1">
|
||||||
|
<field name="name">同步资源库材料</field>
|
||||||
|
<field name="model_id" ref="model_sf_production_materials"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">model.sync_production_materials()</field>
|
||||||
|
<field name="interval_number">1</field>
|
||||||
|
<field name="interval_type">days</field>
|
||||||
|
<field name="numbercall">-1</field>
|
||||||
|
<field name="doall" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.cron" id="sf_cron2">
|
||||||
|
<field name="name">同步资源库材料型号</field>
|
||||||
|
<field name="model_id" ref="model_sf_materials_model"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">model.sync_materials_model()</field>
|
||||||
|
<field name="interval_number">1</field>
|
||||||
|
<field name="interval_type">days</field>
|
||||||
|
<field name="numbercall">-1</field>
|
||||||
|
<field name="doall" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.cron" id="sf_cron3">
|
||||||
|
<field name="name">同步资源库表面工艺</field>
|
||||||
|
<field name="model_id" ref="model_sf_production_process"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">model.sync_production_process()</field>
|
||||||
|
<field name="interval_number">1</field>
|
||||||
|
<field name="interval_type">days</field>
|
||||||
|
<field name="numbercall">-1</field>
|
||||||
|
<field name="doall" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.cron" id="sf_cron4">
|
||||||
|
<field name="name">同步资源库加工工艺</field>
|
||||||
|
<field name="model_id" ref="model_sf_processing_technology"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">model.sync_processing_technology()</field>
|
||||||
|
<field name="interval_number">1</field>
|
||||||
|
<field name="interval_type">days</field>
|
||||||
|
<field name="numbercall">-1</field>
|
||||||
|
<field name="doall" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.cron" id="sf_cron5">
|
||||||
|
<field name="name">同步资源库标签</field>
|
||||||
|
<field name="model_id" ref="model_sf_machine_brand_tags"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">model.sync_machine_brand_tags()</field>
|
||||||
|
<field name="interval_number">1</field>
|
||||||
|
<field name="interval_type">days</field>
|
||||||
|
<field name="numbercall">-1</field>
|
||||||
|
<field name="doall" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.cron" id="sf_cron6">
|
||||||
|
<field name="name">同步资源库控制系统</field>
|
||||||
|
<field name="model_id" ref="model_sf_machine_control_system"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">model.sync_machine_tool_type_control_system()</field>
|
||||||
|
<field name="interval_number">1</field>
|
||||||
|
<field name="interval_type">days</field>
|
||||||
|
<field name="numbercall">-1</field>
|
||||||
|
<field name="doall" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.cron" id="sf_cron7">
|
||||||
|
<field name="name">同步资源库品牌</field>
|
||||||
|
<field name="model_id" ref="model_sf_machine_brand"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">model.sync_machine_brand()</field>
|
||||||
|
<field name="interval_number">1</field>
|
||||||
|
<field name="interval_type">days</field>
|
||||||
|
<field name="numbercall">-1</field>
|
||||||
|
<field name="doall" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- <record model="ir.cron" id="sf_cron8">-->
|
||||||
|
<!-- <field name="name">同步注册机床</field>-->
|
||||||
|
<!-- <field name="model_id" ref="model_mrs_machine_tool"/>-->
|
||||||
|
<!-- <field name="state">code</field>-->
|
||||||
|
<!-- <field name="code">model.enroll_machine_tool()</field>-->
|
||||||
|
<!-- <field name="interval_number">1</field>-->
|
||||||
|
<!-- <field name="interval_type">days</field>-->
|
||||||
|
<!-- <field name="numbercall">-1</field>-->
|
||||||
|
<!-- <field name="doall" eval="False"/>-->
|
||||||
|
<!-- </record>-->
|
||||||
|
|
||||||
|
<record model="ir.cron" id="sf_cron9">
|
||||||
|
<field name="name">同步资源库机床型号</field>
|
||||||
|
<field name="model_id" ref="model_sf_machine_tool_type"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">model.sync_machine_tool_type()</field>
|
||||||
|
<field name="interval_number">1</field>
|
||||||
|
<field name="interval_type">days</field>
|
||||||
|
<field name="numbercall">-1</field>
|
||||||
|
<field name="doall" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.cron" id="sf_cron10">
|
||||||
|
<field name="name">同步资源库刀具类别</field>
|
||||||
|
<field name="model_id" ref="model_sf_cutting_tool_category"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">model.sync_cutting_tool_category()</field>
|
||||||
|
<field name="interval_number">1</field>
|
||||||
|
<field name="interval_type">days</field>
|
||||||
|
<field name="numbercall">-1</field>
|
||||||
|
<field name="doall" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.cron" id="sf_cron11">
|
||||||
|
<field name="name">同步资源库刀具型号</field>
|
||||||
|
<field name="model_id" ref="model_sf_cutting_tool_type"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">model.sync_cutting_tool_type()</field>
|
||||||
|
<field name="interval_number">1</field>
|
||||||
|
<field name="interval_type">days</field>
|
||||||
|
<field name="numbercall">-1</field>
|
||||||
|
<field name="doall" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.cron" id="sf_cron12">
|
||||||
|
<field name="name">同步资源库工序</field>
|
||||||
|
<field name="model_id" ref="model_sf_processing_order"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">model.sync_processing_order()</field>
|
||||||
|
<field name="interval_number">1</field>
|
||||||
|
<field name="interval_type">days</field>
|
||||||
|
<field name="numbercall">-1</field>
|
||||||
|
<field name="doall" eval="False"/>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
5
sf_mrs_connect/models/__init__.py
Normal file
5
sf_mrs_connect/models/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from . import ftp_operate
|
||||||
|
from . import res_config_setting
|
||||||
|
from . import sync_common
|
||||||
|
|
||||||
|
|
||||||
47
sf_mrs_connect/models/ftp_operate.py
Normal file
47
sf_mrs_connect/models/ftp_operate.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import os
|
||||||
|
import posixpath
|
||||||
|
from odoo.modules import get_resource_path
|
||||||
|
from ftplib import FTP
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
# FTP接口类
|
||||||
|
class FtpController():
|
||||||
|
'''
|
||||||
|
这是ftp接口类,在类初始化的时候就连接了ftp服务器,能否成功连接有反馈。
|
||||||
|
类中定义了两个接口:上传接口和删除接口
|
||||||
|
'''
|
||||||
|
|
||||||
|
ftp = FTP()
|
||||||
|
|
||||||
|
def __init__(self, host, port, username, password):
|
||||||
|
try:
|
||||||
|
self.ftp.connect(host, port)
|
||||||
|
self.ftp.login(username, password)
|
||||||
|
logging.info("ftp连接成功")
|
||||||
|
except:
|
||||||
|
logging.info("ftp连接失败")
|
||||||
|
|
||||||
|
# 下载目录下的文件
|
||||||
|
def download_file_tree(self, target_dir, serverdir):
|
||||||
|
if not os.path.exists(serverdir):
|
||||||
|
os.makedirs(serverdir)
|
||||||
|
try:
|
||||||
|
logging.info("进入FTP目录 ")
|
||||||
|
self.ftp.cwd(target_dir) # 切换工作路径
|
||||||
|
logging.info('FTP目录:%s' % target_dir)
|
||||||
|
remotenames = self.ftp.nlst()
|
||||||
|
logging.info('FTP目录文件:%s' % remotenames)
|
||||||
|
for file in remotenames:
|
||||||
|
server = os.path.join(serverdir, file)
|
||||||
|
if file.find(".") != -1:
|
||||||
|
self.download_file(server, file)
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 下载指定目录下的指定文件
|
||||||
|
def download_file(self, serverfile, remotefile):
|
||||||
|
file_handler = open(serverfile, 'wb')
|
||||||
|
self.ftp.retrbinary('RETR ' + remotefile, file_handler.write)
|
||||||
|
file_handler.close()
|
||||||
83
sf_mrs_connect/models/res_config_setting.py
Normal file
83
sf_mrs_connect/models/res_config_setting.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of SmartGo. See LICENSE file for full copyright and licensing details.
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from odoo import api, fields, models
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ResConfigSettings(models.TransientModel):
|
||||||
|
_inherit = 'res.config.settings'
|
||||||
|
|
||||||
|
token = fields.Char(string='TOKEN', default='b811ac06-3f00-11ed-9aed-0242ac110003')
|
||||||
|
sf_secret_key = fields.Char(string='密钥', default='wBmxej38OkErKhD6')
|
||||||
|
sf_url = fields.Char(string='访问地址', default='https://sf.cs.jikimo.com')
|
||||||
|
ftp_host = fields.Char(string='FTP的ip')
|
||||||
|
ftp_port = fields.Char(string='FTP端口')
|
||||||
|
ftp_user = fields.Char(string='FTP用户')
|
||||||
|
ftp_password = fields.Char(string='FTP密码')
|
||||||
|
|
||||||
|
def sf_all_sync(self):
|
||||||
|
self.env['sf.production.materials'].sync_all_production_materials()
|
||||||
|
_logger.info("同步资源库材料")
|
||||||
|
self.env['sf.materials.model'].sync_all_materials_model()
|
||||||
|
_logger.info("同步资源库材料型号")
|
||||||
|
self.env['sf.production.process'].sync_all_production_process()
|
||||||
|
_logger.info("同步资源库表面工艺")
|
||||||
|
self.env['sf.processing.technology'].sync_all_processing_technology()
|
||||||
|
_logger.info("同步资源库加工工艺")
|
||||||
|
self.env['sf.machine.brand.tags'].sync_all_machine_brand_tags()
|
||||||
|
_logger.info("同步资源库品牌类别")
|
||||||
|
self.env['sf.machine.brand'].sync_all_machine_brand()
|
||||||
|
_logger.info("同步资源库品牌")
|
||||||
|
self.env['sf.machine.control_system'].sync_all_machine_tool_type_control_system()
|
||||||
|
_logger.info("同步资源库控制系统")
|
||||||
|
# self.env['sf.machine_tool'].sync_all_machine_tool()
|
||||||
|
# _logger.info("同步机床行业编码")
|
||||||
|
self.env['sf.machine_tool.type'].sync_all_machine_tool_type()
|
||||||
|
_logger.info("同步资源库机床型号")
|
||||||
|
self.env['sf.cutting_tool.category'].sync_all_cutting_tool_category()
|
||||||
|
_logger.info("同步资源库刀具类别")
|
||||||
|
self.env['sf.cutting_tool.type'].sync_all_cutting_tool_type()
|
||||||
|
_logger.info("同步资源库刀具")
|
||||||
|
# self.env['sf.processing.order'].sync_all_processing_order()
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def get_values(self):
|
||||||
|
"""
|
||||||
|
重载获取参数的方法,参数都存在系统参数中
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
values = super(ResConfigSettings, self).get_values()
|
||||||
|
config = self.env['ir.config_parameter'].sudo()
|
||||||
|
token = config.get_param('token', default='')
|
||||||
|
sf_secret_key = config.get_param('sf_secret_key', default='')
|
||||||
|
sf_url = config.get_param('sf_url', default='')
|
||||||
|
ftp_host = config.get_param('ftp_host', default='')
|
||||||
|
ftp_port = config.get_param('ftp_port', default='')
|
||||||
|
ftp_user = config.get_param('ftp_user', default='')
|
||||||
|
ftp_password = config.get_param('ftp_password', default='')
|
||||||
|
|
||||||
|
values.update(
|
||||||
|
token=token,
|
||||||
|
sf_secret_key=sf_secret_key,
|
||||||
|
sf_url=sf_url,
|
||||||
|
ftp_host=ftp_host,
|
||||||
|
ftp_port=ftp_port,
|
||||||
|
ftp_user=ftp_user,
|
||||||
|
ftp_password=ftp_password
|
||||||
|
)
|
||||||
|
return values
|
||||||
|
|
||||||
|
def set_values(self):
|
||||||
|
super(ResConfigSettings, self).set_values()
|
||||||
|
ir_config = self.env['ir.config_parameter'].sudo()
|
||||||
|
ir_config.set_param("token", self.token or "")
|
||||||
|
ir_config.set_param("sf_secret_key", self.sf_secret_key or "")
|
||||||
|
ir_config.set_param("sf_url", self.sf_url or "")
|
||||||
|
ir_config.set_param("ftp_host", self.ftp_host or "")
|
||||||
|
ir_config.set_param("ftp_port", self.ftp_port or "")
|
||||||
|
ir_config.set_param("ftp_user", self.ftp_user or "")
|
||||||
|
ir_config.set_param("ftp_password", self.ftp_password or "")
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user