Merge branch 'feature/销售订单和工单逾期消息推送' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/销售订单和工单逾期消息推送
This commit is contained in:
@@ -3,8 +3,8 @@
|
|||||||
'name': "jikimo_account_process",
|
'name': "jikimo_account_process",
|
||||||
|
|
||||||
'summary': """
|
'summary': """
|
||||||
Short (1 phrase/line) summary of the module's purpose, used as
|
处理会计凭证生成重复名称报错问题
|
||||||
subtitle on modules listing or apps.openerp.com""",
|
""",
|
||||||
|
|
||||||
'description': """
|
'description': """
|
||||||
Long description of module's purpose
|
Long description of module's purpose
|
||||||
|
|||||||
@@ -1,41 +1,99 @@
|
|||||||
.zoomed {
|
|
||||||
position: fixed !important;
|
.processing-capabilities-grid {
|
||||||
top: 50%;
|
display: grid;
|
||||||
left: 50%;
|
grid-template-columns: repeat(6, 1fr);
|
||||||
transform: translate(-50%, -50%) scale(10);
|
gap: 10px;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.many2many_flex {
|
.grid-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.many2many_flex>div {
|
.item-content {
|
||||||
margin-right: 15px;
|
display: flex;
|
||||||
display: flex;
|
flex-direction: column;
|
||||||
flex-direction: column;
|
align-items: center;
|
||||||
align-items: center;
|
text-align: center;
|
||||||
|
}
|
||||||
|
/*控制图片大小*/
|
||||||
|
.item-icon {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.many2many_flex>div>:nth-child(2) {
|
.item-label {
|
||||||
position: relative;
|
font-size: 12px;
|
||||||
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.close {
|
@media (max-width: 1200px) {
|
||||||
width: 20px;
|
.processing-capabilities-grid {
|
||||||
height: 20px;
|
grid-template-columns: repeat(4, 1fr);
|
||||||
position: absolute;
|
}
|
||||||
top: -8.8px;
|
|
||||||
right: -8.8px;
|
|
||||||
color: #fff;
|
|
||||||
background-color: #000;
|
|
||||||
opacity: 0;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 20px;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.img_close {
|
@media (max-width: 768px) {
|
||||||
opacity: 1;
|
.processing-capabilities-grid {
|
||||||
transform: scale(0.1);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
cursor: pointer;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
}
|
}
|
||||||
@@ -4,35 +4,57 @@ import {Many2ManyCheckboxesField} from "@web/views/fields/many2many_checkboxes/m
|
|||||||
import {registry} from "@web/core/registry";
|
import {registry} from "@web/core/registry";
|
||||||
|
|
||||||
export class MyCustomWidget extends Many2ManyCheckboxesField {
|
export class MyCustomWidget extends Many2ManyCheckboxesField {
|
||||||
// 你可以重写或者添加一些方法和属性
|
|
||||||
// 例如,你可以重写setup方法来添加一些事件监听器或者初始化一些变量
|
|
||||||
setup() {
|
setup() {
|
||||||
super.setup(); // 调用父类的setup方法
|
super.setup();
|
||||||
// 你自己的代码
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onImageClick(event) {
|
onImageClick(event, src) {
|
||||||
// 放大图片逻辑
|
event.preventDefault();
|
||||||
// 获取图片元素
|
event.stopPropagation();
|
||||||
const img = event.target;
|
|
||||||
const close = img.nextSibling;
|
|
||||||
|
|
||||||
// 实现放大图片逻辑
|
// 创建预览框
|
||||||
// 比如使用 CSS 放大
|
const previewContainer = document.createElement('div');
|
||||||
img.parentElement.classList.add('zoomed');
|
previewContainer.className = 'image-preview-container';
|
||||||
close.classList.add('img_close');
|
|
||||||
}
|
|
||||||
|
|
||||||
onCloseClick(event) {
|
const previewImg = document.createElement('img');
|
||||||
const close = event.target;
|
previewImg.src = src;
|
||||||
const img = close.previousSibling;
|
previewImg.className = 'image-preview';
|
||||||
img.parentElement.classList.remove('zoomed');
|
// 设置放大的预览图片大小
|
||||||
close.classList.remove('img_close');
|
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";
|
MyCustomWidget.template = "jikimo_frontend.MyCustomWidget";
|
||||||
// MyCustomWidget.supportedTypes = ['many2many'];
|
|
||||||
|
|
||||||
registry.category("fields").add("custom_many2many_checkboxes", MyCustomWidget);
|
registry.category("fields").add("custom_many2many_checkboxes", MyCustomWidget);
|
||||||
|
|
||||||
|
|||||||
@@ -2,25 +2,20 @@
|
|||||||
<templates xml:space="preserve">
|
<templates xml:space="preserve">
|
||||||
|
|
||||||
<t t-name="jikimo_frontend.MyCustomWidget" owl="1">
|
<t t-name="jikimo_frontend.MyCustomWidget" owl="1">
|
||||||
<div aria-atomic="true" class="many2many_flex">
|
<div aria-atomic="true" class="many2many_flex processing-capabilities-grid">
|
||||||
<t t-foreach="items" t-as="item" t-key="item[0]">
|
<t t-foreach="items" t-as="item" t-key="item[0]">
|
||||||
<div>
|
<div class="grid-item">
|
||||||
<CheckBox
|
<CheckBox
|
||||||
value="isSelected(item)"
|
value="isSelected(item)"
|
||||||
disabled="props.readonly"
|
disabled="props.readonly"
|
||||||
onChange="(ev) => this.onChange(item[0], ev)"
|
onChange="(ev) => this.onChange(item[0], ev)"
|
||||||
>
|
>
|
||||||
<t t-esc="item[1]"/>
|
<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>
|
</CheckBox>
|
||||||
<div t-on-dblclick="onImageClick">
|
|
||||||
<t>
|
|
||||||
<img t-att-src="item[2]" width="50" height="50"/>
|
|
||||||
<div class="close" t-on-click="onCloseClick">×</div>
|
|
||||||
</t>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
</t>
|
</t>
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
|
|||||||
@@ -108,6 +108,10 @@ td.o_required_modifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.color_3 {
|
.color_3 {
|
||||||
|
background-color: #808080;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color_4 {
|
||||||
background-color: rgb(255, 150, 0);
|
background-color: rgb(255, 150, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<!-- hide 登录页面 powerd by odoo 及管理数据库 -->
|
<!-- hide 登录页面 powerd by odoo 及管理数据库 -->
|
||||||
<template id="login_page_layout" inherit_id="web.login_layout" name="Login Page Layout">
|
<template id="login_page_layout" inherit_id="web.login_layout" name="Login Page Layout">
|
||||||
<xpath expr="//div[@class='card-body']/div[last()]" position="replace"></xpath>
|
<!-- <xpath expr="//div[@class='card-body']/div[last()]" position="replace"></xpath> -->
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 隐藏odoo版本信息 -->
|
<!-- 隐藏odoo版本信息 -->
|
||||||
|
|||||||
@@ -109,7 +109,7 @@
|
|||||||
<field name="name">form.sf.machine_tool.type</field>
|
<field name="name">form.sf.machine_tool.type</field>
|
||||||
<field name="model">sf.machine_tool.type</field>
|
<field name="model">sf.machine_tool.type</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="机床型号" delete="0">
|
<form string="机床型号" delete="0">
|
||||||
<sheet>
|
<sheet>
|
||||||
<div class="oe_title">
|
<div class="oe_title">
|
||||||
<h1>
|
<h1>
|
||||||
@@ -129,31 +129,28 @@
|
|||||||
<field name="machine_tool_picture" widget="image" nolabel="1"/>
|
<field name="machine_tool_picture" widget="image" nolabel="1"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<group string="加工能力">
|
<group string="加工能力">
|
||||||
<div>
|
<div>
|
||||||
<field name='jg_image_id' widget="custom_many2many_checkboxes">
|
<field name='jg_image_id' widget="custom_many2many_checkboxes">
|
||||||
|
<tree>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="image" widget="image"/>
|
||||||
|
|
||||||
<tree>
|
</tree>
|
||||||
<field name="name"/>
|
</field>
|
||||||
<field name="image" widget="image"
|
|
||||||
options="{'size': [100, 100], 'click enlarge': True}"/>
|
|
||||||
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</group>
|
</group>
|
||||||
<group string="冷却方式">
|
<group string="冷却方式">
|
||||||
<div>
|
<div>
|
||||||
<field name='lq_image_id' widget="custom_many2many_checkboxes">
|
<field name='lq_image_id' widget="custom_many2many_checkboxes">
|
||||||
|
|
||||||
<tree>
|
<tree>
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="image" widget="image"
|
<field name="image" widget="image"/>
|
||||||
options="{'size': [100, 100], 'click enlarge': True}"/>
|
|
||||||
|
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</group>
|
</group>
|
||||||
@@ -178,7 +175,7 @@
|
|||||||
<field name="workbench_H" class="o_address_zip" required="1"
|
<field name="workbench_H" class="o_address_zip" required="1"
|
||||||
options="{'format': false}"/>
|
options="{'format': false}"/>
|
||||||
</div>
|
</div>
|
||||||
<field name="workpiece_load"/>
|
<field name="workpiece_load"/>
|
||||||
<label for="machine_tool_L" string="机床尺寸(mm)"/>
|
<label for="machine_tool_L" string="机床尺寸(mm)"/>
|
||||||
<div class="test_model">
|
<div class="test_model">
|
||||||
<label for="machine_tool_L" string="长"/>
|
<label for="machine_tool_L" string="长"/>
|
||||||
@@ -192,7 +189,7 @@
|
|||||||
<field name="machine_tool_H" class="o_address_zip"
|
<field name="machine_tool_H" class="o_address_zip"
|
||||||
options="{'format': false}"/>
|
options="{'format': false}"/>
|
||||||
</div>
|
</div>
|
||||||
<label for="T_trough_num" string="T型槽尺寸:"/>
|
<label for="T_trough_num" string="T型槽尺寸:"/>
|
||||||
<div class="test_model">
|
<div class="test_model">
|
||||||
<label for="T_trough_num" string="槽数"/>
|
<label for="T_trough_num" string="槽数"/>
|
||||||
<field name="T_trough_num" class="o_address_zip"
|
<field name="T_trough_num" class="o_address_zip"
|
||||||
@@ -205,20 +202,20 @@
|
|||||||
<field name="T_trough_distance" class="o_address_zip"
|
<field name="T_trough_distance" class="o_address_zip"
|
||||||
options="{'format': false}"/>
|
options="{'format': false}"/>
|
||||||
</div>
|
</div>
|
||||||
<!-- <field name="feed_speed" required="1"/>-->
|
<!-- <field name="feed_speed" required="1"/>-->
|
||||||
<!-- <label for="precision_min" string="X轴定位精度(mm)"/>-->
|
<!-- <label for="precision_min" string="X轴定位精度(mm)"/>-->
|
||||||
<!-- <div class="test_model">-->
|
<!-- <div class="test_model">-->
|
||||||
<!-- <label for="precision_min" string="最小(min)"/>-->
|
<!-- <label for="precision_min" string="最小(min)"/>-->
|
||||||
<!-- <field name="precision_min" class="o_address_zip" required="1"-->
|
<!-- <field name="precision_min" class="o_address_zip" required="1"-->
|
||||||
<!-- options="{'format': false}"/>-->
|
<!-- options="{'format': false}"/>-->
|
||||||
<!-- <span>&nbsp;</span>-->
|
<!-- <span>&nbsp;</span>-->
|
||||||
<!-- <label for="precision_max" string="最大(max)"/>-->
|
<!-- <label for="precision_max" string="最大(max)"/>-->
|
||||||
<!-- <field name="precision_max" class="o_address_zip" required="1"-->
|
<!-- <field name="precision_max" class="o_address_zip" required="1"-->
|
||||||
<!-- options="{'format': false}"/>-->
|
<!-- options="{'format': false}"/>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
|
|
||||||
<!-- <field name="lead_screw" required="1"/>-->
|
<!-- <field name="lead_screw" required="1"/>-->
|
||||||
<!-- <field name="guide_rail" required="1"/>-->
|
<!-- <field name="guide_rail" required="1"/>-->
|
||||||
<field name="number_of_axles" required="1" widget="radio"
|
<field name="number_of_axles" required="1" widget="radio"
|
||||||
options="{'horizontal': true}"/>
|
options="{'horizontal': true}"/>
|
||||||
<label for="x_axis" string="加工行程(mm)"
|
<label for="x_axis" string="加工行程(mm)"
|
||||||
@@ -258,7 +255,7 @@
|
|||||||
</group>
|
</group>
|
||||||
<group string="主轴">
|
<group string="主轴">
|
||||||
<field name="taper_type_id" required="1"/>
|
<field name="taper_type_id" required="1"/>
|
||||||
<label for="distance_min" string="主轴端面-工作台距离(mm)"/>
|
<label for="distance_min" string="主轴端面-工作台距离(mm)"/>
|
||||||
<div class="test_model">
|
<div class="test_model">
|
||||||
<label for="distance_min" string="最小(min)"/>
|
<label for="distance_min" string="最小(min)"/>
|
||||||
<field name="distance_min" class="o_address_zip"
|
<field name="distance_min" class="o_address_zip"
|
||||||
@@ -268,7 +265,7 @@
|
|||||||
<field name="distance_max" class="o_address_zip"
|
<field name="distance_max" class="o_address_zip"
|
||||||
options="{'format': false}"/>
|
options="{'format': false}"/>
|
||||||
</div>
|
</div>
|
||||||
<field name="rotate_speed" string="主轴最高转速(r/min)"
|
<field name="rotate_speed" string="主轴最高转速(r/min)"
|
||||||
options="{'format': false}"/>
|
options="{'format': false}"/>
|
||||||
<field name="spindle_center_distance"/>
|
<field name="spindle_center_distance"/>
|
||||||
<field name="spindle_continuous_power"/>
|
<field name="spindle_continuous_power"/>
|
||||||
@@ -286,50 +283,50 @@
|
|||||||
<page string="进给/精度参数">
|
<page string="进给/精度参数">
|
||||||
<group>
|
<group>
|
||||||
<group string="进给参数">
|
<group string="进给参数">
|
||||||
<field name="X_axis_rapid_traverse_speed"/>
|
<field name="X_axis_rapid_traverse_speed"/>
|
||||||
<field name="Y_axis_rapid_traverse_speed"/>
|
<field name="Y_axis_rapid_traverse_speed"/>
|
||||||
<field name="Z_axis_rapid_traverse_speed"/>
|
<field name="Z_axis_rapid_traverse_speed"/>
|
||||||
<field name="a_axis_rapid_traverse_speed"/>
|
<field name="a_axis_rapid_traverse_speed"/>
|
||||||
<field name="b_axis_rapid_traverse_speed"/>
|
<field name="b_axis_rapid_traverse_speed"/>
|
||||||
<field name="c_axis_rapid_traverse_speed"/>
|
<field name="c_axis_rapid_traverse_speed"/>
|
||||||
<field name="straight_cutting_feed_rate"/>
|
<field name="straight_cutting_feed_rate"/>
|
||||||
<field name="rotary_cutting_feed_rate"/>
|
<field name="rotary_cutting_feed_rate"/>
|
||||||
</group>
|
</group>
|
||||||
<group string="精度参数">
|
<group string="精度参数">
|
||||||
<field name="X_precision"/>
|
<field name="X_precision"/>
|
||||||
<field name="X_precision_repeat"/>
|
<field name="X_precision_repeat"/>
|
||||||
<field name="Y_precision"/>
|
<field name="Y_precision"/>
|
||||||
<field name="Y_precision_repeat"/>
|
<field name="Y_precision_repeat"/>
|
||||||
<field name="Z_precision"/>
|
<field name="Z_precision"/>
|
||||||
<field name="Z_precision_repeat"/>
|
<field name="Z_precision_repeat"/>
|
||||||
<field name="a_precision"/>
|
<field name="a_precision"/>
|
||||||
<field name="a_precision_repeat"/>
|
<field name="a_precision_repeat"/>
|
||||||
<field name="b_precision"/>
|
<field name="b_precision"/>
|
||||||
<field name="b_precision_repeat"/>
|
<field name="b_precision_repeat"/>
|
||||||
<field name="c_precision"/>
|
<field name="c_precision"/>
|
||||||
<field name="c_precision_repeat"/>
|
<field name="c_precision_repeat"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
</page>
|
</page>
|
||||||
<page string="刀库参数">
|
<page string="刀库参数">
|
||||||
<group>
|
<group>
|
||||||
<group string="刀具">
|
<group string="刀具">
|
||||||
<!-- <field name="knife_type" required="1"/>-->
|
<!-- <field name="knife_type" required="1"/>-->
|
||||||
<field name="number_of_knife_library" required="1" options="{'format': false}"/>
|
<field name="number_of_knife_library" required="1" options="{'format': false}"/>
|
||||||
<!-- <field name="tool_speed" required="1"/>-->
|
<!-- <field name="tool_speed" required="1"/>-->
|
||||||
<field name="tool_full_diameter_max"/>
|
<field name="tool_full_diameter_max"/>
|
||||||
<field name="tool_perimeter_diameter_max"/>
|
<field name="tool_perimeter_diameter_max"/>
|
||||||
<field name="tool_long_max"/>
|
<field name="tool_long_max"/>
|
||||||
<!-- <label for="tool_diameter_min" string="刀具刀径(mm)"/>-->
|
<!-- <label for="tool_diameter_min" string="刀具刀径(mm)"/>-->
|
||||||
<!-- <div class="test_model">-->
|
<!-- <div class="test_model">-->
|
||||||
<!-- <label for="tool_diameter_min" string="最小(min)"/>-->
|
<!-- <label for="tool_diameter_min" string="最小(min)"/>-->
|
||||||
<!-- <field name="tool_diameter_min" class="o_address_zip" required="1"-->
|
<!-- <field name="tool_diameter_min" class="o_address_zip" required="1"-->
|
||||||
<!-- options="{'format': false}"/>-->
|
<!-- options="{'format': false}"/>-->
|
||||||
<!-- <span>&nbsp;</span>-->
|
<!-- <span>&nbsp;</span>-->
|
||||||
<!-- <label for="tool_diameter_max" string="最大(max)"/>-->
|
<!-- <label for="tool_diameter_max" string="最大(max)"/>-->
|
||||||
<!-- <field name="tool_diameter_max" class="o_address_zip" required="1"-->
|
<!-- <field name="tool_diameter_max" class="o_address_zip" required="1"-->
|
||||||
<!-- options="{'format': false}"/>-->
|
<!-- options="{'format': false}"/>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<field name="tool_quality_max"/>
|
<field name="tool_quality_max"/>
|
||||||
<field name="T_tool_time"/>
|
<field name="T_tool_time"/>
|
||||||
<field name="C_tool_time"/>
|
<field name="C_tool_time"/>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
""",
|
""",
|
||||||
'category': 'sf',
|
'category': 'sf',
|
||||||
'website': 'https://www.sf.jikimo.com',
|
'website': 'https://www.sf.jikimo.com',
|
||||||
'depends': ['sf_base', 'web_widget_model_viewer', 'mrp_subcontracting', 'purchase_stock', 'uom', ],
|
'depends': ['sf_base', 'mrp_subcontracting', 'purchase_stock', 'uom'],
|
||||||
'data': [
|
'data': [
|
||||||
'data/product_data.xml',
|
'data/product_data.xml',
|
||||||
'data/uom_data.xml',
|
'data/uom_data.xml',
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
'category': 'sf',
|
'category': 'sf',
|
||||||
'author': 'jikimo',
|
'author': 'jikimo',
|
||||||
'website': 'https://sf.cs.jikimo.com',
|
'website': 'https://sf.cs.jikimo.com',
|
||||||
'depends': ['web', 'mail', 'sf_base', 'sf_manufacturing', 'barcodes', ],
|
'depends': ['web', 'sf_manufacturing', 'barcodes'],
|
||||||
'data': [
|
'data': [
|
||||||
# 定义权限组放在最上面
|
# 定义权限组放在最上面
|
||||||
# 权限组
|
# 权限组
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ db_config = {
|
|||||||
"user": "postgres",
|
"user": "postgres",
|
||||||
"password": "postgres",
|
"password": "postgres",
|
||||||
"port": "5432",
|
"port": "5432",
|
||||||
"host": "172.16.10.113"
|
"host": "172.16.10.131"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -24,6 +24,8 @@ def convert_to_seconds(time_str):
|
|||||||
|
|
||||||
if time_str is None:
|
if time_str is None:
|
||||||
return 0
|
return 0
|
||||||
|
if time_str == 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
||||||
|
|
||||||
@@ -1306,9 +1308,9 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
res['data'][item] = {
|
res['data'][item] = {
|
||||||
'wait_time': last_all_time['run_time'] if last_all_time['run_time'] is not None else 0,
|
'wait_time': last_all_time['run_time'] if last_all_time['run_time'] is not None else 0,
|
||||||
'cut_time': last_all_time['process_time'] if last_all_time['process_time'] is not None else 0,
|
'cut_time': last_all_time['process_time'] if last_all_time['process_time'] is not None else 0,
|
||||||
'cut_24_time': last_24_time['process_time'] if last_24_time['process_time'] is not None else 0,
|
'cut_24_time': last_24_time['process_time'] if last_24_time else 0,
|
||||||
'power_on_time': last_all_time['power_on_time'] if last_all_time['power_on_time'] is not None else 0,
|
'power_on_time': last_all_time['power_on_time'] if last_all_time['power_on_time'] is not None else 0,
|
||||||
'power_on_24_time': last_24_time['power_on_time'] if last_24_time['power_on_time'] is not None else 0,
|
'power_on_24_time': last_24_time['power_on_time'] if last_24_time else 0,
|
||||||
'alarm_last_24_time': alarm_last_24_time,
|
'alarm_last_24_time': alarm_last_24_time,
|
||||||
'alarm_last_24_nums': len(list(set(alarm_last_24_nums))),
|
'alarm_last_24_nums': len(list(set(alarm_last_24_nums))),
|
||||||
'idle_count': idle_count,
|
'idle_count': idle_count,
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ class Machine_ftp(models.Model):
|
|||||||
status = fields.Boolean('机床在线状态', readonly=True)
|
status = fields.Boolean('机床在线状态', readonly=True)
|
||||||
# run_status = fields.Selection([('0', '空闲中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')], string='机床运行状态',
|
# run_status = fields.Selection([('0', '空闲中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')], string='机床运行状态',
|
||||||
# readonly=True, default='0')
|
# readonly=True, default='0')
|
||||||
run_status = fields.Char('机床运行状态', readonly=True)
|
# run_status = fields.Char('机床运行状态', readonly=True)
|
||||||
run_time = fields.Char('机床累计运行时长', readonly=True)
|
run_time = fields.Char('机床累计运行时长', readonly=True)
|
||||||
# 机床系统日期
|
# 机床系统日期
|
||||||
system_date = fields.Char('机床系统日期', readonly=True)
|
system_date = fields.Char('机床系统日期', readonly=True)
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ class SfMaintenanceEquipment(models.Model):
|
|||||||
|
|
||||||
crea_url = "/api/machine_tool/create"
|
crea_url = "/api/machine_tool/create"
|
||||||
|
|
||||||
|
run_status = fields.Char('机床运行状态', readonly=True)
|
||||||
|
|
||||||
# AGV运行日志
|
# AGV运行日志
|
||||||
agv_logs = fields.One2many('maintenance.equipment.agv.log', 'equipment_id', string='AGV运行日志')
|
agv_logs = fields.One2many('maintenance.equipment.agv.log', 'equipment_id', string='AGV运行日志')
|
||||||
# 1212修改后的字段
|
# 1212修改后的字段
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ def convert_to_seconds(time_str):
|
|||||||
|
|
||||||
if time_str is None:
|
if time_str is None:
|
||||||
return 0
|
return 0
|
||||||
|
if time_str == 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
||||||
|
|
||||||
@@ -122,24 +124,37 @@ class SfMaintenanceEquipmentOEE(models.Model):
|
|||||||
real_dict = result_time['data'][self.equipment_code]
|
real_dict = result_time['data'][self.equipment_code]
|
||||||
print('=', result_time)
|
print('=', result_time)
|
||||||
if result_time['status'] == 1:
|
if result_time['status'] == 1:
|
||||||
self.online_time = round((convert_to_seconds(real_dict['power_on_time']) - convert_to_seconds(
|
if real_dict['power_on_24_time'] == 0:
|
||||||
real_dict['power_on_24_time'])) / 3600, 2)
|
self.online_time = 0
|
||||||
self.work_time = round(
|
self.idle_time = 0
|
||||||
(convert_to_seconds(real_dict['cut_time']) - convert_to_seconds(real_dict['cut_24_time'])) / 3600,
|
self.idle_rate = 0
|
||||||
2)
|
self.work_rate = 0
|
||||||
|
self.fault_time = 0
|
||||||
|
self.fault_rate = 0
|
||||||
|
self.fault_nums = 0
|
||||||
|
self.idle_nums = 0
|
||||||
|
self.work_time = 0
|
||||||
|
else:
|
||||||
|
self.online_time = round((convert_to_seconds(real_dict['power_on_time']) - convert_to_seconds(
|
||||||
|
real_dict['power_on_24_time'])) / 3600, 2)
|
||||||
|
self.idle_time = float(self.online_time) - float(
|
||||||
|
self.work_time) if self.online_time and self.work_time else 0
|
||||||
|
self.idle_rate = round(
|
||||||
|
float(self.idle_time) / (float(self.online_time) if self.online_time else 1) * 100, 2)
|
||||||
|
self.work_rate = round(
|
||||||
|
float(self.work_time) / (float(self.online_time) if self.online_time else 1) * 100, 2)
|
||||||
|
self.fault_time = (float(real_dict['alarm_last_24_time']) if real_dict[
|
||||||
|
'alarm_last_24_time'] else 0) / 3600
|
||||||
|
self.fault_rate = round(
|
||||||
|
float(self.fault_time) / (float(self.online_time) if self.online_time else 1) * 100, 2)
|
||||||
|
self.fault_nums = real_dict['alarm_last_24_nums']
|
||||||
|
self.idle_nums = real_dict['idle_count']
|
||||||
|
self.work_time = round(
|
||||||
|
(convert_to_seconds(real_dict['cut_time']) - convert_to_seconds(real_dict['cut_24_time'])) / 3600,
|
||||||
|
2)
|
||||||
self.offline_time = 24 - (float(self.online_time) if self.online_time else 0)
|
self.offline_time = 24 - (float(self.online_time) if self.online_time else 0)
|
||||||
self.idle_time = float(self.online_time) - float(
|
|
||||||
self.work_time) if self.online_time and self.work_time else 0
|
|
||||||
self.idle_rate = round(
|
|
||||||
float(self.idle_time) / (float(self.online_time) if self.online_time else 1) * 100, 2)
|
|
||||||
self.work_rate = round(
|
|
||||||
float(self.work_time) / (float(self.online_time) if self.online_time else 1) * 100, 2)
|
|
||||||
self.fault_time = (float(real_dict['alarm_last_24_time']) if real_dict[
|
|
||||||
'alarm_last_24_time'] else 0) / 3600
|
|
||||||
self.fault_rate = round(
|
|
||||||
float(self.fault_time) / (float(self.online_time) if self.online_time else 1) * 100, 2)
|
|
||||||
self.fault_nums = real_dict['alarm_last_24_nums']
|
|
||||||
self.idle_nums = real_dict['idle_count']
|
|
||||||
|
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = response.json()
|
result = response.json()
|
||||||
|
|||||||
@@ -1142,14 +1142,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="show_state" t-attf-class="oe_kanban_global_click o_kanban_record_has_image_fill o_hr_kanban_record oe_kanban_card oe_kanban_global_click
|
<div class="show_state" t-attf-class="oe_kanban_global_click o_kanban_record_has_image_fill o_hr_kanban_record oe_kanban_card oe_kanban_global_click
|
||||||
">
|
">
|
||||||
<div t-attf-class="#{record.state.raw_value == '正常' ? 'color_1' : ''}"></div>
|
<div t-attf-class="#{record.run_status.raw_value == '运行中' ? 'color_1' : ''}"></div>
|
||||||
<div t-attf-class="#{record.state.raw_value == '故障' ? 'color_2' : ''}"></div>
|
<div t-attf-class="#{record.run_status.raw_value == '待机' ? 'color_4' : ''}"></div>
|
||||||
<div t-attf-class="#{record.state.raw_value == '不可用' ? 'color_3' : ''}"></div>
|
<div t-attf-class="#{record.run_status.raw_value == '故障' ? 'color_2' : ''}"></div>
|
||||||
|
<div t-attf-class="#{record.run_status.raw_value == '离线' ? 'color_3' : ''}"></div>
|
||||||
<p class="o_kanban_record_bottom state_zc"
|
<p class="o_kanban_record_bottom state_zc"
|
||||||
t-attf-class="#{record.state.raw_value == '正常' ? 'font_color_1' : ''}
|
t-attf-class="#{record.run_status.raw_value == '运行中' ? 'font_color_1' : ''}
|
||||||
#{record.state.raw_value == '故障' ? 'font_color_2' : ''}
|
#{record.run_status.raw_value == '待机' ? 'font_color_4' : ''}
|
||||||
#{record.state.raw_value == '不可用' ? 'font_color_3' : ''}">
|
#{record.run_status.raw_value == '故障' ? 'font_color_2' : ''}
|
||||||
<field name="state"/>
|
#{record.run_status.raw_value == '离线' ? 'font_color_3' : ''}">
|
||||||
|
<field name="run_status"/>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -296,8 +296,13 @@ class MrpProduction(models.Model):
|
|||||||
# 编程单更新
|
# 编程单更新
|
||||||
def update_programming_state(self):
|
def update_programming_state(self):
|
||||||
try:
|
try:
|
||||||
|
manufacturing_type = 'rework'
|
||||||
|
if self.is_scrap:
|
||||||
|
manufacturing_type = 'scrap'
|
||||||
|
elif self.tool_state == '2':
|
||||||
|
manufacturing_type = 'invalid_tool_rework'
|
||||||
res = {'programming_no': self.programming_no,
|
res = {'programming_no': self.programming_no,
|
||||||
'manufacturing_type': 'rework' if self.is_scrap is False else 'scrap'}
|
'manufacturing_type': manufacturing_type}
|
||||||
logging.info('res=%s:' % res)
|
logging.info('res=%s:' % res)
|
||||||
configsettings = self.env['res.config.settings'].get_values()
|
configsettings = self.env['res.config.settings'].get_values()
|
||||||
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
|
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
import logging
|
||||||
from datetime import timedelta, time
|
from datetime import timedelta, time
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from odoo import fields, models, api
|
from odoo import fields, models, api
|
||||||
@@ -6,6 +7,8 @@ from odoo.addons.resource.models.resource import Intervals
|
|||||||
from odoo.exceptions import UserError, ValidationError
|
from odoo.exceptions import UserError, ValidationError
|
||||||
import math
|
import math
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ResWorkcenter(models.Model):
|
class ResWorkcenter(models.Model):
|
||||||
_name = "mrp.workcenter"
|
_name = "mrp.workcenter"
|
||||||
@@ -225,12 +228,16 @@ class ResWorkcenter(models.Model):
|
|||||||
if plan_ids:
|
if plan_ids:
|
||||||
sum_qty = sum([p.product_qty for p in plan_ids])
|
sum_qty = sum([p.product_qty for p in plan_ids])
|
||||||
date_planned_working_hours = self._compute_effective_working_hours_day1(date_planned)
|
date_planned_working_hours = self._compute_effective_working_hours_day1(date_planned)
|
||||||
if sum_qty >= date_planned_working_hours:
|
default_capacity = round(
|
||||||
|
self.production_line_hour_capacity * date_planned_working_hours, 2)
|
||||||
|
_logger.info('排程日期:%s,计划数量:%s,日产能:%s,日工时:%s' % (
|
||||||
|
date_planned, sum_qty, default_capacity, date_planned_working_hours))
|
||||||
|
if sum_qty >= default_capacity:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# 处理排程是否超过小时产能
|
# 处理排程是否超过小时产能
|
||||||
def deal_available_single_machine_capacity(self, date_planned):
|
def deal_available_single_machine_capacity(self, date_planned, count):
|
||||||
|
|
||||||
date_planned_start = date_planned.strftime('%Y-%m-%d %H:00:00')
|
date_planned_start = date_planned.strftime('%Y-%m-%d %H:00:00')
|
||||||
date_planned_end = date_planned + timedelta(hours=1)
|
date_planned_end = date_planned + timedelta(hours=1)
|
||||||
@@ -242,7 +249,11 @@ class ResWorkcenter(models.Model):
|
|||||||
|
|
||||||
if plan_ids:
|
if plan_ids:
|
||||||
sum_qty = sum([p.product_qty for p in plan_ids])
|
sum_qty = sum([p.product_qty for p in plan_ids])
|
||||||
if sum_qty >= self.production_line_hour_capacity:
|
production_line_hour_capacity = self.production_line_hour_capacity
|
||||||
|
if sum_qty >= production_line_hour_capacity:
|
||||||
|
message = '当前计划开始时间不能预约排程,超过生产线小时产能(%d件)%d件' % (
|
||||||
|
production_line_hour_capacity, count)
|
||||||
|
raise UserError(message)
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -1197,8 +1197,8 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
if record.is_rework is False:
|
if record.is_rework is False:
|
||||||
if not record.material_center_point:
|
if not record.material_center_point:
|
||||||
raise UserError("坯料中心点为空,请检查")
|
raise UserError("坯料中心点为空,请检查")
|
||||||
if record.X_deviation_angle <= 0:
|
# if record.X_deviation_angle <= 0:
|
||||||
raise UserError("X偏差角度小于等于0,请检查!本次计算的X偏差角度为:%s" % record.X_deviation_angle)
|
# raise UserError("X偏差角度小于等于0,请检查!本次计算的X偏差角度为:%s" % record.X_deviation_angle)
|
||||||
record.process_state = '待加工'
|
record.process_state = '待加工'
|
||||||
# record.write({'process_state': '待加工'})
|
# record.write({'process_state': '待加工'})
|
||||||
record.production_id.process_state = '待加工'
|
record.production_id.process_state = '待加工'
|
||||||
@@ -1827,6 +1827,11 @@ class WorkPieceDelivery(models.Model):
|
|||||||
else:
|
else:
|
||||||
raise UserError("接驳站暂未反馈站点实时状态,请稍后再试")
|
raise UserError("接驳站暂未反馈站点实时状态,请稍后再试")
|
||||||
|
|
||||||
|
def delivery_avg(self):
|
||||||
|
is_agv_task_dispatch = self.env['ir.config_parameter'].sudo().get_param('is_agv_task_dispatch')
|
||||||
|
if is_agv_task_dispatch:
|
||||||
|
self._delivery_avg()
|
||||||
|
|
||||||
# 配送至avg小车
|
# 配送至avg小车
|
||||||
def _delivery_avg(self):
|
def _delivery_avg(self):
|
||||||
config = self.env['res.config.settings'].get_values()
|
config = self.env['res.config.settings'].get_values()
|
||||||
@@ -1886,7 +1891,7 @@ class WorkPieceDelivery(models.Model):
|
|||||||
logging.info('delivery_item-name:%s' % delivery_item.name)
|
logging.info('delivery_item-name:%s' % delivery_item.name)
|
||||||
delivery_item.write({
|
delivery_item.write({
|
||||||
'task_delivery_time': fields.Datetime.now(),
|
'task_delivery_time': fields.Datetime.now(),
|
||||||
'status': '待配送'
|
'status': '已下发'
|
||||||
})
|
})
|
||||||
if delivery_item.type == "上产线":
|
if delivery_item.type == "上产线":
|
||||||
delivery_item.workorder_id.write({'is_delivery': True})
|
delivery_item.workorder_id.write({'is_delivery': True})
|
||||||
|
|||||||
@@ -562,7 +562,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<field name="priority" widget="priority"/>
|
<field name="priority" widget="priority"/>
|
||||||
</div>
|
</div>
|
||||||
|
<t t-if="record.brand_id.raw_value">
|
||||||
|
<div class="mt-1">
|
||||||
|
品牌:
|
||||||
|
<field name="brand_id"></field>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
<div name="product_specification_id" class="mt-1">
|
<div name="product_specification_id" class="mt-1">
|
||||||
规格:
|
规格:
|
||||||
<field name="specification_id"/>
|
<field name="specification_id"/>
|
||||||
|
|||||||
@@ -223,8 +223,27 @@
|
|||||||
</page>
|
</page>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//label[1]" position="before">
|
<xpath expr="//label[1]" position="before">
|
||||||
<field name='routing_type' readonly="1"/>
|
<!-- -->
|
||||||
<field name='process_state' attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/>
|
<field name="production_id" invisible="0"/>
|
||||||
|
<field name="duration_expected" invisible="1"/>
|
||||||
|
<field name="date_planned_start" invisible="1"/>
|
||||||
|
<field name="date_planned_finished" invisible="1"/>
|
||||||
|
<field name="duration" widget="mrp_timer"
|
||||||
|
invisible="1" sum="real duration"/>
|
||||||
|
<field name="glb_file" readonly="1" widget="Viewer3D" string="加工模型"/>
|
||||||
|
<field name="manual_quotation" readonly="1"
|
||||||
|
attrs="{'invisible': [('routing_type', '!=', 'CNC加工')]}"/>
|
||||||
|
<field name="processing_panel" readonly="1"
|
||||||
|
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
|
||||||
|
<field name="equipment_id" readonly="1"
|
||||||
|
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割","装夹预调"))]}'/>
|
||||||
|
<field name="production_line_id"
|
||||||
|
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割","装夹预调"))]}'/>
|
||||||
|
<field name="production_line_state" readonly="1"
|
||||||
|
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割","装夹预调"))]}'/>
|
||||||
|
|
||||||
|
<field name='routing_type' invisible="1"/>
|
||||||
|
<field name='process_state' invisible="1"/>
|
||||||
<field name='tag_type' readonly="1" attrs='{"invisible": [("tag_type","=",False)]}'
|
<field name='tag_type' readonly="1" attrs='{"invisible": [("tag_type","=",False)]}'
|
||||||
decoration-danger="tag_type == '重新加工'"/>
|
decoration-danger="tag_type == '重新加工'"/>
|
||||||
<field name="rfid_code" force_save="1" readonly="0" cache="True"
|
<field name="rfid_code" force_save="1" readonly="0" cache="True"
|
||||||
@@ -232,6 +251,24 @@
|
|||||||
<field name="rfid_code_old" readonly="1" attrs="{'invisible': [('rfid_code_old', '=', False)]}"/>
|
<field name="rfid_code_old" readonly="1" attrs="{'invisible': [('rfid_code_old', '=', False)]}"/>
|
||||||
|
|
||||||
</xpath>
|
</xpath>
|
||||||
|
<xpath expr="//form//sheet//group//group//div[2]" position="replace">
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//form//sheet//group//group//div[1]" position="after">
|
||||||
|
<field name="save_name" widget="CopyClipboardChar"
|
||||||
|
attrs="{'invisible':[('routing_type','!=','装夹预调')]}"/>
|
||||||
|
<label for="material_length" string="物料尺寸"/>
|
||||||
|
<div class="o_address_format">
|
||||||
|
<label for="material_length" string="长"/>
|
||||||
|
<field name="material_length" class="o_address_zip"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="material_width" string="宽"/>
|
||||||
|
<field name="material_width" class="o_address_zip"/>
|
||||||
|
<span>&nbsp;</span>
|
||||||
|
<label for="material_height" string="高"/>
|
||||||
|
<field name="material_height" class="o_address_zip"/>
|
||||||
|
</div>
|
||||||
|
<field name="part_number" string="成品的零件图号"/>
|
||||||
|
</xpath>
|
||||||
<xpath expr="//label[1]" position="attributes">
|
<xpath expr="//label[1]" position="attributes">
|
||||||
<attribute name="string">计划加工时间</attribute>
|
<attribute name="string">计划加工时间</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
@@ -248,76 +285,12 @@
|
|||||||
<field name='materiel_width' string="宽"/>
|
<field name='materiel_width' string="宽"/>
|
||||||
<field name='materiel_height' string="高"/>
|
<field name='materiel_height' string="高"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<field name="production_id" position="after" invisible="0">
|
|
||||||
<group>
|
|
||||||
<field name="date_planned_start" invisible="1"/>
|
|
||||||
<field name="date_planned_finished" invisible="1"/>
|
|
||||||
<!-- <field name="production_id" readonly="1"/>-->
|
|
||||||
<field name="duration" widget="mrp_timer"
|
|
||||||
attrs="{'invisible': [('production_state','=', 'draft')], 'readonly': [('is_user_working', '=', True)]}"
|
|
||||||
sum="real duration"/>
|
|
||||||
<field name="glb_file" readonly="1" widget="Viewer3D" string="加工模型"/>
|
|
||||||
<field name="manual_quotation" readonly="1"
|
|
||||||
attrs="{'invisible': [('routing_type', '!=', 'CNC加工')]}"/>
|
|
||||||
<field name="processing_panel" readonly="1"
|
|
||||||
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
|
|
||||||
<field name="equipment_id" readonly="1"
|
|
||||||
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割","装夹预调"))]}'/>
|
|
||||||
<field name="production_line_id"
|
|
||||||
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
|
|
||||||
<field name="production_line_state" readonly="0"
|
|
||||||
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割","装夹预调"))]}'/>
|
|
||||||
<!-- <field name="functional_fixture_id" -->
|
|
||||||
<!-- attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/> -->
|
|
||||||
<!-- <field name="functional_fixture_code" force_save="1" -->
|
|
||||||
<!-- attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/> -->
|
|
||||||
<!-- <field name="functional_fixture_type_id" -->
|
|
||||||
<!-- attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/> -->
|
|
||||||
</group>
|
|
||||||
<!-- <group>-->
|
|
||||||
<!-- <div>-->
|
|
||||||
<!-- <label for="glb_file" string="加工模型"/>-->
|
|
||||||
<!-- <field name="glb_file" readonly="1" widget="Viewer3D"/>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
<!-- <!– <field name="glb_file" string="模型" readonly="1" widget="Viewer3D"/>–>-->
|
|
||||||
<!-- </group>-->
|
|
||||||
|
|
||||||
<!-- <field name="processing_panel" readonly="1" attrs="{'invisible': [('routing_type', 'in', ('获取CNC加工程序','装夹','解除装夹',-->
|
|
||||||
<!-- '前置三元定位检测','后置三元质量检测','解除装夹'))]}"/>-->
|
|
||||||
</field>
|
|
||||||
<!-- <page string="Components" name="components">-->
|
|
||||||
<xpath expr="//page[1]" position="before">
|
<xpath expr="//page[1]" position="before">
|
||||||
<!-- <page string="装夹托盘" attrs='{"invisible": [("routing_type","!=","装夹")]}'>-->
|
|
||||||
<!-- <group>-->
|
|
||||||
<!-- <field name="routing_type" invisible="1"/>-->
|
|
||||||
<!-- <field name="tray_code"/>-->
|
|
||||||
<!-- <field name="tray_id" readonly="1"/>-->
|
|
||||||
<!-- </group>-->
|
|
||||||
<!-- <group>-->
|
|
||||||
<!-- <field name="pro_code" readonly="1" attrs='{"invisible": [("pro_code_ok","=",False)]}'-->
|
|
||||||
<!-- style="color:green"/>-->
|
|
||||||
<!-- <field name="pro_code" readonly="1" attrs='{"invisible": [("pro_code_ok","!=",False)]}'/>-->
|
|
||||||
<!-- <div>-->
|
|
||||||
<!-- <field name="pro_code_ok" invisible="1"/>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
|
|
||||||
<!-- </group>-->
|
|
||||||
<!-- <div class="col-12 col-lg-6 o_setting_box">-->
|
|
||||||
<!-- <button type="object" class="oe_highlight" name="gettray" string="绑定托盘"-->
|
|
||||||
<!-- attrs='{"invisible": ["|","|",("tray_id","!=",False),("state","!=","progress"),("production_id","=",False)]}'/>-->
|
|
||||||
|
|
||||||
<!-- </div>-->
|
|
||||||
<!-- </page>-->
|
|
||||||
<page string="工件装夹" attrs='{"invisible": [("routing_type","!=","装夹预调")]}'>
|
<page string="工件装夹" attrs='{"invisible": [("routing_type","!=","装夹预调")]}'>
|
||||||
<group>
|
<group>
|
||||||
<field name="_barcode_scanned" widget="barcode_handler"/>
|
<field name="_barcode_scanned" widget="barcode_handler"/>
|
||||||
<!-- <group string="卡盘">-->
|
|
||||||
<!-- <field name="chuck_serial_number"/>-->
|
|
||||||
<!-- <field name="chuck_name"/>-->
|
|
||||||
<!-- <field name="chuck_brand_id"/>-->
|
|
||||||
<!-- <field name="chuck_type_id"/>-->
|
|
||||||
<!-- <field name="chuck_model_id"/>-->
|
|
||||||
<!-- </group>-->
|
|
||||||
<group string="托盘">
|
<group string="托盘">
|
||||||
<field name="tray_serial_number" readonly="1" string="序列号"/>
|
<field name="tray_serial_number" readonly="1" string="序列号"/>
|
||||||
</group>
|
</group>
|
||||||
@@ -332,10 +305,6 @@
|
|||||||
<field name="tray_model_id" readonly="1" string="型号"/>
|
<field name="tray_model_id" readonly="1" string="型号"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<group string="加工图纸">
|
|
||||||
<!-- 隐藏加工图纸字段名 -->
|
|
||||||
<field name="processing_drawing" widget="pdf_viewer" string="" readonly="1"/>
|
|
||||||
</group>
|
|
||||||
<group string="预调程序信息">
|
<group string="预调程序信息">
|
||||||
<field name="preset_program_information" colspan="2" nolabel="1"
|
<field name="preset_program_information" colspan="2" nolabel="1"
|
||||||
placeholder="如有预调程序信息请在此处输入....."/>
|
placeholder="如有预调程序信息请在此处输入....."/>
|
||||||
@@ -574,9 +543,7 @@
|
|||||||
<!-- <field name="button_state" invisible="1"/>-->
|
<!-- <field name="button_state" invisible="1"/>-->
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
<group>
|
|
||||||
<field name="cnc_worksheet" string="工作指令" widget="pdf_viewer"/>
|
|
||||||
</group>
|
|
||||||
</page>
|
</page>
|
||||||
<page string="CMM程序" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
|
<page string="CMM程序" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
|
||||||
<field name="cmm_ids" widget="one2many" string="CMM程序" readonly="1">
|
<field name="cmm_ids" widget="one2many" string="CMM程序" readonly="1">
|
||||||
@@ -605,33 +572,43 @@
|
|||||||
|
|
||||||
</page>
|
</page>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//form//sheet//group//group//div[1]" position="after">
|
<!-- <xpath expr="//form//sheet//group//group//div[1]" position="after">-->
|
||||||
<label for="date_start" string="实际加工时间"/>
|
<!-- <label for="date_start" string="实际加工时间"/>-->
|
||||||
<div class="oe_inline">
|
<!-- <div class="oe_inline">-->
|
||||||
<field name="date_start" class="mr8 oe_inline"/>
|
<!-- <field name="date_start" class="mr8 oe_inline"/>-->
|
||||||
<strong class="mr8 oe_inline">到</strong>
|
<!-- <strong class="mr8 oe_inline">到</strong>-->
|
||||||
<field name="date_finished" class="oe_inline"/>
|
<!-- <field name="date_finished" class="oe_inline"/>-->
|
||||||
</div>
|
<!-- </div>-->
|
||||||
</xpath>
|
<!-- </xpath>-->
|
||||||
<xpath expr="//form//sheet//group//group//div[3]" position="after">
|
<xpath expr="//page[@name='time_tracking']//field[@name='time_ids']//tree//field[@name='date_end']"
|
||||||
<field name="save_name" widget="CopyClipboardChar"
|
position="after">
|
||||||
attrs="{'invisible':[('routing_type','!=','装夹预调')]}"/>
|
<field name="duration" string="实际时长"/>
|
||||||
<label for="material_length" string="物料尺寸"/>
|
|
||||||
<div class="o_address_format">
|
|
||||||
<label for="material_length" string="长"/>
|
|
||||||
<field name="material_length" class="o_address_zip"/>
|
|
||||||
<span>&nbsp;</span>
|
|
||||||
<label for="material_width" string="宽"/>
|
|
||||||
<field name="material_width" class="o_address_zip"/>
|
|
||||||
<span>&nbsp;</span>
|
|
||||||
<label for="material_height" string="高"/>
|
|
||||||
<field name="material_height" class="o_address_zip"/>
|
|
||||||
</div>
|
|
||||||
<field name="part_number" string="成品的零件图号"/>
|
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="view_mrp_production_workorder_tray_form_inherit_sf_1" model="ir.ui.view">
|
||||||
|
<field name="name">mrp.production.workorder.tray.form.inherit.sf.1</field>
|
||||||
|
<field name="model">mrp.workorder</field>
|
||||||
|
<field name="inherit_id" ref="sf_manufacturing.view_mrp_production_workorder_tray_form_inherit_sf"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//form//sheet//group//group[2]" position="replace">
|
||||||
|
<group string="装夹图纸" attrs="{'invisible': [('routing_type', '!=', '装夹预调')]}">
|
||||||
|
<!-- 隐藏加工图纸字段名 -->
|
||||||
|
<field name="processing_drawing" widget="pdf_viewer" string="" readonly="1"/>
|
||||||
|
<!-- <field name="production_id" invisible="0"/>-->
|
||||||
|
</group>
|
||||||
|
<group string="工作指令" attrs="{'invisible': [('routing_type', '!=', 'CNC加工')]}">
|
||||||
|
<field name="cnc_worksheet" string="" widget="pdf_viewer"/>
|
||||||
|
</group>
|
||||||
|
</xpath>
|
||||||
|
<!-- <xpath expr="//form//sheet//group//group[1]" position="before">-->
|
||||||
|
<!-- <field name="production_id"/>-->
|
||||||
|
<!-- </xpath>-->
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
<record id="workcenter_form_workorder_search" model="ir.ui.view">
|
<record id="workcenter_form_workorder_search" model="ir.ui.view">
|
||||||
<field name="name">custom.workorder.search</field>
|
<field name="name">custom.workorder.search</field>
|
||||||
<field name="model">mrp.workorder</field>
|
<field name="model">mrp.workorder</field>
|
||||||
@@ -806,7 +783,7 @@
|
|||||||
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
|
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
|
||||||
<!-- <field name="type" readonly="1"/>-->
|
<!-- <field name="type" readonly="1"/>-->
|
||||||
<field name="feeder_station_destination_id" readonly="1" force_save="1"/>
|
<field name="feeder_station_destination_id" readonly="1" force_save="1"/>
|
||||||
<button name="button_delivery" type="object" string="配送" class="oe_highlight"/>
|
<button name="delivery_avg" type="object" string="配送" class="oe_highlight"/>
|
||||||
<button name="action_delivery_history" type="object" class="btn btn-link text-info" icon="fa-history"
|
<button name="action_delivery_history" type="object" class="btn btn-link text-info" icon="fa-history"
|
||||||
string="历史"/>
|
string="历史"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
""",
|
""",
|
||||||
'category': 'sf',
|
'category': 'sf',
|
||||||
'website': 'https://www.sf.jikimo.com',
|
'website': 'https://www.sf.jikimo.com',
|
||||||
'depends': ['sale', 'purchase', 'sf_plan', 'jikimo_message_notify', 'stock', 'sf_quality'],
|
'depends': ['sale', 'purchase', 'sf_plan', 'jikimo_message_notify', 'stock', 'sf_quality','mrp'],
|
||||||
'data': [
|
'data': [
|
||||||
'data/bussiness_node.xml',
|
'data/bussiness_node.xml',
|
||||||
'data/cron_data.xml',
|
'data/cron_data.xml',
|
||||||
|
|||||||
@@ -49,6 +49,23 @@
|
|||||||
<field name="name">工单已下发通知</field>
|
<field name="name">工单已下发通知</field>
|
||||||
<field name="model">mrp.workorder</field>
|
<field name="model">mrp.workorder</field>
|
||||||
</record>
|
</record>
|
||||||
|
<!--发货调拨-->
|
||||||
|
<record id="production_completed_warehouse_reminder" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">生产完工入库提醒</field>
|
||||||
|
<field name="model">mrp.production</field>
|
||||||
|
</record>
|
||||||
|
<record id="order_delivery_reminder" 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">-->
|
||||||
|
<!-- <field name="name">装夹预调工单逾期预警</field>-->
|
||||||
|
<!-- <field name="model">mrp.workorder</field>-->
|
||||||
|
<!-- </record>-->
|
||||||
|
<!-- <record id="bussiness_mrp_workorder_pre_overdue" model="jikimo.message.bussiness.node">-->
|
||||||
|
<!-- <field name="name">装夹预调工单已逾期</field>-->
|
||||||
|
<!-- <field name="model">mrp.workorder</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">
|
||||||
|
|||||||
@@ -187,8 +187,8 @@
|
|||||||
|
|
||||||
|
|
||||||
<record id="template_transfer_inventory_remind" model="jikimo.message.template">
|
<record id="template_transfer_inventory_remind" model="jikimo.message.template">
|
||||||
<field name="menu_id" ref="stock.menu_stock_root"/>
|
<!-- <field name="menu_id" ref="stock.menu_stock_root"/>-->
|
||||||
<field name="action_id" ref="stock.action_picking_tree_ready"/>
|
<!-- <field name="action_id" ref="stock.action_picking_tree_ready"/>-->
|
||||||
<field name="name">调拨入库</field>
|
<field name="name">调拨入库</field>
|
||||||
<field name="model_id" ref="stock.model_stock_picking"/>
|
<field name="model_id" ref="stock.model_stock_picking"/>
|
||||||
<field name="model">stock.picking</field>
|
<field name="model">stock.picking</field>
|
||||||
@@ -196,13 +196,13 @@
|
|||||||
<field name="msgtype">markdown</field>
|
<field name="msgtype">markdown</field>
|
||||||
<field name="urgency">normal</field>
|
<field name="urgency">normal</field>
|
||||||
<field name="content">### 调拨入库通知:
|
<field name="content">### 调拨入库通知:
|
||||||
单号:调拨入库单[{{name}}]({{request_url}})
|
单号:调拨入库单[{{name}}]({{transfer_inventory_special_url}})
|
||||||
事项:完成刀具物料上架入库</field>
|
事项:完成刀具物料上架入库</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="template_tool_expired_remind" model="jikimo.message.template">
|
<record id="template_tool_expired_remind" model="jikimo.message.template">
|
||||||
<field name="menu_id" ref="mrp.menu_mrp_root"/>
|
<!-- <field name="menu_id" ref="mrp.menu_mrp_root"/>-->
|
||||||
<field name="action_id" ref="sf_tool_management.sf_functional_tool_dismantle_view_act"/>
|
<!-- <field name="action_id" ref="sf_tool_management.sf_functional_tool_dismantle_view_act"/>-->
|
||||||
<field name="name">功能刀具寿命到期</field>
|
<field name="name">功能刀具寿命到期</field>
|
||||||
<field name="model_id" ref="sf_tool_management.model_sf_functional_tool_dismantle"/>
|
<field name="model_id" ref="sf_tool_management.model_sf_functional_tool_dismantle"/>
|
||||||
<field name="model">sf.functional.tool.dismantle</field>
|
<field name="model">sf.functional.tool.dismantle</field>
|
||||||
@@ -210,13 +210,13 @@
|
|||||||
<field name="msgtype">markdown</field>
|
<field name="msgtype">markdown</field>
|
||||||
<field name="urgency">normal</field>
|
<field name="urgency">normal</field>
|
||||||
<field name="content">### 功能刀具寿命到期提醒:
|
<field name="content">### 功能刀具寿命到期提醒:
|
||||||
单号:拆解单[{{code}}]({{request_url}})
|
单号:拆解单[{{code}}]({{tool_expired_remind_special_url}})
|
||||||
事项:{{functional_tool_id.tool_name_id.name}}寿命已到期,需拆解</field>
|
事项:{{functional_tool_id.tool_name_id.name}}寿命已到期,需拆解</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="template_tool_assembly_remind" model="jikimo.message.template">
|
<record id="template_tool_assembly_remind" model="jikimo.message.template">
|
||||||
<field name="menu_id" ref="mrp.menu_mrp_root"/>
|
<!-- <field name="menu_id" ref="mrp.menu_mrp_root"/>-->
|
||||||
<field name="action_id" ref="sf_tool_management.sf_functional_tool_assembly_view_act"/>
|
<!-- <field name="action_id" ref="sf_tool_management.sf_functional_tool_assembly_view_act"/>-->
|
||||||
<field name="name">功能刀具组装</field>
|
<field name="name">功能刀具组装</field>
|
||||||
<field name="model_id" ref="sf_tool_management.model_sf_functional_tool_assembly"/>
|
<field name="model_id" ref="sf_tool_management.model_sf_functional_tool_assembly"/>
|
||||||
<field name="model">sf.functional.tool.assembly</field>
|
<field name="model">sf.functional.tool.assembly</field>
|
||||||
@@ -224,8 +224,34 @@
|
|||||||
<field name="msgtype">markdown</field>
|
<field name="msgtype">markdown</field>
|
||||||
<field name="urgency">normal</field>
|
<field name="urgency">normal</field>
|
||||||
<field name="content">### 功能刀具组装通知:
|
<field name="content">### 功能刀具组装通知:
|
||||||
单号:组装任务单[{{name}}]({{request_url}})
|
单号:组装任务单[{{name}}]({{tool_assembly_special_url}})
|
||||||
事项:{{use_tool_time}}前完成组装</field>
|
事项:{{use_tool_time}}前完成组装</field>
|
||||||
|
</record>
|
||||||
|
<record id="template_production_completed_remind" model="jikimo.message.template">
|
||||||
|
<!-- <field name="menu_id" ref="mrp.menu_mrp_root"/>-->
|
||||||
|
<!-- <field name="action_id" ref="mrp.mrp_production_action"/>-->
|
||||||
|
<field name="name">生产完工入库提醒</field>
|
||||||
|
<field name="model_id" ref="mrp.model_mrp_production"/>
|
||||||
|
<field name="model">mrp.production</field>
|
||||||
|
<field name="bussiness_node_id" ref="production_completed_warehouse_reminder"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="urgency">normal</field>
|
||||||
|
<field name="content">### 生产完工入库提醒:
|
||||||
|
单号:生产入库单[{{name}}]({{request_url}})
|
||||||
|
事项:销售订单{{sale_order_name}}已全部产出,请入库处理</field>
|
||||||
|
</record>
|
||||||
|
<record id="template_order_delivery_remind" model="jikimo.message.template">
|
||||||
|
<!-- <field name="menu_id" ref="stock.menu_stock_root"/>-->
|
||||||
|
<!-- <field name="action_id" ref="stock.action_picking_tree_ready"/>-->
|
||||||
|
<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_delivery_reminder"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="urgency">normal</field>
|
||||||
|
<field name="content">### 订单发货提醒:
|
||||||
|
单号:发料出库单[{{name}}]({{request_url}})
|
||||||
|
事项:销售订单{{sale_order_name}}已全部产出并入库,请及时发货</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="template_quality_cnc_test" model="jikimo.message.template">
|
<record id="template_quality_cnc_test" model="jikimo.message.template">
|
||||||
|
|||||||
@@ -7,4 +7,5 @@ from . import sf_message_functional_tool_assembly
|
|||||||
from . import sf_message_purchase
|
from . import sf_message_purchase
|
||||||
from . import sf_message_workorder
|
from . import sf_message_workorder
|
||||||
from . import sf_message_functional_tool_dismantle
|
from . import sf_message_functional_tool_dismantle
|
||||||
|
from . import sf_message_mrp_production
|
||||||
from . import sf_message_quality_cnc_test
|
from . import sf_message_quality_cnc_test
|
||||||
|
|||||||
@@ -13,3 +13,14 @@ class SFMessagefunctionalToolAssembly(models.Model):
|
|||||||
if obj.loading_task_source == '0' and obj.assemble_status == '0':
|
if obj.loading_task_source == '0' and obj.assemble_status == '0':
|
||||||
obj.add_queue('功能刀具组装')
|
obj.add_queue('功能刀具组装')
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_special_url(self,id,tmplate_name,special_name,model_id):
|
||||||
|
menu_id = 0
|
||||||
|
action_id = 0
|
||||||
|
if tmplate_name=='调拨入库' and special_name== 'tool_assembly_special_url':
|
||||||
|
menu_id = self.env.ref('mrp.menu_mrp_root').id
|
||||||
|
action_id = self.env.ref('sf_tool_management.sf_functional_tool_assembly_view_act').id
|
||||||
|
return super(SFMessagefunctionalToolAssembly, self).get_url(id, menu_id, action_id,model_id)
|
||||||
|
else:
|
||||||
|
return super(SFMessagefunctionalToolAssembly, self).get_special_url(id, tmplate_name, special_name, model_id)
|
||||||
@@ -17,3 +17,13 @@ class SFMessagefunctionalToolDismantle(models.Model):
|
|||||||
if obj.dismantle_cause in ['寿命到期报废', '崩刀报废'] and obj.state == '待拆解':
|
if obj.dismantle_cause in ['寿命到期报废', '崩刀报废'] and obj.state == '待拆解':
|
||||||
obj.add_queue('功能刀具寿命到期')
|
obj.add_queue('功能刀具寿命到期')
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def get_special_url(self,id,tmplate_name,special_name,model_id):
|
||||||
|
menu_id = 0
|
||||||
|
action_id = 0
|
||||||
|
if tmplate_name=='调拨入库' and special_name== 'tool_expired_remind_special_url':
|
||||||
|
menu_id = self.env.ref('mrp.menu_mrp_root').id
|
||||||
|
action_id = self.env.ref('sf_tool_management.sf_functional_tool_dismantle_view_act').id
|
||||||
|
return super(SFMessagefunctionalToolDismantle, self).get_url(id, menu_id, action_id,model_id)
|
||||||
|
else:
|
||||||
|
return super(SFMessagefunctionalToolDismantle, self).get_special_url(id, tmplate_name, special_name, model_id)
|
||||||
55
sf_message/models/sf_message_mrp_production.py
Normal file
55
sf_message/models/sf_message_mrp_production.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import re
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
|
|
||||||
|
class SFMessageMrpProduction(models.Model):
|
||||||
|
_name = 'mrp.production'
|
||||||
|
_description = "制造订单"
|
||||||
|
_inherit = ['mrp.production', 'jikimo.message.dispatch']
|
||||||
|
|
||||||
|
@api.depends(
|
||||||
|
'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state',
|
||||||
|
'workorder_ids.state', 'product_qty', 'qty_producing')
|
||||||
|
def _compute_state(self):
|
||||||
|
super(SFMessageMrpProduction, self)._compute_state()
|
||||||
|
for record in self:
|
||||||
|
if record.state in ['scrap', 'done']:
|
||||||
|
# 查询制造订单下的所有未完成的生产订单
|
||||||
|
mrp_production = record.env['mrp.production'].search(
|
||||||
|
[('origin', '=', record.origin), ('state', 'not in', ['scrap', 'done'])])
|
||||||
|
if not mrp_production:
|
||||||
|
mrp_production_queue = self.env["jikimo.message.queue"].search([('res_id', '=', record.id)])
|
||||||
|
if not mrp_production_queue:
|
||||||
|
record.add_queue('生产完工入库提醒')
|
||||||
|
|
||||||
|
# 获取发送消息内容
|
||||||
|
def _get_message(self, message_queue_ids):
|
||||||
|
contents = []
|
||||||
|
for message_queue_id in message_queue_ids:
|
||||||
|
if message_queue_id.message_template_id.name == '生产完工入库提醒':
|
||||||
|
content = message_queue_id.message_template_id.content
|
||||||
|
mrp_production = self.env['mrp.production'].search([('id', '=', int(message_queue_id.res_id))])
|
||||||
|
if mrp_production and len(mrp_production) > 0:
|
||||||
|
stock_picking_sfp = self.env['stock.picking'].search(
|
||||||
|
[('origin', '=', mrp_production.origin), ('picking_type_id.sequence_code', '=', 'SFP'),
|
||||||
|
('state', '=', 'assigned')], limit=1)
|
||||||
|
if stock_picking_sfp:
|
||||||
|
url = self.request_url()
|
||||||
|
content = content.replace('{{name}}', stock_picking_sfp.name).replace(
|
||||||
|
'{{sale_order_name}}', mrp_production.origin).replace('{{request_url}}', url)
|
||||||
|
contents.append(content)
|
||||||
|
return contents
|
||||||
|
|
||||||
|
def request_url(self):
|
||||||
|
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||||
|
action_id = self.env.ref('mrp.mrp_production_action').id
|
||||||
|
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_theme_treehouse')]).id
|
||||||
|
# 查询参数
|
||||||
|
params = {'menu_id': menu_id, 'action': action_id, 'model': 'mrp.production',
|
||||||
|
'view_type': 'kanban'}
|
||||||
|
# 拼接查询参数
|
||||||
|
query_string = urlencode(params)
|
||||||
|
# 拼接URL
|
||||||
|
full_url = url + "/web#" + query_string
|
||||||
|
return full_url
|
||||||
@@ -19,9 +19,7 @@ class SFMessagePurchase(models.Model):
|
|||||||
return contents
|
return contents
|
||||||
|
|
||||||
def request_url(self, id):
|
def request_url(self, id):
|
||||||
we_config_info = self.env['we.config'].sudo().search([], limit=1)
|
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||||
redirect_domain = self.env['we.app'].sudo().search([('id', '=', we_config_info.odoo_app_id.id)]).redirect_domain
|
|
||||||
full_url = 'https://%s/' % redirect_domain
|
|
||||||
action_id = self.env.ref('purchase.purchase_form_action').id
|
action_id = self.env.ref('purchase.purchase_form_action').id
|
||||||
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_website_payment')]).id
|
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_website_payment')]).id
|
||||||
# 查询参数
|
# 查询参数
|
||||||
@@ -31,5 +29,5 @@ class SFMessagePurchase(models.Model):
|
|||||||
# 拼接查询参数
|
# 拼接查询参数
|
||||||
query_string = urlencode(params)
|
query_string = urlencode(params)
|
||||||
# 拼接URL
|
# 拼接URL
|
||||||
full_url = full_url + "web#" + query_string
|
full_url = url + "/web#" + query_string
|
||||||
return full_url
|
return full_url
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class SFMessageSale(models.Model):
|
|||||||
_name = 'sale.order'
|
_name = 'sale.order'
|
||||||
_inherit = ['sale.order', 'jikimo.message.dispatch']
|
_inherit = ['sale.order', 'jikimo.message.dispatch']
|
||||||
|
|
||||||
|
@api.model_create_multi
|
||||||
def create(self, vals_list):
|
def create(self, vals_list):
|
||||||
res = super(SFMessageSale, self).create(vals_list)
|
res = super(SFMessageSale, self).create(vals_list)
|
||||||
if res:
|
if res:
|
||||||
|
|||||||
@@ -23,6 +23,28 @@ class SFMessageStockPicking(models.Model):
|
|||||||
if record.state == 'assigned' and record.check_in == 'PC':
|
if record.state == 'assigned' and record.check_in == 'PC':
|
||||||
record.add_queue('坯料发料提醒')
|
record.add_queue('坯料发料提醒')
|
||||||
|
|
||||||
|
if record.picking_type_id.sequence_code == 'SFP' and record.state == 'done':
|
||||||
|
stock_picking_sfp = record.env['stock.picking'].search(
|
||||||
|
[('origin', '=', record.origin), ('state', '!=', 'done'),
|
||||||
|
('picking_type_id.sequence_code', '=', 'SFP')])
|
||||||
|
if not stock_picking_sfp:
|
||||||
|
stock_picking_send = self.env["jikimo.message.queue"].search([('res_id', '=', record.id)])
|
||||||
|
if not stock_picking_send:
|
||||||
|
record.add_queue('订单发货提醒')
|
||||||
|
|
||||||
|
def deal_stock_picking_sfp(self, message_queue_id): # 处理订单发货提醒
|
||||||
|
content = None
|
||||||
|
stock_picking = self.env['stock.picking'].search([('id', '=', int(message_queue_id.res_id))])
|
||||||
|
stock_picking_out = self.env['stock.picking'].search(
|
||||||
|
[('origin', '=', stock_picking.origin), ('state', '=', 'assigned'),
|
||||||
|
('picking_type_id.sequence_code', '=', 'OUT')])
|
||||||
|
if stock_picking_out and len(stock_picking_out) > 0:
|
||||||
|
content = message_queue_id.message_template_id.content
|
||||||
|
url = self.request_url()
|
||||||
|
content = content.replace('{{name}}', stock_picking_out.name).replace(
|
||||||
|
'{{sale_order_name}}', stock_picking_out.origin).replace('{{request_url}}', url)
|
||||||
|
return content
|
||||||
|
|
||||||
def _get_message(self, message_queue_ids):
|
def _get_message(self, message_queue_ids):
|
||||||
contents = []
|
contents = []
|
||||||
product_id = []
|
product_id = []
|
||||||
@@ -46,15 +68,24 @@ class SFMessageStockPicking(models.Model):
|
|||||||
'{{number}}', str(i)).replace('{{request_url}}', url)
|
'{{number}}', str(i)).replace('{{request_url}}', url)
|
||||||
product_id.append(mrp_production_info.product_id.id)
|
product_id.append(mrp_production_info.product_id.id)
|
||||||
contents.append(content)
|
contents.append(content)
|
||||||
return contents
|
elif message_queue_id.message_template_id.name == '订单发货提醒':
|
||||||
else:
|
content = self.deal_stock_picking_sfp(message_queue_id)
|
||||||
res = super(SFMessageStockPicking, self)._get_message(message_queue_id)
|
if content:
|
||||||
return res
|
contents.append(content)
|
||||||
|
return contents
|
||||||
|
|
||||||
|
def get_special_url(self, id, tmplate_name, special_name, model_id):
|
||||||
|
menu_id = 0
|
||||||
|
action_id = 0
|
||||||
|
if tmplate_name == '调拨入库' and special_name == 'transfer_inventory_special_url':
|
||||||
|
menu_id = self.env.ref('stock.menu_stock_root').id
|
||||||
|
action_id = self.env.ref('stock.action_picking_tree_ready').id
|
||||||
|
return super(SFMessageStockPicking, self).get_url(id, menu_id, action_id, model_id)
|
||||||
|
else:
|
||||||
|
return super(SFMessageStockPicking, self).get_special_url(id, tmplate_name, special_name, model_id)
|
||||||
|
|
||||||
def request_url(self):
|
def request_url(self):
|
||||||
we_config_info = self.env['we.config'].sudo().search([], limit=1)
|
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||||
redirect_domain = self.env['we.app'].sudo().search([('id', '=', we_config_info.odoo_app_id.id)]).redirect_domain
|
|
||||||
full_url = 'https://%s/' % redirect_domain
|
|
||||||
action_id = self.env.ref('stock.stock_picking_type_action').id
|
action_id = self.env.ref('stock.stock_picking_type_action').id
|
||||||
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_theme_treehouse')]).id
|
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_theme_treehouse')]).id
|
||||||
# 查询参数
|
# 查询参数
|
||||||
@@ -63,5 +94,5 @@ class SFMessageStockPicking(models.Model):
|
|||||||
# 拼接查询参数
|
# 拼接查询参数
|
||||||
query_string = urlencode(params)
|
query_string = urlencode(params)
|
||||||
# 拼接URL
|
# 拼接URL
|
||||||
full_url = full_url + "web#" + query_string
|
full_url = url + "/web#" + query_string
|
||||||
return full_url
|
return full_url
|
||||||
|
|||||||
@@ -84,9 +84,7 @@ class SFMessageWork(models.Model):
|
|||||||
return contents
|
return contents
|
||||||
|
|
||||||
def request_url(self):
|
def request_url(self):
|
||||||
we_config_info = self.env['we.config'].sudo().search([], limit=1)
|
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||||
redirect_domain = self.env['we.app'].sudo().search([('id', '=', we_config_info.odoo_app_id.id)]).redirect_domain
|
|
||||||
full_url = 'https://%s/' % redirect_domain
|
|
||||||
action_id = self.env.ref('sf_manufacturing.mrp_workorder_action_tablet').id
|
action_id = self.env.ref('sf_manufacturing.mrp_workorder_action_tablet').id
|
||||||
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_stock_dropshipping')]).id
|
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_stock_dropshipping')]).id
|
||||||
# 查询参数
|
# 查询参数
|
||||||
@@ -95,7 +93,7 @@ class SFMessageWork(models.Model):
|
|||||||
# 拼接查询参数
|
# 拼接查询参数
|
||||||
query_string = urlencode(params)
|
query_string = urlencode(params)
|
||||||
# 拼接URL
|
# 拼接URL
|
||||||
full_url = full_url + "web#" + query_string
|
full_url = url + "/web#" + query_string
|
||||||
return full_url
|
return full_url
|
||||||
|
|
||||||
def _overdue_or_warning_func(self):
|
def _overdue_or_warning_func(self):
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class Sf_Mrs_Connect(http.Controller):
|
|||||||
ret = json.loads(ret['result'])
|
ret = json.loads(ret['result'])
|
||||||
logging.info('下发编程单:%s' % ret)
|
logging.info('下发编程单:%s' % ret)
|
||||||
domain = [('programming_no', '=', ret['programming_no'])]
|
domain = [('programming_no', '=', ret['programming_no'])]
|
||||||
if ret['manufacturing_type'] == 'scrap':
|
if ret['manufacturing_type'] in ('scrap', 'invalid_tool_rework'):
|
||||||
domain += [('state', 'not in', ['done', 'scrap', 'cancel'])]
|
domain += [('state', 'not in', ['done', 'scrap', 'cancel'])]
|
||||||
productions = request.env['mrp.production'].with_user(
|
productions = request.env['mrp.production'].with_user(
|
||||||
request.env.ref("base.user_admin")).search(domain)
|
request.env.ref("base.user_admin")).search(domain)
|
||||||
@@ -96,6 +96,14 @@ class Sf_Mrs_Connect(http.Controller):
|
|||||||
res.update({
|
res.update({
|
||||||
'production_ids': productions.ids
|
'production_ids': productions.ids
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# 对制造订单所以面的cnc工单的程序用刀进行校验
|
||||||
|
try:
|
||||||
|
productions.production_cnc_tool_checkout()
|
||||||
|
except Exception as e:
|
||||||
|
logging.info(f'对cnc工单的程序用刀进行校验报错:{e}')
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
|
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
else:
|
else:
|
||||||
res = {'status': 0, 'message': '该制造订单暂未开始'}
|
res = {'status': 0, 'message': '该制造订单暂未开始'}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class OrderPrice(models.Model):
|
|||||||
return True
|
return True
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return False
|
return False
|
||||||
|
@api.depends('sale_order_id.order_line.remark')
|
||||||
def _compute_bfm_amount_total(self):
|
def _compute_bfm_amount_total(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
amount_total = 0
|
amount_total = 0
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class sf_production_plan(models.Model):
|
|||||||
if self.date_planned_start:
|
if self.date_planned_start:
|
||||||
self.date_planned_finished = self.date_planned_start + timedelta(hours=1)
|
self.date_planned_finished = self.date_planned_start + timedelta(hours=1)
|
||||||
|
|
||||||
#处理计划状态非待排程,计划结束时间为空的数据处理
|
# 处理计划状态非待排程,计划结束时间为空的数据处理
|
||||||
def deal_no_date_planned_finished(self):
|
def deal_no_date_planned_finished(self):
|
||||||
plans = self.env['sf.production.plan'].search(
|
plans = self.env['sf.production.plan'].search(
|
||||||
[('date_planned_finished', '=', False), ('state', 'in', ['processing', 'done', 'finished'])])
|
[('date_planned_finished', '=', False), ('state', 'in', ['processing', 'done', 'finished'])])
|
||||||
@@ -90,6 +90,7 @@ class sf_production_plan(models.Model):
|
|||||||
for item in plans:
|
for item in plans:
|
||||||
if item.date_planned_start:
|
if item.date_planned_start:
|
||||||
item.order_deadline = item.date_planned_start + timedelta(days=7)
|
item.order_deadline = item.date_planned_start + timedelta(days=7)
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None):
|
def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None):
|
||||||
|
|
||||||
@@ -218,7 +219,7 @@ class sf_production_plan(models.Model):
|
|||||||
|
|
||||||
return num
|
return num
|
||||||
|
|
||||||
def do_production_schedule(self):
|
def do_production_schedule(self, count=1):
|
||||||
"""
|
"""
|
||||||
排程方法
|
排程方法
|
||||||
"""
|
"""
|
||||||
@@ -226,7 +227,7 @@ class sf_production_plan(models.Model):
|
|||||||
if not record.production_line_id:
|
if not record.production_line_id:
|
||||||
raise ValidationError("未选择生产线")
|
raise ValidationError("未选择生产线")
|
||||||
else:
|
else:
|
||||||
is_schedule = self.deal_processing_schedule(record.date_planned_start)
|
is_schedule = self.deal_processing_schedule(record.date_planned_start, count)
|
||||||
if not is_schedule:
|
if not is_schedule:
|
||||||
raise ValidationError("排程失败")
|
raise ValidationError("排程失败")
|
||||||
workorder_id_list = record.production_id.workorder_ids.ids
|
workorder_id_list = record.production_id.workorder_ids.ids
|
||||||
@@ -281,7 +282,7 @@ class sf_production_plan(models.Model):
|
|||||||
}
|
}
|
||||||
|
|
||||||
# 处理是否可排程
|
# 处理是否可排程
|
||||||
def deal_processing_schedule(self, date_planned_start):
|
def deal_processing_schedule(self, date_planned_start, count):
|
||||||
for record in self:
|
for record in self:
|
||||||
workcenter_ids = record.production_line_id.mrp_workcenter_ids
|
workcenter_ids = record.production_line_id.mrp_workcenter_ids
|
||||||
if not workcenter_ids:
|
if not workcenter_ids:
|
||||||
@@ -296,7 +297,7 @@ class sf_production_plan(models.Model):
|
|||||||
raise UserError('当前计划开始时间不能预约排程,请在工作时间内排程')
|
raise UserError('当前计划开始时间不能预约排程,请在工作时间内排程')
|
||||||
if not production_lines.deal_available_default_capacity(date_planned_start): # 判断生产线是否可排程
|
if not production_lines.deal_available_default_capacity(date_planned_start): # 判断生产线是否可排程
|
||||||
raise UserError('当前计划开始时间不能预约排程,生产线今日没有可排程的资源')
|
raise UserError('当前计划开始时间不能预约排程,生产线今日没有可排程的资源')
|
||||||
if not production_lines.deal_available_single_machine_capacity(date_planned_start): # 判断生产线是否可排程
|
if not production_lines.deal_available_single_machine_capacity(date_planned_start, count): # 判断生产线是否可排程
|
||||||
raise UserError('当前计划开始时间不能预约排程,生产线该时间段没有可排程的资源')
|
raise UserError('当前计划开始时间不能预约排程,生产线该时间段没有可排程的资源')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -339,7 +339,7 @@
|
|||||||
name="空料架配送"
|
name="空料架配送"
|
||||||
sequence="11"
|
sequence="11"
|
||||||
action="sf_manufacturing.sf_workpiece_delivery_empty_racks_act"
|
action="sf_manufacturing.sf_workpiece_delivery_empty_racks_act"
|
||||||
groups="base.group_system"
|
groups="sf_base.group_sf_order_user,sf_base.group_sf_mrp_manager,sf_base.group_sf_equipment_user"
|
||||||
parent="mrp.menu_mrp_manufacturing"
|
parent="mrp.menu_mrp_manufacturing"
|
||||||
/>
|
/>
|
||||||
<!-- <menuitem -->
|
<!-- <menuitem -->
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ class Action_Plan_All_Wizard(models.TransientModel):
|
|||||||
# 使用传递过来的计划ID
|
# 使用传递过来的计划ID
|
||||||
temp_plan_ids = self.plan_ids
|
temp_plan_ids = self.plan_ids
|
||||||
# 在这里添加您的逻辑来处理这些ID
|
# 在这里添加您的逻辑来处理这些ID
|
||||||
|
count = len(temp_plan_ids) + 1
|
||||||
for plan in temp_plan_ids:
|
for plan in temp_plan_ids:
|
||||||
|
count = count - 1
|
||||||
# 处理每个计划
|
# 处理每个计划
|
||||||
# 比如更新计划状态、分配资源等
|
# 比如更新计划状态、分配资源等
|
||||||
# 示例:plan.state = 'scheduled'
|
# 示例:plan.state = 'scheduled'
|
||||||
@@ -42,7 +44,7 @@ class Action_Plan_All_Wizard(models.TransientModel):
|
|||||||
plan_obj = self.env['sf.production.plan'].browse(plan.id)
|
plan_obj = self.env['sf.production.plan'].browse(plan.id)
|
||||||
plan_obj.production_line_id = self.production_line_id.id
|
plan_obj.production_line_id = self.production_line_id.id
|
||||||
plan.date_planned_start = self.date_planned_start
|
plan.date_planned_start = self.date_planned_start
|
||||||
plan_obj.do_production_schedule()
|
plan_obj.do_production_schedule(count)
|
||||||
# plan_obj.state = 'done'
|
# plan_obj.state = 'done'
|
||||||
print('处理计划:', plan.id, '完成')
|
print('处理计划:', plan.id, '完成')
|
||||||
|
|
||||||
|
|||||||
@@ -17,11 +17,6 @@
|
|||||||
<xpath expr="//field[@name='user_id']" position="replace">
|
<xpath expr="//field[@name='user_id']" position="replace">
|
||||||
<field name="user_id" widget="many2one_avatar_user" context="{'is_sale': True }"/>
|
<field name="user_id" widget="many2one_avatar_user" context="{'is_sale': True }"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//form/header/button[@name='action_quotation_send'][1]" position="replace">
|
|
||||||
<button name="action_quotation_send" string="通过EMAIL发送" type="object"
|
|
||||||
class="btn-primary" data-hotkey="g" context="{'validate_analytic': True}"
|
|
||||||
attrs="{'invisible': ['|','&',('check_status', '!=', 'approved'),('state', 'in', ['draft','cancel']),'&',('check_status', '=', 'approved'),('state', 'in', ['sale','cancel'])]}"/>
|
|
||||||
</xpath>
|
|
||||||
<xpath expr="//form/header/button[@name='action_confirm']" position="after">
|
<xpath expr="//form/header/button[@name='action_confirm']" position="after">
|
||||||
<button name="submit" string="提交" type="object"
|
<button name="submit" string="提交" type="object"
|
||||||
context="{'default_order_id':active_id}"
|
context="{'default_order_id':active_id}"
|
||||||
@@ -52,6 +47,39 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="string">拒绝接单</attribute>
|
<attribute name="string">拒绝接单</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ======================= 销售订单按钮顺序优化 start ======================= -->
|
||||||
|
<xpath expr="//form/header/button[@name='action_quotation_send'][1]" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//form/header/button[@name='action_quotation_send'][4]" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//form/header/button[@id='create_invoice']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//form/header/button[@id='create_invoice_percentage']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//form/header/button[@name='action_cancel']" position="after">
|
||||||
|
<button id="create_invoice" name="%(sale.action_view_sale_advance_payment_inv)d"
|
||||||
|
string="创建结算单"
|
||||||
|
type="action" class="btn-primary" data-hotkey="q"
|
||||||
|
attrs="{'invisible': [('invoice_status', '!=', 'to invoice')]}"/>
|
||||||
|
<button id="create_invoice_percentage" name="%(sale.action_view_sale_advance_payment_inv)d"
|
||||||
|
string="创建结算单"
|
||||||
|
type="action" context="{'default_advance_payment_method': 'percentage'}" data-hotkey="q"
|
||||||
|
attrs="{'invisible': ['|',('invoice_status', '!=', 'no'), ('state', '!=', 'sale')]}"/>
|
||||||
|
<button name="action_quotation_send" string="通过EMAIL发送" type="object"
|
||||||
|
class="btn-primary" data-hotkey="g" context="{'validate_analytic': True}"
|
||||||
|
attrs="{'invisible': ['|','&',('check_status', '!=', 'approved'),('state', 'in', ['draft','cancel']),'&',('check_status', '=', 'approved'),('state', 'in', ['sale','cancel'])]}"/>
|
||||||
|
<button name="action_quotation_send" string="通过EMAIL发送" type="object" states="sent,sale"
|
||||||
|
data-hotkey="g" context="{'validate_analytic': True}"/>
|
||||||
|
</xpath>
|
||||||
|
<!-- ====================== 销售订单按钮顺序优化 end======================== -->
|
||||||
|
|
||||||
|
|
||||||
<xpath expr="//form/header/button[@name='action_draft']" position="attributes">
|
<xpath expr="//form/header/button[@name='action_draft']" position="attributes">
|
||||||
<attribute name="invisible">1</attribute>
|
<attribute name="invisible">1</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
'summary': '智能工厂刀具管理',
|
'summary': '智能工厂刀具管理',
|
||||||
'sequence': 1,
|
'sequence': 1,
|
||||||
'description': """
|
'description': """
|
||||||
在本模块,定义了主要的角色、菜单、基础业务对象
|
在本模块,定义了刀具相关的模型和视图,以及相关的业务逻辑。
|
||||||
""",
|
""",
|
||||||
'category': 'sf',
|
'category': 'sf',
|
||||||
'website': 'https://www.sf.jikimo.com',
|
'website': 'https://www.sf.jikimo.com',
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ class CAMWorkOrderProgramKnifePlan(models.Model):
|
|||||||
|
|
||||||
sf_functional_tool_assembly_id = fields.Many2one('sf.functional.tool.assembly', '功能刀具组装', readonly=True)
|
sf_functional_tool_assembly_id = fields.Many2one('sf.functional.tool.assembly', '功能刀具组装', readonly=True)
|
||||||
|
|
||||||
active = fields.Boolean(string='已归档', default=True)
|
active = fields.Boolean(string='已归档', default=True, groups='base.user_root')
|
||||||
|
|
||||||
@api.depends('functional_tool_name')
|
@api.depends('functional_tool_name')
|
||||||
def _compute_tool_number(self):
|
def _compute_tool_number(self):
|
||||||
@@ -314,37 +314,41 @@ class CAMWorkOrderProgramKnifePlan(models.Model):
|
|||||||
'applicant': None,
|
'applicant': None,
|
||||||
'sf_functional_tool_assembly_id': None})
|
'sf_functional_tool_assembly_id': None})
|
||||||
|
|
||||||
def create_cam_work_plan(self, cnc_processing):
|
def create_cam_work_plan(self, cnc_ids):
|
||||||
"""
|
"""
|
||||||
根据传入的工单信息,查询是否有需要的功能刀具,如果没有则生成CAM工单程序用刀计划
|
根据传入的工单信息,查询是否有需要的功能刀具,如果没有则生成CAM工单程序用刀计划
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 获取编程单号
|
# 获取编程单号
|
||||||
programming_no = cnc_processing.workorder_id.production_id.programming_no
|
programming_no = cnc_ids[0].workorder_id.production_id.programming_no
|
||||||
logging.info(f'编程单号:{programming_no}')
|
logging.info(f'编程单号:{programming_no}')
|
||||||
cam_id = self.env['sf.cam.work.order.program.knife.plan'].sudo().search(
|
for cnc_processing in cnc_ids:
|
||||||
[('programming_no', '=', programming_no),
|
tool_name = cnc_processing.cutting_tool_name
|
||||||
('functional_tool_name', '=', cnc_processing.cutting_tool_name)])
|
cam_id = self.env['sf.cam.work.order.program.knife.plan'].sudo().search(
|
||||||
logging.info(f'CAM装刀计划:{cam_id}')
|
[('programming_no', '=', programming_no),
|
||||||
if not cam_id:
|
('functional_tool_name', '=', tool_name)])
|
||||||
knife_plan = self.env['sf.cam.work.order.program.knife.plan'].sudo().create({
|
if cam_id:
|
||||||
'name': cnc_processing.workorder_id.production_id.name,
|
logging.info(f'编程单号{programming_no}功能刀具名称{tool_name}已存在CAM装刀计划:{cam_id}')
|
||||||
'programming_no': programming_no,
|
else:
|
||||||
'cam_procedure_code': cnc_processing.program_name,
|
knife_plan = self.env['sf.cam.work.order.program.knife.plan'].sudo().create({
|
||||||
'filename': cnc_processing.cnc_id.name,
|
'name': cnc_processing.workorder_id.production_id.name,
|
||||||
'functional_tool_name': cnc_processing.cutting_tool_name,
|
'programming_no': programming_no,
|
||||||
'cam_cutter_spacing_code': cnc_processing.cutting_tool_no,
|
'cam_procedure_code': cnc_processing.program_name,
|
||||||
'process_type': cnc_processing.processing_type,
|
'filename': cnc_processing.cnc_id.name,
|
||||||
'margin_x_y': float(cnc_processing.margin_x_y),
|
'functional_tool_name': tool_name,
|
||||||
'margin_z': float(cnc_processing.margin_z),
|
'cam_cutter_spacing_code': cnc_processing.cutting_tool_no,
|
||||||
'finish_depth': float(cnc_processing.depth_of_processing_z),
|
'process_type': cnc_processing.processing_type,
|
||||||
'extension_length': float(cnc_processing.cutting_tool_extension_length),
|
'margin_x_y': float(cnc_processing.margin_x_y),
|
||||||
'shank_model': cnc_processing.cutting_tool_handle_type,
|
'margin_z': float(cnc_processing.margin_z),
|
||||||
'estimated_processing_time': cnc_processing.estimated_processing_time,
|
'finish_depth': float(cnc_processing.depth_of_processing_z),
|
||||||
})
|
'extension_length': float(cnc_processing.cutting_tool_extension_length),
|
||||||
logging.info('CAM工单程序用刀计划创建成功!!!')
|
'shank_model': cnc_processing.cutting_tool_handle_type,
|
||||||
# 创建装刀请求
|
'estimated_processing_time': cnc_processing.estimated_processing_time,
|
||||||
knife_plan.apply_for_tooling()
|
})
|
||||||
|
logging.info(f'创建CAM工单程序用刀计划:{knife_plan}')
|
||||||
|
# 创建装刀请求
|
||||||
|
knife_plan.apply_for_tooling()
|
||||||
|
logging.info('CAM工单程序用刀计划创建已完成!!!')
|
||||||
|
|
||||||
def unlink_cam_plan(self, production):
|
def unlink_cam_plan(self, production):
|
||||||
for item in production:
|
for item in production:
|
||||||
@@ -683,7 +687,7 @@ class FunctionalToolAssembly(models.Model):
|
|||||||
sf_cam_work_order_program_knife_plan_id = fields.Many2one('sf.cam.work.order.program.knife.plan',
|
sf_cam_work_order_program_knife_plan_id = fields.Many2one('sf.cam.work.order.program.knife.plan',
|
||||||
'CAM工单程序用刀计划', readonly=True)
|
'CAM工单程序用刀计划', readonly=True)
|
||||||
|
|
||||||
active = fields.Boolean(string='已归档', default=True)
|
active = fields.Boolean(string='已归档', default=True, groups='base.user_root')
|
||||||
|
|
||||||
code = fields.Char('功能刀具编码', compute='_compute_code')
|
code = fields.Char('功能刀具编码', compute='_compute_code')
|
||||||
|
|
||||||
@@ -1275,7 +1279,7 @@ class FunctionalToolDismantle(models.Model):
|
|||||||
item.picking_num = 0
|
item.picking_num = 0
|
||||||
|
|
||||||
state = fields.Selection([('待拆解', '待拆解'), ('已拆解', '已拆解')], default='待拆解', tracking=True)
|
state = fields.Selection([('待拆解', '待拆解'), ('已拆解', '已拆解')], default='待拆解', tracking=True)
|
||||||
active = fields.Boolean('有效', default=True)
|
active = fields.Boolean('有效', default=True, groups='base.user_root')
|
||||||
|
|
||||||
# 刀柄
|
# 刀柄
|
||||||
handle_product_id = fields.Many2one('product.product', string='刀柄', compute='_compute_functional_tool_num',
|
handle_product_id = fields.Many2one('product.product', string='刀柄', compute='_compute_functional_tool_num',
|
||||||
|
|||||||
@@ -402,7 +402,7 @@ class FunctionalToolWarning(models.Model):
|
|||||||
dispose_time = fields.Char('处理时间', readonly=True)
|
dispose_time = fields.Char('处理时间', readonly=True)
|
||||||
dispose_func = fields.Char('处理方法/措施', readonly=True)
|
dispose_func = fields.Char('处理方法/措施', readonly=True)
|
||||||
|
|
||||||
active = fields.Boolean(string='已归档', default=True)
|
active = fields.Boolean(string='已归档', default=True, groups='base.user_root')
|
||||||
|
|
||||||
functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具名称')
|
functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具名称')
|
||||||
|
|
||||||
@@ -554,7 +554,7 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
|
|||||||
sf_functional_tool_entity_ids = fields.One2many('sf.functional.cutting.tool.entity', 'safe_inventory_id',
|
sf_functional_tool_entity_ids = fields.One2many('sf.functional.cutting.tool.entity', 'safe_inventory_id',
|
||||||
string='功能刀具信息')
|
string='功能刀具信息')
|
||||||
|
|
||||||
active = fields.Boolean(string='已归档', default=True)
|
active = fields.Boolean(string='已归档', default=True, groups='base.user_root')
|
||||||
|
|
||||||
@api.depends('functional_name_id', 'functional_name_id.diameter', 'functional_name_id.angle',
|
@api.depends('functional_name_id', 'functional_name_id.diameter', 'functional_name_id.angle',
|
||||||
'functional_name_id.functional_cutting_tool_model_id')
|
'functional_name_id.functional_cutting_tool_model_id')
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class CNCprocessing(models.Model):
|
|||||||
# else:
|
# else:
|
||||||
# raise ValidationError("MES装刀指令发送失败")
|
# raise ValidationError("MES装刀指令发送失败")
|
||||||
|
|
||||||
def cnc_tool_checkout(self, cnc_processing_ids):
|
def cnc_tool_checkout_1(self, cnc_processing_ids):
|
||||||
"""
|
"""
|
||||||
根据传入的工单信息,查询是否有需要的功能刀具,如果没有则生成CAM工单程序用刀计划
|
根据传入的工单信息,查询是否有需要的功能刀具,如果没有则生成CAM工单程序用刀计划
|
||||||
"""
|
"""
|
||||||
@@ -128,13 +128,6 @@ class CNCprocessing(models.Model):
|
|||||||
})
|
})
|
||||||
logging.info('工单cnc程序用刀校验已完成!')
|
logging.info('工单cnc程序用刀校验已完成!')
|
||||||
|
|
||||||
@api.model_create_multi
|
|
||||||
def create(self, vals):
|
|
||||||
obj = super(CNCprocessing, self).create(vals)
|
|
||||||
# 调用CAM工单程序用刀计划创建方法
|
|
||||||
self.cnc_tool_checkout(obj)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
class MrpWorkCenter(models.Model):
|
class MrpWorkCenter(models.Model):
|
||||||
_inherit = 'mrp.workcenter'
|
_inherit = 'mrp.workcenter'
|
||||||
@@ -143,3 +136,78 @@ class MrpWorkCenter(models.Model):
|
|||||||
action = self.env.ref('sf_tool_management.sf_functional_tool_assembly_view_act')
|
action = self.env.ref('sf_tool_management.sf_functional_tool_assembly_view_act')
|
||||||
result = action.read()[0]
|
result = action.read()[0]
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class MrpProduction(models.Model):
|
||||||
|
_inherit = 'mrp.production'
|
||||||
|
|
||||||
|
def production_cnc_tool_checkout(self):
|
||||||
|
logging.info('开始进行工单cnc程序用刀校验!!!')
|
||||||
|
invalid_tool = [] # 无效刀
|
||||||
|
invalid_tool_processing_panel = [] # 无效刀加工面
|
||||||
|
missing_tool_1 = [] # 缺刀(机内、线边)
|
||||||
|
missing_tool_2 = [] # 缺刀(库存)
|
||||||
|
for item in self:
|
||||||
|
workorder_ids = item.workorder_ids.filtered(lambda a: a.state not in ['rework', 'cancel'])
|
||||||
|
for workorder_id in workorder_ids:
|
||||||
|
if workorder_id.cnc_ids:
|
||||||
|
for cnc_id in workorder_id.cnc_ids:
|
||||||
|
tool_name = cnc_id.cutting_tool_name
|
||||||
|
# 查询功能刀具在清单中是否存在
|
||||||
|
tool_inventory_id = self.env['sf.tool.inventory'].sudo().search([('name', '=', tool_name)])
|
||||||
|
if not tool_inventory_id:
|
||||||
|
invalid_tool.append(tool_name)
|
||||||
|
invalid_tool_processing_panel.append(workorder_id.processing_panel)
|
||||||
|
continue
|
||||||
|
# 查询功能刀具是否存在库存
|
||||||
|
functional_tools = self.env['sf.functional.cutting.tool.entity'].sudo().search(
|
||||||
|
[('tool_name_id', '=', tool_inventory_id.id), ('functional_tool_status', '=', '正常')])
|
||||||
|
if not functional_tools.filtered(lambda p: p.current_location in ('线边刀库', '机内刀库')):
|
||||||
|
missing_tool_1.append(tool_name) # 判断为 ('线边刀库', '机内刀库') 缺刀
|
||||||
|
if not functional_tools:
|
||||||
|
missing_tool_2.append(tool_name) # 判断为 库存缺刀
|
||||||
|
break
|
||||||
|
# 修改cnc程序的‘刀具状态’
|
||||||
|
workorder_ids = self.env['mrp.workorder'].sudo().search([('production_id', 'in', self.ids)])
|
||||||
|
if invalid_tool:
|
||||||
|
# 修改cnc程序的‘刀具状态’为 ‘无效刀’
|
||||||
|
cnc_ids = self.env['sf.cnc.processing'].sudo().search(
|
||||||
|
[('workorder_id', 'in', workorder_ids.ids), ('cutting_tool_name', 'in', invalid_tool)])
|
||||||
|
if cnc_ids:
|
||||||
|
cnc_ids.write({'tool_state': '2'})
|
||||||
|
# 创建制造订单无效刀检测结果记录
|
||||||
|
for production_id in self:
|
||||||
|
for processing_panel in list(set(invalid_tool_processing_panel)):
|
||||||
|
if not production_id.detection_result_ids.filtered(
|
||||||
|
lambda a: (a.processing_panel == processing_panel and a.detailed_reason == '无效功能刀具'
|
||||||
|
and a.handle_result == '待处理' and a.routing_type == 'CNC加工'
|
||||||
|
and a.rework_reason == 'programming' and a.test_results == '返工')):
|
||||||
|
production_id.detection_result_ids.create({
|
||||||
|
'production_id': production_id.id,
|
||||||
|
'processing_panel': processing_panel,
|
||||||
|
'routing_type': 'CNC加工',
|
||||||
|
'rework_reason': 'programming', # 原因:编程(programming)
|
||||||
|
'detailed_reason': '无效功能刀具',
|
||||||
|
'test_results': '返工',
|
||||||
|
'handle_result': '待处理'
|
||||||
|
})
|
||||||
|
# 自动调用重新获取编程的方法
|
||||||
|
logging.info('cnc用刀校验到无效刀自动调用重新编程方法:update_programming_state()')
|
||||||
|
self[0].update_programming_state()
|
||||||
|
# 修改制造订单 编程状态变为“编程中”
|
||||||
|
self.write({'programming_state': '编程中', 'work_state': '编程中'})
|
||||||
|
if missing_tool_1:
|
||||||
|
# 修改 修改cnc程序的‘刀具状态’ 为 ‘缺刀’
|
||||||
|
cnc_ids = self.env['sf.cnc.processing'].sudo().search(
|
||||||
|
[('workorder_id', 'in', workorder_ids.ids), ('cutting_tool_name', 'in', missing_tool_1)])
|
||||||
|
if cnc_ids:
|
||||||
|
cnc_ids.write({'tool_state': '1'})
|
||||||
|
if missing_tool_2 and not invalid_tool:
|
||||||
|
# 调用CAM工单程序用刀计划创建方法
|
||||||
|
cnc_ids = self.env['sf.cnc.processing'].sudo().search(
|
||||||
|
[('workorder_id', 'in', workorder_ids.filtered(lambda a: a.production_id == self[0].id).ids),
|
||||||
|
('cutting_tool_name', 'in', missing_tool_2)])
|
||||||
|
if cnc_ids:
|
||||||
|
logging.info('调用CAM工单程序用刀计划创建方法!!!')
|
||||||
|
self.env['sf.cam.work.order.program.knife.plan'].sudo().create_cam_work_plan(cnc_ids)
|
||||||
|
logging.info('工单cnc程序用刀校验完成!!!')
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
<field name="barcode_id" invisible="1"/>
|
<field name="barcode_id" invisible="1"/>
|
||||||
<field name="rfid"/>
|
<field name="rfid"/>
|
||||||
<field name="tool_name_id"/>
|
<field name="tool_name_id"/>
|
||||||
<field name="image" widget='image'/>
|
|
||||||
<field name="tool_groups_id"/>
|
<field name="tool_groups_id"/>
|
||||||
<field name="functional_tool_diameter"/>
|
<field name="functional_tool_diameter"/>
|
||||||
<field name="knife_tip_r_angle"/>
|
<field name="knife_tip_r_angle"/>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
<field name="cutting_tool_model_id"/>
|
<field name="cutting_tool_model_id"/>
|
||||||
<field name="specification_id"/>
|
<field name="specification_id"/>
|
||||||
<field name="brand_id"/>
|
<field name="brand_id"/>
|
||||||
<field name="qty_available"/>
|
<field name="qty_available" string="库存数量"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</page>
|
</page>
|
||||||
|
|||||||
@@ -338,7 +338,7 @@ class ShelfLocation(models.Model):
|
|||||||
_name = 'sf.shelf.location'
|
_name = 'sf.shelf.location'
|
||||||
_inherit = ['printing.utils']
|
_inherit = ['printing.utils']
|
||||||
_description = '货位'
|
_description = '货位'
|
||||||
_rec_name = 'barcode'
|
# _rec_name = 'barcode'
|
||||||
_order = 'id asc, create_date asc'
|
_order = 'id asc, create_date asc'
|
||||||
|
|
||||||
# current_location_id = fields.Many2one('sf.shelf.location', string='当前位置')
|
# current_location_id = fields.Many2one('sf.shelf.location', string='当前位置')
|
||||||
|
|||||||
160
tool_service/装夹自动保存检测文件服务/app.py
Normal file
160
tool_service/装夹自动保存检测文件服务/app.py
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
from fastapi import FastAPI, HTTPException
|
||||||
|
from pydantic import BaseModel
|
||||||
|
import os
|
||||||
|
from ftplib import FTP
|
||||||
|
import win32gui
|
||||||
|
import win32con
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
|
# 配置日志记录
|
||||||
|
logging.basicConfig(filename='service.log', level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(levelname)s - %(message)s')
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
class FileUploadRequest(BaseModel):
|
||||||
|
filename: str
|
||||||
|
|
||||||
|
|
||||||
|
# FTP 服务器配置信息
|
||||||
|
ftp_host = '110.52.114.162'
|
||||||
|
ftp_port = 10021
|
||||||
|
ftp_user = 'ftpuser'
|
||||||
|
ftp_password = '123456'
|
||||||
|
ftp_directory = '/home/ftp/ftp_root/ThreeTest/XT/Before/'
|
||||||
|
|
||||||
|
|
||||||
|
def find_child_window(parent_hwnd, class_name):
|
||||||
|
def enum_child_windows(hwnd, lparam):
|
||||||
|
class_name = win32gui.GetClassName(hwnd)
|
||||||
|
if class_name == lparam:
|
||||||
|
child_hwnds.append(hwnd)
|
||||||
|
return True
|
||||||
|
|
||||||
|
child_hwnds = []
|
||||||
|
win32gui.EnumChildWindows(parent_hwnd, enum_child_windows, class_name)
|
||||||
|
return child_hwnds
|
||||||
|
|
||||||
|
|
||||||
|
def find_child_window_by_partial_title(parent_hwnd, partial_title):
|
||||||
|
def enum_child_windows(hwnd, lparam):
|
||||||
|
# 获取窗口的标题
|
||||||
|
title = win32gui.GetWindowText(hwnd)
|
||||||
|
# 检查标题是否包含指定的部分标题
|
||||||
|
if partial_title in title:
|
||||||
|
child_hwnds.append(hwnd)
|
||||||
|
return True
|
||||||
|
|
||||||
|
child_hwnds = []
|
||||||
|
win32gui.EnumChildWindows(parent_hwnd, enum_child_windows, None)
|
||||||
|
return child_hwnds
|
||||||
|
|
||||||
|
|
||||||
|
def find_child_window_by_title(parent_hwnd, title):
|
||||||
|
def enum_child_windows(hwnd, lparam):
|
||||||
|
if win32gui.GetWindowText(hwnd) == lparam:
|
||||||
|
child_hwnds.append(hwnd)
|
||||||
|
return True
|
||||||
|
|
||||||
|
child_hwnds = []
|
||||||
|
win32gui.EnumChildWindows(parent_hwnd, enum_child_windows, title)
|
||||||
|
return child_hwnds
|
||||||
|
|
||||||
|
|
||||||
|
def set_path_and_save(filename):
|
||||||
|
parent_hwnd = win32gui.FindWindow(None, '另存为')
|
||||||
|
|
||||||
|
if parent_hwnd == 0:
|
||||||
|
raise HTTPException(status_code=404, detail="没有找到保存报告的窗口,请检查!")
|
||||||
|
|
||||||
|
# 这里假设“地址:”是你需要的部分标题
|
||||||
|
address_hwnds = find_child_window_by_partial_title(parent_hwnd, "地址:")
|
||||||
|
|
||||||
|
# 确保找到的窗口句柄有效
|
||||||
|
if not address_hwnds:
|
||||||
|
raise HTTPException(status_code=404, detail="未找到地址框,请联系管理员!")
|
||||||
|
|
||||||
|
# 假设找到的第一个窗口是目标组件
|
||||||
|
address_hwnd = address_hwnds[0]
|
||||||
|
logging.info(f"找到地址框地址: {win32gui.GetWindowText(address_hwnd)}")
|
||||||
|
|
||||||
|
# 设置路径
|
||||||
|
local_file_path = os.path.join(win32gui.GetWindowText(address_hwnd).split(' ')[1], filename)
|
||||||
|
logging.info(f"设置路径: {local_file_path}")
|
||||||
|
|
||||||
|
path_hwnds = find_child_window(parent_hwnd, 'Edit')
|
||||||
|
|
||||||
|
if not path_hwnds:
|
||||||
|
raise HTTPException(status_code=404, detail="未找到路径框")
|
||||||
|
|
||||||
|
path_hwnd = path_hwnds[0]
|
||||||
|
win32gui.SendMessage(path_hwnd, win32con.WM_SETTEXT, 0, filename)
|
||||||
|
|
||||||
|
button_hwnds = find_child_window_by_title(parent_hwnd, '保存(&S)')
|
||||||
|
|
||||||
|
if not button_hwnds:
|
||||||
|
raise HTTPException(status_code=404, detail="未找到保存按钮")
|
||||||
|
|
||||||
|
save_button_hwnd = button_hwnds[0]
|
||||||
|
win32gui.PostMessage(save_button_hwnd, win32con.BM_CLICK, 0, 0)
|
||||||
|
|
||||||
|
return local_file_path
|
||||||
|
|
||||||
|
|
||||||
|
def wait_for_file_to_save(filepath, timeout=30):
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
while time.time() - start_time < timeout:
|
||||||
|
if os.path.isfile(filepath):
|
||||||
|
return True
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def upload_file_to_ftp(local_file):
|
||||||
|
|
||||||
|
if not os.path.isfile(local_file):
|
||||||
|
raise HTTPException(status_code=204, detail="文件未找到")
|
||||||
|
|
||||||
|
ftp = FTP()
|
||||||
|
try:
|
||||||
|
ftp.connect(ftp_host, ftp_port)
|
||||||
|
ftp.login(ftp_user, ftp_password)
|
||||||
|
ftp.cwd(ftp_directory)
|
||||||
|
with open(local_file, 'rb') as file:
|
||||||
|
ftp.storbinary(f'STOR {os.path.basename(local_file)}', file)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"文件上传失败: {e}")
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
ftp.quit()
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/get/check/report")
|
||||||
|
async def upload_file(request: FileUploadRequest):
|
||||||
|
# 设置路径框并点击保存
|
||||||
|
local_file_path = set_path_and_save(request.filename)
|
||||||
|
logging.info(f"文件上传请求: {request.filename}")
|
||||||
|
logging.info(f"文件保存路径: {local_file_path}")
|
||||||
|
|
||||||
|
# 等待文件保存完成
|
||||||
|
if not wait_for_file_to_save(local_file_path):
|
||||||
|
raise HTTPException(status_code=500, detail="文件保存超时")
|
||||||
|
|
||||||
|
# 上传文件到 FTP
|
||||||
|
success = upload_file_to_ftp(local_file_path)
|
||||||
|
if success:
|
||||||
|
ftp_file_path = os.path.join(ftp_directory, request.filename)
|
||||||
|
return {"ftp_file_path": ftp_file_path}
|
||||||
|
else:
|
||||||
|
raise HTTPException(status_code=500, detail="文件上传失败")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import uvicorn
|
||||||
|
|
||||||
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||||
5
tool_service/装夹自动保存检测文件服务/readme.md
Normal file
5
tool_service/装夹自动保存检测文件服务/readme.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
app.py是主程序文件,主要功能是监听装夹自动保存文件,并将其上传到FTP服务器。
|
||||||
|
|
||||||
|
需要讲app.py做成一个exe文件,可以用pyinstaller工具。
|
||||||
|
|
||||||
|
然后在windows的计划任务中执行此exe文件即可。
|
||||||
BIN
tool_service/装夹自动保存检测文件服务/装夹检测文件自动保存操作流程.docx
Normal file
BIN
tool_service/装夹自动保存检测文件服务/装夹检测文件自动保存操作流程.docx
Normal file
Binary file not shown.
Reference in New Issue
Block a user