Merge branch refs/heads/develop into refs/heads/feature/修改机床参数bug

This commit is contained in:
龚启豪
2023-12-06 11:35:01 +08:00
51 changed files with 1283 additions and 668 deletions

View File

@@ -31,7 +31,7 @@
'jikimo_frontend/static/src/js/custom_form_status_indicator.js', 'jikimo_frontend/static/src/js/custom_form_status_indicator.js',
'jikimo_frontend/static/src/scss/rowno_in_tree.scss', 'jikimo_frontend/static/src/scss/rowno_in_tree.scss',
# 'jikimo_frontend/static/src/views/list_nums/list_render.xml', # 'jikimo_frontend/static/src/views/list_nums/list_render.xml',
'jikimo_frontend/static/src/list/list_up_down_button.xml', # 'jikimo_frontend/static/src/list/list_up_down_button.xml',
# 'jikimo_frontend/static/src/list/custom_import.js', # 'jikimo_frontend/static/src/list/custom_import.js',
# 'jikimo_frontend/static/src/list/custom_width.js', # 'jikimo_frontend/static/src/list/custom_width.js',
'jikimo_frontend/static/src/views/list_nums/extent_purchase.xml', 'jikimo_frontend/static/src/views/list_nums/extent_purchase.xml',

View File

@@ -11,24 +11,22 @@ export class Many2OneRadioField extends RadioField {
// 你自己的代码 // 你自己的代码
} }
onImageClick(event) { onImageClick(event) {
// 放大图片逻辑 // 放大图片逻辑
// 获取图片元素 // 获取图片元素
const img = event.target; const img = event.target;
const close = img.nextSibling const close = img.nextSibling;
// 实现放大图片逻辑 // 实现放大图片逻辑
// 比如使用 CSS 放大 // 比如使用 CSS 放大
img.parentElement.classList.add('zoomed'); img.parentElement.classList.add('zoomed');
close.classList.add('img_close') close.classList.add('img_close');
} }
onCloseClick(event) { onCloseClick(event) {
const close = event.target; const close = event.target;
const img = close.previousSibling const img = close.previousSibling;
img.parentElement.classList.remove('zoomed') img.parentElement.classList.remove('zoomed');
close.classList.remove('img_close') close.classList.remove('img_close');
} }
get items() { get items() {
@@ -47,10 +45,9 @@ export class Many2OneRadioField extends RadioField {
return []; return [];
} }
} }
} }
Many2OneRadioField.template = "jikimo_frontend.Many2OneRadioField" Many2OneRadioField.template = "jikimo_frontend.Many2OneRadioField";
// MyCustomWidget.supportedTypes = ['many2many']; // MyCustomWidget.supportedTypes = ['many2many'];
registry.category("fields").add("many2one_radio", Many2OneRadioField); registry.category("fields").add("many2one_radio", Many2OneRadioField);

View File

@@ -15,23 +15,24 @@ export class MyCustomWidget extends Many2ManyCheckboxesField {
// 放大图片逻辑 // 放大图片逻辑
// 获取图片元素 // 获取图片元素
const img = event.target; const img = event.target;
const close = img.nextSibling const close = img.nextSibling;
// 实现放大图片逻辑 // 实现放大图片逻辑
// 比如使用 CSS 放大 // 比如使用 CSS 放大
img.parentElement.classList.add('zoomed'); img.parentElement.classList.add('zoomed');
close.classList.add('img_close') close.classList.add('img_close');
} }
onCloseClick(event) { onCloseClick(event) {
const close = event.target; const close = event.target;
const img = close.previousSibling const img = close.previousSibling;
img.parentElement.classList.remove('zoomed') img.parentElement.classList.remove('zoomed');
close.classList.remove('img_close') close.classList.remove('img_close');
} }
} }
MyCustomWidget.template = "jikimo_frontend.MyCustomWidget" MyCustomWidget.template = "jikimo_frontend.MyCustomWidget";
// MyCustomWidget.supportedTypes = ['many2many']; // MyCustomWidget.supportedTypes = ['many2many'];
registry.category("fields").add("custom_many2many_checkboxes", MyCustomWidget); registry.category("fields").add("custom_many2many_checkboxes", MyCustomWidget);

View File

@@ -3,7 +3,7 @@
import {patch} from '@web/core/utils/patch'; import {patch} from '@web/core/utils/patch';
// import { Dialog } from "@web/core/dialog/dialog"; // import { Dialog } from "@web/core/dialog/dialog";
import {_t} from "@web/core/l10n/translation"; import {_t} from "@web/core/l10n/translation";
import {FormStatusIndicator} from "@web/views/form/form_status_indicator/form_status_indicator" import {FormStatusIndicator} from "@web/views/form/form_status_indicator/form_status_indicator";
var Dialog = require('web.Dialog'); var Dialog = require('web.Dialog');
// var {patch} = require("web.utils") 这句话也行 // var {patch} = require("web.utils") 这句话也行

View File

@@ -1,7 +1,7 @@
/** @odoo-module */ /** @odoo-module */
import {patch} from '@web/core/utils/patch'; import {patch} from '@web/core/utils/patch';
import {ListRenderer} from "@web/views/list/list_renderer" import {ListRenderer} from "@web/views/list/list_renderer";
// var {patch} = require("web.utils") 这句话也行 // var {patch} = require("web.utils") 这句话也行
@@ -17,7 +17,7 @@ patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
const table = this.tableRef.el; const table = this.tableRef.el;
const headers = [...table.querySelectorAll("thead th:not(.o_list_actions_header)")]; const headers = [...table.querySelectorAll("thead th:not(.o_list_actions_header)")];
const column_num = headers.length const column_num = headers.length;
if (!this.columnWidths || !this.columnWidths.length) { if (!this.columnWidths || !this.columnWidths.length) {
// no column widths to restore // no column widths to restore
@@ -66,8 +66,8 @@ patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
// } // }
// 判断 this.state.columns 是否存在且长度大于零 // 判断 this.state.columns 是否存在且长度大于零
if (this.state.columns && this.state.columns.length > 0 && if (this.state.columns && this.state.columns.length > 0
this.state.columns[0].name === "sequence") { && this.state.columns[0].name === "sequence") {
widths[1] = { type: "relative", value: 0.1 }; widths[1] = { type: "relative", value: 0.1 };
} }

View File

@@ -76,21 +76,20 @@ class CuttingToolModel(models.Model):
integral_run_out_accuracy_max = fields.Char('整体式刀具端跳精度max') integral_run_out_accuracy_max = fields.Char('整体式刀具端跳精度max')
integral_run_out_accuracy_min = fields.Char('整体式刀具端跳精度min') integral_run_out_accuracy_min = fields.Char('整体式刀具端跳精度min')
fit_blade_shape_id = fields.Many2many('maintenance.equipment.image', 'fit_blade_shape_library_rel', fit_blade_shape_id = fields.Many2one('maintenance.equipment.image',
'适配刀片形状', domain=[('type', '=', '刀片形状')]) '适配刀片形状', domain=[('type', '=', '刀片形状')])
suitable_machining_method_ids = fields.Many2many('maintenance.equipment.image', suitable_machining_method_ids = fields.Many2many('maintenance.equipment.image',
'suitable_machining_method_library_rel', 'suitable_machining_method_library_rel',
'适合加工方式', domain=[('type', '=', '加工能力')]) '适合加工方式', domain=[('type', '=', '加工能力')])
blade_tip_characteristics_id = fields.Many2many('maintenance.equipment.image', blade_tip_characteristics_id = fields.Many2one('maintenance.equipment.image',
'blade_tip_character_library_rel',
'刀尖特征', domain=[('type', '=', '刀尖特征')]) '刀尖特征', domain=[('type', '=', '刀尖特征')])
handle_type_ids = fields.Many2many('maintenance.equipment.image', 'handle_type_library_rel', handle_type_id = fields.Many2one('maintenance.equipment.image',
'柄部类型', domain=[('type', '=', '柄部类型')]) '柄部类型', domain=[('type', '=', '柄部类型')])
cutting_direction_ids = fields.Many2many('maintenance.equipment.image', 'cutting_direction_library_rel', cutting_direction_ids = fields.Many2many('maintenance.equipment.image', 'cutting_direction_library_rel',
'走刀方向', domain=[('type', '=', '走刀方向')]) '走刀方向', domain=[('type', '=', '走刀方向')])
suitable_coolant_ids = fields.Many2many('maintenance.equipment.image', 'suitable_coolant_library_rel', suitable_coolant_ids = fields.Many2many('maintenance.equipment.image', 'suitable_coolant_library_rel',
'适合冷却液', domain=[('type', '=', '冷却液')]) '适合冷却液', domain=[('type', '=', '冷却液')])
compaction_way_ids = fields.Many2many('maintenance.equipment.image', 'compaction_way_library_rel', compaction_way_id = fields.Many2one('maintenance.equipment.image',
'压紧方式', domain=[('type', '=', '压紧方式')]) '压紧方式', domain=[('type', '=', '压紧方式')])
integral_tool_basic_parameters_ids = fields.One2many('sf.tool.materials.basic.parameters', integral_tool_basic_parameters_ids = fields.One2many('sf.tool.materials.basic.parameters',
'standard_library_id', string='整体式刀具基本参数') 'standard_library_id', string='整体式刀具基本参数')
@@ -218,3 +217,16 @@ class MaintenanceStandardImage(models.Model):
new_res.append([id, name, data_uri]) new_res.append([id, name, data_uri])
# 返回新的结果列表 # 返回新的结果列表
return new_res return new_res
@api.model
def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None):
# 调用原生的search_read方法获取记录
records = super(MaintenanceStandardImage, self).search_read(domain, fields, offset, limit, order)
# 遍历每条记录添加image数据
for record in records:
image_field = self.browse(record['id']).image
if image_field:
record['image'] = f"data:image/png;base64,{image_field.decode('utf-8')}"
else:
record['image'] = ""
return records

View File

@@ -170,7 +170,7 @@
</group> </group>
<group string="适配刀片形状" <group string="适配刀片形状"
attrs="{'invisible': [('cutting_tool_type', 'in', ('刀柄','夹头','整体式刀具',False))]}"> attrs="{'invisible': [('cutting_tool_type', 'in', ('刀柄','夹头','整体式刀具',False))]}">
<field name="fit_blade_shape_id" string="" widget="custom_many2many_checkboxes"/> <field name="fit_blade_shape_id" string="" widget="many2one_radio"/>
</group> </group>
<group string="适合加工方式" <group string="适合加工方式"
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}"> attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
@@ -180,15 +180,15 @@
<group string="刀尖特征" <group string="刀尖特征"
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}"> attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
<field name="blade_tip_characteristics_id" string="" <field name="blade_tip_characteristics_id" string=""
widget="custom_many2many_checkboxes"/> widget="many2one_radio"/>
</group> </group>
<group attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}"> <group attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
<group string="柄部类型" attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')]}"> <group string="柄部类型" attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')]}">
<field name="handle_type_ids" string="" widget="custom_many2many_checkboxes"/> <field name="handle_type_id" string="" widget="many2one_radio"/>
</group> </group>
<group string="压紧方式" <group string="压紧方式"
attrs="{'invisible': [('cutting_tool_type', 'not in', ('刀杆','刀盘'))]}"> attrs="{'invisible': [('cutting_tool_type', 'not in', ('刀杆','刀盘'))]}">
<field name="compaction_way_ids" string="" widget="custom_many2many_checkboxes"/> <field name="compaction_way_id" string="" widget="many2one_radio"/>
</group> </group>
</group> </group>
<group attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}"> <group attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">

View File

@@ -10,10 +10,10 @@
""", """,
'category': 'sf', 'category': 'sf',
'website': 'https://www.sf.cs.jikimo.com', 'website': 'https://www.sf.cs.jikimo.com',
'depends': ['sf_base'], 'depends': ['sf_base', 'delivery'],
'data': [ 'data': [
'views/res_partner_view.xml', 'views/res_partner_view.xml',
# 'views/view.xml', 'views/view.xml',
'report/bill_report.xml', 'report/bill_report.xml',
], ],
'demo': [ 'demo': [

View File

@@ -3,9 +3,9 @@ import logging
from datetime import datetime from datetime import datetime
import requests import requests
import cpca import cpca
from odoo.exceptions import UserError # from odoo.exceptions import UserError
from odoo.exceptions import ValidationError # from odoo.exceptions import ValidationError
from odoo import api, fields, models, SUPERUSER_ID, _ from odoo import api, fields, models
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@@ -40,9 +40,22 @@ class JdEclp(models.Model):
# bill = fields.Many2one('ir.attachment', string='物流面单', compute='query_bill_pdf') # bill = fields.Many2one('ir.attachment', string='物流面单', compute='query_bill_pdf')
# bill_show = fields.Binary(string='物流面单展示', readonly=True, related='self.bill.datas') # bill_show = fields.Binary(string='物流面单展示', readonly=True, related='self.bill.datas')
bill_show = fields.Binary(string='物流面单展示', readonly=True) bill_show = fields.Binary(string='物流面单展示', readonly=True)
check_out = fields.Char(string='查询是否为出库单', compute='_check_is_out')
@api.depends('name')
def _check_is_out(self):
"""
判断是否为出库单
"""
if self.name:
is_check_out = self.name.split('/')
self.check_out = is_check_out[1]
@api.depends('carrier_tracking_ref') @api.depends('carrier_tracking_ref')
def query_bill_pdf(self): def query_bill_pdf(self):
"""
查询物流面单并赋值给bill
"""
self.bill = self.env['ir.attachment'].sudo().search([('name', '=', self.carrier_tracking_ref)]) self.bill = self.env['ir.attachment'].sudo().search([('name', '=', self.carrier_tracking_ref)])
@api.depends('origin') @api.depends('origin')
@@ -77,12 +90,12 @@ class JdEclp(models.Model):
self.receiverTownName = self.receiverTownName self.receiverTownName = self.receiverTownName
except Exception as e: except Exception as e:
print(f"Error address is none: {e}") print(f"Error address is none: {e}")
pass
def create_order(self): def create_order(self):
# sale_order_id = self.env['sale.order'].search([('name', '=', self.origin)]) # sale_order_id = self.env['sale.order'].search([('name', '=', self.origin)])
# if self.carrier_id == '京东物流': # if self.carrier_id == '京东物流':
config = self.env['res.config.settings'].get_values()
createTime = str(datetime.now()) createTime = str(datetime.now())
json1 = { json1 = {
'params': { 'params': {
@@ -106,7 +119,7 @@ class JdEclp(models.Model):
}, },
} }
_logger.info('准备调接口1') _logger.info('准备调接口1')
url1 = 'https://bfm.cs.jikimo.com/api/create/jd/order' url1 = config['bfm_url'] + '/api/create/jd/order'
requests.post(url1, json=json1, data=None) requests.post(url1, json=json1, data=None)
_logger.info('调用成功1') _logger.info('调用成功1')
_logger.info('准备调接口2') _logger.info('准备调接口2')
@@ -115,7 +128,7 @@ class JdEclp(models.Model):
'orderNo': self.origin, 'orderNo': self.origin,
}, },
} }
url2 = 'https://bfm.cs.jikimo.com/api/get/jd/no' url2 = config['bfm_url'] + '/api/get/jd/no'
response = requests.post(url2, json=json2, data=None) response = requests.post(url2, json=json2, data=None)
# _logger.info('调用成功2', response.json()['result']['wbNo']) # _logger.info('调用成功2', response.json()['result']['wbNo'])
self.carrier_tracking_ref = response.json()['result']['wbNo'] self.carrier_tracking_ref = response.json()['result']['wbNo']
@@ -124,12 +137,16 @@ class JdEclp(models.Model):
# raise UserError("选择京东物流才能下单呦") # raise UserError("选择京东物流才能下单呦")
def get_bill(self): def get_bill(self):
"""
获取物流面单
"""
config = self.env['res.config.settings'].get_values()
json1 = { json1 = {
'params': { 'params': {
'no': self.origin, 'no': self.origin,
}, },
} }
url1 = 'https://bfm.cs.jikimo.com/api/create/jd/bill' url1 = config['bfm_url'] + '/api/create/jd/bill'
response = requests.post(url1, json=json1, data=None) response = requests.post(url1, json=json1, data=None)
# _logger.info('调用成功2', response.json()) # _logger.info('调用成功2', response.json())
@@ -152,5 +169,3 @@ class JdEclp(models.Model):
# 'model_name': 'stock.picking', # 'model_name': 'stock.picking',
}) })
_logger.info(attachment) _logger.info(attachment)
# _logger.info(attachment.datas)
# _logger.info(attachment.datas_fname)

View File

@@ -3,7 +3,6 @@ import logging
import requests import requests
from odoo import fields, models from odoo import fields, models
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@@ -203,17 +202,17 @@ class FinishStatusChange(models.Model):
[('id', 'child_of', self.picking_type_id.warehouse_id.view_location_id.id), [('id', 'child_of', self.picking_type_id.warehouse_id.view_location_id.id),
('usage', '!=', 'supplier')]) ('usage', '!=', 'supplier')])
if self.env['stock.move'].search([ if self.env['stock.move'].search([
('state', 'in', ['confirmed', 'partially_available', 'waiting', 'assigned']), ('state', 'in', ['confirmed', 'partially_available', 'waiting', 'assigned']),
('product_qty', '>', 0), ('product_qty', '>', 0),
('location_id', 'in', wh_location_ids), ('location_id', 'in', wh_location_ids),
('move_orig_ids', '=', False), ('move_orig_ids', '=', False),
('picking_id', 'not in', self.ids), ('picking_id', 'not in', self.ids),
('product_id', 'in', lines.product_id.ids)], limit=1): ('product_id', 'in', lines.product_id.ids)], limit=1):
action = self.action_view_reception_report() action = self.action_view_reception_report()
action['context'] = {'default_picking_ids': self.ids} action['context'] = {'default_picking_ids': self.ids}
return action return action
out_start_time = str(datetime.now()) # out_start_time = str(datetime.now())
json2 = { json2 = {
'params': { 'params': {
'model_name': 'jikimo.process.order', 'model_name': 'jikimo.process.order',

View File

@@ -1,5 +1,31 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<odoo> <odoo>
<record id="add_check_out_view_picking_form" model="ir.ui.view">
<field name="name">增加一个check_out字段</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<xpath expr="//form//sheet//div[@name='button_box']" position="inside">
<field name="check_out" invisible="True"/>
</xpath>
</field>
</record>
<record id="custom_view_picking_form" model="ir.ui.view">
<field name="name">物流</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<xpath expr="//form//header//button[@name='action_assign']" position="after">
<button string="京东物流下单" name="create_order" type="object" confirm="是否确认物流下单" class="btn-primary"
attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<button string="获取物流面单" name="get_bill" type="object" confirm="是否获取物流面单" class="btn-primary"
attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
</xpath>
</field>
</record>
<record id="tracking_view" model="ir.ui.view"> <record id="tracking_view" model="ir.ui.view">
<field name="name">tracking</field> <field name="name">tracking</field>
<field name="model">stock.picking</field> <field name="model">stock.picking</field>
@@ -7,45 +33,33 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//group//field[@name='carrier_id']" position="after"> <xpath expr="//group//field[@name='carrier_id']" position="after">
<!-- <field name="senderNickName" domain="[('self.name', 'like', '%OUT%')]"/> --> <!-- <field name="senderNickName" domain="[('self.name', 'like', '%OUT%')]"/> -->
<field name="senderNickName" attrs="{'invisible': [('name', 'like', '%OUT%')]}"/> <field name="senderNickName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="expressItemName" attrs="{'invisible': [('name', 'like', '%OUT%')]}"/> <field name="expressItemName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="deliveryType" attrs="{'invisible': [('name', 'like', '%OUT%')]}"/> <field name="deliveryType" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverName" attrs="{'invisible': [('name', 'like', '%OUT%')]}"/> <field name="receiverName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverMobile" attrs="{'invisible': [('name', 'like', '%OUT%')]}"/> <field name="receiverMobile" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverProvinceName" attrs="{'invisible': [('name', 'like', '%OUT%')]}"/> <field name="receiverProvinceName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverCityName" attrs="{'invisible': [('name', 'like', '%OUT%')]}"/> <field name="receiverCityName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverCountyName" attrs="{'invisible': [('name', 'like', '%OUT%')]}"/> <field name="receiverCountyName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverTownName" attrs="{'invisible': [('name', 'like', '%OUT%')]}"/> <field name="receiverTownName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverCompany" attrs="{'invisible': [('name', 'like', '%OUT%')]}"/> <field name="receiverCompany" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="remark" attrs="{'invisible': [('name', 'like', '%OUT%')]}"/> <field name="remark" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="grossWeight"/> <field name="grossWeight" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="grossVolume"/> <field name="grossVolume" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="pickupBeginTime"/> <field name="pickupBeginTime" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="bill"/> <field name="bill_show" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
</xpath> </xpath>
<xpath expr="//group//field[@name='group_id']" position="after"> <xpath expr="//group//field[@name='group_id']" position="after">
<field name="bill_show" widget="pdf_viewer"/> <field name="bill_show" widget="pdf_viewer" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
</xpath> </xpath>
<xpath expr="//group[@name='other_infos']" position="after"> <!-- <xpath expr="//group[@name='other_infos']" position="after"> -->
<!-- <group>--> <!-- <div> -->
<div> <!-- <button string="京东物流下单" name="create_order" type="object" confirm="是否确认物流下单" class="btn-primary"/> -->
<button string="京东物流下单" name="create_order" type="object" confirm="是否确认物流下单" class="btn-primary"/> <!-- </div> -->
</div> <!-- <div> -->
<!-- <button string="获取物流面单" name="get_bill" type="object" confirm="是否获取物流面单" class="btn-primary"/> -->
<!-- </group>--> <!-- </div> -->
<!-- <group>--> <!-- </xpath> -->
<div>
<button string="获取物流面单" name="get_bill" type="object" confirm="是否获取物流面单" class="btn-primary"/>
</div>
<!-- </group>-->
</xpath>
</field> </field>
</record> </record>
</odoo> </odoo>

View File

@@ -390,7 +390,7 @@
</group> </group>
<group string="适配刀片形状" <group string="适配刀片形状"
attrs="{'invisible': [('cutting_tool_type', 'in', ('刀柄','夹头','整体式刀具',False))]}"> attrs="{'invisible': [('cutting_tool_type', 'in', ('刀柄','夹头','整体式刀具',False))]}">
<field name="fit_blade_shape_id" string="" widget="custom_many2many_checkboxes"/> <field name="fit_blade_shape_id" string="" widget="many2one_radio"/>
</group> </group>
<group string="适合加工方式" <group string="适合加工方式"
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}"> attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
@@ -400,16 +400,16 @@
<group string="刀尖特征" <group string="刀尖特征"
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}"> attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
<field name="blade_tip_characteristics_id" string="" <field name="blade_tip_characteristics_id" string=""
widget="custom_many2many_checkboxes"/> widget="many2one_radio"/>
</group> </group>
<group attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}"> <group attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
<group string="柄部类型" <group string="柄部类型"
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')]}"> attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')]}">
<field name="handle_type_ids" string="" widget="custom_many2many_checkboxes"/> <field name="handle_type_id" string="" widget="many2one_radio"/>
</group> </group>
<group string="压紧方式" <group string="压紧方式"
attrs="{'invisible': [('cutting_tool_type', 'not in', ('刀杆','刀盘'))]}"> attrs="{'invisible': [('cutting_tool_type', 'not in', ('刀杆','刀盘'))]}">
<field name="compaction_way_ids" string="" widget="custom_many2many_checkboxes"/> <field name="compaction_way_id" string="" widget="many2one_radio"/>
</group> </group>
</group> </group>
<group attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}"> <group attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">

