Compare commits
6 Commits
feature/优化
...
feature/优化
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85aa13ec0a | ||
|
|
63ea5d67e8 | ||
|
|
5e1f6a09d1 | ||
|
|
8937bfb5dd | ||
|
|
2dc12d6a8c | ||
|
|
94b718533f |
@@ -1,4 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import controllers
|
||||
from . import models
|
||||
@@ -1,35 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': "jikimo_account_process",
|
||||
|
||||
'summary': """
|
||||
处理会计凭证生成重复名称报错问题
|
||||
""",
|
||||
|
||||
'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': ['base', 'account', 'l10n_cn'],
|
||||
|
||||
# always loaded
|
||||
'data': [
|
||||
# 'security/ir.model.access.csv',
|
||||
# 'views/views.xml',
|
||||
# 'views/templates.xml',
|
||||
],
|
||||
# only loaded in demonstration mode
|
||||
'demo': [
|
||||
# 'demo/demo.xml',
|
||||
],
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import controllers
|
||||
@@ -1,21 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# from odoo import http
|
||||
|
||||
|
||||
# class JikimoAccountProcess(http.Controller):
|
||||
# @http.route('/jikimo_account_process/jikimo_account_process', auth='public')
|
||||
# def index(self, **kw):
|
||||
# return "Hello, world"
|
||||
|
||||
# @http.route('/jikimo_account_process/jikimo_account_process/objects', auth='public')
|
||||
# def list(self, **kw):
|
||||
# return http.request.render('jikimo_account_process.listing', {
|
||||
# 'root': '/jikimo_account_process/jikimo_account_process',
|
||||
# 'objects': http.request.env['jikimo_account_process.jikimo_account_process'].search([]),
|
||||
# })
|
||||
|
||||
# @http.route('/jikimo_account_process/jikimo_account_process/objects/<model("jikimo_account_process.jikimo_account_process"):obj>', auth='public')
|
||||
# def object(self, obj, **kw):
|
||||
# return http.request.render('jikimo_account_process.object', {
|
||||
# 'object': obj
|
||||
# })
|
||||
@@ -1,4 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import models
|
||||
from . import account_move
|
||||
@@ -1,23 +0,0 @@
|
||||
from odoo import models, fields, api, _
|
||||
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class CustomAccountMoveLine(models.Model):
|
||||
_inherit = 'account.move'
|
||||
_description = "account move line"
|
||||
|
||||
fapiao = fields.Char(string='发票号', size=20, copy=False, tracking=True, required=True)
|
||||
|
||||
@api.constrains('fapiao')
|
||||
def _check_fapiao(self):
|
||||
for record in self:
|
||||
if record.fapiao and (len(record.fapiao) != 20 or not record.fapiao.isdecimal()):
|
||||
raise ValidationError(_("Fapiao number is an 20-digit number. Please enter a correct one."))
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals):
|
||||
for val in vals:
|
||||
val['name'] = self.env['ir.sequence'].next_by_code('account.move') or '/'
|
||||
# 因为供应商与客户支付创建流程是先创建move line在修改来填充account_payment与move line的关联
|
||||
return super(CustomAccountMoveLine, self).create(vals)
|
||||
@@ -1,18 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# from odoo import models, fields, api
|
||||
|
||||
|
||||
# class jikimo_account_process(models.Model):
|
||||
# _name = 'jikimo_account_process.jikimo_account_process'
|
||||
# _description = 'jikimo_account_process.jikimo_account_process'
|
||||
|
||||
# name = fields.Char()
|
||||
# value = fields.Integer()
|
||||
# value2 = fields.Float(compute="_value_pc", store=True)
|
||||
# description = fields.Text()
|
||||
#
|
||||
# @api.depends('value')
|
||||
# def _value_pc(self):
|
||||
# for record in self:
|
||||
# record.value2 = float(record.value) / 100
|
||||
@@ -21,8 +21,8 @@
|
||||
'web.assets_qweb': [
|
||||
],
|
||||
'web.assets_backend': [
|
||||
# 'jikimo_frontend/static/src/fields/custom_many2many_checkboxes/*',
|
||||
# 'jikimo_frontend/static/src/fields/Many2OneRadioField/*',
|
||||
'jikimo_frontend/static/src/fields/custom_many2many_checkboxes/*',
|
||||
'jikimo_frontend/static/src/fields/Many2OneRadioField/*',
|
||||
# 移除odoo相关标识
|
||||
'jikimo_frontend/static/src/bye_odoo/*',
|
||||
'jikimo_frontend/static/src/scss/custom_style.scss',
|
||||
|
||||
@@ -6,6 +6,6 @@ import { patch } from "web.utils";
|
||||
patch(WebClient.prototype, "kolpolok_custom_title_and_favicon.WebClient", {
|
||||
setup() {
|
||||
this._super();
|
||||
// this.title.setParts({ zopenerp: "JIKIMO" });
|
||||
this.title.setParts({ zopenerp: "JIKIMO" });
|
||||
},
|
||||
});
|
||||
@@ -1,8 +1,3 @@
|
||||
.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){
|
||||
border:1px solid #dee2e6 !important;
|
||||
}
|
||||
|
||||
.custom_required_add::before{
|
||||
content: '*';
|
||||
color: red;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.many2one_radio_field {
|
||||
display: inline-block;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { RadioField } from "@web/views/fields/radio/radio_field"; // 导入单选按钮组件
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
export class Many2OneRadioField extends RadioField {
|
||||
// 你可以重写或者添加一些方法和属性
|
||||
// 例如,你可以重写setup方法来添加一些事件监听器或者初始化一些变量
|
||||
setup() {
|
||||
super.setup(); // 调用父类的setup方法
|
||||
// 你自己的代码
|
||||
}
|
||||
|
||||
onImageClick(event) {
|
||||
// 放大图片逻辑
|
||||
// 获取图片元素
|
||||
const img = event.target;
|
||||
const close = img.nextSibling;
|
||||
// 实现放大图片逻辑
|
||||
// 比如使用 CSS 放大
|
||||
img.parentElement.classList.add('zoomed');
|
||||
close.classList.add('img_close');
|
||||
}
|
||||
|
||||
onCloseClick(event) {
|
||||
const close = event.target;
|
||||
const img = close.previousSibling;
|
||||
img.parentElement.classList.remove('zoomed');
|
||||
close.classList.remove('img_close');
|
||||
}
|
||||
|
||||
get items() {
|
||||
return Many2OneRadioField.getItems(this.props.name, this.props.record);
|
||||
}
|
||||
|
||||
static getItems(fieldName, record) {
|
||||
switch (record.fields[fieldName].type) {
|
||||
case "selection":
|
||||
return record.fields[fieldName].selection;
|
||||
case "many2one": {
|
||||
const value = record.preloadedData[fieldName] || [];
|
||||
return value.map((item) => [item.id, item.display_name, item.image]);
|
||||
}
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Many2OneRadioField.template = "jikimo_frontend.Many2OneRadioField";
|
||||
// MyCustomWidget.supportedTypes = ['many2many'];
|
||||
|
||||
registry.category("fields").add("many2one_radio", Many2OneRadioField);
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="jikimo_frontend.Many2OneRadioField" owl="1">
|
||||
<div
|
||||
role="radiogroup"
|
||||
t-attf-class="o_{{ props.orientation }}"
|
||||
t-att-aria-label="string"
|
||||
>
|
||||
<t t-foreach="items" t-as="item" t-key="item[0]">
|
||||
<div class="form-check o_radio_item many2one_radio_field" aria-atomic="true">
|
||||
<input
|
||||
type="radio"
|
||||
class="form-check-input o_radio_input"
|
||||
t-att-checked="item[0] === value"
|
||||
t-att-disabled="props.readonly"
|
||||
t-att-name="id"
|
||||
t-att-data-value="item[0]"
|
||||
t-att-data-index="item_index"
|
||||
t-att-id="`${id}_${item[0]}`"
|
||||
t-on-change="() => this.onChange(item)"
|
||||
/>
|
||||
<label class="form-check-label o_form_label" t-att-for="`${id}_${item[0]}`" t-esc="item[1]" />
|
||||
<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>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -0,0 +1,41 @@
|
||||
.zoomed {
|
||||
position: fixed !important;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) scale(10);
|
||||
}
|
||||
|
||||
.many2many_flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.many2many_flex>div {
|
||||
margin-right: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.many2many_flex>div>:nth-child(2) {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.close {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
top: -8.8px;
|
||||
right: -8.8px;
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
opacity: 0;
|
||||
text-align: center;
|
||||
line-height: 20px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.img_close {
|
||||
opacity: 1;
|
||||
transform: scale(0.1);
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {Many2ManyCheckboxesField} from "@web/views/fields/many2many_checkboxes/many2many_checkboxes_field";
|
||||
import {registry} from "@web/core/registry";
|
||||
|
||||
export class MyCustomWidget extends Many2ManyCheckboxesField {
|
||||
// 你可以重写或者添加一些方法和属性
|
||||
// 例如,你可以重写setup方法来添加一些事件监听器或者初始化一些变量
|
||||
setup() {
|
||||
super.setup(); // 调用父类的setup方法
|
||||
// 你自己的代码
|
||||
}
|
||||
|
||||
onImageClick(event) {
|
||||
// 放大图片逻辑
|
||||
// 获取图片元素
|
||||
const img = event.target;
|
||||
const close = img.nextSibling;
|
||||
|
||||
// 实现放大图片逻辑
|
||||
// 比如使用 CSS 放大
|
||||
img.parentElement.classList.add('zoomed');
|
||||
close.classList.add('img_close');
|
||||
}
|
||||
|
||||
onCloseClick(event) {
|
||||
const close = event.target;
|
||||
const img = close.previousSibling;
|
||||
img.parentElement.classList.remove('zoomed');
|
||||
close.classList.remove('img_close');
|
||||
}
|
||||
}
|
||||
|
||||
MyCustomWidget.template = "jikimo_frontend.MyCustomWidget";
|
||||
// MyCustomWidget.supportedTypes = ['many2many'];
|
||||
|
||||
registry.category("fields").add("custom_many2many_checkboxes", MyCustomWidget);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="jikimo_frontend.MyCustomWidget" owl="1">
|
||||
<div aria-atomic="true" class="many2many_flex">
|
||||
<t t-foreach="items" t-as="item" t-key="item[0]">
|
||||
<div>
|
||||
<CheckBox
|
||||
value="isSelected(item)"
|
||||
disabled="props.readonly"
|
||||
onChange="(ev) => this.onChange(item[0], ev)"
|
||||
>
|
||||
<t t-esc="item[1]"/>
|
||||
|
||||
</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>
|
||||
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -5,10 +5,9 @@ import {patch} from '@web/core/utils/patch';
|
||||
import {_t} from "@web/core/l10n/translation";
|
||||
import {FormStatusIndicator} from "@web/views/form/form_status_indicator/form_status_indicator";
|
||||
import {ListRenderer} from "@web/views/list/list_renderer";
|
||||
// import {StatusBarField} from "@web/views/fields/statusbar/statusbar_field";
|
||||
import {FormLabel} from "@web/views/form/form_label";
|
||||
import { fieldVisualFeedback } from "@web/views/fields/field";
|
||||
import {StatusBarField} from "@web/views/fields/statusbar/statusbar_field";
|
||||
|
||||
import {Field} from "@web/views/fields/field";
|
||||
|
||||
var Dialog = require('web.Dialog');
|
||||
// var {patch} = require("web.utils") 这句话也行
|
||||
@@ -52,24 +51,8 @@ const tableRequiredList = [
|
||||
'product_template_id', 'product_uom_qty', 'price_unit','product_id','product_qty',
|
||||
'name', 'fault_type', 'maintenance_standards', 'Period'
|
||||
]
|
||||
patch(FormStatusIndicator.prototype, 'jikimo_frontend.FormStatusIndicator', {
|
||||
setup() {
|
||||
owl.onMounted(() => {
|
||||
try {
|
||||
const dom = this.__owl__.bdom.el
|
||||
const buttonsDom = $(dom).find('.o_form_status_indicator_buttons ')
|
||||
if (buttonsDom) {
|
||||
const dom1 = buttonsDom.children('.o_form_button_save')
|
||||
const dom2 = buttonsDom.children('.o_form_button_cancel')
|
||||
dom1.append('保存')
|
||||
dom2.append('不保存')
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
patch(FormStatusIndicator.prototype, 'jikimo_frontend.FormStatusIndicator', {
|
||||
// 你可以重写或者添加一些方法和属性
|
||||
async _onDiscardChanges() {
|
||||
// var self = this;
|
||||
@@ -107,16 +90,38 @@ patch(FormStatusIndicator.prototype, 'jikimo_frontend.FormStatusIndicator', {
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
patch(Field.prototype, 'jikimo_frontend.Field', {
|
||||
setup() {
|
||||
owl.onMounted(this.setRequired);
|
||||
return this._super(...arguments);
|
||||
},
|
||||
setRequired() {
|
||||
const id = this.props.id
|
||||
const isRequired = filedRequiredList[id]
|
||||
if(id == 'number_of_axles') {
|
||||
console.log(isRequired)
|
||||
}
|
||||
if(isRequired) {
|
||||
let dom;
|
||||
dom = $(`label[for=${id}]`)
|
||||
if(isRequired.multiple && dom.length > 1) {
|
||||
dom = dom.eq(-1)
|
||||
dom = dom.parent().parent().next().find('label')
|
||||
}
|
||||
if(isRequired.noLabel) {
|
||||
dom = dom.parent().parent()
|
||||
}
|
||||
let t = dom.html()
|
||||
t = '<i class="c* r" style="color: red;margin-left: -4px">*</i>' + t
|
||||
dom.html(t)
|
||||
}
|
||||
}
|
||||
})
|
||||
patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
|
||||
setup(){
|
||||
owl.onMounted(() => {
|
||||
this.activeElement = this.uiService.activeElement;
|
||||
this.setRequired()
|
||||
this.listherHeaderBodyNum()
|
||||
})
|
||||
owl.onPatched(() => {
|
||||
this.listherHeaderBodyNum()
|
||||
})
|
||||
return this._super(...arguments);
|
||||
},
|
||||
@@ -143,87 +148,52 @@ patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
|
||||
} catch (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)
|
||||
}
|
||||
}
|
||||
})
|
||||
patch(FormLabel.prototype, 'jikimo_frontend.FormLabel', {
|
||||
get className() {
|
||||
|
||||
const { invalid, empty, readonly } = fieldVisualFeedback(
|
||||
this.props.fieldInfo.FieldComponent,
|
||||
this.props.record,
|
||||
this.props.fieldName,
|
||||
this.props.fieldInfo
|
||||
);
|
||||
const classes = this.props.className ? [this.props.className] : [];
|
||||
const otherRequired = filedRequiredList[this.props.fieldName]
|
||||
|
||||
if(this.props.fieldInfo?.rawAttrs?.class?.indexOf('custom_required') >= 0 || otherRequired) {
|
||||
classes.push('custom_required_add')
|
||||
}
|
||||
if (invalid) {
|
||||
classes.push("o_field_invalid");
|
||||
}
|
||||
if (empty) {
|
||||
classes.push("o_form_label_empty");
|
||||
}
|
||||
if (readonly) {
|
||||
classes.push("o_form_label_readonly");
|
||||
}
|
||||
return classes.join(" ");
|
||||
}
|
||||
})
|
||||
|
||||
// 根据进度条设置水印
|
||||
// const statusbar_params = {
|
||||
// '已完工': 'bg-primary',
|
||||
// '完成': 'bg-primary',
|
||||
// '采购订单': 'bg-primary',
|
||||
// '作废': 'bg-danger',
|
||||
// '封存(报废)': 'bg-danger',
|
||||
// }
|
||||
// patch(StatusBarField.prototype, 'jikimo_frontend.StatusBarField', {
|
||||
// setup() {
|
||||
// owl.onMounted(this.ribbons);
|
||||
// return this._super(...arguments);
|
||||
// },
|
||||
// ribbons() {
|
||||
// try {
|
||||
// const dom = $('.o_form_sheet.position-relative')
|
||||
// const status = statusbar_params[this.currentName]
|
||||
// if(status && dom.length) {
|
||||
// dom.prepend(`<div class="o_widget o_widget_web_ribbon">
|
||||
// <div class="ribbon ribbon-top-right">
|
||||
// <span class="bg-opacity-75 ${status}" title="">${this.currentName}</span>
|
||||
// </div>
|
||||
// </div>`)
|
||||
// }
|
||||
// } catch (e) {
|
||||
// console.log(e)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
const statusbar_params = {
|
||||
'已完工': 'bg-primary',
|
||||
'完成': 'bg-primary',
|
||||
'采购订单': 'bg-primary',
|
||||
'作废': 'bg-danger',
|
||||
'封存(报废)': 'bg-danger',
|
||||
}
|
||||
patch(StatusBarField.prototype, 'jikimo_frontend.StatusBarField', {
|
||||
setup() {
|
||||
owl.onMounted(this.ribbons);
|
||||
return this._super(...arguments);
|
||||
},
|
||||
ribbons() {
|
||||
try {
|
||||
const dom = $('.o_form_sheet.position-relative')
|
||||
const status = statusbar_params[this.currentName]
|
||||
if(status && dom.length) {
|
||||
dom.prepend(`<div class="o_widget o_widget_web_ribbon">
|
||||
<div class="ribbon ribbon-top-right">
|
||||
<span class="bg-opacity-75 ${status}" title="">${this.currentName}</span>
|
||||
</div>
|
||||
</div>`)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
$(function () {
|
||||
document.addEventListener('click', function () {
|
||||
const dom = $('.o_form_status_indicator_buttons ')
|
||||
if (dom) {
|
||||
const dom1 = dom.children().eq(0)
|
||||
const dom2 = dom.children().eq(1)
|
||||
if (!dom1.text()) {
|
||||
dom1.append('保存')
|
||||
dom2.append('取消')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function customRequired() {
|
||||
let timer = null
|
||||
@@ -231,6 +201,7 @@ $(function () {
|
||||
clearInterval(timer)
|
||||
timer = setInterval(() => {
|
||||
timer_count++
|
||||
const dom = $('.custom_required')
|
||||
let tableDom = $('.table_custom_required')
|
||||
if (tableDom.length) {
|
||||
tableDom = tableDom.eq(0).parents('tr').children('.table_custom_required')
|
||||
@@ -242,6 +213,17 @@ $(function () {
|
||||
})
|
||||
clearInterval(timer)
|
||||
}
|
||||
if (dom.length) {
|
||||
dom.each(function () {
|
||||
const requiredDom = $(this).parent().prev().find('label')
|
||||
let t = requiredDom.html()
|
||||
if (t && t.indexOf('c*') < 0) {
|
||||
t = '<i class="c*" style="color: red;margin-left: -4px">*</i>' + t
|
||||
}
|
||||
requiredDom.html(t)
|
||||
})
|
||||
clearInterval(timer)
|
||||
}
|
||||
if (timer_count == 20) {
|
||||
clearInterval(timer)
|
||||
}
|
||||
|
||||
@@ -108,10 +108,6 @@ td.o_required_modifier {
|
||||
}
|
||||
|
||||
.color_3 {
|
||||
background-color: #808080;
|
||||
}
|
||||
|
||||
.color_4 {
|
||||
background-color: rgb(255, 150, 0);
|
||||
}
|
||||
|
||||
@@ -530,13 +526,4 @@ div:has(.o_required_modifier) > label::before {
|
||||
// 设置表单页面label文本不换行
|
||||
.o_form_view .o_group .o_wrap_label .o_form_label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
// 修复表格内容覆盖表头bug
|
||||
.o_list_renderer .o_list_table tbody th {
|
||||
position: unset;
|
||||
}
|
||||
|
||||
// 修复搜索面板checkbox样式
|
||||
.o_search_panel .form-check .form-check-label span {
|
||||
position: relative;
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
</t>
|
||||
|
||||
|
||||
|
||||
<!-- 暂存,同一份文件中有问题,拆分后正常工作 -->
|
||||
|
||||
<!-- <t t-name="og.web.ListRenderer.Rows" t-inherit="web.ListRenderer.Rows" t-inherit-mode="extension"> -->
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
<!-- 修改页面头部图标及文字 -->
|
||||
<template id="favicon_icon" inherit_id="web.layout" name="Web layout">
|
||||
<!-- change the title with reliance partner -->
|
||||
<!-- <xpath expr="//head//title" position="before">
|
||||
<xpath expr="//head//title" position="before">
|
||||
<title t-esc="'JIKIMO'"/>
|
||||
</xpath> -->
|
||||
</xpath>
|
||||
<!-- change the default favicon icon with -->
|
||||
<xpath expr="//head//link[@rel='shortcut icon']" position="replace">
|
||||
<link type="image/x-icon" rel="shortcut icon" href="/jikimo_frontend/static/src/img/jikimo-logo.ico"/>
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- hide 登录页面 powerd by odoo 及管理数据库 -->
|
||||
<template id="login_page_layout" inherit_id="web.login_layout" name="Login Page Layout">
|
||||
<!-- <xpath expr="//div[@class='card-body']/div[last()]" position="replace"></xpath> -->
|
||||
<xpath expr="//div[@class='card-body']//div[last()]" position="replace"></xpath>
|
||||
</template>
|
||||
|
||||
<!-- 隐藏odoo版本信息 -->
|
||||
|
||||
@@ -190,7 +190,7 @@ def _create(self, data_list):
|
||||
# 如果该用户组被限制创建或更新操作
|
||||
if rec['is_create_or_update']:
|
||||
raise UserError(
|
||||
_("您没有执行此操作的权限。请联系管理员"))
|
||||
_("You are restricted from performing this operation. Please contact the administrator."))
|
||||
else:
|
||||
# 如果 'access.right' 模型不存在,可以在这里定义备选逻辑
|
||||
# 例如,记录日志、发送通知或者简单地跳过这部分逻辑
|
||||
@@ -301,27 +301,53 @@ def unlink(self):
|
||||
# This is used to restrict the access right to unlink a record
|
||||
current_model_id = self.env['ir.model'].sudo().search(
|
||||
[('model', '=', self._name)]).id
|
||||
access_right_rec = self.env['access.right'].sudo().search_read(
|
||||
[('model_id', '=', current_model_id)], ['model_id', 'is_delete',
|
||||
'groups_id'])
|
||||
if access_right_rec and not self.env.is_admin():
|
||||
for rec in access_right_rec:
|
||||
group_name = self.env['ir.model.data'].sudo().search([
|
||||
('model', '=', 'res.groups'),
|
||||
('res_id', '=', rec['groups_id'][0])
|
||||
]).name
|
||||
module_name = self.env['ir.model.data'].sudo().search([
|
||||
('model', '=', 'res.groups'),
|
||||
('res_id', '=', rec['groups_id'][0])
|
||||
]).module
|
||||
group = module_name + "." + group_name
|
||||
if self.env.user.has_group(group):
|
||||
if rec['is_delete']:
|
||||
raise UserError(_('You are restricted from performing this'
|
||||
' operation. Please contact the'
|
||||
' administrator.'))
|
||||
# access_right_rec = self.env['access.right'].sudo().search_read(
|
||||
# [('model_id', '=', current_model_id)], ['model_id', 'is_delete',
|
||||
# 'groups_id'])
|
||||
# if access_right_rec and not self.env.is_admin():
|
||||
# for rec in access_right_rec:
|
||||
# group_name = self.env['ir.model.data'].sudo().search([
|
||||
# ('model', '=', 'res.groups'),
|
||||
# ('res_id', '=', rec['groups_id'][0])
|
||||
# ]).name
|
||||
# module_name = self.env['ir.model.data'].sudo().search([
|
||||
# ('model', '=', 'res.groups'),
|
||||
# ('res_id', '=', rec['groups_id'][0])
|
||||
# ]).module
|
||||
# group = module_name + "." + group_name
|
||||
# if self.env.user.has_group(group):
|
||||
# if rec['is_delete']:
|
||||
# raise UserError(_('You are restricted from performing this'
|
||||
# ' operation. Please contact the'
|
||||
# ' administrator.'))
|
||||
# 检查 'access.right' 模型是否存在于环境中
|
||||
if 'access.right' in self.env:
|
||||
# current_model_id = self.env['ir.model'].sudo().search([('model', '=', self._name)]).id
|
||||
access_right_rec = self.env['access.right'].sudo().search_read(
|
||||
[('model_id', '=', current_model_id)], ['model_id', 'is_delete', 'groups_id']
|
||||
)
|
||||
|
||||
if access_right_rec and not self.env.is_admin():
|
||||
for rec in access_right_rec:
|
||||
group_data = self.env['ir.model.data'].sudo().search_read(
|
||||
[('model', '=', 'res.groups'), ('res_id', '=', rec['groups_id'][0])],
|
||||
['name', 'module']
|
||||
)
|
||||
|
||||
if group_data:
|
||||
group_name = group_data[0]['name']
|
||||
module_name = group_data[0]['module']
|
||||
group_xml_id = f"{module_name}.{group_name}"
|
||||
|
||||
if self.env.user.has_group(group_xml_id) and rec['is_delete']:
|
||||
raise UserError(
|
||||
_('You are restricted from performing this operation. Please contact the administrator.'))
|
||||
else:
|
||||
# 如果 'access.right' 模型不存在,可以在这里定义备选逻辑
|
||||
pass
|
||||
|
||||
return True
|
||||
|
||||
|
||||
BaseModel._create = _create
|
||||
# BaseModel.unlink = unlink
|
||||
BaseModel.unlink = unlink
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import models
|
||||
from . import wizards
|
||||
@@ -1,39 +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': [
|
||||
'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 models
|
||||
@@ -1,180 +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 = "采购订单"
|
||||
_state_from = ["draft", "to approve", "rejected"]
|
||||
_state_to = ["approved"]
|
||||
|
||||
_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 not record.validation_status == 'validated':
|
||||
raise ValidationError(_('请先完成审批。'))
|
||||
res = super(jikimo_purchase_tier_validation, self).button_confirm()
|
||||
for record in self:
|
||||
if record.state == 'approved':
|
||||
record.order_line._validate_analytic_distribution()
|
||||
record._add_supplier_to_product()
|
||||
# Deal with double validation process
|
||||
if record._approval_allowed():
|
||||
record.button_approve()
|
||||
if record.partner_id not in record.message_partner_ids:
|
||||
record.message_subscribe([record.partner_id.id])
|
||||
return res
|
||||
|
||||
def request_validation(self):
|
||||
for record in self:
|
||||
# 添加通知消息
|
||||
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)
|
||||
|
||||
# 检查是否所有审批都已通过
|
||||
all_approved = all(
|
||||
tier_review.status == 'approved'
|
||||
for tier_review in self.review_ids
|
||||
)
|
||||
|
||||
if self.review_ids and all_approved: # 确保有审批记录
|
||||
self.state = 'approved'
|
||||
|
||||
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,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,32 +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>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1 +0,0 @@
|
||||
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,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,5 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import controllers
|
||||
from . import models
|
||||
from . import wizard
|
||||
@@ -1,39 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': "jikimo_system_order",
|
||||
|
||||
'summary': """
|
||||
系统工单""",
|
||||
|
||||
'description': """
|
||||
用于处理针对系统的工作任务;
|
||||
员工可以通过系统工单发起申请,由维护人员处理以后,填写处理结果。
|
||||
""",
|
||||
|
||||
'author': "机企猫",
|
||||
'website': "http://www.jikimo.com",
|
||||
|
||||
# Categories can be used to filter modules in modules listing
|
||||
# Check https://github.com/odoo/odoo/blob/master/odoo/addons/base/module/module_data.xml
|
||||
# for the full list
|
||||
'category': 'Uncategorized',
|
||||
'version': '0.1',
|
||||
|
||||
# any module necessary for this one to work correctly
|
||||
'depends': ['base','mail'],
|
||||
|
||||
# always loaded
|
||||
'data': [
|
||||
'security/account_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'wizard/order_wizard.xml',
|
||||
'views/notice_user_config.xml',
|
||||
'views/yizuo_system_order_view.xml',
|
||||
'views/work_order_number.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
],
|
||||
# only loaded in demonstration mode
|
||||
'demo': [
|
||||
'demo/demo.xml',
|
||||
],
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import controllers
|
||||
@@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import http
|
||||
|
||||
# class TopSystemOrder(http.Controller):
|
||||
# @http.route('/jikimo_system_order/jikimo_system_order/', auth='public')
|
||||
# def index(self, **kw):
|
||||
# return "Hello, world"
|
||||
|
||||
# @http.route('/jikimo_system_order/jikimo_system_order/objects/', auth='public')
|
||||
# def list(self, **kw):
|
||||
# return http.request.render('jikimo_system_order.listing', {
|
||||
# 'root': '/jikimo_system_order/jikimo_system_order',
|
||||
# 'objects': http.request.env['jikimo_system_order.jikimo_system_order'].search([]),
|
||||
# })
|
||||
|
||||
# @http.route('/jikimo_system_order/jikimo_system_order/objects/<model("jikimo_system_order.jikimo_system_order"):obj>/', auth='public')
|
||||
# def object(self, obj, **kw):
|
||||
# return http.request.render('jikimo_system_order.object', {
|
||||
# 'object': obj
|
||||
# })
|
||||
@@ -1,30 +0,0 @@
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- -->
|
||||
<!-- <record id="object0" model="jikimo_system_order.jikimo_system_order"> -->
|
||||
<!-- <field name="name">Object 0</field> -->
|
||||
<!-- <field name="value">0</field> -->
|
||||
<!-- </record> -->
|
||||
<!-- -->
|
||||
<!-- <record id="object1" model="jikimo_system_order.jikimo_system_order"> -->
|
||||
<!-- <field name="name">Object 1</field> -->
|
||||
<!-- <field name="value">10</field> -->
|
||||
<!-- </record> -->
|
||||
<!-- -->
|
||||
<!-- <record id="object2" model="jikimo_system_order.jikimo_system_order"> -->
|
||||
<!-- <field name="name">Object 2</field> -->
|
||||
<!-- <field name="value">20</field> -->
|
||||
<!-- </record> -->
|
||||
<!-- -->
|
||||
<!-- <record id="object3" model="jikimo_system_order.jikimo_system_order"> -->
|
||||
<!-- <field name="name">Object 3</field> -->
|
||||
<!-- <field name="value">30</field> -->
|
||||
<!-- </record> -->
|
||||
<!-- -->
|
||||
<!-- <record id="object4" model="jikimo_system_order.jikimo_system_order"> -->
|
||||
<!-- <field name="name">Object 4</field> -->
|
||||
<!-- <field name="value">40</field> -->
|
||||
<!-- </record> -->
|
||||
<!-- -->
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1,7 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import constant
|
||||
from . import order_classify
|
||||
from . import system_work_order
|
||||
from . import work_order_template
|
||||
from . import res_config_setting
|
||||
@@ -1,7 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 工单状态
|
||||
STATE_SELECTION = [('draft', u'草稿'), ('unconfirmed', u'待确认'), ('pending', u'待处理'),
|
||||
('processed', u'已处理待评分'), ('completed', u'已完成'), ('closed', u'已关闭')]
|
||||
|
||||
GRADE = [('1', '1非常不满意'), ('2', '2不满意'), ('3', '3一般'), ('4', '4满意'), ('5', '5非常满意')]
|
||||
@@ -1,25 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class OrderClassify(models.Model):
|
||||
_name = 'order.classify'
|
||||
_order = 'sequence, name'
|
||||
|
||||
|
||||
@api.constrains('name')
|
||||
def check_base_name(self):
|
||||
"""类型名称唯一"""
|
||||
name_obj = self.env['order.classify'].search([('name', '=', self.name)])
|
||||
if len(name_obj) >= 2:
|
||||
raise ValidationError(u'该类型已存在')
|
||||
|
||||
# 名称
|
||||
name = fields.Char(string=u'名称', size=20)
|
||||
# 排序
|
||||
sequence = fields.Integer(default=10)
|
||||
# 是否有效
|
||||
state = fields.Boolean(default=True, string='是否有效')
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ResModelWeConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
lost_agent_id = fields.Char('企微通知应用ID')
|
||||
|
||||
@api.model
|
||||
def get_values(self):
|
||||
"""
|
||||
重载获取参数的方法,参数都存在系统参数中
|
||||
:return:
|
||||
"""
|
||||
values = super(ResModelWeConfigSettings, self).get_values()
|
||||
config = self.env['ir.config_parameter'].sudo()
|
||||
lost_agent_id = config.get_param('lost_agent_id', default='')
|
||||
values.update(
|
||||
lost_agent_id=lost_agent_id,
|
||||
)
|
||||
return values
|
||||
|
||||
def set_values(self):
|
||||
super(ResModelWeConfigSettings, self).set_values()
|
||||
ir_config = self.env['ir.config_parameter'].sudo()
|
||||
ir_config.set_param("lost_agent_id", self.lost_agent_id or "")
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo import exceptions
|
||||
from .constant import STATE_SELECTION, GRADE
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
|
||||
class SystemWorkOrder(models.Model):
|
||||
_name = 'system.work.order'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_order = 'date desc'
|
||||
_description = u'系统工单'
|
||||
_rec_name = 'order_number'
|
||||
|
||||
def get_is_technicist(self):
|
||||
self._cr.execute(
|
||||
"select u.id from res_users u left join res_groups_users_rel r on r.uid = u.id where r.gid in (select g.id from res_groups g where g.name = '技术员权限') and u.id ='%s'",
|
||||
(self.env.user.id,))
|
||||
hr = self._cr.dictfetchall()
|
||||
if len(hr) > 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
# def get_user_department_id(self):
|
||||
# """根据用户id系统员工id"""
|
||||
# employee = self.env['hr.employee'].sudo().search([('user_id', '=', self.env.uid)], limit=1)
|
||||
# if employee:
|
||||
# if len(employee) > 0:
|
||||
# if not employee.department_id:
|
||||
# raise exceptions.Warning(u'您当前使用的用户没有所属部门')
|
||||
# return employee.department_id
|
||||
# else:
|
||||
# return False
|
||||
# else:
|
||||
# raise exceptions.Warning(u'您当前使用的用户没有关联员工')
|
||||
|
||||
@api.onchange('order_template_id')
|
||||
def get_title(self):
|
||||
"""选择模板自动填充"""
|
||||
if self.order_template_id:
|
||||
self.title = self.order_template_id.title_template
|
||||
self.text = self.order_template_id.text_template
|
||||
|
||||
# 工单编号
|
||||
order_number = fields.Char(string=u'工单编号', default='/')
|
||||
# 紧急程度
|
||||
urgency_degree = fields.Selection([('0', u'0星'), ('1', u'一星'), ('2', u'二星'), ('3', u'三星'), ('4', u'四星'),
|
||||
('5', u'五星')], string=u'紧急程度', help='五星为最紧急!', default='5')
|
||||
# 工单分类(可以配置,并调整优先级)
|
||||
order_type = fields.Many2one('order.classify', string=u'工单分类', domain=[('state', '=', True)])
|
||||
# 发起人所属公司(res.company)
|
||||
initiator_company_id = fields.Many2one('res.company', string=u'发起人所属公司', default=lambda self: self.env.user.company_id)
|
||||
# 发起人部门(hr.department)
|
||||
# initiator_department_id = fields.Many2one('hr.department', string=u'发起人部门', default=get_user_department_id)
|
||||
# 发起人(hr.employee)
|
||||
initiator_id = fields.Many2one('res.users', string=u'发起人', default=lambda self: self.env.user)
|
||||
# 发起时间
|
||||
date = fields.Datetime(string=u'发起时间', default=lambda self: fields.datetime.now())
|
||||
# 确认人
|
||||
confirm_id = fields.Many2one('res.users', string=u'确认人')
|
||||
# 确认日期
|
||||
confirmation_date = fields.Datetime(string=u'确认时间')
|
||||
# 模板
|
||||
order_template_id = fields.Many2one('work.order.template', string=u'模板', domain=[('state', '=', True)])
|
||||
# 标题
|
||||
title = fields.Char(string=u'标题')
|
||||
# 正文
|
||||
text = fields.Html(string=u'正文')
|
||||
# 状态[草稿\待确认\待处理\已处理\已关闭]
|
||||
state = fields.Selection(STATE_SELECTION, default='draft', string=u'状态')
|
||||
# 关闭原因
|
||||
close_cause = fields.Text(string=u'关闭问题原因')
|
||||
# 关闭时间
|
||||
close_time = fields.Datetime(string=u'关闭问题时间')
|
||||
# 关闭人
|
||||
close_user_id = fields.Many2one('res.users', string=u'关闭人')
|
||||
# 解决人
|
||||
solve_people_id = fields.Many2one('res.users', string=u'解决人')
|
||||
# 用户实际问题
|
||||
users_problem = fields.Text(string=u'用户实际问题')
|
||||
# 最终解决方案
|
||||
solution = fields.Text(string=u'最终解决方案')
|
||||
# 判断是否为技术人员
|
||||
# is_technicist = fields.Boolean(string=u'是否为技术人员', default=get_is_technicist)
|
||||
# 打分
|
||||
grade = fields.Selection(GRADE, string=u'评分')
|
||||
# 评价按钮的显示
|
||||
is_display = fields.Boolean('控制显示评价按钮', compute='compute_is_display')
|
||||
|
||||
def compute_is_display(self):
|
||||
for item in self:
|
||||
if item.state == 'processed' and self.env.user.id == item.initiator_id.id:
|
||||
item.is_display = True
|
||||
else:
|
||||
item.is_display = False
|
||||
|
||||
@api.onchange('order_type')
|
||||
def _onchange_order_type(self):
|
||||
self.order_template_id = None
|
||||
self.title = None
|
||||
self.text = None
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
# 创建编号
|
||||
if vals.get('order_number', '/') == '/':
|
||||
vals['order_number'] = self.env['ir.sequence'].get('system.work.order') or '/'
|
||||
return super(SystemWorkOrder, self).create(vals)
|
||||
|
||||
def do_draft(self, order=None):
|
||||
"""状态草稿"""
|
||||
bill = self
|
||||
if order:
|
||||
bill = order
|
||||
if bill.state == 'unconfirmed':
|
||||
state_remark = u'待确认 --> 草稿'
|
||||
# bill.message_post(u'操作人:%s,操作时间:%s,状态变更过程:%s' % (self.env.user.name,
|
||||
# (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
|
||||
bill.state = 'draft'
|
||||
|
||||
def do_unconfirmed(self):
|
||||
"""状态待确认"""
|
||||
if self.state == 'draft':
|
||||
state_remark = u'草稿 --> 待确认'
|
||||
# self.message_post(u'操作人:%s,操作时间:%s,状态变更过程:%s' % (
|
||||
# self.env.user.name,
|
||||
# (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
|
||||
self.state = 'unconfirmed'
|
||||
# 获取通知人
|
||||
objs = self.env['system.order.notice'].search([])
|
||||
user_ids = objs.notice_user_ids.filtered(lambda item: item.we_employee_id not in ['', False])
|
||||
we_employee_ids = user_ids.mapped('we_employee_id')
|
||||
lost_agent_id = self.env['ir.config_parameter'].sudo().get_param('lost_agent_id')
|
||||
wechat = self.env['we.config'].sudo().get_wechat(agent_id=lost_agent_id)
|
||||
# agent_id, user_ids, content
|
||||
content = """您有一张工单<font color=\"warning\">待处理</font>:**工单标题:{2}**
|
||||
>创建人:{1}
|
||||
>提交时间:{3}
|
||||
>紧急程度:{0}星
|
||||
请查看工单消息,并及时处理!
|
||||
""".format(self.urgency_degree,
|
||||
self.initiator_id.name, self.title, (self.date + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M'))
|
||||
for we_employee_id in we_employee_ids:
|
||||
try:
|
||||
wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_id, content=content)
|
||||
except Exception as e:
|
||||
logging.error('工单处理发送消息异常%s' % str(e))
|
||||
|
||||
return True
|
||||
|
||||
def do_pending(self):
|
||||
"""状态待处理"""
|
||||
if self.state == 'unconfirmed':
|
||||
state_remark = u'待确认 --> 待处理'
|
||||
# self.message_post(u'操作人:%s,操作时间:%s,状态变更过程:%s' % (
|
||||
# self.env.user.name,
|
||||
# (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
|
||||
self.state = 'pending'
|
||||
self.confirm_id = self.env.user
|
||||
self.confirmation_date = fields.datetime.now()
|
||||
return True
|
||||
|
||||
def urned_off(self):
|
||||
"""状态关闭"""
|
||||
if self.close_cause:
|
||||
self.state = 'closed'
|
||||
self.close_time = fields.datetime.now()
|
||||
else:
|
||||
raise ValidationError(u'请注明关闭原因')
|
||||
return True
|
||||
|
||||
def unlink(self):
|
||||
for item in self:
|
||||
if item.state != "draft":
|
||||
raise ValidationError(u'只能删除状态为【草稿】的工单。')
|
||||
elif item.env.uid != item.initiator_id.id:
|
||||
raise ValidationError(u'非本人不能删除')
|
||||
else:
|
||||
super(SystemWorkOrder, item).unlink()
|
||||
@@ -1,38 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class WorkOrderTemplate(models.Model):
|
||||
_name = 'work.order.template'
|
||||
_order = 'num'
|
||||
|
||||
# 编号
|
||||
num = fields.Char(string=u'编号', default='/')
|
||||
# 名称
|
||||
name = fields.Char(string=u'模板名称', required="1")
|
||||
# 分类
|
||||
work_order_type = fields.Many2one('order.classify', string=u'系统工单分类', domain=[('state', '=', True)])
|
||||
# 模板标题
|
||||
title_template = fields.Char(string=u'模板标题')
|
||||
# 模板正文
|
||||
text_template = fields.Html(string=u'模板正文')
|
||||
# 模板说明
|
||||
template_explain = fields.Text(string=u'模板说明')
|
||||
# 是否有效
|
||||
state = fields.Boolean(default=True, string=u'是否有效')
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
# 创建编号
|
||||
if vals.get('num', '/') == '/':
|
||||
vals['num'] = self.env['ir.sequence'].get('work.order.template') or '/'
|
||||
return super(WorkOrderTemplate, self).create(vals)
|
||||
|
||||
|
||||
class SystemOrderNotice(models.Model):
|
||||
_name = 'system.order.notice'
|
||||
_description = '工单处理人设置'
|
||||
|
||||
notice_user_ids = fields.Many2many('res.users', string='工单处理人')
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="0"> <!-- noupdate表示,当模块升级时是否更新本条数据-->
|
||||
<!--运维权限组-->
|
||||
<record id="group_operations_permissions_rwc" model="res.groups">
|
||||
<field name="name">运维权限</field>
|
||||
</record>
|
||||
|
||||
<record id="system_order_user_rule" model="ir.rule">
|
||||
<field name="name">用户访问工单信息</field>
|
||||
<field name="model_id" ref="model_system_work_order"/>
|
||||
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
|
||||
<field name="domain_force">[('initiator_id', '=', user.id)]</field>
|
||||
</record>
|
||||
|
||||
<record id="system_order_group_operations_rule" model="ir.rule">
|
||||
<field name="name">运维访问工单信息</field>
|
||||
<field name="model_id" ref="model_system_work_order"/>
|
||||
<field name="groups" eval="[(4, ref('jikimo_system_order.group_operations_permissions_rwc'))]"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1,16 +0,0 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
|
||||
inside_system_order_classify_r,jikimo_system_order.order_classify,model_order_classify,,1,1,1,1
|
||||
inside_system_work_order_rc,jikimo_system_order.system_work_order,model_system_work_order,,1,1,1,1
|
||||
inside_work_order_template_r,jikimo_system_order.work_order_template,model_work_order_template,,1,1,1,1
|
||||
|
||||
inside_system_order_classify_rwc,jikimo_system_order.order_classify,model_order_classify,group_operations_permissions_rwc,1,1,1,0
|
||||
inside_system_work_order_rwc,jikimo_system_order.system_work_order,model_system_work_order,group_operations_permissions_rwc,1,1,1,0
|
||||
inside_work_order_template_rwc,jikimo_system_order.work_order_template,model_work_order_template,group_operations_permissions_rwc,1,1,1,0
|
||||
|
||||
order_close_wizard_group_user,jikimo_system_order.order_close_wizard,model_order_close_wizard,base.group_user,1,1,1,1
|
||||
order_other_wizard_group_user,jikimo_system_order.order_other_wizard,model_order_other_wizard,base.group_user,1,1,1,1
|
||||
order_technician_wizard_group_user,jikimo_system_order.order_technician_wizard,model_order_technician_wizard,base.group_user,1,1,1,1
|
||||
system_work_order_wizard_group_user,jikimo_system_order.system_work_order_wizard,model_system_work_order_wizard,base.group_user,1,1,1,1
|
||||
|
||||
system_order_notice_group_user,jikimo_system_order.system_order_notice,model_system_order_notice,base.group_user,1,1,1,1
|
||||
|
Binary file not shown.
|
Before Width: | Height: | Size: 2.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 533 B |
@@ -1,58 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<data>
|
||||
# ---------- 工单通知处理人设置 ------------
|
||||
|
||||
<record model="ir.ui.view" id="tree_system_order_notice_view">
|
||||
<field name="name">tree.system.order.notice</field>
|
||||
<field name="model">system.order.notice</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="工单处理人设置" editable="top">
|
||||
<field name="notice_user_ids" widget="many2many_tags" required="1" options="{'no_create': True, 'no_edit': True}"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.ui.view" id="search_system_order_notice_view">
|
||||
<field name="name">search.system.order.notice</field>
|
||||
<field name="model">system.order.notice</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="工单处理人设置">
|
||||
<field name="notice_user_ids" string="模糊搜索"
|
||||
filter_domain="[('notice_user_ids', 'ilike', self)]"/>
|
||||
<separator></separator>
|
||||
|
||||
<field name="notice_user_ids" string="处理人"/>
|
||||
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.actions.act_window" id="action_system_order_notice_view">
|
||||
<field name="name">工单处理人</field>
|
||||
<field name="res_model">system.order.notice</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="domain">[]</field>
|
||||
<field name="context">{}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
[工单处理人] 还没有哦!点左上角的[创建]按钮,沙发归你了!
|
||||
</p>
|
||||
<p>
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="res_config_settings_we_view_form_inherit" model="ir.ui.view">
|
||||
<field name="name">res.config.settings.we.view.form.inherit.bpm</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="base.res_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[hasclass('app_settings_block')]/div[6]" position="after">
|
||||
<div>
|
||||
<h2>企微通知应用ID</h2>
|
||||
<div class="row mt16 o_settings_container" id="jd_api">
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<div class="o_setting_left_pane"/>
|
||||
<div class="o_setting_right_pane">
|
||||
<div class="text-muted">
|
||||
<label for="lost_agent_id"/>
|
||||
<field name="lost_agent_id"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<odoo>
|
||||
<data noupdate="True">
|
||||
<!-- 工单流水号 -->
|
||||
<record id="seq_work_order" model="ir.sequence">
|
||||
<field name="name">seq_work_order</field>
|
||||
<field name="company_id"/>
|
||||
<field name="code">system.work.order</field>
|
||||
<field name="prefix">SO%(year)s%(month)s%(day)s</field>
|
||||
<field name="padding">1</field>
|
||||
</record>
|
||||
|
||||
<!-- 模板编号 -->
|
||||
<record id="seq_order_template" model="ir.sequence">
|
||||
<field name="name">seq_order_template</field>
|
||||
<field name="company_id"/>
|
||||
<field name="code">work.order.template</field>
|
||||
<field name="prefix">TL</field>
|
||||
<field name="padding">1</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1,243 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<odoo>
|
||||
<data>
|
||||
<!--工单信息-->
|
||||
<record model="ir.ui.view" id="work_order_tree">
|
||||
<field name="name">工单信息</field>
|
||||
<field name="model">system.work.order</field><!--对应表单名称-->
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="state" widget="badge" decoration-primary="state == 'draft'"
|
||||
decoration-success="state in ('processed', 'completed')"
|
||||
decoration-danger="state == 'pending'" decoration-warning="state in ('unconfirmed')"/>
|
||||
<field name="order_number"/>
|
||||
<field name="title"/>
|
||||
<field name="initiator_id"/>
|
||||
<field name="date"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!--新建系统工单-->
|
||||
<record model="ir.ui.view" id="ork_order_form">
|
||||
<field name="name">新建系统工单</field>
|
||||
<field name="model">system.work.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header>
|
||||
<field name="is_display" invisible="1"/>
|
||||
<button string='提交' class="oe_highlight" states="draft"
|
||||
type="object" name="do_unconfirmed"
|
||||
attrs="{'invisible': [('state', '!=', 'draft')]}"/>
|
||||
<button string='追回编辑' states="unconfirmed"
|
||||
type="action" name="%(system_work_order_wizard_view_act_window)d"
|
||||
context="{'explain':'确认要执行此操作吗?','object_name':'system.work.order','function_name':'do_draft','object_id':id}"/>
|
||||
|
||||
<button name="do_pending" states="unconfirmed"
|
||||
string="确认可处理" type="object" class="oe_highlight"
|
||||
groups="jikimo_system_order.group_operations_permissions_rwc"/>
|
||||
|
||||
<button string='处理工单' class="oe_highlight" states="pending"
|
||||
type="action" name="%(launch_order_technician_wizard)d"
|
||||
groups="jikimo_system_order.group_operations_permissions_rwc"/>
|
||||
<button string='评价' class="oe_highlight" attrs="{'invisible': [('is_display', '=', False)]}"
|
||||
type="action" name="%(launch_order_other_wizard)d" context="{'active_id':id}"/>
|
||||
<button name="%(launch_order_close_wizard)d" string="关闭该工单"
|
||||
attrs="{'invisible': ['|',('state', '=', 'draft'),'|',('state','=','completed'),('state','=','closed')]}"
|
||||
type="action" context="{'active_id':id}"/>
|
||||
|
||||
<field name="state" widget="statusbar"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<!-- <label for="order_number" class="oe_edit_only"/>-->
|
||||
<group>
|
||||
<field name="order_number" required="True" readonly="1"/>
|
||||
<field name="order_type" required="True" attrs="{'readonly': [('state', '!=', 'draft')]}" options="{'no_create': True}"/>
|
||||
<field name="date" required="True" readonly="True"/>
|
||||
<field name="order_template_id" attrs="{'readonly': [('state', '!=', 'draft')]}"
|
||||
domain="[('work_order_type','=',order_type),('state','=',True)]" options="{'no_create': True}"/>
|
||||
<field name="confirmation_date" readonly="True"/>
|
||||
<field name="urgency_degree" required="True" attrs="{'readonly': [('state','!=','draft')]}" widget="priority"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="initiator_company_id" required="True" readonly="True"/>
|
||||
<!-- <field name="initiator_department_id" required="True" readonly="True"/>-->
|
||||
<field name="initiator_id" required="True" readonly="True"/>
|
||||
<field name="confirm_id" readonly="True"/>
|
||||
<field name="solve_people_id" readonly="True"/>
|
||||
<field name="close_user_id" readonly="True"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="title" attrs="{'readonly': [('state', '!=', 'draft')]}" required="True"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="工单内容">
|
||||
<field name="text" attrs="{'readonly': [('state','!=','draft')]}" required="True"/>
|
||||
</page>
|
||||
<page string="解决方案">
|
||||
<group>
|
||||
<field name="users_problem" readonly="True"/>
|
||||
<field name="solution" readonly="True"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="其他">
|
||||
<group>
|
||||
<field name="close_cause" readonly="True"/>
|
||||
<field name="close_time" readonly="True"/>
|
||||
<field name="grade" readonly="True"/>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<!-- <div class="oe_chatter">-->
|
||||
<!-- <field name="message_follower_ids" widget="mail_followers"/>-->
|
||||
<!-- <field name="message_ids" widget="mail_thread"/>-->
|
||||
<!-- </div>-->
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- 搜索工单 -->
|
||||
<record model="ir.ui.view" id="restaurant_search">
|
||||
<field name="name">搜索工单</field>
|
||||
<field name="model">system.work.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field string='发起人' name="initiator_id" widget="char" required="True"/>
|
||||
<field string='标题' name="title" widget="char"/>
|
||||
<field string='正文' name="text" widget="html"/>
|
||||
<field string='实际问题' name="users_problem" widget="text"/>
|
||||
<field string='解决方案' name="solution" widget="text"/>
|
||||
<filter name="today" string="今日工单" domain="[('date','=',time.strftime('%%Y-%%m-%%d'))]"/>
|
||||
<filter name="yesterday" string="昨日工单"
|
||||
domain="[('date', '=', (context_today() - relativedelta(days=1)).strftime('%Y-%m-%d'))]"/>
|
||||
<filter name="month" string="本月工单"
|
||||
domain="[('date','>=', time.strftime('%Y-%m-01')),('date','<', (context_today() + relativedelta(months=1)).strftime('%Y-%m-01'))]"/>
|
||||
<filter name="last_month" string="上月工单"
|
||||
domain="[('date','<', time.strftime('%Y-%m-01')),('date','>=', (context_today() - relativedelta(months=1)).strftime('%Y-%m-01'))]"/>
|
||||
<filter name="unconfirmed" string="待确认" domain="[('state','=','unconfirmed')]"/>
|
||||
<filter name="pending" string="待处理" domain="[('state','=','pending')]"/>
|
||||
<filter name="processed" string="已处理"
|
||||
domain="['|', ('state','=','processed'), ('state','=','closed')]"/>
|
||||
<group>
|
||||
<filter string='发起人' name="initiator_id" context='{"group_by":"initiator_id"}'/>
|
||||
<filter string='工单分类' name="order_type" context='{"group_by":"order_type"}'/>
|
||||
<filter string='模板' name="order_template_id" context='{"group_by":"order_template_id"}'/>
|
||||
<filter string='状态' name="state" context='{"group_by":"state"}'/>
|
||||
<filter string='紧急情况' name="state" context='{"group_by":"urgency_degree"}'/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="graph_tree">
|
||||
<field name="name">工单图表</field>
|
||||
<field name="model">system.work.order</field><!--对应表单名称-->
|
||||
<field name="arch" type="xml">
|
||||
<pivot>
|
||||
<field name="date" type="row" interval="day"/>
|
||||
<field name="order_type" type="col"/>
|
||||
<field name="state" type="row"/>
|
||||
</pivot>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- 工单 -->
|
||||
<record model="ir.actions.act_window" id="system_order">
|
||||
<field name="name">工单</field>
|
||||
<field name="res_model">system.work.order</field>
|
||||
<field name="view_mode">tree,form,search,graph,pivot</field>
|
||||
</record>
|
||||
|
||||
<!--工单模板信息-->
|
||||
<record model="ir.ui.view" id="order_template_tree">
|
||||
<field name="name">工单模板信息</field>
|
||||
<field name="model">work.order.template</field><!--对应表单名称-->
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="num"/>
|
||||
<field name="name"/>
|
||||
<field name="work_order_type"/>
|
||||
<field name="title_template"/>
|
||||
<field name="template_explain"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!--新建系统工单模板-->
|
||||
<record model="ir.ui.view" id="order_template_form">
|
||||
<field name="name">新建系统工单模板</field>
|
||||
<field name="model">work.order.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="num" required="True" readonly="True"/>
|
||||
<field name="name" required="True"/>
|
||||
<field name="work_order_type" required="True"/>
|
||||
<field name="template_explain" required="True" style="height: 50px;"/>
|
||||
<field name="title_template" required="True"/>
|
||||
<field name="state"/>
|
||||
<field name="text_template" required="True"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- 工单模板 -->
|
||||
<record model="ir.actions.act_window" id="work_template">
|
||||
<field name="name">工单模板</field>
|
||||
<field name="res_model">work.order.template</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<!--工单分类信息-->
|
||||
<record model="ir.ui.view" id="order_type_tree">
|
||||
<field name="name">工单分类信息</field>
|
||||
<field name="model">order.classify</field><!--对应表单名称-->
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!--新建系统分类信息-->
|
||||
<record model="ir.ui.view" id="order_type_form">
|
||||
<field name="name">新建系统分类信息</field>
|
||||
<field name="model">order.classify</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="name" required="True"/>
|
||||
<field name="sequence" invisible="True"/>
|
||||
<field name="state"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- 工单分类 -->
|
||||
<record model="ir.actions.act_window" id="classify">
|
||||
<field name="name">工单分类</field>
|
||||
<field name="res_model">order.classify</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
|
||||
<menuitem name="系统工单" id="work_order_1_list" web_icon="jikimo_system_order,static/description/系统工单.png"/>
|
||||
<menuitem name="工单" id="work_order" parent="work_order_1_list" action="system_order"/>
|
||||
<menuitem name="工单模板" id="work_order_template" parent="work_order_1_list" action="work_template" groups="jikimo_system_order.group_operations_permissions_rwc"/>
|
||||
<menuitem name="工单分类" id="work_order_type" parent="work_order_1_list" action="classify" groups="jikimo_system_order.group_operations_permissions_rwc"/>
|
||||
<menuitem name="工单设置" id="system_order_notice_user_config" parent="work_order_1_list" action="action_system_order_notice_view" groups="jikimo_system_order.group_operations_permissions_rwc"/>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1,6 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import order_other_wizard
|
||||
from . import order_technician_wizard
|
||||
from . import order_close_wizard
|
||||
from . import system_work_order_wizard
|
||||
@@ -1,79 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api
|
||||
from odoo.addons.jikimo_system_order.models.constant import STATE_SELECTION
|
||||
from odoo.exceptions import ValidationError
|
||||
import datetime, logging
|
||||
|
||||
|
||||
class OrderCloseWizard(models.TransientModel):
|
||||
_name = 'order.close.wizard'
|
||||
|
||||
|
||||
def get_context(self):
|
||||
if self._context.get('active_id'):
|
||||
obj = self.env['system.work.order'].browse(self._context.get('active_id'))
|
||||
if obj.initiator_id.id != self.env.user.id:
|
||||
raise ValidationError(u'非本人无法操作')
|
||||
return obj
|
||||
|
||||
order_id = fields.Many2one('system.work.order', string=u'工单ID',
|
||||
default=lambda self: self.get_context().id)
|
||||
# 关闭原因
|
||||
close_cause = fields.Text(string=u'关闭问题原因', default=lambda self: self.get_context().close_cause)
|
||||
# 关闭时间
|
||||
close_time = fields.Datetime(string=u'关闭问题时间', default=fields.datetime.now())
|
||||
# 状态
|
||||
state = fields.Selection(STATE_SELECTION, default='closed', string=u'状态')
|
||||
# 关闭人
|
||||
close_user_id = fields.Many2one('res.users', string=u'关闭人', default=lambda self: self.env.user)
|
||||
|
||||
|
||||
def sure(self):
|
||||
self.order_id.close_cause = self.close_cause
|
||||
self.order_id.close_time = self.close_time
|
||||
if self.order_id.state == 'unconfirmed':
|
||||
state_remark = u'待确认 --> 已关闭'
|
||||
if self.order_id.state == 'pending':
|
||||
state_remark = u'待处理 --> 已关闭'
|
||||
if self.order_id.state == 'processed':
|
||||
state_remark = u'已处理待评分 --> 已关闭'
|
||||
# self.order_id.message_post(u'操作人:%s,操作时间:%s,状态变更过程:%s' % (
|
||||
# self.env.user.name,
|
||||
# (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
|
||||
self.order_id.state = self.state
|
||||
self.order_id.close_user_id = self.close_user_id
|
||||
we_employee_ids = []
|
||||
if self.order_id.initiator_id.we_employee_id:
|
||||
we_employee_ids.append(self.order_id.initiator_id.we_employee_id)
|
||||
lost_agent_id = self.env['ir.config_parameter'].sudo().get_param('lost_agent_id')
|
||||
wechat = self.env['we.config'].sudo().get_wechat(agent_id=lost_agent_id)
|
||||
# agent_id, user_ids, content
|
||||
content = """您提交的工单-**工单标题:{0}**-<font color=\"#FF0000\">**已关闭**</font>
|
||||
>提交时间:{1}
|
||||
>处理时间:{2}
|
||||
>处理人:{3}
|
||||
如有问题,请联系系统管理员!
|
||||
""".format(self.order_id.title,
|
||||
(self.order_id.date + datetime.timedelta(hours=8)).strftime(
|
||||
'%Y-%m-%d %H:%M'), (datetime.datetime.now() + datetime.timedelta(
|
||||
hours=8)).strftime('%Y-%m-%d %H:%M'), self.env.user.name or '')
|
||||
# wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_ids, content=content)
|
||||
for we_employee_id in we_employee_ids:
|
||||
try:
|
||||
wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_id, content=content)
|
||||
except Exception as e:
|
||||
logging.error('工单关闭发送消息异常%s' % str(e))
|
||||
return {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.addons.jikimo_system_order.models.constant import STATE_SELECTION, GRADE
|
||||
import datetime
|
||||
|
||||
|
||||
class OrderOtherWizard(models.TransientModel):
|
||||
_name = 'order.other.wizard'
|
||||
|
||||
|
||||
def get_context(self):
|
||||
if self._context.get('active_id'):
|
||||
obj = self.env['system.work.order'].browse(self._context.get('active_id'))
|
||||
if obj.initiator_id.id != self.env.user.id:
|
||||
raise ValidationError(u'非本人无法操作')
|
||||
return obj
|
||||
|
||||
order_id = fields.Many2one('system.work.order', string=u'工单ID',
|
||||
default=lambda self: self.get_context().id)
|
||||
# 关闭时间
|
||||
close_time = fields.Datetime(string=u'关闭时间', default=fields.datetime.now())
|
||||
# 状态
|
||||
state = fields.Selection(STATE_SELECTION, default='completed', string=u'状态')
|
||||
# 打分
|
||||
grade = fields.Selection(GRADE, string=u'评分')
|
||||
# 关闭人
|
||||
close_user_id = fields.Many2one('res.users', string=u'关闭人', default=lambda self: self.env.user)
|
||||
|
||||
|
||||
def sure(self):
|
||||
self.order_id.close_time = self.close_time
|
||||
self.order_id.grade = self.grade
|
||||
if self.order_id.state == 'processed':
|
||||
state_remark = u'已处理待评分 --> 已完成'
|
||||
# self.order_id.message_post(u'操作人:%s,操作时间:%s,状态变更过程:%s' % (
|
||||
# self.env.user.name,
|
||||
# (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
|
||||
self.order_id.state = self.state
|
||||
self.order_id.close_user_id = self.close_user_id
|
||||
return {}
|
||||
@@ -1,59 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api
|
||||
from odoo.addons.jikimo_system_order.models.constant import STATE_SELECTION
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
|
||||
class OrderTechnicianWizard(models.TransientModel):
|
||||
_name = 'order.technician.wizard'
|
||||
|
||||
order_id = fields.Many2one('system.work.order', string=u'工单ID',
|
||||
default=lambda self: self.env.context.get('active_id'))
|
||||
# 解决人
|
||||
solve_people_id = fields.Many2one('res.users', string=u'解决人', default=lambda self: self.env.user)
|
||||
# 用户实际问题
|
||||
users_problem = fields.Text(string=u'用户实际问题')
|
||||
# 最终解决方案
|
||||
solution = fields.Text(string=u'最终解决方案')
|
||||
# 状态
|
||||
state = fields.Selection(STATE_SELECTION, default='processed', string=u'状态')
|
||||
|
||||
def sure(self):
|
||||
self.order_id.solve_people_id = self.solve_people_id
|
||||
self.order_id.users_problem = self.users_problem
|
||||
self.order_id.solution = self.solution
|
||||
if self.order_id.state == 'pending':
|
||||
state_remark = u'待处理 --> 已处理待评分'
|
||||
# self.order_id.message_post(u'操作人:%s,操作时间:%s,状态变更过程:%s' % (
|
||||
# self.env.user.name,
|
||||
# (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
|
||||
self.order_id.state = self.state
|
||||
# 获取通知人
|
||||
# objs = self.env['system.order.notice'].search([])
|
||||
# user_ids = objs.notice_user_ids.filtered(lambda item: item.we_employee_id not in ['', False])
|
||||
# we_employee_ids = user_ids.mapped('we_employee_id')
|
||||
we_employee_ids = []
|
||||
if self.order_id.initiator_id.we_employee_id:
|
||||
we_employee_ids.append(self.order_id.initiator_id.we_employee_id)
|
||||
print(we_employee_ids)
|
||||
lost_agent_id = self.env['ir.config_parameter'].sudo().get_param('lost_agent_id')
|
||||
wechat = self.env['we.config'].sudo().get_wechat(agent_id=lost_agent_id)
|
||||
# agent_id, user_ids, content
|
||||
content = """您提交的工单-**工单标题:{0}**-<font color=\"info\">**已处理**</font>
|
||||
>提交时间:{1}
|
||||
>处理反馈:{4}
|
||||
>处理时间:{2}
|
||||
>处理人:{3}
|
||||
如有问题,请联系系统管理员!
|
||||
""".format(self.order_id.title,
|
||||
(self.order_id.date + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M'), (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M'), self.env.user.name or '', self.solution or '')
|
||||
# wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_ids, content=content)
|
||||
for we_employee_id in we_employee_ids:
|
||||
try:
|
||||
wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_id, content=content)
|
||||
except Exception as e:
|
||||
logging.error('工单处理发送消息异常%s' % str(e))
|
||||
|
||||
return {}
|
||||
@@ -1,122 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- 技术员向导form-->
|
||||
<record model="ir.ui.view" id="wizard_technician_form_view">
|
||||
<field name="name">技术员向导</field>
|
||||
<field name="model">order.technician.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="技术员编辑">
|
||||
<group>
|
||||
<field name="order_id" required="1" readonly="1"/>
|
||||
<field name="solve_people_id" required="1"/>
|
||||
<field name="users_problem" required="1" style="height: 50px;"/>
|
||||
<field name="solution" required="1" style="height: 50px;"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="sure" string="确定" type="object" class="oe_highlight"/>
|
||||
or
|
||||
<button string="取消" class="oe_link" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="launch_order_technician_wizard">
|
||||
<field name="name">技术员编辑</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">order.technician.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="wizard_technician_form_view"/>
|
||||
<field name="context">{'display_default_code':False}</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
<!-- 其它向导form-->
|
||||
<record model="ir.ui.view" id="wizard_other_form_view">
|
||||
<field name="name">其它向导</field>
|
||||
<field name="model">order.other.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="其它编辑">
|
||||
<group>
|
||||
<field name="order_id" required="1" readonly="1"/>
|
||||
<field name="close_time" required="1" readonly="1"/>
|
||||
<field name="grade" required="1"/>
|
||||
<field name="close_user_id" required="1" readonly="1"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="sure" string="确定" type="object" class="oe_highlight"/>
|
||||
or
|
||||
<button string="取消" class="oe_link" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="launch_order_other_wizard">
|
||||
<field name="name">其它编辑</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">order.other.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="wizard_other_form_view"/>
|
||||
<field name="context">{'display_default_code':False}</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<!--关闭向导form-->
|
||||
<record model="ir.ui.view" id="wizard_close_form_view">
|
||||
<field name="name">关闭向导</field>
|
||||
<field name="model">order.close.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="关闭工单">
|
||||
<group>
|
||||
<field name="order_id" required="1" readonly="1"/>
|
||||
<field name="close_cause" required="1" style="height: 50px;"/>
|
||||
<field name="close_time" required="1" readonly="1"/>
|
||||
<field name="close_user_id" required="1" readonly="1"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="sure" string="确定" type="object" class="oe_highlight"/>
|
||||
or
|
||||
<button string="取消" class="oe_link" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="launch_order_close_wizard">
|
||||
<field name="name">关闭工单</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">order.close.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="wizard_close_form_view"/>
|
||||
<field name="context">{'display_default_code':False}</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<record id="system_work_order_wizard_view" model="ir.ui.view">
|
||||
<field name="name">system_work_order_wizard_view</field>
|
||||
<field name="model">system.work.order.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="二次确认">
|
||||
<field name="explain" readonly="1"/>
|
||||
<footer>
|
||||
<button name="sure" string="确定" type="object" class="oe_highlight"/>
|
||||
or
|
||||
<button string="取消" class="oe_link" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="system_work_order_wizard_view_act_window">
|
||||
<field name="name">二次确认</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">system.work.order.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2017/12/12 9:46
|
||||
# @Author : GuoXiang
|
||||
# @Site :
|
||||
# @File : system_work_order_wizard.py
|
||||
# @Software: PyCharm
|
||||
# @Desc :
|
||||
# @license : Copyright©2018 www.dasmaster.com All Rights Reserved.
|
||||
# @Contact : xg1230205321@163.com
|
||||
from odoo import models, api, fields
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class SystemWorkOrderWizard(models.TransientModel):
|
||||
_name = "system.work.order.wizard"
|
||||
_description = u"追回确认"
|
||||
|
||||
|
||||
def _get_explain(self):
|
||||
if self._context.get('object_id'):
|
||||
obj = self.env['system.work.order'].browse(self._context.get('object_id'))
|
||||
if obj.initiator_id.id != self.env.user.id:
|
||||
raise ValidationError(u'非本人无法操作')
|
||||
if self._context.get('explain'):
|
||||
return self._context["explain"]
|
||||
|
||||
explain = fields.Char(default=_get_explain)
|
||||
|
||||
|
||||
def sure(self):
|
||||
"""
|
||||
确认
|
||||
:return:
|
||||
"""
|
||||
if self._context.get('object_id') and self._context.get('object_name') and self._context.get(
|
||||
'explain') and self._context.get('function_name'):
|
||||
work_sheet_obj = self.env[self._context["object_name"]].search([('id', '=', int(self._context["object_id"]))])
|
||||
class_name = self._context.get('object_name') # 获得对象类名
|
||||
method_name = self._context.get('function_name') # 获得对象的方法
|
||||
obj_function = getattr(self.env[class_name], method_name)
|
||||
obj_function(work_sheet_obj)
|
||||
@@ -1,3 +0,0 @@
|
||||
from . import models
|
||||
from . import controllers
|
||||
from . import wizards
|
||||
@@ -1,32 +0,0 @@
|
||||
{
|
||||
'name': '机企猫 测试助手',
|
||||
'version': '16.0.1.0.0',
|
||||
'category': 'Technical',
|
||||
'summary': '测试数据初始化工具',
|
||||
'description': """
|
||||
用于初始化测试环境数据的工具模块
|
||||
""",
|
||||
'author': 'Jikimo',
|
||||
'website': 'www.jikimo.com',
|
||||
'depends': [
|
||||
'base',
|
||||
'sale_management',
|
||||
'purchase',
|
||||
'mrp',
|
||||
'stock',
|
||||
'account'
|
||||
],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'wizards/jikimo_data_clean_wizard.xml',
|
||||
],
|
||||
'assets': {
|
||||
'web.assets_backend': [
|
||||
'jikimo_test_assistant/static/src/js/data_clean_confirm.js',
|
||||
],
|
||||
},
|
||||
'installable': True,
|
||||
'application': False,
|
||||
'auto_install': False,
|
||||
'license': 'LGPL-3',
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import main
|
||||
@@ -1,86 +0,0 @@
|
||||
from odoo import http
|
||||
import logging
|
||||
import os
|
||||
import json
|
||||
import sys
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class Main(http.Controller):
|
||||
@http.route('/api/pdf2image', type='http', auth='public', methods=['POST'], csrf=False)
|
||||
def convert_pdf_to_image(self, **kwargs):
|
||||
"""将PDF文件转换为图片文件
|
||||
|
||||
Returns:
|
||||
dict: 包含转换后图片url的字典
|
||||
"""
|
||||
res = {}
|
||||
try:
|
||||
# 检查poppler是否可用
|
||||
# if sys.platform.startswith('win'):
|
||||
# if not os.environ.get('POPPLER_PATH'):
|
||||
# return {
|
||||
# 'code': 400,
|
||||
# 'msg': '请先配置POPPLER_PATH环境变量'
|
||||
# }
|
||||
# else:
|
||||
# import shutil
|
||||
# if not shutil.which('pdftoppm'):
|
||||
# return {
|
||||
# 'code': 400,
|
||||
# 'msg': '请先安装poppler-utils'
|
||||
# }
|
||||
|
||||
# 获取上传的PDF文件
|
||||
pdf_file = kwargs.get('file')
|
||||
if not pdf_file:
|
||||
res = {'code': 400, 'msg': '未找到上传的PDF文件'}
|
||||
|
||||
# 检查文件类型
|
||||
if not pdf_file.filename.lower().endswith('.pdf'):
|
||||
res = {'code': 400, 'msg': '请上传PDF格式的文件'}
|
||||
|
||||
# 读取PDF文件内容
|
||||
pdf_content = pdf_file.read()
|
||||
|
||||
# 使用pdf2image转换
|
||||
from pdf2image import convert_from_bytes
|
||||
import tempfile
|
||||
|
||||
# 转换PDF
|
||||
with tempfile.TemporaryDirectory() as path:
|
||||
images = convert_from_bytes(pdf_content)
|
||||
image_urls = []
|
||||
|
||||
# 保存每一页为图片
|
||||
for i, image in enumerate(images):
|
||||
image_path = os.path.join(path, f'page_{i+1}.jpg')
|
||||
image.save(image_path, 'JPEG')
|
||||
|
||||
# 将图片保存到ir.attachment
|
||||
with open(image_path, 'rb') as img_file:
|
||||
attachment = http.request.env['ir.attachment'].sudo().create({
|
||||
'name': f'page_{i+1}.jpg',
|
||||
'datas': img_file.read(),
|
||||
'type': 'binary',
|
||||
'access_token': kwargs.get('access_token') or '123'
|
||||
})
|
||||
image_urls.append({
|
||||
'page': i+1,
|
||||
'url': f'/web/content/{attachment.id}'
|
||||
})
|
||||
|
||||
res = {
|
||||
'code': 200,
|
||||
'msg': '转换成功',
|
||||
'data': image_urls
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
_logger.error('PDF转换失败: %s', str(e))
|
||||
res = {
|
||||
'code': 500,
|
||||
'msg': f'转换失败: {str(e)}'
|
||||
}
|
||||
return json.JSONEncoder().encode(res)
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_jikimo_data_clean_wizard,jikimo_test_assistant.jikimo_data_clean_wizard,model_jikimo_data_clean_wizard,base.group_system,1,1,1,1
|
||||
|
@@ -1,50 +0,0 @@
|
||||
odoo.define('jikimo_test_assistant.action_clean_data_confirm', function (require) {
|
||||
const core = require('web.core');
|
||||
const ajax = require('web.ajax');
|
||||
const Dialog = require('web.Dialog');
|
||||
var rpc = require('web.rpc');
|
||||
var _t = core._t;
|
||||
|
||||
async function action_clean_data_confirm(parent, {params}) {
|
||||
let message = "确认清理数据?<br/>"
|
||||
message += "日期:"+ params.date + "以前<br/>"
|
||||
message += "模型:" + params.model_names.join(',')
|
||||
const dialog = new Dialog(parent, {
|
||||
title: "确认",
|
||||
$content: $('<div>').append(message),
|
||||
buttons: [
|
||||
{ text: "确认", classes: 'btn-primary jikimo_button_confirm', close: true, click: () => actionCleanDataConfirm(parent, params) },
|
||||
{ text: "取消", close: true },
|
||||
],
|
||||
});
|
||||
dialog.open();
|
||||
|
||||
|
||||
async function actionCleanDataConfirm(parent, params) {
|
||||
rpc.query({
|
||||
model: 'jikimo.data.clean.wizard',
|
||||
method: 'action_clean_data',
|
||||
args: [params.active_id],
|
||||
kwargs: {
|
||||
context: params.context,
|
||||
}
|
||||
}).then(res => {
|
||||
parent.services.action.doAction({
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'target': 'new',
|
||||
'params': {
|
||||
'message': '数据清理成功!',
|
||||
'type': 'success',
|
||||
'sticky': false,
|
||||
'next': {'type': 'ir.actions.act_window_close'},
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
core.action_registry.add('action_clean_data_confirm', action_clean_data_confirm);
|
||||
return action_clean_data_confirm;
|
||||
});
|
||||
@@ -1,2 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import jikimo_data_clean_wizard
|
||||
@@ -1,99 +0,0 @@
|
||||
from odoo import models, fields, api
|
||||
from datetime import datetime
|
||||
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class JikimoDataCleanWizard(models.TransientModel):
|
||||
_name = 'jikimo.data.clean.wizard'
|
||||
_description = '业务数据清理'
|
||||
|
||||
date = fields.Date(string='截止日期', required=True, default=fields.Date.context_today)
|
||||
model_ids = fields.Many2many('ir.model', string='业务模型', domain=[
|
||||
('model', 'in', [
|
||||
'sale.order', # 销售订单
|
||||
'purchase.order', # 采购订单
|
||||
'mrp.production', # 生产订单
|
||||
'stock.picking', # 库存调拨
|
||||
'account.move', # 会计凭证
|
||||
])
|
||||
])
|
||||
|
||||
def action_clean_data(self):
|
||||
self.ensure_one()
|
||||
model_list = self.model_ids.mapped('model')
|
||||
|
||||
# 销售订单清理(排除已交付,已锁定,已取消)
|
||||
if 'sale.order' in model_list:
|
||||
self.model_cancel('sale.order', except_states=['delivered', 'done', 'cancel'])
|
||||
|
||||
# 采购订单清理(排除采购订单,已锁定,已取消)
|
||||
if 'purchase.order' in model_list:
|
||||
self.model_cancel('purchase.order', except_states=['purchase', 'done', 'cancel'])
|
||||
|
||||
# 生产订单清理(排除返工,报废,完成,已取消)
|
||||
if 'mrp.production' in model_list:
|
||||
self.model_cancel('mrp.production', except_states=['rework', 'scrap', 'done', 'cancel'])
|
||||
|
||||
# 工单清理 (排除返工,完成,已取消)
|
||||
if 'mrp.workorder' in model_list:
|
||||
self.model_cancel('mrp.production', except_states=['rework', 'done', 'cancel'])
|
||||
|
||||
# 排程单清理 (排除已完成,已取消)
|
||||
if 'mrp.workorder' in model_list:
|
||||
self.model_cancel('mrp.production', except_states=['finished', 'cancel'])
|
||||
|
||||
# 工单库存移动 (排除完成,已取消)
|
||||
if 'stock.move' in model_list:
|
||||
self.model_cancel('stock.move')
|
||||
|
||||
# 库存调拨清理 (排除完成,已取消)
|
||||
if 'stock.picking' in model_list:
|
||||
self.model_cancel('stock.picking')
|
||||
|
||||
# 会计凭证清理 (排除已过账,已取消)
|
||||
if 'account.move' in model_list:
|
||||
self.model_cancel('account.move', except_states=['posted', 'cancel'])
|
||||
|
||||
return True
|
||||
|
||||
def model_cancel(self, model_name, state_field='state', to_state='cancel',except_states=('done', 'cancel')):
|
||||
table = self.env[model_name]._table
|
||||
if isinstance(except_states, list):
|
||||
except_states = tuple(except_states)
|
||||
sql = """
|
||||
UPDATE
|
||||
%s SET %s = '%s'
|
||||
WHERE
|
||||
create_date < '%s'
|
||||
AND state NOT IN %s
|
||||
""" % (table, state_field, to_state, self.date.strftime('%Y-%m-%d'), except_states)
|
||||
self.env.cr.execute(sql)
|
||||
self.env.cr.commit()
|
||||
|
||||
@api.model
|
||||
def get_confirm_message(self):
|
||||
date_str = self.date.strftime('%Y-%m-%d') if self.date else ''
|
||||
model_names = ', '.join([model.name for model in self.model_ids])
|
||||
return {
|
||||
'date': date_str,
|
||||
'model_names': model_names
|
||||
}
|
||||
|
||||
def action_clean_data_confirm(self):
|
||||
model_names = self.model_ids.mapped('display_name')
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'action_clean_data_confirm',
|
||||
'params': {
|
||||
'model_names': model_names,
|
||||
'date': self.date,
|
||||
'active_id': self.id,
|
||||
'context': self.env.context
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Form View -->
|
||||
<record id="view_jikimo_data_clean_form" model="ir.ui.view">
|
||||
<field name="name">jikimo.data.clean.wizard.form</field>
|
||||
<field name="model">jikimo.data.clean.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="业务数据清理">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="date"/>
|
||||
<field name="model_ids" widget="many2many_tags"/>
|
||||
</group>
|
||||
</sheet>
|
||||
<footer>
|
||||
<button name="action_clean_data_confirm"
|
||||
string="确认清理"
|
||||
type="object"
|
||||
class="btn-primary"/>
|
||||
<button special="cancel"
|
||||
string="取消"
|
||||
class="btn-secondary"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Action -->
|
||||
<record id="action_jikimo_data_clean" model="ir.actions.act_window">
|
||||
<field name="name">业务数据清理</field>
|
||||
<field name="res_model">jikimo.data.clean.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<!-- Menu -->
|
||||
<menuitem id="menu_test_root"
|
||||
name="测试"
|
||||
parent="base.menu_custom"
|
||||
sequence="100"/>
|
||||
|
||||
<menuitem id="menu_jikimo_data_clean"
|
||||
name="业务数据清理"
|
||||
parent="menu_test_root"
|
||||
action="action_jikimo_data_clean"
|
||||
sequence="10"/>
|
||||
</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,94 +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
|
||||
from datetime import datetime
|
||||
|
||||
_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加工'),
|
||||
('state', '!=', 'rework')
|
||||
])
|
||||
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', '')
|
||||
})
|
||||
# 申请重新编程
|
||||
workorder.production_id.update_programming_state(trigger_time=datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
reprogramming_reason=ret.get('Error', ''))
|
||||
workorder.production_id.write({'programming_state': '编程中', 'work_state': '编程中', 'is_rework': False})
|
||||
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,24 +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">
|
||||
<field name="routing_type" invisible="1"/>
|
||||
<page string="异常记录" name="workorder_exception" attrs='{"invisible": ["!", ("individuation_page_list", "ilike", "ER")]}'>
|
||||
<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
|
||||
msgid "wooden stool top"
|
||||
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 "打印标签"
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models, api
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class MrpProduction(models.Model):
|
||||
@@ -12,8 +12,7 @@ class MrpProduction(models.Model):
|
||||
check_ids = fields.One2many('quality.check', 'production_id', string="Checks")
|
||||
|
||||
def _split_productions(self, amounts=False, cancel_remaining_qty=False, set_consumed_qty=False):
|
||||
productions = super()._split_productions(amounts=amounts, cancel_remaining_qty=cancel_remaining_qty,
|
||||
set_consumed_qty=set_consumed_qty)
|
||||
productions = super()._split_productions(amounts=amounts, cancel_remaining_qty=cancel_remaining_qty, set_consumed_qty=set_consumed_qty)
|
||||
backorders = productions[1:]
|
||||
if not backorders:
|
||||
return productions
|
||||
@@ -21,4 +20,3 @@ class MrpProduction(models.Model):
|
||||
if wo.current_quality_check_id.component_id:
|
||||
wo.current_quality_check_id._update_component_quantity()
|
||||
return productions
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class MrpWorkcenter(models.Model):
|
||||
|
||||
class MrpProductionWorkcenterLine(models.Model):
|
||||
_name = 'mrp.workorder'
|
||||
_inherit = ['mrp.workorder', 'barcodes.barcode_events_mixin', 'mail.thread', 'mail.activity.mixin']
|
||||
_inherit = ['mrp.workorder', 'barcodes.barcode_events_mixin']
|
||||
|
||||
quality_point_ids = fields.Many2many('quality.point', compute='_compute_quality_point_ids', store=True)
|
||||
quality_point_count = fields.Integer('Steps', compute='_compute_quality_point_count')
|
||||
@@ -47,17 +47,14 @@ class MrpProductionWorkcenterLine(models.Model):
|
||||
|
||||
is_last_lot = fields.Boolean('Is Last lot', compute='_compute_is_last_lot')
|
||||
is_first_started_wo = fields.Boolean('Is The first Work Order', compute='_compute_is_last_unfinished_wo')
|
||||
is_last_unfinished_wo = fields.Boolean('Is Last Work Order To Process', compute='_compute_is_last_unfinished_wo',
|
||||
store=False)
|
||||
is_last_unfinished_wo = fields.Boolean('Is Last Work Order To Process', compute='_compute_is_last_unfinished_wo', store=False)
|
||||
lot_id = fields.Many2one(related='current_quality_check_id.lot_id', readonly=False)
|
||||
move_id = fields.Many2one(related='current_quality_check_id.move_id', readonly=False)
|
||||
move_line_id = fields.Many2one(related='current_quality_check_id.move_line_id', readonly=False)
|
||||
move_line_ids = fields.One2many(related='move_id.move_line_ids')
|
||||
quality_state = fields.Selection(related='current_quality_check_id.quality_state', string="Quality State",
|
||||
readonly=False)
|
||||
quality_state = fields.Selection(related='current_quality_check_id.quality_state', string="Quality State", readonly=False)
|
||||
qty_done = fields.Float(related='current_quality_check_id.qty_done', readonly=False)
|
||||
test_type_id = fields.Many2one('quality.point.test_type', 'Test Type',
|
||||
related='current_quality_check_id.test_type_id')
|
||||
test_type_id = fields.Many2one('quality.point.test_type', 'Test Type', related='current_quality_check_id.test_type_id')
|
||||
test_type = fields.Char(related='test_type_id.technical_name')
|
||||
user_id = fields.Many2one(related='current_quality_check_id.user_id', readonly=False)
|
||||
worksheet_page = fields.Integer('Worksheet page')
|
||||
@@ -68,8 +65,7 @@ class MrpProductionWorkcenterLine(models.Model):
|
||||
def _compute_quality_point_ids(self):
|
||||
for workorder in self:
|
||||
quality_points = workorder.operation_id.quality_point_ids
|
||||
quality_points = quality_points.filtered(
|
||||
lambda qp: not qp.product_ids or workorder.production_id.product_id in qp.product_ids)
|
||||
quality_points = quality_points.filtered(lambda qp: not qp.product_ids or workorder.production_id.product_id in qp.product_ids)
|
||||
workorder.quality_point_ids = quality_points
|
||||
|
||||
@api.depends('operation_id')
|
||||
@@ -95,8 +91,7 @@ class MrpProductionWorkcenterLine(models.Model):
|
||||
@api.depends('check_ids')
|
||||
def _compute_finished_product_check_ids(self):
|
||||
for wo in self:
|
||||
wo.finished_product_check_ids = wo.check_ids.filtered(
|
||||
lambda c: c.finished_product_sequence == wo.qty_produced)
|
||||
wo.finished_product_check_ids = wo.check_ids.filtered(lambda c: c.finished_product_sequence == wo.qty_produced)
|
||||
|
||||
def write(self, values):
|
||||
res = super().write(values)
|
||||
@@ -143,8 +138,7 @@ class MrpProductionWorkcenterLine(models.Model):
|
||||
self.finished_lot_id = self.env['stock.lot'].create({
|
||||
'product_id': self.product_id.id,
|
||||
'company_id': self.company_id.id,
|
||||
'name': self.env['stock.lot']._get_next_serial(self.company_id, self.product_id) or self.env[
|
||||
'ir.sequence'].next_by_code('stock.lot.serial'),
|
||||
'name': self.env['stock.lot']._get_next_serial(self.company_id, self.product_id) or self.env['ir.sequence'].next_by_code('stock.lot.serial'),
|
||||
})
|
||||
|
||||
def _create_subsequent_checks(self):
|
||||
@@ -158,7 +152,7 @@ class MrpProductionWorkcenterLine(models.Model):
|
||||
"""
|
||||
# Create another quality check if necessary
|
||||
next_check = self.current_quality_check_id.next_check_id
|
||||
if next_check.component_id != self.current_quality_check_id.product_id or \
|
||||
if next_check.component_id != self.current_quality_check_id.product_id or\
|
||||
next_check.point_id != self.current_quality_check_id.point_id:
|
||||
# TODO: manage reservation here
|
||||
|
||||
@@ -285,8 +279,7 @@ class MrpProductionWorkcenterLine(models.Model):
|
||||
if self.current_quality_check_id:
|
||||
team = self.current_quality_check_id.team_id
|
||||
else:
|
||||
team = self.env['quality.alert.team'].search(
|
||||
['|', ('company_id', '=', self.company_id.id), ('company_id', '=', False)], limit=1)
|
||||
team = self.env['quality.alert.team'].search(['|', ('company_id', '=', self.company_id.id), ('company_id', '=', False)], limit=1)
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'quality.check',
|
||||
@@ -327,8 +320,7 @@ class MrpProductionWorkcenterLine(models.Model):
|
||||
production = wo.production_id
|
||||
|
||||
move_raw_ids = wo.move_raw_ids.filtered(lambda m: m.state not in ('done', 'cancel'))
|
||||
move_finished_ids = wo.move_finished_ids.filtered(
|
||||
lambda m: m.state not in ('done', 'cancel') and m.product_id != wo.production_id.product_id)
|
||||
move_finished_ids = wo.move_finished_ids.filtered(lambda m: m.state not in ('done', 'cancel') and m.product_id != wo.production_id.product_id)
|
||||
previous_check = self.env['quality.check']
|
||||
for point in wo.quality_point_ids:
|
||||
# Check if we need a quality control for this point
|
||||
@@ -350,13 +342,11 @@ class MrpProductionWorkcenterLine(models.Model):
|
||||
if point.test_type == 'register_byproducts':
|
||||
moves = move_finished_ids.filtered(lambda m: m.product_id == point.component_id)
|
||||
if not moves:
|
||||
moves = production.move_finished_ids.filtered(
|
||||
lambda m: not m.operation_id and m.product_id == point.component_id)
|
||||
moves = production.move_finished_ids.filtered(lambda m: not m.operation_id and m.product_id == point.component_id)
|
||||
elif point.test_type == 'register_consumed_materials':
|
||||
moves = move_raw_ids.filtered(lambda m: m.product_id == point.component_id)
|
||||
if not moves:
|
||||
moves = production.move_raw_ids.filtered(
|
||||
lambda m: not m.operation_id and m.product_id == point.component_id)
|
||||
moves = production.move_raw_ids.filtered(lambda m: not m.operation_id and m.product_id == point.component_id)
|
||||
else:
|
||||
check = self.env['quality.check'].create(values)
|
||||
previous_check.next_check_id = check
|
||||
@@ -373,10 +363,8 @@ class MrpProductionWorkcenterLine(models.Model):
|
||||
processed_move |= moves
|
||||
|
||||
# Generate quality checks associated with unreferenced components
|
||||
moves_without_check = ((move_raw_ids | move_finished_ids) - processed_move).filtered(lambda move: (
|
||||
move.has_tracking != 'none' and not move.raw_material_production_id.use_auto_consume_components_lots) or move.operation_id)
|
||||
quality_team_id = self.env['quality.alert.team'].search(
|
||||
['|', ('company_id', '=', wo.company_id.id), ('company_id', '=', False)], limit=1).id
|
||||
moves_without_check = ((move_raw_ids | move_finished_ids) - processed_move).filtered(lambda move: (move.has_tracking != 'none' and not move.raw_material_production_id.use_auto_consume_components_lots) or move.operation_id)
|
||||
quality_team_id = self.env['quality.alert.team'].search(['|', ('company_id', '=', wo.company_id.id), ('company_id', '=', False)], limit=1).id
|
||||
for move in moves_without_check:
|
||||
values = {
|
||||
'production_id': production.id,
|
||||
@@ -424,8 +412,7 @@ class MrpProductionWorkcenterLine(models.Model):
|
||||
|
||||
backorder = False
|
||||
# Trigger the backorder process if we produce less than expected
|
||||
if float_compare(self.qty_producing, self.qty_remaining,
|
||||
precision_rounding=self.product_uom_id.rounding) == -1 and self.is_first_started_wo:
|
||||
if float_compare(self.qty_producing, self.qty_remaining, precision_rounding=self.product_uom_id.rounding) == -1 and self.is_first_started_wo:
|
||||
backorder = self.production_id._split_productions()[1:]
|
||||
for workorder in backorder.workorder_ids:
|
||||
if workorder.product_tracking == 'serial':
|
||||
@@ -436,8 +423,7 @@ class MrpProductionWorkcenterLine(models.Model):
|
||||
else:
|
||||
if self.operation_id:
|
||||
backorder = (self.production_id.procurement_group_id.mrp_production_ids - self.production_id).filtered(
|
||||
lambda p: p.workorder_ids.filtered(lambda wo: wo.operation_id == self.operation_id).state not in (
|
||||
'cancel', 'done')
|
||||
lambda p: p.workorder_ids.filtered(lambda wo: wo.operation_id == self.operation_id).state not in ('cancel', 'done')
|
||||
)[:1]
|
||||
else:
|
||||
index = list(self.production_id.workorder_ids).index(self)
|
||||
@@ -456,8 +442,7 @@ class MrpProductionWorkcenterLine(models.Model):
|
||||
wo.current_quality_check_id._update_component_quantity()
|
||||
if not self.env.context.get('no_start_next'):
|
||||
if self.operation_id:
|
||||
return backorder.workorder_ids.filtered(
|
||||
lambda wo: wo.operation_id == self.operation_id).open_tablet_view()
|
||||
return backorder.workorder_ids.filtered(lambda wo: wo.operation_id == self.operation_id).open_tablet_view()
|
||||
else:
|
||||
index = list(self.production_id.workorder_ids).index(self)
|
||||
return backorder.workorder_ids[index].open_tablet_view()
|
||||
@@ -481,8 +466,7 @@ class MrpProductionWorkcenterLine(models.Model):
|
||||
|
||||
def open_tablet_view(self):
|
||||
self.ensure_one()
|
||||
if not self.is_user_working and self.working_state != 'blocked' and self.state in (
|
||||
'ready', 'waiting', 'progress', 'pending'):
|
||||
if not self.is_user_working and self.working_state != 'blocked' and self.state in ('ready', 'waiting', 'progress', 'pending'):
|
||||
self.button_start()
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("mrp_workorder.tablet_client_action")
|
||||
action['target'] = 'fullscreen'
|
||||
@@ -537,8 +521,7 @@ class MrpProductionWorkcenterLine(models.Model):
|
||||
data = {
|
||||
'mrp.workorder': self.read(self._get_fields_for_tablet(), load=False)[0],
|
||||
'quality.check': self.check_ids._get_fields_for_tablet(sorted_check_list),
|
||||
'operation': self.operation_id.read(self.operation_id._get_fields_for_tablet())[
|
||||
0] if self.operation_id else {},
|
||||
'operation': self.operation_id.read(self.operation_id._get_fields_for_tablet())[0] if self.operation_id else {},
|
||||
'working_state': self.workcenter_id.working_state,
|
||||
'views': {
|
||||
'workorder': self.env.ref('mrp_workorder.mrp_workorder_view_form_tablet').id,
|
||||
@@ -570,8 +553,7 @@ class MrpProductionWorkcenterLine(models.Model):
|
||||
|
||||
return {
|
||||
'duration': self.duration,
|
||||
'position': bisect_left(last30op, self.duration),
|
||||
# which position regarded other workorders ranked by duration
|
||||
'position': bisect_left(last30op, self.duration), # which position regarded other workorders ranked by duration
|
||||
'quality_score': score,
|
||||
'show_rainbow': show_rainbow,
|
||||
}
|
||||
|
||||
@@ -1,44 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Manufacturing Order for Planing view -->
|
||||
<record id="mrp_production_tree_view_planning" model="ir.ui.view">
|
||||
<field name="name">mrp.production.tree.inherit.planning</field>
|
||||
<field name="model">mrp.production</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree default_order="date_planned_start asc" decoration-info="state=='confirmed'"
|
||||
decoration-danger="date_planned_start<current_date and state not in ('done','cancel')"
|
||||
decoration-muted="state in ('done','cancel')" string="Manufacturing Orders" name="Production">
|
||||
<field name="message_needaction" invisible="1"/>
|
||||
<field name="name"/>
|
||||
<field name="date_planned_start"/>
|
||||
<field name="product_id"/>
|
||||
<field name="product_qty" sum="Total Qty" string="Quantity"/>
|
||||
<field name="product_uom_id" string="Unit of Measure" options="{'no_open':True,'no_create':True}"
|
||||
groups="uom.group_uom"/>
|
||||
<field name="reservation_state" string="Availability"/>
|
||||
<field name="origin"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<!-- Manufacturing Order for Planing view -->
|
||||
<record id="mrp_production_tree_view_planning" model="ir.ui.view">
|
||||
<field name="name">mrp.production.tree.inherit.planning</field>
|
||||
<field name="model">mrp.production</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree default_order="date_planned_start asc" decoration-info="state=='confirmed'" decoration-danger="date_planned_start<current_date and state not in ('done','cancel')" decoration-muted="state in ('done','cancel')" string="Manufacturing Orders" name="Production">
|
||||
<field name="message_needaction" invisible="1"/>
|
||||
<field name="name"/>
|
||||
<field name="date_planned_start"/>
|
||||
<field name="product_id"/>
|
||||
<field name="product_qty" sum="Total Qty" string="Quantity"/>
|
||||
<field name="product_uom_id" string="Unit of Measure" options="{'no_open':True,'no_create':True}" groups="uom.group_uom"/>
|
||||
<field name="reservation_state" string="Availability"/>
|
||||
<field name="origin"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- <record id="mrp_production_form_inherit_planning" model="ir.ui.view">-->
|
||||
<!-- <field name="name">mrp.production.form_inherit_planning</field>-->
|
||||
<!-- <field name="model">mrp.production</field>-->
|
||||
<!-- <field name="inherit_id" ref="mrp.mrp_production_form_view"/>-->
|
||||
<!-- <field name="arch" type="xml">-->
|
||||
<!-- <xpath expr="div[hasclass('oe_chatter')]" position="replace">-->
|
||||
<!-- <!– 这里放置替换后的内容 –>-->
|
||||
<!-- </xpath>-->
|
||||
<!-- <xpath expr="//notebook" position="after">-->
|
||||
<!-- <div class="oe_chatter">-->
|
||||
<!-- <field name="message_follower_ids"/>-->
|
||||
<!-- <field name="activity_ids"/>-->
|
||||
<!-- <field name="message_ids"/>-->
|
||||
<!-- </div>-->
|
||||
<!-- </xpath>-->
|
||||
<!-- </field>-->
|
||||
<!-- </record>-->
|
||||
<!-- <record id="mrp_production_form_inherit_planning" model="ir.ui.view">-->
|
||||
<!-- <field name="name">mrp.production.form_inherit_planning</field>-->
|
||||
<!-- <field name="model">mrp.production</field>-->
|
||||
<!-- <field name="inherit_id" ref="mrp.mrp_production_form_view"/>-->
|
||||
<!-- <field name="arch" type="xml">-->
|
||||
<!-- <xpath expr="div[hasclass('oe_chatter')]" position="replace">-->
|
||||
<!-- <!– 这里放置替换后的内容 –>-->
|
||||
<!-- </xpath>-->
|
||||
<!-- <xpath expr="//notebook" position="after">-->
|
||||
<!-- <div class="oe_chatter">-->
|
||||
<!-- <field name="message_follower_ids"/>-->
|
||||
<!-- <field name="activity_ids"/>-->
|
||||
<!-- <field name="message_ids"/>-->
|
||||
<!-- </div>-->
|
||||
<!-- </xpath>-->
|
||||
<!-- </field>-->
|
||||
<!-- </record>-->
|
||||
|
||||
<record id="mrp_production_view_search_inherit_planning" model="ir.ui.view">
|
||||
<field name="name">mrp.production.search.view.inherit.planning</field>
|
||||
@@ -46,9 +43,7 @@
|
||||
<field name="inherit_id" ref="mrp.view_mrp_production_filter"/>
|
||||
<field name="arch" type="xml">
|
||||
<filter name="filter_planned" position="attributes">
|
||||
<attribute name="domain">[('is_planned', '=', True), ('date_planned_start', '!=', False),
|
||||
('date_planned_finished', '!=', False)]
|
||||
</attribute>
|
||||
<attribute name="domain">[('is_planned', '=', True), ('date_planned_start', '!=', False), ('date_planned_finished', '!=', False)]</attribute>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
@@ -56,33 +51,30 @@
|
||||
<record id="production_order_unplan_server_action" model="ir.actions.server">
|
||||
<field name="name">Unplan orders</field>
|
||||
<field name="model_id" ref="mrp.model_mrp_production"/>
|
||||
<field name="binding_model_id" ref="mrp.model_mrp_production"/>
|
||||
<field name="binding_model_id" ref="mrp.model_mrp_production" />
|
||||
<field name="binding_view_types">list</field>
|
||||
<field name="state">code</field>
|
||||
<field name="code">records.button_unplan()</field>
|
||||
</record>
|
||||
|
||||
<record id="mrp.act_product_mrp_production_workcenter" model="ir.actions.act_window">
|
||||
<field name="domain">[('bom_id', '!=', False), ('bom_id.operation_ids.workcenter_id', '=', active_id),
|
||||
('date_planned_start', '!=', False), ('date_planned_finished', '!=', False)]
|
||||
</field>
|
||||
<field name="domain">[('bom_id', '!=', False), ('bom_id.operation_ids.workcenter_id', '=', active_id), ('date_planned_start', '!=', False), ('date_planned_finished', '!=', False)]</field>
|
||||
<field name="view_id" ref="mrp_production_tree_view_planning"/>
|
||||
</record>
|
||||
|
||||
|
||||
<menuitem id="mrp_workorder_menu_planning"
|
||||
name="Work Orders"
|
||||
sequence="2"
|
||||
parent="mrp.mrp_planning_menu_root"
|
||||
groups="mrp.group_mrp_routings"/>
|
||||
name="Work Orders"
|
||||
sequence="2"
|
||||
parent="mrp.mrp_planning_menu_root"
|
||||
groups="mrp.group_mrp_routings"/>
|
||||
<menuitem id="menu_mrp_workorder_production"
|
||||
name="Planning by Production"
|
||||
sequence="1"
|
||||
action="mrp.action_mrp_workorder_production"
|
||||
parent="mrp_workorder_menu_planning"/>
|
||||
name="Planning by Production"
|
||||
sequence="1"
|
||||
action="mrp.action_mrp_workorder_production"
|
||||
parent="mrp_workorder_menu_planning"/>
|
||||
<menuitem id="menu_mrp_workorder_workcenter"
|
||||
name="Planning by Workcenter"
|
||||
sequence="2"
|
||||
action="mrp_workorder.action_mrp_workorder_dependencies_workcenter"
|
||||
parent="mrp_workorder_menu_planning"/>
|
||||
name="Planning by Workcenter"
|
||||
sequence="2"
|
||||
action="mrp_workorder.action_mrp_workorder_dependencies_workcenter"
|
||||
parent="mrp_workorder_menu_planning"/>
|
||||
</odoo>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user