Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop
# Conflicts: # sf_base/models/sf_base.py # sf_manufacturing_orders/__manifest__.py # sf_route_workcenter/models/workcenter.py
This commit is contained in:
268
sf_base/models/sf_base.py
Normal file
268
sf_base/models/sf_base.py
Normal file
@@ -0,0 +1,268 @@
|
||||
# -*- 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 = 'mrs.machine.brand.tags'
|
||||
_description = '标签'
|
||||
name = fields.Char('名称', size=50)
|
||||
color = fields.Integer('颜色', default=0)
|
||||
|
||||
|
||||
class MachineControlSystem(models.Model):
|
||||
_name = 'mrs.machine.control_system'
|
||||
_description = '控制系统'
|
||||
|
||||
code = fields.Char('编码', size=10)
|
||||
name = fields.Char('名称', size=10)
|
||||
brand_id = fields.Many2one('mrs.machine.brand', '品牌')
|
||||
active = fields.Boolean('有效', default=True)
|
||||
|
||||
|
||||
# 品牌标签
|
||||
class MachineBrand(models.Model):
|
||||
_name = 'mrs.machine.brand'
|
||||
_description = '品牌'
|
||||
|
||||
name = fields.Char('名称')
|
||||
tag_ids = fields.Many2many('mrs.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 = 'mrs.machine_tool'
|
||||
_description = '机床'
|
||||
MTcode = fields.Char("编码")
|
||||
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('备注')
|
||||
precision = fields.Float('加工精度')
|
||||
control_system_id = fields.Many2one('mrs.machine.control_system',
|
||||
string="控制系统")
|
||||
# 多个机床型号对应一个机床
|
||||
type_id = fields.Many2one('mrs.machine_tool.type', '型号')
|
||||
brand_id = fields.Many2one('mrs.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('注册日期')
|
||||
|
||||
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']
|
||||
mrs_secret_key = sf_sync_config['mrs_secret_key']
|
||||
headers = Common.get_headers(self, token, mrs_secret_key)
|
||||
strurl = sf_sync_config['mrs_url'] + self.crea_url
|
||||
objs_all = request.env['mrs.machine_tool'].sudo().search([])
|
||||
machine_tool_list = []
|
||||
if objs_all:
|
||||
for item in objs_all:
|
||||
val = {
|
||||
'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['mrs.machine.control_system'].search(
|
||||
[('id', '=', item.control_system_id.id)]).code,
|
||||
'type_id': self.env['mrs.machine_tool.type'].search([('id', '=', item.type_id.id)]).code,
|
||||
'brand_id': self.env['mrs.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}, headers=headers)
|
||||
print(r)
|
||||
if r == 200:
|
||||
raise ValidationError("机床注册成功")
|
||||
else:
|
||||
raise ValidationError("没有注册机床信息")
|
||||
|
||||
|
||||
class MachineToolType(models.Model):
|
||||
_name = 'mrs.machine_tool.type'
|
||||
_description = '机床型号'
|
||||
# _order = 'priority desc, code, name, id'
|
||||
|
||||
name = fields.Char('名称')
|
||||
brand_id = fields.Many2one('mrs.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('mrs.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('mrs.machine.control_system',
|
||||
string="控制系统")
|
||||
active = fields.Boolean('有效', default=True)
|
||||
code = fields.Char('编码')
|
||||
|
||||
|
||||
# 刀具
|
||||
class CuttingTool(models.Model):
|
||||
_name = 'mrs.cutting_tool.category'
|
||||
_description = '刀具类别'
|
||||
|
||||
code = fields.Char('编码')
|
||||
name = fields.Char('名称')
|
||||
|
||||
remark = fields.Text('备注')
|
||||
active = fields.Boolean('有效', default=True)
|
||||
|
||||
|
||||
class CuttingToolType(models.Model):
|
||||
_name = 'mrs.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('mrs.cutting_tool.category', string='刀具类别')
|
||||
brand_id = fields.Many2one('mrs.machine.brand', string='品牌')
|
||||
remark = fields.Text('备注')
|
||||
active = fields.Boolean('有效', default=True)
|
||||
|
||||
|
||||
class CNCprocessing(models.Model):
|
||||
_name = 'cnc.processing'
|
||||
_description = "CNC加工"
|
||||
|
||||
cnc_id = fields.Many2one('ir.attachment')
|
||||
FNo = fields.Char(string="序号")
|
||||
FPGName = fields.Char(string="程序名")
|
||||
FKnifeName = fields.Char(string="刀具名称")
|
||||
FDNo = fields.Char(string="刀号")
|
||||
FWorkType = fields.Char(string="加工类型")
|
||||
FXY = fields.Char(string="余量_X/Y")
|
||||
FZ = fields.Char(string="余量_Z")
|
||||
FJGSD = fields.Char(string="加工深度(Z)")
|
||||
FSCCD = fields.Char(string="刀具伸出长度")
|
||||
FDJSpec = fields.Char(string="刀柄型号")
|
||||
FJGDate = fields.Char(string="预计加工时间")
|
||||
FComment = fields.Char(string="备注")
|
||||
@@ -296,9 +296,8 @@
|
||||
<search string="托盘">
|
||||
<field name="name" string="名称" filter_domain="[('name','ilike',self)]"/>
|
||||
<field name="code" string="编码" filter_domain="[('code','ilike',self)]"/>
|
||||
<!-- <field name="state" string="状态" filter_domain="[('state','ilike',self)]"/>-->
|
||||
<group string="分组">
|
||||
<filter name="state" string="状态" domain="[]" context="{'group_by': 'state'}"/>
|
||||
<filter name="state" string="状态" domain="[]" context="{'group_by': 'state'}"/>
|
||||
</group>
|
||||
</search>
|
||||
|
||||
@@ -338,5 +337,6 @@
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
26
sf_manufacturing_orders/__manifest__.py
Normal file
26
sf_manufacturing_orders/__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': '',
|
||||
'website': 'https://www.sf.jikimo.com',
|
||||
'depends': ['mrp'],
|
||||
'data': [
|
||||
'views/sf_production.xml',
|
||||
|
||||
|
||||
],
|
||||
'demo': [
|
||||
],
|
||||
'qweb': [
|
||||
],
|
||||
'installable': True,
|
||||
'application': False,
|
||||
'auto_install': False,
|
||||
}
|
||||
@@ -203,115 +203,59 @@ class StockRule(models.Model):
|
||||
|
||||
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)
|
||||
#
|
||||
# product = self.env['product.template'].search(
|
||||
# ["&", ("id", '=', procurement.product_id.id), ('single_manufacturing', '!=', False)])
|
||||
# if not product:
|
||||
# productions_values_by_company[procurement.company_id.id].append(
|
||||
# rule._prepare_mo_vals1(*procurement, bom))
|
||||
# else:
|
||||
# orders_vals = rule._prepare_mo_vals(*procurement, bom)
|
||||
#
|
||||
# if isinstance(orders_vals, list):
|
||||
# for vals in orders_vals:
|
||||
# productions_values_by_company[procurement.company_id.id].append(vals)
|
||||
# else:
|
||||
# productions_values_by_company[procurement.company_id.id].append(orders_vals)
|
||||
#
|
||||
# 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)
|
||||
# a = self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
|
||||
# b = self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
|
||||
# c = 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)
|
||||
# return True
|
||||
#
|
||||
# def _prepare_mo_vals1(self, product_id, product_qty, product_uom, location_id, name, origin, company_id, values,
|
||||
# bom):
|
||||
# date_planned = self._get_date_planned(product_id, company_id, values)
|
||||
# date_deadline = values.get('date_deadline') or date_planned + relativedelta(
|
||||
# days=company_id.manufacturing_lead) + relativedelta(days=product_id.produce_delay)
|
||||
# mo_values = {
|
||||
# 'origin': origin,
|
||||
# 'product_id': product_id.id,
|
||||
# 'product_description_variants': values.get('product_description_variants'),
|
||||
# 'product_qty': product_qty,
|
||||
# 'product_uom_id': product_uom.id,
|
||||
# 'location_src_id': self.location_src_id.id or self.picking_type_id.default_location_src_id.id or location_id.id,
|
||||
# 'location_dest_id': location_id.id,
|
||||
# 'bom_id': bom.id,
|
||||
# 'date_deadline': date_deadline,
|
||||
# 'date_planned_start': date_planned,
|
||||
# 'date_planned_finished': fields.Datetime.from_string(values['date_planned']),
|
||||
# 'procurement_group_id': False,
|
||||
# 'propagate_cancel': self.propagate_cancel,
|
||||
# 'orderpoint_id': values.get('orderpoint_id', False) and values.get('orderpoint_id').id,
|
||||
# 'picking_type_id': self.picking_type_id.id or values['warehouse_id'].manu_type_id.id,
|
||||
# 'company_id': company_id.id,
|
||||
# 'move_dest_ids': values.get('move_dest_ids') and [(4, x.id) for x in values['move_dest_ids']] or False,
|
||||
# 'user_id': False,
|
||||
# }
|
||||
# # Use the procurement group created in _run_pull mrp override
|
||||
# # Preserve the origin from the original stock move, if available
|
||||
# if location_id.warehouse_id.manufacture_steps == 'pbm_sam' and values.get('move_dest_ids') and values.get(
|
||||
# 'group_id') and values['move_dest_ids'][0].origin != values['group_id'].name:
|
||||
# origin = values['move_dest_ids'][0].origin
|
||||
# mo_values.update({
|
||||
# 'name': values['group_id'].name,
|
||||
# 'procurement_group_id': values['group_id'].id,
|
||||
# 'origin': origin,
|
||||
# })
|
||||
# return mo_values
|
||||
#
|
||||
# def _prepare_mo_vals(self, product_id, product_qty, product_uom, location_id, name, origin, company_id, values,
|
||||
# bom):
|
||||
#
|
||||
# mo_lists = []
|
||||
# for num in range(1, int(product_qty) + 1):
|
||||
# vals = super(StockRule, self)._prepare_mo_vals(product_id, 1.0, product_uom, location_id, name, origin,
|
||||
# company_id, values,
|
||||
# bom)
|
||||
# vals.update({
|
||||
# # 'name': values['group_id'].name,
|
||||
# 'procurement_group_id': values['group_id'].id,
|
||||
# # 'origin': origin,
|
||||
# })
|
||||
# mo_lists.append(vals)
|
||||
#
|
||||
# return mo_lists
|
||||
@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.lot_producing_id._get_next_serial(production.company_id, production.product_id)
|
||||
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
|
||||
@@ -10,10 +10,13 @@
|
||||
""",
|
||||
'category': 'YZ',
|
||||
'website': 'https://www.sf.cs.jikimo.com',
|
||||
'depends': ['mrp', 'sf_base','hr_holidays'],
|
||||
'depends': ['mrp', 'sf_base', 'hr_holidays', 'maintenance'],
|
||||
'data': [
|
||||
'views/sf_tray_view.xml',
|
||||
'views/sf_workorder.xml',
|
||||
'views/maintenance_views.xml',
|
||||
'views/mrp_views.xml',
|
||||
'views/mrp_workcenter_views.xml',
|
||||
'report/sf_tray_report.xml'
|
||||
],
|
||||
'demo': [
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
# -*-coding:utf-8-*-
|
||||
from . import workcenter
|
||||
from . import workcenter
|
||||
from . import mrp_maintenance
|
||||
|
||||
184
sf_route_workcenter/models/mrp_maintenance.py
Normal file
184
sf_route_workcenter/models/mrp_maintenance.py
Normal file
@@ -0,0 +1,184 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from datetime import timedelta, datetime
|
||||
from collections import defaultdict
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.addons.resource.models.resource import Intervals
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class MrpWorkcenter(models.Model):
|
||||
_inherit = "mrp.workcenter"
|
||||
|
||||
equipment_ids = fields.One2many(
|
||||
'maintenance.equipment', 'workcenter_id', string="Maintenance Equipment",
|
||||
check_company=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_route_workcenter.mrp_workorder_action_tablet")
|
||||
return action
|
||||
else:
|
||||
return super(MrpWorkcenter, self).action_work_order()
|
||||
|
||||
def _get_unavailability_intervals(self, start_datetime, end_datetime):
|
||||
res = super(MrpWorkcenter, 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 + INTERVAL '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 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)]")
|
||||
|
||||
|
||||
class MrpProduction(models.Model):
|
||||
_inherit = "mrp.production"
|
||||
|
||||
maintenance_count = fields.Integer(compute='_compute_maintenance_count', string="Number of maintenance requests")
|
||||
request_ids = fields.One2many('maintenance.request', 'production_id')
|
||||
|
||||
@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
|
||||
|
||||
|
||||
class MrpProductionWorkcenterLine(models.Model):
|
||||
_inherit = "mrp.workorder"
|
||||
|
||||
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)]
|
||||
}
|
||||
339
sf_route_workcenter/models/workcenter.py
Normal file
339
sf_route_workcenter/models/workcenter.py
Normal file
@@ -0,0 +1,339 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of SmartGo. See LICENSE file for full copyright and licensing details.
|
||||
import base64
|
||||
import logging
|
||||
import math
|
||||
|
||||
from io import BytesIO
|
||||
from odoo import api, fields, models, SUPERUSER_ID
|
||||
from pystrich.code128 import Code128Encoder
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CNCprocessing(models.Model):
|
||||
_inherit = 'cnc.processing'
|
||||
_description = "CNC加工"
|
||||
|
||||
workorder_id = fields.Many2one('mrp.workorder', string="工单")
|
||||
|
||||
|
||||
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:
|
||||
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
|
||||
|
||||
|
||||
'''
|
||||
工单绑定托盘信息
|
||||
'''
|
||||
|
||||
|
||||
class MrpWorkOrder(models.Model):
|
||||
_inherit = 'mrp.workorder'
|
||||
_description = '工单'
|
||||
|
||||
tray_ids = fields.One2many('sf.tray', 'workorder_id', string='托盘')
|
||||
# def get_tray_info(self):
|
||||
# @api.onchange('X_axis', 'Y_axis', 'Z_axis')
|
||||
# def get_center_point(self):
|
||||
# return 'X:%s,Y:%s,Z:%s' % (self.X_axis, self.Y_axis, self.Z_axis)
|
||||
# 加工面
|
||||
# surface = fields.Selection([("前面", "前面"), ("后面", "后面"), ("左面", "左面"), ("右面", "右面"),
|
||||
# ("上面", "上面")], string="加工面1")
|
||||
|
||||
material_center_point = fields.Char(string='配料中心点')
|
||||
X1_axis = fields.Float(string='Lx1', default=0)
|
||||
Y1_axis = fields.Float(string='Ly1', default=0)
|
||||
Z1_axis = fields.Float(string='Lz1', default=0)
|
||||
X2_axis = fields.Float(string='Lx2', default=0)
|
||||
Y2_axis = fields.Float(string='Ly2', default=0)
|
||||
Z2_axis = fields.Float(string='Lz2', default=0)
|
||||
X3_axis = fields.Float(string='Fx3', default=0)
|
||||
Y3_axis = fields.Float(string='Fy3', default=0)
|
||||
Z3_axis = fields.Float(string='Fz3', default=0)
|
||||
X4_axis = fields.Float(string='Fx4', default=0)
|
||||
Y4_axis = fields.Float(string='Fy4', default=0)
|
||||
Z4_axis = fields.Float(string='Fz4', default=0)
|
||||
X5_axis = fields.Float(string='Rx5', default=0)
|
||||
Y5_axis = fields.Float(string='Ry5', default=0)
|
||||
Z5_axis = fields.Float(string='Rz5', default=0)
|
||||
X6_axis = fields.Float(string='Rx6', default=0)
|
||||
Y6_axis = fields.Float(string='Ry6', default=0)
|
||||
Z6_axis = fields.Float(string='Rz6', default=0)
|
||||
X7_axis = fields.Float(string='Bx7', default=0)
|
||||
Y7_axis = fields.Float(string='By7', default=0)
|
||||
Z7_axis = fields.Float(string='Bz7', default=0)
|
||||
X8_axis = fields.Float(string='Bx8', default=0)
|
||||
Y8_axis = fields.Float(string='By8', default=0)
|
||||
Z8_axis = fields.Float(string='Bz8', default=0)
|
||||
X9_axis = fields.Float(string='Uz9', default=0)
|
||||
Y9_axis = fields.Float(string='Uz9', default=0)
|
||||
Z9_axis = fields.Float(string='Uz9', default=0)
|
||||
X10_axis = fields.Float(string='Uz10', default=0)
|
||||
Y10_axis = fields.Float(string='Uz10', default=0)
|
||||
Z10_axis = fields.Float(string='Uz10', default=0)
|
||||
|
||||
# 计算配料中心点和与x轴倾斜度方法
|
||||
def getcenter(self):
|
||||
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 * y7) - (x5 - x6) * (x8 * y7 - x7 * y8)) / (
|
||||
(x7 - x8) * (y5 - y6) - (x5 - x6) * (y7 - y8));
|
||||
y1 = ((y7 - y8) * (y6 * x5 - y5 * x7) - (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((x7 - x8), (y7 - y8))
|
||||
jdz = jd * 180 / math.pi
|
||||
print("(%s,%s)" % (x, y))
|
||||
self.material_center_point = ("(%s,%s,%s)" % (x, y, z))
|
||||
self.X_deviation_angle = jdz
|
||||
|
||||
X_deviation_angle = fields.Integer(string="X轴偏差度", default=0)
|
||||
|
||||
test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")], string="检测结果")
|
||||
|
||||
cnc_ids = fields.One2many("cnc.processing", 'workorder_id', string="CNC加工")
|
||||
|
||||
tray_code = fields.Char(string="托盘")
|
||||
|
||||
# 扫码绑定托盘方法
|
||||
def gettray(self):
|
||||
if self.tray_code != False:
|
||||
values = self.env['sf.tray'].search([("code", "=", self.tray_code)])
|
||||
if values:
|
||||
if values.state == "占用":
|
||||
raise ValidationError('该托盘已占用')
|
||||
if values.state == "报损":
|
||||
raise ValidationError('该托盘已损坏')
|
||||
else:
|
||||
values.update({
|
||||
'workorder_id': self,
|
||||
'production_id': self.production_id,
|
||||
'state': '占用',
|
||||
})
|
||||
else:
|
||||
raise ValidationError('该托盘编码已失效')
|
||||
else:return ""
|
||||
|
||||
|
||||
# 解除托盘绑定
|
||||
def unbindtray(self):
|
||||
tray = self.env['sf.tray'].search([("production_id", "=", self.production_id.id)])
|
||||
if tray:
|
||||
tray.unclamp()
|
||||
|
||||
return ""
|
||||
|
||||
def recreateManufacturing(self):
|
||||
"""
|
||||
重新生成制造订单
|
||||
"""
|
||||
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)
|
||||
|
||||
# print(productions)
|
||||
return ""
|
||||
|
||||
def recreateWorkerOrder(self):
|
||||
"""
|
||||
返工重新生成工单
|
||||
"""
|
||||
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_workorder1(self.processing_panel)
|
||||
return ""
|
||||
|
||||
def fetchCNC(self):
|
||||
return ""
|
||||
|
||||
|
||||
'''
|
||||
制造订单绑定托盘信息
|
||||
'''
|
||||
|
||||
|
||||
class MrpProduction(models.Model):
|
||||
_inherit = 'mrp.production'
|
||||
_description = "制造订单"
|
||||
tray_ids = fields.One2many('sf.tray', 'production_id', string="托盘")
|
||||
|
||||
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 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
|
||||
processing_panel_len = len(k)
|
||||
for k in (production.product_id.model_processing_panel.split(',')):
|
||||
routingworkcenter = self.env['sf.model.type.routing.sort'].search(
|
||||
[('model_type_id', '=', production.product_id.model_type_id.id)],
|
||||
|
||||
order='sequence asc'
|
||||
)
|
||||
i += 1
|
||||
for route in routingworkcenter:
|
||||
|
||||
if route.routing_type == 'CNC加工':
|
||||
workorders_values.append(
|
||||
self.env['mrp.workorder'].json_workorder_str(k, production, route))
|
||||
if route.routing_type == '后置三元质量检测':
|
||||
workorders_values.append(
|
||||
self.env['mrp.workorder'].json_workorder_str(k, production, route))
|
||||
|
||||
production.workorder_ids = workorders_values
|
||||
for workorder in production.workorder_ids:
|
||||
workorder.duration_expected = workorder._get_duration_expected()
|
||||
|
||||
|
||||
class Attachment(models.Model):
|
||||
_inherit = 'ir.attachment'
|
||||
|
||||
cnc_model = fields.Binary('cnc文件', attachment=False)
|
||||
model_name = fields.Char('模型名称')
|
||||
@@ -1,6 +1,6 @@
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- 打印尺寸-->
|
||||
<record id="sf_tray1" model="report.paperformat">
|
||||
<field name="name">Dymo Label Sheet</field>
|
||||
<field name="default" eval="True"/>
|
||||
@@ -16,6 +16,7 @@
|
||||
<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>
|
||||
@@ -27,7 +28,7 @@
|
||||
<field name="paperformat_id" ref="sf_route_workcenter.sf_tray1"/>
|
||||
|
||||
</record>
|
||||
|
||||
<!-- 打印模板-->
|
||||
<template id="sf_tray_template">
|
||||
<t t-call="web.html_container">
|
||||
<t t-call="web.external_layout">
|
||||
|
||||
95
sf_route_workcenter/views/maintenance_views.xml
Normal file
95
sf_route_workcenter/views/maintenance_views.xml
Normal file
@@ -0,0 +1,95 @@
|
||||
<?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>
|
||||
60
sf_route_workcenter/views/mrp_views.xml
Normal file
60
sf_route_workcenter/views/mrp_views.xml
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- MRP.WORKCENTER -->
|
||||
<record id="mrp_workcenter_view_form_inherit_maintenance" model="ir.ui.view">
|
||||
<field name="name">mrp.workcenter.form.inherit.maintenance</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>
|
||||
</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="Maintenance Request"/>
|
||||
</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>
|
||||
37
sf_route_workcenter/views/mrp_workcenter_views.xml
Normal file
37
sf_route_workcenter/views/mrp_workcenter_views.xml
Normal file
@@ -0,0 +1,37 @@
|
||||
<odoo>
|
||||
<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">
|
||||
No work orders to do!
|
||||
</p><p>
|
||||
Work orders are operations to do as part of a manufacturing order.
|
||||
Operations are defined in the bill of materials or added in the manufacturing order directly.
|
||||
</p><p>
|
||||
Use the table work center control panel to register operations in the shop floor directly.
|
||||
The tablet provides worksheets for your workers and allow them to scrap products, track time,
|
||||
launch a maintenance request, perform quality tests, etc.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_mrp_dashboard"
|
||||
name="工作中心概述"
|
||||
action="mrp.mrp_workcenter_kanban_action"
|
||||
groups="mrp.group_mrp_routings"
|
||||
parent="mrp.menu_mrp_root"
|
||||
sequence="5"/>
|
||||
|
||||
</odoo>
|
||||
@@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="sf_tray_form_inherit" model="ir.ui.view">
|
||||
|
||||
@@ -1,6 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record model="ir.actions.act_window" id="mrp_workorder_action_tablet">
|
||||
<field name="name">Work Orders</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">mrp.workorder</field>
|
||||
<field name="view_mode">kanban,tree,form</field>
|
||||
<field name="view_ids" eval="[(5, 0, 0),
|
||||
(0, 0, {'view_mode': 'kanban', 'view_id': ref('mrp.workcenter_line_kanban')}),
|
||||
(0, 0, {'view_mode': 'tree', 'view_id': ref('mrp.mrp_production_workorder_tree_editable_view')}) ]"/>
|
||||
<field name="target">fullscreen</field>
|
||||
<field name="domain">[('state', 'not in', ['done', 'cancel'])]</field>
|
||||
<field name="context">{'search_default_workcenter_id': active_id}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_workorder">
|
||||
No work orders to do!
|
||||
</p><p>
|
||||
Work orders are operations to do as part of a manufacturing order.
|
||||
Operations are defined in the bill of materials or added in the manufacturing order directly.
|
||||
</p><p>
|
||||
Use the table work center control panel to register operations in the shop floor directly.
|
||||
The tablet provides worksheets for your workers and allow them to scrap products, track time,
|
||||
launch a maintenance request, perform quality tests, etc.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="sf_install_the_tray_workorder_form_view" model="ir.ui.view">
|
||||
<field name="name">装夹工序工单</field>
|
||||
<field name="model">mrp.workorder</field>
|
||||
@@ -22,8 +48,9 @@
|
||||
|
||||
<field name="routing_type" invisible="1"/>
|
||||
<field name="processing_panel" readonly = "1" />
|
||||
<field name="tray_code"/>
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<button type="object" class="oe_highlight" name="gettray" string="扫描托盘"
|
||||
<button type="object" class="oe_highlight" name="gettray" string="绑定托盘"
|
||||
attrs='{"invisible": [("production_id","=",False)]}'
|
||||
/>
|
||||
</div>
|
||||
@@ -46,97 +73,97 @@
|
||||
<div>左面:</div>
|
||||
<div></div>
|
||||
<div class="o_address_city">
|
||||
<label for="X1_axis" string="Lx1"/>
|
||||
<label for="X1_axis" string="x1"/>
|
||||
<field name='X1_axis' class="o_address_city"/>
|
||||
<label for="Y1_axis" string="Ly1"/>
|
||||
<label for="Y1_axis" string="y1"/>
|
||||
<field name='Y1_axis' class="o_address_city"/>
|
||||
<label for="Z1_axis" string="Lz1"/>
|
||||
<label for="Z1_axis" string="z1"/>
|
||||
<field name='Z1_axis' class="o_address_city"/>
|
||||
</div>
|
||||
<div class="o_address_city">
|
||||
<label for="X2_axis" string="Lx2"/>
|
||||
<label for="X2_axis" string="x2"/>
|
||||
<field name='X2_axis' class="o_address_city"/>
|
||||
<label for="Y2_axis" string="Ly2"/>
|
||||
<label for="Y2_axis" string="y2"/>
|
||||
<field name='Y2_axis' class="o_address_city"/>
|
||||
<label for="Z2_axis" string="Lz2"/>
|
||||
<label for="Z2_axis" string="z2"/>
|
||||
<field name='Z2_axis' class="o_address_city"/>
|
||||
</div>
|
||||
<div>前面:</div>
|
||||
<div></div>
|
||||
<div class="o_address_city">
|
||||
<label for="X3_axis" string="Fx1"/>
|
||||
<label for="X3_axis" string="x1"/>
|
||||
<field name='X3_axis' class="o_address_city"/>
|
||||
<label for="Y3_axis" string="Fy1"/>
|
||||
<label for="Y3_axis" string="y1"/>
|
||||
<field name='Y3_axis' class="o_address_city"/>
|
||||
<label for="Z3_axis" string="Fz1"/>
|
||||
<label for="Z3_axis" string="z1"/>
|
||||
<field name='Z3_axis' class="o_address_city"/>
|
||||
</div>
|
||||
<div class="o_address_city">
|
||||
<label for="X4_axis" string="Fx2"/>
|
||||
<label for="X4_axis" string="x2"/>
|
||||
<field name='X4_axis' class="o_address_city"/>
|
||||
<label for="Y4_axis" string="Fy2"/>
|
||||
<label for="Y4_axis" string="y2"/>
|
||||
<field name='Y4_axis' class="o_address_city"/>
|
||||
<label for="Z4_axis" string="Fz2"/>
|
||||
<label for="Z4_axis" string="z2"/>
|
||||
<field name='Z4_axis' class="o_address_city"/>
|
||||
</div>
|
||||
<div>右面:</div>
|
||||
<div></div>
|
||||
<div class="o_address_city">
|
||||
<label for="X5_axis" string="Rx1"/>
|
||||
<label for="X5_axis" string="x1"/>
|
||||
<field name='X5_axis' class="o_address_city"/>
|
||||
<label for="Y5_axis" string="Ry1"/>
|
||||
<label for="Y5_axis" string="y1"/>
|
||||
<field name='Y5_axis' class="o_address_city"/>
|
||||
<label for="Z5_axis" string="Rz1"/>
|
||||
<label for="Z5_axis" string="z1"/>
|
||||
<field name='Z5_axis' class="o_address_city"/>
|
||||
</div>
|
||||
<div class="o_address_city">
|
||||
<label for="X6_axis" string="Rx2"/>
|
||||
<label for="X6_axis" string="x2"/>
|
||||
<field name='X6_axis' class="o_address_city"/>
|
||||
<label for="Y6_axis" string="Ry2"/>
|
||||
<label for="Y6_axis" string="y2"/>
|
||||
<field name='Y6_axis' class="o_address_city"/>
|
||||
<label for="Z6_axis" string="Rz2"/>
|
||||
<label for="Z6_axis" string="z2"/>
|
||||
<field name='Z6_axis' class="o_address_city"/>
|
||||
</div>
|
||||
<div>下面:</div>
|
||||
<div></div>
|
||||
<div class="o_address_city">
|
||||
<label for="X7_axis" string="Bx1"/>
|
||||
<label for="X7_axis" string="x1"/>
|
||||
<field name='X7_axis' class="o_address_city"/>
|
||||
<label for="Y7_axis" string="By1"/>
|
||||
<label for="Y7_axis" string="y1"/>
|
||||
<field name='Y7_axis' class="o_address_city"/>
|
||||
<label for="Z7_axis" string="Bz1"/>
|
||||
<label for="Z7_axis" string="z1"/>
|
||||
<field name='Z7_axis' class="o_address_city"/>
|
||||
</div>
|
||||
<div class="o_address_city">
|
||||
<label for="X8_axis" string="Bx2"/>
|
||||
<label for="X8_axis" string="x2"/>
|
||||
<field name='X8_axis' class="o_address_city"/>
|
||||
<label for="Y8_axis" string="By2"/>
|
||||
<label for="Y8_axis" string="y2"/>
|
||||
<field name='Y8_axis' class="o_address_city"/>
|
||||
<label for="Z8_axis" string="Bz2"/>
|
||||
<label for="Z8_axis" string="z2"/>
|
||||
<field name='Z8_axis' class="o_address_city"/>
|
||||
</div>
|
||||
<div>上面:</div>
|
||||
<div></div>
|
||||
<div class="o_address_city">
|
||||
<label for="X9_axis" string="Ux1"/>
|
||||
<label for="X9_axis" string="x1"/>
|
||||
<field name='X9_axis' class="o_address_city"/>
|
||||
<label for="Y9_axis" string="Uy1"/>
|
||||
<label for="Y9_axis" string="y1"/>
|
||||
<field name='Y9_axis' class="o_address_city"/>
|
||||
<label for="Z9_axis" string="Uz1"/>
|
||||
<label for="Z9_axis" string="z1"/>
|
||||
<field name='Z9_axis' class="o_address_city"/>
|
||||
</div>
|
||||
<div class="o_address_city">
|
||||
<label for="X10_axis" string="Ux2"/>
|
||||
<label for="X10_axis" string="x2"/>
|
||||
<field name='X10_axis' class="o_address_city"/>
|
||||
<label for="Y10_axis" string="Uy2"/>
|
||||
<label for="Y10_axis" string="y2"/>
|
||||
<field name='Y10_axis' class="o_address_city"/>
|
||||
<label for="Z10_axis" string="Uz2"/>
|
||||
<label for="Z10_axis" string="z2"/>
|
||||
<field name='Z10_axis' class="o_address_city"/>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<button type="object" class="oe_highlight" name="getcenter" string="中心定位"
|
||||
<button type="object" class="oe_highlight" name="getcenter" string="计算定位"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -153,11 +180,12 @@
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//page[last()]" position="after">
|
||||
<page string="CNC加工" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
|
||||
<group>
|
||||
<page string="CNC程序" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
|
||||
|
||||
|
||||
<field name="cnc_ids" widget="one2many">
|
||||
<tree>
|
||||
<field name="cnc_id"/>
|
||||
<field name="FNo" />
|
||||
<field name="FPGName" />
|
||||
<field name="FKnifeName" />
|
||||
@@ -174,8 +202,6 @@
|
||||
|
||||
</field>
|
||||
|
||||
|
||||
</group>
|
||||
</page>
|
||||
|
||||
</xpath>
|
||||
|
||||
Reference in New Issue
Block a user