View File

@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
import time
import json import json
import base64 import base64
import logging import logging
@@ -9,7 +8,7 @@ from zipfile import ZipFile
from datetime import datetime, timedelta from datetime import datetime, timedelta
import requests import requests
from odoo.http import request from odoo.http import request
from odoo import fields, models, api, _ from odoo import fields, models, api
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.addons.sf_machine_connect.models import py2opcua, ftp_operate from odoo.addons.sf_machine_connect.models import py2opcua, ftp_operate
@@ -466,7 +465,7 @@ class WorkCenterBarcode(models.Model):
raise UserError('NC下发执行超时, 请检查下发状态') raise UserError('NC下发执行超时, 请检查下发状态')
def get__state(self): def get__state(self):
pay_time = str(datetime.now()) # pay_time = str(datetime.now())
json = { json = {
'params': { 'params': {
'model_name': 'jikimo.process.order', 'model_name': 'jikimo.process.order',
@@ -534,7 +533,7 @@ class WorkCenterBarcode(models.Model):
action1 = json.dumps(action) action1 = json.dumps(action)
return action1 return action1
else: else:
return False return None
def cnc_file_download(self): def cnc_file_download(self):
""" """
@@ -544,7 +543,7 @@ class WorkCenterBarcode(models.Model):
# 如果没有附件,直接返回 # 如果没有附件,直接返回
if not self.cnc_ids: if not self.cnc_ids:
return return None
# 处理第一个附件的文件名 # 处理第一个附件的文件名
first_attachment = self.cnc_ids[0].cnc_id first_attachment = self.cnc_ids[0].cnc_id

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import logging import logging
from odoo import api, fields, models, _ from odoo import api, fields, models
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)

View File

@@ -97,13 +97,12 @@ export class CodeField extends Component {
} else { } else {
if (records[0].state === '占用') { if (records[0].state === '占用') {
// console.log('此托盘已占用,请检查') // console.log('此托盘已占用,请检查')
alert('此托盘已占用,请检查') alert('此托盘已占用,请检查');
} else { } else {
// console.log('此托盘已损坏,请登记') // console.log('此托盘已损坏,请登记')
alert('此托盘已损坏,请登记') alert('此托盘已损坏,请登记');
} }
} }
} else { } else {
const searchInput = this.autocompleteContainerRef.el.querySelector("input"); const searchInput = this.autocompleteContainerRef.el.querySelector("input");
searchInput.value = barcode; searchInput.value = barcode;
@@ -114,7 +113,7 @@ export class CodeField extends Component {
} }
} }
} }
CodeField.template = 'sf_machine_connect.CodeField' CodeField.template = 'sf_machine_connect.CodeField';
// Register the field in the registry // Register the field in the registry
CodeField.props = standardFieldProps; CodeField.props = standardFieldProps;
registry.category("fields").add("code", CodeField); registry.category("fields").add("code", CodeField);

View File

@@ -8,7 +8,7 @@ const { Component, xml } = owl;
export class BarcodeHandlerField extends Component { export class BarcodeHandlerField extends Component {
setup() { setup() {
this.actionService = useService("action") this.actionService = useService("action");
const barcode = useService("barcode"); const barcode = useService("barcode");
// this.rpc = useService("rpc"); // this.rpc = useService("rpc");
// useBus(barcode.bus, "barcode_scanned", this.onBarcodeScanned.bind(this)); // useBus(barcode.bus, "barcode_scanned", this.onBarcodeScanned.bind(this));
@@ -18,7 +18,7 @@ export class BarcodeHandlerField extends Component {
// const { data } = await this.env.services.rpc('/web/dataset/call_kw', params); // const { data } = await this.env.services.rpc('/web/dataset/call_kw', params);
const response = await this.env.services.rpc('/web/dataset/call_kw', params); const response = await this.env.services.rpc('/web/dataset/call_kw', params);
// return response // return response
const responseObject = JSON.parse(response) const responseObject = JSON.parse(response);
return responseObject; return responseObject;
} }
@@ -58,8 +58,7 @@ export class BarcodeHandlerField extends Component {
} else { } else {
// console.error("Barcode not found or RPC call failed."); // console.error("Barcode not found or RPC call failed.");
} }
} };
} }
BarcodeHandlerField.template = xml``; BarcodeHandlerField.template = xml``;

View File

@@ -2,15 +2,11 @@
import base64 import base64
import logging import logging
import os import os
import json
import hashlib
import time
from datetime import datetime from datetime import datetime
import requests from odoo import fields, models
from odoo import fields, models, api, _ # from odoo.exceptions import ValidationError
from odoo.exceptions import ValidationError
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.addons.sf_machine_connect.models import py2opcua, ftp_operate from odoo.addons.sf_machine_connect.models import ftp_operate
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)

View File

@@ -1,4 +1,4 @@
from odoo import fields, models, api from odoo import fields, models
class ModelType(models.Model): class ModelType(models.Model):

View File

@@ -87,22 +87,20 @@ class ResProductMo(models.Model):
cutting_tool_run_out_accuracy_max = fields.Float('端跳精度max', digits=(6, 1)) cutting_tool_run_out_accuracy_max = fields.Float('端跳精度max', digits=(6, 1))
cutting_tool_run_out_accuracy_min = fields.Float('端跳精度min', digits=(6, 1)) cutting_tool_run_out_accuracy_min = fields.Float('端跳精度min', digits=(6, 1))
cutting_tool_blade_tip_working_size = fields.Char('刀尖处理尺寸(R半径mm/倒角)', size=20) cutting_tool_blade_tip_working_size = fields.Char('刀尖处理尺寸(R半径mm/倒角)', size=20)
fit_blade_shape_id = fields.Many2many('maintenance.equipment.image', 'rel_fit_blade_shape_product_template', fit_blade_shape_id = fields.Many2one('maintenance.equipment.image',
'适配刀片形状', domain=[('type', '=', '刀片形状')]) '适配刀片形状', domain=[('type', '=', '刀片形状')])
suitable_machining_method_ids = fields.Many2many('maintenance.equipment.image', suitable_machining_method_ids = fields.Many2many('maintenance.equipment.image',
'rel_machining_product_template', '适合加工方式', 'rel_machining_product_template', '适合加工方式',
domain=[('type', '=', '加工能力')]) domain=[('type', '=', '加工能力')])
blade_tip_characteristics_id = fields.Many2many('maintenance.equipment.image', blade_tip_characteristics_id = fields.Many2one('maintenance.equipment.image', '刀尖特征',
'rel_blade_tip_product_template', '刀尖特征', domain=[('type', '=', '刀尖特征')])
domain=[('type', '=', '刀尖特征')]) handle_type_id = fields.Many2one('maintenance.equipment.image', '柄部类型', domain=[('type', '=', '柄部类型')])
handle_type_ids = fields.Many2many('maintenance.equipment.image', 'rel_handle_product_template', '柄部类型',
domain=[('type', '=', '柄部类型')])
cutting_direction_ids = fields.Many2many('maintenance.equipment.image', 'rel_cutting_product_template', cutting_direction_ids = fields.Many2many('maintenance.equipment.image', 'rel_cutting_product_template',
'走刀方向', domain=[('type', '=', '走刀方向')]) '走刀方向', domain=[('type', '=', '走刀方向')])
suitable_coolant_ids = fields.Many2many('maintenance.equipment.image', 'rel_coolant_product_template', suitable_coolant_ids = fields.Many2many('maintenance.equipment.image', 'rel_coolant_product_template',
'适合冷却液', domain=[('type', '=', '冷却液')]) '适合冷却液', domain=[('type', '=', '冷却液')])
compaction_way_ids = fields.Many2many('maintenance.equipment.image', 'rel_compaction_product_template', compaction_way_id = fields.Many2one('maintenance.equipment.image',
'压紧方式', domain=[('type', '=', '压紧方式')]) '压紧方式', domain=[('type', '=', '压紧方式')])
@api.onchange('cutting_tool_material_id') @api.onchange('cutting_tool_material_id')
def _onchange_cutting_tool_material_id(self): def _onchange_cutting_tool_material_id(self):
@@ -141,10 +139,10 @@ class ResProductMo(models.Model):
item.fit_blade_shape_id = False item.fit_blade_shape_id = False
item.suitable_machining_method_ids = False item.suitable_machining_method_ids = False
item.blade_tip_characteristics_id = False item.blade_tip_characteristics_id = False
item.handle_type_ids = False item.handle_type_id = False
item.cutting_direction_ids = False item.cutting_direction_ids = False
item.suitable_coolant_ids = False item.suitable_coolant_ids = False
item.compaction_way_ids = False item.compaction_way_id = False
item.cutting_speed_ids = False item.cutting_speed_ids = False
item.feed_per_tooth_ids = False item.feed_per_tooth_ids = False
@@ -288,13 +286,9 @@ class ResProductMo(models.Model):
self.suitable_machining_method_ids = [(6, 0, [])] if not \ self.suitable_machining_method_ids = [(6, 0, [])] if not \
self.cutting_tool_model_id.suitable_machining_method_ids \ self.cutting_tool_model_id.suitable_machining_method_ids \
else [(6, 0, self.cutting_tool_model_id.suitable_machining_method_ids.ids)] else [(6, 0, self.cutting_tool_model_id.suitable_machining_method_ids.ids)]
self.blade_tip_characteristics_id = [(6, 0, [])] if not \ self.blade_tip_characteristics_id = self.cutting_tool_model_id.blade_tip_characteristics_id.id
self.cutting_tool_model_id.blade_tip_characteristics_id \
else [(6, 0, self.cutting_tool_model_id.blade_tip_characteristics_id.ids)]
self.handle_type_ids = [(6, 0, self.handle_type_id = self.cutting_tool_model_id.handle_type_id.id
[])] if not self.cutting_tool_model_id.handle_type_ids else [
(6, 0, self.cutting_tool_model_id.handle_type_ids.ids)]
self.cutting_direction_ids = [(6, 0, self.cutting_direction_ids = [(6, 0,
[])] if not self.cutting_tool_model_id.cutting_direction_ids else [ [])] if not self.cutting_tool_model_id.cutting_direction_ids else [
@@ -303,9 +297,8 @@ class ResProductMo(models.Model):
self.suitable_coolant_ids = [(6, 0, self.suitable_coolant_ids = [(6, 0,
[])] if not self.cutting_tool_model_id.suitable_coolant_ids else [ [])] if not self.cutting_tool_model_id.suitable_coolant_ids else [
(6, 0, self.cutting_tool_model_id.suitable_coolant_ids.ids)] (6, 0, self.cutting_tool_model_id.suitable_coolant_ids.ids)]
self.compaction_way_ids = [(6, 0, self.compaction_way_id = self.cutting_tool_model_id.compaction_way_id.id
[])] if not self.cutting_tool_model_id.compaction_way_ids else [ self.fit_blade_shape_id = self.cutting_tool_model_id.fit_blade_shape_id.id
(6, 0, self.cutting_tool_model_id.compaction_way_ids.ids)]
else: else:
self.cutting_tool_type_id = False self.cutting_tool_type_id = False
self.brand_id = False self.brand_id = False
@@ -338,28 +331,28 @@ class ResProductMo(models.Model):
self.fit_blade_shape_id = False self.fit_blade_shape_id = False
self.suitable_machining_method_ids = False self.suitable_machining_method_ids = False
self.blade_tip_characteristics_id = False self.blade_tip_characteristics_id = False
self.handle_type_ids = False self.handle_type_id = False
self.cutting_direction_ids = False self.cutting_direction_ids = False
self.suitable_coolant_ids = False self.suitable_coolant_ids = False
self.compaction_way_ids = False self.compaction_way_id = False
self.cutting_speed_ids = False self.cutting_speed_ids = False
self.feed_per_tooth_ids = False self.feed_per_tooth_ids = False
@api.constrains('fit_blade_shape_id', 'suitable_machining_method_ids', 'blade_tip_characteristics_id', @api.constrains('fit_blade_shape_id', 'suitable_machining_method_ids', 'blade_tip_characteristics_id',
'handle_type_ids', 'cutting_direction_ids', 'suitable_coolant_ids', 'compaction_way_ids') 'handle_type_id', 'cutting_direction_ids', 'suitable_coolant_ids', 'compaction_way_id')
def _check_cutting_tool_ability(self): def _check_cutting_tool_ability(self):
if self.cutting_tool_type in ['整体式刀具', '刀片', '刀杆', '刀盘']: if self.cutting_tool_type in ['整体式刀具', '刀片', '刀杆', '刀盘']:
if self.cutting_tool_type in ['刀片', '刀杆', '刀盘']: if self.cutting_tool_type in ['刀片', '刀杆', '刀盘']:
if not self.fit_blade_shape_id: if not self.fit_blade_shape_id:
raise ValidationError("请选择适配刀片形状") raise ValidationError("请选择适配刀片形状")
if self.cutting_tool_type in ['刀杆', '刀盘']: if self.cutting_tool_type in ['刀杆', '刀盘']:
if not self.compaction_way_ids: if not self.compaction_way_id:
raise ValidationError("请选择压紧方式") raise ValidationError("请选择压紧方式")
if self.cutting_tool_type == '刀片': if self.cutting_tool_type == '刀片':
if not self.suitable_coolant_ids: if not self.suitable_coolant_ids:
raise ValidationError("请选择适合冷却液") raise ValidationError("请选择适合冷却液")
elif self.cutting_tool_type == '整体式刀具': elif self.cutting_tool_type == '整体式刀具':
if not self.handle_type_ids: if not self.handle_type_id:
raise ValidationError("请选择柄部类型") raise ValidationError("请选择柄部类型")
if not self.suitable_coolant_ids: if not self.suitable_coolant_ids:
raise ValidationError("请选择适合冷却液") raise ValidationError("请选择适合冷却液")
@@ -880,3 +873,13 @@ class SfMaintenanceEquipmentTool(models.Model):
for record in self: for record in self:
if record.code: if record.code:
record.name = record.code record.name = record.code
@api.model_create_multi
def create(self, vals_list):
tools = super().create(vals_list)
for tool in tools:
self.env['sf.machine.table.tool.changing.apply'].sudo().create({
'maintenance_equipment_id': tool.equipment_id.id,
'cutter_spacing_code_id': tool.id
})
return tools

View File

@@ -5,7 +5,7 @@ import logging
import json import json
from re import split as regex_split from re import split as regex_split
from re import findall as regex_findall from re import findall as regex_findall
from datetime import datetime, timedelta from datetime import datetime
import requests import requests
from odoo import SUPERUSER_ID, _, api, fields, models from odoo import SUPERUSER_ID, _, api, fields, models
from odoo.tools import float_compare from odoo.tools import float_compare
@@ -149,8 +149,8 @@ class StockRule(models.Model):
raise ProcurementException(errors) raise ProcurementException(errors)
for company_id, productions_values in productions_values_by_company.items(): 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 # create the MO as SUPERUSER because the current user may not have the rights to do it
# launched by a sale for example) # (mto product launched by a sale for example)
'''创建制造订单''' '''创建制造订单'''
productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create( productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create(
productions_values) productions_values)
@@ -201,18 +201,17 @@ class StockRule(models.Model):
sale_order = self.env['sale.order'].sudo().search([('name', '=', production.origin)]) sale_order = self.env['sale.order'].sudo().search([('name', '=', production.origin)])
if sale_order: if sale_order:
bb = sale_order.deadline_of_delivery self.env['sf.production.plan'].sudo().with_company(company_id). \
productions = self.env['sf.production.plan'].with_user(SUPERUSER_ID).sudo().with_company(company_id). \ create({
create({ 'name': production.name,
'name': production.name, 'order_deadline': sale_order.deadline_of_delivery,
'order_deadline': sale_order.deadline_of_delivery, 'production_id': production.id,
'production_id': production.id, 'date_planned_start': production.date_planned_start,
'date_planned_start': production.date_planned_start, 'origin': production.origin,
'origin': production.origin, 'product_qty': production.product_qty,
'product_qty': production.product_qty, 'product_id': production.product_id.id,
'product_id': production.product_id.id, 'state': 'draft',
'state': 'draft', })
})
return True return True

View File

@@ -17,16 +17,10 @@ class CustomKanbanController extends KanbanController {
this.workOrders = await this.getAllWorkOrders(); this.workOrders = await this.getAllWorkOrders();
// this.workOrdersNew = this.workOrders; // this.workOrdersNew = this.workOrders;
// console.log('lines222222222', this.workOrders); // console.log('lines222222222', this.workOrders);
//
// console.log(typeof this.workOrders); // console.log(typeof this.workOrders);
// console.log(Array.isArray(this.workOrders)); // console.log(Array.isArray(this.workOrders));
//
// console.log(this.workOrders.every(order => // console.log(this.workOrders.every(order =>
// typeof order === 'object' && order.id !== undefined)); // typeof order === 'object' && order.id !== undefined));
// var aDiv = document.getElementsByClassName('o_kanban_record') // var aDiv = document.getElementsByClassName('o_kanban_record')
// for (var i = 0; i < aDiv.length; i++) { // for (var i = 0; i < aDiv.length; i++) {
// console.log(aDiv[i]) // console.log(aDiv[i])
@@ -37,8 +31,7 @@ class CustomKanbanController extends KanbanController {
buttonClick(ev) { buttonClick(ev) {
const button = ev.currentTarget; const button = ev.currentTarget;
const id = button.getAttribute('data-id'); const id = button.getAttribute('data-id');
// console.log('true_id', id);
console.log('true_id', id);
// const context = {production_line_show: 'shengchanxian1'} // const context = {production_line_show: 'shengchanxian1'}
this.env.services.rpc('/web/dataset/call_kw', { this.env.services.rpc('/web/dataset/call_kw', {
model: 'mrp.workcenter', model: 'mrp.workcenter',
@@ -58,8 +51,8 @@ class CustomKanbanController extends KanbanController {
// console.log('response', response); // console.log('response', response);
location.reload(); location.reload();
window.onload = function () { window.onload = function () {
button.classList.add('choose') button.classList.add('choose');
} };
}); });
}); });
} }
@@ -82,8 +75,6 @@ class CustomKanbanController extends KanbanController {
// 你可以在这里处理响应,例如将其存储在控制器的状态中 // 你可以在这里处理响应,例如将其存储在控制器的状态中
return response; return response;
} }
} }
CustomKanbanController.template = "sf_manufacturing.CustomKanbanView1"; CustomKanbanController.template = "sf_manufacturing.CustomKanbanView1";

