解决冲突
This commit is contained in:
@@ -20,7 +20,7 @@
|
|||||||
'version': '0.1',
|
'version': '0.1',
|
||||||
|
|
||||||
# any module necessary for this one to work correctly
|
# any module necessary for this one to work correctly
|
||||||
'depends': ['base', 'account'],
|
'depends': ['base', 'account', 'l10n_cn'],
|
||||||
|
|
||||||
# always loaded
|
# always loaded
|
||||||
'data': [
|
'data': [
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from odoo import models, fields, api
|
from odoo import models, fields, api, _
|
||||||
|
|
||||||
from odoo.exceptions import ValidationError
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
@@ -7,6 +7,14 @@ class CustomAccountMoveLine(models.Model):
|
|||||||
_inherit = 'account.move'
|
_inherit = 'account.move'
|
||||||
_description = "account move line"
|
_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
|
@api.model_create_multi
|
||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
for val in vals:
|
for val in vals:
|
||||||
|
|||||||
@@ -21,8 +21,8 @@
|
|||||||
'web.assets_qweb': [
|
'web.assets_qweb': [
|
||||||
],
|
],
|
||||||
'web.assets_backend': [
|
'web.assets_backend': [
|
||||||
'jikimo_frontend/static/src/fields/custom_many2many_checkboxes/*',
|
# 'jikimo_frontend/static/src/fields/custom_many2many_checkboxes/*',
|
||||||
'jikimo_frontend/static/src/fields/Many2OneRadioField/*',
|
# 'jikimo_frontend/static/src/fields/Many2OneRadioField/*',
|
||||||
# 移除odoo相关标识
|
# 移除odoo相关标识
|
||||||
'jikimo_frontend/static/src/bye_odoo/*',
|
'jikimo_frontend/static/src/bye_odoo/*',
|
||||||
'jikimo_frontend/static/src/scss/custom_style.scss',
|
'jikimo_frontend/static/src/scss/custom_style.scss',
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
.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){
|
.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;
|
border:1px solid #dee2e6 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom_required_add::before{
|
||||||
|
content: '*';
|
||||||
|
color: red;
|
||||||
}
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
.many2one_radio_field {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
/** @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);
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
|
|
||||||
.processing-capabilities-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(6, 1fr);
|
|
||||||
gap: 10px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
/*控制图片大小*/
|
|
||||||
.item-icon {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-label {
|
|
||||||
font-size: 12px;
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
|
||||||
.processing-capabilities-grid {
|
|
||||||
grid-template-columns: repeat(4, 1fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.processing-capabilities-grid {
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.processing-capabilities-grid {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.image-preview-container {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(0, 0, 0, 0.9);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
z-index: 1000;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-preview-container.show {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-preview {
|
|
||||||
max-width: 90%;
|
|
||||||
max-height: 90%;
|
|
||||||
object-fit: contain;
|
|
||||||
box-shadow: 0 0 20px rgba(255, 255, 255, 0.2);
|
|
||||||
border-radius: 5px;
|
|
||||||
transform: scale(0.9);
|
|
||||||
transition: transform 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-preview-container.show .image-preview {
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-preview-close {
|
|
||||||
position: absolute;
|
|
||||||
top: 20px;
|
|
||||||
right: 30px;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 40px;
|
|
||||||
font-weight: bold;
|
|
||||||
transition: 0.3s;
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-preview-close:hover,
|
|
||||||
.image-preview-close:focus {
|
|
||||||
opacity: 1;
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
/** @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() {
|
|
||||||
super.setup();
|
|
||||||
}
|
|
||||||
|
|
||||||
onImageClick(event, src) {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
|
|
||||||
// 创建预览框
|
|
||||||
const previewContainer = document.createElement('div');
|
|
||||||
previewContainer.className = 'image-preview-container';
|
|
||||||
|
|
||||||
const previewImg = document.createElement('img');
|
|
||||||
previewImg.src = src;
|
|
||||||
previewImg.className = 'image-preview';
|
|
||||||
// 设置放大的预览图片大小
|
|
||||||
previewImg.style.width = '600px';
|
|
||||||
previewImg.style.height = 'auto'; // 保持宽高比
|
|
||||||
|
|
||||||
const closeButton = document.createElement('span');
|
|
||||||
closeButton.innerHTML = '×';
|
|
||||||
closeButton.className = 'image-preview-close';
|
|
||||||
|
|
||||||
previewContainer.appendChild(previewImg);
|
|
||||||
previewContainer.appendChild(closeButton);
|
|
||||||
document.body.appendChild(previewContainer);
|
|
||||||
|
|
||||||
// 添加关闭预览的事件监听器
|
|
||||||
const closePreview = () => {
|
|
||||||
previewContainer.classList.remove('show');
|
|
||||||
setTimeout(() => {
|
|
||||||
document.body.removeChild(previewContainer);
|
|
||||||
}, 300);
|
|
||||||
};
|
|
||||||
|
|
||||||
closeButton.addEventListener('click', closePreview);
|
|
||||||
|
|
||||||
// 点击预览框外部也可以关闭
|
|
||||||
previewContainer.addEventListener('click', (e) => {
|
|
||||||
if (e.target === previewContainer) {
|
|
||||||
closePreview();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 使用 setTimeout 来触发过渡效果
|
|
||||||
setTimeout(() => {
|
|
||||||
previewContainer.classList.add('show');
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MyCustomWidget.template = "jikimo_frontend.MyCustomWidget";
|
|
||||||
|
|
||||||
registry.category("fields").add("custom_many2many_checkboxes", MyCustomWidget);
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<?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 processing-capabilities-grid">
|
|
||||||
<t t-foreach="items" t-as="item" t-key="item[0]">
|
|
||||||
<div class="grid-item">
|
|
||||||
<CheckBox
|
|
||||||
value="isSelected(item)"
|
|
||||||
disabled="props.readonly"
|
|
||||||
onChange="(ev) => this.onChange(item[0], ev)"
|
|
||||||
>
|
|
||||||
<div class="item-content">
|
|
||||||
<img t-att-src="item[2]" class="item-icon" t-on-click="(ev) => this.onImageClick(ev, item[2])"/>
|
|
||||||
<span class="item-label"><t t-esc="item[1]"/></span>
|
|
||||||
</div>
|
|
||||||
</CheckBox>
|
|
||||||
</div>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</t>
|
|
||||||
|
|
||||||
</templates>
|
|
||||||
@@ -6,8 +6,9 @@ import {_t} from "@web/core/l10n/translation";
|
|||||||
import {FormStatusIndicator} from "@web/views/form/form_status_indicator/form_status_indicator";
|
import {FormStatusIndicator} from "@web/views/form/form_status_indicator/form_status_indicator";
|
||||||
import {ListRenderer} from "@web/views/list/list_renderer";
|
import {ListRenderer} from "@web/views/list/list_renderer";
|
||||||
// import {StatusBarField} from "@web/views/fields/statusbar/statusbar_field";
|
// 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 {Field} from "@web/views/fields/field";
|
|
||||||
|
|
||||||
var Dialog = require('web.Dialog');
|
var Dialog = require('web.Dialog');
|
||||||
// var {patch} = require("web.utils") 这句话也行
|
// var {patch} = require("web.utils") 这句话也行
|
||||||
@@ -51,7 +52,6 @@ const tableRequiredList = [
|
|||||||
'product_template_id', 'product_uom_qty', 'price_unit','product_id','product_qty',
|
'product_template_id', 'product_uom_qty', 'price_unit','product_id','product_qty',
|
||||||
'name', 'fault_type', 'maintenance_standards', 'Period'
|
'name', 'fault_type', 'maintenance_standards', 'Period'
|
||||||
]
|
]
|
||||||
|
|
||||||
patch(FormStatusIndicator.prototype, 'jikimo_frontend.FormStatusIndicator', {
|
patch(FormStatusIndicator.prototype, 'jikimo_frontend.FormStatusIndicator', {
|
||||||
setup() {
|
setup() {
|
||||||
owl.onMounted(() => {
|
owl.onMounted(() => {
|
||||||
@@ -107,33 +107,7 @@ 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', {
|
patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
|
||||||
setup(){
|
setup(){
|
||||||
owl.onMounted(() => {
|
owl.onMounted(() => {
|
||||||
@@ -191,7 +165,33 @@ patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
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 = {
|
// const statusbar_params = {
|
||||||
@@ -231,7 +231,6 @@ $(function () {
|
|||||||
clearInterval(timer)
|
clearInterval(timer)
|
||||||
timer = setInterval(() => {
|
timer = setInterval(() => {
|
||||||
timer_count++
|
timer_count++
|
||||||
const dom = $('.custom_required')
|
|
||||||
let tableDom = $('.table_custom_required')
|
let tableDom = $('.table_custom_required')
|
||||||
if (tableDom.length) {
|
if (tableDom.length) {
|
||||||
tableDom = tableDom.eq(0).parents('tr').children('.table_custom_required')
|
tableDom = tableDom.eq(0).parents('tr').children('.table_custom_required')
|
||||||
@@ -243,17 +242,6 @@ $(function () {
|
|||||||
})
|
})
|
||||||
clearInterval(timer)
|
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) {
|
if (timer_count == 20) {
|
||||||
clearInterval(timer)
|
clearInterval(timer)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from . import controllers
|
|
||||||
from . import models
|
from . import models
|
||||||
from . import wizards
|
from . import wizards
|
||||||
|
|||||||
@@ -24,9 +24,6 @@
|
|||||||
|
|
||||||
# always loaded
|
# always loaded
|
||||||
'data': [
|
'data': [
|
||||||
'security/ir.model.access.csv',
|
|
||||||
'data/documents_data.xml',
|
|
||||||
'wizards/upload_file_wizard_view.xml',
|
|
||||||
'views/views.xml',
|
'views/views.xml',
|
||||||
],
|
],
|
||||||
# only loaded in demonstration mode
|
# only loaded in demonstration mode
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from . import controllers
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# from odoo import http
|
|
||||||
|
|
||||||
|
|
||||||
# class JikimoPurchaseTierValidation(http.Controller):
|
|
||||||
# @http.route('/jikimo_purchase_tier_validation/jikimo_purchase_tier_validation', auth='public')
|
|
||||||
# def index(self, **kw):
|
|
||||||
# return "Hello, world"
|
|
||||||
|
|
||||||
# @http.route('/jikimo_purchase_tier_validation/jikimo_purchase_tier_validation/objects', auth='public')
|
|
||||||
# def list(self, **kw):
|
|
||||||
# return http.request.render('jikimo_purchase_tier_validation.listing', {
|
|
||||||
# 'root': '/jikimo_purchase_tier_validation/jikimo_purchase_tier_validation',
|
|
||||||
# 'objects': http.request.env['jikimo_purchase_tier_validation.jikimo_purchase_tier_validation'].search([]),
|
|
||||||
# })
|
|
||||||
|
|
||||||
# @http.route('/jikimo_purchase_tier_validation/jikimo_purchase_tier_validation/objects/<model("jikimo_purchase_tier_validation.jikimo_purchase_tier_validation"):obj>', auth='public')
|
|
||||||
# def object(self, obj, **kw):
|
|
||||||
# return http.request.render('jikimo_purchase_tier_validation.object', {
|
|
||||||
# 'object': obj
|
|
||||||
# })
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<odoo>
|
|
||||||
<data noupdate="1">
|
|
||||||
<!-- 创建采购合同文件夹 -->
|
|
||||||
<record id="documents_purchase_contracts_folder" model="documents.folder">
|
|
||||||
<field name="name">采购合同</field>
|
|
||||||
<field name="description">存放采购合同相关文件</field>
|
|
||||||
<field name="sequence">10</field>
|
|
||||||
</record>
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<odoo>
|
|
||||||
<data>
|
|
||||||
<!--
|
|
||||||
<record id="object0" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
|
|
||||||
<field name="name">Object 0</field>
|
|
||||||
<field name="value">0</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="object1" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
|
|
||||||
<field name="name">Object 1</field>
|
|
||||||
<field name="value">10</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="object2" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
|
|
||||||
<field name="name">Object 2</field>
|
|
||||||
<field name="value">20</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="object3" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
|
|
||||||
<field name="name">Object 3</field>
|
|
||||||
<field name="value">30</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="object4" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
|
|
||||||
<field name="name">Object 4</field>
|
|
||||||
<field name="value">40</field>
|
|
||||||
</record>
|
|
||||||
-->
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
||||||
@@ -21,12 +21,8 @@ class jikimo_purchase_tier_validation(models.Model):
|
|||||||
|
|
||||||
def button_confirm(self):
|
def button_confirm(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
# if record.need_validation and record.validation_status != 'validated':
|
|
||||||
# raise ValidationError(_('此操作需要至少对一条记录进行审批。\n请发起审批申请。'))
|
|
||||||
if record.state in ['to approve']:
|
if record.state in ['to approve']:
|
||||||
raise ValidationError(_('请先完成审批。'))
|
raise ValidationError(_('请先完成审批。'))
|
||||||
# if record.state == 'approved':
|
|
||||||
# record.state = 'purchase'
|
|
||||||
res = super(jikimo_purchase_tier_validation, self).button_confirm()
|
res = super(jikimo_purchase_tier_validation, self).button_confirm()
|
||||||
for record in self:
|
for record in self:
|
||||||
if record.state == 'approved':
|
if record.state == 'approved':
|
||||||
@@ -39,45 +35,8 @@ class jikimo_purchase_tier_validation(models.Model):
|
|||||||
record.message_subscribe([record.partner_id.id])
|
record.message_subscribe([record.partner_id.id])
|
||||||
return res
|
return res
|
||||||
|
|
||||||
# def button_confirm(self):
|
|
||||||
# self = self.with_context(skip_validation=True)
|
|
||||||
# return super().button_confirm()
|
|
||||||
#
|
|
||||||
# def _check_state_conditions(self, vals):
|
|
||||||
# self.ensure_one()
|
|
||||||
# if self._context.get('skip_validation'):
|
|
||||||
# return False
|
|
||||||
# return (
|
|
||||||
# self._check_state_from_condition()
|
|
||||||
# and vals.get(self._state_field) in self._state_to
|
|
||||||
# )
|
|
||||||
|
|
||||||
def request_validation(self):
|
def request_validation(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
error_messages = []
|
|
||||||
|
|
||||||
# 检查必填字段
|
|
||||||
required_fields = {
|
|
||||||
'partner_ref': '合同名称',
|
|
||||||
'contract_number': '合同编号'
|
|
||||||
}
|
|
||||||
|
|
||||||
missing_fields = [
|
|
||||||
name for field, name in required_fields.items()
|
|
||||||
if not record[field]
|
|
||||||
]
|
|
||||||
|
|
||||||
if missing_fields:
|
|
||||||
error_messages.append('* 如下字段要求必须填写:%s' % '、'.join(missing_fields))
|
|
||||||
|
|
||||||
# 检查合同文件
|
|
||||||
if not record.contract_document_id:
|
|
||||||
error_messages.append('* 必须点击上传合同文件')
|
|
||||||
|
|
||||||
# 如果有任何错误,一次性显示所有错误信息
|
|
||||||
if error_messages:
|
|
||||||
raise ValidationError('\n'.join(error_messages))
|
|
||||||
|
|
||||||
# 添加通知消息
|
# 添加通知消息
|
||||||
if hasattr(record, 'message_post'):
|
if hasattr(record, 'message_post'):
|
||||||
current_user = self.env.user.name
|
current_user = self.env.user.name
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
|
||||||
access_ir_attachment_wizard,ir.attachment.wizard,model_ir_attachment_wizard,base.group_user,1,1,1,1
|
|
||||||
|
@@ -1,24 +0,0 @@
|
|||||||
<odoo>
|
|
||||||
<data>
|
|
||||||
<!--
|
|
||||||
<template id="listing">
|
|
||||||
<ul>
|
|
||||||
<li t-foreach="objects" t-as="object">
|
|
||||||
<a t-attf-href="#{ root }/objects/#{ object.id }">
|
|
||||||
<t t-esc="object.display_name"/>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
<template id="object">
|
|
||||||
<h1><t t-esc="object.display_name"/></h1>
|
|
||||||
<dl>
|
|
||||||
<t t-foreach="object._fields" t-as="field">
|
|
||||||
<dt><t t-esc="field"/></dt>
|
|
||||||
<dd><t t-esc="object[field]"/></dd>
|
|
||||||
</t>
|
|
||||||
</dl>
|
|
||||||
</template>
|
|
||||||
-->
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
||||||
@@ -23,76 +23,10 @@
|
|||||||
<xpath expr="//header/field[@name='state']" position="replace">
|
<xpath expr="//header/field[@name='state']" position="replace">
|
||||||
<field name="state" widget="statusbar" statusbar_visible="draft,sent,to approve, approved, purchase" readonly="1"/>
|
<field name="state" widget="statusbar" statusbar_visible="draft,sent,to approve, approved, purchase" readonly="1"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
|
|
||||||
<xpath expr="//header/button[last()]" position="after">
|
<xpath expr="//header/button[last()]" position="after">
|
||||||
<button name="button_cancel" states="draft,to approve,sent,purchase" string="取消" type="object" data-hotkey="x" />
|
<button name="button_cancel" states="draft,to approve,sent,purchase" string="取消" type="object" data-hotkey="x" />
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
<xpath expr="//header/button[@name='action_rfq_send'][1]" position="before">
|
|
||||||
<field name="validation_status" invisible="1"/>
|
|
||||||
<field name="is_upload_contract_file" invisible="1"/>
|
|
||||||
<button name="upload_contract_file" string="上传合同" type="object" class="oe_highlight" attrs="{'invisible': ['|', '|', ('validation_status', '!=', 'no'), ('is_upload_contract_file', '=', True), ('state', 'not in', ['draft', 'sent'])]}"/>]}"/>
|
|
||||||
<button name="delete_contract_file" string="删除合同" type="object" class="oe_highlight" attrs="{'invisible': ['|', ('validation_status', '!=', 'no'), ('is_upload_contract_file', '=', False)]}"/>
|
|
||||||
</xpath>
|
|
||||||
<xpath expr="//notebook/page[1]" position="before">
|
|
||||||
<page string="合同" name="contract_documents"
|
|
||||||
attrs="{'invisible': [('contract_document_id', '=', False)]}"
|
|
||||||
autofocus="autofocus">
|
|
||||||
<group>
|
|
||||||
<group>
|
|
||||||
<field name="contract_document_id" invisible="1"/>
|
|
||||||
<field name="contract_file_name" invisible="1"/>
|
|
||||||
<field name="contract_file"
|
|
||||||
widget="adaptive_viewer"
|
|
||||||
filename="contract_file_name"/>
|
|
||||||
</group>
|
|
||||||
</group>
|
|
||||||
</page>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<!-- actions opening views on models -->
|
|
||||||
<!--
|
|
||||||
<record model="ir.actions.act_window" id="jikimo_purchase_tier_validation.action_window">
|
|
||||||
<field name="name">jikimo_purchase_tier_validation window</field>
|
|
||||||
<field name="res_model">jikimo_purchase_tier_validation.jikimo_purchase_tier_validation</field>
|
|
||||||
<field name="view_mode">tree,form</field>
|
|
||||||
</record>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- server action to the one above -->
|
|
||||||
<!--
|
|
||||||
<record model="ir.actions.server" id="jikimo_purchase_tier_validation.action_server">
|
|
||||||
<field name="name">jikimo_purchase_tier_validation server</field>
|
|
||||||
<field name="model_id" ref="model_jikimo_purchase_tier_validation_jikimo_purchase_tier_validation"/>
|
|
||||||
<field name="state">code</field>
|
|
||||||
<field name="code">
|
|
||||||
action = {
|
|
||||||
"type": "ir.actions.act_window",
|
|
||||||
"view_mode": "tree,form",
|
|
||||||
"res_model": model._name,
|
|
||||||
}
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- Top menu item -->
|
|
||||||
<!--
|
|
||||||
<menuitem name="jikimo_purchase_tier_validation" id="jikimo_purchase_tier_validation.menu_root"/>
|
|
||||||
-->
|
|
||||||
<!-- menu categories -->
|
|
||||||
<!--
|
|
||||||
<menuitem name="Menu 1" id="jikimo_purchase_tier_validation.menu_1" parent="jikimo_purchase_tier_validation.menu_root"/>
|
|
||||||
<menuitem name="Menu 2" id="jikimo_purchase_tier_validation.menu_2" parent="jikimo_purchase_tier_validation.menu_root"/>
|
|
||||||
-->
|
|
||||||
<!-- actions -->
|
|
||||||
<!--
|
|
||||||
<menuitem name="List" id="jikimo_purchase_tier_validation.menu_1_list" parent="jikimo_purchase_tier_validation.menu_1"
|
|
||||||
action="jikimo_purchase_tier_validation.action_window"/>
|
|
||||||
<menuitem name="Server to list" id="jikimo_purchase_tier_validation" parent="jikimo_purchase_tier_validation.menu_2"
|
|
||||||
action="jikimo_purchase_tier_validation.action_server"/>
|
|
||||||
-->
|
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
@@ -1,2 +1 @@
|
|||||||
from . import upload_file_wizard
|
from . import comment_wizard
|
||||||
from . import comment_wizard
|
|
||||||
|
|||||||
@@ -1,114 +0,0 @@
|
|||||||
from odoo import models, fields, api, _
|
|
||||||
|
|
||||||
|
|
||||||
class IrAttachmentWizard(models.TransientModel):
|
|
||||||
_name = 'ir.attachment.wizard'
|
|
||||||
_description = '文件上传向导'
|
|
||||||
|
|
||||||
attachment = fields.Binary(string='选择文件', required=True)
|
|
||||||
filename = fields.Char(string='文件名')
|
|
||||||
res_model = fields.Char()
|
|
||||||
res_id = fields.Integer()
|
|
||||||
|
|
||||||
# def action_upload_file(self):
|
|
||||||
# self.ensure_one()
|
|
||||||
# # 首先创建 ir.attachment
|
|
||||||
# attachment = self.env['ir.attachment'].create({
|
|
||||||
# 'name': self.filename,
|
|
||||||
# 'type': 'binary',
|
|
||||||
# 'datas': self.attachment,
|
|
||||||
# 'res_model': self.res_model,
|
|
||||||
# 'res_id': self.res_id,
|
|
||||||
# })
|
|
||||||
#
|
|
||||||
# # 获取默认的文档文件夹
|
|
||||||
# workspace = self.env['documents.folder'].search([('name', '=', '采购合同')], limit=1)
|
|
||||||
#
|
|
||||||
# # 创建 documents.document 记录
|
|
||||||
# document = self.env['documents.document'].create({
|
|
||||||
# 'name': self.filename,
|
|
||||||
# 'attachment_id': attachment.id,
|
|
||||||
# 'folder_id': workspace.id,
|
|
||||||
# 'res_model': self.res_model,
|
|
||||||
# 'res_id': self.res_id,
|
|
||||||
# })
|
|
||||||
#
|
|
||||||
# return {
|
|
||||||
# 'type': 'ir.actions.client',
|
|
||||||
# 'tag': 'display_notification',
|
|
||||||
# 'params': {
|
|
||||||
# 'title': _('成功'),
|
|
||||||
# 'message': _('文件上传成功'),
|
|
||||||
# 'type': 'success',
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
|
|
||||||
def action_upload_file(self):
|
|
||||||
self.ensure_one()
|
|
||||||
# 获取当前用户的 partner_id
|
|
||||||
current_partner = self.env.user.partner_id
|
|
||||||
# 首先创建 ir.attachment
|
|
||||||
attachment = self.env['ir.attachment'].create({
|
|
||||||
'name': self.filename,
|
|
||||||
'type': 'binary',
|
|
||||||
'datas': self.attachment,
|
|
||||||
'res_model': self.res_model,
|
|
||||||
'res_id': self.res_id,
|
|
||||||
})
|
|
||||||
|
|
||||||
# 获取默认的文档文件夹
|
|
||||||
workspace = self.env['documents.folder'].search([('name', '=', '采购合同')], limit=1)
|
|
||||||
|
|
||||||
# 创建 documents.document 记录
|
|
||||||
document = self.env['documents.document'].create({
|
|
||||||
'name': self.filename,
|
|
||||||
'attachment_id': attachment.id,
|
|
||||||
'folder_id': workspace.id,
|
|
||||||
'res_model': self.res_model,
|
|
||||||
'res_id': self.res_id,
|
|
||||||
'partner_id': current_partner.id,
|
|
||||||
})
|
|
||||||
|
|
||||||
# 更新采购订单的合同文档字段
|
|
||||||
purchase_order = self.env['purchase.order'].browse(self.res_id)
|
|
||||||
purchase_order.write({
|
|
||||||
'contract_document_id': document.id,
|
|
||||||
'is_upload_contract_file': True
|
|
||||||
})
|
|
||||||
|
|
||||||
# 显示成功消息并关闭向导
|
|
||||||
message = {
|
|
||||||
'type': 'ir.actions.client',
|
|
||||||
'tag': 'display_notification',
|
|
||||||
'params': {
|
|
||||||
'title': _('成功'),
|
|
||||||
'message': _('文件上传成功'),
|
|
||||||
'type': 'success',
|
|
||||||
'sticky': False, # 自动消失
|
|
||||||
'next': {
|
|
||||||
'type': 'ir.actions.act_window_close'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return message
|
|
||||||
|
|
||||||
|
|
||||||
# def action_upload_file(self):
|
|
||||||
# self.ensure_one()
|
|
||||||
# attachment = self.env['ir.attachment'].create({
|
|
||||||
# 'name': self.filename,
|
|
||||||
# 'type': 'binary',
|
|
||||||
# 'datas': self.attachment,
|
|
||||||
# 'res_model': self.res_model,
|
|
||||||
# 'res_id': self.res_id,
|
|
||||||
# })
|
|
||||||
# return {
|
|
||||||
# 'type': 'ir.actions.client',
|
|
||||||
# 'tag': 'display_notification',
|
|
||||||
# 'params': {
|
|
||||||
# 'title': _('成功'),
|
|
||||||
# 'message': _('文件上传成功'),
|
|
||||||
# 'type': 'success',
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<odoo>
|
|
||||||
<record id="view_upload_file_wizard_form" model="ir.ui.view">
|
|
||||||
<field name="name">ir.attachment.wizard.form</field>
|
|
||||||
<field name="model">ir.attachment.wizard</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<form string="上传文件">
|
|
||||||
<group>
|
|
||||||
<field name="attachment" widget="binary" filename="filename" options="{'accepted_file_extensions': '.pdf,.doc,.docx,.jpg,.jpeg,.png'}"/>
|
|
||||||
<field name="filename" invisible="1"/>
|
|
||||||
<field name="res_model" invisible="1"/>
|
|
||||||
<field name="res_id" invisible="1"/>
|
|
||||||
</group>
|
|
||||||
<footer>
|
|
||||||
<button name="action_upload_file" string="确认上传" type="object" class="btn-primary"/>
|
|
||||||
<button string="取消" class="btn-secondary" special="cancel"/>
|
|
||||||
</footer>
|
|
||||||
</form>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
</odoo>
|
|
||||||
@@ -9,6 +9,7 @@ function getDomData() {
|
|||||||
table.hide()
|
table.hide()
|
||||||
const thead = customTable.children('thead')
|
const thead = customTable.children('thead')
|
||||||
const tbody = customTable.children('tbody')
|
const tbody = customTable.children('tbody')
|
||||||
|
const tfooter = customTable.children('tfoot')
|
||||||
const tableData = []
|
const tableData = []
|
||||||
const tbody_child = tbody.children()
|
const tbody_child = tbody.children()
|
||||||
|
|
||||||
@@ -16,30 +17,29 @@ function getDomData() {
|
|||||||
|
|
||||||
for (let v = 0; v < tbody_child_len; v++) { // 将数据取出来到tableData里面
|
for (let v = 0; v < tbody_child_len; v++) { // 将数据取出来到tableData里面
|
||||||
const data = tbody_child[v].innerText.split('\t')
|
const data = tbody_child[v].innerText.split('\t')
|
||||||
// console.log('dom data',data)
|
|
||||||
const [index, deep, name, Φ, value] = data
|
const [index, deep, name, Φ, value] = data
|
||||||
tableData.push({index, deep, name, Φ, value})
|
tableData.push({ index, deep, name, Φ, value })
|
||||||
}
|
}
|
||||||
const ΦList = [...new Set(tableData.map(_ => _.name))] // ΦList去重
|
const ΦList = [...new Set(tableData.map(_ => _.Φ))] // ΦList去重
|
||||||
const newTableData = {}
|
const newTableData = {}
|
||||||
tableData.forEach(_ => {
|
tableData.forEach(_ => {
|
||||||
const key = _.deep + '|' + _.Φ
|
const key = _.deep + '|' + _.name
|
||||||
!newTableData[key] ? newTableData[key] = {i: _.index} : '';
|
!newTableData[key] ? newTableData[key] = { i: _.index } : '';
|
||||||
if (_.Φ) { // 去除没有Φ的脏数据
|
if (_.Φ) { // 去除没有Φ的脏数据
|
||||||
newTableData[key]['Φ' + _.Φ] = _.value
|
newTableData[key]['Φ' + _.Φ] = _.value
|
||||||
newTableData[key]['Φ' + _.Φ + 'i'] = _.index
|
newTableData[key]['Φ' + _.Φ + 'i'] = _.index
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// console.log('qwdh',tableData, ΦList, newTableData);
|
// console.log(tableData, ΦList, newTableData);
|
||||||
|
|
||||||
if (ΦList.filter(_ => _).length == 0) return;
|
if (ΦList.filter(_ => _).length == 0) return;
|
||||||
handleThead(thead, ΦList)
|
handleThead(thead, ΦList, tfooter)
|
||||||
|
|
||||||
handleTbody(tbody, newTableData, ΦList, table)
|
handleTbody(tbody, newTableData, ΦList, table )
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重新设置表头、
|
// 重新设置表头、
|
||||||
function handleThead(thead, ΦList) {
|
function handleThead(thead, ΦList, tfooter) {
|
||||||
const dom = thead.children().eq(0).children()
|
const dom = thead.children().eq(0).children()
|
||||||
const len = dom.length
|
const len = dom.length
|
||||||
dom.eq(0).attr('rowspan', 2)
|
dom.eq(0).attr('rowspan', 2)
|
||||||
@@ -47,7 +47,11 @@ function handleThead(thead, ΦList) {
|
|||||||
len == 5 ? dom.eq(2).attr('rowspan', 2) : ''
|
len == 5 ? dom.eq(2).attr('rowspan', 2) : ''
|
||||||
dom.eq(-2).attr('colspan', ΦList.length)
|
dom.eq(-2).attr('colspan', ΦList.length)
|
||||||
dom.eq(-1).remove()
|
dom.eq(-1).remove()
|
||||||
|
if(tfooter && tfooter.length) {
|
||||||
|
tfooter.children().each(function () {
|
||||||
|
$(this).children().eq(-1).remove()
|
||||||
|
})
|
||||||
|
}
|
||||||
const tr = document.createElement('tr')
|
const tr = document.createElement('tr')
|
||||||
for (let v = 0; v < ΦList.length; v++) {
|
for (let v = 0; v < ΦList.length; v++) {
|
||||||
const th = document.createElement('th')
|
const th = document.createElement('th')
|
||||||
@@ -68,7 +72,6 @@ function handleTbody(tbody, newTableData, ΦList, table) {
|
|||||||
// b = b.split('=')[1].split('%')[0]
|
// b = b.split('=')[1].split('%')[0]
|
||||||
// return a - b
|
// return a - b
|
||||||
// })
|
// })
|
||||||
// console.log('wqoqw ',ΦList)
|
|
||||||
data.forEach(_ => {
|
data.forEach(_ => {
|
||||||
i++
|
i++
|
||||||
const tr = $('<tr class="o_data_row"></tr>')
|
const tr = $('<tr class="o_data_row"></tr>')
|
||||||
@@ -98,61 +101,6 @@ function handleTbody(tbody, newTableData, ΦList, table) {
|
|||||||
// // }
|
// // }
|
||||||
tbody.append(tr)
|
tbody.append(tr)
|
||||||
})
|
})
|
||||||
// $(document).click(function (e) {
|
|
||||||
// if ($(e.target).attr('coustomTd')) {
|
|
||||||
// const orginV = $('[coustomInput=1]').children('input').val()
|
|
||||||
// $('[coustomInput=1]').parent().html(orginV)
|
|
||||||
// const v = $(e.target).attr('val')
|
|
||||||
// console.log($(e.target));
|
|
||||||
// $(e.target).html('')
|
|
||||||
// const input = $('<div coustomInput="1" name="feed_per_tooth" class="o_field_widget o_field_char"><input class="o_input" type="text" autocomplete="off" maxlength="20"></div>')
|
|
||||||
// input.children('input').val(v)
|
|
||||||
// $(e.target).append(input)
|
|
||||||
// input.children('input').focus()
|
|
||||||
// input.children('input').select()
|
|
||||||
// } else if ($(e.target).attr('coustomInput')) {
|
|
||||||
//
|
|
||||||
// } else {
|
|
||||||
// const orginV = $('[coustomInput=1]').children('input').val()
|
|
||||||
// $('[coustomInput=1]').parent().html(orginV)
|
|
||||||
// const v = $(e.target).attr('val')
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// $(document).off('change') // 防止重复绑定
|
|
||||||
// $(document).on('change', '[coustomInput] input', function () {
|
|
||||||
// $(this).parents('td').attr('val', $(this).val());
|
|
||||||
// var eve1 = new Event('change');
|
|
||||||
// var eve2 = new Event('input');
|
|
||||||
// var eve3 = new Event('click');
|
|
||||||
// const i = $(this).parents('td').attr('col');
|
|
||||||
// let patchDom = table.find('tbody').children('tr').eq(i - 1);
|
|
||||||
//
|
|
||||||
// if (patchDom.length === 0) {
|
|
||||||
// console.error('No such row found');
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// patchDom = patchDom.children().eq(-1);
|
|
||||||
//
|
|
||||||
// setTimeout(() => {
|
|
||||||
// if (patchDom.length === 0) {
|
|
||||||
// console.error('No such cell found');
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// patchDom[0].dispatchEvent(eve3); // Simulate click event
|
|
||||||
//
|
|
||||||
// setTimeout(() => {
|
|
||||||
// patchDom = patchDom.find('input');
|
|
||||||
// if (patchDom.length === 0) {
|
|
||||||
// console.error('No input found in the target cell');
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// patchDom.val($(this).val());
|
|
||||||
// patchDom[0].dispatchEvent(eve2);
|
|
||||||
// patchDom[0].dispatchEvent(eve1);
|
|
||||||
// }, 200);
|
|
||||||
// }, 500);
|
|
||||||
// });
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,4 +24,18 @@
|
|||||||
|
|
||||||
.o_search_panel.account_root {
|
.o_search_panel.account_root {
|
||||||
flex: unset !important;
|
flex: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.multi-line {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
> label.o_form_label {
|
||||||
|
width: 52px;
|
||||||
|
}
|
||||||
|
> span {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
> div {
|
||||||
|
flex: 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -138,7 +138,7 @@
|
|||||||
<field name="brand_id" required="1"/>
|
<field name="brand_id" required="1"/>
|
||||||
<label for="integral_run_out_accuracy_min" string="端跳精度"
|
<label for="integral_run_out_accuracy_min" string="端跳精度"
|
||||||
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')]}"/>
|
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')]}"/>
|
||||||
<div class="o_address_format"
|
<div class="o_address_format multi-line"
|
||||||
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')]}">
|
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')]}">
|
||||||
<label for="integral_run_out_accuracy_min" string="最小"/>
|
<label for="integral_run_out_accuracy_min" string="最小"/>
|
||||||
<field name="integral_run_out_accuracy_min" class="o_address_zip"
|
<field name="integral_run_out_accuracy_min" class="o_address_zip"
|
||||||
@@ -177,33 +177,33 @@
|
|||||||
</group>
|
</group>
|
||||||
<group string="适配刀片形状"
|
<group string="适配刀片形状"
|
||||||
attrs="{'invisible': [('cutting_tool_type', 'in', ('刀柄','夹头','整体式刀具',False))]}">
|
attrs="{'invisible': [('cutting_tool_type', 'in', ('刀柄','夹头','整体式刀具',False))]}">
|
||||||
<field name="fit_blade_shape_id" string="" widget="many2one_radio"/>
|
<field name="fit_blade_shape_id" string="" widget="many2one_radio" attrs="{'showExpand': True}"/>
|
||||||
</group>
|
</group>
|
||||||
<group string="适合加工方式"
|
<group string="适合加工方式"
|
||||||
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
|
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
|
||||||
<field name="suitable_machining_method_ids" string=""
|
<field name="suitable_machining_method_ids" string=""
|
||||||
widget="custom_many2many_checkboxes"/>
|
widget="custom_many2many_checkboxes" attrs="{'showExpand': True}"/>
|
||||||
</group>
|
</group>
|
||||||
<group string="刀尖特征"
|
<group string="刀尖特征"
|
||||||
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
|
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
|
||||||
<field name="blade_tip_characteristics_id" string=""
|
<field name="blade_tip_characteristics_id" string=""
|
||||||
widget="many2one_radio"/>
|
widget="many2one_radio" attrs="{'showExpand': True}"/>
|
||||||
</group>
|
</group>
|
||||||
<group attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
|
<group attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
|
||||||
<group string="柄部类型" attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')]}">
|
<group string="柄部类型" attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')]}">
|
||||||
<field name="handle_type_id" string="" widget="many2one_radio"/>
|
<field name="handle_type_id" string="" widget="many2one_radio" attrs="{'showExpand': True}"/>
|
||||||
</group>
|
</group>
|
||||||
<group string="压紧方式"
|
<group string="压紧方式"
|
||||||
attrs="{'invisible': [('cutting_tool_type', 'not in', ('刀杆','刀盘'))]}">
|
attrs="{'invisible': [('cutting_tool_type', 'not in', ('刀杆','刀盘'))]}">
|
||||||
<field name="compaction_way_id" string="" widget="many2one_radio"/>
|
<field name="compaction_way_id" string="" widget="many2one_radio" attrs="{'showExpand': True}"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<group attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
|
<group attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
|
||||||
<group string="走刀方向">
|
<group string="走刀方向">
|
||||||
<field name="cutting_direction_ids" string="" widget="custom_many2many_checkboxes"/>
|
<field name="cutting_direction_ids" string="" widget="custom_many2many_checkboxes" attrs="{'showExpand': True}"/>
|
||||||
</group>
|
</group>
|
||||||
<group string="适合冷却方式">
|
<group string="适合冷却方式">
|
||||||
<field name="suitable_coolant_ids" string="" widget="custom_many2many_checkboxes"/>
|
<field name="suitable_coolant_ids" string="" widget="custom_many2many_checkboxes" attrs="{'showExpand': True}" />
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<notebook>
|
<notebook>
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class StatusChange(models.Model):
|
|||||||
logging.info('函数已经执行=============')
|
logging.info('函数已经执行=============')
|
||||||
|
|
||||||
# 使用super()来调用原始方法(在本例中为'sale.order'模型的'action_cancel'方法)
|
# 使用super()来调用原始方法(在本例中为'sale.order'模型的'action_cancel'方法)
|
||||||
res = super(StatusChange, self).action_cancel()
|
res = super(StatusChange, self.with_context(disable_cancel_warning=True)).action_cancel()
|
||||||
|
|
||||||
# 原有方法执行后,进行额外的操作(如调用外部API)
|
# 原有方法执行后,进行额外的操作(如调用外部API)
|
||||||
logging.info('函数已经执行=============2')
|
logging.info('函数已经执行=============2')
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
'wizard/production_technology_re_adjust_wizard_views.xml',
|
'wizard/production_technology_re_adjust_wizard_views.xml',
|
||||||
'wizard/mrp_workorder_batch_replan_wizard_views.xml',
|
'wizard/mrp_workorder_batch_replan_wizard_views.xml',
|
||||||
'wizard/sf_programming_reason_views.xml',
|
'wizard/sf_programming_reason_views.xml',
|
||||||
|
'wizard/sale_order_cancel_views.xml',
|
||||||
'views/mrp_views_menus.xml',
|
'views/mrp_views_menus.xml',
|
||||||
'views/agv_scheduling_views.xml',
|
'views/agv_scheduling_views.xml',
|
||||||
'views/stock_lot_views.xml',
|
'views/stock_lot_views.xml',
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ class MrpProduction(models.Model):
|
|||||||
programming_no = fields.Char('编程单号')
|
programming_no = fields.Char('编程单号')
|
||||||
work_state = fields.Char('业务状态')
|
work_state = fields.Char('业务状态')
|
||||||
programming_state = fields.Selection(
|
programming_state = fields.Selection(
|
||||||
[('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'), ('已下发', '已下发')],
|
[('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'), ('已下发', '已下发'), ('已取消', '已取消')],
|
||||||
string='编程状态',
|
string='编程状态',
|
||||||
tracking=True)
|
tracking=True)
|
||||||
glb_file = fields.Binary("glb模型文件")
|
glb_file = fields.Binary("glb模型文件")
|
||||||
@@ -646,6 +646,28 @@ class MrpProduction(models.Model):
|
|||||||
logging.info('update_programming_state error:%s' % e)
|
logging.info('update_programming_state error:%s' % e)
|
||||||
raise UserError("更新编程单状态失败,请联系管理员")
|
raise UserError("更新编程单状态失败,请联系管理员")
|
||||||
|
|
||||||
|
# 修改编程单状态
|
||||||
|
def _change_programming_state(self):
|
||||||
|
try:
|
||||||
|
res = {"programming_no": self.programming_no, "state": "已取消"}
|
||||||
|
logging.info('res=%s:' % res)
|
||||||
|
configsettings = self.env['res.config.settings'].get_values()
|
||||||
|
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
|
||||||
|
url = '/api/intelligent_programming/set_state'
|
||||||
|
config_url = configsettings['sf_url'] + url
|
||||||
|
ret = requests.post(config_url, json=res, data=None, headers=config_header)
|
||||||
|
ret = ret.json()
|
||||||
|
result = json.loads(ret['result'])
|
||||||
|
logging.info('change_programming_state-ret:%s' % result)
|
||||||
|
if result['status'] == 1:
|
||||||
|
self.write({'programming_state': '已取消'})
|
||||||
|
else:
|
||||||
|
raise UserError(ret['message'])
|
||||||
|
except Exception as e:
|
||||||
|
logging.info('change_programming_state error:%s' % e)
|
||||||
|
raise UserError("修改编程单状态失败,请联系管理员")
|
||||||
|
|
||||||
|
|
||||||
# cnc程序获取
|
# cnc程序获取
|
||||||
def fetchCNC(self, production_names):
|
def fetchCNC(self, production_names):
|
||||||
cnc = self.env['mrp.production'].search([('id', '=', self.id)])
|
cnc = self.env['mrp.production'].search([('id', '=', self.id)])
|
||||||
@@ -1707,7 +1729,7 @@ class sf_programming_record(models.Model):
|
|||||||
programming_method = fields.Selection([
|
programming_method = fields.Selection([
|
||||||
('auto', '自动'),
|
('auto', '自动'),
|
||||||
('manual operation', '人工')], string="编程方式")
|
('manual operation', '人工')], string="编程方式")
|
||||||
current_programming_count = fields.Integer('当前编程次数')
|
current_programming_count = fields.Integer('重新编程次数')
|
||||||
target_production_id = fields.Char('目标制造单号')
|
target_production_id = fields.Char('目标制造单号')
|
||||||
apply_time = fields.Datetime('申请时间')
|
apply_time = fields.Datetime('申请时间')
|
||||||
send_time = fields.Datetime('下发时间')
|
send_time = fields.Datetime('下发时间')
|
||||||
|
|||||||
@@ -1087,7 +1087,8 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
if (workorder.production_id.production_type == '人工线下加工'
|
if (workorder.production_id.production_type == '人工线下加工'
|
||||||
and workorder.production_id.schedule_state == '已排'
|
and workorder.production_id.schedule_state == '已排'
|
||||||
and len(workorder.production_id.picking_ids.filtered(
|
and len(workorder.production_id.picking_ids.filtered(
|
||||||
lambda w: w.state not in ['done', 'cancel'])) == 0):
|
lambda w: w.state not in ['done', 'cancel'])) == 0
|
||||||
|
and workorder.production_id.programming_state == '已编程'):
|
||||||
if workorder.is_subcontract is True:
|
if workorder.is_subcontract is True:
|
||||||
purchase_orders_id = self._get_surface_technics_purchase_ids()
|
purchase_orders_id = self._get_surface_technics_purchase_ids()
|
||||||
if purchase_orders_id.state == 'purchase':
|
if purchase_orders_id.state == 'purchase':
|
||||||
|
|||||||
@@ -151,6 +151,23 @@ class SaleOrder(models.Model):
|
|||||||
product_bom_purchase.with_user(self.env.ref("base.user_admin")).bom_create_line_has(
|
product_bom_purchase.with_user(self.env.ref("base.user_admin")).bom_create_line_has(
|
||||||
purchase_embryo)
|
purchase_embryo)
|
||||||
return super(SaleOrder, self).action_confirm()
|
return super(SaleOrder, self).action_confirm()
|
||||||
|
|
||||||
|
def action_show_cancel_wizard(self):
|
||||||
|
wizard = self.env['sf.sale.order.cancel.wizard'].create({
|
||||||
|
'order_id': self.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
# 创建关联单据行
|
||||||
|
self.env['sf.sale.order.cancel.line'].create_from_order(wizard.id, self)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'name': '取消销售订单',
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'res_model': 'sf.sale.order.cancel.wizard',
|
||||||
|
'view_mode': 'form',
|
||||||
|
'target': 'new',
|
||||||
|
'res_id': wizard.id,
|
||||||
|
}
|
||||||
|
|
||||||
class SaleOrderLine(models.Model):
|
class SaleOrderLine(models.Model):
|
||||||
_inherit = 'sale.order.line'
|
_inherit = 'sale.order.line'
|
||||||
|
|||||||
@@ -611,6 +611,18 @@ class StockPicking(models.Model):
|
|||||||
return sequence_id
|
return sequence_id
|
||||||
|
|
||||||
def button_validate(self):
|
def button_validate(self):
|
||||||
|
# 校验“收料入库单、客供料入库单”是否已经分配序列号,如果没有分配则自动分配
|
||||||
|
if self.picking_type_id.use_existing_lots is False and self.picking_type_id.use_create_lots is True:
|
||||||
|
for move in self.move_ids:
|
||||||
|
if not move.move_line_nosuggest_ids:
|
||||||
|
move.action_show_details()
|
||||||
|
else:
|
||||||
|
# 对已经生成的序列号做唯一性校验,如果重复则重新生成新的序列号
|
||||||
|
line_lot_name = [line_id.lot_name for line_id in move.move_line_nosuggest_ids]
|
||||||
|
lot_ids = self.env['stock.lot'].sudo().search([('name', 'in', line_lot_name)])
|
||||||
|
if lot_ids:
|
||||||
|
move.action_clear_lines_show_details()
|
||||||
|
move.action_show_details()
|
||||||
res = super().button_validate()
|
res = super().button_validate()
|
||||||
picking_type_in = self.env.ref('sf_manufacturing.outcontract_picking_in').id
|
picking_type_in = self.env.ref('sf_manufacturing.outcontract_picking_in').id
|
||||||
if res is True and self.picking_type_id.id == picking_type_in:
|
if res is True and self.picking_type_id.id == picking_type_in:
|
||||||
@@ -844,7 +856,8 @@ class ReStockMove(models.Model):
|
|||||||
self.next_serial = self._get_tool_next_serial(self.company_id, self.product_id, self.origin)
|
self.next_serial = self._get_tool_next_serial(self.company_id, self.product_id, self.origin)
|
||||||
else:
|
else:
|
||||||
self.next_serial = self.env['stock.lot']._get_next_serial(self.company_id, self.product_id)
|
self.next_serial = self.env['stock.lot']._get_next_serial(self.company_id, self.product_id)
|
||||||
if self.picking_type_id.sequence_code == 'DL' and not self.move_line_nosuggest_ids:
|
if (self.picking_type_id.use_existing_lots is False
|
||||||
|
and self.picking_type_id.use_create_lots is True and not self.move_line_nosuggest_ids):
|
||||||
self.action_assign_serial_show_details()
|
self.action_assign_serial_show_details()
|
||||||
elif self.product_id.tracking == "lot":
|
elif self.product_id.tracking == "lot":
|
||||||
self._put_tool_lot(self.company_id, self.product_id, self.origin)
|
self._put_tool_lot(self.company_id, self.product_id, self.origin)
|
||||||
|
|||||||
@@ -192,3 +192,5 @@ access_sf_programming_reason,sf_programming_reason,model_sf_programming_reason,b
|
|||||||
access_sf_programming_record,sf_programming_record,model_sf_programming_record,base.group_user,1,1,1,0
|
access_sf_programming_record,sf_programming_record,model_sf_programming_record,base.group_user,1,1,1,0
|
||||||
access_sf_work_individuation_page,sf_work_individuation_page,model_sf_work_individuation_page,sf_base.group_sf_mrp_user,1,1,1,0
|
access_sf_work_individuation_page,sf_work_individuation_page,model_sf_work_individuation_page,sf_base.group_sf_mrp_user,1,1,1,0
|
||||||
access_sf_work_individuation_page_group_plan_dispatch,sf_work_individuation_page_group_plan_dispatch,model_sf_work_individuation_page,sf_base.group_plan_dispatch,1,1,0,0
|
access_sf_work_individuation_page_group_plan_dispatch,sf_work_individuation_page_group_plan_dispatch,model_sf_work_individuation_page,sf_base.group_plan_dispatch,1,1,0,0
|
||||||
|
access_sf_sale_order_cancel_wizard,sf_sale_order_cancel_wizard,model_sf_sale_order_cancel_wizard,sf_base.group_sf_order_user,1,1,1,0
|
||||||
|
access_sf_sale_order_cancel_line,sf_sale_order_cancel_line,model_sf_sale_order_cancel_line,sf_base.group_sf_order_user,1,1,1,0
|
||||||
|
@@ -18,15 +18,35 @@
|
|||||||
<xpath expr="//page/field[@name='order_line']/tree/field[@name='remark']" position="before">
|
<xpath expr="//page/field[@name='order_line']/tree/field[@name='remark']" position="before">
|
||||||
<field name="supply_method" attrs="{'invisible': [('state', '=', 'draft')], 'required': [('state', '=', 'supply method')]}" />
|
<field name="supply_method" attrs="{'invisible': [('state', '=', 'draft')], 'required': [('state', '=', 'supply method')]}" />
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//header/button[@name='action_cancel']" position="attributes">
|
<xpath expr="//field[@name='order_line']/tree/field[@name='glb_url']" position="before">
|
||||||
<attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute>
|
<field name="part_number" optional="show" class="section_and_note_text"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
|
<!-- <xpath expr="//header/button[@name='action_cancel']" position="attributes"> -->
|
||||||
|
<!-- <attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute> -->
|
||||||
|
<!-- </xpath> -->
|
||||||
<xpath expr="//header/button[@name='action_cancel']" position="attributes">
|
<xpath expr="//header/button[@name='action_cancel']" position="attributes">
|
||||||
<attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute>
|
<attribute name="attrs">{'invisible': [('state', 'not in', ['draft', 'supply method'])]}</attribute>
|
||||||
|
<attribute name="confirm">警告:取消操作将不可逆,是否确定要取消该单据?</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//header/button[@name='action_quotation_send'][5]" position="attributes">
|
<xpath expr="//header/button[@name='action_quotation_send'][5]" position="attributes">
|
||||||
<attribute name="attrs">{'invisible': ['|','&',('check_status', '!=', 'approved'),('state', 'in', ['draft','cancel','supply method']),'&',('check_status', '=', 'approved'),('state', 'in', ['sale','cancel','supply method'])]}</attribute>
|
<attribute name="attrs">{'invisible': ['|','&',('check_status', '!=', 'approved'),('state', 'in', ['draft','cancel','supply method']),'&',('check_status', '=', 'approved'),('state', 'in', ['sale','cancel','supply method'])]}</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
|
<xpath expr="//header/button[@name='action_cancel']" position="after">
|
||||||
|
<button
|
||||||
|
name="action_show_cancel_wizard"
|
||||||
|
string="取消"
|
||||||
|
type="object"
|
||||||
|
attrs="{'invisible': [('state', 'not in', ['sale', 'processing'])]}"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
name="action_show_cancel_wizard"
|
||||||
|
string="取消清单"
|
||||||
|
type="object"
|
||||||
|
attrs="{'invisible': [('state', 'not in', ['cancel'])]}"
|
||||||
|
/>
|
||||||
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|||||||
@@ -5,3 +5,4 @@ from . import production_technology_wizard
|
|||||||
from . import production_technology_re_adjust_wizard
|
from . import production_technology_re_adjust_wizard
|
||||||
from . import mrp_workorder_batch_replan_wizard
|
from . import mrp_workorder_batch_replan_wizard
|
||||||
from . import sf_programming_reason
|
from . import sf_programming_reason
|
||||||
|
from . import sale_order_cancel
|
||||||
|
|||||||
394
sf_manufacturing/wizard/sale_order_cancel.py
Normal file
394
sf_manufacturing/wizard/sale_order_cancel.py
Normal file
@@ -0,0 +1,394 @@
|
|||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
|
||||||
|
class SFSaleOrderCancelWizard(models.TransientModel):
|
||||||
|
_name = 'sf.sale.order.cancel.wizard'
|
||||||
|
_description = '销售订单取消向导'
|
||||||
|
|
||||||
|
order_id = fields.Many2one('sale.order', string='销售订单')
|
||||||
|
related_docs = fields.One2many('sf.sale.order.cancel.line', 'wizard_id', string='相关单据')
|
||||||
|
has_movement = fields.Boolean(compute='_compute_has_movement', string='是否有异动')
|
||||||
|
display_message = fields.Char(compute='_compute_display_message', string='显示消息')
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def default_get(self, fields_list):
|
||||||
|
defaults = super().default_get(fields_list)
|
||||||
|
if self._context.get('active_id'):
|
||||||
|
order = self.env['sale.order'].browse(self._context.get('active_id'))
|
||||||
|
defaults['order_id'] = order.id
|
||||||
|
# 创建向导时自动创建关联单据行
|
||||||
|
wizard = self.create(defaults)
|
||||||
|
self.env['sf.sale.order.cancel.line'].create_from_order(wizard.id, order)
|
||||||
|
defaults['related_docs'] = wizard.related_docs.ids
|
||||||
|
return defaults
|
||||||
|
|
||||||
|
@api.depends('related_docs.cancel_reason')
|
||||||
|
def _compute_has_movement(self):
|
||||||
|
for wizard in self:
|
||||||
|
docs_has_movement = any(doc.cancel_reason for doc in wizard.related_docs)
|
||||||
|
order_canceled = wizard.order_id.state == 'cancel'
|
||||||
|
wizard.has_movement = docs_has_movement or order_canceled
|
||||||
|
|
||||||
|
@api.depends('has_movement', 'related_docs', 'related_docs.doc_state')
|
||||||
|
def _compute_display_message(self):
|
||||||
|
for wizard in self:
|
||||||
|
# 如果没有相关记录,显示为空
|
||||||
|
if not wizard.related_docs:
|
||||||
|
wizard.display_message = '无下游单据'
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 检查是否所有记录都是已取消状态
|
||||||
|
all_canceled = all(doc.doc_state == '已取消' for doc in wizard.related_docs)
|
||||||
|
if all_canceled:
|
||||||
|
wizard.display_message = '取消的下游单据如下:'
|
||||||
|
else:
|
||||||
|
wizard.display_message = '部分或全部下游单据存在异动,无法取消,详情如下:' if wizard.has_movement else '确认所有下游单据全部取消?'
|
||||||
|
|
||||||
|
def action_confirm_cancel(self):
|
||||||
|
self.ensure_one()
|
||||||
|
|
||||||
|
# 取消销售订单关联的采购单
|
||||||
|
purchase_orders = self.env['purchase.order'].search([
|
||||||
|
('origin', '=', self.order_id.name)
|
||||||
|
])
|
||||||
|
if purchase_orders:
|
||||||
|
purchase_orders.write({'state': 'cancel'})
|
||||||
|
|
||||||
|
# 取消销售订单
|
||||||
|
result = self.order_id.action_cancel()
|
||||||
|
|
||||||
|
# 取消关联的制造订单及其采购单
|
||||||
|
manufacturing_orders = self.env['mrp.production'].search([
|
||||||
|
('origin', '=', self.order_id.name)
|
||||||
|
])
|
||||||
|
for mo in manufacturing_orders:
|
||||||
|
# 取消制造订单关联的采购单,但保持关联关系
|
||||||
|
mo_purchase_orders = self.env['purchase.order'].search([
|
||||||
|
('origin', '=', mo.name)
|
||||||
|
])
|
||||||
|
if mo_purchase_orders:
|
||||||
|
mo_purchase_orders.write({'state': 'cancel'})
|
||||||
|
|
||||||
|
# 取消制造订单的质检单
|
||||||
|
mo_quality_checks = self.env['quality.check'].search([
|
||||||
|
('production_id', '=', mo.id)
|
||||||
|
])
|
||||||
|
if mo_quality_checks:
|
||||||
|
mo_quality_checks.write({'quality_state': 'cancel'})
|
||||||
|
|
||||||
|
# 取消制造订单
|
||||||
|
mo.action_cancel()
|
||||||
|
|
||||||
|
# 取消制造订单关联的编程单
|
||||||
|
mo._change_programming_state()
|
||||||
|
|
||||||
|
# 取消组件的制造单关联的采购单
|
||||||
|
for comp_mo in self.env['mrp.production'].search([
|
||||||
|
('origin', '=', mo.name)
|
||||||
|
]):
|
||||||
|
comp_purchase_orders = self.env['purchase.order'].search([
|
||||||
|
('origin', '=', comp_mo.name)
|
||||||
|
])
|
||||||
|
if comp_purchase_orders:
|
||||||
|
comp_purchase_orders.button_cancel()
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class SFSaleOrderCancelLine(models.TransientModel):
|
||||||
|
_name = 'sf.sale.order.cancel.line'
|
||||||
|
_description = '销售订单取消行'
|
||||||
|
|
||||||
|
wizard_id = fields.Many2one('sf.sale.order.cancel.wizard')
|
||||||
|
sequence = fields.Integer('序号')
|
||||||
|
category = fields.Char('大类')
|
||||||
|
doc_name = fields.Char('单据名称')
|
||||||
|
operation_type = fields.Char('作业类型')
|
||||||
|
doc_number = fields.Char('单据编号')
|
||||||
|
line_number = fields.Char('行号')
|
||||||
|
product_name = fields.Char('产品名称')
|
||||||
|
quantity = fields.Float('数量')
|
||||||
|
doc_state = fields.Char('单据状态')
|
||||||
|
cancel_reason = fields.Char('禁止取消原因')
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def create_from_order(self, wizard_id, order):
|
||||||
|
sequence = 1
|
||||||
|
lines = []
|
||||||
|
map_dict = {
|
||||||
|
'waiting': '等待其他作业',
|
||||||
|
'to approve': '待批准',
|
||||||
|
'technology_to_confirmed': '待工艺确认',
|
||||||
|
'confirmed': '已确认',
|
||||||
|
'pending': '等待其他工单',
|
||||||
|
'none': '待处理',
|
||||||
|
'draft': '询价',
|
||||||
|
'cancel': '已取消',
|
||||||
|
'pass': '通过的',
|
||||||
|
'fail': '失败的',
|
||||||
|
'done': '已完成',
|
||||||
|
'rework': '返工',
|
||||||
|
'purchase': '采购订单',
|
||||||
|
'ready': '就绪',
|
||||||
|
'approved': '已批准',
|
||||||
|
'pending_cam': '待加工',
|
||||||
|
'progress': '加工中',
|
||||||
|
'assigned': '就绪'
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查销售订单
|
||||||
|
if order.invoice_ids:
|
||||||
|
a = 0
|
||||||
|
for invoice in order.invoice_ids:
|
||||||
|
a += 1
|
||||||
|
vals = {
|
||||||
|
'wizard_id': wizard_id,
|
||||||
|
'sequence': sequence,
|
||||||
|
'category': '销售',
|
||||||
|
'doc_name': '销售订单',
|
||||||
|
'operation_type': '',
|
||||||
|
'doc_number': invoice.name,
|
||||||
|
'line_number': a,
|
||||||
|
'product_name': invoice.product_id.name,
|
||||||
|
'quantity': invoice.quantity,
|
||||||
|
'doc_state': invoice.state,
|
||||||
|
'cancel_reason': '已有异动' if invoice.state != 'draft' else ''
|
||||||
|
}
|
||||||
|
lines.append(self.create(vals))
|
||||||
|
sequence += 1
|
||||||
|
|
||||||
|
# 检查交货单
|
||||||
|
if order.picking_ids:
|
||||||
|
b = 0
|
||||||
|
for picking in order.picking_ids:
|
||||||
|
b += 1
|
||||||
|
vals = {
|
||||||
|
'wizard_id': wizard_id,
|
||||||
|
'sequence': sequence,
|
||||||
|
'category': '库存',
|
||||||
|
'doc_name': '交货单',
|
||||||
|
'operation_type': '调拨',
|
||||||
|
'doc_number': picking.name,
|
||||||
|
'line_number': b,
|
||||||
|
'product_name': picking.product_id.name if picking.product_id else '',
|
||||||
|
# 'quantity': picking.product_qty if hasattr(picking, 'product_qty') else 0,
|
||||||
|
'quantity': sum(picking.move_ids.mapped('product_uom_qty') or [0]),
|
||||||
|
'doc_state': map_dict.get(picking.state, picking.state),
|
||||||
|
'cancel_reason': '已有异动' if picking.state not in ['draft', 'cancel', 'waiting'] else ''
|
||||||
|
}
|
||||||
|
lines.append(self.create(vals))
|
||||||
|
sequence += 1
|
||||||
|
|
||||||
|
# 检查销售订单直接关联的采购单
|
||||||
|
purchase_orders = self.env['purchase.order'].search([
|
||||||
|
('origin', '=', order.name)
|
||||||
|
])
|
||||||
|
if purchase_orders:
|
||||||
|
c = 0
|
||||||
|
for po in purchase_orders:
|
||||||
|
c += 1
|
||||||
|
vals = {
|
||||||
|
'wizard_id': wizard_id,
|
||||||
|
'sequence': sequence,
|
||||||
|
'category': '采购',
|
||||||
|
'doc_name': '询价单',
|
||||||
|
'operation_type': po.picking_type_id.name,
|
||||||
|
'doc_number': po.name,
|
||||||
|
'line_number': c,
|
||||||
|
'product_name': po.order_line[0].product_id.name if po.order_line else '',
|
||||||
|
'quantity': po.order_line[0].product_qty if po.order_line else 0,
|
||||||
|
'doc_state': map_dict.get(po.state, po.state),
|
||||||
|
'cancel_reason': '已有异动' if po.state not in ['draft', 'cancel'] else ''
|
||||||
|
}
|
||||||
|
lines.append(self.create(vals))
|
||||||
|
sequence += 1
|
||||||
|
|
||||||
|
# 检查制造订单
|
||||||
|
manufacturing_orders = self.env['mrp.production'].search([
|
||||||
|
('origin', '=', order.name)
|
||||||
|
])
|
||||||
|
d = 0
|
||||||
|
for mo in manufacturing_orders:
|
||||||
|
# 添加制造订单本身
|
||||||
|
d += 1
|
||||||
|
vals = {
|
||||||
|
'wizard_id': wizard_id,
|
||||||
|
'sequence': sequence,
|
||||||
|
'category': '制造',
|
||||||
|
'doc_name': '制造订单',
|
||||||
|
'doc_number': mo.name,
|
||||||
|
'operation_type': '',
|
||||||
|
'line_number': d,
|
||||||
|
'product_name': mo.product_id.name,
|
||||||
|
'quantity': mo.product_qty,
|
||||||
|
'doc_state': map_dict.get(mo.state, mo.state),
|
||||||
|
'cancel_reason': '已有异动' if mo.state not in ['technology_to_confirmed', 'cancel'] else ''
|
||||||
|
}
|
||||||
|
lines.append(self.create(vals))
|
||||||
|
sequence += 1
|
||||||
|
|
||||||
|
# 检查制造订单关联的采购单
|
||||||
|
purchase_orders = self.env['purchase.order'].search([
|
||||||
|
('origin', '=', mo.name)
|
||||||
|
])
|
||||||
|
if purchase_orders:
|
||||||
|
e = 0
|
||||||
|
for po in purchase_orders:
|
||||||
|
e += 1
|
||||||
|
vals = {
|
||||||
|
'wizard_id': wizard_id,
|
||||||
|
'sequence': sequence,
|
||||||
|
'category': '制造',
|
||||||
|
'doc_name': '询价单',
|
||||||
|
'doc_number': po.name,
|
||||||
|
'line_number': e,
|
||||||
|
'operation_type': po.picking_type_id.name,
|
||||||
|
'product_name': po.order_line[0].product_id.name if po.order_line else '',
|
||||||
|
'quantity': po.order_line[0].product_qty if po.order_line else 0,
|
||||||
|
'doc_state': map_dict.get(po.state, po.state),
|
||||||
|
'cancel_reason': '已有异动' if po.state not in ['draft', 'cancel'] else ''
|
||||||
|
}
|
||||||
|
lines.append(self.create(vals))
|
||||||
|
sequence += 1
|
||||||
|
|
||||||
|
# 检查制造订单的领料单
|
||||||
|
if mo.picking_ids:
|
||||||
|
f = 0
|
||||||
|
for picking in mo.picking_ids:
|
||||||
|
f += 1
|
||||||
|
vals = {
|
||||||
|
'wizard_id': wizard_id,
|
||||||
|
'sequence': sequence,
|
||||||
|
'category': '制造',
|
||||||
|
'doc_name': '库存移动',
|
||||||
|
'doc_number': picking.name,
|
||||||
|
'line_number': f,
|
||||||
|
'operation_type': picking.picking_type_id.name,
|
||||||
|
'product_name': picking.product_id.name if picking.product_id else '',
|
||||||
|
'quantity': sum(picking.move_ids.mapped('product_uom_qty') or [0]),
|
||||||
|
'doc_state': map_dict.get(picking.state, picking.state),
|
||||||
|
'cancel_reason': '已有异动' if picking.state not in ['draft', 'cancel', 'waiting'] else ''
|
||||||
|
}
|
||||||
|
lines.append(self.create(vals))
|
||||||
|
sequence += 1
|
||||||
|
|
||||||
|
# 检查制造订单的工单
|
||||||
|
if mo.workorder_ids:
|
||||||
|
g = 0
|
||||||
|
for workorder in mo.workorder_ids:
|
||||||
|
g += 1
|
||||||
|
vals = {
|
||||||
|
'wizard_id': wizard_id,
|
||||||
|
'sequence': sequence,
|
||||||
|
'category': '制造',
|
||||||
|
'doc_name': '工单',
|
||||||
|
'doc_number': workorder.name,
|
||||||
|
'line_number': g,
|
||||||
|
'operation_type': workorder.workcenter_id.name,
|
||||||
|
'product_name': mo.product_id.name,
|
||||||
|
'quantity': workorder.qty_production,
|
||||||
|
'doc_state': map_dict.get(workorder.state, workorder.state),
|
||||||
|
'cancel_reason': '已有异动' if workorder.state not in ['draft', 'cancel', 'pending',
|
||||||
|
'waiting'] else ''
|
||||||
|
}
|
||||||
|
lines.append(self.create(vals))
|
||||||
|
sequence += 1
|
||||||
|
|
||||||
|
# 检查制造订单组件的采购单和制造单
|
||||||
|
for move in mo.move_raw_ids:
|
||||||
|
# # 检查组件的采购单
|
||||||
|
# component_pos = self.env['purchase.order'].search([
|
||||||
|
# ('origin', '=', mo.name),
|
||||||
|
# ('order_line.product_id', '=', move.product_id.id)
|
||||||
|
# ])
|
||||||
|
# for po in component_pos:
|
||||||
|
# vals = {
|
||||||
|
# 'wizard_id': wizard_id,
|
||||||
|
# 'sequence': sequence,
|
||||||
|
# 'category': '制造',
|
||||||
|
# 'doc_name': '组件采购单',
|
||||||
|
# 'operation_type': '组件采购',
|
||||||
|
# 'doc_number': po.name,
|
||||||
|
# 'product_name': move.product_id.name,
|
||||||
|
# 'quantity': po.order_line[0].product_qty if po.order_line else 0,
|
||||||
|
# 'doc_state': po.state,
|
||||||
|
# 'cancel_reason': '已有异动' if po.state not in ['draft', 'cancel'] else ''
|
||||||
|
# }
|
||||||
|
# lines.append(self.create(vals))
|
||||||
|
# sequence += 1
|
||||||
|
|
||||||
|
# 检查组件的制造单
|
||||||
|
component_mos = self.env['mrp.production'].search([
|
||||||
|
('origin', '=', mo.name),
|
||||||
|
('product_id', '=', move.product_id.id)
|
||||||
|
])
|
||||||
|
h = 0
|
||||||
|
for comp_mo in component_mos:
|
||||||
|
h += 1
|
||||||
|
vals = {
|
||||||
|
'wizard_id': wizard_id,
|
||||||
|
'sequence': sequence,
|
||||||
|
'category': '制造',
|
||||||
|
'doc_name': '组件制造单',
|
||||||
|
'operation_type': '',
|
||||||
|
'doc_number': comp_mo.name,
|
||||||
|
'line_number': h,
|
||||||
|
'product_name': move.product_id.name,
|
||||||
|
'quantity': comp_mo.product_qty,
|
||||||
|
'doc_state': map_dict.get(comp_mo.state, comp_mo.state),
|
||||||
|
'cancel_reason': '已有异动' if comp_mo.state not in ['technology_to_confirmed'] else ''
|
||||||
|
}
|
||||||
|
lines.append(self.create(vals))
|
||||||
|
sequence += 1
|
||||||
|
|
||||||
|
# 检查制造订单的质检单
|
||||||
|
quality_checks = self.env['quality.check'].search([
|
||||||
|
('production_id', '=', mo.id)
|
||||||
|
])
|
||||||
|
if quality_checks:
|
||||||
|
i = 0
|
||||||
|
for check in quality_checks:
|
||||||
|
i += 1
|
||||||
|
vals = {
|
||||||
|
'wizard_id': wizard_id,
|
||||||
|
'sequence': sequence,
|
||||||
|
'category': '制造',
|
||||||
|
'doc_name': '质检单',
|
||||||
|
'operation_type': '',
|
||||||
|
'doc_number': check.name,
|
||||||
|
'line_number': i,
|
||||||
|
'product_name': check.product_id.name,
|
||||||
|
'quantity': 1,
|
||||||
|
'doc_state': map_dict.get(check.quality_state, check.quality_state),
|
||||||
|
'cancel_reason': '已有异动' if check.quality_state not in ['none', 'cancel'] else ''
|
||||||
|
}
|
||||||
|
lines.append(self.create(vals))
|
||||||
|
sequence += 1
|
||||||
|
|
||||||
|
# 检查制造订单的编程单
|
||||||
|
cloud_programming = mo._cron_get_programming_state()
|
||||||
|
if cloud_programming:
|
||||||
|
vals = {
|
||||||
|
'wizard_id': wizard_id,
|
||||||
|
'sequence': sequence,
|
||||||
|
'category': '编程',
|
||||||
|
'doc_name': '编程单',
|
||||||
|
'operation_type': '',
|
||||||
|
'doc_number': cloud_programming['programming_no'],
|
||||||
|
'line_number': 1,
|
||||||
|
'product_name': cloud_programming['production_order_no'],
|
||||||
|
'quantity': 1,
|
||||||
|
'doc_state': cloud_programming['programming_state'],
|
||||||
|
'cancel_reason': ''
|
||||||
|
}
|
||||||
|
lines.append(self.create(vals))
|
||||||
|
sequence += 1
|
||||||
|
|
||||||
|
unique_lines = {}
|
||||||
|
for line in lines:
|
||||||
|
doc_number = line.doc_number
|
||||||
|
if doc_number not in unique_lines:
|
||||||
|
unique_lines[doc_number] = line
|
||||||
|
|
||||||
|
# 返回去重后的记录列表
|
||||||
|
return list(unique_lines.values())
|
||||||
42
sf_manufacturing/wizard/sale_order_cancel_views.xml
Normal file
42
sf_manufacturing/wizard/sale_order_cancel_views.xml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_sf_sale_order_cancel_wizard" model="ir.ui.view">
|
||||||
|
<field name="name">sf.sale.order.cancel.wizard.form</field>
|
||||||
|
<field name="model">sf.sale.order.cancel.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="下游单据清单">
|
||||||
|
<group>
|
||||||
|
<field name="order_id" invisible="1"/>
|
||||||
|
<field name="has_movement" invisible="1"/>
|
||||||
|
</group>
|
||||||
|
<div class="alert alert-warning" role="alert">
|
||||||
|
<field name="display_message" readonly="1" nolabel="1"/>
|
||||||
|
</div>
|
||||||
|
<field name="related_docs">
|
||||||
|
<tree string="下游单据" create="false" edit="false" delete="false">
|
||||||
|
<!-- <field name="sequence" string="序号"/> -->
|
||||||
|
<field name="category" string="大类"/>
|
||||||
|
<field name="doc_name" string="单据名称"/>
|
||||||
|
<field name="operation_type" string="作业类型"/>
|
||||||
|
<field name="doc_number" string="单据编号"/>
|
||||||
|
<field name="line_number" string="行号"/>
|
||||||
|
<field name="product_name" string="产品名称"/>
|
||||||
|
<field name="quantity" string="数量"/>
|
||||||
|
<field name="doc_state" string="单据状态"/>
|
||||||
|
<field name="cancel_reason" string="禁止取消原因"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
<footer>
|
||||||
|
<button name="action_confirm_cancel"
|
||||||
|
string="确认取消"
|
||||||
|
type="object"
|
||||||
|
class="btn-primary"
|
||||||
|
attrs="{'invisible': [('has_movement', '=', True)]}"/>
|
||||||
|
<button string="关闭"
|
||||||
|
class="btn-secondary"
|
||||||
|
special="cancel"/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
@@ -68,6 +68,11 @@
|
|||||||
<field name="model">stock.picking</field>
|
<field name="model">stock.picking</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="order_quality_done" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">调拨单质检完成提醒</field>
|
||||||
|
<field name="model">stock.picking</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
<record id="bussiness_mrp_workorder_pre_overdue_warning" model="jikimo.message.bussiness.node">
|
<record id="bussiness_mrp_workorder_pre_overdue_warning" model="jikimo.message.bussiness.node">
|
||||||
<field name="name">装夹预调工单逾期预警</field>
|
<field name="name">装夹预调工单逾期预警</field>
|
||||||
<field name="model">mrp.workorder</field>
|
<field name="model">mrp.workorder</field>
|
||||||
|
|||||||
@@ -252,6 +252,18 @@
|
|||||||
<field name="content">### 订单发货提醒:
|
<field name="content">### 订单发货提醒:
|
||||||
单号:发料出库单[{{name}}]({{request_url}})
|
单号:发料出库单[{{name}}]({{request_url}})
|
||||||
事项:销售订单{{sale_order_name}}已全部产出并入库,请及时发货</field>
|
事项:销售订单{{sale_order_name}}已全部产出并入库,请及时发货</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="template_order_quality_done" model="jikimo.message.template">
|
||||||
|
<field name="name">调拨单质检完成提醒</field>
|
||||||
|
<field name="model_id" ref="stock.model_stock_picking"/>
|
||||||
|
<field name="model">stock.picking</field>
|
||||||
|
<field name="bussiness_node_id" ref="order_quality_done"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="urgency">normal</field>
|
||||||
|
<field name="content">### {{picking_type_name}}待处理提醒:
|
||||||
|
单号:{{name}}
|
||||||
|
事项:质量检查已完成</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="template_quality_cnc_test" model="jikimo.message.template">
|
<record id="template_quality_cnc_test" model="jikimo.message.template">
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ class SFMessageStockPicking(models.Model):
|
|||||||
_description = "库存调拨"
|
_description = "库存调拨"
|
||||||
_inherit = ['stock.picking', 'jikimo.message.dispatch']
|
_inherit = ['stock.picking', 'jikimo.message.dispatch']
|
||||||
|
|
||||||
|
quality_check_ids = fields.One2many('quality.check', 'picking_id', '质量检测单')
|
||||||
|
|
||||||
@api.model_create_multi
|
@api.model_create_multi
|
||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
result = super(SFMessageStockPicking, self).create(vals)
|
result = super(SFMessageStockPicking, self).create(vals)
|
||||||
@@ -20,7 +22,8 @@ class SFMessageStockPicking(models.Model):
|
|||||||
logging.info('add_queue调拨入库 error:%s' % e)
|
logging.info('add_queue调拨入库 error:%s' % e)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@api.depends('move_type', 'immediate_transfer', 'move_ids.state', 'move_ids.picking_id')
|
@api.depends('move_type', 'immediate_transfer', 'move_ids.state', 'move_ids.picking_id',
|
||||||
|
'quality_check_ids.quality_state')
|
||||||
def _compute_state(self):
|
def _compute_state(self):
|
||||||
super(SFMessageStockPicking, self)._compute_state()
|
super(SFMessageStockPicking, self)._compute_state()
|
||||||
try:
|
try:
|
||||||
@@ -48,6 +51,8 @@ class SFMessageStockPicking(models.Model):
|
|||||||
all_ready_or_done = all(picking.state in ['assigned', 'done'] for picking in stock_picking_list)
|
all_ready_or_done = all(picking.state in ['assigned', 'done'] for picking in stock_picking_list)
|
||||||
if all_ready_or_done:
|
if all_ready_or_done:
|
||||||
mrp_production.add_queue('工序外协发料通知')
|
mrp_production.add_queue('工序外协发料通知')
|
||||||
|
if all(qc.quality_state in ['pass', 'fail'] for qc in record.quality_check_ids):
|
||||||
|
record.add_queue('调拨单质检完成提醒')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.info('add_queue_compute_state error:%s' % e)
|
logging.info('add_queue_compute_state error:%s' % e)
|
||||||
|
|
||||||
@@ -83,6 +88,11 @@ class SFMessageStockPicking(models.Model):
|
|||||||
content = self.deal_stock_picking_sfp(message_queue_id)
|
content = self.deal_stock_picking_sfp(message_queue_id)
|
||||||
if content:
|
if content:
|
||||||
contents.append(content)
|
contents.append(content)
|
||||||
|
elif message_queue_id.message_template_id.name == '调拨单质检完成提醒':
|
||||||
|
content = message_queue_id.message_template_id.content
|
||||||
|
content = content.replace('{{picking_type_name}}', self.picking_type_id.name).replace(
|
||||||
|
'{{name}}', self.name)
|
||||||
|
contents.append(content)
|
||||||
return contents, message_queue_ids
|
return contents, message_queue_ids
|
||||||
|
|
||||||
def get_special_url(self, id, tmplate_name, special_name, model_id):
|
def get_special_url(self, id, tmplate_name, special_name, model_id):
|
||||||
|
|||||||
@@ -1988,6 +1988,9 @@ class CuttingSpeed(models.Model):
|
|||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
cutting_speed.write({
|
cutting_speed.write({
|
||||||
|
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
|
||||||
|
[('code', '=', item['standard_library_code'].replace("JKM", result[
|
||||||
|
'factory_short_name']))]).id,
|
||||||
'execution_standard_id': self.env['sf.international.standards'].search(
|
'execution_standard_id': self.env['sf.international.standards'].search(
|
||||||
[('code', '=', item['execution_standard_code'])]).id,
|
[('code', '=', item['execution_standard_code'])]).id,
|
||||||
'material_name_id': self.env['sf.materials.model'].search(
|
'material_name_id': self.env['sf.materials.model'].search(
|
||||||
@@ -2040,6 +2043,9 @@ class CuttingSpeed(models.Model):
|
|||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
cutting_speed.write({
|
cutting_speed.write({
|
||||||
|
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
|
||||||
|
[('code', '=', item['standard_library_code'].replace("JKM", result[
|
||||||
|
'factory_short_name']))]).id,
|
||||||
'execution_standard_id': self.env['sf.international.standards'].search(
|
'execution_standard_id': self.env['sf.international.standards'].search(
|
||||||
[('code', '=', item['execution_standard_code'])]).id,
|
[('code', '=', item['execution_standard_code'])]).id,
|
||||||
'material_name_id': self.env['sf.materials.model'].search(
|
'material_name_id': self.env['sf.materials.model'].search(
|
||||||
@@ -2130,6 +2136,9 @@ class CuttingSpeed(models.Model):
|
|||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
feed_per_tooth.write({
|
feed_per_tooth.write({
|
||||||
|
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
|
||||||
|
[('code', '=', item['standard_library_code'].replace("JKM", result[
|
||||||
|
'factory_short_name']))]).id,
|
||||||
'materials_type_id': self.env['sf.materials.model'].search(
|
'materials_type_id': self.env['sf.materials.model'].search(
|
||||||
[('materials_no', '=', item['materials_type_code'])]).id,
|
[('materials_no', '=', item['materials_type_code'])]).id,
|
||||||
'cutting_width_depth_id': self.env['sf.cutting.width.depth'].search(
|
'cutting_width_depth_id': self.env['sf.cutting.width.depth'].search(
|
||||||
@@ -2168,6 +2177,9 @@ class CuttingSpeed(models.Model):
|
|||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
feed_per_tooth.write({
|
feed_per_tooth.write({
|
||||||
|
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
|
||||||
|
[('code', '=', item['standard_library_code'].replace("JKM", result[
|
||||||
|
'factory_short_name']))]).id,
|
||||||
'materials_type_id': self.env['sf.materials.model'].search(
|
'materials_type_id': self.env['sf.materials.model'].search(
|
||||||
[('materials_no', '=', item['materials_type_code'])]).id,
|
[('materials_no', '=', item['materials_type_code'])]).id,
|
||||||
'cutting_width_depth_id': self.env['sf.cutting.width.depth'].search(
|
'cutting_width_depth_id': self.env['sf.cutting.width.depth'].search(
|
||||||
@@ -2454,6 +2466,11 @@ class CuttingToolBasicParameters(models.Model):
|
|||||||
else:
|
else:
|
||||||
self.search([('code', '=', integral_tool_item['code'])]).write({
|
self.search([('code', '=', integral_tool_item['code'])]).write({
|
||||||
'name': integral_tool_item['name'],
|
'name': integral_tool_item['name'],
|
||||||
|
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
|
||||||
|
[(
|
||||||
|
'code', '=',
|
||||||
|
integral_tool_item['standard_library_code'].replace("JKM", result[
|
||||||
|
'factory_short_name']))]).id,
|
||||||
'total_length': integral_tool_item['total_length'],
|
'total_length': integral_tool_item['total_length'],
|
||||||
'blade_diameter': integral_tool_item['blade_diameter'],
|
'blade_diameter': integral_tool_item['blade_diameter'],
|
||||||
'blade_length': integral_tool_item['blade_length'],
|
'blade_length': integral_tool_item['blade_length'],
|
||||||
@@ -2516,6 +2533,9 @@ class CuttingToolBasicParameters(models.Model):
|
|||||||
else:
|
else:
|
||||||
self.search([('code', '=', blade_item['code'])]).write({
|
self.search([('code', '=', blade_item['code'])]).write({
|
||||||
'name': blade_item['name'],
|
'name': blade_item['name'],
|
||||||
|
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
|
||||||
|
[('code', '=', blade_item['standard_library_code'].replace("JKM", result[
|
||||||
|
'factory_short_name']))]).id,
|
||||||
'length': blade_item['length'],
|
'length': blade_item['length'],
|
||||||
'thickness': blade_item['thickness'],
|
'thickness': blade_item['thickness'],
|
||||||
'cutting_blade_length': blade_item['cutting_blade_length'],
|
'cutting_blade_length': blade_item['cutting_blade_length'],
|
||||||
@@ -2573,6 +2593,9 @@ class CuttingToolBasicParameters(models.Model):
|
|||||||
else:
|
else:
|
||||||
self.search([('code', '=', chuck_item['code'])]).write({
|
self.search([('code', '=', chuck_item['code'])]).write({
|
||||||
'name': chuck_item['name'],
|
'name': chuck_item['name'],
|
||||||
|
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
|
||||||
|
[('code', '=', chuck_item['standard_library_code'].replace("JKM", result[
|
||||||
|
'factory_short_name']))]).id,
|
||||||
'er_size_model': chuck_item['size_model'],
|
'er_size_model': chuck_item['size_model'],
|
||||||
'min_clamping_diameter': chuck_item['clamping_diameter_min'],
|
'min_clamping_diameter': chuck_item['clamping_diameter_min'],
|
||||||
'max_clamping_diameter': chuck_item['clamping_diameter_max'],
|
'max_clamping_diameter': chuck_item['clamping_diameter_max'],
|
||||||
@@ -2632,6 +2655,9 @@ class CuttingToolBasicParameters(models.Model):
|
|||||||
else:
|
else:
|
||||||
self.search([('code', '=', cutter_arbor_item['code'])]).write({
|
self.search([('code', '=', cutter_arbor_item['code'])]).write({
|
||||||
'name': cutter_arbor_item['name'],
|
'name': cutter_arbor_item['name'],
|
||||||
|
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
|
||||||
|
[('code', '=', cutter_arbor_item['standard_library_code'].replace("JKM", result[
|
||||||
|
'factory_short_name']))]).id,
|
||||||
'height': cutter_arbor_item['height'],
|
'height': cutter_arbor_item['height'],
|
||||||
'width': cutter_arbor_item['width'],
|
'width': cutter_arbor_item['width'],
|
||||||
'total_length': cutter_arbor_item['total_length'],
|
'total_length': cutter_arbor_item['total_length'],
|
||||||
@@ -2697,6 +2723,9 @@ class CuttingToolBasicParameters(models.Model):
|
|||||||
else:
|
else:
|
||||||
self.search([('code', '=', cutter_head_item['code'])]).write({
|
self.search([('code', '=', cutter_head_item['code'])]).write({
|
||||||
'name': cutter_head_item['name'],
|
'name': cutter_head_item['name'],
|
||||||
|
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
|
||||||
|
[('code', '=', cutter_head_item['standard_library_code'].replace("JKM", result[
|
||||||
|
'factory_short_name']))]).id,
|
||||||
'install_blade_tip_num': cutter_head_item['number_blade_installed'],
|
'install_blade_tip_num': cutter_head_item['number_blade_installed'],
|
||||||
'blade_diameter': cutter_head_item['blade_diameter'],
|
'blade_diameter': cutter_head_item['blade_diameter'],
|
||||||
'cutter_head_diameter': cutter_head_item['cutter_diameter'],
|
'cutter_head_diameter': cutter_head_item['cutter_diameter'],
|
||||||
@@ -2727,6 +2756,9 @@ class CuttingToolBasicParameters(models.Model):
|
|||||||
[('code', '=', knife_handle_item['code']), ('active', 'in', [True, False])])
|
[('code', '=', knife_handle_item['code']), ('active', 'in', [True, False])])
|
||||||
val = {
|
val = {
|
||||||
'name': knife_handle_item['name'],
|
'name': knife_handle_item['name'],
|
||||||
|
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
|
||||||
|
[('code', '=', knife_handle_item['standard_library_code'].replace("JKM", result[
|
||||||
|
'factory_short_name']))]).id,
|
||||||
'taper_shank_model': knife_handle_item['taper_shank_model'],
|
'taper_shank_model': knife_handle_item['taper_shank_model'],
|
||||||
'total_length': knife_handle_item['total_length'],
|
'total_length': knife_handle_item['total_length'],
|
||||||
'flange_shank_length': knife_handle_item['flange_length'],
|
'flange_shank_length': knife_handle_item['flange_length'],
|
||||||
@@ -2751,9 +2783,6 @@ class CuttingToolBasicParameters(models.Model):
|
|||||||
if not knife_handle:
|
if not knife_handle:
|
||||||
val['code'] = knife_handle_item['code']
|
val['code'] = knife_handle_item['code']
|
||||||
val['cutting_tool_type'] = '刀柄'
|
val['cutting_tool_type'] = '刀柄'
|
||||||
val['standard_library_id'] = self.env['sf.cutting_tool.standard.library'].search(
|
|
||||||
[('code', '=', knife_handle_item['standard_library_code'].replace("JKM", result[
|
|
||||||
'factory_short_name']))]).id
|
|
||||||
self.create(val)
|
self.create(val)
|
||||||
else:
|
else:
|
||||||
self.search([('code', '=', knife_handle_item['code'])]).write(val)
|
self.search([('code', '=', knife_handle_item['code'])]).write(val)
|
||||||
@@ -2809,6 +2838,11 @@ class CuttingToolBasicParameters(models.Model):
|
|||||||
else:
|
else:
|
||||||
integral_tool.write({
|
integral_tool.write({
|
||||||
'name': integral_tool_item['name'],
|
'name': integral_tool_item['name'],
|
||||||
|
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
|
||||||
|
[(
|
||||||
|
'code', '=',
|
||||||
|
integral_tool_item['standard_library_code'].replace("JKM", result[
|
||||||
|
'factory_short_name']))]).id,
|
||||||
'total_length': integral_tool_item['total_length'],
|
'total_length': integral_tool_item['total_length'],
|
||||||
'blade_diameter': integral_tool_item['blade_diameter'],
|
'blade_diameter': integral_tool_item['blade_diameter'],
|
||||||
'blade_length': integral_tool_item['blade_length'],
|
'blade_length': integral_tool_item['blade_length'],
|
||||||
@@ -2871,6 +2905,9 @@ class CuttingToolBasicParameters(models.Model):
|
|||||||
else:
|
else:
|
||||||
blade.write({
|
blade.write({
|
||||||
'name': blade_item['name'],
|
'name': blade_item['name'],
|
||||||
|
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
|
||||||
|
[('code', '=', blade_item['standard_library_code'].replace("JKM", result[
|
||||||
|
'factory_short_name']))]).id,
|
||||||
'length': blade_item['length'],
|
'length': blade_item['length'],
|
||||||
'thickness': blade_item['thickness'],
|
'thickness': blade_item['thickness'],
|
||||||
'cutting_blade_length': blade_item['cutting_blade_length'],
|
'cutting_blade_length': blade_item['cutting_blade_length'],
|
||||||
@@ -2928,6 +2965,9 @@ class CuttingToolBasicParameters(models.Model):
|
|||||||
else:
|
else:
|
||||||
chuck.write({
|
chuck.write({
|
||||||
'name': chuck_item['name'],
|
'name': chuck_item['name'],
|
||||||
|
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
|
||||||
|
[('code', '=', chuck_item['standard_library_code'].replace("JKM", result[
|
||||||
|
'factory_short_name']))]).id,
|
||||||
'er_size_model': chuck_item['size_model'],
|
'er_size_model': chuck_item['size_model'],
|
||||||
'min_clamping_diameter': chuck_item['clamping_diameter_min'],
|
'min_clamping_diameter': chuck_item['clamping_diameter_min'],
|
||||||
'max_clamping_diameter': chuck_item['clamping_diameter_max'],
|
'max_clamping_diameter': chuck_item['clamping_diameter_max'],
|
||||||
@@ -2987,6 +3027,9 @@ class CuttingToolBasicParameters(models.Model):
|
|||||||
else:
|
else:
|
||||||
cutter_arbor.write({
|
cutter_arbor.write({
|
||||||
'name': cutter_arbor_item['name'],
|
'name': cutter_arbor_item['name'],
|
||||||
|
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
|
||||||
|
[('code', '=', cutter_arbor_item['standard_library_code'].replace("JKM", result[
|
||||||
|
'factory_short_name']))]).id,
|
||||||
'height': cutter_arbor_item['height'],
|
'height': cutter_arbor_item['height'],
|
||||||
'width': cutter_arbor_item['width'],
|
'width': cutter_arbor_item['width'],
|
||||||
'total_length': cutter_arbor_item['total_length'],
|
'total_length': cutter_arbor_item['total_length'],
|
||||||
@@ -3053,6 +3096,9 @@ class CuttingToolBasicParameters(models.Model):
|
|||||||
else:
|
else:
|
||||||
cutter_head.write({
|
cutter_head.write({
|
||||||
'name': cutter_head_item['name'],
|
'name': cutter_head_item['name'],
|
||||||
|
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
|
||||||
|
[('code', '=', cutter_head_item['standard_library_code'].replace("JKM", result[
|
||||||
|
'factory_short_name']))]).id,
|
||||||
'install_blade_tip_num': cutter_head_item['number_blade_installed'],
|
'install_blade_tip_num': cutter_head_item['number_blade_installed'],
|
||||||
'blade_diameter': cutter_head_item['blade_diameter'],
|
'blade_diameter': cutter_head_item['blade_diameter'],
|
||||||
'cutter_head_diameter': cutter_head_item['cutter_diameter'],
|
'cutter_head_diameter': cutter_head_item['cutter_diameter'],
|
||||||
@@ -3114,6 +3160,9 @@ class CuttingToolBasicParameters(models.Model):
|
|||||||
else:
|
else:
|
||||||
knife_handle.write({
|
knife_handle.write({
|
||||||
'name': knife_handle_item['name'],
|
'name': knife_handle_item['name'],
|
||||||
|
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
|
||||||
|
[('code', '=', knife_handle_item['standard_library_code'].replace("JKM", result[
|
||||||
|
'factory_short_name']))]).id,
|
||||||
'total_length': knife_handle_item['total_length'],
|
'total_length': knife_handle_item['total_length'],
|
||||||
'taper_shank_model': knife_handle_item['taper_shank_model'],
|
'taper_shank_model': knife_handle_item['taper_shank_model'],
|
||||||
'flange_shank_length': knife_handle_item['flange_length'],
|
'flange_shank_length': knife_handle_item['flange_length'],
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ class QualityCheck(models.Model):
|
|||||||
('waiting', '等待'),
|
('waiting', '等待'),
|
||||||
('none', '待处理'),
|
('none', '待处理'),
|
||||||
('pass', '通过的'),
|
('pass', '通过的'),
|
||||||
('fail', '失败的')], string='状态', tracking=True, store=True,
|
('fail', '失败的'),
|
||||||
|
('cancel', '已取消'), ], string='状态', tracking=True, store=True,
|
||||||
default='none', copy=False, compute='_compute_quality_state')
|
default='none', copy=False, compute='_compute_quality_state')
|
||||||
|
|
||||||
individuation_page_PTD = fields.Boolean('个性化记录(是否显示后置三元检测[PTD]页签)', related='workorder_id.individuation_page_PTD')
|
individuation_page_PTD = fields.Boolean('个性化记录(是否显示后置三元检测[PTD]页签)', related='workorder_id.individuation_page_PTD')
|
||||||
@@ -84,6 +85,17 @@ class QualityCheck(models.Model):
|
|||||||
raise ValidationError('请填写【判定结果】里的信息')
|
raise ValidationError('请填写【判定结果】里的信息')
|
||||||
if self.test_results == '合格':
|
if self.test_results == '合格':
|
||||||
raise ValidationError('请重新选择【判定结果】-【检测结果】')
|
raise ValidationError('请重新选择【判定结果】-【检测结果】')
|
||||||
|
if self.workorder_id.routing_type != 'CNC加工' and self.workorder_id.individuation_page_PTD is False:
|
||||||
|
self.workorder_id.production_id.write({'detection_result_ids': [(0, 0, {
|
||||||
|
'rework_reason': self.reason,
|
||||||
|
'detailed_reason': self.detailed_reason,
|
||||||
|
'processing_panel': self.workorder_id.processing_panel,
|
||||||
|
'routing_type': self.workorder_id.routing_type,
|
||||||
|
'handle_result': '待处理',
|
||||||
|
'test_results': self.test_results,
|
||||||
|
'test_report': self.workorder_id.detection_report})],
|
||||||
|
'is_scrap': True if self.test_results == '报废' else False
|
||||||
|
})
|
||||||
if self.workorder_id.state not in ['done']:
|
if self.workorder_id.state not in ['done']:
|
||||||
self.workorder_id.write(
|
self.workorder_id.write(
|
||||||
{'test_results': self.test_results, 'reason': self.reason, 'detailed_reason': self.detailed_reason})
|
{'test_results': self.test_results, 'reason': self.reason, 'detailed_reason': self.detailed_reason})
|
||||||
@@ -110,4 +122,3 @@ class QualityCheck(models.Model):
|
|||||||
return "零件特采发送成功"
|
return "零件特采发送成功"
|
||||||
else:
|
else:
|
||||||
raise ValidationError("零件特采发送失败")
|
raise ValidationError("零件特采发送失败")
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
'web.assets_backend': [
|
'web.assets_backend': [
|
||||||
'sf_sale/static/js/setTableWidth.js',
|
'sf_sale/static/js/setTableWidth.js',
|
||||||
'sf_sale/static/src/css/purchase_list.css',
|
'sf_sale/static/src/css/purchase_list.css',
|
||||||
|
'sf_sale/static/lib/*',
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
'demo': [
|
'demo': [
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ class ReSaleOrder(models.Model):
|
|||||||
'name': '%s/%s/%s/%s/%s/%s' % (
|
'name': '%s/%s/%s/%s/%s/%s' % (
|
||||||
self.format_float(product.model_long),
|
self.format_float(product.model_long),
|
||||||
self.format_float(product.model_width),
|
self.format_float(product.model_width),
|
||||||
self.format_float(product.model_height),
|
self.format_float(product.model_height),
|
||||||
self.format_float(product.model_volume),
|
self.format_float(product.model_volume),
|
||||||
machining_accuracy_name,
|
machining_accuracy_name,
|
||||||
product.materials_id.name),
|
product.materials_id.name),
|
||||||
|
|||||||
18
sf_sale/static/lib/merge_field.js
Normal file
18
sf_sale/static/lib/merge_field.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/** @odoo-module */
|
||||||
|
|
||||||
|
import { Component } from "@odoo/owl";
|
||||||
|
import { registry } from "@web/core/registry";
|
||||||
|
|
||||||
|
|
||||||
|
export class MergeField extends Component {
|
||||||
|
get mergeValue() {
|
||||||
|
const data = this.props.record.data;
|
||||||
|
|
||||||
|
const v = data?.product_uom_qty
|
||||||
|
const unit = data?.product_uom[1]
|
||||||
|
return `${v} ${unit}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MergeField.template = "jikimo_sf.MergeField";
|
||||||
|
|
||||||
|
registry.category("fields").add("merge_field", MergeField);
|
||||||
8
sf_sale/static/lib/merge_field.xml
Normal file
8
sf_sale/static/lib/merge_field.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
|
||||||
|
<t t-name="jikimo_sf.MergeField" owl="1">
|
||||||
|
<span t-esc="mergeValue"/>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</templates>
|
||||||
@@ -1,3 +1,11 @@
|
|||||||
.purchase_order_list_name {
|
.purchase_order_list_name {
|
||||||
min-width: 62px !important;
|
min-width: 62px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_list_renderer .o_list_table .o_data_row td.o_data_cell.o_field_cell.o_list_char.section_and_note_text, .section_and_note_text span{
|
||||||
|
white-space: wrap!important;
|
||||||
|
overflow: auto!important;
|
||||||
|
text-overflow: unset!important;
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
</field>
|
</field>
|
||||||
<xpath expr="//field[@name='date_order']" position="after">
|
<xpath expr="//field[@name='date_order']" position="after">
|
||||||
<field name="payment_term_id" attrs="{'readonly': ['|', ('invoice_status','=', 'invoiced'), ('state', '=', 'done')]}" options="{'no_create': True}"/>
|
<field name="payment_term_id" attrs="{'readonly': ['|', ('invoice_status','=', 'invoiced'), ('state', '=', 'done')]}" options="{'no_create': True}"/>
|
||||||
<field name="contract_summary"/>
|
<!-- <field name="contract_summary"/>-->
|
||||||
</xpath>
|
</xpath>
|
||||||
<field name="partner_ref" position="attributes">
|
<field name="partner_ref" position="attributes">
|
||||||
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}
|
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}
|
||||||
|
|||||||
@@ -112,6 +112,7 @@
|
|||||||
<xpath expr="//field[@name='order_line']/tree/field[@name='product_template_id']" position="attributes">
|
<xpath expr="//field[@name='order_line']/tree/field[@name='product_template_id']" position="attributes">
|
||||||
<attribute name="options">{'no_create': True}</attribute>
|
<attribute name="options">{'no_create': True}</attribute>
|
||||||
<attribute name="context">{'is_sale_order_line': True }</attribute>
|
<attribute name="context">{'is_sale_order_line': True }</attribute>
|
||||||
|
<attribute name="class">section_and_note_text</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='order_line']" position="attributes">
|
<xpath expr="//field[@name='order_line']" position="attributes">
|
||||||
<attribute name="attrs">{'readonly': [('state', 'in', ['cancel','sale'])]}</attribute>
|
<attribute name="attrs">{'readonly': [('state', 'in', ['cancel','sale'])]}</attribute>
|
||||||
@@ -122,6 +123,13 @@
|
|||||||
<field name="manual_quotation" readonly="1"/>
|
<field name="manual_quotation" readonly="1"/>
|
||||||
<field name="is_incoming_material" readonly="1"/>
|
<field name="is_incoming_material" readonly="1"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
|
<xpath expr="//field[@name='order_line']/tree/field[@name='product_uom_qty']" position="replace">
|
||||||
|
<field name="product_uom_qty" string="数量" widget="merge_field" optional="show" />
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='order_line']/tree/field[@name='product_uom']" position="attributes">
|
||||||
|
<attribute name="optional">hide</attribute>
|
||||||
|
</xpath>
|
||||||
<field name="user_id" position="attributes">
|
<field name="user_id" position="attributes">
|
||||||
<attribute name="attrs">{'readonly': [('state', 'in', ['cancel','sale'])]}</attribute>
|
<attribute name="attrs">{'readonly': [('state', 'in', ['cancel','sale'])]}</attribute>
|
||||||
</field>
|
</field>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class StockPicking(models.Model):
|
|||||||
@api.depends('name')
|
@api.depends('name')
|
||||||
def _compute_pro_purchase_count(self):
|
def _compute_pro_purchase_count(self):
|
||||||
for sp in self:
|
for sp in self:
|
||||||
if sp:
|
if sp.name and sp.name != '/':
|
||||||
po_ids = self.env['purchase.order'].sudo().search([
|
po_ids = self.env['purchase.order'].sudo().search([
|
||||||
('origin', 'like', sp.name), ('purchase_type', '=', 'standard')])
|
('origin', 'like', sp.name), ('purchase_type', '=', 'standard')])
|
||||||
if po_ids:
|
if po_ids:
|
||||||
@@ -52,7 +52,7 @@ class StockPicking(models.Model):
|
|||||||
@api.depends('name')
|
@api.depends('name')
|
||||||
def _compute_pro_out_purchase_count(self):
|
def _compute_pro_out_purchase_count(self):
|
||||||
for sp in self:
|
for sp in self:
|
||||||
if sp:
|
if sp.name and sp.name != '/':
|
||||||
po_ids = self.env['purchase.order'].sudo().search([
|
po_ids = self.env['purchase.order'].sudo().search([
|
||||||
('origin', 'like', sp.name), ('purchase_type', '=', 'outsourcing')])
|
('origin', 'like', sp.name), ('purchase_type', '=', 'outsourcing')])
|
||||||
if po_ids:
|
if po_ids:
|
||||||
|
|||||||
@@ -951,6 +951,12 @@ class SfStockPicking(models.Model):
|
|||||||
"""
|
"""
|
||||||
批量调拨,非就绪状态的会被忽略,完成后有通知提示
|
批量调拨,非就绪状态的会被忽略,完成后有通知提示
|
||||||
"""
|
"""
|
||||||
|
# 对所以调拨单的质检单进行是否完成校验
|
||||||
|
sp_ids = [sp.id for sp in self]
|
||||||
|
qc_ids = self.env['quality.check'].sudo().search(
|
||||||
|
[('picking_id', 'in', sp_ids), ('quality_state', 'in', ['waiting', 'none'])])
|
||||||
|
if qc_ids:
|
||||||
|
raise ValidationError(f'单据{[qc.picking_id.name for qc in qc_ids]}未完成质量检查,完成后再试。')
|
||||||
for record in self:
|
for record in self:
|
||||||
if record.state != 'assigned':
|
if record.state != 'assigned':
|
||||||
continue
|
continue
|
||||||
|
|||||||
3
web_widget_model_viewer/static/src/js/3d_viewer.css
Normal file
3
web_widget_model_viewer/static/src/js/3d_viewer.css
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.model-viewer-in-list {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
@@ -64,11 +64,16 @@ StepViewer.supportedTypes = ["binary"];
|
|||||||
StepViewer.props = {
|
StepViewer.props = {
|
||||||
...standardFieldProps,
|
...standardFieldProps,
|
||||||
url: {type: String, optional: true},
|
url: {type: String, optional: true},
|
||||||
|
isInList: {type: Boolean, optional: true},
|
||||||
};
|
};
|
||||||
|
|
||||||
StepViewer.extractProps = ({attrs}) => {
|
StepViewer.extractProps = ({attrs}) => {
|
||||||
|
const modifiedAttrs = JSON.parse(attrs.modifiers || '{}');
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
url: attrs.options.url,
|
url: attrs.options.url,
|
||||||
|
isInList: modifiedAttrs.isInList,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
<t t-if="props.value">
|
<t t-if="props.value">
|
||||||
<model-viewer
|
<model-viewer
|
||||||
|
t-att-class="props.isInList ? 'model-viewer-in-list' : ''"
|
||||||
t-att-src='props.url'
|
t-att-src='props.url'
|
||||||
name="3D model"
|
name="3D model"
|
||||||
alt="3D model"
|
alt="3D model"
|
||||||
|
|||||||
Reference in New Issue
Block a user