Compare commits
2 Commits
feature/pa
...
master_sf_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39afc99b8f | ||
|
|
f230ad55fb |
@@ -3,8 +3,8 @@
|
|||||||
'name': "jikimo_account_process",
|
'name': "jikimo_account_process",
|
||||||
|
|
||||||
'summary': """
|
'summary': """
|
||||||
处理会计凭证生成重复名称报错问题
|
Short (1 phrase/line) summary of the module's purpose, used as
|
||||||
""",
|
subtitle on modules listing or apps.openerp.com""",
|
||||||
|
|
||||||
'description': """
|
'description': """
|
||||||
Long description of module's purpose
|
Long description of module's purpose
|
||||||
|
|||||||
@@ -1,99 +1,41 @@
|
|||||||
|
.zoomed {
|
||||||
.processing-capabilities-grid {
|
position: fixed !important;
|
||||||
display: grid;
|
top: 50%;
|
||||||
grid-template-columns: repeat(6, 1fr);
|
left: 50%;
|
||||||
gap: 10px;
|
transform: translate(-50%, -50%) scale(10);
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-item {
|
.many2many_flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-content {
|
.many2many_flex>div {
|
||||||
|
margin-right: 15px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
/*控制图片大小*/
|
|
||||||
.item-icon {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-label {
|
.many2many_flex>div>:nth-child(2) {
|
||||||
font-size: 12px;
|
position: relative;
|
||||||
word-break: break-word;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
.close {
|
||||||
.processing-capabilities-grid {
|
width: 20px;
|
||||||
grid-template-columns: repeat(4, 1fr);
|
height: 20px;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.processing-capabilities-grid {
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.processing-capabilities-grid {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.image-preview-container {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(0, 0, 0, 0.9);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
z-index: 1000;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-preview-container.show {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-preview {
|
|
||||||
max-width: 90%;
|
|
||||||
max-height: 90%;
|
|
||||||
object-fit: contain;
|
|
||||||
box-shadow: 0 0 20px rgba(255, 255, 255, 0.2);
|
|
||||||
border-radius: 5px;
|
|
||||||
transform: scale(0.9);
|
|
||||||
transition: transform 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-preview-container.show .image-preview {
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-preview-close {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 20px;
|
top: -8.8px;
|
||||||
right: 30px;
|
right: -8.8px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 40px;
|
background-color: #000;
|
||||||
font-weight: bold;
|
opacity: 0;
|
||||||
transition: 0.3s;
|
text-align: center;
|
||||||
cursor: pointer;
|
line-height: 20px;
|
||||||
opacity: 0.7;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-preview-close:hover,
|
.img_close {
|
||||||
.image-preview-close:focus {
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
text-decoration: none;
|
transform: scale(0.1);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@@ -4,57 +4,35 @@ import {Many2ManyCheckboxesField} from "@web/views/fields/many2many_checkboxes/m
|
|||||||
import {registry} from "@web/core/registry";
|
import {registry} from "@web/core/registry";
|
||||||
|
|
||||||
export class MyCustomWidget extends Many2ManyCheckboxesField {
|
export class MyCustomWidget extends Many2ManyCheckboxesField {
|
||||||
|
// 你可以重写或者添加一些方法和属性
|
||||||
|
// 例如,你可以重写setup方法来添加一些事件监听器或者初始化一些变量
|
||||||
setup() {
|
setup() {
|
||||||
super.setup();
|
super.setup(); // 调用父类的setup方法
|
||||||
|
// 你自己的代码
|
||||||
}
|
}
|
||||||
|
|
||||||
onImageClick(event, src) {
|
onImageClick(event) {
|
||||||
event.preventDefault();
|
// 放大图片逻辑
|
||||||
event.stopPropagation();
|
// 获取图片元素
|
||||||
|
const img = event.target;
|
||||||
|
const close = img.nextSibling;
|
||||||
|
|
||||||
// 创建预览框
|
// 实现放大图片逻辑
|
||||||
const previewContainer = document.createElement('div');
|
// 比如使用 CSS 放大
|
||||||
previewContainer.className = 'image-preview-container';
|
img.parentElement.classList.add('zoomed');
|
||||||
|
close.classList.add('img_close');
|
||||||
const previewImg = document.createElement('img');
|
|
||||||
previewImg.src = src;
|
|
||||||
previewImg.className = 'image-preview';
|
|
||||||
// 设置放大的预览图片大小
|
|
||||||
previewImg.style.width = '600px';
|
|
||||||
previewImg.style.height = 'auto'; // 保持宽高比
|
|
||||||
|
|
||||||
const closeButton = document.createElement('span');
|
|
||||||
closeButton.innerHTML = '×';
|
|
||||||
closeButton.className = 'image-preview-close';
|
|
||||||
|
|
||||||
previewContainer.appendChild(previewImg);
|
|
||||||
previewContainer.appendChild(closeButton);
|
|
||||||
document.body.appendChild(previewContainer);
|
|
||||||
|
|
||||||
// 添加关闭预览的事件监听器
|
|
||||||
const closePreview = () => {
|
|
||||||
previewContainer.classList.remove('show');
|
|
||||||
setTimeout(() => {
|
|
||||||
document.body.removeChild(previewContainer);
|
|
||||||
}, 300);
|
|
||||||
};
|
|
||||||
|
|
||||||
closeButton.addEventListener('click', closePreview);
|
|
||||||
|
|
||||||
// 点击预览框外部也可以关闭
|
|
||||||
previewContainer.addEventListener('click', (e) => {
|
|
||||||
if (e.target === previewContainer) {
|
|
||||||
closePreview();
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// 使用 setTimeout 来触发过渡效果
|
onCloseClick(event) {
|
||||||
setTimeout(() => {
|
const close = event.target;
|
||||||
previewContainer.classList.add('show');
|
const img = close.previousSibling;
|
||||||
}, 10);
|
img.parentElement.classList.remove('zoomed');
|
||||||
|
close.classList.remove('img_close');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MyCustomWidget.template = "jikimo_frontend.MyCustomWidget";
|
MyCustomWidget.template = "jikimo_frontend.MyCustomWidget";
|
||||||
|
// MyCustomWidget.supportedTypes = ['many2many'];
|
||||||
|
|
||||||
registry.category("fields").add("custom_many2many_checkboxes", MyCustomWidget);
|
registry.category("fields").add("custom_many2many_checkboxes", MyCustomWidget);
|
||||||
|
|
||||||
|
|||||||
@@ -2,20 +2,25 @@
|
|||||||
<templates xml:space="preserve">
|
<templates xml:space="preserve">
|
||||||
|
|
||||||
<t t-name="jikimo_frontend.MyCustomWidget" owl="1">
|
<t t-name="jikimo_frontend.MyCustomWidget" owl="1">
|
||||||
<div aria-atomic="true" class="many2many_flex processing-capabilities-grid">
|
<div aria-atomic="true" class="many2many_flex">
|
||||||
<t t-foreach="items" t-as="item" t-key="item[0]">
|
<t t-foreach="items" t-as="item" t-key="item[0]">
|
||||||
<div class="grid-item">
|
<div>
|
||||||
<CheckBox
|
<CheckBox
|
||||||
value="isSelected(item)"
|
value="isSelected(item)"
|
||||||
disabled="props.readonly"
|
disabled="props.readonly"
|
||||||
onChange="(ev) => this.onChange(item[0], ev)"
|
onChange="(ev) => this.onChange(item[0], ev)"
|
||||||
>
|
>
|
||||||
<div class="item-content">
|
<t t-esc="item[1]"/>
|
||||||
<img t-att-src="item[2]" class="item-icon" t-on-click="(ev) => this.onImageClick(ev, item[2])"/>
|
|
||||||
<span class="item-label"><t t-esc="item[1]"/></span>
|
|
||||||
</div>
|
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
|
<div t-on-dblclick="onImageClick">
|
||||||
|
<t>
|
||||||
|
<img t-att-src="item[2]" width="50" height="50"/>
|
||||||
|
<div class="close" t-on-click="onCloseClick">×</div>
|
||||||
|
</t>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</t>
|
</t>
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
|
|||||||
@@ -139,7 +139,6 @@ patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
|
|||||||
owl.onMounted(() => {
|
owl.onMounted(() => {
|
||||||
this.activeElement = this.uiService.activeElement;
|
this.activeElement = this.uiService.activeElement;
|
||||||
this.setRequired()
|
this.setRequired()
|
||||||
this.listherHeaderBodyNum()
|
|
||||||
})
|
})
|
||||||
return this._super(...arguments);
|
return this._super(...arguments);
|
||||||
},
|
},
|
||||||
@@ -166,26 +165,6 @@ patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
listherHeaderBodyNum() {
|
|
||||||
const dom = this.tableRef.el
|
|
||||||
try {
|
|
||||||
const thead = $(dom).children('thead')
|
|
||||||
const tbody = $(dom).children('tbody')
|
|
||||||
const thead_tr = thead.children().eq(0)
|
|
||||||
const tbody_tr = tbody.children().eq(0)
|
|
||||||
const thead_th_num = thead_tr.children().length
|
|
||||||
const tbody_tr_num = tbody_tr.children().length
|
|
||||||
const num = thead_th_num - tbody_tr_num
|
|
||||||
if(num == -1) {
|
|
||||||
tbody.children('tr').each(function () {
|
|
||||||
$(this).children('td').eq(0).remove()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -108,10 +108,6 @@ td.o_required_modifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.color_3 {
|
.color_3 {
|
||||||
background-color: #808080;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color_4 {
|
|
||||||
background-color: rgb(255, 150, 0);
|
background-color: rgb(255, 150, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,7 +532,3 @@ div:has(.o_required_modifier) > label::before {
|
|||||||
position: unset;
|
position: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修复搜索面板checkbox样式
|
|
||||||
.o_search_panel .form-check .form-check-label span {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
@@ -190,7 +190,7 @@ def _create(self, data_list):
|
|||||||
# 如果该用户组被限制创建或更新操作
|
# 如果该用户组被限制创建或更新操作
|
||||||
if rec['is_create_or_update']:
|
if rec['is_create_or_update']:
|
||||||
raise UserError(
|
raise UserError(
|
||||||
_("您没有执行此操作的权限。请联系管理员"))
|
_("You are restricted from performing this operation. Please contact the administrator."))
|
||||||
else:
|
else:
|
||||||
# 如果 'access.right' 模型不存在,可以在这里定义备选逻辑
|
# 如果 'access.right' 模型不存在,可以在这里定义备选逻辑
|
||||||
# 例如,记录日志、发送通知或者简单地跳过这部分逻辑
|
# 例如,记录日志、发送通知或者简单地跳过这部分逻辑
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from . import controllers
|
|
||||||
from . import models
|
|
||||||
from . import wizards
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
{
|
|
||||||
'name': "机企猫 采购审批流程",
|
|
||||||
|
|
||||||
'summary': """
|
|
||||||
Short (1 phrase/line) summary of the module's purpose, used as
|
|
||||||
subtitle on modules listing or apps.openerp.com""",
|
|
||||||
|
|
||||||
'description': """
|
|
||||||
Long description of module's purpose
|
|
||||||
""",
|
|
||||||
|
|
||||||
'author': "My Company",
|
|
||||||
'website': "https://www.yourcompany.com",
|
|
||||||
|
|
||||||
# Categories can be used to filter modules in modules listing
|
|
||||||
# Check https://github.com/odoo/odoo/blob/16.0/odoo/addons/base/data/ir_module_category_data.xml
|
|
||||||
# for the full list
|
|
||||||
'category': 'Uncategorized',
|
|
||||||
'version': '0.1',
|
|
||||||
|
|
||||||
# any module necessary for this one to work correctly
|
|
||||||
'depends': ['purchase', 'purchase_tier_validation', 'documents', 'purchase_request', 'account', 'purchase_order_approved'],
|
|
||||||
|
|
||||||
# always loaded
|
|
||||||
'data': [
|
|
||||||
'security/ir.model.access.csv',
|
|
||||||
'data/documents_data.xml',
|
|
||||||
'wizards/upload_file_wizard_view.xml',
|
|
||||||
'views/views.xml',
|
|
||||||
],
|
|
||||||
# only loaded in demonstration mode
|
|
||||||
'demo': [
|
|
||||||
'demo/demo.xml',
|
|
||||||
],
|
|
||||||
|
|
||||||
'assets': {
|
|
||||||
'web.assets_backend': [
|
|
||||||
'jikimo_purchase_tier_validation/static/src/js/ir_model_extend.js',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from . import controllers
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# from odoo import http
|
|
||||||
|
|
||||||
|
|
||||||
# class JikimoPurchaseTierValidation(http.Controller):
|
|
||||||
# @http.route('/jikimo_purchase_tier_validation/jikimo_purchase_tier_validation', auth='public')
|
|
||||||
# def index(self, **kw):
|
|
||||||
# return "Hello, world"
|
|
||||||
|
|
||||||
# @http.route('/jikimo_purchase_tier_validation/jikimo_purchase_tier_validation/objects', auth='public')
|
|
||||||
# def list(self, **kw):
|
|
||||||
# return http.request.render('jikimo_purchase_tier_validation.listing', {
|
|
||||||
# 'root': '/jikimo_purchase_tier_validation/jikimo_purchase_tier_validation',
|
|
||||||
# 'objects': http.request.env['jikimo_purchase_tier_validation.jikimo_purchase_tier_validation'].search([]),
|
|
||||||
# })
|
|
||||||
|
|
||||||
# @http.route('/jikimo_purchase_tier_validation/jikimo_purchase_tier_validation/objects/<model("jikimo_purchase_tier_validation.jikimo_purchase_tier_validation"):obj>', auth='public')
|
|
||||||
# def object(self, obj, **kw):
|
|
||||||
# return http.request.render('jikimo_purchase_tier_validation.object', {
|
|
||||||
# 'object': obj
|
|
||||||
# })
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<odoo>
|
|
||||||
<data noupdate="1">
|
|
||||||
<!-- 创建采购合同文件夹 -->
|
|
||||||
<record id="documents_purchase_contracts_folder" model="documents.folder">
|
|
||||||
<field name="name">采购合同</field>
|
|
||||||
<field name="description">存放采购合同相关文件</field>
|
|
||||||
<field name="sequence">10</field>
|
|
||||||
</record>
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<odoo>
|
|
||||||
<data>
|
|
||||||
<!--
|
|
||||||
<record id="object0" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
|
|
||||||
<field name="name">Object 0</field>
|
|
||||||
<field name="value">0</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="object1" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
|
|
||||||
<field name="name">Object 1</field>
|
|
||||||
<field name="value">10</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="object2" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
|
|
||||||
<field name="name">Object 2</field>
|
|
||||||
<field name="value">20</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="object3" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
|
|
||||||
<field name="name">Object 3</field>
|
|
||||||
<field name="value">30</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="object4" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
|
|
||||||
<field name="name">Object 4</field>
|
|
||||||
<field name="value">40</field>
|
|
||||||
</record>
|
|
||||||
-->
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from . import models
|
|
||||||
@@ -1,215 +0,0 @@
|
|||||||
from odoo import models, fields, api, _
|
|
||||||
from odoo.exceptions import ValidationError
|
|
||||||
import logging
|
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class jikimo_purchase_tier_validation(models.Model):
|
|
||||||
_name = 'purchase.order'
|
|
||||||
_inherit = ['purchase.order', 'tier.validation']
|
|
||||||
_description = "采购订单"
|
|
||||||
|
|
||||||
_tier_validation_buttons_xpath = "/form/header/button[@id='draft_confirm'][1]"
|
|
||||||
|
|
||||||
contract_document_id = fields.Many2one('documents.document', string='合同文件')
|
|
||||||
contract_file = fields.Binary(related='contract_document_id.datas', string='合同文件内容')
|
|
||||||
contract_file_name = fields.Char(related='contract_document_id.attachment_id.name', string='文件名')
|
|
||||||
|
|
||||||
# 是否已上传合同文件
|
|
||||||
is_upload_contract_file = fields.Boolean(string='是否已上传合同文件', default=False)
|
|
||||||
|
|
||||||
def button_confirm(self):
|
|
||||||
for record in self:
|
|
||||||
if record.need_validation and record.validation_status != 'validated':
|
|
||||||
raise ValidationError(_('此操作需要至少对一条记录进行审批。\n请发起审批申请。'))
|
|
||||||
if record.state in ['to approve']:
|
|
||||||
raise ValidationError(_('请先完成审批。'))
|
|
||||||
if record.state == 'approved':
|
|
||||||
record.state = 'purchase'
|
|
||||||
return super().button_confirm()
|
|
||||||
|
|
||||||
# def button_confirm(self):
|
|
||||||
# self = self.with_context(skip_validation=True)
|
|
||||||
# return super().button_confirm()
|
|
||||||
#
|
|
||||||
# def _check_state_conditions(self, vals):
|
|
||||||
# self.ensure_one()
|
|
||||||
# if self._context.get('skip_validation'):
|
|
||||||
# return False
|
|
||||||
# return (
|
|
||||||
# self._check_state_from_condition()
|
|
||||||
# and vals.get(self._state_field) in self._state_to
|
|
||||||
# )
|
|
||||||
|
|
||||||
def request_validation(self):
|
|
||||||
for record in self:
|
|
||||||
error_messages = []
|
|
||||||
|
|
||||||
# 检查必填字段
|
|
||||||
required_fields = {
|
|
||||||
'partner_ref': '合同名称',
|
|
||||||
'contract_number': '合同编号'
|
|
||||||
}
|
|
||||||
|
|
||||||
missing_fields = [
|
|
||||||
name for field, name in required_fields.items()
|
|
||||||
if not record[field]
|
|
||||||
]
|
|
||||||
|
|
||||||
if missing_fields:
|
|
||||||
error_messages.append('* 如下字段要求必须填写:%s' % '、'.join(missing_fields))
|
|
||||||
|
|
||||||
# 检查合同文件
|
|
||||||
if not record.contract_document_id:
|
|
||||||
error_messages.append('* 必须点击上传合同文件')
|
|
||||||
|
|
||||||
# 如果有任何错误,一次性显示所有错误信息
|
|
||||||
if error_messages:
|
|
||||||
raise ValidationError('\n'.join(error_messages))
|
|
||||||
|
|
||||||
# 添加通知消息
|
|
||||||
if hasattr(record, 'message_post'):
|
|
||||||
current_user = self.env.user.name
|
|
||||||
record.message_post(
|
|
||||||
body=f"<strong>{current_user}</strong> 提交审批",
|
|
||||||
message_type='notification',
|
|
||||||
subtype_xmlid='mail.mt_note'
|
|
||||||
)
|
|
||||||
res = super(jikimo_purchase_tier_validation, self).request_validation()
|
|
||||||
self.state = 'to approve'
|
|
||||||
return res
|
|
||||||
|
|
||||||
def restart_validation(self):
|
|
||||||
res = super(jikimo_purchase_tier_validation, self).restart_validation()
|
|
||||||
self.state = 'draft'
|
|
||||||
return res
|
|
||||||
|
|
||||||
def _validate_tier(self, tiers=False):
|
|
||||||
res = super(jikimo_purchase_tier_validation, self)._validate_tier(tiers)
|
|
||||||
tier_reviews = tiers or self.review_ids
|
|
||||||
|
|
||||||
# 检查是否所有审批都已通过
|
|
||||||
all_approved = all(
|
|
||||||
tier_review.status == 'approved'
|
|
||||||
for tier_review in tier_reviews
|
|
||||||
)
|
|
||||||
|
|
||||||
if all_approved and tier_reviews: # 确保有审批记录
|
|
||||||
self.state = 'approved'
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
def _rejected_tier(self, tiers=False):
|
|
||||||
res = super(jikimo_purchase_tier_validation, self)._rejected_tier(tiers)
|
|
||||||
self.state = 'draft'
|
|
||||||
return res
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def _get_under_validation_exceptions(self):
|
|
||||||
res = super(jikimo_purchase_tier_validation, self)._get_under_validation_exceptions()
|
|
||||||
res.append("state")
|
|
||||||
return res
|
|
||||||
|
|
||||||
# 上传合同文件
|
|
||||||
def upload_contract_file(self):
|
|
||||||
print('upload_contract_file===========================')
|
|
||||||
# self.ensure_one()
|
|
||||||
# return {
|
|
||||||
# 'name': _('上传合同文件'),
|
|
||||||
# 'type': 'ir.actions.act_window',
|
|
||||||
# 'res_model': 'ir.attachment',
|
|
||||||
# 'view_mode': 'form',
|
|
||||||
# 'view_type': 'form',
|
|
||||||
# 'target': 'new',
|
|
||||||
# 'context': {
|
|
||||||
# 'default_res_model': self._name,
|
|
||||||
# 'default_res_id': self.id,
|
|
||||||
# 'default_type': 'binary',
|
|
||||||
# 'default_mimetype': 'application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,image/jpeg,image/png',
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
|
|
||||||
self.ensure_one()
|
|
||||||
action = {
|
|
||||||
'type': 'ir.actions.act_window',
|
|
||||||
'name': _('上传合同文件'),
|
|
||||||
'res_model': 'ir.attachment.wizard', # 我们需要创建一个新的向导模型
|
|
||||||
'view_mode': 'form',
|
|
||||||
'target': 'new',
|
|
||||||
'context': {
|
|
||||||
'default_res_model': self._name,
|
|
||||||
'default_res_id': self.id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return action
|
|
||||||
|
|
||||||
# 删除合同文件
|
|
||||||
def delete_contract_file(self):
|
|
||||||
self.ensure_one()
|
|
||||||
if self.contract_document_id:
|
|
||||||
try:
|
|
||||||
document = self.contract_document_id
|
|
||||||
|
|
||||||
# 清空关联
|
|
||||||
self.write({
|
|
||||||
'contract_document_id': False,
|
|
||||||
'contract_file': False,
|
|
||||||
'contract_file_name': False
|
|
||||||
})
|
|
||||||
|
|
||||||
# 删除文档
|
|
||||||
if document:
|
|
||||||
document.with_context(no_attachment=True).sudo().unlink()
|
|
||||||
|
|
||||||
self.is_upload_contract_file = False
|
|
||||||
|
|
||||||
# 返回视图动作来刷新当前表单
|
|
||||||
return {
|
|
||||||
'type': 'ir.actions.act_window',
|
|
||||||
'res_model': 'purchase.order',
|
|
||||||
'res_id': self.id,
|
|
||||||
'view_mode': 'form',
|
|
||||||
'view_type': 'form',
|
|
||||||
'target': 'current',
|
|
||||||
'flags': {'mode': 'readonly'},
|
|
||||||
}
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
_logger.error('删除合同文件时出错: %s', str(e))
|
|
||||||
return {
|
|
||||||
'type': 'ir.actions.client',
|
|
||||||
'tag': 'display_notification',
|
|
||||||
'params': {
|
|
||||||
'title': _('错误'),
|
|
||||||
'message': _('删除文件时出现错误'),
|
|
||||||
'type': 'danger',
|
|
||||||
'sticky': True,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
'type': 'ir.actions.client',
|
|
||||||
'tag': 'display_notification',
|
|
||||||
'params': {
|
|
||||||
'title': _('提示'),
|
|
||||||
'message': _('没有需要删除的合同文件'),
|
|
||||||
'type': 'warning',
|
|
||||||
'sticky': False,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class jikimo_purchase_request(models.Model):
|
|
||||||
_inherit = 'purchase.request'
|
|
||||||
_description = "采购申请"
|
|
||||||
|
|
||||||
|
|
||||||
class jikimo_account_payment(models.Model):
|
|
||||||
_inherit = 'account.payment'
|
|
||||||
_description = "付款单"
|
|
||||||
|
|
||||||
|
|
||||||
class jikimo_account_move(models.Model):
|
|
||||||
_inherit = 'account.move'
|
|
||||||
_description = "发票账单"
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
|
||||||
access_ir_attachment_wizard,ir.attachment.wizard,model_ir_attachment_wizard,base.group_user,1,1,1,1
|
|
||||||
|
@@ -1,14 +0,0 @@
|
|||||||
/** @odoo-module **/
|
|
||||||
|
|
||||||
import {registerPatch} from "@mail/model/model_core";
|
|
||||||
|
|
||||||
registerPatch({
|
|
||||||
name: "ir.model.review",
|
|
||||||
fields: {
|
|
||||||
availableWebViews: {
|
|
||||||
compute() {
|
|
||||||
return ["list", "form", "activity"];
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<odoo>
|
|
||||||
<data>
|
|
||||||
<!--
|
|
||||||
<template id="listing">
|
|
||||||
<ul>
|
|
||||||
<li t-foreach="objects" t-as="object">
|
|
||||||
<a t-attf-href="#{ root }/objects/#{ object.id }">
|
|
||||||
<t t-esc="object.display_name"/>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
<template id="object">
|
|
||||||
<h1><t t-esc="object.display_name"/></h1>
|
|
||||||
<dl>
|
|
||||||
<t t-foreach="object._fields" t-as="field">
|
|
||||||
<dt><t t-esc="field"/></dt>
|
|
||||||
<dd><t t-esc="object[field]"/></dd>
|
|
||||||
</t>
|
|
||||||
</dl>
|
|
||||||
</template>
|
|
||||||
-->
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<odoo>
|
|
||||||
<data>
|
|
||||||
<record model="ir.ui.view" id="tier_validation_view_approved_purchase_order_form_inherit">
|
|
||||||
<field name="name">tier_validation_view_approved_purchase_order_form_inherit</field>
|
|
||||||
<field name="model">purchase.order</field>
|
|
||||||
<field name="inherit_id" ref="purchase_order_approved.purchase_order_form"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<xpath expr="//button[@name='button_release']" position="replace">
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.ui.view" id="tier_validation_view_purchase_order_form_inherit">
|
|
||||||
<field name="name">tier_validation_view_purchase_order_form_inherit</field>
|
|
||||||
<field name="model">purchase.order</field>
|
|
||||||
<field name="inherit_id" ref="purchase.purchase_order_form"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<xpath expr="//header/button[@name='button_approve']" position="replace">
|
|
||||||
</xpath>
|
|
||||||
<xpath expr="//header/button[@name='button_cancel']" position="replace">
|
|
||||||
</xpath>
|
|
||||||
<xpath expr="//header/field[@name='state']" position="replace">
|
|
||||||
<field name="state" widget="statusbar" statusbar_visible="draft,sent,to approve, approved, purchase" readonly="1"/>
|
|
||||||
</xpath>
|
|
||||||
|
|
||||||
|
|
||||||
<xpath expr="//header/button[last()]" position="after">
|
|
||||||
<button name="button_cancel" states="draft,to approve,sent,purchase" string="取消" type="object" data-hotkey="x" />
|
|
||||||
</xpath>
|
|
||||||
|
|
||||||
<xpath expr="//header/button[@name='action_rfq_send'][1]" position="before">
|
|
||||||
<field name="validation_status" invisible="1"/>
|
|
||||||
<field name="is_upload_contract_file" invisible="1"/>
|
|
||||||
<button name="upload_contract_file" string="上传合同" type="object" class="oe_highlight" attrs="{'invisible': ['|', '|', ('validation_status', '!=', 'no'), ('is_upload_contract_file', '=', True), ('state', 'not in', ['draft', 'sent'])]}"/>]}"/>
|
|
||||||
<button name="delete_contract_file" string="删除合同" type="object" class="oe_highlight" attrs="{'invisible': ['|', ('validation_status', '!=', 'no'), ('is_upload_contract_file', '=', False)]}"/>
|
|
||||||
</xpath>
|
|
||||||
<xpath expr="//notebook/page[1]" position="before">
|
|
||||||
<page string="合同" name="contract_documents"
|
|
||||||
attrs="{'invisible': [('contract_document_id', '=', False)]}"
|
|
||||||
autofocus="autofocus">
|
|
||||||
<group>
|
|
||||||
<group>
|
|
||||||
<field name="contract_document_id" invisible="1"/>
|
|
||||||
<field name="contract_file_name" invisible="1"/>
|
|
||||||
<field name="contract_file"
|
|
||||||
widget="adaptive_viewer"
|
|
||||||
filename="contract_file_name"/>
|
|
||||||
</group>
|
|
||||||
</group>
|
|
||||||
</page>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- actions opening views on models -->
|
|
||||||
<!--
|
|
||||||
<record model="ir.actions.act_window" id="jikimo_purchase_tier_validation.action_window">
|
|
||||||
<field name="name">jikimo_purchase_tier_validation window</field>
|
|
||||||
<field name="res_model">jikimo_purchase_tier_validation.jikimo_purchase_tier_validation</field>
|
|
||||||
<field name="view_mode">tree,form</field>
|
|
||||||
</record>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- server action to the one above -->
|
|
||||||
<!--
|
|
||||||
<record model="ir.actions.server" id="jikimo_purchase_tier_validation.action_server">
|
|
||||||
<field name="name">jikimo_purchase_tier_validation server</field>
|
|
||||||
<field name="model_id" ref="model_jikimo_purchase_tier_validation_jikimo_purchase_tier_validation"/>
|
|
||||||
<field name="state">code</field>
|
|
||||||
<field name="code">
|
|
||||||
action = {
|
|
||||||
"type": "ir.actions.act_window",
|
|
||||||
"view_mode": "tree,form",
|
|
||||||
"res_model": model._name,
|
|
||||||
}
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- Top menu item -->
|
|
||||||
<!--
|
|
||||||
<menuitem name="jikimo_purchase_tier_validation" id="jikimo_purchase_tier_validation.menu_root"/>
|
|
||||||
-->
|
|
||||||
<!-- menu categories -->
|
|
||||||
<!--
|
|
||||||
<menuitem name="Menu 1" id="jikimo_purchase_tier_validation.menu_1" parent="jikimo_purchase_tier_validation.menu_root"/>
|
|
||||||
<menuitem name="Menu 2" id="jikimo_purchase_tier_validation.menu_2" parent="jikimo_purchase_tier_validation.menu_root"/>
|
|
||||||
-->
|
|
||||||
<!-- actions -->
|
|
||||||
<!--
|
|
||||||
<menuitem name="List" id="jikimo_purchase_tier_validation.menu_1_list" parent="jikimo_purchase_tier_validation.menu_1"
|
|
||||||
action="jikimo_purchase_tier_validation.action_window"/>
|
|
||||||
<menuitem name="Server to list" id="jikimo_purchase_tier_validation" parent="jikimo_purchase_tier_validation.menu_2"
|
|
||||||
action="jikimo_purchase_tier_validation.action_server"/>
|
|
||||||
-->
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
from . import upload_file_wizard
|
|
||||||
from . import comment_wizard
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
from odoo import models, fields
|
|
||||||
|
|
||||||
|
|
||||||
class CommentWizard(models.TransientModel):
|
|
||||||
_inherit = "comment.wizard"
|
|
||||||
|
|
||||||
def add_comment(self):
|
|
||||||
|
|
||||||
rec = self.env[self.res_model].browse(self.res_id)
|
|
||||||
|
|
||||||
self.review_ids = rec.review_ids
|
|
||||||
|
|
||||||
result = super(CommentWizard, self).add_comment()
|
|
||||||
|
|
||||||
return result
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
from odoo import models, fields, api, _
|
|
||||||
|
|
||||||
|
|
||||||
class IrAttachmentWizard(models.TransientModel):
|
|
||||||
_name = 'ir.attachment.wizard'
|
|
||||||
_description = '文件上传向导'
|
|
||||||
|
|
||||||
attachment = fields.Binary(string='选择文件', required=True)
|
|
||||||
filename = fields.Char(string='文件名')
|
|
||||||
res_model = fields.Char()
|
|
||||||
res_id = fields.Integer()
|
|
||||||
|
|
||||||
# def action_upload_file(self):
|
|
||||||
# self.ensure_one()
|
|
||||||
# # 首先创建 ir.attachment
|
|
||||||
# attachment = self.env['ir.attachment'].create({
|
|
||||||
# 'name': self.filename,
|
|
||||||
# 'type': 'binary',
|
|
||||||
# 'datas': self.attachment,
|
|
||||||
# 'res_model': self.res_model,
|
|
||||||
# 'res_id': self.res_id,
|
|
||||||
# })
|
|
||||||
#
|
|
||||||
# # 获取默认的文档文件夹
|
|
||||||
# workspace = self.env['documents.folder'].search([('name', '=', '采购合同')], limit=1)
|
|
||||||
#
|
|
||||||
# # 创建 documents.document 记录
|
|
||||||
# document = self.env['documents.document'].create({
|
|
||||||
# 'name': self.filename,
|
|
||||||
# 'attachment_id': attachment.id,
|
|
||||||
# 'folder_id': workspace.id,
|
|
||||||
# 'res_model': self.res_model,
|
|
||||||
# 'res_id': self.res_id,
|
|
||||||
# })
|
|
||||||
#
|
|
||||||
# return {
|
|
||||||
# 'type': 'ir.actions.client',
|
|
||||||
# 'tag': 'display_notification',
|
|
||||||
# 'params': {
|
|
||||||
# 'title': _('成功'),
|
|
||||||
# 'message': _('文件上传成功'),
|
|
||||||
# 'type': 'success',
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
|
|
||||||
def action_upload_file(self):
|
|
||||||
self.ensure_one()
|
|
||||||
# 获取当前用户的 partner_id
|
|
||||||
current_partner = self.env.user.partner_id
|
|
||||||
# 首先创建 ir.attachment
|
|
||||||
attachment = self.env['ir.attachment'].create({
|
|
||||||
'name': self.filename,
|
|
||||||
'type': 'binary',
|
|
||||||
'datas': self.attachment,
|
|
||||||
'res_model': self.res_model,
|
|
||||||
'res_id': self.res_id,
|
|
||||||
})
|
|
||||||
|
|
||||||
# 获取默认的文档文件夹
|
|
||||||
workspace = self.env['documents.folder'].search([('name', '=', '采购合同')], limit=1)
|
|
||||||
|
|
||||||
# 创建 documents.document 记录
|
|
||||||
document = self.env['documents.document'].create({
|
|
||||||
'name': self.filename,
|
|
||||||
'attachment_id': attachment.id,
|
|
||||||
'folder_id': workspace.id,
|
|
||||||
'res_model': self.res_model,
|
|
||||||
'res_id': self.res_id,
|
|
||||||
'partner_id': current_partner.id,
|
|
||||||
})
|
|
||||||
|
|
||||||
# 更新采购订单的合同文档字段
|
|
||||||
purchase_order = self.env['purchase.order'].browse(self.res_id)
|
|
||||||
purchase_order.write({
|
|
||||||
'contract_document_id': document.id,
|
|
||||||
'is_upload_contract_file': True
|
|
||||||
})
|
|
||||||
|
|
||||||
# 显示成功消息并关闭向导
|
|
||||||
message = {
|
|
||||||
'type': 'ir.actions.client',
|
|
||||||
'tag': 'display_notification',
|
|
||||||
'params': {
|
|
||||||
'title': _('成功'),
|
|
||||||
'message': _('文件上传成功'),
|
|
||||||
'type': 'success',
|
|
||||||
'sticky': False, # 自动消失
|
|
||||||
'next': {
|
|
||||||
'type': 'ir.actions.act_window_close'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return message
|
|
||||||
|
|
||||||
|
|
||||||
# def action_upload_file(self):
|
|
||||||
# self.ensure_one()
|
|
||||||
# attachment = self.env['ir.attachment'].create({
|
|
||||||
# 'name': self.filename,
|
|
||||||
# 'type': 'binary',
|
|
||||||
# 'datas': self.attachment,
|
|
||||||
# 'res_model': self.res_model,
|
|
||||||
# 'res_id': self.res_id,
|
|
||||||
# })
|
|
||||||
# return {
|
|
||||||
# 'type': 'ir.actions.client',
|
|
||||||
# 'tag': 'display_notification',
|
|
||||||
# 'params': {
|
|
||||||
# 'title': _('成功'),
|
|
||||||
# 'message': _('文件上传成功'),
|
|
||||||
# 'type': 'success',
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<odoo>
|
|
||||||
<record id="view_upload_file_wizard_form" model="ir.ui.view">
|
|
||||||
<field name="name">ir.attachment.wizard.form</field>
|
|
||||||
<field name="model">ir.attachment.wizard</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<form string="上传文件">
|
|
||||||
<group>
|
|
||||||
<field name="attachment" widget="binary" filename="filename" options="{'accepted_file_extensions': '.pdf,.doc,.docx,.jpg,.jpeg,.png'}"/>
|
|
||||||
<field name="filename" invisible="1"/>
|
|
||||||
<field name="res_model" invisible="1"/>
|
|
||||||
<field name="res_id" invisible="1"/>
|
|
||||||
</group>
|
|
||||||
<footer>
|
|
||||||
<button name="action_upload_file" string="确认上传" type="object" class="btn-primary"/>
|
|
||||||
<button string="取消" class="btn-secondary" special="cancel"/>
|
|
||||||
</footer>
|
|
||||||
</form>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
</odoo>
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from . import models
|
|
||||||
from . import controllers
|
|
||||||
|
|
||||||
from odoo import api, SUPERUSER_ID
|
|
||||||
|
|
||||||
def _data_install(cr, registry):
|
|
||||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
|
||||||
# 获取所有需要设置的产品模板
|
|
||||||
env.ref('jikimo_sale_multiple_supply_methods.product_template_purchase').product_variant_id.write({'active': False, 'is_bfm': True})
|
|
||||||
env.ref('jikimo_sale_multiple_supply_methods.product_template_manual_processing').product_variant_id.write({'active': False, 'single_manufacturing': True, 'is_bfm': True})
|
|
||||||
env.ref('jikimo_sale_multiple_supply_methods.product_template_default').product_variant_id.write({'active': False, 'is_bfm': True})
|
|
||||||
env.ref('jikimo_sale_multiple_supply_methods.product_template_embryo_customer_provided').product_variant_id.write({'active': False})
|
|
||||||
env.ref('jikimo_sale_multiple_supply_methods.product_template_outsourcing').product_variant_id.write({'active': False, 'is_bfm': True})
|
|
||||||
|
|
||||||
# 为三步制造增加规则
|
|
||||||
warehouse = env['stock.warehouse'].search([('company_id', '=', env.company.id)], limit=1)
|
|
||||||
product_route_id = warehouse.pbm_route_id
|
|
||||||
# 创建规则:原料存货区 -> 制造前, 坯料存货区 -> 制造前, 制造后 -> 坯料存货区, 制造后 -> 成品存货区
|
|
||||||
raw_material_location_id = env['stock.location'].search([('name', '=', '坯料存货区')], limit=1)
|
|
||||||
picking_type_production = warehouse.pbm_type_id
|
|
||||||
picking_type_stock = warehouse.sam_type_id
|
|
||||||
material_location_id = env['stock.location'].search([('name', '=', '原料存货区')], limit=1)
|
|
||||||
# 为mto增加规则
|
|
||||||
mto_route_id = env.ref('stock.route_warehouse0_mto')
|
|
||||||
# 创建规则:原料存货区 -> 外包位置, 坯料存货区 -> 外包位置
|
|
||||||
subcontracting_location_id = env.company.subcontracting_location_id
|
|
||||||
picking_type_subcontracting = warehouse.subcontracting_resupply_type_id
|
|
||||||
# 为补给外包商增加规则
|
|
||||||
resupply_route_id = warehouse.subcontracting_route_id
|
|
||||||
|
|
||||||
rules_data = [
|
|
||||||
{
|
|
||||||
'name': 'WH: 原料存货区 → 制造前',
|
|
||||||
'location_src_id': material_location_id.id,
|
|
||||||
'location_dest_id': warehouse.pbm_loc_id.id,
|
|
||||||
'route_id': product_route_id.id,
|
|
||||||
'picking_type_id': picking_type_production.id,
|
|
||||||
'action': 'pull',
|
|
||||||
'sequence': 20,
|
|
||||||
'warehouse_id': warehouse.id,
|
|
||||||
'procure_method': 'mts_else_mto',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'name': 'WH: 坯料存货区 → 制造前',
|
|
||||||
'location_src_id': raw_material_location_id.id,
|
|
||||||
'location_dest_id': warehouse.pbm_loc_id.id,
|
|
||||||
'route_id': product_route_id.id,
|
|
||||||
'picking_type_id': picking_type_production.id,
|
|
||||||
'action': 'pull',
|
|
||||||
'sequence': 21,
|
|
||||||
'warehouse_id': warehouse.id,
|
|
||||||
'procure_method': 'mts_else_mto',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'name': 'WH: 制造后 → 坯料存货区',
|
|
||||||
'location_src_id': warehouse.sam_loc_id.id,
|
|
||||||
'location_dest_id': raw_material_location_id.id,
|
|
||||||
'route_id': product_route_id.id,
|
|
||||||
'picking_type_id': picking_type_stock.id,
|
|
||||||
'action': 'push',
|
|
||||||
'sequence': 23,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'name': 'WH: 制造后 → 成品存货区',
|
|
||||||
'location_src_id': warehouse.sam_loc_id.id,
|
|
||||||
'location_dest_id': env['stock.location'].search([('name', '=', '成品存货区')], limit=1).id,
|
|
||||||
'route_id': product_route_id.id,
|
|
||||||
'picking_type_id': picking_type_stock.id,
|
|
||||||
'action': 'push',
|
|
||||||
'sequence': 24,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'name': 'WH: 原料存货区 → 外包位置 (MTO)',
|
|
||||||
'location_src_id': material_location_id.id,
|
|
||||||
'location_dest_id': subcontracting_location_id.id,
|
|
||||||
'route_id': mto_route_id.id,
|
|
||||||
'picking_type_id': picking_type_subcontracting.id,
|
|
||||||
'action': 'pull',
|
|
||||||
'sequence': 24,
|
|
||||||
'warehouse_id': warehouse.id,
|
|
||||||
'procure_method': 'mts_else_mto',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'name': 'WH: 坯料存货区 → 外包位置 (MTO)',
|
|
||||||
'location_src_id': raw_material_location_id.id,
|
|
||||||
'location_dest_id': subcontracting_location_id.id,
|
|
||||||
'route_id': mto_route_id.id,
|
|
||||||
'picking_type_id': picking_type_subcontracting.id,
|
|
||||||
'action': 'pull',
|
|
||||||
'sequence': 25,
|
|
||||||
'warehouse_id': warehouse.id,
|
|
||||||
'procure_method': 'mts_else_mto',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'name': 'WH: 坯料存货区 → 外包位置',
|
|
||||||
'location_src_id': raw_material_location_id.id,
|
|
||||||
'location_dest_id': subcontracting_location_id.id,
|
|
||||||
'route_id': resupply_route_id.id,
|
|
||||||
'picking_type_id': picking_type_subcontracting.id,
|
|
||||||
'action': 'pull',
|
|
||||||
'sequence': 26,
|
|
||||||
'warehouse_id': warehouse.id,
|
|
||||||
'procure_method': 'make_to_stock',
|
|
||||||
}
|
|
||||||
]
|
|
||||||
# 遍历每个规则数据,执行创建或更新操作
|
|
||||||
for rule_data in rules_data:
|
|
||||||
_create_or_update_stock_rule(env, rule_data)
|
|
||||||
|
|
||||||
def _create_or_update_stock_rule(env, rule_data):
|
|
||||||
# 尝试查找现有的 stock.rule
|
|
||||||
existing_rule = env['stock.rule'].search([
|
|
||||||
('name', '=', rule_data['name']),
|
|
||||||
('route_id', '=', rule_data.get('route_id'))
|
|
||||||
], limit=1)
|
|
||||||
|
|
||||||
if existing_rule:
|
|
||||||
# 如果存在,更新现有记录
|
|
||||||
existing_rule.write(rule_data)
|
|
||||||
else:
|
|
||||||
# 如果不存在,创建新记录
|
|
||||||
env['stock.rule'].create(rule_data)
|
|
||||||
|
|
||||||
def _data_uninstall(cr, registry):
|
|
||||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
|
||||||
warehouse = env['stock.warehouse'].search([('company_id', '=', env.company.id)], limit=1)
|
|
||||||
product_route_id = warehouse.pbm_route_id
|
|
||||||
resupply_route_id = warehouse.subcontracting_route_id
|
|
||||||
mto_route_id = env.ref('stock.route_warehouse0_mto')
|
|
||||||
# Fail unlink means that the route is used somewhere (e.g. route_id on stock.rule). In this case
|
|
||||||
# we don't try to do anything.
|
|
||||||
try:
|
|
||||||
with env.cr.savepoint():
|
|
||||||
env['stock.rule'].search([('name', 'in', ('WH: 原料存货区 → 制造前', 'WH: 坯料存货区 → 制造前', 'WH: 制造后 → 坯料存货区', 'WH: 制造后 → 成品存货区')), ('route_id', '=', product_route_id.id)]).unlink()
|
|
||||||
env['stock.rule'].search([('name', 'in', ('WH: 原料存货区 → 外包位置 (MTO)', 'WH: 坯料存货区 → 外包位置 (MTO)')), ('route_id', '=', mto_route_id.id)]).unlink()
|
|
||||||
env['stock.rule'].search([('name', '=', 'WH: 坯料存货区 → 外包位置'), ('route_id', '=', resupply_route_id.id)]).unlink()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
{
|
|
||||||
'name': '机企猫 多供货方式',
|
|
||||||
'version': '16.0.1.0.0',
|
|
||||||
'summary': """ 报价单提供(自动化产线加工/人工线下加工/外购/委外加工)多种供货方式选择 """,
|
|
||||||
'author': 'fox',
|
|
||||||
'website': '',
|
|
||||||
'category': '',
|
|
||||||
'depends': ['sf_dlm', 'sale_stock', 'sf_sale', 'sale'],
|
|
||||||
"data": [
|
|
||||||
'security/ir.model.access.csv',
|
|
||||||
'data/stock_routes.xml',
|
|
||||||
'data/product_data.xml',
|
|
||||||
# 'views/product_product_views.xml',
|
|
||||||
],'assets': {
|
|
||||||
# 'web.assets_backend': [
|
|
||||||
# 'jikimo_sale_multiple_supply_methods/static/src/**/*'
|
|
||||||
# ],
|
|
||||||
},
|
|
||||||
'post_init_hook': '_data_install',
|
|
||||||
'uninstall_hook': '_data_uninstall',
|
|
||||||
'application': True,
|
|
||||||
'installable': True,
|
|
||||||
'auto_install': False,
|
|
||||||
'license': 'LGPL-3',
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<odoo>
|
|
||||||
<data noupdate="1">
|
|
||||||
<record id="product_template_manual_processing" model="product.template">
|
|
||||||
<field name="name">人工线下加工模板</field>
|
|
||||||
<field name="active" eval="False"/>
|
|
||||||
<field name="categ_id" ref="sf_dlm.product_category_finished_sf"/>
|
|
||||||
<field name="route_ids"
|
|
||||||
eval="[ref('stock.route_warehouse0_mto'), ref('mrp.route_warehouse0_manufacture')]"/>
|
|
||||||
<field name="invoice_policy">delivery</field>
|
|
||||||
<field name="detailed_type">product</field>
|
|
||||||
<field name="purchase_ok">false</field>
|
|
||||||
<field name="uom_id" ref="uom.product_uom_unit"/>
|
|
||||||
<field name="uom_po_id" ref="uom.product_uom_unit"/>
|
|
||||||
<field name="company_id" ref="base.main_company"/>
|
|
||||||
<field name="single_manufacturing">true</field>
|
|
||||||
<field name="tracking">serial</field>
|
|
||||||
<!-- <field name="categ_type">成品</field> -->
|
|
||||||
<field name="is_manual_processing">true</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="product_template_purchase" model="product.template">
|
|
||||||
<field name="name">成品外购模板</field>
|
|
||||||
<field name="active" eval="False"/>
|
|
||||||
<field name="categ_id" ref="sf_dlm.product_category_finished_sf"/>
|
|
||||||
<field name="route_ids"
|
|
||||||
eval="[ref('stock.route_warehouse0_mto'), ref('purchase_stock.route_warehouse0_buy')]"/>
|
|
||||||
<field name="tracking">serial</field>
|
|
||||||
<field name="detailed_type">product</field>
|
|
||||||
<field name="uom_id" ref="uom.product_uom_unit"/>
|
|
||||||
<field name="uom_po_id" ref="uom.product_uom_unit"/>
|
|
||||||
<field name="company_id" ref="base.main_company"/>
|
|
||||||
<!-- <field name="categ_type">成品</field> -->
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="product_template_outsourcing" model="product.template">
|
|
||||||
<field name="name">成品委外加工模板</field>
|
|
||||||
<field name="active" eval="False"/>
|
|
||||||
<field name="categ_id" ref="sf_dlm.product_category_finished_sf"/>
|
|
||||||
<field name="route_ids"
|
|
||||||
eval="[ref('stock.route_warehouse0_mto'), ref('purchase_stock.route_warehouse0_buy'), ref('mrp_subcontracting.route_resupply_subcontractor_mto')]"/>
|
|
||||||
<field name="tracking">serial</field>
|
|
||||||
<field name="detailed_type">product</field>
|
|
||||||
<field name="uom_id" ref="uom.product_uom_unit"/>
|
|
||||||
<field name="uom_po_id" ref="uom.product_uom_unit"/>
|
|
||||||
<field name="company_id" ref="base.main_company"/>
|
|
||||||
<!-- <field name="categ_type">成品</field> -->
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="product_template_default" model="product.template">
|
|
||||||
<field name="name">成品初始化模板</field>
|
|
||||||
<field name="active" eval="False"/>
|
|
||||||
<field name="categ_id" ref="sf_dlm.product_category_finished_sf"/>
|
|
||||||
<field name="route_ids" eval="[]"/>
|
|
||||||
<field name="tracking">serial</field>
|
|
||||||
<field name="detailed_type">product</field>
|
|
||||||
<field name="uom_id" ref="uom.product_uom_unit"/>
|
|
||||||
<field name="uom_po_id" ref="uom.product_uom_unit"/>
|
|
||||||
<field name="company_id" ref="base.main_company"/>
|
|
||||||
<!-- <field name="categ_type">成品</field> -->
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- 供应商信息(业务平台),由于数据是python创建,只能指定ID -->
|
|
||||||
<record id="product_supplierinfo_bfm" model="product.supplierinfo">
|
|
||||||
<field name="partner_id" eval="91"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="product_template_embryo_customer_provided" model="product.template">
|
|
||||||
<field name="name">坯料客供料模板</field>
|
|
||||||
<field name="active" eval="False"/>
|
|
||||||
<field name="categ_id" ref="sf_dlm.product_category_embryo_sf"/>
|
|
||||||
<field name="route_ids" eval="[
|
|
||||||
ref('stock.route_warehouse0_mto'),
|
|
||||||
ref('mrp_subcontracting.route_resupply_subcontractor_mto'),
|
|
||||||
ref('jikimo_sale_multiple_supply_methods.route_material_processing')]"/>
|
|
||||||
<field name="sale_ok">false</field>
|
|
||||||
<field name="tracking">serial</field>
|
|
||||||
<field name="detailed_type">product</field>
|
|
||||||
<field name="uom_id" ref="uom.product_uom_unit"/>
|
|
||||||
<field name="uom_po_id" ref="uom.product_uom_unit"/>
|
|
||||||
<field name="company_id" ref="base.main_company"/>
|
|
||||||
<!-- <field name="categ_type">坯料</field> -->
|
|
||||||
<field name="seller_ids" eval="[ref('jikimo_sale_multiple_supply_methods.product_supplierinfo_bfm')]"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="sf_dlm.product_embryo_sf_self_machining" model="product.product">
|
|
||||||
<field name="is_manual_processing">true</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="sf_dlm.product_embryo_sf_self_machining" model="product.product">
|
|
||||||
<field name="route_ids" eval="[(4, ref('mrp_subcontracting.route_resupply_subcontractor_mto'))]"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="sf_dlm.product_embryo_sf_purchase" model="product.product">
|
|
||||||
<field name="route_ids" eval="[(4, ref('mrp_subcontracting.route_resupply_subcontractor_mto'))]"/>
|
|
||||||
</record>
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<odoo>
|
|
||||||
<data noupdate="1">
|
|
||||||
<record id="route_material_processing" model="stock.route">
|
|
||||||
<field name="name">带料加工</field>
|
|
||||||
<field name="product_selectable">true</field>
|
|
||||||
<field name="warehouse_selectable">true</field>
|
|
||||||
<field name="warehouse_ids" eval="[ref('stock.warehouse0')]"/>
|
|
||||||
<field name="sequence">16</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="material_picking_in" model="stock.picking.type">
|
|
||||||
<field name="name">客供料入库</field>
|
|
||||||
<field name="code">incoming</field>
|
|
||||||
<field name="active">true</field>
|
|
||||||
<field name="company_id" ref="base.main_company"/>
|
|
||||||
<field name="sequence_code">DL</field>
|
|
||||||
<field name="warehouse_id" ref="stock.warehouse0"/>
|
|
||||||
<field name="default_location_src_id" ref="stock.stock_location_customers"/>
|
|
||||||
<field name="default_location_dest_id" eval="25"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="rule_material_receiving" model="stock.rule">
|
|
||||||
<field name="name">带料收货</field>
|
|
||||||
<field name="route_id" ref="route_material_processing"/>
|
|
||||||
<field name="location_dest_id" ref="stock.stock_location_company"/>
|
|
||||||
<field name="location_src_id" ref="stock.stock_location_customers"/>
|
|
||||||
<field name="picking_type_id" ref="material_picking_in"/>
|
|
||||||
<field name="action">pull</field>
|
|
||||||
</record>
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from . import product_template
|
|
||||||
from . import mrp_bom
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
from odoo import models, fields
|
|
||||||
|
|
||||||
class MrpBom(models.Model):
|
|
||||||
_inherit = 'mrp.bom'
|
|
||||||
|
|
||||||
# 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品后再次进行创建bom
|
|
||||||
def bom_create(self, product, bom_type, product_type):
|
|
||||||
bom_id = super(MrpBom, self).bom_create(product, bom_type, product_type)
|
|
||||||
|
|
||||||
# 成品的供应商从模板中获取
|
|
||||||
if product_type == 'product':
|
|
||||||
bom_id.subcontractor_id = product.product_tmpl_id.seller_ids.partner_id.id
|
|
||||||
return bom_id
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
from odoo import models, fields, api
|
|
||||||
|
|
||||||
class ProductTemplate(models.Model):
|
|
||||||
_inherit = 'product.template'
|
|
||||||
|
|
||||||
is_manual_processing = fields.Boolean(string='人工线下加工')
|
|
||||||
is_customer_provided = fields.Boolean(string='客供料')
|
|
||||||
|
|
||||||
def copy_template(self, product_template_id):
|
|
||||||
if not isinstance(product_template_id, ProductTemplate):
|
|
||||||
raise ValueError('%s必须是ProductTemplate类型' % product_template_id)
|
|
||||||
|
|
||||||
self.route_ids = product_template_id.route_ids
|
|
||||||
self.categ_id = product_template_id.categ_id
|
|
||||||
self.invoice_policy = product_template_id.invoice_policy
|
|
||||||
self.detailed_type = product_template_id.detailed_type
|
|
||||||
self.purchase_ok = product_template_id.purchase_ok
|
|
||||||
self.uom_id = product_template_id.uom_id
|
|
||||||
self.uom_po_id = product_template_id.uom_po_id
|
|
||||||
self.company_id = product_template_id.company_id
|
|
||||||
self.single_manufacturing = product_template_id.single_manufacturing
|
|
||||||
self.tracking = product_template_id.tracking
|
|
||||||
self.is_bfm = product_template_id.is_bfm
|
|
||||||
self.is_manual_processing = product_template_id.is_manual_processing
|
|
||||||
# 复制 seller_ids
|
|
||||||
self.seller_ids = [(0, 0, {'partner_id': seller.partner_id.id, 'delay': 1.0, 'price': seller.price}) for seller in product_template_id.seller_ids]
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
|
||||||
access_sale_order_group_production_engineer,sale.order_group_production_engineer,sale.model_sale_order,sf_base.group_production_engineer,1,1,0,0
|
|
||||||
access_sale_order_line_group_production_engineer,sale_order_line_group_production_engineer,sale.model_sale_order_line,sf_base.group_production_engineer,1,1,0,0
|
|
||||||
access_product_product_group_production_engineer,product_product_group_production_engineer,product.model_product_product,sf_base.group_production_engineer,1,0,0,0
|
|
||||||
access_product_template_group_production_engineer,product_template_group_production_engineer,product.model_product_template,sf_base.group_production_engineer,1,0,0,0
|
|
||||||
access_stock_picking_group_production_engineer,stock_picking_group_production_engineer,stock.model_stock_picking,sf_base.group_production_engineer,1,0,0,0
|
|
||||||
access_stock_move_group_production_engineer,stock_move_group_production_engineer,stock.model_stock_move,sf_base.group_production_engineer,1,0,0,0
|
|
||||||
access_mrp_bom_group_production_engineer,mrp_bom_group_production_engineer,mrp.model_mrp_bom,sf_base.group_production_engineer,1,0,0,0
|
|
||||||
|
@@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<odoo>
|
|
||||||
<!-- 由于该模块不能依赖sf_dlm_management, 该功能只能在sf_dlm_management中实现,并且依赖该模块-->
|
|
||||||
<record id="view_product_product_form_inherit_sf" model="ir.ui.view">
|
|
||||||
<field name="name">view.product.template.form.inherit.sf</field>
|
|
||||||
<field name="model">product.template</field>
|
|
||||||
<field name="inherit_id" ref="sf_dlm_management.view_sale_product_template_form_inherit_sf"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<xpath expr="//field[@name='manual_quotation']" position="after">
|
|
||||||
<field name="is_customer_provided" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])], 'readonly': True}" />
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
</odoo>
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from . import models
|
|
||||||
from . import controllers
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
||||||
{
|
|
||||||
'name': '机企猫 工单异常记录',
|
|
||||||
'version': '1.0',
|
|
||||||
'summary': '记录工单的异常日志',
|
|
||||||
'sequence': 1,
|
|
||||||
'category': 'sf',
|
|
||||||
'website': 'https://www.sf.jikimo.com',
|
|
||||||
'depends': ['sf_manufacturing', 'sf_mrs_connect'],
|
|
||||||
'data': [
|
|
||||||
'views/mrp_workorder_views.xml',
|
|
||||||
'security/ir.model.access.csv',
|
|
||||||
],
|
|
||||||
'demo': [
|
|
||||||
],
|
|
||||||
'license': 'LGPL-3',
|
|
||||||
'installable': True,
|
|
||||||
'application': False,
|
|
||||||
'auto_install': False,
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
from . import main
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
from odoo import http, fields
|
|
||||||
from odoo.http import request
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
from odoo.addons.sf_mrs_connect.controllers.controllers import Sf_Mrs_Connect
|
|
||||||
from odoo.addons.sf_manufacturing.controllers.controllers import Manufacturing_Connect
|
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
class WorkorderExceptionConroller(http.Controller):
|
|
||||||
|
|
||||||
@http.route('/AutoDeviceApi/BillError', type='json', auth='public', methods=['GET', 'POST'], csrf=False,
|
|
||||||
cors="*")
|
|
||||||
def workder_exception(self, **kw):
|
|
||||||
"""
|
|
||||||
记录工单异常
|
|
||||||
:param kw:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
_logger.info('workder_exception:%s' % kw)
|
|
||||||
try:
|
|
||||||
res = {'Succeed': True, 'ErrorCode': 0, 'Error': ''}
|
|
||||||
datas = request.httprequest.data
|
|
||||||
ret = json.loads(datas)
|
|
||||||
if not ret.get('RfidCode') or not ret.get('coding'):
|
|
||||||
res = {'Succeed': False, 'ErrorCode': 400, 'Error': '参数错误'}
|
|
||||||
return json.JSONEncoder().encode(res)
|
|
||||||
|
|
||||||
# 通过RfidCode获取就绪的CNC工单
|
|
||||||
workorder = request.env['mrp.workorder'].sudo().search([
|
|
||||||
('rfid_code', '=', ret['RfidCode']),
|
|
||||||
('routing_type', '=', 'CNC加工'),
|
|
||||||
])
|
|
||||||
if not workorder:
|
|
||||||
res = {'Succeed': False, 'ErrorCode': 401, 'Error': '无效的工单'}
|
|
||||||
return json.JSONEncoder().encode(res)
|
|
||||||
|
|
||||||
# 创建工单异常记录,关联工单
|
|
||||||
request.env['jikimo.workorder.exception'].sudo().create({
|
|
||||||
'workorder_id': workorder.id,
|
|
||||||
'exception_code': ret.get('coding'),
|
|
||||||
'exception_content': ret.get('Error', '')
|
|
||||||
})
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
|
||||||
_logger.info('workder_exception error:%s' % e)
|
|
||||||
return json.JSONEncoder().encode(res)
|
|
||||||
|
|
||||||
|
|
||||||
class SfMrsConnectController(Sf_Mrs_Connect):
|
|
||||||
|
|
||||||
@http.route('/api/cnc_processing/create', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
|
||||||
cors="*")
|
|
||||||
def get_cnc_processing_create(self, **kw):
|
|
||||||
"""
|
|
||||||
更新工单异常记录【'YC001', 'YC004'】
|
|
||||||
"""
|
|
||||||
res = super(SfMrsConnectController, self).get_cnc_processing_create(**kw)
|
|
||||||
# 如果有未完成的YC0001、YC0004异常记录,则标记为完成
|
|
||||||
res = json.loads(res)
|
|
||||||
_logger.info('已进入工单异常:%s' % res)
|
|
||||||
if res.get('production_ids'):
|
|
||||||
try:
|
|
||||||
productions = request.env['mrp.production'].sudo().search([('id', 'in', res.get('production_ids'))])
|
|
||||||
if productions.workorder_ids:
|
|
||||||
productions.workorder_ids.handle_exception(['YC0001', 'YC0004'])
|
|
||||||
except Exception as e:
|
|
||||||
_logger.info('更新工单异常记录失败:%s' % e)
|
|
||||||
return json.JSONEncoder().encode(res)
|
|
||||||
|
|
||||||
class ManufactruingController(Manufacturing_Connect):
|
|
||||||
|
|
||||||
@http.route('/AutoDeviceApi/FeedBackStart', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
|
|
||||||
cors="*")
|
|
||||||
def button_Work_START(self, **kw):
|
|
||||||
"""
|
|
||||||
更新工单异常记录【'YC0002', 'YC0003'】
|
|
||||||
"""
|
|
||||||
res = super(ManufactruingController, self).button_Work_START(**kw)
|
|
||||||
res = json.loads(res)
|
|
||||||
_logger.info('已进入工单异常:%s' % res)
|
|
||||||
if res.get('workorder_id'):
|
|
||||||
try:
|
|
||||||
workorder = request.env['mrp.workorder'].sudo().browse(int(res.get('workorder_id')))
|
|
||||||
workorder.handle_exception(['YC0002', 'YC0003'])
|
|
||||||
except Exception as e:
|
|
||||||
_logger.info('更新工单异常记录失败:%s' % e)
|
|
||||||
return json.JSONEncoder().encode(res)
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from . import jikimo_workorder_exception
|
|
||||||
from . import mrp_workorder
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
from odoo import models, fields
|
|
||||||
|
|
||||||
|
|
||||||
class JikimoWorkorderException(models.Model):
|
|
||||||
_name = 'jikimo.workorder.exception'
|
|
||||||
_description = '工单异常记录'
|
|
||||||
_order = 'id desc'
|
|
||||||
|
|
||||||
workorder_id = fields.Many2one('mrp.workorder', string='工单')
|
|
||||||
exception_code = fields.Char('异常编码')
|
|
||||||
exception_content = fields.Char('反馈的异常/问题信息')
|
|
||||||
completion_time = fields.Datetime('处理完成时间')
|
|
||||||
state = fields.Selection([('pending', '进行中'), ('done', '已处理')], string='状态', default='pending')
|
|
||||||
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
from odoo import models, fields
|
|
||||||
import logging
|
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
class MrpWorkorder(models.Model):
|
|
||||||
_inherit = 'mrp.workorder'
|
|
||||||
|
|
||||||
exception_ids = fields.One2many('jikimo.workorder.exception', 'workorder_id', string='工单异常记录')
|
|
||||||
|
|
||||||
def write(self, values):
|
|
||||||
if values.get('test_results') and self.exception_ids:
|
|
||||||
pending_exception = self.exception_ids.filtered(
|
|
||||||
lambda exc: exc.state == 'pending' and exc.exception_code == 'YC0005'
|
|
||||||
)
|
|
||||||
if pending_exception:
|
|
||||||
pending_exception.write({
|
|
||||||
'completion_time': fields.Datetime.now(),
|
|
||||||
'state': 'done'
|
|
||||||
})
|
|
||||||
return super(MrpWorkorder, self).write(values)
|
|
||||||
|
|
||||||
def handle_exception(self, exception_codes):
|
|
||||||
"""
|
|
||||||
处理异常
|
|
||||||
:param exception_codes: 需要处理的异常编码列表
|
|
||||||
"""
|
|
||||||
if not isinstance(exception_codes, list):
|
|
||||||
exception_codes = [exception_codes]
|
|
||||||
if self.exception_ids:
|
|
||||||
_logger.info('workorder.exception_ids:%s' % self.exception_ids)
|
|
||||||
pending_exception = self.exception_ids.filtered(
|
|
||||||
lambda exc: exc.state == 'pending' and exc.exception_code in exception_codes
|
|
||||||
)
|
|
||||||
_logger.info('pending_exception:%s' % pending_exception)
|
|
||||||
if pending_exception:
|
|
||||||
pending_exception.write({
|
|
||||||
'completion_time': fields.Datetime.now(),
|
|
||||||
'state': 'done'
|
|
||||||
})
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
|
|
||||||
"access_jikimo_workorder_exception","access.jikimo.workorder.exception","model_jikimo_workorder_exception","mrp.group_mrp_user",1,1,1,0
|
|
||||||
"access_jikimo_workorder_exception_group_quality","access.jikimo.workorder.exception.group_quality","model_jikimo_workorder_exception","sf_base.group_quality",1,1,1,0
|
|
||||||
"access_jikimo_workorder_exception_group_quality_director","access.jikimo.workorder.exception.group_quality_director","model_jikimo_workorder_exception","sf_base.group_quality_director",1,1,1,0
|
|
||||||
|
|
||||||
|
@@ -1,2 +0,0 @@
|
|||||||
from . import common
|
|
||||||
from . import test_jikimo_workorder_exception
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
||||||
from odoo import fields, Command
|
|
||||||
from odoo.tests.common import TransactionCase, HttpCase, tagged, Form
|
|
||||||
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
import base64
|
|
||||||
from lxml import etree
|
|
||||||
|
|
||||||
@tagged('post_install', '-at_install')
|
|
||||||
class TestJikimoWorkorderExceptionCommon(TransactionCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestJikimoWorkorderExceptionCommon, self).setUp()
|
|
||||||
# 获取名字为“1#自动生产线”的制造中心
|
|
||||||
workcenter = self.env['mrp.workcenter'].search([('name', '=', '1#自动生产线')], limit=1)
|
|
||||||
# 创建一个产品
|
|
||||||
product_product = self.env['product.product'].create({
|
|
||||||
'name': '测试产品',
|
|
||||||
'type': 'product',
|
|
||||||
})
|
|
||||||
uom_unit = self.env.ref('uom.product_uom_unit')
|
|
||||||
# 创建一个bom
|
|
||||||
self.bom = self.env['mrp.bom'].create({
|
|
||||||
'product_id': product_product.id,
|
|
||||||
'product_tmpl_id': product_product.product_tmpl_id.id,
|
|
||||||
'product_uom_id': uom_unit.id,
|
|
||||||
'product_qty': 1.0,
|
|
||||||
'type': 'normal',
|
|
||||||
})
|
|
||||||
# 创建一个制造订单
|
|
||||||
self.production = self.env['mrp.production'].create({
|
|
||||||
'name': 'Test Production',
|
|
||||||
'product_id': product_product.id,
|
|
||||||
'bom_id': self.bom.id,
|
|
||||||
'company_id': self.env.ref('base.main_company').id,
|
|
||||||
})
|
|
||||||
# 创建一个测试工单
|
|
||||||
self.workorder = self.env['mrp.workorder'].create({
|
|
||||||
'name': 'Test order',
|
|
||||||
'workcenter_id': workcenter.id,
|
|
||||||
'product_uom_id': self.bom.product_uom_id.id,
|
|
||||||
'production_id': self.production.id,
|
|
||||||
'duration_expected': 1.0,
|
|
||||||
'rfid_code': 'test-123456',
|
|
||||||
'routing_type': 'CNC加工'
|
|
||||||
})
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
import json
|
|
||||||
from datetime import datetime
|
|
||||||
from odoo.addons.jikimo_workorder_exception.tests.common import TestJikimoWorkorderExceptionCommon
|
|
||||||
|
|
||||||
class TestJikimoWorkorderException(TestJikimoWorkorderExceptionCommon):
|
|
||||||
|
|
||||||
def test_create_exception_record(self):
|
|
||||||
exception_record = self.env['jikimo.workorder.exception'].create({
|
|
||||||
'workorder_id': self.workorder.id,
|
|
||||||
'exception_code': 'YC0001',
|
|
||||||
'exception_content': '无CNC编程'
|
|
||||||
})
|
|
||||||
|
|
||||||
self.assertTrue(exception_record)
|
|
||||||
self.assertEqual(exception_record.exception_content, '无CNC编程')
|
|
||||||
self.assertEqual(exception_record.workorder_id.id, self.workorder.id)
|
|
||||||
self.assertEqual(exception_record.exception_code, 'YC0001')
|
|
||||||
|
|
||||||
def test_handle_exception(self):
|
|
||||||
exception_record = self.env['jikimo.workorder.exception'].create({
|
|
||||||
'workorder_id': self.workorder.id,
|
|
||||||
'exception_code': 'YC0001',
|
|
||||||
'exception_content': '无CNC编程'
|
|
||||||
})
|
|
||||||
self.workorder.handle_exception('YC0001')
|
|
||||||
self.assertEqual(exception_record.state, 'done')
|
|
||||||
# 判断完成时间是否为当前分钟
|
|
||||||
self.assertEqual(exception_record.completion_time.minute, datetime.now().minute)
|
|
||||||
|
|
||||||
def test_handle_exception_with_invalid_code(self):
|
|
||||||
exception_record = self.env['jikimo.workorder.exception'].create({
|
|
||||||
'workorder_id': self.workorder.id,
|
|
||||||
'exception_code': 'YC0001',
|
|
||||||
'exception_content': '无CNC编程'
|
|
||||||
})
|
|
||||||
self.workorder.handle_exception(['YC0002', 'YC0004'])
|
|
||||||
self.assertEqual(exception_record.state, 'pending')
|
|
||||||
self.assertEqual(exception_record.completion_time, False)
|
|
||||||
|
|
||||||
|
|
||||||
def test_handle_exception_with_test_results(self):
|
|
||||||
exception_record = self.env['jikimo.workorder.exception'].create({
|
|
||||||
'workorder_id': self.workorder.id,
|
|
||||||
'exception_code': 'YC0005',
|
|
||||||
'exception_content': '工单加工失败'
|
|
||||||
})
|
|
||||||
self.workorder.write({
|
|
||||||
'test_results': '返工',
|
|
||||||
'reason': 'cutter',
|
|
||||||
'detailed_reason': '刀坏了',
|
|
||||||
})
|
|
||||||
self.assertEqual(exception_record.state, 'done')
|
|
||||||
self.assertEqual(exception_record.completion_time.minute, datetime.now().minute)
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<odoo>
|
|
||||||
<data>
|
|
||||||
<record id="jikimo_workorder_exception_form_view_inherit" model="ir.ui.view">
|
|
||||||
<field name="name">mrp.workorder.form</field>
|
|
||||||
<field name="model">mrp.workorder</field>
|
|
||||||
<field name="inherit_id" ref="mrp.mrp_production_workorder_form_view_inherit"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<xpath expr="//notebook/page[last()]" position="after">
|
|
||||||
<page string="异常记录" name="workorder_exception" attrs="{'invisible': [('routing_type', '!=', 'CNC加工')]}">
|
|
||||||
<field name="exception_ids" nolabel="1" readonly="1">
|
|
||||||
<tree create="false" delete="false" edit="false">
|
|
||||||
<field name="exception_content" string="反馈的异常/问题信息"/>
|
|
||||||
<field name="create_date" string="时间"/>
|
|
||||||
<field name="completion_time"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</page>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from . import models
|
|
||||||
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
||||||
{
|
|
||||||
'name': '机企猫 工单异常消息通知',
|
|
||||||
'version': '1.0',
|
|
||||||
'summary': '当产生工单异常时,发送消息通知',
|
|
||||||
'sequence': 1,
|
|
||||||
'category': 'sf',
|
|
||||||
'website': 'https://www.sf.jikimo.com',
|
|
||||||
'depends': ['jikimo_workorder_exception', 'jikimo_message_notify'],
|
|
||||||
'data': [
|
|
||||||
'data/bussiness_node.xml',
|
|
||||||
'data/template_data.xml',
|
|
||||||
# 'security/ir.model.access.csv',
|
|
||||||
],
|
|
||||||
'demo': [
|
|
||||||
],
|
|
||||||
'license': 'LGPL-3',
|
|
||||||
'installable': True,
|
|
||||||
'application': False,
|
|
||||||
'auto_install': False,
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<?xml version="1.0" ?>
|
|
||||||
<odoo>
|
|
||||||
<data noupdate="1">
|
|
||||||
<record id="bussiness_no_functional_tool" model="jikimo.message.bussiness.node">
|
|
||||||
<field name="name">无功能刀具</field>
|
|
||||||
<field name="model">jikimo.workorder.exception</field>
|
|
||||||
</record>
|
|
||||||
<record id="bussiness_no_position_data" model="jikimo.message.bussiness.node">
|
|
||||||
<field name="name">无定位数据</field>
|
|
||||||
<field name="model">jikimo.workorder.exception</field>
|
|
||||||
</record>
|
|
||||||
<record id="bussiness_processing_failure" model="jikimo.message.bussiness.node">
|
|
||||||
<field name="name">加工失败</field>
|
|
||||||
<field name="model">jikimo.workorder.exception</field>
|
|
||||||
</record>
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
<?xml version="1.0" ?>
|
|
||||||
<odoo>
|
|
||||||
<data noupdate="1">
|
|
||||||
<record id="template_no_function_tool" model="jikimo.message.template">
|
|
||||||
<field name="name">生产线无功能刀具提醒</field>
|
|
||||||
<field name="model_id" ref="jikimo_workorder_exception_notify.model_jikimo_workorder_exception"/>
|
|
||||||
<field name="model">jikimo.workorder.exception</field>
|
|
||||||
<field name="bussiness_node_id" ref="bussiness_no_functional_tool"/>
|
|
||||||
<field name="msgtype">markdown</field>
|
|
||||||
<field name="urgency">urgent</field>
|
|
||||||
<field name="content">### 生产线无功能刀具提醒
|
|
||||||
单号:工单[{{workorder_id.production_id.name}}]({{url}})
|
|
||||||
原因:生产线无加工程序要用的功能刀具</field>
|
|
||||||
</record>
|
|
||||||
<record id="template_no_position_data" model="jikimo.message.template">
|
|
||||||
<field name="name">工单无定位数据提醒</field>
|
|
||||||
<field name="model_id" ref="jikimo_workorder_exception_notify.model_jikimo_workorder_exception"/>
|
|
||||||
<field name="model">jikimo.workorder.exception</field>
|
|
||||||
<field name="bussiness_node_id" ref="bussiness_no_position_data"/>
|
|
||||||
<field name="msgtype">markdown</field>
|
|
||||||
<field name="urgency">urgent</field>
|
|
||||||
<field name="content">### 工单无定位数据提醒
|
|
||||||
单号:工单[{{workorder_id.production_id.name}}]({{url}})
|
|
||||||
原因:无装夹定位测量数据</field>
|
|
||||||
</record>
|
|
||||||
<record id="template_processing_failure" model="jikimo.message.template">
|
|
||||||
<field name="name">工单加工失败提醒</field>
|
|
||||||
<field name="model_id" ref="jikimo_workorder_exception_notify.model_jikimo_workorder_exception"/>
|
|
||||||
<field name="model">jikimo.workorder.exception</field>
|
|
||||||
<field name="bussiness_node_id" ref="bussiness_processing_failure"/>
|
|
||||||
<field name="msgtype">markdown</field>
|
|
||||||
<field name="urgency">urgent</field>
|
|
||||||
<field name="content">### 工单加工失败提醒
|
|
||||||
单号:工单[{{workorder_id.production_id.name}}]({{url}})
|
|
||||||
原因:加工失败,工件下产线处理</field>
|
|
||||||
</record>
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from . import jikimo_message_template
|
|
||||||
from . import jikimo_workorder_exception
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
from odoo import models
|
|
||||||
|
|
||||||
|
|
||||||
class JikimoMessageTemplate(models.Model):
|
|
||||||
_inherit = "jikimo.message.template"
|
|
||||||
|
|
||||||
def _get_message_model(self):
|
|
||||||
res = super(JikimoMessageTemplate, self)._get_message_model()
|
|
||||||
res.append('jikimo.workorder.exception')
|
|
||||||
return res
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
from odoo import models, api
|
|
||||||
from odoo.addons.sf_base.commons.common import Common
|
|
||||||
import requests, logging
|
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class JikimoWorkorderException(models.Model):
|
|
||||||
_name = 'jikimo.workorder.exception'
|
|
||||||
_inherit = ['jikimo.workorder.exception', 'jikimo.message.dispatch']
|
|
||||||
|
|
||||||
@api.model_create_multi
|
|
||||||
def create(self, vals_list):
|
|
||||||
res = super(JikimoWorkorderException, self).create(vals_list)
|
|
||||||
# 根据异常编码发送消息提醒
|
|
||||||
try:
|
|
||||||
for rec in res:
|
|
||||||
if rec.exception_code == 'YC0001':
|
|
||||||
# 无CNC程序,调用cloud接口
|
|
||||||
data = {'name': rec.workorder_id.production_id.programming_no, 'exception_code': 'YC0001'}
|
|
||||||
configsettings = self.env['res.config.settings'].sudo().get_values()
|
|
||||||
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
|
|
||||||
url = '/api/message/workorder_exception'
|
|
||||||
config_url = configsettings['sf_url'] + url
|
|
||||||
data['token'] = configsettings['token']
|
|
||||||
ret = requests.post(config_url, json=data, headers=config_header)
|
|
||||||
ret = ret.json()
|
|
||||||
_logger.info('无CNC程序异常消息推送接口:%s' % ret)
|
|
||||||
elif rec.exception_code == 'YC0002':
|
|
||||||
# 无功能刀具
|
|
||||||
rec.add_queue('无功能刀具')
|
|
||||||
elif rec.exception_code == 'YC0003':
|
|
||||||
# 无定位数据
|
|
||||||
rec.add_queue('无定位数据')
|
|
||||||
elif rec.exception_code == 'YC0004':
|
|
||||||
# 无FTP文件,调用cloud接口
|
|
||||||
data = {'name': rec.workorder_id.production_id.programming_no, 'exception_code': 'YC0004'}
|
|
||||||
configsettings = self.env['res.config.settings'].sudo().get_values()
|
|
||||||
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
|
|
||||||
url = '/api/message/workorder_exception'
|
|
||||||
config_url = configsettings['sf_url'] + url
|
|
||||||
data['token'] = configsettings['token']
|
|
||||||
ret = requests.post(config_url, json=data, headers=config_header)
|
|
||||||
ret = ret.json()
|
|
||||||
_logger.info('无FTP文件异常消息推送接口:%s' % ret)
|
|
||||||
elif rec.exception_code == 'YC0005':
|
|
||||||
# 加工失败
|
|
||||||
rec.add_queue('加工失败')
|
|
||||||
except Exception as e:
|
|
||||||
_logger.error('异常编码发送消息提醒失败:%s' % e)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def _get_message(self, message_queue_ids):
|
|
||||||
contents, _ = super(JikimoWorkorderException, self)._get_message(message_queue_ids)
|
|
||||||
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
|
||||||
action_id = self.env.ref('mrp.mrp_production_action').id
|
|
||||||
for index, content in enumerate(contents):
|
|
||||||
exception_id = self.env['jikimo.workorder.exception'].browse(message_queue_ids[index].res_id)
|
|
||||||
url = url + '/web#id=%s&view_type=form&action=%s' % (exception_id.workorder_id.production_id.id, action_id)
|
|
||||||
contents[index] = content.replace('{{url}}', url)
|
|
||||||
return contents, message_queue_ids
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
from . import common
|
|
||||||
from . import test_jikimo_workorder_exception_notify
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
||||||
from odoo import fields, Command
|
|
||||||
from odoo.tests.common import TransactionCase, HttpCase, tagged, Form
|
|
||||||
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
import base64
|
|
||||||
from lxml import etree
|
|
||||||
|
|
||||||
@tagged('post_install', '-at_install')
|
|
||||||
class TestJikimoWorkorderExceptionNotifyCommonNotify(TransactionCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestJikimoWorkorderExceptionNotifyCommonNotify, self).setUp()
|
|
||||||
# 获取最后一个工单
|
|
||||||
self.workorder = self.env['mrp.workorder'].search([], order='id desc', limit=1)
|
|
||||||
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
import json
|
|
||||||
from datetime import datetime
|
|
||||||
from odoo.addons.jikimo_workorder_exception_notify.tests.common import TestJikimoWorkorderExceptionNotifyCommonNotify
|
|
||||||
|
|
||||||
class TestJikimoWorkorderExceptionNotify(TestJikimoWorkorderExceptionNotifyCommonNotify):
|
|
||||||
|
|
||||||
def test_create_message_template(self):
|
|
||||||
self.assertTrue(self.env['jikimo.message.template'].search([
|
|
||||||
('name', '=', '生产线无功能刀具提醒'),
|
|
||||||
('model', '=', 'jikimo.workorder.exception')
|
|
||||||
]))
|
|
||||||
self.assertTrue(self.env['jikimo.message.template'].search([
|
|
||||||
('name', '=', '工单无定位数据提醒'),
|
|
||||||
('model', '=', 'jikimo.workorder.exception')
|
|
||||||
]))
|
|
||||||
self.assertTrue(self.env['jikimo.message.template'].search([
|
|
||||||
('name', '=', '工单加工失败提醒'),
|
|
||||||
('model', '=', 'jikimo.workorder.exception')
|
|
||||||
]))
|
|
||||||
|
|
||||||
def test_create_message_queue_yc0001(self):
|
|
||||||
exception_record = self.env['jikimo.workorder.exception'].create({
|
|
||||||
'workorder_id': self.workorder.id,
|
|
||||||
'exception_code': 'YC0001',
|
|
||||||
'exception_content': '无CNC程序'
|
|
||||||
})
|
|
||||||
|
|
||||||
message_record = self.env['jikimo.message.queue'].search([
|
|
||||||
('res_id', '=', exception_record.id),
|
|
||||||
('model', '=', 'jikimo.workorder.exception'),
|
|
||||||
('message_status', '=', 'pending')
|
|
||||||
])
|
|
||||||
self.assertFalse(message_record)
|
|
||||||
|
|
||||||
def test_create_message_queue_yc0002(self):
|
|
||||||
exception_record = self.env['jikimo.workorder.exception'].create({
|
|
||||||
'workorder_id': self.workorder.id,
|
|
||||||
'exception_code': 'YC0002',
|
|
||||||
'exception_content': '无功能刀具'
|
|
||||||
})
|
|
||||||
|
|
||||||
bussiness_node = self.env['jikimo.message.bussiness.node'].search([
|
|
||||||
('name', '=', '无功能刀具'),
|
|
||||||
('model', '=', 'jikimo.workorder.exception')
|
|
||||||
])
|
|
||||||
|
|
||||||
message_template = self.env['jikimo.message.template'].search([
|
|
||||||
('bussiness_node_id', '=', bussiness_node.id),
|
|
||||||
('model', '=', 'jikimo.workorder.exception')
|
|
||||||
])
|
|
||||||
|
|
||||||
message_record = self.env['jikimo.message.queue'].search([
|
|
||||||
('res_id', '=', exception_record.id),
|
|
||||||
('model', '=', 'jikimo.workorder.exception'),
|
|
||||||
('message_status', '=', 'pending'),
|
|
||||||
('message_template_id', '=', message_template.id)
|
|
||||||
])
|
|
||||||
self.assertTrue(message_record)
|
|
||||||
|
|
||||||
def test_create_message_queue_yc0003(self):
|
|
||||||
exception_record = self.env['jikimo.workorder.exception'].create({
|
|
||||||
'workorder_id': self.workorder.id,
|
|
||||||
'exception_code': 'YC0003',
|
|
||||||
'exception_content': '无定位数据'
|
|
||||||
})
|
|
||||||
|
|
||||||
bussiness_node = self.env['jikimo.message.bussiness.node'].search([
|
|
||||||
('name', '=', '无定位数据'),
|
|
||||||
('model', '=', 'jikimo.workorder.exception')
|
|
||||||
])
|
|
||||||
|
|
||||||
message_template = self.env['jikimo.message.template'].search([
|
|
||||||
('bussiness_node_id', '=', bussiness_node.id),
|
|
||||||
('model', '=', 'jikimo.workorder.exception')
|
|
||||||
])
|
|
||||||
|
|
||||||
message_record = self.env['jikimo.message.queue'].search([
|
|
||||||
('res_id', '=', exception_record.id),
|
|
||||||
('model', '=', 'jikimo.workorder.exception'),
|
|
||||||
('message_status', '=', 'pending'),
|
|
||||||
('message_template_id', '=', message_template.id)
|
|
||||||
])
|
|
||||||
self.assertTrue(message_record)
|
|
||||||
|
|
||||||
def test_create_message_queue_yc0004(self):
|
|
||||||
exception_record = self.env['jikimo.workorder.exception'].create({
|
|
||||||
'workorder_id': self.workorder.id,
|
|
||||||
'exception_code': 'YC0004',
|
|
||||||
'exception_content': '无CNC程序'
|
|
||||||
})
|
|
||||||
|
|
||||||
message_record = self.env['jikimo.message.queue'].search([
|
|
||||||
('res_id', '=', exception_record.id),
|
|
||||||
('model', '=', 'jikimo.workorder.exception'),
|
|
||||||
('message_status', '=', 'pending')
|
|
||||||
])
|
|
||||||
self.assertFalse(message_record)
|
|
||||||
|
|
||||||
def test_get_message(self):
|
|
||||||
exception_record = self.env['jikimo.workorder.exception'].create({
|
|
||||||
'workorder_id': self.workorder.id,
|
|
||||||
'exception_code': 'YC0002',
|
|
||||||
'exception_content': '无功能刀具'
|
|
||||||
})
|
|
||||||
message_queue_ids = self.env['jikimo.message.queue'].search([
|
|
||||||
('res_id', '=', exception_record.id),
|
|
||||||
('model', '=', 'jikimo.workorder.exception'),
|
|
||||||
('message_status', '=', 'pending')
|
|
||||||
])
|
|
||||||
message = self.env['jikimo.workorder.exception']._get_message(message_queue_ids)
|
|
||||||
self.assertTrue(message)
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1273,18 +1273,3 @@ msgstr ""
|
|||||||
#: model:product.template,description_sale:mrp_workorder.product_template_stool_top
|
#: model:product.template,description_sale:mrp_workorder.product_template_stool_top
|
||||||
msgid "wooden stool top"
|
msgid "wooden stool top"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. module: mrp_workorder
|
|
||||||
#: model:quality.point.test_type,name:mrp_workorder.test_type_register_consumed_materials
|
|
||||||
msgid "Register Consumed Materials"
|
|
||||||
msgstr "登记消耗材料"
|
|
||||||
|
|
||||||
#. module: mrp_workorder
|
|
||||||
#: model:quality.point.test_type,name:mrp_workorder.test_type_register_byproducts
|
|
||||||
msgid "Register By-products"
|
|
||||||
msgstr "按产品注册"
|
|
||||||
|
|
||||||
#. module: mrp_workorder
|
|
||||||
#: model:quality.point.test_type,name:mrp_workorder.test_type_print_label
|
|
||||||
msgid "Print label"
|
|
||||||
msgstr "打印标签"
|
|
||||||
@@ -138,7 +138,7 @@ if env.user.has_group('mrp.group_mrp_workorder_dependencies'):
|
|||||||
<button name="openMenuPopup" t-att-disabled="isBlocked" class="btn btn-secondary o_workorder_icon_btn fa fa-bars" type="workorder_event" title="menu"/>
|
<button name="openMenuPopup" t-att-disabled="isBlocked" class="btn btn-secondary o_workorder_icon_btn fa fa-bars" type="workorder_event" title="menu"/>
|
||||||
<span groups="mrp_workorder.group_mrp_wo_tablet_timer">
|
<span groups="mrp_workorder.group_mrp_wo_tablet_timer">
|
||||||
<button name="button_pending" type="object" class="btn btn-secondary" attrs="{'invisible': ['|', ('is_user_working', '=', False), ('working_state', '=', 'blocked')]}" barcode_trigger="pause" string="PAUSE"/>
|
<button name="button_pending" type="object" class="btn btn-secondary" attrs="{'invisible': ['|', ('is_user_working', '=', False), ('working_state', '=', 'blocked')]}" barcode_trigger="pause" string="PAUSE"/>
|
||||||
<button name="button_start" type="object" class="btn btn-warning" attrs="{'invisible': ['|', '|', ('is_user_working', '=', True), ('working_state', '=', 'blocked'), ('state', '=', ('done','rework', 'cancel'))]}" barcode_trigger="pause" string="CONTINUE"/>
|
<button name="button_start" type="object" class="btn btn-warning" attrs="{'invisible': ['|', '|', ('is_user_working', '=', True), ('working_state', '=', 'blocked'), ('state', '=', ('done', 'cancel'))]}" barcode_trigger="pause" string="CONTINUE"/>
|
||||||
<button name="button_unblock" type="object" class="btn btn-secondary btn-danger o_unblock" attrs="{'invisible': [('working_state', '!=', 'blocked')]}">Unblock</button>
|
<button name="button_unblock" type="object" class="btn btn-secondary btn-danger o_unblock" attrs="{'invisible': [('working_state', '!=', 'blocked')]}">Unblock</button>
|
||||||
<field name="duration" widget="mrp_timer" class="ms-1" readonly="1"/>
|
<field name="duration" widget="mrp_timer" class="ms-1" readonly="1"/>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1050,13 +1050,3 @@ msgstr "工作中心故障"
|
|||||||
#: model:ir.model.fields,field_description:quality.field_quality_point_test_type__active
|
#: model:ir.model.fields,field_description:quality.field_quality_point_test_type__active
|
||||||
msgid "active"
|
msgid "active"
|
||||||
msgstr "有效"
|
msgstr "有效"
|
||||||
|
|
||||||
#. module: quality
|
|
||||||
#: model:quality.point.test_type,name:quality.test_type_instructions
|
|
||||||
msgid "Instructions"
|
|
||||||
msgstr "使用说明"
|
|
||||||
|
|
||||||
#. module: quality
|
|
||||||
#: model:quality.point.test_type,name:quality.test_type_picture
|
|
||||||
msgid "Take a Picture"
|
|
||||||
msgstr "照片"
|
|
||||||
@@ -15,7 +15,7 @@ class TestType(models.Model):
|
|||||||
_description = "Quality Control Test Type"
|
_description = "Quality Control Test Type"
|
||||||
|
|
||||||
# Used instead of selection field in order to hide a choice depending on the view.
|
# Used instead of selection field in order to hide a choice depending on the view.
|
||||||
name = fields.Char('Name', required=True,translate=True)
|
name = fields.Char('Name', required=True)
|
||||||
technical_name = fields.Char('Technical name', required=True)
|
technical_name = fields.Char('Technical name', required=True)
|
||||||
active = fields.Boolean('active', default=True)
|
active = fields.Boolean('active', default=True)
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
'sequence': 120,
|
'sequence': 120,
|
||||||
'summary': 'Control the quality of your products',
|
'summary': 'Control the quality of your products',
|
||||||
'website': 'https://www.odoo.com/app/quality',
|
'website': 'https://www.odoo.com/app/quality',
|
||||||
'depends': ['quality', 'sf_manufacturing'],
|
'depends': ['quality'],
|
||||||
'description': """
|
'description': """
|
||||||
Quality Control
|
Quality Control
|
||||||
===============
|
===============
|
||||||
|
|||||||
@@ -1185,14 +1185,3 @@ msgstr "请先进行质量检查!"
|
|||||||
#: model_terms:ir.ui.view,arch_db:quality_control.quality_alert_team_view_form
|
#: model_terms:ir.ui.view,arch_db:quality_control.quality_alert_team_view_form
|
||||||
msgid "e.g. The QA Masters"
|
msgid "e.g. The QA Masters"
|
||||||
msgstr "例如:QA大师"
|
msgstr "例如:QA大师"
|
||||||
|
|
||||||
|
|
||||||
#. module: quality_control
|
|
||||||
#: model:quality.point.test_type,name:quality_control.test_type_passfail
|
|
||||||
msgid "Pass - Fail"
|
|
||||||
msgstr "通过-失败"
|
|
||||||
|
|
||||||
#. module: quality_control
|
|
||||||
#: model:quality.point.test_type,name:quality_control.test_type_measure
|
|
||||||
msgid "Measure"
|
|
||||||
msgstr "测量"
|
|
||||||
@@ -6,4 +6,3 @@ from . import stock_move
|
|||||||
from . import stock_move_line
|
from . import stock_move_line
|
||||||
from . import stock_picking
|
from . import stock_picking
|
||||||
from . import stock_lot
|
from . import stock_lot
|
||||||
from . import product_category
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
||||||
from math import sqrt
|
|
||||||
from dateutil.relativedelta import relativedelta
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
import random
|
|
||||||
|
|
||||||
from odoo import api, models, fields, _
|
|
||||||
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, float_round
|
|
||||||
from odoo.osv.expression import OR
|
|
||||||
|
|
||||||
|
|
||||||
class ProductCategory(models.Model):
|
|
||||||
_inherit = 'product.category'
|
|
||||||
@api.model
|
|
||||||
def name_search(self, name='', args=None, operator='ilike', limit=100):
|
|
||||||
if args is None:
|
|
||||||
args = []
|
|
||||||
# 添加过滤条件,确保只返回名称为 'abc' 的记录
|
|
||||||
args += [('name', 'not in', ['Saleable', 'Expenses', 'Deliveries'])]
|
|
||||||
|
|
||||||
# 调用父类的 name_search 方法
|
|
||||||
return super(ProductCategory, self).name_search(name, args=args, operator=operator, limit=limit)
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def search(self, args, limit=100, offset=0, order=None, count=False):
|
|
||||||
# 添加过滤条件,确保只返回名称不在指定列表中的记录
|
|
||||||
args += [('name', 'not in', ['Saleable', 'Expenses', 'Deliveries'])]
|
|
||||||
|
|
||||||
# 调用父类的 search 方法
|
|
||||||
return super(ProductCategory, self).search(args, limit=limit, offset=offset, order=order, count=count)
|
|
||||||
@@ -7,7 +7,6 @@ from datetime import datetime
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
from odoo import api, models, fields, _
|
from odoo import api, models, fields, _
|
||||||
from odoo.api import depends
|
|
||||||
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, float_round
|
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, float_round
|
||||||
from odoo.osv.expression import OR
|
from odoo.osv.expression import OR
|
||||||
|
|
||||||
@@ -123,13 +122,7 @@ class QualityPoint(models.Model):
|
|||||||
|
|
||||||
class QualityCheck(models.Model):
|
class QualityCheck(models.Model):
|
||||||
_inherit = "quality.check"
|
_inherit = "quality.check"
|
||||||
part_name = fields.Char('零件名称', compute='_compute_part_name_number', readonly=True)
|
|
||||||
part_number = fields.Char('零件图号', compute='_compute_part_name_number', readonly=True)
|
|
||||||
@depends('product_id')
|
|
||||||
def _compute_part_name_number(self):
|
|
||||||
for record in self:
|
|
||||||
record.part_number = record.product_id.part_number
|
|
||||||
record.part_name = record.product_id.part_name
|
|
||||||
failure_message = fields.Html(related='point_id.failure_message', readonly=True)
|
failure_message = fields.Html(related='point_id.failure_message', readonly=True)
|
||||||
measure = fields.Float('Measure', default=0.0, digits='Quality Tests', tracking=True)
|
measure = fields.Float('Measure', default=0.0, digits='Quality Tests', tracking=True)
|
||||||
measure_success = fields.Selection([
|
measure_success = fields.Selection([
|
||||||
@@ -159,34 +152,6 @@ class QualityCheck(models.Model):
|
|||||||
is_lot_tested_fractionally = fields.Boolean(related='point_id.is_lot_tested_fractionally')
|
is_lot_tested_fractionally = fields.Boolean(related='point_id.is_lot_tested_fractionally')
|
||||||
testing_percentage_within_lot = fields.Float(related="point_id.testing_percentage_within_lot")
|
testing_percentage_within_lot = fields.Float(related="point_id.testing_percentage_within_lot")
|
||||||
product_tracking = fields.Selection(related='product_id.tracking')
|
product_tracking = fields.Selection(related='product_id.tracking')
|
||||||
quality_check_type = fields.Selection([
|
|
||||||
('采购入库检', '采购入库检'),
|
|
||||||
('客供料入库检', '客供料入库检'),
|
|
||||||
('退货入库检', '退货入库检'),
|
|
||||||
('生产入库检', '生产入库检'),
|
|
||||||
('外协入库检', '外协入库检'),
|
|
||||||
('成品发货检', '成品发货检'),
|
|
||||||
('工序外协发货检', '工序外协发货检'),
|
|
||||||
('委外坯料发货检', '委外坯料发货检')], string='类型', compute='_compute_quality_check_type', store=True)
|
|
||||||
|
|
||||||
@api.depends('picking_id')
|
|
||||||
def _compute_quality_check_type(self):
|
|
||||||
for check in self:
|
|
||||||
if check.picking_id:
|
|
||||||
picking_type = check.picking_id.picking_type_id.sequence_code
|
|
||||||
type_mapping = {
|
|
||||||
'IN': '采购入库检',
|
|
||||||
'DL': '客供料入库检',
|
|
||||||
'RET': '退货入库检',
|
|
||||||
'SFP': '生产入库检',
|
|
||||||
'OCIN': '外协入库检',
|
|
||||||
'OUT': '成品发货检',
|
|
||||||
'OCOUT': '工序外协发货检',
|
|
||||||
'RES': '委外坯料发货检',
|
|
||||||
}
|
|
||||||
check.quality_check_type = type_mapping.get(picking_type, False)
|
|
||||||
else:
|
|
||||||
check.quality_check_type = False
|
|
||||||
|
|
||||||
@api.depends('measure_success')
|
@api.depends('measure_success')
|
||||||
def _compute_warning_message(self):
|
def _compute_warning_message(self):
|
||||||
@@ -329,19 +294,6 @@ class QualityAlert(models.Model):
|
|||||||
_inherit = "quality.alert"
|
_inherit = "quality.alert"
|
||||||
|
|
||||||
title = fields.Char('Title')
|
title = fields.Char('Title')
|
||||||
part_number = fields.Char(string='零件图号', compute='_compute_part_info', store=True)
|
|
||||||
part_name = fields.Char(string='零件名称', compute='_compute_part_info', store=True)
|
|
||||||
|
|
||||||
@api.depends('product_id', 'picking_id')
|
|
||||||
def _compute_part_info(self):
|
|
||||||
for alert in self:
|
|
||||||
if alert.product_tmpl_id.categ_id.name == '成品':
|
|
||||||
alert.part_number = alert.product_id.part_number
|
|
||||||
alert.part_name = alert.product_id.part_name
|
|
||||||
elif alert.product_id.categ_id.name == '坯料':
|
|
||||||
if alert.picking_id.move_ids_without_package:
|
|
||||||
alert.part_number = alert.picking_id.move_ids_without_package[0].part_number
|
|
||||||
alert.part_name = alert.picking_id.move_ids_without_package[0].part_name
|
|
||||||
|
|
||||||
def action_see_check(self):
|
def action_see_check(self):
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -90,8 +90,6 @@
|
|||||||
<field name="lot_id" context="{'default_product_id': product_id}"
|
<field name="lot_id" context="{'default_product_id': product_id}"
|
||||||
groups="stock.group_production_lot"/>
|
groups="stock.group_production_lot"/>
|
||||||
<field name="picking_id"/>
|
<field name="picking_id"/>
|
||||||
<field name="part_name"/>
|
|
||||||
<field name="part_number"/>
|
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="team_id"/>
|
<field name="team_id"/>
|
||||||
@@ -152,10 +150,6 @@
|
|||||||
<field name="date_assign" position="after">
|
<field name="date_assign" position="after">
|
||||||
<field name="company_id" groups="base.main_company"/>
|
<field name="company_id" groups="base.main_company"/>
|
||||||
</field>
|
</field>
|
||||||
<field name="product_tmpl_id" position="after">
|
|
||||||
<field name="part_name" optional="show"/>
|
|
||||||
<field name="part_number" optional="show"/>
|
|
||||||
</field>
|
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
@@ -209,7 +203,7 @@
|
|||||||
<record id="quality_alert_action_check" model="ir.actions.act_window">
|
<record id="quality_alert_action_check" model="ir.actions.act_window">
|
||||||
<field name="name">Quality Alerts</field>
|
<field name="name">Quality Alerts</field>
|
||||||
<field name="res_model">quality.alert</field>
|
<field name="res_model">quality.alert</field>
|
||||||
<field name="view_mode">tree,kanban,form,pivot,graph,calendar</field>
|
<field name="view_mode">kanban,tree,form,pivot,graph,calendar</field>
|
||||||
<field name="help" type="html">
|
<field name="help" type="html">
|
||||||
<p class="o_view_nocontent_smiling_face">
|
<p class="o_view_nocontent_smiling_face">
|
||||||
Create a new quality alert
|
Create a new quality alert
|
||||||
@@ -266,8 +260,6 @@
|
|||||||
<field name="company_id" invisible="1"/>
|
<field name="company_id" invisible="1"/>
|
||||||
<field name="product_id" attrs="{'invisible' : [('measure_on', '=', 'operation')]}"/>
|
<field name="product_id" attrs="{'invisible' : [('measure_on', '=', 'operation')]}"/>
|
||||||
<field name="measure_on" attrs="{'readonly': [('point_id', '!=', False)]}"/>
|
<field name="measure_on" attrs="{'readonly': [('point_id', '!=', False)]}"/>
|
||||||
<field name="part_name"/>
|
|
||||||
<field name="part_number"/>
|
|
||||||
<field name="show_lot_text" invisible="1"/>
|
<field name="show_lot_text" invisible="1"/>
|
||||||
<field name="move_line_id" invisible="1"/>
|
<field name="move_line_id" invisible="1"/>
|
||||||
<field name="product_tracking" invisible="1"/>
|
<field name="product_tracking" invisible="1"/>
|
||||||
@@ -397,8 +389,6 @@
|
|||||||
<field name="name" decoration-bf="1"/>
|
<field name="name" decoration-bf="1"/>
|
||||||
<field name="measure_on" optional="show"/>
|
<field name="measure_on" optional="show"/>
|
||||||
<field name='product_id' optional="show"/>
|
<field name='product_id' optional="show"/>
|
||||||
<field name="part_name" optional="hide"/>
|
|
||||||
<field name='part_number' optional="show"/>
|
|
||||||
<field name="lot_id" invisible="context.get('show_lots_text')"/>
|
<field name="lot_id" invisible="context.get('show_lots_text')"/>
|
||||||
<field name="lot_name" invisible="not context.get('show_lots_text')"/>
|
<field name="lot_name" invisible="not context.get('show_lots_text')"/>
|
||||||
<field name="picking_id" optional="hide" string="Transfer"/>
|
<field name="picking_id" optional="hide" string="Transfer"/>
|
||||||
@@ -456,10 +446,6 @@
|
|||||||
<filter string="Control Point" name="groupby_point_id" context="{'group_by': 'point_id'}"/>
|
<filter string="Control Point" name="groupby_point_id" context="{'group_by': 'point_id'}"/>
|
||||||
<filter string="Team" name="groupby_team_id" context="{'group_by': 'team_id'}"/>
|
<filter string="Team" name="groupby_team_id" context="{'group_by': 'team_id'}"/>
|
||||||
</group>
|
</group>
|
||||||
<searchpanel>
|
|
||||||
<field name="quality_check_type" icon="fa-filter" enable_counters="1"/>
|
|
||||||
<field name="quality_state" icon="fa-filter" enable_counters="1"/>
|
|
||||||
</searchpanel>
|
|
||||||
</search>
|
</search>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
@@ -519,7 +505,6 @@
|
|||||||
<field name="name">Quality Checks</field>
|
<field name="name">Quality Checks</field>
|
||||||
<field name="res_model">quality.check</field>
|
<field name="res_model">quality.check</field>
|
||||||
<field name="view_mode">tree,kanban,form,pivot,graph</field>
|
<field name="view_mode">tree,kanban,form,pivot,graph</field>
|
||||||
<field name="context">{'is_web_request': True}</field>
|
|
||||||
<field name="help" type="html">
|
<field name="help" type="html">
|
||||||
<p class="o_view_nocontent_smiling_face">
|
<p class="o_view_nocontent_smiling_face">
|
||||||
No quality check found
|
No quality check found
|
||||||
@@ -1048,7 +1033,7 @@
|
|||||||
name="Overview"
|
name="Overview"
|
||||||
action="quality_alert_team_action"
|
action="quality_alert_team_action"
|
||||||
parent="menu_quality_root"
|
parent="menu_quality_root"
|
||||||
sequence="5" active="False"/>
|
sequence="5"/>
|
||||||
|
|
||||||
<menuitem
|
<menuitem
|
||||||
id="menu_quality_control"
|
id="menu_quality_control"
|
||||||
|
|||||||
@@ -38,17 +38,3 @@ class Manufacturing_Connect(http.Controller):
|
|||||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||||
logging.info('get_maintenance_tool_groups_Info error:%s' % e)
|
logging.info('get_maintenance_tool_groups_Info error:%s' % e)
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
|
|
||||||
|
|
||||||
class MultiInheritController():
|
|
||||||
_sub_classes = []
|
|
||||||
|
|
||||||
def __init_subclass__(cls):
|
|
||||||
"""
|
|
||||||
多继承,解决多个字类时方法调用super的问题
|
|
||||||
"""
|
|
||||||
super().__init_subclass__()
|
|
||||||
if len(cls._sub_classes) > 0 and cls not in cls._sub_classes:
|
|
||||||
cls.__bases__ = (cls._sub_classes[-1],)
|
|
||||||
if cls not in cls._sub_classes:
|
|
||||||
cls._sub_classes.append(cls)
|
|
||||||
|
|||||||
@@ -394,30 +394,3 @@ class MachineToolCategory(models.Model):
|
|||||||
active = fields.Boolean('有效', default=True)
|
active = fields.Boolean('有效', default=True)
|
||||||
category = fields.Selection([('shukong', u'数控'), ('putong', u'普通')], string=u'机床类别',
|
category = fields.Selection([('shukong', u'数控'), ('putong', u'普通')], string=u'机床类别',
|
||||||
default='shukong')
|
default='shukong')
|
||||||
|
|
||||||
|
|
||||||
class MachiningAccuracy(models.Model):
|
|
||||||
_name = 'sf.machining.accuracy'
|
|
||||||
_description = '加工精度'
|
|
||||||
name = fields.Char('一般公差', index=True)
|
|
||||||
standard_tolerance = fields.Char(string="标准公差")
|
|
||||||
sync_id = fields.Char('同步ID')
|
|
||||||
|
|
||||||
|
|
||||||
class ReSaleOrder(models.Model):
|
|
||||||
_inherit = 'sale.order'
|
|
||||||
|
|
||||||
person_of_delivery = fields.Char('收货人')
|
|
||||||
telephone_of_delivery = fields.Char('电话号码')
|
|
||||||
address_of_delivery = fields.Char('联系地址')
|
|
||||||
|
|
||||||
|
|
||||||
class EmbryoRedundancy(models.Model):
|
|
||||||
_name = "sf.embryo.redundancy"
|
|
||||||
|
|
||||||
code = fields.Char('编码', required=True)
|
|
||||||
name = fields.Char('名称', required=True)
|
|
||||||
long = fields.Float('长度(mm)', required=True)
|
|
||||||
width = fields.Float('宽度(mm)', required=True)
|
|
||||||
height = fields.Float('高度(mm)', required=True)
|
|
||||||
active = fields.Boolean('有效', default=True)
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class MrsMaterialModel(models.Model):
|
|||||||
finish_machining = fields.Float("精加工Vc(m/min)")
|
finish_machining = fields.Float("精加工Vc(m/min)")
|
||||||
remark = fields.Text("备注")
|
remark = fields.Text("备注")
|
||||||
gain_way = fields.Selection(
|
gain_way = fields.Selection(
|
||||||
[("自加工", "自加工"), ("外协", "委外加工"), ("采购", "采购")],
|
[("自加工", "自加工"), ("外协", "外协"), ("采购", "采购")],
|
||||||
default="", string="获取方式")
|
default="", string="获取方式")
|
||||||
supplier_ids = fields.One2many('sf.supplier.sort', 'materials_model_id', string='供应商')
|
supplier_ids = fields.One2many('sf.supplier.sort', 'materials_model_id', string='供应商')
|
||||||
active = fields.Boolean('有效', default=True)
|
active = fields.Boolean('有效', default=True)
|
||||||
@@ -100,7 +100,6 @@ class MrsProductionProcess(models.Model):
|
|||||||
travel_day = fields.Float('路途天数/d')
|
travel_day = fields.Float('路途天数/d')
|
||||||
sequence = fields.Integer('排序')
|
sequence = fields.Integer('排序')
|
||||||
|
|
||||||
|
|
||||||
# class MrsProcessingTechnology(models.Model):
|
# class MrsProcessingTechnology(models.Model):
|
||||||
# _name = 'sf.processing.technology'
|
# _name = 'sf.processing.technology'
|
||||||
# _description = '加工工艺'
|
# _description = '加工工艺'
|
||||||
@@ -158,8 +157,6 @@ class MrsProductionProcessParameter(models.Model):
|
|||||||
for parameter in self:
|
for parameter in self:
|
||||||
if parameter.process_id:
|
if parameter.process_id:
|
||||||
name = parameter.process_id.name + '-' + parameter.name
|
name = parameter.process_id.name + '-' + parameter.name
|
||||||
else:
|
|
||||||
name = parameter.name
|
|
||||||
result.append((parameter.id, name))
|
result.append((parameter.id, name))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|||||||
@@ -46,11 +46,6 @@
|
|||||||
<field name="category_id" ref="base.module_category_manufacturing_manufacturing"/>
|
<field name="category_id" ref="base.module_category_manufacturing_manufacturing"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="group_production_engineer" model="res.groups">
|
|
||||||
<field name="name">工艺工程师</field>
|
|
||||||
<field name="implied_ids" eval="[(4, ref('group_sf_mrp_user'))]"/>
|
|
||||||
<field name="category_id" ref="base.module_category_manufacturing_manufacturing"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.module.category" id="module_category_plan">
|
<record model="ir.module.category" id="module_category_plan">
|
||||||
<field name="name">计划</field>
|
<field name="name">计划</field>
|
||||||
@@ -70,7 +65,7 @@
|
|||||||
<record id="group_plan_dispatch" model="res.groups">
|
<record id="group_plan_dispatch" model="res.groups">
|
||||||
<field name="name">计划调度岗</field>
|
<field name="name">计划调度岗</field>
|
||||||
<field name="category_id" ref="module_category_plan"/>
|
<field name="category_id" ref="module_category_plan"/>
|
||||||
<!-- <field name="implied_ids" eval="[(4, ref('base.group_user'))]"/> -->
|
<!-- <field name="implied_ids" eval="[(4, ref('base.group_user'))]"/> -->
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="group_plan_director" model="res.groups">
|
<record id="group_plan_director" model="res.groups">
|
||||||
|
|||||||
@@ -247,10 +247,3 @@ access_sf_cutting_tool_type_group_sf_stock_manager,sf_cutting_tool_type_group_sf
|
|||||||
access_sf_cutting_tool_material_group_plan_dispatch,sf_cutting_tool_material_group_plan_dispatch,model_sf_cutting_tool_material,sf_base.group_plan_dispatch,1,0,0,0
|
access_sf_cutting_tool_material_group_plan_dispatch,sf_cutting_tool_material_group_plan_dispatch,model_sf_cutting_tool_material,sf_base.group_plan_dispatch,1,0,0,0
|
||||||
access_sf_functional_cutting_tool_model_group_plan_dispatch,sf_functional_cutting_tool_model_group_plan_dispatch,model_sf_functional_cutting_tool_model,sf_base.group_plan_dispatch,1,0,0,0
|
access_sf_functional_cutting_tool_model_group_plan_dispatch,sf_functional_cutting_tool_model_group_plan_dispatch,model_sf_functional_cutting_tool_model,sf_base.group_plan_dispatch,1,0,0,0
|
||||||
access_sf_cutting_tool_type_group_plan_dispatch,sf_cutting_tool_type_group_plan_dispatch,model_sf_cutting_tool_type,sf_base.group_plan_dispatch,1,0,0,0
|
access_sf_cutting_tool_type_group_plan_dispatch,sf_cutting_tool_type_group_plan_dispatch,model_sf_cutting_tool_type,sf_base.group_plan_dispatch,1,0,0,0
|
||||||
|
|
||||||
|
|
||||||
access_sf_machining_accuracy,sf_machining_accuracy,model_sf_machining_accuracy,base.group_user,1,0,0,0
|
|
||||||
access_sf_machining_accuracy_admin,sf_machining_accuracy_admin,model_sf_machining_accuracy,base.group_system,1,0,0,0
|
|
||||||
|
|
||||||
access_sf_embryo_redundancy,sf_embryo_redundancy,model_sf_embryo_redundancy,base.group_user,1,0,0,0
|
|
||||||
access_sf_embryo_redundancy_admin,sf_embryo_redundancy_admin,model_sf_embryo_redundancy,base.group_system,1,0,0,0
|
|
||||||
|
|||||||
|
@@ -1,125 +0,0 @@
|
|||||||
// 获取表格数据
|
|
||||||
function getDomData() {
|
|
||||||
const dom = $('div[name=cutting_speed_ids]')
|
|
||||||
if (!dom.length) return
|
|
||||||
const table = dom.find('.o_list_table')
|
|
||||||
const thead = table.children('thead')
|
|
||||||
const tbody = table.children('tbody')
|
|
||||||
const tbody_child = tbody.children()
|
|
||||||
const hideTheadDom = thead.find('[data-name=process_capability]')
|
|
||||||
hideTheadDom.hide().next().hide()
|
|
||||||
hideTheadDom.before('<th customTh>精加工</th><th customTh>粗加工</th>')
|
|
||||||
tbody_child.each(function () {
|
|
||||||
const dom = $(this).children('[name=process_capability]')
|
|
||||||
if(!dom.length) return
|
|
||||||
dom.css('cssText', 'display: none!important').next().css('cssText', 'display: none!important')
|
|
||||||
const isCu = dom.text() == '粗加工' // 是否粗加工
|
|
||||||
const v = dom.next().text() // 切削速度
|
|
||||||
dom.after(`<td customSpeed="1" name="process_capability" is="精加工" val="${ v }">${!isCu ? v : ''}</td><td customSpeed="1" name="process_capability" is="粗加工" val="${ v }">${isCu ? v : ''}</td>`)
|
|
||||||
setListenClick()
|
|
||||||
})
|
|
||||||
return;
|
|
||||||
handleTbody(tbody, newTableData, ΦList, table)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听点击
|
|
||||||
function setListenClick() {
|
|
||||||
$(document).click(function (e) {
|
|
||||||
if ($(e.target).attr('customSpeed')) {
|
|
||||||
const orginV = $('[customInput=1]').children('input').val()
|
|
||||||
$('[customInput=1]').parent().html(orginV)
|
|
||||||
const v = $(e.target).attr('val')
|
|
||||||
const is = $(e.target).attr('is')
|
|
||||||
$(e.target).html('')
|
|
||||||
const input = $('<div customInput="1" is="' + is + '" class="o_field_widget o_field_char"><input class="o_input" type="text" autocomplete="off" maxlength="20"></div>')
|
|
||||||
input.children('input').val(v)
|
|
||||||
$(e.target).append(input)
|
|
||||||
input.children('input').focus()
|
|
||||||
input.children('input').select()
|
|
||||||
} else if ($(e.target).attr('customInput')) {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
const orginV = $('[customInput=1]').children('input').val()
|
|
||||||
$('[customInput=1]').parent().html(orginV)
|
|
||||||
const v = $(e.target).attr('val')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
$(document).off('change') // 防止重复绑定
|
|
||||||
$(document).on('change', '[customInput] input', async function () {
|
|
||||||
$(this).parents('td').attr('val', $(this).val())
|
|
||||||
$(this).parents('td').siblings('[customspeed]').attr('val', $(this).val())
|
|
||||||
var eve1 = new Event('change')
|
|
||||||
var eve2 = new Event('input')
|
|
||||||
var eve3 = new Event('click')
|
|
||||||
let patchSpeedDom = $(this).parents('td').siblings('[name=cutting_speed]')
|
|
||||||
let patchProcessDom = $(this).parents('td').siblings('[name=process_capability]')
|
|
||||||
$(this).parents('td').siblings('[customspeed]').text('') // 清空其他加工类型的数据
|
|
||||||
await timeOut(500)
|
|
||||||
patchProcessDom[0].dispatchEvent(eve3)
|
|
||||||
await timeOut(200)
|
|
||||||
const processVal = $(this).parent().attr('is')
|
|
||||||
patchProcessDom.find('select').val(`"${processVal}"`) // 设置源select的val为“加工类型 is”、
|
|
||||||
patchProcessDom.attr("data-tooltip", `${processVal}`)
|
|
||||||
patchProcessDom.find('select')[0].dispatchEvent(eve1)
|
|
||||||
|
|
||||||
patchSpeedDom[0].dispatchEvent(eve3)
|
|
||||||
await timeOut(200)
|
|
||||||
patchSpeedDom.find('input').val($(this).val())
|
|
||||||
await timeOut(50)
|
|
||||||
patchSpeedDom.find('input')[0].dispatchEvent(eve2)
|
|
||||||
patchSpeedDom.find('input')[0].dispatchEvent(eve1)
|
|
||||||
})
|
|
||||||
$(document).off('blur') // 防止重复绑定
|
|
||||||
$(document).on('blur', '[customInput] input', async function () {
|
|
||||||
if(!$(this).length) return
|
|
||||||
|
|
||||||
$(this).parents('td').siblings('[customspeed]').text('') // 清空其他加工类型的数据
|
|
||||||
let patchProcessDom = $(this).parents('td').siblings('[name=process_capability]')
|
|
||||||
try {
|
|
||||||
patchProcessDom[0].dispatchEvent(new Event('click'))
|
|
||||||
const processVal = $(this).parent().attr('is')
|
|
||||||
patchProcessDom.find('select').val(`"${processVal}"`) // 设置源select的val为“加工类型 is”、
|
|
||||||
patchProcessDom.attr("data-tooltip", `${processVal}`)
|
|
||||||
patchProcessDom.find('select')[0].dispatchEvent(new Event('change'))
|
|
||||||
} catch {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
function timeOut(time) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
setTimeout(() => {
|
|
||||||
resolve()
|
|
||||||
}, time)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function listenAdd() {
|
|
||||||
$('td.o_field_x2many_list_row_add a').click(async function () {
|
|
||||||
await timeOut(500)
|
|
||||||
const tr = $('.o_list_table').children('tbody').children('tr').eq(-2)
|
|
||||||
if(tr.children('td').eq(2).text() == '') {
|
|
||||||
const dom = tr.children('[name=process_capability]')
|
|
||||||
if(!dom.length) return
|
|
||||||
dom.css('cssText', 'display: none!important').next().css('cssText', 'display: none!important')
|
|
||||||
const isCu = dom.text() == '粗加工' // 是否粗加工
|
|
||||||
const v = dom.next().text() // 切削速度
|
|
||||||
dom.after(`<td customSpeed="1" name="process_capability" is="精加工" val="${ v }">${!isCu ? v : ''}</td><td customSpeed="1" name="process_capability" is="粗加工" val="${ v }">${isCu ? v : ''}</td>`)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function listenSave() {
|
|
||||||
$('.o_form_button_save').click( async function () {
|
|
||||||
await timeOut(1000)
|
|
||||||
if($(this).parent().next().length) return
|
|
||||||
$('th[customTh],td[cusomSpeed]').remove()
|
|
||||||
getDomData()
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
listenAdd()
|
|
||||||
listenSave()
|
|
||||||
getDomData()
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
// 因为表格可以拖动设置宽度,所以需要用js设置初始宽度
|
|
||||||
function setBasicParamTableWidth() {
|
|
||||||
// const _100px = 'th[data-name="cutting_blade_length"],th[data-name="cutting_blade_length"],th[data-name="name"],th[data-name="tip_handling_size"],th[data-name="cutting_depth_max"],th[data-name="diameter_inner_circle"],th[data-name="diameter_mounting_hole" ],th[data-name="radius_tip_re" ],th[data-name="is_chip_breaker"],th[data-name="chip_breaker_type_code"],th[data-name="blade_profile"]'
|
|
||||||
// const _65px = 'th[data-name="edge_angle"],th[data-name="relief_angle"],[data-name="total_length"],th[data-name="length"],th[data-name="thickness"],th[data-name="blade_number"]'
|
|
||||||
// const _80px = 'th[data-name="arbor_diameter"],th[data-name="head_height"],th[data-name="head_width"],th[data-name="head_length"],th[data-name="blade_diameter"],th[data-name="blade_length"] ,th[data-name="neck_length"] ,th[data-name="neck_diameter"] ,th[data-name="shank_diameter"],th[data-name="shank_length"],th[data-name="tip_diameter"],th[data-name="knife_tip_taper"],th[data-name="blade_helix_angle"] ,th[data-name="blade_width"],th[data-name="blade_depth"]'
|
|
||||||
// const _50px = 'th[data-name="pitch"],th[data-name="width"],th[data-name="height"]'
|
|
||||||
|
|
||||||
const basicParamDom = $('.fixTableCss')
|
|
||||||
// const basicParamDom_100px = basicParamDom.find(_100px) // 四字以上
|
|
||||||
// const basicParamDom_65px = basicParamDom.find(_65px) // 大概三个字加单位
|
|
||||||
// const basicParamDom_80px = basicParamDom.find(_80px) // 大概四个字加单位
|
|
||||||
// const basicParamDom_50px= basicParamDom.find(_50px) // 大概两个字加单位
|
|
||||||
//
|
|
||||||
// basicParamDom_100px.css({'width': '100px', 'max-width': 'auto', ',min-width': 'auto'})
|
|
||||||
// basicParamDom_65px.css({'width': '65px', 'max-width': 'auto', ',min-width': 'auto'})
|
|
||||||
// basicParamDom_80px.css({'width': '80px', 'max-width': 'auto', ',min-width': 'auto'})
|
|
||||||
// basicParamDom_50px.css({'width': '50px', 'max-width': 'auto', ',min-width': 'auto'})
|
|
||||||
let dom = []
|
|
||||||
try {
|
|
||||||
dom = basicParamDom.find('table').find('thead').children().children()
|
|
||||||
|
|
||||||
} catch {
|
|
||||||
dom = []
|
|
||||||
}
|
|
||||||
if (!dom) return
|
|
||||||
dom.each(function () {
|
|
||||||
if ($(this).hasClass('row_no') >= 0) { // 序号列
|
|
||||||
// 不设置 通过css设置
|
|
||||||
}
|
|
||||||
const text = $(this).text().split('(')
|
|
||||||
if ($(this).attr('data-name') == 'name' || text[0].length > 4) {
|
|
||||||
$(this).width('100px')
|
|
||||||
} else if(text[0].length == 4){
|
|
||||||
$(this).width('80px')
|
|
||||||
} else if(text[0].length == 3){
|
|
||||||
$(this).width('65px')
|
|
||||||
} else if(text[0].length == 2){
|
|
||||||
$(this).width('50px')
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
setBasicParamTableWidth()
|
|
||||||
$('.o_field_many2one_selection').on('click', $('#cutting_tool_material_id + ul'), function () {
|
|
||||||
setTimeout(setBasicParamTableWidth, 500)
|
|
||||||
})
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
// 获取表格数据
|
|
||||||
function getDomData() {
|
|
||||||
const dom = $('#updateTable').prev()
|
|
||||||
if (!dom.length) return
|
|
||||||
const table = $('#updateTable').prev().find('.o_list_table')
|
|
||||||
const customTable = table.clone()
|
|
||||||
customTable.addClass('customTable')
|
|
||||||
table.parent().append(customTable)
|
|
||||||
table.hide()
|
|
||||||
const thead = customTable.children('thead')
|
|
||||||
const tbody = customTable.children('tbody')
|
|
||||||
const tableData = []
|
|
||||||
const tbody_child = tbody.children()
|
|
||||||
|
|
||||||
const tbody_child_len = tbody_child.length
|
|
||||||
|
|
||||||
for (let v = 0; v < tbody_child_len; v++) { // 将数据取出来到tableData里面
|
|
||||||
const data = tbody_child[v].innerText.split('\t')
|
|
||||||
// console.log('dom data',data)
|
|
||||||
const [index, deep, name, Φ, value] = data
|
|
||||||
tableData.push({index, deep, name, Φ, value})
|
|
||||||
}
|
|
||||||
const ΦList = [...new Set(tableData.map(_ => _.name))] // ΦList去重
|
|
||||||
const newTableData = {}
|
|
||||||
tableData.forEach(_ => {
|
|
||||||
const key = _.deep + '|' + _.Φ
|
|
||||||
!newTableData[key] ? newTableData[key] = {i: _.index} : '';
|
|
||||||
if (_.Φ) { // 去除没有Φ的脏数据
|
|
||||||
newTableData[key]['Φ' + _.Φ] = _.value
|
|
||||||
newTableData[key]['Φ' + _.Φ + 'i'] = _.index
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// console.log('qwdh',tableData, ΦList, newTableData);
|
|
||||||
|
|
||||||
if (ΦList.filter(_ => _).length == 0) return;
|
|
||||||
handleThead(thead, ΦList)
|
|
||||||
|
|
||||||
handleTbody(tbody, newTableData, ΦList, table)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重新设置表头、
|
|
||||||
function handleThead(thead, ΦList) {
|
|
||||||
const dom = thead.children().eq(0).children()
|
|
||||||
const len = dom.length
|
|
||||||
dom.eq(0).attr('rowspan', 2)
|
|
||||||
dom.eq(1).attr('rowspan', 2)
|
|
||||||
len == 5 ? dom.eq(2).attr('rowspan', 2) : ''
|
|
||||||
dom.eq(-2).attr('colspan', ΦList.length)
|
|
||||||
dom.eq(-1).remove()
|
|
||||||
|
|
||||||
const tr = document.createElement('tr')
|
|
||||||
for (let v = 0; v < ΦList.length; v++) {
|
|
||||||
const th = document.createElement('th')
|
|
||||||
th.innerText = 'Φ' + ΦList[v]
|
|
||||||
tr.append(th)
|
|
||||||
}
|
|
||||||
thead.append(tr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重新设置表格
|
|
||||||
function handleTbody(tbody, newTableData, ΦList, table) {
|
|
||||||
console.log(newTableData)
|
|
||||||
tbody.html('')
|
|
||||||
let i = 0
|
|
||||||
const data = Object.keys(newTableData)
|
|
||||||
// data.sort((a, b) => {
|
|
||||||
// a = a.split('=')[1].split('%')[0]
|
|
||||||
// b = b.split('=')[1].split('%')[0]
|
|
||||||
// return a - b
|
|
||||||
// })
|
|
||||||
// console.log('wqoqw ',ΦList)
|
|
||||||
data.forEach(_ => {
|
|
||||||
i++
|
|
||||||
const tr = $('<tr class="o_data_row"></tr>')
|
|
||||||
const td0 = $('<td></td>')
|
|
||||||
td0.text(i)
|
|
||||||
tr.append(td0)
|
|
||||||
const lit = _.split('|')
|
|
||||||
//
|
|
||||||
const td1 = $('<td></td>')
|
|
||||||
const td2 = $('<td></td>')
|
|
||||||
td1.text(lit[0])
|
|
||||||
td2.text(lit[1] || '')
|
|
||||||
tr.append(td1)
|
|
||||||
tr.append(td2)
|
|
||||||
ΦList.forEach(Φ => {
|
|
||||||
const td = $('<td class="o_data_cell cursor-pointer o_field_cell o_list_char"></td>')
|
|
||||||
td.text(newTableData[_]['Φ' + Φ])
|
|
||||||
td.attr('col', newTableData[_]['Φ' + Φ + 'i'])
|
|
||||||
td.attr('val', newTableData[_]['Φ' + Φ])
|
|
||||||
td.attr('coustomTd', 1)
|
|
||||||
tr.append(td)
|
|
||||||
})
|
|
||||||
// // for (let j = 0; j < ΦList.length; j++) {
|
|
||||||
// // const td = document.createElement('td')
|
|
||||||
// // td.innerText = newTableData[data[v]][_]
|
|
||||||
// // th.append(td)
|
|
||||||
// // }
|
|
||||||
tbody.append(tr)
|
|
||||||
})
|
|
||||||
// $(document).click(function (e) {
|
|
||||||
// if ($(e.target).attr('coustomTd')) {
|
|
||||||
// const orginV = $('[coustomInput=1]').children('input').val()
|
|
||||||
// $('[coustomInput=1]').parent().html(orginV)
|
|
||||||
// const v = $(e.target).attr('val')
|
|
||||||
// console.log($(e.target));
|
|
||||||
// $(e.target).html('')
|
|
||||||
// const input = $('<div coustomInput="1" name="feed_per_tooth" class="o_field_widget o_field_char"><input class="o_input" type="text" autocomplete="off" maxlength="20"></div>')
|
|
||||||
// input.children('input').val(v)
|
|
||||||
// $(e.target).append(input)
|
|
||||||
// input.children('input').focus()
|
|
||||||
// input.children('input').select()
|
|
||||||
// } else if ($(e.target).attr('coustomInput')) {
|
|
||||||
//
|
|
||||||
// } else {
|
|
||||||
// const orginV = $('[coustomInput=1]').children('input').val()
|
|
||||||
// $('[coustomInput=1]').parent().html(orginV)
|
|
||||||
// const v = $(e.target).attr('val')
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// $(document).off('change') // 防止重复绑定
|
|
||||||
// $(document).on('change', '[coustomInput] input', function () {
|
|
||||||
// $(this).parents('td').attr('val', $(this).val());
|
|
||||||
// var eve1 = new Event('change');
|
|
||||||
// var eve2 = new Event('input');
|
|
||||||
// var eve3 = new Event('click');
|
|
||||||
// const i = $(this).parents('td').attr('col');
|
|
||||||
// let patchDom = table.find('tbody').children('tr').eq(i - 1);
|
|
||||||
//
|
|
||||||
// if (patchDom.length === 0) {
|
|
||||||
// console.error('No such row found');
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// patchDom = patchDom.children().eq(-1);
|
|
||||||
//
|
|
||||||
// setTimeout(() => {
|
|
||||||
// if (patchDom.length === 0) {
|
|
||||||
// console.error('No such cell found');
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// patchDom[0].dispatchEvent(eve3); // Simulate click event
|
|
||||||
//
|
|
||||||
// setTimeout(() => {
|
|
||||||
// patchDom = patchDom.find('input');
|
|
||||||
// if (patchDom.length === 0) {
|
|
||||||
// console.error('No input found in the target cell');
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// patchDom.val($(this).val());
|
|
||||||
// patchDom[0].dispatchEvent(eve2);
|
|
||||||
// patchDom[0].dispatchEvent(eve1);
|
|
||||||
// }, 200);
|
|
||||||
// }, 500);
|
|
||||||
// });
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
getDomData()
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
.o_list_renderer .o_list_table tbody > tr > td:not(.o_list_record_selector):not(.o_handle_cell):not(.o_list_button):not(.o_list_record_remove) {
|
|
||||||
white-space: nowrap !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-truncate {
|
|
||||||
overflow: unset !important;
|
|
||||||
text-overflow: unset !important;
|
|
||||||
white-space: unset !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 设置表格不超出页面宽度
|
|
||||||
.o_form_view .o_field_widget .o_list_renderer {
|
|
||||||
width: calc(100% - 64px) !important;
|
|
||||||
margin:0 auto;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 表格针对处理
|
|
||||||
.fixTableCss {
|
|
||||||
text-align: center;
|
|
||||||
.o_list_number_th,.o_list_number {
|
|
||||||
text-align: center!important;
|
|
||||||
}
|
|
||||||
.ui-sortable {
|
|
||||||
tr > td:first-child {
|
|
||||||
padding: 0!important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.row_no {
|
|
||||||
padding: 0!important;;
|
|
||||||
width: 35px!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
th[data-name="total_length"],th[data-name="length"],th[data-name="thickness"] {
|
|
||||||
.flex-row-reverse {
|
|
||||||
span {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 其他不能用js处理的表格
|
|
||||||
.otherTableFix {
|
|
||||||
th[data-name="cutting_tool_material_id"] {
|
|
||||||
width: 100px!important;
|
|
||||||
}
|
|
||||||
th[data-name="ramping_angle_max"],th[data-name="ramping_angle_min"] {
|
|
||||||
width: 200px!important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,7 +14,6 @@
|
|||||||
|
|
||||||
.img-fluid {
|
.img-fluid {
|
||||||
max-width: unset !important;
|
max-width: unset !important;
|
||||||
width: 40px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.o_inner_group .img-fluid {
|
.o_inner_group .img-fluid {
|
||||||
|
|||||||
@@ -132,9 +132,11 @@
|
|||||||
<group string="加工能力">
|
<group string="加工能力">
|
||||||
<div>
|
<div>
|
||||||
<field name='jg_image_id' widget="custom_many2many_checkboxes">
|
<field name='jg_image_id' widget="custom_many2many_checkboxes">
|
||||||
|
|
||||||
<tree>
|
<tree>
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="image" widget="image"/>
|
<field name="image" widget="image"
|
||||||
|
options="{'size': [100, 100], 'click enlarge': True}"/>
|
||||||
|
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
@@ -147,7 +149,8 @@
|
|||||||
|
|
||||||
<tree>
|
<tree>
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="image" widget="image"/>
|
<field name="image" widget="image"
|
||||||
|
options="{'size': [100, 100], 'click enlarge': True}"/>
|
||||||
|
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
@@ -202,20 +205,20 @@
|
|||||||
<field name="T_trough_distance" class="o_address_zip"
|
<field name="T_trough_distance" class="o_address_zip"
|
||||||
options="{'format': false}"/>
|
options="{'format': false}"/>
|
||||||
</div>
|
</div>
|
||||||
<!-- <field name="feed_speed" required="1"/>-->
|
<!-- <field name="feed_speed" required="1"/>-->
|
||||||
<!-- <label for="precision_min" string="X轴定位精度(mm)"/>-->
|
<!-- <label for="precision_min" string="X轴定位精度(mm)"/>-->
|
||||||
<!-- <div class="test_model">-->
|
<!-- <div class="test_model">-->
|
||||||
<!-- <label for="precision_min" string="最小(min)"/>-->
|
<!-- <label for="precision_min" string="最小(min)"/>-->
|
||||||
<!-- <field name="precision_min" class="o_address_zip" required="1"-->
|
<!-- <field name="precision_min" class="o_address_zip" required="1"-->
|
||||||
<!-- options="{'format': false}"/>-->
|
<!-- options="{'format': false}"/>-->
|
||||||
<!-- <span>&nbsp;</span>-->
|
<!-- <span>&nbsp;</span>-->
|
||||||
<!-- <label for="precision_max" string="最大(max)"/>-->
|
<!-- <label for="precision_max" string="最大(max)"/>-->
|
||||||
<!-- <field name="precision_max" class="o_address_zip" required="1"-->
|
<!-- <field name="precision_max" class="o_address_zip" required="1"-->
|
||||||
<!-- options="{'format': false}"/>-->
|
<!-- options="{'format': false}"/>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
|
|
||||||
<!-- <field name="lead_screw" required="1"/>-->
|
<!-- <field name="lead_screw" required="1"/>-->
|
||||||
<!-- <field name="guide_rail" required="1"/>-->
|
<!-- <field name="guide_rail" required="1"/>-->
|
||||||
<field name="number_of_axles" required="1" widget="radio"
|
<field name="number_of_axles" required="1" widget="radio"
|
||||||
options="{'horizontal': true}"/>
|
options="{'horizontal': true}"/>
|
||||||
<label for="x_axis" string="加工行程(mm)"
|
<label for="x_axis" string="加工行程(mm)"
|
||||||
@@ -311,22 +314,22 @@
|
|||||||
<page string="刀库参数">
|
<page string="刀库参数">
|
||||||
<group>
|
<group>
|
||||||
<group string="刀具">
|
<group string="刀具">
|
||||||
<!-- <field name="knife_type" required="1"/>-->
|
<!-- <field name="knife_type" required="1"/>-->
|
||||||
<field name="number_of_knife_library" required="1" options="{'format': false}"/>
|
<field name="number_of_knife_library" required="1" options="{'format': false}"/>
|
||||||
<!-- <field name="tool_speed" required="1"/>-->
|
<!-- <field name="tool_speed" required="1"/>-->
|
||||||
<field name="tool_full_diameter_max"/>
|
<field name="tool_full_diameter_max"/>
|
||||||
<field name="tool_perimeter_diameter_max"/>
|
<field name="tool_perimeter_diameter_max"/>
|
||||||
<field name="tool_long_max"/>
|
<field name="tool_long_max"/>
|
||||||
<!-- <label for="tool_diameter_min" string="刀具刀径(mm)"/>-->
|
<!-- <label for="tool_diameter_min" string="刀具刀径(mm)"/>-->
|
||||||
<!-- <div class="test_model">-->
|
<!-- <div class="test_model">-->
|
||||||
<!-- <label for="tool_diameter_min" string="最小(min)"/>-->
|
<!-- <label for="tool_diameter_min" string="最小(min)"/>-->
|
||||||
<!-- <field name="tool_diameter_min" class="o_address_zip" required="1"-->
|
<!-- <field name="tool_diameter_min" class="o_address_zip" required="1"-->
|
||||||
<!-- options="{'format': false}"/>-->
|
<!-- options="{'format': false}"/>-->
|
||||||
<!-- <span>&nbsp;</span>-->
|
<!-- <span>&nbsp;</span>-->
|
||||||
<!-- <label for="tool_diameter_max" string="最大(max)"/>-->
|
<!-- <label for="tool_diameter_max" string="最大(max)"/>-->
|
||||||
<!-- <field name="tool_diameter_max" class="o_address_zip" required="1"-->
|
<!-- <field name="tool_diameter_max" class="o_address_zip" required="1"-->
|
||||||
<!-- options="{'format': false}"/>-->
|
<!-- options="{'format': false}"/>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<field name="tool_quality_max"/>
|
<field name="tool_quality_max"/>
|
||||||
<field name="T_tool_time"/>
|
<field name="T_tool_time"/>
|
||||||
<field name="C_tool_time"/>
|
<field name="C_tool_time"/>
|
||||||
@@ -614,45 +617,4 @@
|
|||||||
<field name="res_model">sf.machine.control_system</field>
|
<field name="res_model">sf.machine.control_system</field>
|
||||||
<field name="view_mode">tree</field>
|
<field name="view_mode">tree</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
#------------------加工精度------------------
|
|
||||||
<record model="ir.ui.view" id="tree_sf_machining_accuracy_view">
|
|
||||||
<field name="name">tree.sf.machining.accuracy</field>
|
|
||||||
<field name="model">sf.machining.accuracy</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<tree string="加工精度" create="0" edit="0" delete="0">
|
|
||||||
<field name="name"/>
|
|
||||||
<field name="standard_tolerance"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="action_sf_machining_accuracy" model="ir.actions.act_window">
|
|
||||||
<field name="name">加工精度</field>
|
|
||||||
<field name="type">ir.actions.act_window</field>
|
|
||||||
<field name="res_model">sf.machining.accuracy</field>
|
|
||||||
<field name="view_mode">tree</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
#------------------坯料冗余量------------------
|
|
||||||
<record model="ir.ui.view" id="tree_sf_embryo_redundancy_view">
|
|
||||||
<field name="name">tree.sf.embryo.redundancy</field>
|
|
||||||
<field name="model">sf.embryo.redundancy</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<tree string="坯料冗余量" create="0" edit="0" delete="0">
|
|
||||||
<field name="name"/>
|
|
||||||
<field name="code"/>
|
|
||||||
<field name="long"/>
|
|
||||||
<field name="width"/>
|
|
||||||
<field name="height"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="action_sf_embryo_redundancy" model="ir.actions.act_window">
|
|
||||||
<field name="name">坯料冗余量</field>
|
|
||||||
<field name="type">ir.actions.act_window</field>
|
|
||||||
<field name="res_model">sf.embryo.redundancy</field>
|
|
||||||
<field name="view_mode">tree</field>
|
|
||||||
</record>
|
|
||||||
</odoo>
|
</odoo>
|
||||||
@@ -141,25 +141,12 @@
|
|||||||
sequence="1"
|
sequence="1"
|
||||||
action="action_sf_machine_brand"/>
|
action="action_sf_machine_brand"/>
|
||||||
|
|
||||||
<menuitem
|
|
||||||
id="menu_sf_embryo_redundancy"
|
|
||||||
parent="menu_sf_base"
|
|
||||||
name="坯料冗余"
|
|
||||||
sequence="2"
|
|
||||||
action="action_sf_embryo_redundancy"/>
|
|
||||||
|
|
||||||
<menuitem
|
|
||||||
id="menu_sf_machining_accuracy"
|
|
||||||
parent="menu_sf_base"
|
|
||||||
name="加工精度"
|
|
||||||
sequence="3"
|
|
||||||
action="action_sf_machining_accuracy"/>
|
|
||||||
|
|
||||||
<menuitem
|
<menuitem
|
||||||
id="menu_sf_machine_control_system"
|
id="menu_sf_machine_control_system"
|
||||||
parent="menu_sf_base"
|
parent="menu_sf_base"
|
||||||
name="数控系统"
|
name="数控系统"
|
||||||
sequence="4"
|
sequence="1"
|
||||||
action="action_sf_machine_control_system"/>
|
action="action_sf_machine_control_system"/>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -360,7 +360,6 @@
|
|||||||
<field name="cooling_jacket"/>
|
<field name="cooling_jacket"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
<script src="/sf_base/static/js/setTableWidth.js?time=3"></script>
|
|
||||||
</page>
|
</page>
|
||||||
<page string="切削速度Vc"
|
<page string="切削速度Vc"
|
||||||
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀片'))]}">
|
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀片'))]}">
|
||||||
@@ -381,9 +380,6 @@
|
|||||||
<field name="cutting_speed"/>
|
<field name="cutting_speed"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
<script src="/sf_base/static/js/customTable.js?time=3"></script>
|
|
||||||
<script src="/sf_base/static/js/setTableWidth.js?time=3"></script>
|
|
||||||
|
|
||||||
</page>
|
</page>
|
||||||
<page string="每齿走刀量fz"
|
<page string="每齿走刀量fz"
|
||||||
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀片'))]}">
|
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀片'))]}">
|
||||||
@@ -396,8 +392,6 @@
|
|||||||
<field name="feed_per_tooth"/>
|
<field name="feed_per_tooth"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
<div id="updateTable"></div>
|
|
||||||
<script src="/sf_base/static/js/updateTable.js?time=3"></script>
|
|
||||||
<field name="feed_per_tooth_ids_3"
|
<field name="feed_per_tooth_ids_3"
|
||||||
attrs="{'invisible': [('cutting_tool_type', 'not in', ('刀片'))]}">
|
attrs="{'invisible': [('cutting_tool_type', 'not in', ('刀片'))]}">
|
||||||
<tree editable="bottom" class="center" create="0" delete="0">
|
<tree editable="bottom" class="center" create="0" delete="0">
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
|
||||||
|
|
||||||
from odoo import http
|
from odoo import http
|
||||||
from odoo.http import request
|
from odoo.http import request
|
||||||
|
|
||||||
@@ -125,8 +123,7 @@ class Sf_Bf_Connect(http.Controller):
|
|||||||
res['factory_order_no'] = order_id.name
|
res['factory_order_no'] = order_id.name
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback_error = traceback.format_exc()
|
logging.info('get_bfm_process_order_list error:%s' % e)
|
||||||
logging.error('get_bfm_process_order_list error: %s' % traceback_error)
|
|
||||||
res['status'] = -1
|
res['status'] = -1
|
||||||
res['message'] = '工厂创建销售订单和产品失败,请联系管理员'
|
res['message'] = '工厂创建销售订单和产品失败,请联系管理员'
|
||||||
request.cr.rollback()
|
request.cr.rollback()
|
||||||
|
|||||||
@@ -151,12 +151,6 @@ class JdEclp(models.Model):
|
|||||||
_logger.info('准备调接口1')
|
_logger.info('准备调接口1')
|
||||||
url1 = config['bfm_url_new'] + '/api/create/jd/order'
|
url1 = config['bfm_url_new'] + '/api/create/jd/order'
|
||||||
requests.post(url1, json=json1, data=None)
|
requests.post(url1, json=json1, data=None)
|
||||||
# ===============修改销售订单状态为【物流中】===================
|
|
||||||
item = self.env['sale.order'].sudo().search([('name', '=', self.origin)])
|
|
||||||
if not item:
|
|
||||||
raise ValidationError('没有查询到订单号为【%s】的销售订单!' % self.origin)
|
|
||||||
else:
|
|
||||||
item.write({'state': 'physical_distribution'})
|
|
||||||
_logger.info('调用成功1')
|
_logger.info('调用成功1')
|
||||||
_logger.info('准备调接口2')
|
_logger.info('准备调接口2')
|
||||||
json2 = {
|
json2 = {
|
||||||
|
|||||||
@@ -36,9 +36,6 @@ class StatusChange(models.Model):
|
|||||||
# 使用super()来调用原始方法(在本例中为'sale.order'模型的'action_confirm'方法)
|
# 使用super()来调用原始方法(在本例中为'sale.order'模型的'action_confirm'方法)
|
||||||
try:
|
try:
|
||||||
res = super(StatusChange, self).action_confirm()
|
res = super(StatusChange, self).action_confirm()
|
||||||
# 修改销售订单状态为【加工中】
|
|
||||||
self.write({'state': 'processing'})
|
|
||||||
logging.info('原生方法返回结果:%s' % res)
|
|
||||||
# 原有方法执行后,进行额外的操作(如调用外部API)
|
# 原有方法执行后,进行额外的操作(如调用外部API)
|
||||||
process_start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
process_start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||||
config = self.env['res.config.settings'].get_values()
|
config = self.env['res.config.settings'].get_values()
|
||||||
@@ -64,7 +61,6 @@ class StatusChange(models.Model):
|
|||||||
traceback_error = traceback.format_exc()
|
traceback_error = traceback.format_exc()
|
||||||
logging.error("工厂加工同步订单状态失败:%s " % traceback_error)
|
logging.error("工厂加工同步订单状态失败:%s " % traceback_error)
|
||||||
raise UserError(e)
|
raise UserError(e)
|
||||||
logging.info('最终返回值:%s' % res)
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def action_cancel(self):
|
def action_cancel(self):
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
<field name="inherit_id" ref="stock.view_picking_form"/>
|
<field name="inherit_id" ref="stock.view_picking_form"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//form//sheet//notebook//page[@name='operations']" position="after">
|
<xpath expr="//form//sheet//notebook//page[@name='operations']" position="after">
|
||||||
<page string="发货信息" name="tracking" attrs="{'invisible': [('picking_type_code', '!=', 'outgoing')]}">
|
<page string="发货信息" name="tracking">
|
||||||
<group>
|
<group>
|
||||||
<group>
|
<group>
|
||||||
<field name="senderNickName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
|
<field name="senderNickName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
""",
|
""",
|
||||||
'category': 'sf',
|
'category': 'sf',
|
||||||
'website': 'https://www.sf.jikimo.com',
|
'website': 'https://www.sf.jikimo.com',
|
||||||
'depends': ['sf_base', 'mrp_subcontracting', 'purchase_stock', 'uom'],
|
'depends': ['sf_base', 'web_widget_model_viewer', 'mrp_subcontracting', 'purchase_stock', 'uom', ],
|
||||||
'data': [
|
'data': [
|
||||||
'data/product_data.xml',
|
'data/product_data.xml',
|
||||||
'data/uom_data.xml',
|
'data/uom_data.xml',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<data noupdate="1">
|
<data>
|
||||||
<record id="product_category_embryo_sf" model="product.category">
|
<record id="product_category_embryo_sf" model="product.category">
|
||||||
<field name="name">坯料</field>
|
<field name="name">坯料</field>
|
||||||
<field name="type">坯料</field>
|
<field name="type">坯料</field>
|
||||||
|
|||||||
@@ -1,3 +1 @@
|
|||||||
from . import product_supplierinfo
|
from . import product_supplierinfo
|
||||||
from . import stock_rule_inherit
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
import logging
|
|
||||||
from itertools import groupby
|
|
||||||
from odoo import models, fields, api, _
|
|
||||||
|
|
||||||
|
|
||||||
class StockRuleInherit(models.Model):
|
|
||||||
_inherit = 'stock.rule'
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def _run_buy(self, procurements):
|
|
||||||
# 判断补货组的采购类型
|
|
||||||
procurements_group = {'standard': [], 'outsourcing': []}
|
|
||||||
for procurement, rule in procurements:
|
|
||||||
is_outsourcing = False
|
|
||||||
product = procurement.product_id
|
|
||||||
# 获取主 BOM
|
|
||||||
bom = self.env['mrp.bom'].search([('product_tmpl_id', '=', product.product_tmpl_id.id)], limit=1)
|
|
||||||
|
|
||||||
if bom:
|
|
||||||
# 遍历 BOM 中的组件(即坯料等)
|
|
||||||
for line in bom.bom_line_ids:
|
|
||||||
raw_material = line.product_id
|
|
||||||
# 检查路线
|
|
||||||
for route in raw_material.route_ids:
|
|
||||||
# print('route.name:', route.name)
|
|
||||||
if route.name == '按订单补给外包商':
|
|
||||||
is_outsourcing = True
|
|
||||||
|
|
||||||
if is_outsourcing:
|
|
||||||
procurements_group['outsourcing'].append((procurement, rule))
|
|
||||||
else:
|
|
||||||
procurements_group['standard'].append((procurement, rule))
|
|
||||||
|
|
||||||
for key, value in procurements_group.items():
|
|
||||||
super(StockRuleInherit, self)._run_buy(value)
|
|
||||||
|
|
||||||
if key == 'outsourcing':
|
|
||||||
for procurement, rule in value:
|
|
||||||
supplier = procurement.values.get('supplier')
|
|
||||||
if supplier:
|
|
||||||
domain = rule._make_po_get_domain(procurement.company_id, procurement.values,
|
|
||||||
supplier.partner_id)
|
|
||||||
logging.info("domain=============: %s", domain)
|
|
||||||
po = self.env['purchase.order'].sudo().search([
|
|
||||||
('partner_id', '=', supplier.partner_id.id),
|
|
||||||
('company_id', '=', procurement.company_id.id), # 保证公司一致
|
|
||||||
('origin', 'like', procurement.origin), # 根据来源匹配
|
|
||||||
('state', '=', 'draft') # 状态为草稿
|
|
||||||
], limit=1)
|
|
||||||
logging.info("po=: %s", po)
|
|
||||||
if po:
|
|
||||||
po.write({'purchase_type': 'outsourcing'})
|
|
||||||
|
|
||||||
# # 首先调用父类的 _run_buy 方法,以保留原有逻辑
|
|
||||||
# super(StockRuleInherit, self)._run_buy(procurements)
|
|
||||||
|
|
||||||
# 然后在这里添加自定义的逻辑
|
|
||||||
# for procurement, rule in procurements:
|
|
||||||
# product = procurement.product_id
|
|
||||||
# # 获取主 BOM
|
|
||||||
# bom = self.env['mrp.bom'].search([('product_tmpl_id', '=', product.product_tmpl_id.id)], limit=1)
|
|
||||||
|
|
||||||
# if bom:
|
|
||||||
# # 遍历 BOM 中的组件(即坯料等)
|
|
||||||
# for line in bom.bom_line_ids:
|
|
||||||
# raw_material = line.product_id
|
|
||||||
# # 检查路线
|
|
||||||
# for route in raw_material.route_ids:
|
|
||||||
# # print('route.name:', route.name)
|
|
||||||
# if route.name == '按订单补给外包商': # 或者用 route.id 检查精确的路线
|
|
||||||
# print("按订单补给外包商============是")
|
|
||||||
# # 使用 procurement.values['supplier'] 获取供应商
|
|
||||||
# supplier = procurement.values.get('supplier')
|
|
||||||
# if supplier:
|
|
||||||
# domain = rule._make_po_get_domain(procurement.company_id, procurement.values,
|
|
||||||
# supplier.partner_id)
|
|
||||||
# logging.info("domain=============: %s", domain)
|
|
||||||
# po = self.env['purchase.order'].sudo().search([
|
|
||||||
# ('partner_id', '=', supplier.partner_id.id),
|
|
||||||
# ('company_id', '=', procurement.company_id.id), # 保证公司一致
|
|
||||||
# ('origin', '=', procurement.origin), # 根据来源匹配
|
|
||||||
# ('state', '=', 'draft') # 状态为草稿
|
|
||||||
# ], limit=1)
|
|
||||||
# logging.info("po=: %s", po)
|
|
||||||
# if po:
|
|
||||||
# po.write({'purchase_type': 'outsourcing'})
|
|
||||||
# break
|
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
""",
|
""",
|
||||||
'category': 'sf',
|
'category': 'sf',
|
||||||
'website': 'https://www.sf.jikimo.com',
|
'website': 'https://www.sf.jikimo.com',
|
||||||
'depends': ['sf_sale', 'sf_dlm', 'sf_manufacturing','jikimo_attachment_viewer'],
|
'depends': ['sf_sale', 'sf_dlm', 'sf_manufacturing'],
|
||||||
'data': [
|
'data': [
|
||||||
'data/stock_data.xml',
|
'data/stock_data.xml',
|
||||||
'views/product_template_management_view.xml',
|
'views/product_template_management_view.xml',
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
<odoo>
|
<odoo>
|
||||||
<data>
|
<data>
|
||||||
<record id="mrp.product_template_action" model="ir.actions.act_window">
|
<record id="mrp.product_template_action" model="ir.actions.act_window">
|
||||||
<field name="view_mode">tree,kanban,form,activity</field>
|
|
||||||
<field name="context">
|
<field name="context">
|
||||||
{"search_default_categ_id":1,"search_default_consumable": 1, 'default_detailed_type': 'product'}
|
{"search_default_categ_id":1,"search_default_consumable": 1, 'default_detailed_type': 'product'}
|
||||||
</field>
|
</field>
|
||||||
@@ -16,10 +15,8 @@
|
|||||||
<field name='categ_id' class="custom_required" attrs="{'readonly': [('id', '!=', False)]}"/>
|
<field name='categ_id' class="custom_required" attrs="{'readonly': [('id', '!=', False)]}"/>
|
||||||
<field name='is_bfm' invisible="1"/>
|
<field name='is_bfm' invisible="1"/>
|
||||||
<field name='categ_type' invisible="1"/>
|
<field name='categ_type' invisible="1"/>
|
||||||
<field name='part_name' attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
|
|
||||||
<field name='part_number' attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
|
<field name='part_number' attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
|
||||||
<field name='manual_quotation' attrs="{'invisible':[('upload_model_file', '=', [])]}"/>
|
<field name='manual_quotation' attrs="{'invisible':[('upload_model_file', '=', [])]}"/>
|
||||||
<field name="is_customer_provided" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])], 'readonly': True}" />
|
|
||||||
<field name="upload_model_file"
|
<field name="upload_model_file"
|
||||||
widget="many2many_binary"
|
widget="many2many_binary"
|
||||||
attrs="{'invisible': ['|', '|',('categ_type', '!=', '成品'),('categ_type', '=', False),('is_bfm','=', True)]}"/>
|
attrs="{'invisible': ['|', '|',('categ_type', '!=', '成品'),('categ_type', '=', False),('is_bfm','=', True)]}"/>
|
||||||
@@ -66,9 +63,6 @@
|
|||||||
attrs="{'invisible': [('categ_type', '!=', '夹具')],'required': [('categ_type', '=', '夹具')]}"
|
attrs="{'invisible': [('categ_type', '!=', '夹具')],'required': [('categ_type', '=', '夹具')]}"
|
||||||
domain="[('fixture_model_id','=',fixture_model_id)]"/>
|
domain="[('fixture_model_id','=',fixture_model_id)]"/>
|
||||||
</field>
|
</field>
|
||||||
<xpath expr="//field[@name='uom_id']" position="before">
|
|
||||||
<field name="is_manual_processing" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])], 'readonly': True}" />
|
|
||||||
</xpath>
|
|
||||||
<xpath expr="//label[@for='volume']" position="before">
|
<xpath expr="//label[@for='volume']" position="before">
|
||||||
<label for="length" string="尺寸"
|
<label for="length" string="尺寸"
|
||||||
attrs="{'invisible':[('product_variant_count', '>', 1), ('is_product_variant', '=', False)]}"/>
|
attrs="{'invisible':[('product_variant_count', '>', 1), ('is_product_variant', '=', False)]}"/>
|
||||||
@@ -115,17 +109,6 @@
|
|||||||
'刀具')], 'required': True}
|
'刀具')], 'required': True}
|
||||||
</attribute>
|
</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//sheet//notebook" position="inside">
|
|
||||||
<page string="2D加工图纸" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])]}">
|
|
||||||
<field name='machining_drawings' widget="adaptive_viewer"/>
|
|
||||||
</page>
|
|
||||||
</xpath>
|
|
||||||
<xpath expr="//sheet//notebook" position="inside">
|
|
||||||
<page string="质检标准" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])]}">
|
|
||||||
<field name='quality_standard' widget="adaptive_viewer"/>
|
|
||||||
|
|
||||||
</page>
|
|
||||||
</xpath>
|
|
||||||
<!-- <xpath expr="//field[@name='default_code']" position="attributes">-->
|
<!-- <xpath expr="//field[@name='default_code']" position="attributes">-->
|
||||||
<!-- <attribute name="attrs">{'readonly': [('categ_type', '=', '刀具')], 'invisible':-->
|
<!-- <attribute name="attrs">{'readonly': [('categ_type', '=', '刀具')], 'invisible':-->
|
||||||
<!-- [('product_variant_count', '>' , 1)]}-->
|
<!-- [('product_variant_count', '>' , 1)]}-->
|
||||||
|
|||||||
@@ -2,4 +2,3 @@
|
|||||||
|
|
||||||
from . import hr_employee
|
from . import hr_employee
|
||||||
from . import res_config_setting
|
from . import res_config_setting
|
||||||
from . import res_users
|
|
||||||
|
|||||||
@@ -11,52 +11,6 @@ class JkmPracticeEmployee(models.Model):
|
|||||||
|
|
||||||
we_id = fields.Char(string='企微ID', index=True)
|
we_id = fields.Char(string='企微ID', index=True)
|
||||||
|
|
||||||
@api.model_create_multi
|
|
||||||
def create(self, vals_list):
|
|
||||||
for val in vals_list:
|
|
||||||
if 'work_email' in val:
|
|
||||||
val["we_id"] = self._get_we_id(val.get('work_email'))
|
|
||||||
return super(JkmPracticeEmployee, self).create(vals_list)
|
|
||||||
|
|
||||||
def write(self, vals):
|
|
||||||
if 'work_email' in vals:
|
|
||||||
vals["we_id"] = self._get_we_id(vals.get('work_email'))
|
|
||||||
return super(JkmPracticeEmployee, self).write(vals)
|
|
||||||
|
|
||||||
def unlink(self):
|
|
||||||
for record in self:
|
|
||||||
res_partner_obj = record.env['res.partner'].sudo().search([('email', '=', record.work_email)])
|
|
||||||
if res_partner_obj:
|
|
||||||
res_partner_obj.unlink()
|
|
||||||
res = super(JkmPracticeEmployee, self).unlink()
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
@api.depends('work_contact_id', 'work_contact_id.mobile', 'work_contact_id.email')
|
|
||||||
def _compute_work_contact_details(self):
|
|
||||||
for employee in self:
|
|
||||||
if employee.work_contact_id:
|
|
||||||
employee.mobile_phone = employee.work_contact_id.mobile
|
|
||||||
employee.work_email = employee.work_contact_id.email
|
|
||||||
if employee.work_contact_id.email:
|
|
||||||
employee.we_id = self._get_we_id(employee.work_contact_id.email)
|
|
||||||
|
|
||||||
def _get_we_id(self, work_email):
|
|
||||||
json1 = {
|
|
||||||
'params': {
|
|
||||||
'work_email': work_email
|
|
||||||
}
|
|
||||||
}
|
|
||||||
url = '/api/get/we_id/info'
|
|
||||||
config = self.env['res.config.settings'].get_values()
|
|
||||||
ret = requests.post((config['ims_url'] + url), json=json1, data={})
|
|
||||||
result = ret.json()['result']
|
|
||||||
if result['code'] == 200:
|
|
||||||
if result['we_id']:
|
|
||||||
return result['we_id']
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _employee_info_sync(self):
|
def _employee_info_sync(self):
|
||||||
url = '/api/get/organization'
|
url = '/api/get/organization'
|
||||||
config = self.env['res.config.settings'].get_values()
|
config = self.env['res.config.settings'].get_values()
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import random
|
|
||||||
from odoo import models, fields, api
|
|
||||||
from odoo.http import request
|
|
||||||
from odoo.exceptions import AccessDenied
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class ResUsers(models.Model):
|
|
||||||
_inherit = 'res.users'
|
|
||||||
|
|
||||||
we_employee_id = fields.Char(string=u'企业微信账号', related='employee_id.we_id', default="")
|
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
'category': 'sf',
|
'category': 'sf',
|
||||||
'author': 'jikimo',
|
'author': 'jikimo',
|
||||||
'website': 'https://sf.cs.jikimo.com',
|
'website': 'https://sf.cs.jikimo.com',
|
||||||
'depends': ['web', 'sf_manufacturing', 'barcodes'],
|
'depends': ['web', 'mail', 'sf_base', 'sf_manufacturing', 'barcodes', ],
|
||||||
'data': [
|
'data': [
|
||||||
# 定义权限组放在最上面
|
# 定义权限组放在最上面
|
||||||
# 权限组
|
# 权限组
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
# 只有它被屏蔽了
|
# 只有它被屏蔽了
|
||||||
# 'views/SfWorkOrderBarcodes.xml',
|
# 'views/SfWorkOrderBarcodes.xml',
|
||||||
'views/WorkCenterBarcodes.xml',
|
'views/WorkCenterBarcodes.xml',
|
||||||
# 'views/Stock_picking_Barcodes.xml',
|
'views/Stock_picking_Barcodes.xml',
|
||||||
'views/machine_monitor.xml',
|
'views/machine_monitor.xml',
|
||||||
'views/machine_info_present.xml',
|
'views/machine_info_present.xml',
|
||||||
'views/delivery_record.xml',
|
'views/delivery_record.xml',
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ db_config = {
|
|||||||
"user": "postgres",
|
"user": "postgres",
|
||||||
"password": "postgres",
|
"password": "postgres",
|
||||||
"port": "5432",
|
"port": "5432",
|
||||||
"host": "172.16.10.131"
|
"host": "172.16.10.113"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -24,8 +24,6 @@ def convert_to_seconds(time_str):
|
|||||||
|
|
||||||
if time_str is None:
|
if time_str is None:
|
||||||
return 0
|
return 0
|
||||||
if time_str == 0:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
||||||
|
|
||||||
@@ -402,45 +400,20 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
try:
|
try:
|
||||||
plan_obj = request.env['sf.production.plan'].sudo()
|
plan_obj = request.env['sf.production.plan'].sudo()
|
||||||
production_obj = request.env['mrp.production'].sudo()
|
production_obj = request.env['mrp.production'].sudo()
|
||||||
work_order_obj = request.env['mrp.workorder'].sudo()
|
|
||||||
line_list = ast.literal_eval(kw['line_list'])
|
line_list = ast.literal_eval(kw['line_list'])
|
||||||
# print('line_list: %s' % line_list)
|
# print('line_list: %s' % line_list)
|
||||||
for line in line_list:
|
for line in line_list:
|
||||||
|
|
||||||
|
# 工单计划量
|
||||||
|
plan_data_total_counts = production_obj.search_count(
|
||||||
|
[('production_line_id.name', '=', line), ('state', 'not in', ['cancel']),
|
||||||
|
('active', '=', True)])
|
||||||
|
# 工单完成量
|
||||||
|
plan_data_finish_counts = plan_obj.search_count(
|
||||||
|
[('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
|
||||||
# # 工单计划量
|
# # 工单计划量
|
||||||
# plan_data_total_counts = production_obj.search_count(
|
# plan_data_plan_counts = plan_obj.search_count(
|
||||||
# [('production_line_id.name', '=', line), ('state', 'not in', ['cancel']),
|
# [('production_line_id.name', '=', line), ('state', 'not in', ['finished'])])
|
||||||
# ('active', '=', True)])
|
|
||||||
|
|
||||||
# 工单计划量切换为CNC工单
|
|
||||||
plan_data_total_counts = work_order_obj.search_count(
|
|
||||||
[('production_id.production_line_id.name', '=', line),
|
|
||||||
('state', 'in', ['ready', 'progress', 'done']), ('routing_type', '=', 'CNC加工')])
|
|
||||||
|
|
||||||
# # 工单完成量
|
|
||||||
# plan_data_finish_counts = plan_obj.search_count(
|
|
||||||
# [('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
|
|
||||||
|
|
||||||
# 工单完成量切换为CNC工单
|
|
||||||
plan_data_finish_counts = work_order_obj.search_count(
|
|
||||||
[('production_id.production_line_id.name', '=', line),
|
|
||||||
('state', 'in', ['done']), ('routing_type', '=', 'CNC加工')])
|
|
||||||
|
|
||||||
# 超期完成量
|
|
||||||
# 搜索所有已经完成的工单
|
|
||||||
plan_data_overtime = work_order_obj.search([
|
|
||||||
('production_id.production_line_id.name', '=', line),
|
|
||||||
('state', 'in', ['done']),
|
|
||||||
('routing_type', '=', 'CNC加工')
|
|
||||||
])
|
|
||||||
|
|
||||||
# 使用 filtered 进行字段比较
|
|
||||||
plan_data_overtime_counts = plan_data_overtime.filtered(
|
|
||||||
lambda order: order.date_finished > order.date_planned_finished
|
|
||||||
)
|
|
||||||
|
|
||||||
# 获取数量
|
|
||||||
plan_data_overtime_counts = len(plan_data_overtime_counts)
|
|
||||||
|
|
||||||
# 查找符合条件的生产计划记录
|
# 查找符合条件的生产计划记录
|
||||||
plan_data = plan_obj.search([
|
plan_data = plan_obj.search([
|
||||||
@@ -542,10 +515,7 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
'on_time_rate': on_time_rate,
|
'on_time_rate': on_time_rate,
|
||||||
# 'detection_data': detection_data,
|
# 'detection_data': detection_data,
|
||||||
'detection_data': plan_data_finish_counts,
|
'detection_data': plan_data_finish_counts,
|
||||||
'pass_rate': (plan_data_finish_counts - plan_data_fault_counts) / plan_data_finish_counts,
|
'pass_rate': (plan_data_finish_counts - plan_data_fault_counts) / plan_data_finish_counts
|
||||||
'plan_data_overtime_counts': plan_data_overtime_counts,
|
|
||||||
'overtime_rate': plan_data_overtime_counts / plan_data_finish_counts
|
|
||||||
if plan_data_finish_counts > 0 else 0,
|
|
||||||
}
|
}
|
||||||
res['data'][line] = data
|
res['data'][line] = data
|
||||||
|
|
||||||
@@ -1264,7 +1234,6 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
"""
|
"""
|
||||||
获取
|
获取
|
||||||
"""
|
"""
|
||||||
logging.info("kw=============:%s" % kw)
|
|
||||||
res = {'status': 1, 'message': '成功', 'data': {}}
|
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||||
# 连接数据库
|
# 连接数据库
|
||||||
conn = psycopg2.connect(**db_config)
|
conn = psycopg2.connect(**db_config)
|
||||||
@@ -1273,7 +1242,6 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
time_threshold = datetime.now() - timedelta(days=1)
|
time_threshold = datetime.now() - timedelta(days=1)
|
||||||
|
|
||||||
alarm_last_24_time = 0.0
|
alarm_last_24_time = 0.0
|
||||||
alarm_all_time = 0.0
|
|
||||||
|
|
||||||
def fetch_result_as_dict(cursor):
|
def fetch_result_as_dict(cursor):
|
||||||
"""辅助函数:将查询结果转为字典"""
|
"""辅助函数:将查询结果转为字典"""
|
||||||
@@ -1334,49 +1302,17 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
alarm_last_24_time += float(result[0])
|
alarm_last_24_time += float(result[0])
|
||||||
else:
|
else:
|
||||||
alarm_last_24_time += 0.0
|
alarm_last_24_time += 0.0
|
||||||
|
|
||||||
alarm_all_nums = []
|
|
||||||
with conn.cursor() as cur:
|
|
||||||
cur.execute("""
|
|
||||||
SELECT DISTINCT ON (alarm_start_time) alarm_time, alarm_start_time
|
|
||||||
FROM device_data
|
|
||||||
WHERE device_name = %s
|
|
||||||
AND alarm_start_time IS NOT NULL;
|
|
||||||
""", (item,))
|
|
||||||
results = cur.fetchall()
|
|
||||||
for result in results:
|
|
||||||
alarm_all_nums.append(result[1])
|
|
||||||
if result[0]:
|
|
||||||
if float(result[0]) >= 1000:
|
|
||||||
continue
|
|
||||||
alarm_all_time += float(result[0])
|
|
||||||
else:
|
|
||||||
alarm_all_time += 0.0
|
|
||||||
|
|
||||||
# with conn.cursor() as cur:
|
|
||||||
# cur.execute("""
|
|
||||||
# SELECT * FROM device_data
|
|
||||||
# WHERE device_name = %s
|
|
||||||
# AND total_count IS NOT NULL
|
|
||||||
# ORDER BY time ASC
|
|
||||||
# LIMIT 1;
|
|
||||||
# """, (item, ))
|
|
||||||
# total_count = fetch_result_as_dict(cur)
|
|
||||||
|
|
||||||
# 返回数据
|
# 返回数据
|
||||||
res['data'][item] = {
|
res['data'][item] = {
|
||||||
'wait_time': last_all_time['run_time'] if last_all_time['run_time'] is not None else 0,
|
'wait_time': last_all_time['run_time'] if last_all_time['run_time'] is not None else 0,
|
||||||
'cut_time': last_all_time['process_time'] if last_all_time['process_time'] is not None else 0,
|
'cut_time': last_all_time['process_time'] if last_all_time['process_time'] is not None else 0,
|
||||||
'cut_24_time': last_24_time['process_time'] if last_24_time else 0,
|
'cut_24_time': last_24_time['process_time'] if last_24_time['process_time'] is not None else 0,
|
||||||
'power_on_time': last_all_time['power_on_time'] if last_all_time['power_on_time'] is not None else 0,
|
'power_on_time': last_all_time['power_on_time'] if last_all_time['power_on_time'] is not None else 0,
|
||||||
'power_on_24_time': last_24_time['power_on_time'] if last_24_time else 0,
|
'power_on_24_time': last_24_time['power_on_time'] if last_24_time['power_on_time'] is not None else 0,
|
||||||
'alarm_last_24_time': alarm_last_24_time,
|
'alarm_last_24_time': alarm_last_24_time,
|
||||||
'alarm_last_24_nums': len(list(set(alarm_last_24_nums))),
|
'alarm_last_24_nums': len(list(set(alarm_last_24_nums))),
|
||||||
'idle_count': idle_count,
|
'idle_count': idle_count,
|
||||||
'first_online_time': first_online_duration,
|
'first_online_time': first_online_duration,
|
||||||
'alarm_all_time': alarm_all_time,
|
|
||||||
'alarm_all_nums': len(list(set(alarm_all_nums)))
|
|
||||||
# 'total_count': total_count['total_count'] if total_count else 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ class Machine_ftp(models.Model):
|
|||||||
status = fields.Boolean('机床在线状态', readonly=True)
|
status = fields.Boolean('机床在线状态', readonly=True)
|
||||||
# run_status = fields.Selection([('0', '空闲中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')], string='机床运行状态',
|
# run_status = fields.Selection([('0', '空闲中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')], string='机床运行状态',
|
||||||
# readonly=True, default='0')
|
# readonly=True, default='0')
|
||||||
# run_status = fields.Char('机床运行状态', readonly=True)
|
run_status = fields.Char('机床运行状态', readonly=True)
|
||||||
run_time = fields.Char('机床累计运行时长', readonly=True)
|
run_time = fields.Char('机床累计运行时长', readonly=True)
|
||||||
# 机床系统日期
|
# 机床系统日期
|
||||||
system_date = fields.Char('机床系统日期', readonly=True)
|
system_date = fields.Char('机床系统日期', readonly=True)
|
||||||
|
|||||||
@@ -10,9 +10,6 @@
|
|||||||
<searchpanel>
|
<searchpanel>
|
||||||
<field name="routing_type" select="multi" string="工序类型" icon="fa-building" enable_counters="1"/>
|
<field name="routing_type" select="multi" string="工序类型" icon="fa-building" enable_counters="1"/>
|
||||||
<field name="state" select="multi" string="状态" icon="fa-building" enable_counters="1"/>
|
<field name="state" select="multi" string="状态" icon="fa-building" enable_counters="1"/>
|
||||||
|
|
||||||
<field name="construction_period_status" select="multi" icon="fa-building" enable_counters="1"/>
|
|
||||||
<field name="tag_type" select="multi" icon="fa-building" enable_counters="1"/>
|
|
||||||
<!-- <field name="manual_quotation" select="multi" string="" icon="fa-building" enable_counters="1"/>-->
|
<!-- <field name="manual_quotation" select="multi" string="" icon="fa-building" enable_counters="1"/>-->
|
||||||
</searchpanel>
|
</searchpanel>
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,3 @@
|
|||||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
from . import wizard
|
|
||||||
|
|||||||
@@ -11,14 +11,12 @@
|
|||||||
'security/group_security.xml',
|
'security/group_security.xml',
|
||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
'security/ir_rule_data.xml',
|
'security/ir_rule_data.xml',
|
||||||
'data/scheduled_actions.xml',
|
|
||||||
'views/maintenance_logs_views.xml',
|
'views/maintenance_logs_views.xml',
|
||||||
'views/maintenance_equipment_oee_views.xml',
|
'views/maintenance_equipment_oee_views.xml',
|
||||||
'views/maintenance_views.xml',
|
'views/maintenance_views.xml',
|
||||||
'views/equipment_maintenance_standards_views.xml',
|
'views/equipment_maintenance_standards_views.xml',
|
||||||
'views/maintenance_request_views.xml',
|
'views/maintenance_request_views.xml',
|
||||||
'views/maintenance_equipment_category_views.xml',
|
'views/maintenance_equipment_category_views.xml',
|
||||||
'wizard/maintenance_request_wizard.xml',
|
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
'application': False,
|
'application': False,
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
<odoo>
|
|
||||||
<data noupdate="1">
|
|
||||||
<record id="ir_cron_oee_get_running_datas" model="ir.cron">
|
|
||||||
<field name="name">设备运行数据</field>
|
|
||||||
<field name="model_id" ref="model_maintenance_equipment_oee"/>
|
|
||||||
<field name="state">code</field>
|
|
||||||
<field name="code">model.get_running_datas()</field>
|
|
||||||
<field name="interval_number">15</field>
|
|
||||||
<field name="interval_type">minutes</field>
|
|
||||||
<field name="numbercall">-1</field>
|
|
||||||
<field name="active" eval="True"/>
|
|
||||||
</record>
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import json
|
import json
|
||||||
import base64
|
import base64
|
||||||
import logging
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import requests
|
import requests
|
||||||
from odoo.addons.sf_base.commons.common import Common
|
from odoo.addons.sf_base.commons.common import Common
|
||||||
from odoo import api, fields, models, _
|
from odoo import api, fields, models, _
|
||||||
from odoo.exceptions import UserError, ValidationError
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
|
||||||
class SfMaintenanceEquipmentCategory(models.Model):
|
class SfMaintenanceEquipmentCategory(models.Model):
|
||||||
@@ -39,8 +38,6 @@ class SfMaintenanceEquipment(models.Model):
|
|||||||
|
|
||||||
crea_url = "/api/machine_tool/create"
|
crea_url = "/api/machine_tool/create"
|
||||||
|
|
||||||
run_status = fields.Char('机床运行状态', readonly=True)
|
|
||||||
|
|
||||||
# AGV运行日志
|
# AGV运行日志
|
||||||
agv_logs = fields.One2many('maintenance.equipment.agv.log', 'equipment_id', string='AGV运行日志')
|
agv_logs = fields.One2many('maintenance.equipment.agv.log', 'equipment_id', string='AGV运行日志')
|
||||||
# 1212修改后的字段
|
# 1212修改后的字段
|
||||||
@@ -123,13 +120,6 @@ class SfMaintenanceEquipment(models.Model):
|
|||||||
'sf_maintenance_equipment_ids', string='设备维保标准')
|
'sf_maintenance_equipment_ids', string='设备维保标准')
|
||||||
eq_maintenance_id = fields.Many2one('equipment.maintenance.standards', string='设备保养标准',
|
eq_maintenance_id = fields.Many2one('equipment.maintenance.standards', string='设备保养标准',
|
||||||
domain="[('maintenance_type','=','保养')]")
|
domain="[('maintenance_type','=','保养')]")
|
||||||
|
|
||||||
initial_action_date = fields.Date(string='重置保养日期')
|
|
||||||
initial_action_date_old = fields.Date(string='重置保养日期(旧)')
|
|
||||||
next_action_date = fields.Date(string='下次预防保养')
|
|
||||||
|
|
||||||
initial_overhaul_date = fields.Date(string='重置维修日期')
|
|
||||||
initial_overhaul_date_old = fields.Date(string='重置维修日期(旧)')
|
|
||||||
overhaul_date = fields.Date(string='下次预防检修')
|
overhaul_date = fields.Date(string='下次预防检修')
|
||||||
overhaul_period = fields.Integer(string='预防检修频次')
|
overhaul_period = fields.Integer(string='预防检修频次')
|
||||||
overhaul_duration = fields.Float(string='检修时长')
|
overhaul_duration = fields.Float(string='检修时长')
|
||||||
@@ -137,61 +127,6 @@ class SfMaintenanceEquipment(models.Model):
|
|||||||
overhaul_id = fields.Many2one('equipment.maintenance.standards', string='设备检修标准',
|
overhaul_id = fields.Many2one('equipment.maintenance.standards', string='设备检修标准',
|
||||||
domain="[('maintenance_type','=','检修')]")
|
domain="[('maintenance_type','=','检修')]")
|
||||||
|
|
||||||
def confirm_maintenance(self):
|
|
||||||
"""
|
|
||||||
确认保养/检修
|
|
||||||
"""
|
|
||||||
context = self.env.context
|
|
||||||
if context['type'] == '保养':
|
|
||||||
if not self.initial_action_date:
|
|
||||||
raise ValidationError('重置保养日期不能为空!!')
|
|
||||||
elif self.initial_action_date < fields.Date.today():
|
|
||||||
raise ValidationError('重置保养日期不能小于当前日期!!')
|
|
||||||
elif context['type'] == '检修':
|
|
||||||
if not self.initial_overhaul_date:
|
|
||||||
raise ValidationError('重置检修日期不能为空!!')
|
|
||||||
elif self.initial_overhaul_date < fields.Date.today():
|
|
||||||
raise ValidationError('重置检修日期不能小于当前日期!!')
|
|
||||||
|
|
||||||
request_ids = self.env['maintenance.request'].search([('stage_id.done', '=', False),
|
|
||||||
('equipment_id', '=', self.id),
|
|
||||||
('maintenance_type', '=', 'preventive'),
|
|
||||||
('sf_maintenance_type', '=', context['type'])])
|
|
||||||
|
|
||||||
if not request_ids:
|
|
||||||
return self.create_maintenance_request(context['type'])
|
|
||||||
else:
|
|
||||||
return {
|
|
||||||
"type": "ir.actions.act_window",
|
|
||||||
"res_model": "maintenance.request.wizard",
|
|
||||||
"views": [[False, "form"]],
|
|
||||||
"target": "new",
|
|
||||||
'context': {
|
|
||||||
'equipment_id': self.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def create_maintenance_request(self, maintenance_request_type):
|
|
||||||
"""
|
|
||||||
根据条件创建维保计划
|
|
||||||
"""
|
|
||||||
if maintenance_request_type == '保养':
|
|
||||||
self._create_new_request(self.initial_action_date + timedelta(days=self.period))
|
|
||||||
self.initial_action_date_old = self.initial_action_date
|
|
||||||
elif maintenance_request_type == '检修':
|
|
||||||
self._create_new_request1(self.initial_overhaul_date + timedelta(days=self.overhaul_period))
|
|
||||||
self.initial_overhaul_date_old = self.initial_overhaul_date
|
|
||||||
return {
|
|
||||||
'type': 'ir.actions.client',
|
|
||||||
'tag': 'display_notification',
|
|
||||||
'params': {
|
|
||||||
'title': f'创建{maintenance_request_type}计划',
|
|
||||||
'message': f'新{maintenance_request_type}维保计划创建成功',
|
|
||||||
'type': 'success',
|
|
||||||
'next': {'type': 'ir.actions.act_window_close'},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@api.onchange('eq_maintenance_id', 'overhaul_id')
|
@api.onchange('eq_maintenance_id', 'overhaul_id')
|
||||||
def _compute_equipment_maintenance_standards_ids(self):
|
def _compute_equipment_maintenance_standards_ids(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
@@ -654,13 +589,11 @@ class SfMaintenanceEquipment(models.Model):
|
|||||||
('equipment_id', '=', equipment.id),
|
('equipment_id', '=', equipment.id),
|
||||||
('sf_maintenance_type', '=', '保养'),
|
('sf_maintenance_type', '=', '保养'),
|
||||||
('stage_id.done', '!=', True),
|
('stage_id.done', '!=', True),
|
||||||
('active', '!=', False),
|
|
||||||
('close_date', '=', False)], order="request_date asc", limit=1)
|
('close_date', '=', False)], order="request_date asc", limit=1)
|
||||||
last_maintenance_done = self.env['maintenance.request'].search([
|
last_maintenance_done = self.env['maintenance.request'].search([
|
||||||
('equipment_id', '=', equipment.id),
|
('equipment_id', '=', equipment.id),
|
||||||
('sf_maintenance_type', '=', '保养'),
|
('sf_maintenance_type', '=', '保养'),
|
||||||
('stage_id.done', '=', True),
|
('stage_id.done', '=', True),
|
||||||
('active', '!=', False),
|
|
||||||
('close_date', '!=', False)], order="close_date desc", limit=1)
|
('close_date', '!=', False)], order="close_date desc", limit=1)
|
||||||
if next_maintenance_todo and last_maintenance_done:
|
if next_maintenance_todo and last_maintenance_done:
|
||||||
next_date = next_maintenance_todo.request_date
|
next_date = next_maintenance_todo.request_date
|
||||||
@@ -689,9 +622,7 @@ class SfMaintenanceEquipment(models.Model):
|
|||||||
if next_date < date_now:
|
if next_date < date_now:
|
||||||
next_date = date_now
|
next_date = date_now
|
||||||
else:
|
else:
|
||||||
if not equipment.initial_action_date:
|
next_date = equipment.effective_date + timedelta(days=equipment.period)
|
||||||
raise ValidationError('重置保养日期不能为空!!!')
|
|
||||||
next_date = equipment.initial_action_date + timedelta(days=equipment.period)
|
|
||||||
equipment.next_action_date = next_date
|
equipment.next_action_date = next_date
|
||||||
else:
|
else:
|
||||||
self.next_action_date = False
|
self.next_action_date = False
|
||||||
@@ -702,13 +633,11 @@ class SfMaintenanceEquipment(models.Model):
|
|||||||
('equipment_id', '=', equipment.id),
|
('equipment_id', '=', equipment.id),
|
||||||
('sf_maintenance_type', '=', '检修'),
|
('sf_maintenance_type', '=', '检修'),
|
||||||
('stage_id.done', '!=', True),
|
('stage_id.done', '!=', True),
|
||||||
('active', '!=', False),
|
|
||||||
('close_date', '=', False)], order="request_date asc", limit=1)
|
('close_date', '=', False)], order="request_date asc", limit=1)
|
||||||
last_maintenance_done = self.env['maintenance.request'].search([
|
last_maintenance_done = self.env['maintenance.request'].search([
|
||||||
('equipment_id', '=', equipment.id),
|
('equipment_id', '=', equipment.id),
|
||||||
('sf_maintenance_type', '=', '检修'),
|
('sf_maintenance_type', '=', '检修'),
|
||||||
('stage_id.done', '=', True),
|
('stage_id.done', '=', True),
|
||||||
('active', '!=', False),
|
|
||||||
('close_date', '!=', False)], order="close_date desc", limit=1)
|
('close_date', '!=', False)], order="close_date desc", limit=1)
|
||||||
if next_maintenance_todo and last_maintenance_done:
|
if next_maintenance_todo and last_maintenance_done:
|
||||||
next_date = next_maintenance_todo.request_date
|
next_date = next_maintenance_todo.request_date
|
||||||
@@ -737,9 +666,7 @@ class SfMaintenanceEquipment(models.Model):
|
|||||||
if next_date < date_now:
|
if next_date < date_now:
|
||||||
next_date = date_now
|
next_date = date_now
|
||||||
else:
|
else:
|
||||||
if not equipment.initial_overhaul_date:
|
next_date = equipment.effective_date + timedelta(days=equipment.overhaul_period)
|
||||||
raise ValidationError('重置维修日期不能为空')
|
|
||||||
next_date = equipment.initial_overhaul_date + timedelta(days=equipment.overhaul_period)
|
|
||||||
equipment.overhaul_date = next_date
|
equipment.overhaul_date = next_date
|
||||||
else:
|
else:
|
||||||
self.overhaul_date = False
|
self.overhaul_date = False
|
||||||
@@ -806,7 +733,6 @@ class SfMaintenanceEquipment(models.Model):
|
|||||||
next_requests = self.env['maintenance.request'].search([('stage_id.done', '=', False),
|
next_requests = self.env['maintenance.request'].search([('stage_id.done', '=', False),
|
||||||
('equipment_id', '=', equipment.id),
|
('equipment_id', '=', equipment.id),
|
||||||
('maintenance_type', '=', 'preventive'),
|
('maintenance_type', '=', 'preventive'),
|
||||||
('active', '=', True),
|
|
||||||
('request_date', '=', equipment.next_action_date),
|
('request_date', '=', equipment.next_action_date),
|
||||||
('sf_maintenance_type', '=', '保养')])
|
('sf_maintenance_type', '=', '保养')])
|
||||||
if not next_requests:
|
if not next_requests:
|
||||||
@@ -815,7 +741,6 @@ class SfMaintenanceEquipment(models.Model):
|
|||||||
next_requests = self.env['maintenance.request'].search([('stage_id.done', '=', False),
|
next_requests = self.env['maintenance.request'].search([('stage_id.done', '=', False),
|
||||||
('equipment_id', '=', equipment.id),
|
('equipment_id', '=', equipment.id),
|
||||||
('maintenance_type', '=', 'preventive'),
|
('maintenance_type', '=', 'preventive'),
|
||||||
('active', '=', True),
|
|
||||||
('request_date', '=', equipment.overhaul_date),
|
('request_date', '=', equipment.overhaul_date),
|
||||||
('sf_maintenance_type', '=', '检修')])
|
('sf_maintenance_type', '=', '检修')])
|
||||||
if not next_requests:
|
if not next_requests:
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user