View File

@@ -1,9 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import logging
import json import json
import base64 import base64
import logging
import os
from datetime import date, timedelta
from odoo import http from odoo import http
from odoo.http import request from odoo.http import request

View File

@@ -1800,6 +1800,7 @@ class Cutting_tool_standard_library(models.Model):
for feed_per_tooth_4_item in item['feed_per_tooth_4']: for feed_per_tooth_4_item in item['feed_per_tooth_4']:
feed_per_tooth_4_list.append( feed_per_tooth_4_list.append(
self.env['sf.feed.per.tooth']._json_feed_per_tooth(feed_per_tooth_4_item)) self.env['sf.feed.per.tooth']._json_feed_per_tooth(feed_per_tooth_4_item))
if not cutting_tool_standard_library: if not cutting_tool_standard_library:
self.create({ self.create({
"code": item['code'].replace("JKM", result['factory_short_name']), "code": item['code'].replace("JKM", result['factory_short_name']),
@@ -1820,21 +1821,22 @@ class Cutting_tool_standard_library(models.Model):
"cutter_pad_ids": [(6, 0, [])] if not item.get('cutter_pad_codes') else self.env[ "cutter_pad_ids": [(6, 0, [])] if not item.get('cutter_pad_codes') else self.env[
'sf.cutting_tool.standard.library']._get_ids(item['cutter_pad_codes'], 'sf.cutting_tool.standard.library']._get_ids(item['cutter_pad_codes'],
result['factory_short_name']), result['factory_short_name']),
"fit_blade_shape_id": [(6, 0, [])] if not item.get('fit_blade_shape') else self.env[ "fit_blade_shape_id": False if not item['fit_blade_shape'] else self.env[
'maintenance.equipment.image']._get_ids(item['fit_blade_shape']), 'maintenance.equipment.image'].search(
[('name', '=', item['fit_blade_shape'])]).id,
"suitable_machining_method_ids": [(6, 0, [])] if not item.get( "suitable_machining_method_ids": [(6, 0, [])] if not item.get(
'suitable_machining_methods') else self.env['maintenance.equipment.image']._get_ids( 'suitable_machining_methods') else self.env['maintenance.equipment.image']._get_ids(
item['suitable_machining_methods']), item['suitable_machining_methods']),
"blade_tip_characteristics_id": [(6, 0, [])] if not item.get('blade_tip_characteristics') else "blade_tip_characteristics_id": self.env['maintenance.equipment.image'].search(
self.env['maintenance.equipment.image']._get_ids(item['blade_tip_characteristics']), [('name', '=', item['blade_tip_characteristics'])]).id,
"handle_type_ids": [(6, 0, [])] if not item.get('handle_type') else self.env[ "handle_type_id": self.env['maintenance.equipment.image'].search(
'maintenance.equipment.image']._get_ids(item['handle_type']), [('name', '=', item['handle_type'])]).id,
"cutting_direction_ids": [(6, 0, [])] if not item.get('cutting_direction') else self.env[ "cutting_direction_ids": [(6, 0, [])] if not item.get('cutting_direction') else self.env[
'maintenance.equipment.image']._get_ids(item['cutting_direction']), 'maintenance.equipment.image']._get_ids(item['cutting_direction']),
"suitable_coolant_ids": [(6, 0, [])] if not item.get('suitable_coolant') else self.env[ "suitable_coolant_ids": [(6, 0, [])] if not item.get('suitable_coolant') else self.env[
'maintenance.equipment.image']._get_ids(item['suitable_coolant']), 'maintenance.equipment.image']._get_ids(item['suitable_coolant']),
"compaction_way_ids": [(6, 0, [])] if not item.get('compaction_way') else self.env[ "compaction_way_id": self.env['maintenance.equipment.image'].search(
'maintenance.equipment.image']._get_ids(item['compaction_way']), [('name', '=', item['compaction_way'])]).id,
"integral_tool_basic_parameters_ids": integral_tool_basic_param_list, "integral_tool_basic_parameters_ids": integral_tool_basic_param_list,
"blade_basic_parameters_ids": blade_basic_param_list, "blade_basic_parameters_ids": blade_basic_param_list,
"cutter_bar_basic_parameters_ids": cutter_arbor_basic_param_list, "cutter_bar_basic_parameters_ids": cutter_arbor_basic_param_list,
@@ -1868,21 +1870,22 @@ class Cutting_tool_standard_library(models.Model):
"cutter_pad_ids": [(6, 0, [])] if not item.get('cutter_pad_codes') else self.env[ "cutter_pad_ids": [(6, 0, [])] if not item.get('cutter_pad_codes') else self.env[
'sf.cutting_tool.standard.library']._get_ids(item['cutter_pad_codes'], 'sf.cutting_tool.standard.library']._get_ids(item['cutter_pad_codes'],
result['factory_short_name']), result['factory_short_name']),
"fit_blade_shape_id": [(6, 0, [])] if not item.get('fit_blade_shape') else self.env[ "fit_blade_shape_id": False if not item['fit_blade_shape'] else self.env[
'maintenance.equipment.image']._get_ids(item['fit_blade_shape']), 'maintenance.equipment.image'].search(
[('name', '=', item['fit_blade_shape'])]).id,
"suitable_machining_method_ids": [(6, 0, [])] if not item.get( "suitable_machining_method_ids": [(6, 0, [])] if not item.get(
'suitable_machining_methods') else self.env['maintenance.equipment.image']._get_ids( 'suitable_machining_methods') else self.env['maintenance.equipment.image']._get_ids(
item['suitable_machining_methods']), item['suitable_machining_methods']),
"blade_tip_characteristics_id": [(6, 0, [])] if not item.get('blade_tip_characteristics') else "blade_tip_characteristics_id": self.env['maintenance.equipment.image'].search(
self.env['maintenance.equipment.image']._get_ids(item['blade_tip_characteristics']), [('name', '=', item['blade_tip_characteristics'])]).id,
"handle_type_ids": [(6, 0, [])] if not item.get('handle_type') else self.env[ "handle_type_id": self.env['maintenance.equipment.image'].search(
'maintenance.equipment.image']._get_ids(item['handle_type']), [('name', '=', item['handle_type'])]).id,
"cutting_direction_ids": [(6, 0, [])] if not item.get('cutting_direction') else self.env[ "cutting_direction_ids": [(6, 0, [])] if not item.get('cutting_direction') else self.env[
'maintenance.equipment.image']._get_ids(item['cutting_direction']), 'maintenance.equipment.image']._get_ids(item['cutting_direction']),
"suitable_coolant_ids": [(6, 0, [])] if not item.get('suitable_coolant') else self.env[ "suitable_coolant_ids": [(6, 0, [])] if not item.get('suitable_coolant') else self.env[
'maintenance.equipment.image']._get_ids(item['suitable_coolant']), 'maintenance.equipment.image']._get_ids(item['suitable_coolant']),
"compaction_way_ids": [(6, 0, [])] if not item.get('compaction_way') else self.env[ "compaction_way_id": self.env['maintenance.equipment.image'].search(
'maintenance.equipment.image']._get_ids(item['compaction_way']), [('name', '=', item['compaction_way'])]).id,
"integral_tool_basic_parameters_ids": integral_tool_basic_param_list, "integral_tool_basic_parameters_ids": integral_tool_basic_param_list,
"blade_basic_parameters_ids": blade_basic_param_list, "blade_basic_parameters_ids": blade_basic_param_list,
"cutter_bar_basic_parameters_ids": cutter_arbor_basic_param_list, "cutter_bar_basic_parameters_ids": cutter_arbor_basic_param_list,
@@ -1985,21 +1988,22 @@ class Cutting_tool_standard_library(models.Model):
"cutter_pad_ids": [(6, 0, [])] if not item.get('cutter_pad_codes') else self.env[ "cutter_pad_ids": [(6, 0, [])] if not item.get('cutter_pad_codes') else self.env[
'sf.cutting_tool.standard.library']._get_ids(item['cutter_pad_codes'], 'sf.cutting_tool.standard.library']._get_ids(item['cutter_pad_codes'],
result['factory_short_name']), result['factory_short_name']),
"fit_blade_shape_id": [(6, 0, [])] if not item.get('fit_blade_shape') else self.env[ "fit_blade_shape_id": False if not item['fit_blade_shape'] else self.env[
'maintenance.equipment.image']._get_ids(item['fit_blade_shape']), 'maintenance.equipment.image'].search(
[('name', '=', item['fit_blade_shape'])]).id,
"suitable_machining_method_ids": [(6, 0, [])] if not item.get( "suitable_machining_method_ids": [(6, 0, [])] if not item.get(
'suitable_machining_method') else self.env['maintenance.equipment.image']._get_ids( 'suitable_machining_method') else self.env['maintenance.equipment.image']._get_ids(
item['suitable_machining_method']), item['suitable_machining_method']),
"blade_tip_characteristics_id": [(6, 0, [])] if not item.get('blade_tip_characteristics') else "blade_tip_characteristics_id": self.env['maintenance.equipment.image'].search(
self.env['maintenance.equipment.image']._get_ids(item['blade_tip_characteristics']), [('name', '=', item['blade_tip_characteristics'])]).id,
"handle_type_ids": [(6, 0, [])] if not item.get('handle_type') else self.env[ "handle_type_id": self.env['maintenance.equipment.image'].search(
'maintenance.equipment.image']._get_ids(item['handle_type']), [('name', '=', item['handle_type'])]).id,
"cutting_direction_ids": [(6, 0, [])] if not item.get('cutting_direction') else self.env[ "cutting_direction_ids": [(6, 0, [])] if not item.get('cutting_direction') else self.env[
'maintenance.equipment.image']._get_ids(item['cutting_direction']), 'maintenance.equipment.image']._get_ids(item['cutting_direction']),
"suitable_coolant_ids": [(6, 0, [])] if not item.get('suitable_coolant') else self.env[ "suitable_coolant_ids": [(6, 0, [])] if not item.get('suitable_coolant') else self.env[
'maintenance.equipment.image']._get_ids(item['suitable_coolant']), 'maintenance.equipment.image']._get_ids(item['suitable_coolant']),
"compaction_way_ids": [(6, 0, [])] if not item.get('compaction_way') else self.env[ "compaction_way_id": self.env['maintenance.equipment.image'].search(
'maintenance.equipment.image']._get_ids(item['compaction_way']), [('name', '=', item['compaction_way'])]).id,
"integral_tool_basic_parameters_ids": integral_tool_basic_param_list, "integral_tool_basic_parameters_ids": integral_tool_basic_param_list,
"blade_basic_parameters_ids": blade_basic_param_list, "blade_basic_parameters_ids": blade_basic_param_list,
"cutter_bar_basic_parameters_ids": cutter_arbor_basic_param_list, "cutter_bar_basic_parameters_ids": cutter_arbor_basic_param_list,
@@ -2033,21 +2037,22 @@ class Cutting_tool_standard_library(models.Model):
"cutter_pad_ids": [(6, 0, [])] if not item.get('cutter_pad_codes') else self.env[ "cutter_pad_ids": [(6, 0, [])] if not item.get('cutter_pad_codes') else self.env[
'sf.cutting_tool.standard.library']._get_ids(item['cutter_pad_codes'], 'sf.cutting_tool.standard.library']._get_ids(item['cutter_pad_codes'],
result['factory_short_name']), result['factory_short_name']),
"fit_blade_shape_id": [(6, 0, [])] if not item.get('fit_blade_shape') else self.env[ "fit_blade_shape_id": False if not item['fit_blade_shape'] else self.env[
'maintenance.equipment.image']._get_ids(item['fit_blade_shape']), 'maintenance.equipment.image'].search(
[('name', '=', item['fit_blade_shape'])]).id,
"suitable_machining_method_ids": [(6, 0, [])] if not item.get( "suitable_machining_method_ids": [(6, 0, [])] if not item.get(
'suitable_machining_methods') else self.env['maintenance.equipment.image']._get_ids( 'suitable_machining_methods') else self.env['maintenance.equipment.image']._get_ids(
item['suitable_machining_methods']), item['suitable_machining_methods']),
"blade_tip_characteristics_id": [(6, 0, [])] if not item.get('blade_tip_characteristics') else "blade_tip_characteristics_id": self.env['maintenance.equipment.image'].search(
self.env['maintenance.equipment.image']._get_ids(item['blade_tip_characteristics']), [('name', '=', item['blade_tip_characteristics'])]).id,
"handle_type_ids": [(6, 0, [])] if not item.get('handle_type') else self.env[ "handle_type_id": self.env['maintenance.equipment.image'].search(
'maintenance.equipment.image']._get_ids(item['handle_type']), [('name', '=', item['handle_type'])]).id,
"cutting_direction_ids": [(6, 0, [])] if not item.get('cutting_direction') else self.env[ "cutting_direction_ids": [(6, 0, [])] if not item.get('cutting_direction') else self.env[
'maintenance.equipment.image']._get_ids(item['cutting_direction']), 'maintenance.equipment.image']._get_ids(item['cutting_direction']),
"suitable_coolant_ids": [(6, 0, [])] if not item.get('suitable_coolant') else self.env[ "suitable_coolant_ids": [(6, 0, [])] if not item.get('suitable_coolant') else self.env[
'maintenance.equipment.image']._get_ids(item['suitable_coolant']), 'maintenance.equipment.image']._get_ids(item['suitable_coolant']),
"compaction_way_ids": [(6, 0, [])] if not item.get('compaction_way') else self.env[ "compaction_way_id": self.env['maintenance.equipment.image'].search(
'maintenance.equipment.image']._get_ids(item['compaction_way']), [('name', '=', item['compaction_way'])]).id,
"integral_tool_basic_parameters_ids": integral_tool_basic_param_list, "integral_tool_basic_parameters_ids": integral_tool_basic_param_list,
"blade_basic_parameters_ids": blade_basic_param_list, "blade_basic_parameters_ids": blade_basic_param_list,
"cutter_bar_basic_parameters_ids": cutter_arbor_basic_param_list, "cutter_bar_basic_parameters_ids": cutter_arbor_basic_param_list,

View File

@@ -16,6 +16,7 @@
'depends': ['sf_manufacturing'], 'depends': ['sf_manufacturing'],
'data': [ 'data': [
'security/ir.model.access.csv', 'security/ir.model.access.csv',
# 'security/rules.xml',
'views/view.xml', 'views/view.xml',
], ],

View File

@@ -11,6 +11,7 @@ from odoo.exceptions import UserError, ValidationError
class sf_production_plan(models.Model): class sf_production_plan(models.Model):
_name = 'sf.production.plan' _name = 'sf.production.plan'
_description = 'sf_production_plan' _description = 'sf_production_plan'
_inherit = ['mail.thread']
_order = 'create_date desc' _order = 'create_date desc'
state = fields.Selection([ state = fields.Selection([
@@ -20,6 +21,7 @@ class sf_production_plan(models.Model):
('finished', '已完成') ('finished', '已完成')
], string='工单状态', tracking=True) ], string='工单状态', tracking=True)
name = fields.Char(string='工单编号') name = fields.Char(string='工单编号')
active = fields.Boolean(string='已归档', default=True)
# selected = fields.Boolean(default=False) # selected = fields.Boolean(default=False)
# order_number = fields.Char(string='订单号') # order_number = fields.Char(string='订单号')
order_deadline = fields.Datetime(string='订单交期') order_deadline = fields.Datetime(string='订单交期')
@@ -49,6 +51,26 @@ class sf_production_plan(models.Model):
sequence = fields.Integer(string='序号', copy=False, readonly=True, index=True) sequence = fields.Integer(string='序号', copy=False, readonly=True, index=True)
current_operation_name = fields.Char(string='当前工序名称', size=64, default='生产计划') current_operation_name = fields.Char(string='当前工序名称', size=64, default='生产计划')
# @api.model
# def _search(self, args, offset=0, limit=None, order=None, count=False, access_rights_uid=None):
# """
# 默认修改筛选
# """
# return super(sf_production_plan, self.with_context(active_test=False))._search(
# args, offset, limit, order, count, access_rights_uid)
def archive(self):
"""
归档
"""
self.write({'active': False})
def unarchive(self):
"""
取消归档
"""
self.write({'active': True})
@api.model @api.model
def get_import_templates(self): def get_import_templates(self):
"""returns the xlsx import template file""" """returns the xlsx import template file"""

View File

@@ -1,8 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sf_production_plan,sf.production.plan,model_sf_production_plan,base.group_user,1,1,1,1 access_sf_production_plan,sf.production.plan,model_sf_production_plan,base.group_user,1,0,0,0
access_sf_machine_schedule,sf.machine.schedule,model_sf_machine_schedule,base.group_user,1,1,1,1 access_sf_production_plan_for_dispatch,sf.production.plan for dispatch,model_sf_production_plan,sf_base.group_plan_dispatch,1,1,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_sf_production_plan sf.production.plan model_sf_production_plan base.group_user 1 1 0 1 0 1 0
3 access_sf_machine_schedule access_sf_production_plan_for_dispatch sf.machine.schedule sf.production.plan for dispatch model_sf_machine_schedule model_sf_production_plan base.group_user sf_base.group_plan_dispatch 1 1 1 1 0

View File

@@ -0,0 +1,9 @@
<odoo>
<record id="plan_order_rule_own" model="ir.rule">
<field name="name">Own Orders Only</field>
<field name="model_id" ref="model_sf_production_plan"/>
<field name="groups" eval="[(4, ref('sf_base.group_plan_dispatch'))]"/>
<!-- <field name="domain_force">[('user_id', '=', user.id)]</field> -->
<field name="domain_force">[('create_uid', '=', user.id)]</field>
</record>
</odoo>

View File

@@ -6,9 +6,6 @@
<field name="model">sf.production.plan</field> <field name="model">sf.production.plan</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="订单计划"> <tree string="订单计划">
<!-- <field name="selected" widget="boolean_toggle"/> -->
<!-- sequence、pl_no、pl_name、quantity、plan_start_time、plan_end_time、actual_start_time、actual_end_time、state、create_uid、create_date -->
<!-- <field name="sequence"/> -->
<field name="state" widget="badge" decoration-warning="state == 'draft'" decoration-success="state == 'done'"/> <field name="state" widget="badge" decoration-warning="state == 'draft'" decoration-success="state == 'done'"/>
<field name="name"/> <field name="name"/>
<field name="origin"/> <field name="origin"/>
@@ -18,8 +15,8 @@
<field name="date_planned_start"/> <field name="date_planned_start"/>
<field name="date_planned_finished"/> <field name="date_planned_finished"/>
<field name="schedule_setting"/> <field name="schedule_setting"/>
<button name="do_production_schedule" class="btn schedule_done" string="生产排程" type="object" attrs="{'invisible': [('state', 'not in', ['draft'])]}"/> <button name="do_production_schedule" class="btn schedule_done" string="生产排程" type="object" attrs="{'invisible': [('state', 'not in', ['draft'])]}" groups="sf_base.group_plan_dispatch"/>
<button name="cancel_production_schedule" class="btn schedule_cancel" string="取消排程" type="object" attrs="{'invisible': [('state', 'not in', ['done'])]}"/> <button name="cancel_production_schedule" class="btn schedule_cancel" string="取消排程" type="object" attrs="{'invisible': [('state', 'not in', ['done'])]}" groups="sf_base.group_plan_dispatch"/>
</tree> </tree>
</field> </field>
</record> </record>
@@ -31,8 +28,11 @@
<form string="订单计划"> <form string="订单计划">
<header> <header>
<!-- <button string="执行排程" name="do_production_schedule" type="object" class="oe_highlight" icon="fa-step-forward"/> --> <!-- <button string="执行排程" name="do_production_schedule" type="object" class="oe_highlight" icon="fa-step-forward"/> -->
<button string="执行排程" name="do_production_schedule" type="object" class="oe_highlight" options='{"calendar_view": true, "date_begin": "2020-01-01", "date_end": "2020-12-31"}'/> <button string="执行排程" name="do_production_schedule" type="object" class="oe_highlight" options='{"calendar_view": true, "date_begin": "2020-01-01", "date_end": "2020-12-31"}' groups="sf_base.group_plan_dispatch"/>
<button string="取消排程" name="cancel_production_schedule" type="object" class="oe_highlight"/> <button string="取消排程" name="cancel_production_schedule" type="object" class="oe_highlight" groups="sf_base.group_plan_dispatch"/>
<button name="archive" type="object" string="归档" icon="fa-archive" class="oe_highlight" attrs="{'invisible': [('active', '=', False)]}"/>
<button name="unarchive" type="object" string="取消归档" icon="fa-archive" class="oe_highlight" attrs="{'invisible': [('active', '=', True)]}"/>
<!-- <button string="销售单" name="test_sale_order" type="object" class="oe_highlight"/> --> <!-- <button string="销售单" name="test_sale_order" type="object" class="oe_highlight"/> -->
<!-- <button string="测试流程" name="liucheng_cs" type="object" class="oe_highlight"/> --> <!-- <button string="测试流程" name="liucheng_cs" type="object" class="oe_highlight"/> -->
<field name="state" widget="statusbar" statusbar_visible="draft,done,processing,finished"/> <field name="state" widget="statusbar" statusbar_visible="draft,done,processing,finished"/>
@@ -45,6 +45,7 @@
</div> </div>
<group> <group>
<group string="基本信息"> <group string="基本信息">
<field name="active" invisible="1"/>
<field name="production_id" widget="many2one_button"/> <field name="production_id" widget="many2one_button"/>
<field name="product_id"/> <field name="product_id"/>
<field name="origin"/> <field name="origin"/>
@@ -60,6 +61,17 @@
<field name="actual_end_time"/> <field name="actual_end_time"/>
<field name="state"/> <field name="state"/>
<field name="shift" widget="time"/> <field name="shift" widget="time"/>
<!-- Chatter -->
<!-- <div class="oe_chatter"> -->
<!-- <field name="message_follower_ids" widget="mail_followers"/> -->
<!-- <field name="message_ids" widget="mail_thread"/> -->
<!-- <field name="activity_ids"/> -->
<!-- </div> -->
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
</div>
</group> </group>
<!-- <group string="规格信息" col="1"> --> <!-- <group string="规格信息" col="1"> -->
<!-- <group col="3"> --> <!-- <group col="3"> -->
@@ -104,6 +116,9 @@
<field name="model">sf.production.plan</field> <field name="model">sf.production.plan</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="订单计划"> <search string="订单计划">
<!-- Your other filters go here -->
<filter name="archived" string="已归档" domain="[('active','=',False)]"/>
<filter name="not archived" string="未归档" domain="[('active','=',True)]"/>
<field name="name"/> <field name="name"/>
<field name="product_qty"/> <field name="product_qty"/>
<field name="date_planned_start"/> <field name="date_planned_start"/>
@@ -261,6 +276,7 @@
name="计划" name="计划"
sequence="150" sequence="150"
action="sf_production_plan_action" action="sf_production_plan_action"
groups="sf_base.group_plan_dispatch"
/> />
<!-- <record model="ir.ui.menu" id="mrp_custom_menu" inherit_id="mrp.menu_mrp_manufacturing"> --> <!-- <record model="ir.ui.menu" id="mrp_custom_menu" inherit_id="mrp.menu_mrp_manufacturing"> -->

View File

@@ -23,6 +23,21 @@ class ProcedureEquipmentResourceSetting(models.Model):
create_time = fields.Datetime(string='新增时间', default=lambda self: fields.Datetime.now(), readonly=True) create_time = fields.Datetime(string='新增时间', default=lambda self: fields.Datetime.now(), readonly=True)
participate_in_scheduling = fields.Boolean(string='参与排程', default=True) participate_in_scheduling = fields.Boolean(string='参与排程', default=True)
# check_status = fields.Selection([('unchecked', '未审核'), ('checked', '已审核')], '审核状态', default='unchecked')
check_status = fields.Boolean(string='启用状态', default=False, readonly=True)
def action_check(self):
"""
审核启用
"""
self.check_status = True
def action_uncheck(self):
"""
审核禁用
"""
self.check_status = False
@api.depends('work_center_name_id') @api.depends('work_center_name_id')
def _compute_equipment_name_id(self): def _compute_equipment_name_id(self):
for record in self: for record in self:

View File

@@ -38,6 +38,19 @@ class WorkLogSetting(models.Model):
update_time = fields.Datetime(string='更新时间', default=lambda self: fields.Datetime.now()) update_time = fields.Datetime(string='更新时间', default=lambda self: fields.Datetime.now())
setting_to_calendar_ids = fields.One2many('sf.work.schedule.calendar', 'name_id', '工作日历') setting_to_calendar_ids = fields.One2many('sf.work.schedule.calendar', 'name_id', '工作日历')
check_status = fields.Boolean(string='启用状态', default=False, readonly=True)
def action_check(self):
"""
审核启用
"""
self.check_status = True
def action_uncheck(self):
"""
审核禁用
"""
self.check_status = False
@api.depends('working_shift_ids') @api.depends('working_shift_ids')
def _compute_working_shift_ids(self): def _compute_working_shift_ids(self):
@@ -157,13 +170,13 @@ class WorkLogSetting(models.Model):
:return: :return:
""" """
weekdays = { weekdays = {
'星期一': 'Monday', 'Monday': 'Monday', '星期一': 'Monday', 'Monday': 'Monday',
'星期二': 'Tuesday', 'Tuesday': 'Tuesday', '星期二': 'Tuesday', 'Tuesday': 'Tuesday',
'星期三': 'Wednesday', 'Wednesday': 'Wednesday', '星期三': 'Wednesday', 'Wednesday': 'Wednesday',
'星期四': 'Thursday', 'Thursday': 'Thursday', '星期四': 'Thursday', 'Thursday': 'Thursday',
'星期五': 'Friday', 'Friday': 'Friday', '星期五': 'Friday', 'Friday': 'Friday',
'星期六': 'Saturday', 'Saturday': 'Saturday', '星期六': 'Saturday', 'Saturday': 'Saturday',
'星期日': 'Sunday', 'Sunday': 'Sunday', '星期日': 'Sunday', 'Sunday': 'Sunday',
} }
english_weekdays = [] english_weekdays = []
for chinese_weekday in chinese_weekdays: for chinese_weekday in chinese_weekdays:
@@ -179,13 +192,13 @@ class WorkLogSetting(models.Model):
:return: :return:
""" """
weekdays = { weekdays = {
'星期一': 'Monday', 'Monday': 'Monday', '星期一': 'Monday', 'Monday': 'Monday',
'星期二': 'Tuesday', 'Tuesday': 'Tuesday', '星期二': 'Tuesday', 'Tuesday': 'Tuesday',
'星期三': 'Wednesday', 'Wednesday': 'Wednesday', '星期三': 'Wednesday', 'Wednesday': 'Wednesday',
'星期四': 'Thursday', 'Thursday': 'Thursday', '星期四': 'Thursday', 'Thursday': 'Thursday',
'星期五': 'Friday', 'Friday': 'Friday', '星期五': 'Friday', 'Friday': 'Friday',
'星期六': 'Saturday', 'Saturday': 'Saturday', '星期六': 'Saturday', 'Saturday': 'Saturday',
'星期日': 'Sunday', 'Sunday': 'Sunday', '星期日': 'Sunday', 'Sunday': 'Sunday',
} }
weekday = weekdays.get(chinese_weekday) weekday = weekdays.get(chinese_weekday)
if weekday: if weekday:
@@ -233,6 +246,19 @@ class WorkingShift(models.Model):
start_time = fields.Datetime('班次开始时间') start_time = fields.Datetime('班次开始时间')
end_time = fields.Datetime('班次结束时间') end_time = fields.Datetime('班次结束时间')
remark = fields.Char('备注') remark = fields.Char('备注')
check_status = fields.Boolean(string='启用状态', default=False, readonly=True)
def action_check(self):
"""
审核启用
"""
self.check_status = True
def action_uncheck(self):
"""
审核禁用
"""
self.check_status = False
class DayOff(models.Model): class DayOff(models.Model):
@@ -247,6 +273,19 @@ class DayOff(models.Model):
('Friday ', '星期五'), ('Friday ', '星期五'),
('Saturday ', '星期六'), ('Saturday ', '星期六'),
('Sunday ', '星期日')], '休息日名称') ('Sunday ', '星期日')], '休息日名称')
check_status = fields.Boolean(string='启用状态', default=False, readonly=True)
def action_check(self):
"""
审核启用
"""
self.check_status = True
def action_uncheck(self):
"""
审核禁用
"""
self.check_status = False
class WorkScheduleCalendar(models.Model): class WorkScheduleCalendar(models.Model):

View File

@@ -16,6 +16,10 @@
<field name="status"/> <field name="status"/>
<field name="update_person"/> <field name="update_person"/>
<field name="update_time"/> <field name="update_time"/>
<field name="check_status" widget="boolean_toggle"/>
<button name="action_check" string="启用" type="object" class="oe_highlight" attrs="{'invisible': [('check_status', '=', True)]}" groups="sf_base.group_plan_director"/>
<button name="action_uncheck" string="禁用" type="object" class="oe_highlight" attrs="{'invisible': [('check_status', '=', False)]}" groups="sf_base.group_plan_director"/>
<button string="查看日历" <button string="查看日历"
type="object" type="object"
name="open_work_schedule_calendar" name="open_work_schedule_calendar"
@@ -46,6 +50,8 @@
</div> </div>
<group> <group>
<group> <group>
<field name="check_status" widget="boolean_toggle"/>
<field name="code"/> <field name="code"/>
<field name="day_off_ids" widget="many2many_tags" placeholder="请选择" <field name="day_off_ids" widget="many2many_tags" placeholder="请选择"
options="{'no_create': True, 'no_quick_create': True}"/> options="{'no_create': True, 'no_quick_create': True}"/>
@@ -82,6 +88,9 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="休息日" editable="bottom" delete="0"> <tree string="休息日" editable="bottom" delete="0">
<field name="name"/> <field name="name"/>
<field name="check_status" widget="boolean_toggle"/>
<button name="action_check" string="启用" type="object" class="oe_highlight" attrs="{'invisible': [('check_status', '=', True)]}" groups="sf_base.group_plan_director"/>
<button name="action_uncheck" string="禁用" type="object" class="oe_highlight" attrs="{'invisible': [('check_status', '=', False)]}" groups="sf_base.group_plan_director"/>
</tree> </tree>
</field> </field>
</record> </record>
@@ -104,6 +113,9 @@
<field name="start_time" placeholder="请选择"/> <field name="start_time" placeholder="请选择"/>
<field name="end_time" placeholder="请选择"/> <field name="end_time" placeholder="请选择"/>
<field name="remark"/> <field name="remark"/>
<field name="check_status" widget="boolean_toggle"/>
<button name="action_check" string="启用" type="object" class="oe_highlight" attrs="{'invisible': [('check_status', '=', True)]}" groups="sf_base.group_plan_director"/>
<button name="action_uncheck" string="禁用" type="object" class="oe_highlight" attrs="{'invisible': [('check_status', '=', False)]}" groups="sf_base.group_plan_director"/>
</tree> </tree>
</field> </field>
</record> </record>
@@ -221,6 +233,10 @@
<field name="status"/> <field name="status"/>
<field name="participate_in_scheduling"/> <field name="participate_in_scheduling"/>
<field name="name" invisible="True"/> <field name="name" invisible="True"/>
<field name="check_status" widget="boolean_toggle"/>
<button name="action_check" string="启用" type="object" class="oe_highlight" attrs="{'invisible': [('check_status', '=', True)]}" groups="sf_base.group_plan_director"/>
<button name="action_uncheck" string="禁用" type="object" class="oe_highlight" attrs="{'invisible': [('check_status', '=', False)]}" groups="sf_base.group_plan_director"/>
</tree> </tree>
</field> </field>
</record> </record>
@@ -230,6 +246,10 @@
<field name="model">sf.procedure.equipment.resource.setting</field> <field name="model">sf.procedure.equipment.resource.setting</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="产线设备资源设置"> <form string="产线设备资源设置">
<header>
<button name="action_check" string="启用" type="object" class="oe_highlight" attrs="{'invisible': [('check_status', '=', True)]}" groups="sf_base.group_plan_director"/>
<button name="action_uncheck" string="禁用" type="object" class="oe_highlight" attrs="{'invisible': [('check_status', '=', False)]}" groups="sf_base.group_plan_director"/>
</header>
<sheet> <sheet>
<div> <div>
<h1> <h1>
@@ -238,6 +258,8 @@
</div> </div>
<group> <group>
<group> <group>
<field name="check_status" widget="boolean_toggle"/>
<field name="work_center_name_id" placeholder="请选择"/> <field name="work_center_name_id" placeholder="请选择"/>
<field name="production_capacity"/> <field name="production_capacity"/>
<field name="working_calendar_id" placeholder="请选择"/> <field name="working_calendar_id" placeholder="请选择"/>

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from odoo import models, fields, api, _ from odoo import models, fields
class SfQualityPoint(models.Model): class SfQualityPoint(models.Model):

View File

@@ -1,24 +1,24 @@
<odoo> <odoo>
<data noupdate="1"> <data noupdate="1">
<!-- <record model="ir.rule" id="crm_team_rule_salemanager">--> <record model="ir.rule" id="crm_team_rule_salemanager">
<!-- <field name="name">销售经理只可以查看本人所在的团队</field>--> <field name="name">销售经理只可以查看本人所在的团队</field>
<!-- <field name="model_id" ref="model_crm_team"/>--> <field name="model_id" ref="sales_team.model_crm_team"/>
<!-- <field name="domain_force">[('crm_team_member_ids.user_id','=',user.id)]</field>--> <field name="domain_force">[('crm_team_member_ids.user_id','=',user.id)]</field>
<!-- <field name="groups" eval="[(4, ref('sf_base.group_sale_salemanager'))]"/>--> <field name="groups" eval="[(4, ref('sf_base.group_sale_salemanager'))]"/>
<!-- <field name="perm_read" eval="1"/>--> <field name="perm_read" eval="1"/>
<!-- <field name="perm_write" eval="0"/>--> <field name="perm_write" eval="0"/>
<!-- <field name="perm_create" eval="0"/>--> <field name="perm_create" eval="0"/>
<!-- </record>--> </record>
<!-- <record model="ir.rule" id="crm_team_rule_director">--> <record model="ir.rule" id="crm_team_rule_director">
<!-- <field name="name">销售总监</field>--> <field name="name">销售总监</field>
<!-- <field name="model_id" ref="model_crm_team"/>--> <field name="model_id" ref="sales_team.model_crm_team"/>
<!-- <field name="domain_force">[(1,'=',1)]</field>--> <field name="domain_force">[(1,'=',1)]</field>
<!-- <field name="groups" eval="[(4, ref('sf_base.group_sale_director'))]"/>--> <field name="groups" eval="[(4, ref('sf_base.group_sale_director'))]"/>
<!-- <field name="perm_read" eval="1"/>--> <field name="perm_read" eval="1"/>
<!-- <field name="perm_write" eval="1"/>--> <field name="perm_write" eval="1"/>
<!-- <field name="perm_create" eval="1"/>--> <field name="perm_create" eval="1"/>
<!-- </record>--> </record>
<!-- <record model="ir.rule" id="sale_order_rule_director">--> <!-- <record model="ir.rule" id="sale_order_rule_director">-->
<!-- <field name="name">销售总监查看所有的订单</field>--> <!-- <field name="name">销售总监查看所有的订单</field>-->

View File

@@ -10,12 +10,10 @@
""", """,
'category': 'sf', 'category': 'sf',
'website': 'https://www.sf.jikimo.com', 'website': 'https://www.sf.jikimo.com',
'depends': ['sf_base', 'sf_manufacturing'], 'depends': ['sf_manufacturing'],
'data': [ 'data': [
'security/group_security.xml', 'security/group_security.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
# 'views/tool_base_views.xml',
# # 'views/menu_view.xml',
'wizard/wizard_view.xml', 'wizard/wizard_view.xml',
'views/tool_base_views.xml', 'views/tool_base_views.xml',
'views/menu_view.xml', 'views/menu_view.xml',

View File

@@ -6,11 +6,6 @@ from odoo import SUPERUSER_ID
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
# class FunctionalCuttingToolEntity(models.Model):
# _name = 'sf.functional.cutting.tool.entity'
# _inherit = 'sf.functional.cutting.tool'
# _inherits = {'sf.functional.cutting.tool': 'sf_functional_cutting_tool_entity_id'}
# _description = '功能刀具管理'
class FunctionalCuttingToolEntity(models.Model): class FunctionalCuttingToolEntity(models.Model):
_name = 'sf.functional.cutting.tool.entity' _name = 'sf.functional.cutting.tool.entity'
_description = '功能刀具列表' _description = '功能刀具列表'
@@ -30,7 +25,7 @@ class FunctionalCuttingToolEntity(models.Model):
tool_loading_length = fields.Float(string='装刀长(mm)', readonly=True) tool_loading_length = fields.Float(string='装刀长(mm)', readonly=True)
functional_tool_length = fields.Float(string='伸出长(mm)', readonly=True) functional_tool_length = fields.Float(string='伸出长(mm)', readonly=True)
effective_length = fields.Float(string='有效长(mm)', readonly=True) effective_length = fields.Float(string='有效长(mm)', readonly=True)
tool_room_num = fields.Integer(string='刀具房数量', readonly=True, ) tool_room_num = fields.Integer(string='刀具房数量', readonly=True)
line_edge_knife_library_num = fields.Integer(string='线边刀库数量', readonly=True) line_edge_knife_library_num = fields.Integer(string='线边刀库数量', readonly=True)
machine_knife_library_num = fields.Integer(string='机内刀库数量', readonly=True) machine_knife_library_num = fields.Integer(string='机内刀库数量', readonly=True)
max_lifetime_value = fields.Integer(string='最大寿命值(min)', readonly=True) max_lifetime_value = fields.Integer(string='最大寿命值(min)', readonly=True)
@@ -41,16 +36,18 @@ class FunctionalCuttingToolEntity(models.Model):
current_location_id = fields.Many2one('stock.location', string='当前位置', readonly=True) current_location_id = fields.Many2one('stock.location', string='当前位置', readonly=True)
image = fields.Binary('图片', readonly=True) image = fields.Binary('图片', readonly=True)
@api.depends('current_location_id') # @api.depends('current_location_id')
def _compute_location_num(self): # def _compute_location_num(self):
""" # """
计算库存位置数量 # 计算库存位置数量
""" # """
for obj in self: # for obj in self:
if obj.current_location_id.name in ('组装后', '刀具房'): # if obj.current_location_id:
obj.tool_room_num = 1 # if obj.current_location_id.name in ('组装后', '刀具房'):
obj.line_edge_knife_library_num = 0 # obj.tool_room_num = 1
obj.machine_knife_library_num = 0 # obj.line_edge_knife_library_num = 0
# obj.machine_knife_library_num = 0
#
@api.model @api.model
def _read_group_mrs_cutting_tool_type_id(self, categories, domain, order): def _read_group_mrs_cutting_tool_type_id(self, categories, domain, order):
@@ -86,12 +83,12 @@ class FunctionalCuttingToolEntity(models.Model):
suitable_machining_method_ids = fields.Many2many( suitable_machining_method_ids = fields.Many2many(
'maintenance.equipment.image', 'rel_machining_product_template_tool_entity', '适合加工方式', 'maintenance.equipment.image', 'rel_machining_product_template_tool_entity', '适合加工方式',
domain=[('type', '=', '加工能力')], related='cutting_tool_integral_model_id.suitable_machining_method_ids') domain=[('type', '=', '加工能力')], related='cutting_tool_integral_model_id.suitable_machining_method_ids')
blade_tip_characteristics_id = fields.Many2many( blade_tip_characteristics_id = fields.Many2one(
'maintenance.equipment.image', 'rel_blade_tip_product_template_tool_entity', '刀尖特征', 'maintenance.equipment.image', '刀尖特征',
domain=[('type', '=', '刀尖特征')], related='cutting_tool_integral_model_id.blade_tip_characteristics_id') domain=[('type', '=', '刀尖特征')], related='cutting_tool_integral_model_id.blade_tip_characteristics_id')
handle_type_ids = fields.Many2many( handle_type_id = fields.Many2one(
'maintenance.equipment.image', 'rel_handle_product_template_tool_entity', '柄部类型', 'maintenance.equipment.image', '柄部类型',
domain=[('type', '=', '柄部类型')], related='cutting_tool_integral_model_id.handle_type_ids') domain=[('type', '=', '柄部类型')], related='cutting_tool_integral_model_id.handle_type_id')
cutting_direction_ids = fields.Many2many( cutting_direction_ids = fields.Many2many(
'maintenance.equipment.image', 'rel_cutting_product_template_tool_entity', '走刀方向', 'maintenance.equipment.image', 'rel_cutting_product_template_tool_entity', '走刀方向',
domain=[('type', '=', '走刀方向')], related='cutting_tool_integral_model_id.cutting_direction_ids') domain=[('type', '=', '走刀方向')], related='cutting_tool_integral_model_id.cutting_direction_ids')
@@ -118,6 +115,14 @@ class FunctionalCuttingToolEntity(models.Model):
result['domain'] = [('lot_id', '=', self.barcode_id.id), ('qty_done', '>', 0)] result['domain'] = [('lot_id', '=', self.barcode_id.id), ('qty_done', '>', 0)]
return result return result
def open_safety_stock(self):
action = self.env.ref('sf_tool_management.sf_real_time_distribution_of_functional_tools_view_act')
result = action.read()[0]
result['domain'] = [('name', '=', self.name), ('diameter', '=', self.functional_tool_diameter),
('knife_tip_r_angle', '=', self.knife_tip_r_angle),
('coarse_middle_thin', '=', self.coarse_middle_thin)]
return result
class FunctionalToolWarning(models.Model): class FunctionalToolWarning(models.Model):
_name = 'sf.functional.tool.warning' _name = 'sf.functional.tool.warning'
@@ -178,113 +183,80 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
_name = 'sf.real.time.distribution.of.functional.tools' _name = 'sf.real.time.distribution.of.functional.tools'
_description = '功能刀具安全库存' _description = '功能刀具安全库存'
functional_cutting_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', readonly=True) name = fields.Char('功能刀具名称')
mrs_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', readonly=True, sf_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型',
group_expand='_read_mrs_cutting_tool_type_ids', group_expand='_read_mrs_cutting_tool_type_ids', store=True)
store=True, diameter = fields.Integer(string='刀具直径(mm)')
compute='_compute_functional_cutting_tool_ids') knife_tip_r_angle = fields.Float(string='刀尖R角(mm)')
tool_stock_num = fields.Integer(string='刀具房数量')
side_shelf_num = fields.Integer(string='线边刀库数量')
on_tool_stock_num = fields.Integer(string='机内刀库数量')
tool_stock_total = fields.Integer(string='当前库存量', readonly=True, compute='_compute_tool_stock_total')
min_stock_num = fields.Integer('最低库存量')
max_stock_num = fields.Integer('最高库存量')
batch_replenishment_num = fields.Integer('批次补货量')
unit = fields.Char('单位')
image = fields.Binary('图片')
coarse_middle_thin = fields.Selection([("1", ""), ('2', ''), ('3', '')], string='粗/中/精')
whether_standard_knife = fields.Boolean(string='是否标准刀', default=True)
# 能力特征信息
suitable_machining_method_ids = fields.Many2many(
'maintenance.equipment.image', 'rel_machining_product_template_distribution', '适合加工方式',
domain=[('type', '=', '加工能力')],
related='sf_functional_cutting_tool_entity_ids.suitable_machining_method_ids')
blade_tip_characteristics_id = fields.Many2one(
'maintenance.equipment.image', '刀尖特征',
domain=[('type', '=', '刀尖特征')],
related='sf_functional_cutting_tool_entity_ids.blade_tip_characteristics_id')
handle_type_id = fields.Many2one(
'maintenance.equipment.image', '柄部类型',
domain=[('type', '=', '柄部类型')], related='sf_functional_cutting_tool_entity_ids.handle_type_id')
cutting_direction_ids = fields.Many2many(
'maintenance.equipment.image', 'rel_cutting_product_template_distribution', '走刀方向',
domain=[('type', '=', '走刀方向')], related='sf_functional_cutting_tool_entity_ids.cutting_direction_ids')
suitable_coolant_ids = fields.Many2many(
'maintenance.equipment.image', 'rel_coolant_product_template_distribution', '适合冷却液',
domain=[('type', '=', '冷却液')], related='sf_functional_cutting_tool_entity_ids.suitable_coolant_ids')
sf_functional_cutting_tool_entity_ids = fields.Many2many('sf.functional.cutting.tool.entity',
'sf_functional_cutting_tool_entity_ref',
string='功能刀具列表信息', readonly=True)
@api.model @api.model
def _read_mrs_cutting_tool_type_ids(self, categories, domain, order): def _read_mrs_cutting_tool_type_ids(self, categories, domain, order):
mrs_cutting_tool_type_ids = categories._search([], order=order, access_rights_uid=SUPERUSER_ID) mrs_cutting_tool_type_ids = categories._search([], order=order, access_rights_uid=SUPERUSER_ID)
return categories.browse(mrs_cutting_tool_type_ids) return categories.browse(mrs_cutting_tool_type_ids)
@api.depends('functional_cutting_tool_id')
def _compute_functional_cutting_tool_ids(self):
for record in self:
if record:
record.mrs_cutting_tool_type_id = record.functional_cutting_tool_id.mrs_cutting_tool_type_id.id
barcode_id = fields.Many2one('stock.lot', string='功能刀具序列号', readonly=True)
name = fields.Char('名称', invisible=True, readonly=True)
functional_tool_name_id = fields.Many2one('product.product', string='功能刀具名称', readonly=True)
# 整体式刀具型号
cutting_tool_integral_model_id = fields.Many2one(
'product.product', string='整体式刀具型号', readonly=True,
domain=[('cutting_tool_material_id', '=', '整体式刀具')],
related='functional_cutting_tool_id.cutting_tool_integral_model_id')
# 刀片型号
cutting_tool_blade_model_id = fields.Many2one(
'product.product', string='刀片型号', readonly=True,
domain=[('cutting_tool_material_id', '=', '刀片')],
related='functional_cutting_tool_id.cutting_tool_blade_model_id')
# 刀杆型号
cutting_tool_cutterbar_model_id = fields.Many2one(
'product.product', string='刀杆型号', readonly=True,
domain=[('cutting_tool_material_id', '=', '刀杆')],
related='functional_cutting_tool_id.cutting_tool_cutterbar_model_id')
# 刀盘型号
cutting_tool_cutterpad_model_id = fields.Many2one(
'product.product', string='刀盘型号', readonly=True,
domain=[('cutting_tool_material_id', '=', '刀盘')],
related='functional_cutting_tool_id.cutting_tool_cutterpad_model_id')
# 刀柄型号
cutting_tool_cutterhandle_model_id = fields.Many2one(
'product.product', string='刀柄型号', readonly=True,
domain=[('cutting_tool_material_id', '=', '刀柄')],
related='functional_cutting_tool_id.cutting_tool_cutterhandle_model_id')
# 夹头型号
cutting_tool_cutterhead_model_id = fields.Many2one(
'product.product', string='夹头型号', readonly=True,
domain=[('cutting_tool_material_id', '=', '夹头')],
related='functional_cutting_tool_id.cutting_tool_cutterhead_model_id')
diameter = fields.Float('直径(mm)', readonly=True)
tool_grade = fields.Selection([('1', 'P1'), ('2', 'P2'), ('3', 'P3'), ('4', 'P4'), ('5', 'P5'), ('6', 'P6')],
string='刀具等级', readonly=True)
machining_accuracy = fields.Float('加工精度(mm)', readonly=True)
tool_length = fields.Float('装刀长(mm)', readonly=True)
blade_number = fields.Integer('刃数', readonly=True)
integral_blade_length = fields.Float('整体刃长(mm)', readonly=True)
effective_blade_length = fields.Float('有效刃长(mm)', readonly=True)
max_life = fields.Float('最大寿命值', readonly=True)
is_standard = fields.Selection([('1', ''), ('0', '')], '是否标准刀', readonly=True,
compute='_compute_functional_cutting_tool_id')
applicable_range = fields.Char('适用范围', readonly=True)
image = fields.Binary('图片', readonly=True)
@api.depends('functional_cutting_tool_id')
def _compute_functional_cutting_tool_id(self):
if self.functional_cutting_tool_id:
self.tool_grade = self.functional_cutting_tool_id.tool_grade
self.is_standard = self.functional_cutting_tool_id.is_standard
# 功能刀具实时分布
tool_stock_num = fields.Integer(string='刀具房库存数量', readonly=False)
side_shelf_num = fields.Integer(string='线边货架货架数量', readonly=False)
on_tool_stock_num = fields.Integer(string='机内刀库库存数量', readonly=False)
tool_stock_total = fields.Integer(string='合计(库存)', readonly=True, compute='_compute_tool_stock_total')
return_reuse_num_re = fields.Integer(string='归还再用数量(精)', readonly=False)
return_reuse_num_co = fields.Integer(string='归还再用数量(粗)', readonly=False)
return_processing_num = fields.Integer(string='归还需磨削数量', readonly=False)
return_total = fields.Integer(string='合计(归还)', readonly=True, compute='_compute_return_total')
total = fields.Integer(string='总计', readonly=True, compute='_compute_total')
remark = fields.Char(string='备注/说明', readonly=False)
@api.depends('tool_stock_num', 'side_shelf_num', 'on_tool_stock_num') @api.depends('tool_stock_num', 'side_shelf_num', 'on_tool_stock_num')
def _compute_tool_stock_total(self): def _compute_tool_stock_total(self):
for record in self: for record in self:
if record: if record:
self.tool_stock_total = record.tool_stock_num + record.side_shelf_num + record.on_tool_stock_num record.tool_stock_total = record.tool_stock_num + record.side_shelf_num + record.on_tool_stock_num
@api.depends('return_reuse_num_re', 'return_reuse_num_co', 'return_processing_num') # @api.depends('tool_stock_total', 'min_stock_num', 'max_stock_num')
def _compute_return_total(self): # def _compute_batch_replenishment_num(self):
for record in self: # for record in self:
if record: # if record.tool_stock_total < record.min_stock_num:
self.return_total = (record.return_reuse_num_re + record.return_reuse_num_co + # record.batch_replenishment_num = record.max_stock_num - record.tool_stock_total
record.return_processing_num)
@api.depends('tool_stock_total', 'return_total') def create_or_edit_safety_stock(self, vals, sf_functional_cutting_tool_entity_ids):
def _compute_total(self): """
for record in self: 根据传入的信息新增或者更新功能刀具安全库存的信息
if record: """
self.total = record.tool_stock_total + record.return_total print(vals)
# 根据功能刀具名称、直径或尖刀R角、粗/中/精查询该功能刀具是否已经存在
record = self.env['sf.real.time.distribution.of.functional.tools'].search(
[('name', '=', vals['name']), ('sf_cutting_tool_type_id', '=', vals['sf_cutting_tool_type_id']),
('diameter', '=', vals['diameter']), ('knife_tip_r_angle', '=', vals['knife_tip_r_angle']),
('coarse_middle_thin', '=', vals['coarse_middle_thin'])])
print(record)
if len(record) > 0:
for obj in record:
obj.write({'sf_functional_cutting_tool_entity_ids': [(4, sf_functional_cutting_tool_entity_ids.id)]})
else:
vals['sf_functional_cutting_tool_entity_ids'] = sf_functional_cutting_tool_entity_ids.ids
self.env['sf.real.time.distribution.of.functional.tools'].create(vals)
class MachineTableToolChangingApply(models.Model): class MachineTableToolChangingApply(models.Model):
@@ -293,15 +265,17 @@ class MachineTableToolChangingApply(models.Model):
_order = 'cutter_spacing_code_id' _order = 'cutter_spacing_code_id'
name = fields.Char('名称', related='maintenance_equipment_id.name', store=True) name = fields.Char('名称', related='maintenance_equipment_id.name', store=True)
maintenance_equipment_id = fields.Many2one('maintenance.equipment', string='CNC机床', required=True, readonly=False, # 设备信息
maintenance_equipment_id = fields.Many2one('maintenance.equipment', string='CNC机床', readonly=True,
domain=[('category_id.equipment_type', '=', '机床')]) domain=[('category_id.equipment_type', '=', '机床')])
production_line_id = fields.Many2one('sf.production.line', string='生产线', readonly=True, production_line_id = fields.Many2one('sf.production.line', string='生产线', readonly=True,
group_expand='_read_group_names') group_expand='_read_group_names')
machine_table_type_id = fields.Many2one('maintenance.equipment.category', string='机床类型', readonly=True, machine_table_type_id = fields.Many2one('maintenance.equipment.category', string='机床类型', readonly=True,
compute='_compute_machine_table_type_id') compute='_compute_machine_table_type_id')
machine_tool_code = fields.Char(string='机台号', store=True, invisible=True, readonly=True) machine_tool_code = fields.Char(string='机台号', store=True, invisible=True, readonly=True)
cutter_spacing_code_id = fields.Many2one('maintenance.equipment.tool', string='刀位号', readonly=False, cutter_spacing_code_id = fields.Many2one('maintenance.equipment.tool', string='刀位号', readonly=True,
required=True, domain="[('equipment_id', '=', maintenance_equipment_id)]") required=True, domain="[('equipment_id', '=', maintenance_equipment_id)]")
# 功能刀具信息
functional_tool_name = fields.Char(string='刀具名称', related='functional_tool_name_id.name', store=True) functional_tool_name = fields.Char(string='刀具名称', related='functional_tool_name_id.name', store=True)
barcode_id = fields.Many2one('stock.lot', string='功能刀具序列号', store=True, barcode_id = fields.Many2one('stock.lot', string='功能刀具序列号', store=True,
domain=[('product_id.name', '=', '功能刀具')], domain=[('product_id.name', '=', '功能刀具')],
@@ -313,7 +287,7 @@ class MachineTableToolChangingApply(models.Model):
tool_position_interface_type = fields.Selection( tool_position_interface_type = fields.Selection(
[('BT刀柄式', 'BT刀柄式'), ('SK刀柄式', 'SK刀柄式'), ('HSK刀柄式', 'HSK刀柄式'), [('BT刀柄式', 'BT刀柄式'), ('SK刀柄式', 'SK刀柄式'), ('HSK刀柄式', 'HSK刀柄式'),
('CAT刀柄式', 'CAT刀柄式'), ('ISO刀盘式', 'ISO刀盘式'), ('DIN刀盘式', 'DIN刀盘式'), ('CAT刀柄式', 'CAT刀柄式'), ('ISO刀盘式', 'ISO刀盘式'), ('DIN刀盘式', 'DIN刀盘式'),
('直装固定式', '直装固定式')], string='刀位接口型号', required=True) ('直装固定式', '直装固定式')], string='刀位接口型号')
diameter = fields.Integer(string='刀具直径(mm)', ) diameter = fields.Integer(string='刀具直径(mm)', )
knife_tip_r_angle = fields.Float(string='刀尖R角(mm)') knife_tip_r_angle = fields.Float(string='刀尖R角(mm)')
max_lifetime_value = fields.Integer(string='最大寿命值(min)') max_lifetime_value = fields.Integer(string='最大寿命值(min)')
@@ -339,7 +313,7 @@ class MachineTableToolChangingApply(models.Model):
@api.depends('alarm_value', 'used_value') @api.depends('alarm_value', 'used_value')
def _compute_functional_tool_status(self): def _compute_functional_tool_status(self):
for record in self: for record in self:
if record.alarm_value <= record.used_value: if record.alarm_value < record.used_value:
record.functional_tool_status = '报警' record.functional_tool_status = '报警'
else: else:
record.functional_tool_status = '正常' record.functional_tool_status = '正常'
@@ -397,7 +371,7 @@ class MachineTableToolChangingApply(models.Model):
'use_tool_time': fields.Datetime.now() + timedelta(hours=4), 'use_tool_time': fields.Datetime.now() + timedelta(hours=4),
'production_line_name_id': self.production_line_id.id, 'production_line_name_id': self.production_line_id.id,
'machine_tool_name_id': self.maintenance_equipment_id.id, 'machine_tool_name_id': self.maintenance_equipment_id.id,
'applicant': self.applicant, 'applicant': '系统自动',
'apply_time': fields.Datetime.now(), 'apply_time': fields.Datetime.now(),
'cutter_spacing_code_id': self.cutter_spacing_code_id.id, 'cutter_spacing_code_id': self.cutter_spacing_code_id.id,
'whether_standard_knife': self.whether_standard_knife, 'whether_standard_knife': self.whether_standard_knife,
@@ -407,20 +381,7 @@ class MachineTableToolChangingApply(models.Model):
machine_table_tool_changing_apply.write( machine_table_tool_changing_apply.write(
{'status': '1', {'status': '1',
'sf_functional_tool_assembly_id': sf_functional_tool_assembly}) 'sf_functional_tool_assembly_id': sf_functional_tool_assembly.id})
# def new_assembly_task(self, vals):
# """
# 新建组装任务
# :param vals:
# :return:
# """
# # 增加设置直径的值
# tool_changing_apply = self.env['sf.machine.table.tool.changing.apply'].search(
# [('name', '=', vals['name'])])
# vals['functional_tool_diameter'] = tool_changing_apply.diameter
#
# self.env['sf.functional.tool.assembly'].create(vals)
def revocation_1(self): def revocation_1(self):
""" """
@@ -538,7 +499,7 @@ class CAMWorkOrderProgramKnifePlan(models.Model):
class FunctionalToolAssembly(models.Model): class FunctionalToolAssembly(models.Model):
_name = 'sf.functional.tool.assembly' _name = 'sf.functional.tool.assembly'
_description = '功能刀具组装' _description = '功能刀具组装'
_order = 'use_tool_time asc' _order = 'use_tool_time asc'
@api.depends('functional_tool_name') @api.depends('functional_tool_name')
@@ -732,6 +693,20 @@ class FunctionalToolAssembly(models.Model):
code = False code = False
return code return code
def get_functional_tool(self, val):
functional_tools = self.env['sf.functional.tool.assembly'].search(
[('after_assembly_functional_tool_name', '=', val.get('after_assembly_functional_tool_name')),
('after_assembly_functional_tool_diameter', '=', val.get('after_assembly_functional_tool_diameter')),
('after_assembly_knife_tip_r_angle', '=', val.get('after_assembly_knife_tip_r_angle')),
('after_assembly_coarse_middle_thin', '=', val.get('after_assembly_coarse_middle_thin'))])
for functional_tool in functional_tools:
if functional_tool.barcode_id.quant_ids[-1].location_id.name == '刀具线边库':
return functional_tool
for functional_tool in functional_tools:
if functional_tool.barcode_id.quant_ids[-1].location_id.name == '刀具房':
return functional_tool
return False
def automated_assembly(self): def automated_assembly(self):
""" """
todo 自动组装 todo 自动组装

View File

@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from odoo import fields, models, api, SUPERUSER_ID from odoo import fields, models, api, SUPERUSER_ID
from odoo.exceptions import ValidationError
# from odoo.exceptions import ValidationError
# 刀具物料搜索 # 刀具物料搜索
@@ -99,12 +101,9 @@ class SfToolMaterialSearch(models.Model):
suitable_machining_method_ids = fields.Many2many('maintenance.equipment.image', suitable_machining_method_ids = fields.Many2many('maintenance.equipment.image',
'rel_machining_product_template_material_search', '适合加工方式', 'rel_machining_product_template_material_search', '适合加工方式',
domain=[('type', '=', '加工能力')]) domain=[('type', '=', '加工能力')])
blade_tip_characteristics_id = fields.Many2many('maintenance.equipment.image', blade_tip_characteristics_id = fields.Many2one('maintenance.equipment.image', '刀尖特征',
'rel_blade_tip_product_template_material_search', '刀尖特征', domain=[('type', '=', '刀尖特征')])
domain=[('type', '=', '刀尖特征')]) handle_type_id = fields.Many2one('maintenance.equipment.image', '柄部类型', domain=[('type', '=', '柄部类型')])
handle_type_ids = fields.Many2many('maintenance.equipment.image',
'rel_handle_product_template_material_search', '柄部类型',
domain=[('type', '=', '柄部类型')])
cutting_direction_ids = fields.Many2many('maintenance.equipment.image', cutting_direction_ids = fields.Many2many('maintenance.equipment.image',
'rel_cutting_product_template_material_search', '走刀方向', 'rel_cutting_product_template_material_search', '走刀方向',
domain=[('type', '=', '走刀方向')]) domain=[('type', '=', '走刀方向')])

View File

@@ -61,6 +61,16 @@
</span> </span>
</div> </div>
</button> </button>
<button class="oe_stat_button"
name="open_safety_stock"
icon="fa-list-ul"
type="object">
<div class="o_field_widget o_stat_info">
<span>
安全库存
</span>
</div>
</button>
</div> </div>
<div class="oe_title"> <div class="oe_title">
<h1> <h1>
@@ -106,12 +116,12 @@
<group> <group>
<group string="刀尖特征"> <group string="刀尖特征">
<field name="blade_tip_characteristics_id" string="" <field name="blade_tip_characteristics_id" string=""
widget="custom_many2many_checkboxes" widget="many2one_radio"
domain="[('id','in',blade_tip_characteristics_id)]"/> domain="[('id','in',blade_tip_characteristics_id)]"/>
</group> </group>
<group string="柄部类型"> <group string="柄部类型">
<field name="handle_type_ids" string="" widget="custom_many2many_checkboxes" <field name="handle_type_id" string="" widget="many2one_radio"
domain="[('id','in',handle_type_ids)]"/> domain="[('id','in',handle_type_id)]"/>
</group> </group>
</group> </group>
<group> <group>
@@ -264,126 +274,100 @@
</record> </record>
<!-- =====================================功能刀具实时分布============================================================= --> <!-- =====================================功能刀具安全库存=================================================== -->
<record id="sf_real_time_distribution_of_functional_tools_view_tree" model="ir.ui.view"> <record id="sf_real_time_distribution_of_functional_tools_view_tree" model="ir.ui.view">
<field name="name">功能刀具实时分布</field> <field name="name">功能刀具安全库存</field>
<field name="model">sf.real.time.distribution.of.functional.tools</field> <field name="model">sf.real.time.distribution.of.functional.tools</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree create="0" edit="0" delete="0"> <tree create="0" edit="0" delete="0">
<field name="barcode_id"/> <field name="name"/>
<field name="functional_tool_name_id"/> <field name="sf_cutting_tool_type_id" invisible="True"/>
<field name="mrs_cutting_tool_type_id"/> <field name="diameter"/>
<field name="cutting_tool_integral_model_id" optional="hide"/> <field name="knife_tip_r_angle"/>
<field name="cutting_tool_blade_model_id" optional="hide"/> <field name="coarse_middle_thin"/>
<field name="cutting_tool_cutterbar_model_id" optional="hide"/>
<field name="cutting_tool_cutterpad_model_id" optional="hide"/>
<field name="cutting_tool_cutterhandle_model_id" optional="hide"/>
<field name="cutting_tool_cutterhead_model_id" optional="hide"/>
<field name="tool_stock_num"/> <field name="tool_stock_num"/>
<field name="side_shelf_num"/> <field name="side_shelf_num"/>
<field name="on_tool_stock_num"/> <field name="on_tool_stock_num"/>
<field name="tool_stock_total"/> <field name="tool_stock_total"/>
<field name="return_reuse_num_re" optional="hide"/> <field name="min_stock_num"/>
<field name="return_reuse_num_co" optional="hide"/> <field name="max_stock_num"/>
<field name="return_processing_num" optional="hide"/> <field name="batch_replenishment_num"/>
<field name="return_total" optional="hide"/> <field name="unit"/>
<field name="total" optional="hide"/>
<field name="remark"/>
</tree> </tree>
</field> </field>
</record> </record>
<record id="sf_real_time_distribution_of_functional_tools_view_form" model="ir.ui.view"> <record id="sf_real_time_distribution_of_functional_tools_view_form" model="ir.ui.view">
<field name="name">功能刀具实时分布</field> <field name="name">功能刀具安全库存</field>
<field name="model">sf.real.time.distribution.of.functional.tools</field> <field name="model">sf.real.time.distribution.of.functional.tools</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form create="0" edit="0" delete="0"> <form create="0" edit="1" delete="0">
<sheet> <sheet>
<div class="oe_title"> <div class="oe_title">
<h1> <h1>
<field name="barcode_id" readonly="1"/> <field name="name"/>
</h1> </h1>
</div> </div>
<group> <group>
<group> <group>
<field name="functional_tool_name_id" invisible="False"/> <field name="sf_cutting_tool_type_id" readonly="1"/>
<field name="mrs_cutting_tool_type_id"/> <field name="diameter" readonly="1"/>
<field name="knife_tip_r_angle" readonly="1"/>
<field name="cutting_tool_integral_model_id" <field name="coarse_middle_thin" readonly="1"/>
options="{'no_create': True, 'no_quick_create': True}" <field name="whether_standard_knife" readonly="1"/>
attrs="{'invisible': [('cutting_tool_blade_model_id', '!=', False)]}" <field name="min_stock_num"/>
/> <field name="max_stock_num"/>
<field name="cutting_tool_blade_model_id" <field name="batch_replenishment_num"/>
options="{'no_create': True, 'no_quick_create': True}"
attrs="{'invisible': [('cutting_tool_integral_model_id', '!=', False)]}"
/>
<field name="cutting_tool_cutterbar_model_id"
options="{'no_create': True, 'no_quick_create': True}"
attrs="{'invisible': ['|',('cutting_tool_cutterpad_model_id','!=',False),('cutting_tool_blade_model_id', '=', False)]}"
/>
<field name="cutting_tool_cutterpad_model_id"
options="{'no_create': True, 'no_quick_create': True}"
attrs="{'invisible': ['|',('cutting_tool_cutterbar_model_id','!=',False),('cutting_tool_blade_model_id', '=', False)]}"
/>
<field name="cutting_tool_cutterhandle_model_id"
options="{'no_create': True, 'no_quick_create': True}"/>
<field name="cutting_tool_cutterhead_model_id"
options="{'no_create': True, 'no_quick_create': True}"/>
</group> </group>
<group> <group>
<field name="image" nolabel="1" widget="image"/> <field name="image" widget='image' readonly="1"/>
</group>
</group>
<group col="1">
<group string="适合加工方式">
<field name="suitable_machining_method_ids" string=""
widget="custom_many2many_checkboxes" readonly="1"
domain="[('id','in',suitable_machining_method_ids)]"/>
</group>
<group>
<group string="刀尖特征">
<field name="blade_tip_characteristics_id" string=""
widget="many2one_radio" readonly="1"
domain="[('id','in',blade_tip_characteristics_id)]"/>
</group>
<group string="柄部类型">
<field name="handle_type_id" string=""
widget="many2one_radio" readonly="1"
domain="[('id','in',handle_type_id)]"/>
</group>
</group>
<group>
<group string="走刀方向">
<field name="cutting_direction_ids" string="" widget="custom_many2many_checkboxes" readonly="1"
domain="[('id','in',cutting_direction_ids)]"/>
</group>
<group string="适合冷却液">
<field name="suitable_coolant_ids" string="" widget="custom_many2many_checkboxes" readonly="1"
domain="[('id','in',suitable_coolant_ids)]"/>
</group>
</group> </group>
</group> </group>
<notebook> <notebook>
<page string="实时分布信息"> <page string="刀具信息">
<group> <field name="sf_functional_cutting_tool_entity_ids" widget="many2many">
<group> <tree edit="0" create="0" delete="0">
<group> <field name="barcode_id"/>
<field name="tool_stock_num"/> <field name="functional_tool_name_id"/>
<field name="return_reuse_num_re"/> <field name="new_former"/>
</group> <field name="tool_loading_length"/>
<group> <field name="functional_tool_length"/>
<field name="side_shelf_num"/> <field name="effective_length"/>
<field name="return_reuse_num_co"/> <field name="max_lifetime_value"/>
</group> <field name="alarm_value"/>
</group> <field name="used_value"/>
<group> <field name="functional_tool_status"/>
<group> </tree>
<field name="on_tool_stock_num"/> </field>
<field name="return_processing_num"/>
</group>
<group>
<field name="tool_stock_total"/>
<field name="return_total"/>
<field name="total"/>
</group>
</group>
</group>
</page>
<page string="参数信息">
<group>
<group>
<field name="diameter"/>
<field name="tool_grade"/>
<field name="machining_accuracy"/>
<field name="tool_length"/>
<field name="blade_number"/>
</group>
<group>
<field name="integral_blade_length"/>
<field name="effective_blade_length"/>
<field name="max_life"/>
<field name="is_standard" default="false"/>
<field name="applicable_range"/>
</group>
</group>
</page>
<page string="其他信息">
<group>
<field name="remark"/>
</group>
</page> </page>
</notebook> </notebook>
</sheet> </sheet>
@@ -392,28 +376,31 @@
</record> </record>
<record id="sf_real_time_distribution_of_functional_tools_view_search" model="ir.ui.view"> <record id="sf_real_time_distribution_of_functional_tools_view_search" model="ir.ui.view">
<field name="name">功能刀具实时分布</field> <field name="name">功能刀具安全库存</field>
<field name="model">sf.real.time.distribution.of.functional.tools</field> <field name="model">sf.real.time.distribution.of.functional.tools</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search> <search>
<field name="barcode_id"/> <field name="name"/>
<field name="functional_tool_name_id"/> <field name="sf_cutting_tool_type_id" invisible="True"/>
<field name="mrs_cutting_tool_type_id"/> <field name="diameter"/>
<field name="cutting_tool_integral_model_id" optional="hide"/> <field name="knife_tip_r_angle"/>
<field name="cutting_tool_blade_model_id" optional="hide"/> <field name="tool_stock_num"/>
<field name="cutting_tool_cutterbar_model_id" optional="hide"/> <field name="side_shelf_num"/>
<field name="cutting_tool_cutterpad_model_id" optional="hide"/> <field name="on_tool_stock_num"/>
<field name="cutting_tool_cutterhandle_model_id" optional="hide"/> <field name="tool_stock_total"/>
<field name="cutting_tool_cutterhead_model_id" optional="hide"/> <field name="min_stock_num"/>
<field name="max_stock_num"/>
<field name="batch_replenishment_num"/>
<field name="unit"/>
<searchpanel> <searchpanel>
<field name="mrs_cutting_tool_type_id" enable_counters="1" icon="fa-building"/> <field name="sf_cutting_tool_type_id" enable_counters="1" icon="fa-building"/>
</searchpanel> </searchpanel>
</search> </search>
</field> </field>
</record> </record>
<record id="sf_real_time_distribution_of_functional_tools_view_act" model="ir.actions.act_window"> <record id="sf_real_time_distribution_of_functional_tools_view_act" model="ir.actions.act_window">
<field name="name">功能刀具实时分布</field> <field name="name">功能刀具安全库存</field>
<field name="type">ir.actions.act_window</field> <field name="type">ir.actions.act_window</field>
<field name="res_model">sf.real.time.distribution.of.functional.tools</field> <field name="res_model">sf.real.time.distribution.of.functional.tools</field>
<field name="view_mode">tree,form,search</field> <field name="view_mode">tree,form,search</field>
@@ -484,7 +471,7 @@
<field name="name">机床换刀申请</field> <field name="name">机床换刀申请</field>
<field name="model">sf.machine.table.tool.changing.apply</field> <field name="model">sf.machine.table.tool.changing.apply</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree> <tree create="0" delete="0">
<field name="name" invisible="1"/> <field name="name" invisible="1"/>
<field name="production_line_id" invisible="1"/> <field name="production_line_id" invisible="1"/>
<field name="maintenance_equipment_id"/> <field name="maintenance_equipment_id"/>
@@ -650,15 +637,15 @@
<sheet> <sheet>
<div class="oe_title"> <div class="oe_title">
<h1> <h1>
<field name="maintenance_equipment_id" readonly="0" placeholder="请选择"/> <field name="maintenance_equipment_id" readonly="1" placeholder="请选择"/>
</h1> </h1>
</div> </div>
<group> <group>
<group> <group>
<field name="production_line_id"/> <field name="production_line_id"/>
<field name="machine_table_type_id"/> <field name="machine_table_type_id"/>
<field name="cutter_spacing_code_id"/> <field name="cutter_spacing_code_id" options="{'no_create': True}"/>
<field name="tool_position_interface_type" placeholder="请选择"/> <field name="tool_position_interface_type" placeholder="请选择" required="1"/>
<field name="sf_functional_tool_assembly_id" string="组装单"/> <field name="sf_functional_tool_assembly_id" string="组装单"/>
</group> </group>
</group> </group>
@@ -714,6 +701,7 @@
<field name="applicant" optional="hide"/> <field name="applicant" optional="hide"/>
<searchpanel> <searchpanel>
<field name="production_line_id" enable_counters="1" icon="fa-building"/> <field name="production_line_id" enable_counters="1" icon="fa-building"/>
<field name="maintenance_equipment_id" enable_counters="1" icon="fa-building"/>
<field name="functional_tool_status" enable_counters="1" icon="fa-building"/> <field name="functional_tool_status" enable_counters="1" icon="fa-building"/>
</searchpanel> </searchpanel>
</search> </search>

View File

@@ -231,10 +231,10 @@
</group> </group>
<group> <group>
<group string="刀尖特征"> <group string="刀尖特征">
<field name="blade_tip_characteristics_id" string="" widget="custom_many2many_checkboxes"/> <field name="blade_tip_characteristics_id" string="" widget="many2one_radio"/>
</group> </group>
<group string="柄部类型"> <group string="柄部类型">
<field name="handle_type_ids" string="" widget="custom_many2many_checkboxes"/> <field name="handle_type_id" string="" widget="many2one_radio"/>
</group> </group>
</group> </group>
<group> <group>

View File

@@ -57,42 +57,52 @@ class ToolChangeRequirementInformation(models.TransientModel):
确认换刀申请(按键) 确认换刀申请(按键)
:return: :return:
""" """
print('已运行')
record = self.env['sf.machine.table.tool.changing.apply'].search( record = self.env['sf.machine.table.tool.changing.apply'].search(
[('maintenance_equipment_id', '=', self.maintenance_equipment_id.id), [('maintenance_equipment_id', '=', self.maintenance_equipment_id.id),
('cutter_spacing_code_id', '=', self.cutter_spacing_code_id.id) ('cutter_spacing_code_id', '=', self.cutter_spacing_code_id.id)
]) ])
print('运行record_1')
# 功能刀具组装创建新任务(new_assembly_task) # 搜索满足需求的功能刀具
sf_functional_tool_assembly = self.env['sf.functional.tool.assembly'].sudo().create({ functional_tool = self.env['sf.functional.tool.assembly'].get_functional_tool({
'functional_tool_name': self.replacement_tool_name, 'after_assembly_functional_tool_name': self.replacement_tool_name,
'functional_tool_type_id': self.replacement_tool_type_id.id, 'after_assembly_functional_tool_diameter': self.replacement_diameter,
'functional_tool_diameter': self.replacement_diameter, 'after_assembly_knife_tip_r_angle': self.replacement_knife_tip_r_angle,
'knife_tip_r_angle': self.replacement_knife_tip_r_angle, 'after_assembly_coarse_middle_thin': self.replacement_tool_coarse_middle_thin
'coarse_middle_thin': self.replacement_tool_coarse_middle_thin,
'new_former': self.new_former,
'tool_loading_length': self.replacement_tool_setting_length,
'functional_tool_length': self.replacement_extension_length,
'effective_length': self.replacement_effective_length,
'loading_task_source': '1',
'use_tool_time': self.used_tool_time,
'production_line_name_id': self.production_line_id.id,
'machine_tool_name_id': self.maintenance_equipment_id.id,
'applicant': self.applicant,
'apply_time': fields.Datetime.now(),
'cutter_spacing_code_id': self.cutter_spacing_code_id.id,
'whether_standard_knife': self.whether_standard_knife,
'reason_for_applying': self.reason_for_applying,
'sf_machine_table_tool_changing_apply_id': record.id
}) })
print('sf_functional_tool_assembly:', sf_functional_tool_assembly) # 如果有满足需求的刀具,就返回刀具信息
# 修改机床换刀申请状态 if functional_tool:
record.write({ record.write({'status': '3'})
'status': '1', # todo 将功能刀具信息传递到机床
'sf_functional_tool_assembly_id': sf_functional_tool_assembly return functional_tool
}) # 如果没有满足需求的刀具,就创建功能刀具组装单
print('运行成功') else:
# 功能刀具组装创建新任务(new_assembly_task)
sf_functional_tool_assembly = self.env['sf.functional.tool.assembly'].sudo().create({
'functional_tool_name': self.replacement_tool_name,
'functional_tool_type_id': self.replacement_tool_type_id.id,
'functional_tool_diameter': self.replacement_diameter,
'knife_tip_r_angle': self.replacement_knife_tip_r_angle,
'coarse_middle_thin': self.replacement_tool_coarse_middle_thin,
'new_former': self.new_former,
'tool_loading_length': self.replacement_tool_setting_length,
'functional_tool_length': self.replacement_extension_length,
'effective_length': self.replacement_effective_length,
'loading_task_source': '1',
'use_tool_time': self.used_tool_time,
'production_line_name_id': self.production_line_id.id,
'machine_tool_name_id': self.maintenance_equipment_id.id,
'applicant': self.applicant,
'apply_time': fields.Datetime.now(),
'cutter_spacing_code_id': self.cutter_spacing_code_id.id,
'whether_standard_knife': self.whether_standard_knife,
'reason_for_applying': self.reason_for_applying,
'sf_machine_table_tool_changing_apply_id': record.id
})
# 修改机床换刀申请状态
record.write({
'status': '1',
'sf_functional_tool_assembly_id': sf_functional_tool_assembly.id
})
# 关闭弹出窗口 # 关闭弹出窗口
return {'type': 'ir.actions.act_window_close'} return {'type': 'ir.actions.act_window_close'}
@@ -186,7 +196,9 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
# 功能刀具组装信息 # 功能刀具组装信息
# 整体式刀具型号 # 整体式刀具型号
integral_code_id = fields.Many2one('stock.lot', string='整体式刀具序列号', integral_code_id = fields.Many2one('stock.lot', string='整体式刀具序列号',
domain=[('product_id.cutting_tool_material_id.name', '=', '整体式刀具')]) domain=[('product_id.cutting_tool_material_id.name', '=', '整体式刀具'),
('quant_ids.location_id.name', 'in', ['刀具房']),
('quant_ids.quantity', '>', 0)])
cutting_tool_integral_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='整体式刀具型号', cutting_tool_integral_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='整体式刀具型号',
readonly=True) readonly=True)
integral_name = fields.Char('整体式刀具名称', readonly=True) integral_name = fields.Char('整体式刀具名称', readonly=True)
@@ -194,14 +206,18 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
# 刀片型号 # 刀片型号
blade_code_id = fields.Many2one('stock.lot', '刀片序列号', blade_code_id = fields.Many2one('stock.lot', '刀片序列号',
domain=[('product_id.cutting_tool_material_id.name', '=', '刀片')]) domain=[('product_id.cutting_tool_material_id.name', '=', '刀片'),
('quant_ids.location_id.name', 'in', ['刀具房']),
('quant_ids.quantity', '>', 0)])
cutting_tool_blade_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀片型号', readonly=True) cutting_tool_blade_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀片型号', readonly=True)
blade_name = fields.Char('刀片名称', readonly=True) blade_name = fields.Char('刀片名称', readonly=True)
sf_tool_brand_id_2 = fields.Many2one('sf.machine.brand', '刀片品牌', readonly=True) sf_tool_brand_id_2 = fields.Many2one('sf.machine.brand', '刀片品牌', readonly=True)
# 刀杆型号 # 刀杆型号
bar_code_id = fields.Many2one('stock.lot', '刀杆序列号', bar_code_id = fields.Many2one('stock.lot', '刀杆序列号',
domain=[('product_id.cutting_tool_material_id.name', '=', '刀杆')]) domain=[('product_id.cutting_tool_material_id.name', '=', '刀杆'),
('quant_ids.location_id.name', 'in', ['刀具房']),
('quant_ids.quantity', '>', 0)])
cutting_tool_cutterbar_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀杆型号', cutting_tool_cutterbar_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀杆型号',
readonly=True) readonly=True)
bar_name = fields.Char('刀杆名称', readonly=True) bar_name = fields.Char('刀杆名称', readonly=True)
@@ -209,7 +225,9 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
# 刀盘型号 # 刀盘型号
pad_code_id = fields.Many2one('stock.lot', '刀盘序列号', pad_code_id = fields.Many2one('stock.lot', '刀盘序列号',
domain=[('product_id.cutting_tool_material_id.name', '=', '刀盘')]) domain=[('product_id.cutting_tool_material_id.name', '=', '刀盘'),
('quant_ids.location_id.name', 'in', ['刀具房']),
('quant_ids.quantity', '>', 0)])
cutting_tool_cutterpad_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀盘型号', cutting_tool_cutterpad_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀盘型号',
readonly=True) readonly=True)
pad_name = fields.Char('刀盘名称', readonly=True) pad_name = fields.Char('刀盘名称', readonly=True)
@@ -217,7 +235,9 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
# 刀柄型号 # 刀柄型号
handle_code_id = fields.Many2one('stock.lot', '刀柄序列号', required=True, handle_code_id = fields.Many2one('stock.lot', '刀柄序列号', required=True,
domain=[('product_id.cutting_tool_material_id.name', '=', '刀柄')]) domain=[('product_id.cutting_tool_material_id.name', '=', '刀柄'),
('quant_ids.location_id.name', 'in', ['刀具房']),
('quant_ids.quantity', '>', 0)])
cutting_tool_cutterhandle_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀柄型号', cutting_tool_cutterhandle_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀柄型号',
readonly=True) readonly=True)
handle_name = fields.Char('刀柄名称', readonly=True) handle_name = fields.Char('刀柄名称', readonly=True)
@@ -225,7 +245,9 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
# 夹头型号 # 夹头型号
chuck_code_id = fields.Many2one('stock.lot', '夹头序列号', required=True, chuck_code_id = fields.Many2one('stock.lot', '夹头序列号', required=True,
domain=[('product_id.cutting_tool_material_id.name', '=', '夹头')]) domain=[('product_id.cutting_tool_material_id.name', '=', '夹头'),
('quant_ids.location_id.name', 'in', ['刀具房']),
('quant_ids.quantity', '>', 0)])
cutting_tool_cutterhead_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='夹头型号', cutting_tool_cutterhead_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='夹头型号',
readonly=True) readonly=True)
chuck_name = fields.Char('夹头名称', readonly=True, compute='_compute_auto_fill') chuck_name = fields.Char('夹头名称', readonly=True, compute='_compute_auto_fill')
@@ -315,14 +337,14 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
string='组装后功能刀具类型') string='组装后功能刀具类型')
after_assembly_functional_tool_diameter = fields.Integer(string='组装后功能刀具直径(mm)') after_assembly_functional_tool_diameter = fields.Integer(string='组装后功能刀具直径(mm)')
after_assembly_knife_tip_r_angle = fields.Float(string='组装后刀尖R角(mm)') after_assembly_knife_tip_r_angle = fields.Float(string='组装后刀尖R角(mm)')
after_assembly_new_former = fields.Selection([('0', ''), ('1', '')], string='组装后新/旧') after_assembly_new_former = fields.Selection([('0', ''), ('1', '')], string='组装后新/旧', default='0')
cut_time = fields.Integer(string='已切削时间(min)') cut_time = fields.Integer(string='已切削时间(min)')
cut_length = fields.Float(string='已切削长度(mm)') cut_length = fields.Float(string='已切削长度(mm)')
cut_number = fields.Integer(string='已切削次数') cut_number = fields.Integer(string='已切削次数')
after_assembly_whether_standard_knife = fields.Boolean(string='组装后是否标准刀', default=True) after_assembly_whether_standard_knife = fields.Boolean(string='组装后是否标准刀', default=True)
after_assembly_coarse_middle_thin = fields.Selection([("1", ""), ('2', ''), ('3', '')], after_assembly_coarse_middle_thin = fields.Selection([("1", ""), ('2', ''), ('3', '')],
string='组装后粗/中/精') string='组装后粗/中/精', default='3')
after_assembly_max_lifetime_value = fields.Integer(string='组装后最大寿命值(min)') after_assembly_max_lifetime_value = fields.Integer(string='组装后最大寿命值(min)')
after_assembly_alarm_value = fields.Integer(string='组装后报警值(min)') after_assembly_alarm_value = fields.Integer(string='组装后报警值(min)')
after_assembly_used_value = fields.Integer(string='组装后已使用值(min)') after_assembly_used_value = fields.Integer(string='组装后已使用值(min)')
@@ -339,6 +361,7 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
功能刀具组装 功能刀具组装
:return: :return:
""" """
# 获取组装单对象
functional_tool_assembly = self.env['sf.functional.tool.assembly'].search([ functional_tool_assembly = self.env['sf.functional.tool.assembly'].search([
('assembly_order_code', '=', self.assembly_order_code), ('assembly_order_code', '=', self.assembly_order_code),
('machine_tool_name_id', '=', self.machine_tool_name_id.id), ('machine_tool_name_id', '=', self.machine_tool_name_id.id),
@@ -348,29 +371,45 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
# 对物料做必填判断 # 对物料做必填判断
self.materials_must_be_judged() self.materials_must_be_judged()
product_id = self.env['product.product'].search([('name', '=', '功能刀具')])
# 创建组装入库单 # 创建组装入库单
# 创建功能刀具批次/序列号记录 # 创建功能刀具批次/序列号记录
stock_lot = self.create_assemble_warehouse_receipt(functional_tool_assembly) stock_lot = product_id.create_assemble_warehouse_receipt(self.id, functional_tool_assembly)
# 创建刀具组装入库单 # 创建刀具组装入库单
self.create_stocking_picking(stock_lot) self.create_stocking_picking(stock_lot)
# 封装功能刀具数据 # 刀具物料出库
desc_1 = self.get_desc_1(stock_lot) if self.integral_code_id:
product_id.tool_material_stock_moves(self.integral_code_id)
if self.blade_code_id:
product_id.tool_material_stock_moves(self.blade_code_id)
if self.bar_code_id:
product_id.tool_material_stock_moves(self.bar_code_id)
if self.pad_code_id:
product_id.tool_material_stock_moves(self.pad_code_id)
if self.handle_code_id:
product_id.tool_material_stock_moves(self.handle_code_id)
if self.chuck_code_id:
product_id.tool_material_stock_moves(self.chuck_code_id)
# 创建功能刀具列表记录 # ============================创建功能刀具列表、安全库存记录===============================
# 封装功能刀具数据 # 封装功能刀具数据
desc_2 = self.get_desc_2(stock_lot, functional_tool_assembly) desc_2 = self.get_desc_2(stock_lot, functional_tool_assembly)
# 创建功能刀具列表、功能刀具预警、功能刀具实时分布、功能刀具出入库记录 # 创建功能刀具列表记录
record_1 = self.env['sf.functional.cutting.tool.entity'].create(desc_2) record_1 = self.env['sf.functional.cutting.tool.entity'].create(desc_2)
# self.env['sf.real.time.distribution.of.functional.tools'].create({ # 创建安全库存信息
# 'functional_cutting_tool_id': record_1.id self.env['sf.real.time.distribution.of.functional.tools'].create_or_edit_safety_stock({
# }) 'name': self.after_assembly_functional_tool_name,
# self.env['sf.inbound.and.outbound.records.of.functional.tools'].create({ 'sf_cutting_tool_type_id': self.after_assembly_functional_tool_type_id.id,
# 'functional_cutting_tool_id': record_1.id 'diameter': self.after_assembly_functional_tool_diameter,
# }) 'knife_tip_r_angle': self.after_assembly_knife_tip_r_angle,
'coarse_middle_thin': self.after_assembly_coarse_middle_thin,
}, record_1)
# =====================修改功能刀具组装单、机床换刀申请、CAM工单程序用刀计划的状态==============
# 封装功能刀具数据
desc_1 = self.get_desc_1(stock_lot)
# 修改功能刀具组装单信息 # 修改功能刀具组装单信息
functional_tool_assembly.write(desc_1) functional_tool_assembly.write(desc_1)
if functional_tool_assembly.sf_machine_table_tool_changing_apply_id: if functional_tool_assembly.sf_machine_table_tool_changing_apply_id:
# 修改机床换刀申请的状态 # 修改机床换刀申请的状态
self.env['sf.machine.table.tool.changing.apply'].sudo().search([ self.env['sf.machine.table.tool.changing.apply'].sudo().search([
@@ -414,71 +453,6 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
# 将刀具组装入库单的状态更改为就绪 # 将刀具组装入库单的状态更改为就绪
picking_id.action_confirm() picking_id.action_confirm()
def create_assemble_warehouse_receipt(self, functional_tool_assembly):
"""
创建功能刀具批次/序列号记录
"""
product_id = self.env['product.product'].search([('name', '=', '功能刀具')])
stock_lot = self.env['stock.lot'].create({
'name': self.get_stock_lot_name(),
'product_id': product_id.id,
'company_id': self.env.company.id
})
# 创建功能刀具该批次/序列号 库存移动和移动历史
self.create_stock_quant(product_id, stock_lot, functional_tool_assembly)
return stock_lot
def create_stock_quant(self, product_id, stock_lot, functional_tool_assembly):
"""
创建功能刀具该批次/序列号 库存移动和移动历史
"""
# 获取位置对象
stock_location_id = self.env['stock.location'].search([('name', '=', '组装后')])
location_inventory_id = self.env['stock.location'].search([('name', '=', 'Inventory adjustment')])
# 创建库存移动
stock_move_id = self.env['stock.move'].create({
'name': '功能刀具组装出库',
'product_id': product_id.id,
'location_id': location_inventory_id.id,
'location_dest_id': stock_location_id.id,
'product_uom_qty': 1.00,
'state': 'done'
})
# 创建移动历史
stock_move_line_id = self.env['stock.move.line'].create({
'product_id': product_id.id,
'functional_tool_name_id': functional_tool_assembly.id,
'lot_id': stock_lot.id,
'move_id': stock_move_id.id,
'install_tool_time': fields.Datetime.now(),
'qty_done': 1.0,
'state': 'done'
})
return stock_move_id, stock_move_line_id
def get_stock_lot_name(self):
"""
生成功能刀具序列号
"""
code = 'JKM-T-' + str(self.functional_tool_type_id.code) + '-' + str(self.functional_tool_diameter) + '-'
new_time = fields.Date.today()
code += str(new_time) + '-'
stock_lot_id = self.env['stock.lot'].sudo().search(
[('name', 'like', new_time), ('product_id.name', '=', '功能刀具')],
limit=1,
order="id desc"
)
if not stock_lot_id:
num = "%03d" % 1
else:
m = int(stock_lot_id.name[-3:]) + 1
num = "%03d" % m
return code + str(num)
def get_desc_1(self, stock_lot): def get_desc_1(self, stock_lot):
return { return {
'barcode_id': stock_lot.id, 'barcode_id': stock_lot.id,
@@ -543,3 +517,90 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
'cut_number': self.cut_number, 'cut_number': self.cut_number,
'current_location_id': stock_lot.quant_ids.location_id.ids[-1], 'current_location_id': stock_lot.quant_ids.location_id.ids[-1],
} }
class ProductProduct(models.Model):
_inherit = 'product.product'
def create_assemble_warehouse_receipt(self, tool_assembly_order_id, functional_tool_assembly):
"""
创建功能刀具批次/序列号记录
"""
product_id = self.env['product.product'].search([('name', '=', '功能刀具')])
stock_lot = self.env['stock.lot'].create({
'name': self.get_stock_lot_name(tool_assembly_order_id),
'product_id': product_id.id,
'company_id': self.env.company.id
})
# 获取位置对象
location_inventory_id = self.env['stock.location'].search([('name', '=', 'Production')])
stock_location_id = self.env['stock.location'].search([('name', '=', '组装后')])
# 创建功能刀具该批次/序列号 库存移动和移动历史
stock_lot.create_stock_quant(location_inventory_id, stock_location_id, functional_tool_assembly.id)
return stock_lot
def get_stock_lot_name(self, tool_assembly_order_id):
"""
生成功能刀具序列号
"""
tool_assembly_order = self.env['sf.functional.tool.assembly.order'].search(
[('id', '=', tool_assembly_order_id)])
code = 'JKM-T-' + str(tool_assembly_order.after_assembly_functional_tool_type_id.code) + '-' + str(
tool_assembly_order.after_assembly_functional_tool_diameter) + '-'
new_time = fields.Date.today()
code += str(new_time) + '-'
stock_lot_id = self.env['stock.lot'].sudo().search(
[('name', 'like', new_time), ('product_id.name', '=', '功能刀具')],
limit=1,
order="id desc"
)
if not stock_lot_id:
num = "%03d" % 1
else:
m = int(stock_lot_id.name[-3:]) + 1
num = "%03d" % m
return code + str(num)
def tool_material_stock_moves(self, tool_material):
"""
对刀具物料进行库存移动到 刀具组装位置
"""
# 获取位置对象
location_inventory_id = tool_material.quant_ids.location_id[-1]
print(location_inventory_id)
stock_location_id = self.env['stock.location'].search([('name', '=', '刀具组装位置')])
# 创建功能刀具该批次/序列号 库存移动和移动历史
tool_material.create_stock_quant(location_inventory_id, stock_location_id, None)
class StockLot(models.Model):
_inherit = 'stock.lot'
def create_stock_quant(self, location_inventory_id, stock_location_id, functional_tool_assembly_id):
"""
对功能刀具组装过程的功能刀具和刀具物料进行库存移动,以及创建移动历史
"""
# 创建库存移动记录
stock_move_id = self.env['stock.move'].create({
'name': '功能刀具组装',
'product_id': self.product_id.id,
'location_id': location_inventory_id.id,
'location_dest_id': stock_location_id.id,
'product_uom_qty': 1.00,
'state': 'done'
})
# 创建移动历史记录
stock_move_line_id = self.env['stock.move.line'].create({
'product_id': self.product_id.id,
'functional_tool_name_id': functional_tool_assembly_id,
'lot_id': self.id,
'move_id': stock_move_id.id,
'install_tool_time': fields.Datetime.now(),
'qty_done': 1.0,
'state': 'done'
})
return stock_move_id, stock_move_line_id

View File

@@ -15,6 +15,8 @@
'security/sf_stock_security.xml', 'security/sf_stock_security.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'views/view.xml', 'views/view.xml',
'views/shelf_location.xml',
'views/change_stock_move_views.xml',
], ],
'demo': [ 'demo': [
], ],

View File

@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import logging
from odoo import api, fields, models from odoo import api, fields, models
from odoo.osv import expression from odoo.osv import expression
@@ -11,10 +12,13 @@ class SfLocation(models.Model):
barcode = fields.Char('Barcode', copy=False, size=15) barcode = fields.Char('Barcode', copy=False, size=15)
# 仓库类别selection库区、库位、货位 # 仓库类别selection库区、库位、货位
# location_type = fields.Selection([
# ('库区', '库区'),
# ('货架', '货架'),
# ('货位', '货位')
# ], string='存储类型')
location_type = fields.Selection([ location_type = fields.Selection([
('库区', '库区'), ('库区', '库区')
('货架', '货架'),
('货位', '货位')
], string='存储类型') ], string='存储类型')
# 库区类型selection拣货区、存货区、收货区、退货区、次品区 # 库区类型selection拣货区、存货区、收货区、退货区、次品区
area_type = fields.Selection([ area_type = fields.Selection([
@@ -24,6 +28,10 @@ class SfLocation(models.Model):
('退货区', '退货区'), ('退货区', '退货区'),
('次品区', '次品区') ('次品区', '次品区')
], string='库区类型') ], string='库区类型')
# 当前位置
current_location_id = fields.Many2one('sf.shelf.location', string='当前位置')
# 目的位置
destination_location_id = fields.Many2one('sf.shelf.location', string='目的位置')
# 存储类型selection库区、货架 # 存储类型selection库区、货架
# storage_type = fields.Selection([ # storage_type = fields.Selection([
# ('库区', '库区'), # ('库区', '库区'),
@@ -173,6 +181,117 @@ class SfLocation(models.Model):
# return res # return res
# 生成货位 # 生成货位
def create_location(self):
"""
当仓库类型为货架时,自动生成其下面的货位,数量为货架层数*层数容量
"""
pass
# if self.location_type == '货架':
# for i in range(self.shelf_layer):
# for j in range(self.layer_capacity):
# self.create({
# 'name': self.name + '-' + str(i + 1) + '层' + '-' + str(j + 1) + '位置',
# 'location_id': self.id,
# 'location_type': '货位',
# 'barcode': self.generate_barcode(i, j),
# 'location_status': '空闲'
# })
def generate_barcode(self, i, j):
"""
生成货位条码
"""
pass
# # 这里是你生成barcode的代码
# # area_type_barcode = self.location_id.barcode
# area_type_barcode = self.barcode
# i_str = str(i + 1).zfill(3) # 确保是两位数如果不足两位左侧补0
# j_str = str(j + 1).zfill(3) # 确保是两位数如果不足两位左侧补0
# return area_type_barcode + self.channel + self.direction + '-' + self.barcode + '-' + i_str + '-' + j_str
class ShelfLocation(models.Model):
_name = 'sf.shelf.location'
_description = '货架货位'
_order = 'name'
name = fields.Char('名称', required=True, size=20)
barcode = fields.Char('编码', copy=False, size=15)
# 仓库类别selection库区、库位、货位
location_type = fields.Selection([
('货架', '货架'),
('货位', '货位')
], string='存储类型')
# 绑定库区
shelf_location_id = fields.Many2one('stock.location', string='所属库区', domain=[('location_type', '=', '库区')])
location_id = fields.Many2one('stock.location', string='所属库区', domain=[('location_type', '=', '库区')])
# 产品类别 关联product.category
# product_type = fields.Many2many('product.category', string='产品类别')
# picking_product_type = fields.Many2many('stock.picking', string='调拨产品类别', related='location_dest_id.product_type')
# 货架独有字段通道、方向、货架高度m、货架层数、层数容量
channel = fields.Char(string='通道')
direction = fields.Selection([
('R', 'R'),
('L', 'L')
], string='方向')
shelf_height = fields.Float(string='货架高度(m)')
shelf_layer = fields.Integer(string='货架层数')
layer_capacity = fields.Integer(string='层数容量')
# 货位独有字段:货位状态、产品(关联产品对象)、产品序列号(关联产品序列号对象)
location_status = fields.Selection([
('空闲', '空闲'),
('占用', '占用'),
('禁用', '禁用')
], string='货位状态', default='空闲')
# product_id = fields.Many2one('product.template', string='产品')
product_id = fields.Many2one('product.product', string='产品', compute='_compute_product_id', readonly=True)
product_sn_id = fields.Many2one('stock.lot', string='产品序列号')
hide_shelf = fields.Boolean(compute='_compute_hide_what', string='隐藏货架')
hide_location = fields.Boolean(compute='_compute_hide_what', string='隐藏货位')
@api.onchange('shelf_location_id')
def _onchange_shelf_location_id(self):
"""
根据货架的所属库区修改货位的所属库区
"""
all_location = self.env['sf.shelf.location'].search([('name', 'ilike', self.name)])
for record in self:
for location in all_location:
location.location_id = record.shelf_location_id.id
@api.depends('product_sn_id')
def _compute_product_id(self):
"""
根据产品序列号,获取产品
"""
for record in self:
if record.product_sn_id:
record.product_id = record.product_sn_id.product_id
record.location_status = '占用'
else:
record.product_id = False
record.location_status = '空闲'
@api.depends('location_type')
def _compute_hide_what(self):
"""
根据仓库类别,隐藏不需要的字段
:return:
"""
for record in self:
record.hide_shelf = False
record.hide_location = False
if record.location_type and record.location_type == '货架':
record.hide_shelf = True
elif record.location_type and record.location_type == '货位':
record.hide_location = True
else:
pass
def create_location(self): def create_location(self):
""" """
当仓库类型为货架时,自动生成其下面的货位,数量为货架层数*层数容量 当仓库类型为货架时,自动生成其下面的货位,数量为货架层数*层数容量
@@ -180,13 +299,17 @@ class SfLocation(models.Model):
if self.location_type == '货架': if self.location_type == '货架':
for i in range(self.shelf_layer): for i in range(self.shelf_layer):
for j in range(self.layer_capacity): for j in range(self.layer_capacity):
self.create({ location_name = self.name + '-' + str(i + 1) + '' + '-' + str(j + 1) + '位置'
'name': self.name + '-' + str(i + 1) + '' + '-' + str(j + 1) + '位置', # 检查是否已经有同名的位置存在
'location_id': self.id, existing_location = self.search([('name', '=', location_name)])
'location_type': '货位', if not existing_location:
'barcode': self.generate_barcode(i, j), self.create({
'location_status': '空闲' 'name': location_name,
}) 'location_id': self.shelf_location_id.id,
'location_type': '货位',
'barcode': self.generate_barcode(i, j),
'location_status': '空闲',
})
def generate_barcode(self, i, j): def generate_barcode(self, i, j):
""" """
@@ -199,11 +322,121 @@ class SfLocation(models.Model):
j_str = str(j + 1).zfill(3) # 确保是两位数如果不足两位左侧补0 j_str = str(j + 1).zfill(3) # 确保是两位数如果不足两位左侧补0
return area_type_barcode + self.channel + self.direction + '-' + self.barcode + '-' + i_str + '-' + j_str return area_type_barcode + self.channel + self.direction + '-' + self.barcode + '-' + i_str + '-' + j_str
# def generate_barcode(self, i, j):
# # 这里是你生成barcode的代码 class Sf_stock_move_line(models.Model):
# area_type_barcode = self.location_id.barcode _inherit = 'stock.move.line'
# return area_type_barcode + self.channel + self.direction + '-' + self.barcode + '-' + str(i + 1) + '-'
# + str(j + 1) current_location_id = fields.Many2one(
'sf.shelf.location', string='当前货位', compute='_compute_current_location_id', store=True)
# location_dest_id = fields.Many2one('stock.location', string='目标库位')
location_dest_id_product_type = fields.Many2many(related='location_dest_id.product_type')
location_dest_id_value = fields.Integer(compute='_compute_location_dest_id_value', store=True)
# def button_test(self):
# print(self.picking_id.name)
# stock_picking = self.env['stock.picking'].search([('name', '=', self.picking_id.name)], limit=1)
# print(self.picking_id.name)
# print(aa.move_line_ids.lot_id.name)
# # 获取当前的stock.picking对象
# current_picking = self.env['stock.picking'].search([('name', '=', self.picking_id.name)], limit=1)
#
# # 获取当前picking的第一个stock.move对象
# current_move = current_picking.move_ids[0] if current_picking.move_ids else False
#
# # 如果存在相关的stock.move对象
# if current_move:
# # 获取源stock.move对象
# origin_move = current_move.move_orig_ids[0] if current_move.move_orig_ids else False
#
# # 从源stock.move对象获取源stock.picking对象
# origin_picking = origin_move.picking_id if origin_move else False
# # 现在origin_picking就是current_picking的上一步
# # 获取目标stock.move对象
# dest_move = current_move.move_dest_ids[0] if current_move.move_dest_ids else False
#
# # 从目标stock.move对象获取目标stock.picking对象
# dest_picking = dest_move.picking_id if dest_move else False
# # 现在dest_picking就是current_picking的下一步
@api.depends('location_id')
def _compute_current_location_id(self):
for record in self:
# 使用record代替self来引用当前遍历到的记录
logging.info('record.picking_id.name: %s' % record.picking_id.name)
logging.info('record.env: %s' % record.env['stock.picking'].search([('name', '=', record.picking_id.name)]))
# 获取当前的stock.picking对象
current_picking = record.env['stock.picking'].search([('name', '=', record.picking_id.name)], limit=1)
# 获取当前picking的第一个stock.move对象
current_move = current_picking.move_ids[0] if current_picking.move_ids else False
# 如果存在相关的stock.move对象
if current_move:
# 获取源stock.move对象
origin_move = current_move.move_orig_ids[0] if current_move.move_orig_ids else False
# 从源stock.move对象获取源stock.picking对象
origin_picking = origin_move.picking_id if origin_move else False
# 如果前一个调拨单有目标货位
if origin_picking:
for i in current_picking.move_line_ids:
for j in origin_picking.move_line_ids:
if j.destination_location_id and i.lot_id == j.lot_id:
# 更新当前记录的current_location_id字段
record.current_location_id = j.destination_location_id
# # 获取目标stock.move对象
# dest_move = current_move.move_dest_ids[0] if current_move.move_dest_ids else False
#
# # 从目标stock.move对象获取目标stock.picking对象
# dest_picking = dest_move.picking_id if dest_move else False
# # 现在dest_picking就是current_picking的下一步
# 是一张单据一张单据往下走的,所以这里的目标货位是上一张单据的当前货位,且这样去计算是可以的。
@api.depends('location_dest_id')
def _compute_location_dest_id_value(self):
for record in self:
record.location_dest_id_value = record.location_dest_id.id if record.location_dest_id else False
destination_location_id = fields.Many2one(
'sf.shelf.location', string='目标货位')
@api.onchange('destination_location_id')
def _compute_destination_location_id(self):
for record in self:
shelf_location_obj = self.env['sf.shelf.location'].search(
[('product_sn_id', '=', record.lot_id.id)])
if shelf_location_obj:
shelf_location_obj.product_sn_id = False
# obj = self.env['sf.shelf.location'].search([('location_id', '=',
# self.destination_location_id.id)])
obj = self.env['sf.shelf.location'].search([('name', '=',
self.destination_location_id.name)])
if obj:
obj.product_sn_id = record.lot_id.id
else:
pass
else:
obj = self.env['sf.shelf.location'].search([('name', '=',
self.destination_location_id.name)])
if obj:
obj.product_sn_id = record.lot_id.id
class SfStockPicking(models.Model):
_inherit = 'stock.picking'
def button_validate(self):
"""
重写验证方法,当验证时意味着调拨单已经完成,已经移动到了目标货位,所以需要将当前货位的状态改为空闲
"""
res = super(SfStockPicking, self).button_validate()
for line in self.move_line_ids:
if line:
if line.current_location_id:
line.current_location_id.product_sn_id = False
line.current_location_id = False
return res
class SfProcurementGroup(models.Model): class SfProcurementGroup(models.Model):

View File

@@ -1,12 +1,11 @@
odoo.define('sf_warehouse.custom_kanban', function (require) { odoo.define('sf_warehouse.custom_kanban', function (require) {
"use strict" "use strict";
var KanbanRenderer = require('web.KanbanRenderer'); var KanbanRenderer = require('web.KanbanRenderer');
KanbanRenderer.include({ KanbanRenderer.include({
_render: function () { _render: function () {
var self = this; var self = this;
return this._super.apply(this, arguments).then(function () { return this._super.apply(this, arguments).then(function () {
var colorGuide = $('<div class="color-guide"> \ var colorGuide = $('<div class="color-guide"> \
<span class="color-guide-item" style="background-color: red;"></span> \ <span class="color-guide-item" style="background-color: red;"></span> \
<span class="color-guide-item" style="background-color: green;"></span> \ <span class="color-guide-item" style="background-color: green;"></span> \
@@ -16,5 +15,4 @@ odoo.define('sf_warehouse.custom_kanban', function (require) {
}); });
}, },
}); });
}); });

View File

@@ -1,9 +1,7 @@
/** @odoo-module **/ /** @odoo-module **/
import { registry } from "@web/core/registry"; import {registry} from "@web/core/registry";
import { CharField } from '@web/views/fields/char/char_field'; import {CharField} from '@web/views/fields/char/char_field';
// 继承CharField组件实现自定义组件:当光标聚焦于输入框时,选中输入框内容 // 继承CharField组件实现自定义组件:当光标聚焦于输入框时,选中输入框内容
class CustomChar extends CharField { class CustomChar extends CharField {
@@ -20,7 +18,7 @@ class CustomChar extends CharField {
// 当光标聚焦于输入框时,选中输入框内容 // 当光标聚焦于输入框时,选中输入框内容
this.input.el.addEventListener('focus', function () { this.input.el.addEventListener('focus', function () {
this.select(); this.select();
}) });
} }
@@ -72,13 +70,10 @@ class CustomChar extends CharField {
// } // }
// } // }
// this.$input.on('focus', function () { // this.$input.on('focus', function () {
// $(this).select(); // $(this).select();
// }); // });
// 当光标聚焦于输入框时,选中输入框内容 // 当光标聚焦于输入框时,选中输入框内容
} }
registry.category("fields").add("custom_char", CustomChar); registry.category("fields").add("custom_char", CustomChar);

View File

@@ -1,8 +1,8 @@
/** @odoo-module */ /** @odoo-module */
import { KanbanController } from "@web/views/kanban/kanban_controller"; import {KanbanController} from "@web/views/kanban/kanban_controller";
import { kanbanView } from "@web/views/kanban/kanban_view"; import {kanbanView} from "@web/views/kanban/kanban_view";
import { registry } from "@web/core/registry"; import {registry} from "@web/core/registry";
// the controller usually contains the Layout and the renderer. // the controller usually contains the Layout and the renderer.
class CustomKanbanController extends KanbanController { class CustomKanbanController extends KanbanController {

View File

@@ -1,8 +1,7 @@
/** @odoo-module **/ /** @odoo-module **/
import { registry } from "@web/core/registry"; import {registry} from "@web/core/registry";
import { Many2OneField } from '@web/views/fields/many2one/many2one_field'; import {Many2OneField} from '@web/views/fields/many2one/many2one_field';
// 继承FieldMany2One组件实现自定义组件:当光标聚焦于输入框时,选中输入框内容 // 继承FieldMany2One组件实现自定义组件:当光标聚焦于输入框时,选中输入框内容
@@ -17,14 +16,14 @@ class CustomMany2One extends Many2OneField {
// console.log('CustomMany2One.setup11111111111111'); // console.log('CustomMany2One.setup11111111111111');
super.setup(); super.setup();
} }
onMounted() { onMounted() {
// console.log('CustomMany2One.onMounted1'); // console.log('CustomMany2One.onMounted1');
// 当光标聚焦于输入框时,选中输入框内容 // 当光标聚焦于输入框时,选中输入框内容
this.input.el.addEventListener('focus', function () { this.input.el.addEventListener('focus', function () {
this.select(); this.select();
}) });
} }
} }
registry.category("fields").add("custom_many2one", CustomMany2One); registry.category("fields").add("custom_many2one", CustomMany2One);

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="sf_stock_move_line_tree" model="ir.ui.view">
<field name="name">sf.stock.move.line.tree</field>
<field name="model">stock.move.line</field>
<field name="inherit_id" ref="stock.view_stock_move_line_detailed_operation_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='location_id'][2]" position="after">
<field name="current_location_id"/>
</xpath>
<xpath expr="//field[@name='location_dest_id'][2]" position="after">
<field name="destination_location_id" domain="[
('location_type', '=', '货位'),
('location_id', '=', location_dest_id_value),
('location_status', '=', '空闲')
]"/>
<!-- <field name="location_dest_id_product_type"/> -->
<!-- <field name="location_dest_id"/> -->
<field name="location_dest_id_value" invisible="1"/>
<!-- <button name="button_test" string="测试" type="object" class="oe_highlight"/> -->
</xpath>
</field>
</record>
<record id="sf_stock_move_line_form" model="ir.ui.view">
<field name="name">sf.stock.move.line.form</field>
<field name="model">stock.move.line</field>
<field name="inherit_id" ref="stock.view_move_line_form"/>
<field name="arch" type="xml">
<xpath expr="//form//sheet//group//group//field[@name='location_id']" position="after">
<field name="current_location_id" options="{'no_create': False}"/>
</xpath>
<xpath expr="//form//sheet//group//group//field[@name='location_dest_id']" position="after">
<field name="destination_location_id" options="{'no_create': False}"/>
</xpath>
</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,178 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="view_shelf_location_tree" model="ir.ui.view">
<field name="name">Shelf Location tree</field>
<field name="model">sf.shelf.location</field>
<field name="arch" type="xml">
<tree string="Shelf Location">
<field name="name" string="名称"/>
<field name="barcode" string="编码"/>
<field name="location_type"/>
</tree>
</field>
</record>
<record id="view_shelf_location_form" model="ir.ui.view">
<field name="name">Shelf Location form</field>
<field name="model">sf.shelf.location</field>
<field name="arch" type="xml">
<form string="Shelf Location">
<header>
<button string="生成货位" name="create_location" type="object" class="oe_highlight" attrs="{'invisible': [('hide_shelf', '=', False)]}"/>
</header>
<sheet>
<group>
<field name="hide_shelf" invisible="1"/>
<field name="hide_location" invisible="1"/>
<field name="name" string="名称"/>
<field name="barcode" string="编码"/>
<field name="location_type"/>
<field name="shelf_location_id" attrs="{'invisible': [('location_type', '=', '货位')]}"/>
<field name="location_id" attrs="{'readonly': [('location_type', '=', '货位')], 'invisible': [('location_type', '=', '货架')]}"/>
<field name="channel" attrs="{'invisible': [('hide_shelf', '=', False)], 'required': [('hide_shelf', '!=', False)]}"/>
<field name="direction" attrs="{'invisible': [('hide_shelf', '=', False)], 'required': [('hide_shelf', '!=', False)]}"/>
<field name="product_sn_id" attrs="{'invisible': [('hide_location', '=', False)]}"/>
<!-- <field name="product_type" widget="many2many_tags"/> -->
<field name="shelf_height" attrs="{'invisible': [('hide_shelf', '=', False)], 'required': [('hide_shelf', '!=', False)]}"/>
<field name="shelf_layer" attrs="{'invisible': [('hide_shelf', '=', False)], 'required': [('hide_shelf', '!=', False)]}"/>
<field name="layer_capacity" attrs="{'invisible': [('hide_shelf', '=', False)], 'required': [('hide_shelf', '!=', False)]}"/>
<!-- <field name="product_id" attrs="{'invisible': [('hide_location', '=', False)], 'required': [('hide_location', '!=', False), ('location_status', '=', '占用')]}"/> -->
<field name="product_id" attrs="{'invisible': [('hide_location', '=', False)]}"/>
<!-- <field name="product_type" attrs="{'invisible': [('hide_location', '=', False)], 'required': [('hide_location', '!=', False), ('location_status', '=', '占用')]}" widget="many2many_tags"/> -->
<field name="location_status" attrs="{'invisible': [('hide_location', '=', False)], 'required': [('hide_location', '!=', False)]}"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="shelf_location_kanban_view" model="ir.ui.view">
<field name="name">shelf.location.kanban</field>
<field name="model">sf.shelf.location</field>
<field name="arch" type="xml">
<kanban class="o_kanban_mobile" js_class="custom_kanban">
<templates>
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_card oe_kanban_global_click
#{record.location_status.raw_value == '空闲' ? 'kanban_color_1' : ''}
#{record.location_status.raw_value == '占用' ? 'kanban_color_2' : ''}
#{record.location_status.raw_value == '禁用' ? 'kanban_color_3' : ''}">
<!-- 标题 -->
<div class="o_kanban_card_header">
<div class="o_kanban_card_header_title">
<field name="name"/>
</div>
</div>
<!-- 内容 -->
<div class="o_kanban_record_bottom">
<field name="location_status"/>
</div>
<div class="o_kanban_record_bottom">
<field name="product_sn_id"/>
<span> | </span>
<field name="product_id"/>
</div>
</div>
</t>
<!-- <t t-name="kanban-box"> -->
<!-- <div t-attf-class="oe_kanban_card oe_kanban_global_click -->
<!-- #{record.location_status.raw_value == '空闲' ? 'kanban_color_1' : ''} -->
<!-- #{record.location_status.raw_value == '占用' ? 'kanban_color_2' : ''} -->
<!-- #{record.location_status.raw_value == '禁用' ? 'kanban_color_3' : ''}"> -->
<!-- --><!-- 看板内容 -->
<!-- </div> -->
<!-- <div t-attf-class="oe_kanban_card"> -->
<!-- --><!-- 标题 -->
<!-- <div class="o_kanban_card_header"> -->
<!-- <div class="o_kanban_card_header_title"> -->
<!-- <field name="name"/> -->
<!-- </div> -->
<!-- </div> -->
<!-- --><!-- 内容 -->
<!-- <div class="o_kanban_record_bottom"> -->
<!-- <field name="location_status"/> -->
<!-- </div> -->
<!-- <div class="o_kanban_record_bottom"> -->
<!-- <field name="product_sn_id"/> -->
<!-- <span> | </span> -->
<!-- <field name="product_id"/> -->
<!-- </div> -->
<!-- </div> -->
<!-- </t> -->
</templates>
</kanban>
</field>
</record>
<!-- 搜索视图 -->
<record id="shelf_location_search_view" model="ir.ui.view">
<field name="name">shelf.location.search</field>
<field name="model">sf.shelf.location</field>
<field name="arch" type="xml">
<search string="货架货位">
<searchpanel class="account_root">
<!-- <field name="location_type" icon="fa-filter"/> -->
<field name="location_id" select="multi" icon="fa-filter"/>
<!-- <field name="location_status" icon="fa-filter"/> -->
</searchpanel>
</search>
</field>
</record>
<record id="shelf_location_kanban_action_id" model="ir.actions.act_window">
<field name="name">货架货位</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sf.shelf.location</field>
<field name="view_mode">kanban,form</field>
<field name="domain">[('location_type', '=', '货位')]</field>
</record>
<!-- <record id="example_action" model="ir.actions.act_window"> -->
<!-- <field name="name">Example</field> -->
<!-- <field name="type">ir.actions.act_window</field> -->
<!-- <field name="res_model">stock.location</field> -->
<!-- <field name="view_mode">kanban</field> -->
<!-- <field name="searchpanel">true</field> -->
<!-- <field name="searchpanel_field_label">货架</field> -->
<!-- <field name="searchpanel_field_name">parent_id</field> -->
<!-- <field name="searchpanel_field_group_by">['parent_id']</field> -->
<!-- <field name="domain">[('location_type', '=', '货位')]</field> -->
<!-- </record> -->
<menuitem id="shelf_location_kanban_menu" name="货位看板" parent="stock.menu_stock_root"
sequence="51"
action="shelf_location_kanban_action_id"/>
<record id="action_sf_shelf_location" model="ir.actions.act_window">
<field name="name">货架货位</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sf.shelf.location</field>
<field name="view_mode">tree,form</field>
</record>
<!-- <record id="example_action" model="ir.actions.act_window"> -->
<!-- <field name="name">Example</field> -->
<!-- <field name="type">ir.actions.act_window</field> -->
<!-- <field name="res_model">stock.location</field> -->
<!-- <field name="view_mode">kanban</field> -->
<!-- <field name="searchpanel">true</field> -->
<!-- <field name="searchpanel_field_label">货架</field> -->
<!-- <field name="searchpanel_field_name">parent_id</field> -->
<!-- <field name="searchpanel_field_group_by">['parent_id']</field> -->
<!-- <field name="domain">[('location_type', '=', '货位')]</field> -->
<!-- </record> -->
<!-- <menuitem id="menu_stock_location" name="货位状态" parent="stock.menu_stock_root" -->
<!-- sequence="50" -->
<!-- action="kanban_action_id"/> -->
<menuitem id="menu_sf_shelf_location" name="货架货位" parent="stock.menu_warehouse_config"
sequence="2"
action="action_sf_shelf_location"/>
</data>
</odoo>

View File

@@ -32,6 +32,8 @@
<field name="product_sn_id" attrs="{'invisible': [('hide_location', '=', False)], 'required': [('hide_location', '!=', False), ('location_status', '=', '空闲')]}"/> <field name="product_sn_id" attrs="{'invisible': [('hide_location', '=', False)], 'required': [('hide_location', '!=', False), ('location_status', '=', '空闲')]}"/>
<!-- <field name="time_test" widget="timepicker"/>--> <!-- <field name="time_test" widget="timepicker"/>-->
<field name="area_type" attrs="{'invisible': [('hide_area', '=', False)], 'required': [('hide_area', '!=', False)]}"/> <field name="area_type" attrs="{'invisible': [('hide_area', '=', False)], 'required': [('hide_area', '!=', False)]}"/>
<field name="current_location_id" attrs="{'invisible': [('hide_area', '=', False)]}"/>
<field name="destination_location_id" attrs="{'invisible': [('hide_area', '=', False)]}"/>
</group> </group>
<group> <group>
@@ -156,9 +158,9 @@
<!-- </record> --> <!-- </record> -->
<menuitem id="menu_stock_location" name="货位状态" parent="stock.menu_stock_root" <!-- <menuitem id="menu_stock_location" name="货位状态" parent="stock.menu_stock_root" -->
sequence="50" <!-- sequence="50" -->
action="kanban_action_id"/> <!-- action="kanban_action_id"/> -->
</data> </data>

View File

@@ -26,15 +26,15 @@ export class StepViewer extends Component {
model: this.props.record.resModel, model: this.props.record.resModel,
id: JSON.stringify(this.props.record.data['id']), id: JSON.stringify(this.props.record.data['id']),
field: this.props.name field: this.props.name
} };
url = url_props['base_url'].replace('http://', 'https://') + '/web/content/' + url_props['model'] + '/' + url_props['id'] + '/' + url_props['field'] + '?download=true' url = url_props['base_url'].replace('http://', 'https://') + '/web/content/' + url_props['model'] + '/' + url_props['id'] + '/' + url_props['field'] + '?download=true';
// url = 'http://localhost:8069'+'/web/content/'+url_props['model']+'/'+url_props['id']+'/'+url_props['field']+'?download=true' // url = 'http://localhost:8069'+'/web/content/'+url_props['model']+'/'+url_props['id']+'/'+url_props['field']+'?download=true'
// console.log('url111111', url) // console.log('url111111', url)
return url return url;
} else { } else {
url = "data:model/gltf-binary;base64," + this.props.value; url = "data:model/gltf-binary;base64," + this.props.value;
// console.log('url2', url) // console.log('url2', url)
return url return url;
// localStorage.setItem('url',url) // localStorage.setItem('url',url)
// let new_url = localStorage.getItem(('url')) // let new_url = localStorage.getItem(('url'))
// var oViewer = document.getElementsByTagName('model-viewer')[0]; // var oViewer = document.getElementsByTagName('model-viewer')[0];