Compare commits
285 Commits
feature/or
...
master_sf_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39afc99b8f | ||
|
|
b74bc62804 | ||
|
|
94f57629e5 | ||
|
|
e4c845a9f6 | ||
|
|
d239509299 | ||
|
|
457f0aa2ac | ||
|
|
84266364e3 | ||
|
|
ea88d8284a | ||
|
|
d2ec420f57 | ||
|
|
97d33adabd | ||
|
|
c11f3cc66c | ||
|
|
6b4a7e35a9 | ||
|
|
11100c9260 | ||
|
|
941c3ca43a | ||
|
|
05f9528ca9 | ||
|
|
b205945f65 | ||
|
|
c1dc28488a | ||
|
|
547d6608e6 | ||
|
|
dacb3cc076 | ||
|
|
282657fbca | ||
|
|
7e516c6f0b | ||
|
|
34e2a49bef | ||
|
|
ff4bdd2f2d | ||
|
|
b239fdf847 | ||
|
|
47feb4cf3c | ||
|
|
63b732ff42 | ||
|
|
50d63c28d6 | ||
|
|
73b7ff7d1b | ||
|
|
ccdcd01372 | ||
|
|
198296f0f8 | ||
|
|
f122343e31 | ||
|
|
82fa39f1a3 | ||
|
|
841e1b4ce2 | ||
|
|
1ab62d7724 | ||
|
|
28a46d395b | ||
|
|
64c66f1272 | ||
|
|
f8d957486b | ||
|
|
1866607967 | ||
|
|
40efdf6f3b | ||
|
|
f910df3ce5 | ||
|
|
c99c96a9ea | ||
|
|
95cc557577 | ||
|
|
fa78389f47 | ||
|
|
f7381c43aa | ||
|
|
76c5db61da | ||
|
|
88ffc34a68 | ||
|
|
4f65b34aeb | ||
|
|
4a26d18b46 | ||
|
|
664ac8128a | ||
|
|
ce285818cf | ||
|
|
6f7811a843 | ||
|
|
54ed90e892 | ||
|
|
f8060113d9 | ||
|
|
48ab891991 | ||
|
|
7f384c3f56 | ||
|
|
01a2771dfb | ||
|
|
45b6214ddd | ||
|
|
c96ff3f5b4 | ||
|
|
d84758232e | ||
|
|
479953e082 | ||
|
|
27881589d4 | ||
|
|
79ec3f2c91 | ||
|
|
715b4181be | ||
|
|
0ccda10c65 | ||
|
|
5b4376b5f9 | ||
|
|
1050f7616b | ||
|
|
3a9cc0c09c | ||
|
|
4d63b90373 | ||
|
|
43c4614650 | ||
|
|
eba9ccf083 | ||
|
|
947100a9d4 | ||
|
|
ed4924651a | ||
|
|
3cc2bb94fe | ||
|
|
8ecb3eb50a | ||
|
|
819d89c278 | ||
|
|
66d47c60bc | ||
|
|
0340563749 | ||
|
|
271e23a67f | ||
|
|
de42382f58 | ||
|
|
3728809d10 | ||
|
|
d4e2ace8a6 | ||
|
|
1d4188df7e | ||
|
|
a2323293ca | ||
|
|
29bd1c2968 | ||
|
|
6c35ec13fd | ||
|
|
83107b05e2 | ||
|
|
5fb7165306 | ||
|
|
c569b60d5c | ||
|
|
bd2748659a | ||
|
|
3ee233c0bc | ||
|
|
d63081dffa | ||
|
|
0932ec95bf | ||
|
|
7361da0e5d | ||
|
|
2dbddf532c | ||
|
|
eb867e62d8 | ||
|
|
d75504960a | ||
|
|
93efbf742e | ||
|
|
7c3ac138b5 | ||
|
|
63444d3dd1 | ||
|
|
2fcad742b8 | ||
|
|
f341ef5e83 | ||
|
|
d488824e3d | ||
|
|
0c28700f75 | ||
|
|
48d97f3e57 | ||
|
|
ef17c9920c | ||
|
|
ef643a5a72 | ||
|
|
b7ff8d0bd5 | ||
|
|
1e721d68bf | ||
|
|
7454297dcd | ||
|
|
4e0a023c36 | ||
|
|
155c5eb329 | ||
|
|
45718ab925 | ||
|
|
562c8dda8d | ||
|
|
84a37a970f | ||
|
|
37cc02e97a | ||
|
|
7a8753408b | ||
|
|
eb853d0be9 | ||
|
|
52438733d7 | ||
|
|
b986dd8473 | ||
|
|
98fdb581fb | ||
|
|
ea7380288c | ||
|
|
9beecab21d | ||
|
|
2e6932e054 | ||
|
|
5ca9e028d8 | ||
|
|
64e2b71a34 | ||
|
|
8d52c94aed | ||
|
|
9d91a1c99f | ||
|
|
d6fdeafef6 | ||
|
|
371b3a9d65 | ||
|
|
36c1c7b170 | ||
|
|
38d3a7901d | ||
|
|
2223259d13 | ||
|
|
aba270182d | ||
|
|
c9378fc9fe | ||
|
|
378850682d | ||
|
|
02c35803da | ||
|
|
75b60c1ec8 | ||
|
|
2cd4424ba1 | ||
|
|
d52f0aed6e | ||
|
|
d197fc5b9e | ||
|
|
34280fe24b | ||
|
|
fd88a37aec | ||
|
|
0082a308fa | ||
|
|
b5f5826c80 | ||
|
|
5024a9254d | ||
|
|
2a14a630d5 | ||
|
|
ef8ea2599f | ||
|
|
e9ab4270a9 | ||
|
|
a99d651509 | ||
|
|
c54a5b36d4 | ||
|
|
ae844cf203 | ||
|
|
ec66ea76ba | ||
|
|
8643c41193 | ||
|
|
bed0483496 | ||
|
|
a648208656 | ||
|
|
98af6d0530 | ||
|
|
5af1953e04 | ||
|
|
b1a08be57b | ||
|
|
15981aadf1 | ||
|
|
e19cb52e70 | ||
|
|
5269d09d0e | ||
|
|
e7e64720c6 | ||
|
|
a6701a842e | ||
|
|
d697bd13a1 | ||
|
|
6e20a466ce | ||
|
|
dd5c9775fc | ||
|
|
82c274591c | ||
|
|
4f73f57ddf | ||
|
|
269141dfb2 | ||
|
|
cab0e1ce0b | ||
|
|
d4ff7ffaa9 | ||
|
|
d238d09cc3 | ||
|
|
4cee5213bb | ||
|
|
759e4a481d | ||
|
|
1213afe834 | ||
|
|
35fda7106a | ||
|
|
b8baa84270 | ||
|
|
6591e663b6 | ||
|
|
bad5c8d489 | ||
|
|
68f8c94332 | ||
|
|
ddfb233452 | ||
|
|
bbf62d2302 | ||
|
|
17b09c1f6d | ||
|
|
e0fc70ec60 | ||
|
|
4558fc0336 | ||
|
|
4609ec442a | ||
|
|
eb7d9e4168 | ||
|
|
8706e25b0d | ||
|
|
85fea64f49 | ||
|
|
f74215c9f6 | ||
|
|
cc8906980c | ||
|
|
0990d73075 | ||
|
|
7d877a0cbb | ||
|
|
e13bad8483 | ||
|
|
22ebb1bbe1 | ||
|
|
bc888d7984 | ||
|
|
c92d4f7868 | ||
|
|
667a2a81fb | ||
|
|
4a22071f94 | ||
|
|
ec2075c874 | ||
|
|
ebc9716019 | ||
|
|
72c9c9872e | ||
|
|
2b541fe041 | ||
|
|
f6e371f223 | ||
|
|
0aefe9e656 | ||
|
|
4f8f29e41a | ||
|
|
ffb32c7ce2 | ||
|
|
3cacca80e9 | ||
|
|
a3356fe195 | ||
|
|
3c70e1623e | ||
|
|
51628c081a | ||
|
|
18ebe6bcb4 | ||
|
|
63ac4f2b44 | ||
|
|
1b6ca170c0 | ||
|
|
2966260f51 | ||
|
|
c4069995e0 | ||
|
|
54920982a2 | ||
|
|
f947a3ed58 | ||
|
|
5e86d67316 | ||
|
|
7354a19696 | ||
|
|
78e3d77000 | ||
|
|
47ad2410cf | ||
|
|
fa2eb7b3cf | ||
|
|
73f161ffa4 | ||
|
|
ff06d5c3be | ||
|
|
78cb177ac5 | ||
|
|
9601502360 | ||
|
|
739b7e600c | ||
|
|
27da7639b2 | ||
|
|
258e3bdb9f | ||
|
|
7f3e9927d5 | ||
|
|
927f726030 | ||
|
|
7576200fc4 | ||
|
|
4ffb9d636f | ||
|
|
24f0547343 | ||
|
|
402a323673 | ||
|
|
5e1685417f | ||
|
|
9dfe34ce9a | ||
|
|
190d6da217 | ||
|
|
8946b44280 | ||
|
|
f21d085462 | ||
|
|
43f53197c4 | ||
|
|
ac43be1262 | ||
|
|
8bf1c56efd | ||
|
|
d66791dc41 | ||
|
|
ce6b36a77e | ||
|
|
4acae01009 | ||
|
|
213b76a280 | ||
|
|
c675e1d67c | ||
|
|
8a9f6ab34f | ||
|
|
8aca6ce084 | ||
|
|
cb0c093006 | ||
|
|
1e7d7008b4 | ||
|
|
ef453dbc1e | ||
|
|
9f1e635c8d | ||
|
|
92037f3f04 | ||
|
|
4ba0566d27 | ||
|
|
5d870d53f6 | ||
|
|
6a674cdb5b | ||
|
|
fbce132173 | ||
|
|
7ce2f6c797 | ||
|
|
7502792438 | ||
|
|
9efb13cf01 | ||
|
|
b14e263a2e | ||
|
|
6f41ef3047 | ||
|
|
de29f0c938 | ||
|
|
589a24f595 | ||
|
|
85186f94f5 | ||
|
|
ffc363a31c | ||
|
|
a047ad3bb6 | ||
|
|
cc3289c834 | ||
|
|
e1093a3c69 | ||
|
|
ba955ca658 | ||
|
|
f8a12b1fe3 | ||
|
|
ccbe311c58 | ||
|
|
55694ae303 | ||
|
|
5a7ba7f87e | ||
|
|
d36da3df24 | ||
|
|
dec5ddd154 | ||
|
|
01c57a8691 | ||
|
|
2808005ce9 | ||
|
|
2ddfbb0dde | ||
|
|
d5c82e4a28 | ||
|
|
436adc5ff6 | ||
|
|
f230ad55fb |
@@ -6,6 +6,6 @@ import { patch } from "web.utils";
|
||||
patch(WebClient.prototype, "kolpolok_custom_title_and_favicon.WebClient", {
|
||||
setup() {
|
||||
this._super();
|
||||
this.title.setParts({ zopenerp: "JIKIMO" });
|
||||
// this.title.setParts({ zopenerp: "JIKIMO" });
|
||||
},
|
||||
});
|
||||
@@ -5,9 +5,9 @@
|
||||
<!-- 修改页面头部图标及文字 -->
|
||||
<template id="favicon_icon" inherit_id="web.layout" name="Web layout">
|
||||
<!-- change the title with reliance partner -->
|
||||
<xpath expr="//head//title" position="before">
|
||||
<!-- <xpath expr="//head//title" position="before">
|
||||
<title t-esc="'JIKIMO'"/>
|
||||
</xpath>
|
||||
</xpath> -->
|
||||
<!-- change the default favicon icon with -->
|
||||
<xpath expr="//head//link[@rel='shortcut icon']" position="replace">
|
||||
<link type="image/x-icon" rel="shortcut icon" href="/jikimo_frontend/static/src/img/jikimo-logo.ico"/>
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- hide 登录页面 powerd by odoo 及管理数据库 -->
|
||||
<template id="login_page_layout" inherit_id="web.login_layout" name="Login Page Layout">
|
||||
<xpath expr="//div[@class='card-body']//div[last()]" position="replace"></xpath>
|
||||
<!-- <xpath expr="//div[@class='card-body']/div[last()]" position="replace"></xpath> -->
|
||||
</template>
|
||||
|
||||
<!-- 隐藏odoo版本信息 -->
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
pystrich
|
||||
cpca
|
||||
pycryptodome==3.20
|
||||
|
||||
@@ -321,7 +321,7 @@ class ToolInventory(models.Model):
|
||||
prefix = fields.Char('前缀')
|
||||
postfix = fields.Char('后缀')
|
||||
diameter = fields.Float('直径(mm)')
|
||||
angle = fields.Float('R角(mm)')
|
||||
angle = fields.Float('R角(mm)',default=0)
|
||||
tool_length = fields.Float('刀具总长(mm)')
|
||||
blade_length = fields.Float('避空长/刃长(mm)')
|
||||
knife_head_name = fields.Char('刀头名称')
|
||||
|
||||
@@ -21,8 +21,9 @@ class ToolMaterialsBasicParameters(models.Model):
|
||||
neck_length = fields.Float('颈部长度(mm)')
|
||||
handle_diameter = fields.Float('柄部直径(mm)')
|
||||
handle_length = fields.Float('柄部长度(mm)')
|
||||
blade_tip_diameter = fields.Integer('刀尖直径(mm)')
|
||||
blade_tip_working_size = fields.Char('刀尖处理尺寸(R半径mm/倒角度)', size=20)
|
||||
blade_tip_diameter = fields.Float('刀尖直径(mm)')
|
||||
blade_tip_working_size = fields.Char('刀尖倒角度(°)', size=20)
|
||||
tip_r_size = fields.Float('刀尖R角(mm)',default=0)
|
||||
blade_tip_taper = fields.Integer('刀尖锥度(°)')
|
||||
blade_diameter = fields.Float('刃部直径(mm)')
|
||||
blade_length = fields.Float('刃部长度(mm)')
|
||||
@@ -37,7 +38,7 @@ class ToolMaterialsBasicParameters(models.Model):
|
||||
width = fields.Float('宽度(mm)')
|
||||
cutting_blade_length = fields.Float('切削刃长(mm)')
|
||||
relief_angle = fields.Integer('后角(°)')
|
||||
blade_tip_circular_arc_radius = fields.Char('刀尖圆弧半径(mm)', size=20)
|
||||
blade_tip_circular_arc_radius = fields.Char('刀尖圆弧半径(mm)', size=20,default='0')
|
||||
inscribed_circle_diameter = fields.Float('内接圆直径IC/D(mm)')
|
||||
install_aperture_diameter = fields.Float('安装孔直径(mm)')
|
||||
chip_breaker_groove = fields.Selection([('无', '无'), ('单面', '单面'), ('双面', '双面')],
|
||||
|
||||
@@ -242,3 +242,8 @@ access_sf_fixture_materials_basic_parameters_group_sf_stock_manager,sf_fixture_m
|
||||
access_sf_multi_mounting_type_group_sf_stock_manager,sf_multi_mounting_type_group_sf_stock_manager,model_sf_multi_mounting_type,sf_base.group_sf_stock_manager,1,0,0,0
|
||||
access_sf_machine_brand_group_sf_stock_manager,sf_machine_brand_group_sf_stock_manager,model_sf_machine_brand,sf_base.group_sf_stock_manager,1,0,0,0
|
||||
access_sf_cutting_tool_type_group_sf_stock_manager,sf_cutting_tool_type_group_sf_stock_manager,model_sf_cutting_tool_type,sf_base.group_sf_stock_manager,1,0,0,0
|
||||
|
||||
|
||||
access_sf_cutting_tool_material_group_plan_dispatch,sf_cutting_tool_material_group_plan_dispatch,model_sf_cutting_tool_material,sf_base.group_plan_dispatch,1,0,0,0
|
||||
access_sf_functional_cutting_tool_model_group_plan_dispatch,sf_functional_cutting_tool_model_group_plan_dispatch,model_sf_functional_cutting_tool_model,sf_base.group_plan_dispatch,1,0,0,0
|
||||
access_sf_cutting_tool_type_group_plan_dispatch,sf_cutting_tool_type_group_plan_dispatch,model_sf_cutting_tool_type,sf_base.group_plan_dispatch,1,0,0,0
|
||||
|
||||
|
@@ -30,6 +30,7 @@ patch(barcodeGenericHandlers, "start", {
|
||||
"O-CMD.PAGER-FIRST": () => updatePager("first"),
|
||||
"O-CMD.PAGER-LAST": () => updatePager("last"),
|
||||
"O-CMD.CONFIRM": () => customClickOnButton(".jikimo_button_confirm"),
|
||||
"O-CMD.FLUSHED": () => customClickOnButton(".jikimo_button_flushed"),
|
||||
};
|
||||
|
||||
barcode.bus.addEventListener("barcode_scanned", (ev) => {
|
||||
|
||||
@@ -159,9 +159,6 @@ td.o_required_modifier {
|
||||
display:inline;
|
||||
}
|
||||
.diameter{
|
||||
display: flex !important;
|
||||
justify-content: flex-start !important;
|
||||
align-items: center !important;
|
||||
}
|
||||
.o_address_format {
|
||||
display: flex !important;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<field name="handle_length"/>
|
||||
<field name="blade_tip_diameter"/>
|
||||
<field name="blade_tip_working_size"/>
|
||||
<field name="tip_r_size"/>
|
||||
<field name="blade_tip_taper"/>
|
||||
<field name="blade_diameter"/>
|
||||
<field name="blade_length"/>
|
||||
@@ -95,6 +96,7 @@
|
||||
<field name="handle_length"/>
|
||||
<field name="blade_tip_diameter"/>
|
||||
<field name="blade_tip_working_size"/>
|
||||
<field name="tip_r_size"/>
|
||||
<field name="blade_tip_taper"/>
|
||||
<field name="blade_diameter"/>
|
||||
<field name="blade_length"/>
|
||||
@@ -139,6 +141,7 @@
|
||||
</group>
|
||||
<group attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')]}">
|
||||
<field name="blade_tip_working_size"/>
|
||||
<field name="tip_r_size"/>
|
||||
<field name="blade_tip_diameter" class="diameter"/>
|
||||
<field name="blade_tip_taper"/>
|
||||
<field name="blade_helix_angle"/>
|
||||
|
||||
@@ -222,6 +222,7 @@
|
||||
<field name="handle_diameter" class="diameter"/>
|
||||
<field name="handle_length"/>
|
||||
<field name="blade_tip_working_size" class="du"/>
|
||||
<field name="tip_r_size"/>
|
||||
<field name="blade_tip_diameter" class="diameter"/>
|
||||
<field name="blade_tip_taper" class="du"/>
|
||||
<field name="blade_helix_angle" class="du"/>
|
||||
@@ -577,7 +578,7 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_cutting_tool_material_search">
|
||||
<record model="ir.ui.view" id="view_cutting_tool_inventory_search">
|
||||
<field name="name">sf.tool.inventory.search</field>
|
||||
<field name="model">sf.tool.inventory</field>
|
||||
<field name="arch" type="xml">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
import logging
|
||||
import requests
|
||||
@@ -53,11 +54,13 @@ class StatusChange(models.Model):
|
||||
if not ret.get('error'):
|
||||
logging.info('接口已经执行=============')
|
||||
else:
|
||||
logging.error('工厂加工同步订单状态失败 {}'.format(ret))
|
||||
raise UserError('工厂加工同步订单状态失败')
|
||||
traceback_error = traceback.format_exc()
|
||||
logging.error("bfm订单状态同步失败:%s" % traceback_error)
|
||||
raise UserError('工厂加工同步订单状态到bfm失败')
|
||||
except UserError as e:
|
||||
logging.error('工厂加工同步订单状态失败 {}'.format(e))
|
||||
raise UserError('工厂加工同步订单状态失败')
|
||||
traceback_error = traceback.format_exc()
|
||||
logging.error("工厂加工同步订单状态失败:%s " % traceback_error)
|
||||
raise UserError(e)
|
||||
return res
|
||||
|
||||
def action_cancel(self):
|
||||
|
||||
@@ -191,6 +191,8 @@
|
||||
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')],'readonly': [('id', '!=', False)]}"/>
|
||||
<field name="cutting_tool_blade_tip_working_size"
|
||||
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')],'readonly': [('id', '!=', False)]}"/>
|
||||
<field name="cutting_tool_blade_tip_r_size"
|
||||
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')],'readonly': [('id', '!=', False)]}"/>
|
||||
<field name="cutting_tool_blade_tip_diameter" string="刀尖直径(mm)" class="diameter"
|
||||
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')],'readonly': [('id', '!=', False)]}"/>
|
||||
<field name="cutting_tool_blade_tip_taper" string="刀尖锥度(°)"
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
'data': [
|
||||
'views/hr_employee.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/res_users_view.xml',
|
||||
'data/cron_data.xml',
|
||||
],
|
||||
'demo': [
|
||||
|
||||
@@ -2,4 +2,3 @@
|
||||
|
||||
from . import hr_employee
|
||||
from . import res_config_setting
|
||||
from . import res_users
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models, fields, api
|
||||
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ResUsers(models.Model):
|
||||
_inherit = 'res.users'
|
||||
|
||||
we_employee_id = fields.Char(string=u'企业微信账号', default="")
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="view_users_account_form" model="ir.ui.view">
|
||||
<field name="name">res.users.account.form</field>
|
||||
<field name="model">res.users</field>
|
||||
<field name="inherit_id" ref="base.view_users_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<page name="preferences" position="after">
|
||||
<page name="account" string="企业微信">
|
||||
<group>
|
||||
<field name="we_employee_id"/>
|
||||
</group>
|
||||
</page>
|
||||
</page>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
'views/machine_info_present.xml',
|
||||
'views/delivery_record.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/maintenance_views.xml',
|
||||
|
||||
],
|
||||
'assets': {
|
||||
|
||||
@@ -162,7 +162,7 @@ class Sf_Dashboard_Connect(http.Controller):
|
||||
# 关机时长:初次上线时间 - 通电时间
|
||||
'power_off_time': power_off_time,
|
||||
# 关机率:关机时长/初次上线时间
|
||||
'power_off_rate': power_off_rate,
|
||||
# 'power_off_rate': power_off_rate,
|
||||
'first_online_duration': first_online_duration,
|
||||
# 停机时间:关机时间 - 运行时间
|
||||
# 停机时长:关机时间 - 初次上线时间
|
||||
@@ -187,11 +187,11 @@ class Sf_Dashboard_Connect(http.Controller):
|
||||
"""
|
||||
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||
logging.info('前端请求日志数据的参数为:%s' % kw)
|
||||
# 连接数据库
|
||||
conn = psycopg2.connect(**db_config)
|
||||
cur = conn.cursor()
|
||||
|
||||
try:
|
||||
# 连接数据库
|
||||
conn = psycopg2.connect(**db_config)
|
||||
cur = conn.cursor()
|
||||
machine_list = ast.literal_eval(kw['machine_list'])
|
||||
begin_time_str = kw['begin_time'].strip('"')
|
||||
end_time_str = kw['end_time'].strip('"')
|
||||
@@ -231,6 +231,100 @@ class Sf_Dashboard_Connect(http.Controller):
|
||||
res['message'] = '前端请求日志数据失败,原因:%s' % e
|
||||
return json.dumps(res)
|
||||
|
||||
finally:
|
||||
if cur:
|
||||
cur.close()
|
||||
if conn:
|
||||
conn.close()
|
||||
|
||||
@http.route('/api/logs/page_data', type='http', auth='public', methods=['GET', 'POST'],
|
||||
csrf=False, cors="*")
|
||||
def logs_page_data(self, **kw):
|
||||
"""
|
||||
拿到日志数据返回给大屏展示(支持时间戳分页)
|
||||
:param kw:
|
||||
:return:
|
||||
"""
|
||||
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||
logging.info('前端请求日志数据的参数为:%s' % kw)
|
||||
|
||||
# 连接数据库
|
||||
conn = psycopg2.connect(**db_config)
|
||||
cur = conn.cursor()
|
||||
try:
|
||||
# 获取并解析传递的参数
|
||||
machine_list = ast.literal_eval(kw.get('machine_list', '[]'))
|
||||
begin_time_str = kw.get('begin_time', '').strip('"')
|
||||
end_time_str = kw.get('end_time', '').strip('"')
|
||||
page = int(kw.get('page', 1)) # 默认页码为1
|
||||
page_size = int(kw.get('page_size', 80)) # 默认每页条数为10
|
||||
|
||||
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
||||
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# 计算分页的 offset
|
||||
offset = (page - 1) * page_size
|
||||
|
||||
# 先查询符合条件的总记录数
|
||||
total_records = 0
|
||||
for item in machine_list:
|
||||
count_sql = '''
|
||||
SELECT COUNT(*)
|
||||
FROM device_data
|
||||
WHERE device_name = %s AND time >= %s AND time <= %s;
|
||||
'''
|
||||
# 执行总记录数查询
|
||||
cur.execute(count_sql, (item, begin_time, end_time))
|
||||
record_count = cur.fetchone()[0] # 获取总记录数
|
||||
total_records += record_count
|
||||
|
||||
# 计算总页数
|
||||
if total_records > 0:
|
||||
total_pages = (total_records + page_size - 1) // page_size # 向上取整
|
||||
else:
|
||||
total_pages = 0
|
||||
|
||||
# 将总页数和总记录数返回到响应中
|
||||
res['total_records'] = total_records
|
||||
res['total_pages'] = total_pages
|
||||
|
||||
for item in machine_list:
|
||||
sql = '''
|
||||
SELECT time, device_state, program_name
|
||||
FROM device_data
|
||||
WHERE device_name = %s AND time >= %s AND time <= %s
|
||||
ORDER BY time DESC
|
||||
LIMIT %s OFFSET %s;
|
||||
'''
|
||||
# 执行SQL命令,使用参数绑定
|
||||
cur.execute(sql, (item, begin_time, end_time, page_size, offset))
|
||||
results = cur.fetchall()
|
||||
|
||||
# 将数据按照 equipment_code 进行分组
|
||||
if item not in res['data']:
|
||||
res['data'][item] = []
|
||||
|
||||
for result in results:
|
||||
res['data'][item].append({
|
||||
'time': result[0].strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'state': result[1],
|
||||
'production_name': result[2],
|
||||
})
|
||||
|
||||
return json.dumps(res) # 返回分页数据
|
||||
|
||||
except Exception as e:
|
||||
logging.info('前端请求日志数据失败,原因:%s' % e)
|
||||
res['status'] = -1
|
||||
res['message'] = '前端请求日志数据失败,原因:%s' % e
|
||||
return json.dumps(res)
|
||||
|
||||
finally:
|
||||
if cur:
|
||||
cur.close()
|
||||
if conn:
|
||||
conn.close()
|
||||
|
||||
# 返回CNC机床列表
|
||||
@http.route('/api/CNCList', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
|
||||
cors="*")
|
||||
@@ -378,7 +472,8 @@ class Sf_Dashboard_Connect(http.Controller):
|
||||
pass_rate = 1
|
||||
if pass_nums:
|
||||
pass_rate = round(
|
||||
(len(pass_nums) / detection_data if len(plan_data_finish_orders) > 0 else 0), 3)
|
||||
# (len(pass_nums) / detection_data if len(plan_data_finish_orders) > 0 else 0), 3)
|
||||
(len(pass_nums) / len(plan_data_finish_orders) if len(plan_data_finish_orders) > 0 else 0), 3)
|
||||
|
||||
# 返工率
|
||||
rework_rate = round(
|
||||
@@ -418,8 +513,9 @@ class Sf_Dashboard_Connect(http.Controller):
|
||||
'plan_data_progress_deviation': plan_data_progress_deviation,
|
||||
'plan_data_rework_counts': plan_data_rework_counts,
|
||||
'on_time_rate': on_time_rate,
|
||||
'detection_data': detection_data,
|
||||
'pass_rate': pass_rate
|
||||
# 'detection_data': detection_data,
|
||||
'detection_data': plan_data_finish_counts,
|
||||
'pass_rate': (plan_data_finish_counts - plan_data_fault_counts) / plan_data_finish_counts
|
||||
}
|
||||
res['data'][line] = data
|
||||
|
||||
@@ -489,11 +585,14 @@ class Sf_Dashboard_Connect(http.Controller):
|
||||
|
||||
for time_interval in time_intervals:
|
||||
start_time, end_time = time_interval
|
||||
# print(start_time, end_time)
|
||||
orders = plan_obj.search([('production_line_id.name', '=', line), ('state', 'in', ['finished']),
|
||||
(date_field_name, '>=', start_time.strftime('%Y-%m-%d 00:00:00')),
|
||||
(date_field_name, '<', end_time.strftime('%Y-%m-%d 00:00:00'))
|
||||
])
|
||||
|
||||
orders = plan_obj.search([
|
||||
('production_line_id.name', '=', line),
|
||||
('state', 'in', ['finished']),
|
||||
(date_field_name, '>=', start_time.strftime('%Y-%m-%d %H:%M:%S')),
|
||||
(date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')) # 包括结束时间
|
||||
])
|
||||
|
||||
# 使用小时和分钟作为键,确保每个小时的数据有独立的键
|
||||
key = start_time.strftime('%H:%M:%S') # 只取小时:分钟:秒作为键
|
||||
time_count_dict[key] = len(orders)
|
||||
@@ -623,13 +722,19 @@ class Sf_Dashboard_Connect(http.Controller):
|
||||
# 未完成订单
|
||||
not_done_orders = plan_obj.search(
|
||||
[('production_line_id.name', '=', line), ('state', 'not in', ['finished']),
|
||||
('production_id.state', 'not in', ['cancel']), ('active', '=', True)
|
||||
('production_id.state', 'not in', ['cancel', 'done']), ('active', '=', True)
|
||||
])
|
||||
# print(not_done_orders)
|
||||
|
||||
# 完成订单
|
||||
# 获取当前时间,并计算24小时前的时间
|
||||
current_time = datetime.now()
|
||||
time_24_hours_ago = current_time - timedelta(hours=24)
|
||||
|
||||
finish_orders = plan_obj.search([
|
||||
('production_line_id.name', '=', line), ('state', 'in', ['finished']),
|
||||
('production_id.state', 'not in', ['cancel']), ('active', '=', True)
|
||||
('production_id.state', 'not in', ['cancel']), ('active', '=', True),
|
||||
('actual_end_time', '>=', time_24_hours_ago)
|
||||
])
|
||||
# print(finish_orders)
|
||||
|
||||
@@ -687,6 +792,8 @@ class Sf_Dashboard_Connect(http.Controller):
|
||||
not_done_data.append(line_dict)
|
||||
|
||||
for finish_order in finish_orders:
|
||||
if not finish_order.actual_end_time:
|
||||
continue
|
||||
blank_name = ''
|
||||
try:
|
||||
blank_name = finish_order.production_id.move_raw_ids[0].product_id.name
|
||||
@@ -707,7 +814,8 @@ class Sf_Dashboard_Connect(http.Controller):
|
||||
'material': material,
|
||||
'dimensions': dimensions,
|
||||
'order_qty': finish_order.product_qty,
|
||||
'finish_time': finish_order.actual_end_time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'finish_time': finish_order.actual_end_time.strftime(
|
||||
'%Y-%m-%d %H:%M:%S') if finish_order.actual_end_time else ' '
|
||||
|
||||
}
|
||||
done_data.append(line_dict)
|
||||
@@ -745,10 +853,10 @@ class Sf_Dashboard_Connect(http.Controller):
|
||||
'''
|
||||
|
||||
sql2 = '''
|
||||
SELECT DISTINCT ON (alarm_time) alarm_time, alarm_repair_time
|
||||
SELECT DISTINCT ON (alarm_start_time) alarm_time, alarm_start_time
|
||||
FROM device_data
|
||||
WHERE device_name = %s AND alarm_time IS NOT NULL
|
||||
ORDER BY alarm_time, time;
|
||||
WHERE device_name = %s AND alarm_start_time IS NOT NULL
|
||||
ORDER BY alarm_start_time, time;
|
||||
|
||||
'''
|
||||
# 执行SQL命令
|
||||
@@ -764,8 +872,11 @@ class Sf_Dashboard_Connect(http.Controller):
|
||||
res['data'][item] = {'idle_count': row[0]}
|
||||
alarm_count = []
|
||||
for row in result2:
|
||||
alarm_count.append(row[0])
|
||||
total_alarm_time += abs(float(row[0]))
|
||||
alarm_count.append(row[1])
|
||||
if row[0]:
|
||||
total_alarm_time += abs(float(row[0]))
|
||||
else:
|
||||
total_alarm_time += 0.0
|
||||
if len(list(set(alarm_count))) == 1:
|
||||
if list(set(alarm_count))[0] is None:
|
||||
alarm_count_num = 0
|
||||
@@ -791,54 +902,27 @@ class Sf_Dashboard_Connect(http.Controller):
|
||||
"""
|
||||
查询设备的异常情况
|
||||
"""
|
||||
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||
res = {'status': 1, 'message': '成功', 'data': []}
|
||||
logging.info('前端请求机床数据的参数为:%s' % kw)
|
||||
|
||||
# 连接数据库
|
||||
conn = psycopg2.connect(**db_config)
|
||||
cur = conn.cursor()
|
||||
try:
|
||||
# 获取请求的机床数据
|
||||
maintenance_logs_obj = request.env['sf.maintenance.logs'].sudo()
|
||||
# # 获取请求的机床数据
|
||||
# machine_list = ast.literal_eval(kw['machine_list'])
|
||||
# idle_times = []
|
||||
# idle_dict = {}
|
||||
|
||||
# for item in machine_list:
|
||||
sql = '''
|
||||
SELECT DISTINCT ON (alarm_time) alarm_time, alarm_message, system_date, system_time, alarm_repair_time
|
||||
FROM device_data
|
||||
WHERE alarm_time IS NOT NULL
|
||||
ORDER BY alarm_time, time;
|
||||
# machine_data = equipment_obj.search([('code', '=', item)])
|
||||
for log in maintenance_logs_obj.search([]):
|
||||
res['data'].append({
|
||||
'name': log.name,
|
||||
'alarm_time': log.alarm_time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'fault_alarm_info': log.fault_alarm_info if log.fault_alarm_info else ' ',
|
||||
'fault_process': log.fault_process if log.fault_process else ' ',
|
||||
})
|
||||
|
||||
'''
|
||||
# 执行SQL命令
|
||||
cur.execute(sql)
|
||||
result = cur.fetchall()
|
||||
# print('result', result)
|
||||
|
||||
# 将查询结果转换为字典列表
|
||||
data = []
|
||||
for row in result:
|
||||
record = {
|
||||
'alarm_time': row[0],
|
||||
'alarm_message': row[1],
|
||||
'system_date': row[2],
|
||||
'system_time': row[3],
|
||||
'alarm_repair_time': row[4]
|
||||
}
|
||||
data.append(record)
|
||||
|
||||
# 将数据填充到返回结果中
|
||||
res['data'] = data
|
||||
|
||||
# 返回统计结果
|
||||
return json.dumps(res, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
return json.dumps(res)
|
||||
finally:
|
||||
cur.close()
|
||||
conn.close()
|
||||
logging.error(f"An error occurred: {e}")
|
||||
|
||||
return json.dumps(res)
|
||||
|
||||
# 设备oee
|
||||
@http.route('/api/OEE', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
||||
@@ -1155,27 +1239,80 @@ class Sf_Dashboard_Connect(http.Controller):
|
||||
conn = psycopg2.connect(**db_config)
|
||||
# 获取请求的机床数据
|
||||
machine_list = ast.literal_eval(kw['machine_list'])
|
||||
time_threshold = datetime.now() - timedelta(days=1)
|
||||
|
||||
alarm_last_24_time = 0.0
|
||||
|
||||
def fetch_result_as_dict(cursor):
|
||||
"""辅助函数:将查询结果转为字典"""
|
||||
columns = [desc[0] for desc in cursor.description]
|
||||
return dict(zip(columns, cursor.fetchone())) if cursor.rowcount != 0 else None
|
||||
|
||||
# 获取当前时间的时间戳
|
||||
current_timestamp = datetime.now().timestamp()
|
||||
for item in machine_list:
|
||||
euipment_obj = request.env['maintenance.equipment'].sudo().search([('code', '=', item)])
|
||||
# 机床上线时间段
|
||||
first_online_duration = current_timestamp - euipment_obj.first_online_time.timestamp()
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("""
|
||||
SELECT * FROM device_data
|
||||
WHERE device_name = %s
|
||||
AND device_state != '离线'
|
||||
ORDER BY time DESC
|
||||
LIMIT 1;
|
||||
""", (item,))
|
||||
SELECT * FROM device_data
|
||||
WHERE device_name = %s
|
||||
AND device_state != '离线' AND process_time IS NOT NULL
|
||||
ORDER BY time DESC
|
||||
LIMIT 1;
|
||||
""", (item,))
|
||||
last_all_time = fetch_result_as_dict(cur)
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("""
|
||||
SELECT * FROM device_data
|
||||
WHERE device_name = %s
|
||||
AND device_state != '离线' AND time >= %s AND process_time IS NOT NULL
|
||||
ORDER BY time ASC
|
||||
LIMIT 1;
|
||||
""", (item, time_threshold))
|
||||
last_24_time = fetch_result_as_dict(cur)
|
||||
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("""
|
||||
SELECT COUNT(*)
|
||||
FROM (
|
||||
SELECT DISTINCT ON (idle_start_time) idle_start_time
|
||||
FROM device_data
|
||||
WHERE device_name = %s AND idle_start_time IS NOT NULL AND time >= %s
|
||||
ORDER BY idle_start_time, time
|
||||
) subquery;
|
||||
""", (item, time_threshold))
|
||||
idle_count = cur.fetchone()[0]
|
||||
|
||||
alarm_last_24_nums = []
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("""
|
||||
SELECT DISTINCT ON (alarm_start_time) alarm_time, alarm_start_time
|
||||
FROM device_data
|
||||
WHERE device_name = %s
|
||||
AND alarm_start_time IS NOT NULL AND time >= %s;
|
||||
""", (item, time_threshold))
|
||||
results = cur.fetchall()
|
||||
for result in results:
|
||||
alarm_last_24_nums.append(result[1])
|
||||
if result[0]:
|
||||
if float(result[0]) >= 1000:
|
||||
continue
|
||||
alarm_last_24_time += float(result[0])
|
||||
else:
|
||||
alarm_last_24_time += 0.0
|
||||
# 返回数据
|
||||
res['data'][item] = {
|
||||
'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,
|
||||
'power_on_time': last_all_time['power_on_time'] if last_all_time['power_on_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,
|
||||
'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,
|
||||
'alarm_last_24_time': alarm_last_24_time,
|
||||
'alarm_last_24_nums': len(list(set(alarm_last_24_nums))),
|
||||
'idle_count': idle_count,
|
||||
'first_online_time': first_online_duration,
|
||||
}
|
||||
|
||||
conn.close()
|
||||
|
||||
@@ -285,26 +285,6 @@ class Machine_ftp(models.Model):
|
||||
# # 开动率
|
||||
run_rate = fields.Char('开动率', readonly=True)
|
||||
|
||||
# 同步CNC设备到oee
|
||||
def sync_oee(self):
|
||||
"""
|
||||
同步CNC设备到oee
|
||||
:return:
|
||||
"""
|
||||
for record in self:
|
||||
record.ensure_one()
|
||||
cnc_oee_dict = {
|
||||
'equipment_id': record.id,
|
||||
'type_id': record.type_id.id,
|
||||
'machine_tool_picture': record.machine_tool_picture,
|
||||
'equipment_code': record.code,
|
||||
'function_type': record.function_type,
|
||||
}
|
||||
if self.env['maintenance.equipment.oee.logs'].search([('equipment_id', '=', record.id)]):
|
||||
self.env['maintenance.equipment.oee.logs'].write(cnc_oee_dict)
|
||||
else:
|
||||
self.env['maintenance.equipment.oee.logs'].create(cnc_oee_dict)
|
||||
|
||||
|
||||
class WorkCenterBarcode(models.Model):
|
||||
"""
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<group string='状态监控'>
|
||||
<group>
|
||||
<!-- <field name="timestamp"/> -->
|
||||
<field name="status"/>
|
||||
<!-- <field name="status"/> -->
|
||||
<field name="run_status"/>
|
||||
<field name="run_time"/>
|
||||
<field name="system_date"/>
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<!-- 修改设备列表视图-->
|
||||
<record id="sf_machine_hr_equipment_view_tree_inherit" model="ir.ui.view">
|
||||
<field name="name">sf.machine.hr.equipment.view.tree.inherit</field>
|
||||
<field name="model">maintenance.equipment</field>
|
||||
<field name="inherit_id" ref="maintenance.hr_equipment_view_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//tree" position="inside">
|
||||
<header>
|
||||
<button name="sync_oee" type="object" string="同步设备至OEE"/>
|
||||
</header>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -24,7 +24,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<h2>获取检测报告服务配置</h2>
|
||||
<div class="row mt16 o_settings_container" id="jd_api">
|
||||
<div class="row mt16 o_settings_container" id="check_report_config">
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<div class="o_setting_left_pane"/>
|
||||
<div class="o_setting_right_pane">
|
||||
@@ -38,6 +38,18 @@
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='check_report_config']/div" position="after">
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="is_get_detection_file"/>
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<div class="text-muted">
|
||||
<label for="is_get_detection_file"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
@@ -38,8 +38,7 @@ class SfMaintenanceEquipment(models.Model):
|
||||
|
||||
crea_url = "/api/machine_tool/create"
|
||||
|
||||
|
||||
#AGV运行日志
|
||||
# AGV运行日志
|
||||
agv_logs = fields.One2many('maintenance.equipment.agv.log', 'equipment_id', string='AGV运行日志')
|
||||
# 1212修改后的字段
|
||||
number_of_axles = fields.Selection(
|
||||
@@ -117,7 +116,6 @@ class SfMaintenanceEquipment(models.Model):
|
||||
# num = "%04d" % m
|
||||
# return num
|
||||
|
||||
|
||||
equipment_maintenance_standards_ids = fields.Many2many('equipment.maintenance.standards',
|
||||
'sf_maintenance_equipment_ids', string='设备维保标准')
|
||||
eq_maintenance_id = fields.Many2one('equipment.maintenance.standards', string='设备保养标准',
|
||||
@@ -179,7 +177,8 @@ class SfMaintenanceEquipment(models.Model):
|
||||
type_id = fields.Many2one('sf.machine_tool.type', '型号')
|
||||
|
||||
state = fields.Selection(
|
||||
[("正常", "正常"), ("故障停机", "故障停机"), ("计划维保", "计划维保"), ("空闲", "空闲"), ("封存(报废)", "封存(报废)")],
|
||||
[("正常", "正常"), ("故障停机", "故障停机"), ("计划维保", "计划维保"), ("空闲", "空闲"),
|
||||
("封存(报废)", "封存(报废)")],
|
||||
default='正常', string="机床状态")
|
||||
run_time = fields.Char('总运行时长')
|
||||
# 0606新增字段
|
||||
@@ -328,7 +327,7 @@ class SfMaintenanceEquipment(models.Model):
|
||||
item.tool_diameter_min = item.type_id.tool_diameter_min
|
||||
item.machine_tool_category = item.type_id.machine_tool_category.id
|
||||
item.brand_id = item.type_id.brand_id.id
|
||||
#新增修改字段
|
||||
# 新增修改字段
|
||||
item.taper_type_id = item.type_id.taper_type_id.id
|
||||
item.function_type = item.type_id.function_type
|
||||
item.a_axis = item.type_id.a_axis
|
||||
@@ -370,7 +369,6 @@ class SfMaintenanceEquipment(models.Model):
|
||||
item.image_id = item.type_id.jg_image_id.ids
|
||||
item.image_lq_id = item.type_id.lq_image_id.ids
|
||||
|
||||
|
||||
# AGV小车设备参数
|
||||
AGV_L = fields.Char('AGV尺寸(长)')
|
||||
AGV_W = fields.Char('AGV尺寸(宽)')
|
||||
@@ -461,18 +459,6 @@ class SfMaintenanceEquipment(models.Model):
|
||||
original_value = fields.Char('原值')
|
||||
incomplete_value = fields.Char('残值')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 注册同步机床
|
||||
def enroll_machine_tool(self):
|
||||
sf_sync_config = self.env['res.config.settings'].get_values()
|
||||
@@ -763,7 +749,7 @@ class SfMaintenanceEquipment(models.Model):
|
||||
image_id = fields.Many2many('maintenance.equipment.image', 'equipment_id',
|
||||
domain="[('type', '=', '加工能力')]")
|
||||
image_lq_id = fields.Many2many('maintenance.equipment.image', 'equipment_lq_id', string='冷却方式',
|
||||
domain="[('type', '=', '冷却方式')]")
|
||||
domain="[('type', '=', '冷却方式')]")
|
||||
|
||||
|
||||
class SfRobotAxisNum(models.Model):
|
||||
@@ -777,4 +763,5 @@ class SfRobotAxisNum(models.Model):
|
||||
weight = fields.Char('最大负载(kg)')
|
||||
permissible_load_torque = fields.Char('允许负载扭矩(N-m)')
|
||||
permissible_inertial_torque = fields.Char('允许惯性扭矩(kg-m²)')
|
||||
equipment_id = fields.Many2one('maintenance.equipment', string='机器人', domain="[('equipment_type', '=', '机器人')]")
|
||||
equipment_id = fields.Many2one('maintenance.equipment', string='机器人',
|
||||
domain="[('equipment_type', '=', '机器人')]")
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
# -*-coding:utf-8-*-
|
||||
from odoo import fields, models
|
||||
from odoo import fields, models, api
|
||||
|
||||
|
||||
class SfMaintenanceLogs(models.Model):
|
||||
_name = 'sf.maintenance.logs'
|
||||
_description = '设备故障日志'
|
||||
_order = 'alarm_time desc'
|
||||
|
||||
code = fields.Char(string='编码')
|
||||
name = fields.Char(string='名称')
|
||||
code = fields.Char(string='编码', readonly=True)
|
||||
name = fields.Char(string='名称', compute='_compute_name')
|
||||
type = fields.Selection([('type1', '类型1'), ('type2', '类型2')], string='类型')
|
||||
brand = fields.Many2one('sf.machine.brand', related='maintenance_equipment_id.brand_id', string='品牌')
|
||||
maintenance_equipment_id = fields.Many2one('maintenance.equipment', string='机台号')
|
||||
@@ -28,3 +29,13 @@ class SfMaintenanceLogs(models.Model):
|
||||
fault_duration = fields.Float(string='故障时长')
|
||||
note = fields.Text(string='备注')
|
||||
active = fields.Boolean('Active', default=True)
|
||||
|
||||
@api.depends('code')
|
||||
def _compute_name(self):
|
||||
for record in self:
|
||||
if record.code:
|
||||
record.name = self.env['maintenance.equipment'].sudo().search([('code', '=', record.code), ('active', '=', True)]).name
|
||||
record.maintenance_equipment_id = self.env['maintenance.equipment'].sudo().search([('code', '=', record.code), ('active', '=', True)]).id
|
||||
else:
|
||||
record.name = ''
|
||||
|
||||
|
||||
@@ -1,5 +1,46 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import json
|
||||
import datetime
|
||||
import requests
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
def convert_to_seconds(time_str):
|
||||
# 修改正则表达式,使 H、M、S 部分可选
|
||||
|
||||
if time_str is None:
|
||||
return 0
|
||||
|
||||
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
||||
|
||||
match = re.match(pattern, time_str)
|
||||
|
||||
if match:
|
||||
# 提取各时间单位,如果某个单位缺失则默认设为0
|
||||
hours = int(match.group(1)) if match.group(1) else 0
|
||||
minutes = int(match.group(2)) if match.group(2) else 0
|
||||
seconds = int(match.group(3)) if match.group(3) else 0
|
||||
|
||||
# 计算总秒数
|
||||
total_seconds = hours * 3600 + minutes * 60 + seconds
|
||||
if total_seconds == 0:
|
||||
# return None
|
||||
pattern = r"(?:(\d+)小时)?(?:(\d+)分钟)?(?:(\d+)秒)?"
|
||||
match = re.match(pattern, time_str)
|
||||
if match:
|
||||
# 提取各时间单位,如果某个单位缺失则默认设为0
|
||||
hours = int(match.group(1)) if match.group(1) else 0
|
||||
minutes = int(match.group(2)) if match.group(2) else 0
|
||||
seconds = int(match.group(3)) if match.group(3) else 0
|
||||
|
||||
# 计算总秒数
|
||||
total_seconds = hours * 3600 + minutes * 60 + seconds
|
||||
return total_seconds
|
||||
else:
|
||||
return None
|
||||
return total_seconds
|
||||
|
||||
|
||||
class SfMaintenanceEquipmentOEE(models.Model):
|
||||
@@ -9,24 +50,225 @@ class SfMaintenanceEquipmentOEE(models.Model):
|
||||
name = fields.Char('设备oee')
|
||||
equipment_id = fields.Many2one('maintenance.equipment', '机台号',
|
||||
domain="[('category_id.equipment_type', '=', '机床'),('state_zc', '=', '已注册')]")
|
||||
|
||||
equipment_code = fields.Char('设备编码', related='equipment_id.code', store=True)
|
||||
|
||||
type_id = fields.Many2one('sf.machine_tool.type', '型号', related='equipment_id.type_id')
|
||||
machine_tool_picture = fields.Binary('设备图片', related='equipment_id.machine_tool_picture')
|
||||
state = fields.Selection(
|
||||
[("正常", "正常"), ("故障停机", "故障停机"), ("计划维保", "计划维保"), ("空闲", "空闲"),
|
||||
("封存(报废)", "封存(报废)")],
|
||||
default='正常', string="机床状态", related='equipment_id.state')
|
||||
run_time = fields.Float('加工时长(h)')
|
||||
equipment_time = fields.Float('开机时长(h)')
|
||||
done_nums = fields.Integer('加工件数')
|
||||
utilization_rate = fields.Char('可用率')
|
||||
fault_time = fields.Float('故障时长')
|
||||
fault_nums = fields.Integer('故障次数')
|
||||
# 故障率
|
||||
fault_rate = fields.Char('故障率')
|
||||
|
||||
online_time = fields.Char('开机时长(小时)', reaonly='True')
|
||||
|
||||
offline_time = fields.Char('关机时长(小时)', reaonly='True')
|
||||
idle_nums = fields.Integer('待机次数', reaonly='True')
|
||||
# 待机时长
|
||||
|
||||
idle_time = fields.Char('待机时长(小时)', reaonly='True')
|
||||
|
||||
# 待机率
|
||||
idle_rate = fields.Char('待机率(%)', reaonly='True')
|
||||
|
||||
work_time = fields.Char('加工时长(小时)', reaonly='True')
|
||||
work_rate = fields.Char('可用率(%)', reaonly='True')
|
||||
fault_time = fields.Char('故障时长(小时)', reaonly='True')
|
||||
fault_rate = fields.Char('故障率(%)', reaonly='True')
|
||||
fault_nums = fields.Integer('故障次数', reaonly='True')
|
||||
|
||||
# 设备故障日志
|
||||
sf_maintenance_logs_ids = fields.One2many('sf.maintenance.logs', 'maintenance_equipment_oee_id', '设备故障日志',
|
||||
related='equipment_id.sf_maintenance_logs_ids')
|
||||
oee_logs = fields.One2many('maintenance.equipment.oee.logs', 'equipment_oee_id', string='运行日志')
|
||||
|
||||
day_logs_detail = fields.Html('日运行日志详情')
|
||||
history_logs_detail = fields.Html('历史运行日志详情')
|
||||
begin_time = fields.Date('开始时间')
|
||||
end_time = fields.Date('结束时间')
|
||||
|
||||
# 获取日志详情
|
||||
def get_day_logs(self):
|
||||
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||
print(base_url)
|
||||
config = self.env['ir.config_parameter'].sudo()
|
||||
# url = 'http://172.16.10.112:8069/api/logs/list'
|
||||
# url_time = 'http://localhost:9069/api/RunningTimeDetail'
|
||||
url = base_url + '/api/logs/list'
|
||||
url_time = base_url + '/api/RunningTimeDetail'
|
||||
machine_list = [self.equipment_code]
|
||||
begin_time = datetime.datetime.now().strftime('%Y-%m-%d') + ' 00:00:00'
|
||||
end_time = datetime.datetime.now().strftime('%Y-%m-%d') + ' 23:59:59'
|
||||
|
||||
# 请求的数据
|
||||
data = {
|
||||
"machine_list": str(machine_list),
|
||||
"begin_time": begin_time,
|
||||
"end_time": end_time
|
||||
}
|
||||
|
||||
data_time = {
|
||||
"machine_list": str(machine_list)
|
||||
}
|
||||
|
||||
print(data)
|
||||
|
||||
# 发送POST请求
|
||||
response = requests.post(url, json={}, data=data)
|
||||
response_time = requests.post(url_time, json={}, data=data_time)
|
||||
# print(response.json()) # 输出服务器返回的响应
|
||||
print(response_time.json())
|
||||
if response_time.status_code == 200:
|
||||
result_time = response_time.json()
|
||||
real_dict = result_time['data'][self.equipment_code]
|
||||
print('=', result_time)
|
||||
if result_time['status'] == 1:
|
||||
self.online_time = round((convert_to_seconds(real_dict['power_on_time']) - convert_to_seconds(
|
||||
real_dict['power_on_24_time'])) / 3600, 2)
|
||||
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.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:
|
||||
result = response.json()
|
||||
print('============', result)
|
||||
if result['status'] == 1:
|
||||
logs_list = result['data'][self.equipment_code]
|
||||
logs_detail = ''
|
||||
log_state = ''
|
||||
for log in logs_list:
|
||||
if log['state'] != log_state:
|
||||
print('loooooooooooooooooooogs', log)
|
||||
production_name = log['production_name'] if log['production_name'] else ' '
|
||||
logs_detail += '<tr><td>' + log['time'] + '</td><td>' + log[
|
||||
'state'] + '</td><td>' + production_name + '</td></tr>'
|
||||
log_state = log['state']
|
||||
# self.day_logs_detail = '<table><tr><th>时间</th><th>事件/状态</th><th>加工工单</th></tr>' + logs_detail + '</table>'
|
||||
self.day_logs_detail = '''
|
||||
<table border="1" style="border-collapse: collapse; width: 100%; text-align: center;">
|
||||
<tr style="background-color: #f2f2f2;">
|
||||
<th style="padding: 8px; border: 1px solid #ddd;">时间</th>
|
||||
<th style="padding: 8px; border: 1px solid #ddd;">事件/状态</th>
|
||||
<th style="padding: 8px; border: 1px solid #ddd;">加工工单</th>
|
||||
</tr>
|
||||
{logs_detail}
|
||||
</table>
|
||||
'''.format(logs_detail=logs_detail)
|
||||
|
||||
else:
|
||||
self.day_logs_detail = '获取日志失败'
|
||||
else:
|
||||
self.day_logs_detail = '获取日志失败'
|
||||
|
||||
# 获取历史日志详情
|
||||
def get_history_logs(self):
|
||||
config = self.env['ir.config_parameter'].sudo()
|
||||
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||
# url = 'http://172.16.10.112:8069/api/logs/list'
|
||||
url = base_url + '/api/logs/list'
|
||||
url_time = base_url + '/api/RunningTimeDetail'
|
||||
machine_list = [self.equipment_code]
|
||||
if not self.begin_time:
|
||||
raise UserError('请选择开始时间')
|
||||
if not self.end_time:
|
||||
raise UserError('请选择结束时间')
|
||||
|
||||
begin_time = self.begin_time.strftime('%Y-%m-%d') + ' 00:00:00'
|
||||
end_time = self.end_time.strftime('%Y-%m-%d') + ' 23:59:59'
|
||||
|
||||
# 请求的数据
|
||||
data = {
|
||||
"machine_list": str(machine_list),
|
||||
"begin_time": begin_time,
|
||||
"end_time": end_time
|
||||
}
|
||||
|
||||
print(data)
|
||||
|
||||
# 发送POST请求
|
||||
response = requests.post(url, json={}, data=data)
|
||||
print(response.json()) # 输出服务器返回的响应
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
print('============', result)
|
||||
if result['status'] == 1:
|
||||
logs_list = result['data'][self.equipment_code]
|
||||
logs_detail = ''
|
||||
log_state = ''
|
||||
for log in logs_list:
|
||||
if log['state'] != log_state:
|
||||
production_name = log['production_name'] if log['production_name'] else ' '
|
||||
logs_detail += '<tr><td>' + log['time'] + '</td><td>' + log[
|
||||
'state'] + '</td><td>' + production_name + '</td></tr>'
|
||||
log_state = log['state']
|
||||
# self.day_logs_detail = '<table><tr><th>时间</th><th>事件/状态</th><th>加工工单</th></tr>' + logs_detail + '</table>'
|
||||
self.history_logs_detail = '''
|
||||
<table border="1" style="border-collapse: collapse; width: 100%; text-align: center;">
|
||||
<tr style="background-color: #f2f2f2;">
|
||||
<th style="padding: 8px; border: 1px solid #ddd;">时间</th>
|
||||
<th style="padding: 8px; border: 1px solid #ddd;">事件/状态</th>
|
||||
<th style="padding: 8px; border: 1px solid #ddd;">加工工单</th>
|
||||
</tr>
|
||||
{logs_detail}
|
||||
</table>
|
||||
'''.format(logs_detail=logs_detail)
|
||||
|
||||
else:
|
||||
self.history_logs_detail = '获取日志失败'
|
||||
else:
|
||||
self.history_logs_detail = '获取日志失败'
|
||||
|
||||
# 下载历史日志
|
||||
def download_history_logs(self):
|
||||
config = self.env['ir.config_parameter'].sudo()
|
||||
url = 'http://172.16.10.112:8069/api/logs/list'
|
||||
machine_list = [self.equipment_code]
|
||||
if not self.begin_time:
|
||||
raise UserError('请选择开始时间')
|
||||
if not self.end_time:
|
||||
raise UserError('请选择结束时间')
|
||||
|
||||
begin_time = self.begin_time.strftime('%Y-%m-%d') + ' 00:00:00'
|
||||
end_time = self.end_time.strftime('%Y-%m-%d') + ' 23:59:59'
|
||||
|
||||
# 请求的数据
|
||||
data = {
|
||||
"machine_list": str(machine_list),
|
||||
"begin_time": begin_time,
|
||||
"end_time": end_time
|
||||
}
|
||||
|
||||
print(data)
|
||||
|
||||
# 发送POST请求
|
||||
response = requests.post(url, json={}, data=data)
|
||||
print(response.json()) # 输出服务器返回的响应
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
print('============', result)
|
||||
if result['status'] == 1:
|
||||
logs_list = result['data'][self.equipment_code]
|
||||
logs_detail = ''
|
||||
for log in logs_list:
|
||||
production_name = log['production_name'] if log['production_name'] else ' '
|
||||
# todo 下载日志
|
||||
|
||||
else:
|
||||
self.history_logs_detail = '下载日志失败'
|
||||
else:
|
||||
self.history_logs_detail = '下载日志失败'
|
||||
|
||||
def name_get(self):
|
||||
result = []
|
||||
@@ -95,4 +337,3 @@ class SfMaintenanceEquipmentOEELogDetail(models.Model):
|
||||
log_id = fields.Many2one('maintenance.equipment.oee.logs', '日志')
|
||||
# equipment_code = fields.Char('设备编码', related='log_id.equipment_code')
|
||||
equipment_code = fields.Char('设备编码', readonly='True')
|
||||
|
||||
|
||||
@@ -67,6 +67,3 @@ access_sf_cutting_tool_type_admin_sf_group_equipment_user,sf_cutting_tool_type_a
|
||||
access_sf_cutting_tool_type_group_purchase_director_sf_group_equipment_user,sf_cutting_tool_type_group_purchase_director,sf_base.model_sf_cutting_tool_type,sf_maintenance.sf_group_equipment_user,1,0,0,0
|
||||
access_sf_cutting_tool_type_group_sale_director_sf_group_equipment_user,sf_cutting_tool_type_group_sale_director,sf_base.model_sf_cutting_tool_type,sf_maintenance.sf_group_equipment_user,1,0,0,0
|
||||
access_sf_cutting_tool_type_group_plan_director_sf_group_equipment_user,sf_cutting_tool_type_group_plan_director,sf_base.model_sf_cutting_tool_type,sf_maintenance.sf_group_equipment_user,1,0,0,0
|
||||
|
||||
access_maintenance_equipment_oee_logs,maintenance_equipment_oee_logs,model_maintenance_equipment_oee_logs,sf_maintenance.sf_group_equipment_manager,1,1,1,1
|
||||
access_maintenance_equipment_oee_log_detail,maintenance_equipment_oee_log_detail,model_maintenance_equipment_oee_log_detail,sf_maintenance.sf_group_equipment_manager,1,1,1,1
|
||||
|
@@ -8,13 +8,13 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="equipment_id"/>
|
||||
<field name="equipment_time"/>
|
||||
<field name="run_time"/>
|
||||
<field name="done_nums"/>
|
||||
<field name="utilization_rate"/>
|
||||
<field name="online_time"/>
|
||||
<field name="work_time"/>
|
||||
<field name="work_rate"/>
|
||||
<field name="fault_time"/>
|
||||
<field name="fault_nums"/>
|
||||
<field name="fault_rate"/>
|
||||
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -37,32 +37,102 @@
|
||||
|
||||
<group>
|
||||
<group>
|
||||
<field name="type_id" readonly="1"/>
|
||||
<field name="equipment_time"/>
|
||||
<field name="run_time"/>
|
||||
<field name="done_nums"/>
|
||||
<field name="utilization_rate"/>
|
||||
<field name="fault_time"/>
|
||||
<field name="fault_nums"/>
|
||||
<group>
|
||||
<field name="equipment_id" domain="[('name','ilike','加工中心')]"/>
|
||||
<field name="type_id"/>
|
||||
<field name="state"/>
|
||||
<field name="equipment_code"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<field name="machine_tool_picture" widget="image" readonly="1"/>
|
||||
<group>
|
||||
<!-- <field name="state" nolabel="1"/> -->
|
||||
<!-- <field name="state" string=""/> -->
|
||||
</group>
|
||||
<group>
|
||||
<field name="machine_tool_picture" widget="image" nolabel="1"/>
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="online_time" readonly="1"/>
|
||||
<field name="offline_time" readonly="1"/>
|
||||
<field name="fault_rate" readonly="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="idle_nums" readonly="1"/>
|
||||
<field name="fault_time" readonly="1"/>
|
||||
<field name="fault_nums" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="idle_time"/>
|
||||
<field name="idle_rate"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="work_time"/>
|
||||
<field name="work_rate"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
</group>
|
||||
<!-- <notebook> -->
|
||||
<!-- <page string="运行日志"> -->
|
||||
<!-- <field name="oee_logs"> -->
|
||||
<!-- <tree create="1" edit="1" delete="1" editable="bottom"> -->
|
||||
<!-- <field name = 'run_time'/> -->
|
||||
<!-- <field name = 'state'/> -->
|
||||
<!-- <field name = 'workorder_id'/> -->
|
||||
<!-- <field name = 'time'/> -->
|
||||
<!-- <field name = 'color' widget="color"/> -->
|
||||
<!-- </tree> -->
|
||||
<!-- </field> -->
|
||||
<notebook>
|
||||
<page string="24H日志详情">
|
||||
<group>
|
||||
<button name="get_day_logs" type="object" string="查看24H日志" t-attf-style="white-space:nowrap;"/>
|
||||
</group>
|
||||
<field name="day_logs_detail" readonly="1" widget="html"/>
|
||||
<!-- <field name="page_num"/> -->
|
||||
<!-- <group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <button name="previous_day_logs" type="object" string="上一页" t-attf-style="white-space:nowrap;"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <button name="next_day_logs" type="object" string="下一页" t-attf-style="white-space:nowrap;"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- </group> -->
|
||||
|
||||
|
||||
<!-- <field name="day_logs_detail"/> -->
|
||||
<!-- <field name="day_logs_detail" widget="html" nolabel="1"/> -->
|
||||
<!-- <field name="day_logs_detail" widget="html" nolabel="1" class="oe_form_field oe_form_field_html"/> -->
|
||||
<!-- <field name="day_logs_detail" widget="html" nolabel="1" class="oe_form_field oe_form_field_html" options='{"class": "o_form_readonly"}'/> -->
|
||||
<!-- <field name="day_logs_detail" widget="html" nolabel="1" class="oe_form_field oe_form_field_html" options='{"class": "o_form_readonly", "readonly": true, "style": "width: 100%; height: 400px;"}'/> -->
|
||||
<!-- <group> -->
|
||||
<!-- <div class="oe_html_field"> -->
|
||||
<!-- <div id="pagination_day_logs"> -->
|
||||
<!-- <button id="prev_page_day_logs" disabled="true">Previous</button> -->
|
||||
<!-- <button id="next_page_day_logs">Next</button> -->
|
||||
<!-- </div> -->
|
||||
<!-- </div> -->
|
||||
<!-- </group> -->
|
||||
</page>
|
||||
<!-- <page string="历史日志详情"> -->
|
||||
<!-- <group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="begin_time"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="end_time"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- </group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <button name="get_history_logs" type="object" string="查看历史日志" t-attf-style="white-space:nowrap;"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <button name="download_history_logs" type="object" string="下载历史日志" t-attf-style="white-space:nowrap;"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- </group> -->
|
||||
|
||||
<!-- </group> -->
|
||||
<!-- <field name="history_logs_detail"/> -->
|
||||
<!-- </page> -->
|
||||
<!-- </notebook> -->
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="type" optional="hide"/>
|
||||
<field name="brand"/>
|
||||
<field name="maintenance_equipment_id"/>
|
||||
<!-- <field name="brand"/> -->
|
||||
<field name="name"/>
|
||||
<field name="code_location" optional="hide"/>
|
||||
<field name="fault_code" optional="hide"/>
|
||||
<field name="alarm_time"/>
|
||||
@@ -37,13 +37,14 @@
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<h1>
|
||||
<field name="code" readonly="1"/>
|
||||
<field name="name" readonly="1"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
|
||||
<!-- <field name="name"/> -->
|
||||
|
||||
<field name="code"/>
|
||||
<!-- <field name="type" required="1" widget="radio" options="{'horizontal': true}"/> -->
|
||||
<field name="maintenance_equipment_id"/>
|
||||
<field name="brand"/>
|
||||
@@ -57,7 +58,6 @@
|
||||
</group>
|
||||
<group>
|
||||
<field name="operator"/>
|
||||
|
||||
<field name="fault_process"/>
|
||||
<!-- <field name="alarm_way" required="1" widget="radio" options="{'horizontal': true}"/> -->
|
||||
<field name="recovery_time"/>
|
||||
@@ -105,249 +105,6 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- 设备运行日志 -->
|
||||
<record id="view_maintenance_logs_run_tree" model="ir.ui.view">
|
||||
<field name="name">maintenance.logs.run.tree</field>
|
||||
<field name="model">maintenance.equipment.oee.logs</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="equipment_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_maintenance_logs_run_form" model="ir.ui.view">
|
||||
<field name="name">maintenance.logs.run.form</field>
|
||||
<field name="model">maintenance.equipment.oee.logs</field>
|
||||
<field name="arch" type="xml">
|
||||
<!-- <form string="设备运行日志"> -->
|
||||
<!-- <header> -->
|
||||
<!-- <field name="equipment_id" readonly="1"/> -->
|
||||
<!-- </header> -->
|
||||
<!-- <sheet> -->
|
||||
<!-- <div class="oe_title"> -->
|
||||
<!-- <h1> -->
|
||||
<!-- <field name="start_time" readonly="1"/> -->
|
||||
<!-- </h1> -->
|
||||
<!-- </div> -->
|
||||
<!-- <group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="stop_time" readonly="1"/> -->
|
||||
<!-- <field name="duration" readonly="1"/> -->
|
||||
<!-- <field name="oee" readonly="1"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="note"/> -->
|
||||
<!-- </group> -->
|
||||
|
||||
<!-- </group> -->
|
||||
<!-- </sheet> -->
|
||||
<!-- </form> -->
|
||||
<form string="设备运行日志">
|
||||
<!-- <header> -->
|
||||
<!-- <field name="name" readonly="1"/> -->
|
||||
<!-- </header> -->
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<h1>
|
||||
<field name="name"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="equipment_id" domain="[('name','ilike','加工中心')]"/>
|
||||
<field name="type_id"/>
|
||||
<field name="state"/>
|
||||
<field name="equipment_code"/>
|
||||
<field name="function_type"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<!-- <field name="state" nolabel="1"/> -->
|
||||
<field name="state" string=""/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="machine_tool_picture" widget="image" nolabel="1"/>
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="online_time" readonly="1"/>
|
||||
<field name="offline_time" readonly="1"/>
|
||||
<field name="fault_rate" readonly="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="offline_nums" readonly="1"/>
|
||||
<field name="fault_time" readonly="1"/>
|
||||
<field name="fault_nums" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="idle_time"/>
|
||||
<field name="idle_rate"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="work_time"/>
|
||||
<field name="work_rate"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="24H日志详情">
|
||||
<!-- 筛选出24小时内的日志 -->
|
||||
<!-- <field name="detail_ids" domain="[('time','<',(datetime.datetime.now() - datetime.timedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S'))]"> -->
|
||||
<field name="detail_ids" domain="[('state','ilike','加工')]">
|
||||
<tree>
|
||||
<!-- <field name="sequence"/> -->
|
||||
<field name="time"/>
|
||||
<field name="state"/>
|
||||
<field name="production_name"/>
|
||||
</tree>
|
||||
<!-- <form> -->
|
||||
<!-- <field name="sequence"/> -->
|
||||
<!-- <field name="time"/> -->
|
||||
<!-- <field name="state"/> -->
|
||||
<!-- <field name="production_id"/> -->
|
||||
<!-- </form> -->
|
||||
|
||||
</field>
|
||||
</page>
|
||||
<page string="历史日志详情">
|
||||
<field name="detail_ids">
|
||||
<tree>
|
||||
<!-- <field name="sequence"/> -->
|
||||
<field name="time"/>
|
||||
<field name="state"/>
|
||||
<field name="production_name"/>
|
||||
</tree>
|
||||
<!-- <form> -->
|
||||
<!-- <field name="sequence"/> -->
|
||||
<!-- <field name="time"/> -->
|
||||
<!-- <field name="state"/> -->
|
||||
<!-- <field name="production_id"/> -->
|
||||
<!-- </form> -->
|
||||
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- <record id="view_maintenance_logs_run_search" model="ir.ui.view"> -->
|
||||
<!-- <field name="name">maintenance.logs.run.search</field> -->
|
||||
<!-- <field name="model">maintenance.equipment.oee.logs</field> -->
|
||||
<!-- <field name="arch" type="xml"> -->
|
||||
<!-- <search> -->
|
||||
<!-- <field name="equipment_id"/> -->
|
||||
<!-- <field name="start_time"/> -->
|
||||
<!-- <field name="stop_time"/> -->
|
||||
<!-- <field name="duration"/> -->
|
||||
<!-- <field name="oee"/> -->
|
||||
<!-- <field name="note"/> -->
|
||||
<!-- </search> -->
|
||||
|
||||
<!-- </field> -->
|
||||
<!-- </record> -->
|
||||
|
||||
<!-- 设备运行日志详情 -->
|
||||
<record id="view_maintenance_logs_run_detail_tree" model="ir.ui.view">
|
||||
<field name="name">maintenance.logs.run.detail.tree</field>
|
||||
<field name="model">maintenance.equipment.oee.log.detail</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<!-- <field name="sequence"/> -->
|
||||
<field name="time"/>
|
||||
<field name="state"/>
|
||||
<field name="production_name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_maintenance_logs_run_detail_form" model="ir.ui.view">
|
||||
<field name="name">maintenance.logs.run.detail.form</field>
|
||||
<field name="model">maintenance.equipment.oee.log.detail</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="设备运行日志详情">
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="state"/>
|
||||
<!-- <field name="production_id"/> -->
|
||||
</group>
|
||||
<group>
|
||||
<!-- <field name="sequence"/> -->
|
||||
<field name="time"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- <record id="view_maintenance_logs_run_detail_search" model="ir.ui.view"> -->
|
||||
<!-- <field name="name">maintenance.logs.run.detail.search</field> -->
|
||||
<!-- <field name="model">maintenance.equipment.oee.logs.detail</field> -->
|
||||
<!-- <field name="arch" type="xml"> -->
|
||||
<!-- <search> -->
|
||||
<!-- <field name="equipment_id"/> -->
|
||||
<!-- <field name="start_time"/> -->
|
||||
<!-- <field name="stop_time"/> -->
|
||||
<!-- <field name="duration"/> -->
|
||||
<!-- <field name="oee"/> -->
|
||||
<!-- <field name="note"/> -->
|
||||
<!-- </search> -->
|
||||
<!-- </field> -->
|
||||
<!-- </record> -->
|
||||
|
||||
<!-- 设备运行日志详情action -->
|
||||
<!-- <record id="action_maintenance_logs_run_detail" model="ir.actions.act_window"> -->
|
||||
<!-- <field name="name">设备运行日志详情</field> -->
|
||||
<!-- <field name="type">ir.actions.act_window</field> -->
|
||||
<!-- <field name="res_model">maintenance.equipment.oee.logs.detail</field> -->
|
||||
<!-- <field name="view_mode">tree,form</field> -->
|
||||
<!-- <field name="view_id" ref="view_maintenance_logs_run_detail_tree"/> -->
|
||||
<!-- <field name="help" type="html"> -->
|
||||
<!-- <p class="oe_view_nocontent_create"> -->
|
||||
<!-- 设备运行日志详情 -->
|
||||
<!-- </p> -->
|
||||
<!-- </field> -->
|
||||
<!-- -->
|
||||
<!-- </record> -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<record id="action_maintenance_logs_run" model="ir.actions.act_window">
|
||||
<field name="name">设备运行日志</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">maintenance.equipment.oee.logs</field>
|
||||
<!-- <field name="search_view_id" ref="view_maintenance_logs_run_search"/> -->
|
||||
<field name="view_mode">tree,form</field>
|
||||
<!-- <field name="view_mode">form</field> -->
|
||||
<field name="view_id" ref="view_maintenance_logs_run_tree"/>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
设备运行日志
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<menuitem name="设备运行日志" id="menu_maintenance_logs_run" parent="maintenance.menu_m_request"
|
||||
sequence="10" action="action_maintenance_logs_run"/>
|
||||
|
||||
|
||||
<!-- Action -->
|
||||
|
||||
<record id="action_maintenance_logs" model="ir.actions.act_window">
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
'sf_manufacturing/static/src/scss/kanban_change.scss',
|
||||
'sf_manufacturing/static/src/xml/button_show_on_tree.xml',
|
||||
'sf_manufacturing/static/src/js/workpiece_delivery_wizard_confirm.js',
|
||||
'sf_manufacturing/static/src/js/custom_barcode_handlers.js',
|
||||
]
|
||||
|
||||
},
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
import logging
|
||||
import requests
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ class AgvScheduling(models.Model):
|
||||
def web_search_read(self, domain=None, fields=None, offset=0, limit=None, order=None, count_limit=None):
|
||||
domain = domain or []
|
||||
new_domain = []
|
||||
for index, item in enumerate(domain):
|
||||
for item in domain:
|
||||
if isinstance(item, list):
|
||||
if item[0] == 'delivery_workpieces':
|
||||
new_domain.append('&')
|
||||
@@ -63,7 +63,7 @@ class AgvScheduling(models.Model):
|
||||
continue
|
||||
new_domain.append(item)
|
||||
|
||||
return super(AgvScheduling, self).web_search_read(new_domain, fields, limit=limit, offset=offset)
|
||||
return super(AgvScheduling, self).web_search_read(new_domain, fields, offset, limit, order, count_limit)
|
||||
|
||||
@api.depends('task_completion_time', 'task_delivery_time')
|
||||
def _compute_task_duration(self):
|
||||
@@ -156,17 +156,26 @@ class AgvScheduling(models.Model):
|
||||
if agv_site_state == '空闲':
|
||||
# 查询终点接驳站为agv_site_id的AGV路线
|
||||
task_routes = self.env['sf.agv.task.route'].sudo().search([('end_site_id', '=', agv_site_id)])
|
||||
agv_scheduling = self.env['sf.agv.scheduling'].sudo().search(
|
||||
agv_schedulings = self.env['sf.agv.scheduling'].sudo().search(
|
||||
[('state', '=', '待下发'), ('agv_route_type', 'in', task_routes.mapped('route_type'))],
|
||||
order='id asc',
|
||||
limit=1
|
||||
)
|
||||
task_route = task_routes.filtered(
|
||||
lambda r: r.start_site_id == agv_scheduling.start_site_id and r.start_site_id == agv_scheduling.start_site_id
|
||||
)
|
||||
if task_route:
|
||||
# 下发AGV调度任务并修改接驳站状态为占用
|
||||
agv_scheduling.dispatch_scheduling(task_route)
|
||||
for agv_scheduling in agv_schedulings:
|
||||
# 找到所有起点接驳站匹配的路线
|
||||
start_matched_task_routes = task_routes.filtered(
|
||||
lambda r: r.start_site_id == agv_scheduling.start_site_id
|
||||
)
|
||||
# 如果调度任务有终点接驳站,找到终点接驳站匹配的路线
|
||||
if agv_scheduling.end_site_id:
|
||||
matched_task_routes = start_matched_task_routes.filtered(
|
||||
lambda r: r.end_site_id == agv_scheduling.end_site_id
|
||||
)
|
||||
else:
|
||||
matched_task_routes = start_matched_task_routes
|
||||
if matched_task_routes:
|
||||
# 下发AGV调度任务并修改接驳站状态为占用
|
||||
agv_scheduling.dispatch_scheduling(matched_task_routes[0])
|
||||
break;
|
||||
|
||||
def _delivery_avg(self):
|
||||
config = self.env['res.config.settings'].get_values()
|
||||
|
||||
@@ -318,8 +318,10 @@ class MrpProduction(models.Model):
|
||||
# cnc程序获取
|
||||
def fetchCNC(self, production_names):
|
||||
cnc = self.env['mrp.production'].search([('id', '=', self.id)])
|
||||
quick_order = self.env['quick.easy.order'].search(
|
||||
[('name', '=', cnc.product_id.default_code.rsplit('-', 1)[0])])
|
||||
quick_order = False
|
||||
if cnc.product_id.default_code:
|
||||
quick_order = self.env['quick.easy.order'].search(
|
||||
[('name', '=', cnc.product_id.default_code.rsplit('-', 1)[0])])
|
||||
programme_way = False
|
||||
if cnc.manual_quotation is True:
|
||||
programme_way = 'manual operation'
|
||||
@@ -804,6 +806,10 @@ class MrpProduction(models.Model):
|
||||
backorders = backorders - productions_to_backorder
|
||||
|
||||
productions_not_to_backorder._post_inventory(cancel_backorder=True)
|
||||
# if self.workorder_ids.filtered(lambda w: w.routing_type in ['表面工艺']):
|
||||
# move_finish = self.env['stock.move'].search([('created_production_id', '=', self.id)])
|
||||
# if move_finish:
|
||||
# move_finish._action_assign()
|
||||
productions_to_backorder._post_inventory(cancel_backorder=True)
|
||||
|
||||
# if completed products make other confirmed/partially_available moves available, assign them
|
||||
@@ -984,8 +990,8 @@ class MrpProduction(models.Model):
|
||||
panel_workorder.cmm_ids.sudo().unlink()
|
||||
if panel_workorder.cnc_ids:
|
||||
panel_workorder.cnc_ids.sudo().unlink()
|
||||
self.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan(
|
||||
production)
|
||||
# self.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan(
|
||||
# production)
|
||||
# program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test',
|
||||
# processing_panel)
|
||||
logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import datetime
|
||||
import logging
|
||||
from datetime import timedelta, time
|
||||
from collections import defaultdict
|
||||
from odoo import fields, models, api
|
||||
@@ -6,6 +7,8 @@ from odoo.addons.resource.models.resource import Intervals
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
import math
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ResWorkcenter(models.Model):
|
||||
_name = "mrp.workcenter"
|
||||
@@ -163,6 +166,19 @@ class ResWorkcenter(models.Model):
|
||||
else:
|
||||
record.effective_working_hours_day = 0
|
||||
|
||||
# 计算传入时间日有效工作时长
|
||||
def _compute_effective_working_hours_day1(self, date):
|
||||
effective_working_hours_day = 0
|
||||
for record in self:
|
||||
attendance_ids = [p for p in record.resource_calendar_id.attendance_ids if
|
||||
p.dayofweek == self.get_current_day_of_week(date)]
|
||||
if attendance_ids:
|
||||
for attendance_id in attendance_ids:
|
||||
if attendance_id.hour_from and attendance_id.hour_to:
|
||||
effective_working_hours_day += attendance_id.hour_to - attendance_id.hour_from
|
||||
|
||||
return effective_working_hours_day
|
||||
|
||||
# 获取传入时间是星期几
|
||||
def get_current_day_of_week(self, datetime):
|
||||
day_num = datetime.weekday()
|
||||
@@ -211,7 +227,12 @@ class ResWorkcenter(models.Model):
|
||||
('state', 'not in', ['draft', 'cancel'])])
|
||||
if plan_ids:
|
||||
sum_qty = sum([p.product_qty for p in plan_ids])
|
||||
if sum_qty >= self.default_capacity:
|
||||
date_planned_working_hours = self._compute_effective_working_hours_day1(date_planned)
|
||||
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 True
|
||||
|
||||
|
||||
@@ -144,6 +144,8 @@ class ResMrpWorkOrder(models.Model):
|
||||
# 是否绑定托盘
|
||||
is_trayed = fields.Boolean(string='是否绑定托盘', default=False)
|
||||
|
||||
tag_type = fields.Selection([("重新加工", "重新加工")], string="标签", tracking=True)
|
||||
|
||||
@api.depends('name', 'production_id.name')
|
||||
def _compute_surface_technics_picking_ids(self):
|
||||
for workorder in self:
|
||||
@@ -426,17 +428,18 @@ class ResMrpWorkOrder(models.Model):
|
||||
logging.info('local_file_path:%s' % local_file_path)
|
||||
remote_path = '/home/ftp/ftp_root/ThreeTest/XT/Before/' + local_filename
|
||||
logging.info('remote_path:%s' % remote_path)
|
||||
# if not ftp.file_exists(remote_path):
|
||||
paload_data = {
|
||||
"filename": local_filename
|
||||
}
|
||||
if not ftp_resconfig['get_check_file_path']:
|
||||
raise UserError('请先配置获取检测报告地址')
|
||||
url = ftp_resconfig['get_check_file_path'] + '/get/check/report'
|
||||
response = requests.post(url, json=paload_data)
|
||||
logging.info('response:%s' % response.json())
|
||||
if response.json().get('detail'):
|
||||
raise UserError(response.json().get('detail'))
|
||||
is_get_detection_file = self.env['ir.config_parameter'].sudo().get_param('is_get_detection_file')
|
||||
if not is_get_detection_file:
|
||||
paload_data = {
|
||||
"filename": local_filename
|
||||
}
|
||||
if not ftp_resconfig['get_check_file_path']:
|
||||
raise UserError('请先配置获取检测报告地址')
|
||||
url = ftp_resconfig['get_check_file_path'] + '/get/check/report'
|
||||
response = requests.post(url, json=paload_data)
|
||||
logging.info('response:%s' % response.json())
|
||||
if response.json().get('detail'):
|
||||
raise UserError(response.json().get('detail'))
|
||||
|
||||
if not ftp.file_exists(remote_path):
|
||||
raise UserError(f"文件不存在: {remote_path}")
|
||||
@@ -603,6 +606,8 @@ class ResMrpWorkOrder(models.Model):
|
||||
print("(%.2f,%.2f)" % (x, y))
|
||||
self.material_center_point = ("(%.2f,%.2f,%.2f)" % (x, y, z))
|
||||
self.X_deviation_angle = jdz
|
||||
logging.info("坯料中心点坐标:(%.2f,%.2f)" % (x, y))
|
||||
logging.info("X轴偏差度数:%.2f" % jdz)
|
||||
# 将补偿值写入CNC加工工单
|
||||
workorder = self.env['mrp.workorder'].browse(self.ids)
|
||||
work = workorder.production_id.workorder_ids
|
||||
@@ -705,6 +710,7 @@ class ResMrpWorkOrder(models.Model):
|
||||
'date_planned_finished': datetime.now() + timedelta(days=1),
|
||||
'duration_expected': duration_expected,
|
||||
'duration': 0,
|
||||
'tag_type': '重新加工' if item is False else False,
|
||||
'cnc_ids': False if route.routing_type != 'CNC加工' else self.env['sf.cnc.processing']._json_cnc_processing(
|
||||
k, item),
|
||||
'cmm_ids': False if route.routing_type != 'CNC加工' else self.env['sf.cmm.program']._json_cmm_program(k,
|
||||
@@ -1025,16 +1031,20 @@ class ResMrpWorkOrder(models.Model):
|
||||
# 查询工序最小的非完工、非返工的装夹预调工单
|
||||
work_id = self.search(
|
||||
[('production_id', '=', workorder.production_id.id),
|
||||
('routing_type', '=', '装夹预调'),
|
||||
('state', 'not in', ['rework', 'done', 'cancel'])],
|
||||
limit=1,
|
||||
order="sequence")
|
||||
if workorder == work_id:
|
||||
if workorder.production_id.reservation_state == 'assigned':
|
||||
workorder.state = 'ready'
|
||||
elif workorder.production_id.reservation_state != 'assigned':
|
||||
workorder.state = 'waiting'
|
||||
continue
|
||||
if work_id.routing_type == '装夹预调':
|
||||
if workorder == work_id:
|
||||
if workorder.production_id.reservation_state == 'assigned':
|
||||
workorder.state = 'ready'
|
||||
elif workorder.production_id.reservation_state != 'assigned':
|
||||
workorder.state = 'waiting'
|
||||
continue
|
||||
elif (workorder.name == '装夹预调' and
|
||||
workorder.state not in ['rework', 'done', 'cancel']):
|
||||
if workorder.state != 'pending':
|
||||
workorder.state = 'pending'
|
||||
if workorder.production_id.tool_state in ['1', '2'] and workorder.state == 'ready':
|
||||
workorder.state = 'waiting'
|
||||
continue
|
||||
@@ -1072,6 +1082,12 @@ class ResMrpWorkOrder(models.Model):
|
||||
|
||||
# 重写工单开始按钮方法
|
||||
def button_start(self):
|
||||
if self.routing_type == 'CNC加工':
|
||||
self.env['sf.production.plan'].sudo().search([('name', '=', self.production_id.name)]).write({
|
||||
'state': 'processing',
|
||||
'actual_start_time': datetime.now()
|
||||
})
|
||||
|
||||
if self.routing_type == '装夹预调':
|
||||
# 判断是否有坯料的序列号信息
|
||||
boolean = False
|
||||
@@ -1179,8 +1195,10 @@ class ResMrpWorkOrder(models.Model):
|
||||
if not record.rfid_code and record.is_rework is False:
|
||||
raise UserError("请扫RFID码进行绑定")
|
||||
if record.is_rework is False:
|
||||
if not record.material_center_point or record.X_deviation_angle <= 0:
|
||||
raise UserError("坯料中心点为空或X偏差角度小于等于0")
|
||||
if not record.material_center_point:
|
||||
raise UserError("坯料中心点为空,请检查")
|
||||
if record.X_deviation_angle <= 0:
|
||||
raise UserError("X偏差角度小于等于0,请检查!本次计算的X偏差角度为:%s" % record.X_deviation_angle)
|
||||
record.process_state = '待加工'
|
||||
# record.write({'process_state': '待加工'})
|
||||
record.production_id.process_state = '待加工'
|
||||
@@ -1338,6 +1356,7 @@ class ResMrpWorkOrder(models.Model):
|
||||
arch = etree.fromstring(tree_view['arch'])
|
||||
# 查找 tree 标签
|
||||
tree_element = arch.xpath("//tree")[0]
|
||||
tree_element.set('js_class', 'remove_focus_list_view')
|
||||
|
||||
# 查找或创建 header 标签
|
||||
header_element = tree_element.find('header')
|
||||
@@ -1560,6 +1579,8 @@ class SfWorkOrderBarcodes(models.Model):
|
||||
|
||||
def on_barcode_scanned(self, barcode):
|
||||
logging.info('Rfid:%s' % barcode)
|
||||
if 'O-CMD' in barcode:
|
||||
return None
|
||||
workorder = self.env['mrp.workorder'].browse(self.ids)
|
||||
# workorder_preset = self.env['mrp.workorder'].search(
|
||||
# [('routing_type', '=', '装夹预调'), ('rfid_code', '=', barcode)])
|
||||
@@ -1567,8 +1588,11 @@ class SfWorkOrderBarcodes(models.Model):
|
||||
[('routing_type', '=', '装夹预调'), ('rfid_code', '=', barcode)])
|
||||
if workorder_olds:
|
||||
name = ''
|
||||
tem_list = []
|
||||
for workorder in workorder_olds:
|
||||
name = '%s %s' % (name, workorder.production_id.name)
|
||||
tem_list.append(workorder.production_id.name)
|
||||
for i in list(set(tem_list)):
|
||||
name = '%s %s' % (name, i)
|
||||
raise UserError('该托盘已绑定【%s】制造订单,请先解除绑定!!!' % name)
|
||||
if workorder:
|
||||
if workorder.routing_type == '装夹预调':
|
||||
@@ -1802,6 +1826,11 @@ class WorkPieceDelivery(models.Model):
|
||||
return is_free
|
||||
else:
|
||||
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小车
|
||||
def _delivery_avg(self):
|
||||
@@ -1862,7 +1891,7 @@ class WorkPieceDelivery(models.Model):
|
||||
logging.info('delivery_item-name:%s' % delivery_item.name)
|
||||
delivery_item.write({
|
||||
'task_delivery_time': fields.Datetime.now(),
|
||||
'status': '待配送'
|
||||
'status': '已下发'
|
||||
})
|
||||
if delivery_item.type == "上产线":
|
||||
delivery_item.workorder_id.write({'is_delivery': True})
|
||||
|
||||
@@ -44,6 +44,9 @@ class ResProductMo(models.Model):
|
||||
materials_id = fields.Many2one('sf.production.materials', string='材料')
|
||||
materials_type_id = fields.Many2one('sf.materials.model', string='材料型号',
|
||||
domain="[('materials_id', '=', materials_id)]")
|
||||
# materials_type_id = fields.Many2one(related='cutting_tool_model_id.material_model_id', string='材料型号',
|
||||
# domain="[('materials_id', '=', materials_id)]")
|
||||
# cutting_tool_model_id.material_model_id
|
||||
server_product_process_parameters_id = fields.Many2one('sf.production.process.parameter',
|
||||
string='表面工艺参数(服务产品)')
|
||||
model_process_parameters_ids = fields.Many2many('sf.production.process.parameter', 'process_parameter_rel',
|
||||
@@ -57,40 +60,100 @@ class ResProductMo(models.Model):
|
||||
|
||||
cutting_tool_type_id = fields.Many2one('sf.cutting.tool.type', string='类型',
|
||||
domain="[('cutting_tool_material_id.name', '=', cutting_tool_type)]")
|
||||
|
||||
brand_id = fields.Many2one('sf.machine.brand', '品牌')
|
||||
|
||||
tool_length = fields.Float('长度(mm)')
|
||||
tool_width = fields.Float('宽度(mm)')
|
||||
# cutting_tool_type_id = fields.Many2one(related='cutting_tool_model_id.cutting_tool_type_id', string='类型',
|
||||
# domain="[('cutting_tool_material_id.name', '=', cutting_tool_type)]")
|
||||
# brand_id = fields.Many2one('sf.machine.brand', '品牌')
|
||||
brand_id = fields.Many2one(related='cutting_tool_model_id.brand_id', string='品牌')
|
||||
# cutting_tool_model_id.brand_id
|
||||
# tool_length = fields.Float('长度(mm)')
|
||||
tool_length = fields.Float(related='specification_id.length', string='长度(mm)')
|
||||
# specification_id.length
|
||||
# tool_width = fields.Float('宽度(mm)')
|
||||
tool_width = fields.Float(related='specification_id.width', string='宽度(mm)')
|
||||
# specification_id.width
|
||||
tool_height = fields.Float('高度(mm)')
|
||||
tool_thickness = fields.Float('厚度(mm)')
|
||||
tool_weight = fields.Float('重量(kg)')
|
||||
tool_hardness = fields.Integer('硬度(hrc)')
|
||||
coating_material = fields.Char('涂层材质')
|
||||
# tool_thickness = fields.Float('厚度(mm)')
|
||||
tool_thickness = fields.Float(related='specification_id.thickness', string='厚度(mm)')
|
||||
# specification_id.thickness
|
||||
# tool_weight = fields.Float('重量(kg)')
|
||||
tool_weight = fields.Float(related='specification_id.weight', string='重量(kg)')
|
||||
# specification_id.weight
|
||||
# tool_hardness = fields.Integer('硬度(hrc)')
|
||||
tool_hardness = fields.Integer(related='cutting_tool_model_id.tool_hardness', string='硬度(hrc)')
|
||||
# cutting_tool_model_id.tool_hardness
|
||||
# coating_material = fields.Char('涂层材质')
|
||||
coating_material = fields.Char(related='cutting_tool_model_id.coating_material', string='涂层材质')
|
||||
# cutting_tool_model_id.coating_material
|
||||
# 整体式刀具特有字段
|
||||
cutting_tool_total_length = fields.Float('总长度(mm)', digits=(6, 1))
|
||||
cutting_tool_shank_length = fields.Float('柄部长度(mm)', digits=(6, 1))
|
||||
cutting_tool_blade_length = fields.Float('刃部长度(mm)')
|
||||
cutting_tool_blade_number = fields.Selection(
|
||||
[('0', '0'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8')],
|
||||
string='刃数(个)', default='0')
|
||||
# cutting_tool_total_length = fields.Float('总长度(mm)', digits=(6, 1))
|
||||
cutting_tool_total_length = fields.Float(related='specification_id.total_length', string='总长度(mm)', digits=(6, 1))
|
||||
# specification_id.total_length
|
||||
# cutting_tool_shank_length = fields.Float('柄部长度(mm)', digits=(6, 1))
|
||||
cutting_tool_shank_length = fields.Float(related='specification_id.handle_length', string='柄部长度(mm)',
|
||||
digits=(6, 1))
|
||||
# specification_id.handle_length
|
||||
# cutting_tool_blade_length = fields.Float('刃部长度(mm)')
|
||||
cutting_tool_blade_length = fields.Float(related='specification_id.blade_length', string='刃部长度(mm)')
|
||||
# specification_id.blade_length
|
||||
# cutting_tool_blade_number = fields.Selection(
|
||||
# [('0', '0'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8')],
|
||||
# string='刃数(个)', default='0')
|
||||
cutting_tool_blade_number = fields.Selection(related='specification_id.blade_number', string='刃数(个)')
|
||||
# specification_id.blade_number
|
||||
# 整体式刀具新增字段
|
||||
cutting_tool_neck_length = fields.Float('颈部长度(mm)', digits=(6, 1))
|
||||
cutting_tool_neck_diameter = fields.Float('颈部直径(mm)', digits=(6, 1))
|
||||
cutting_tool_shank_diameter = fields.Float('柄部直径(mm)', digits=(6, 1))
|
||||
cutting_tool_blade_tip_diameter = fields.Float('刀尖直径(mm)', digits=(6, 1))
|
||||
cutting_tool_blade_tip_taper = fields.Integer('刀尖锥度(°)')
|
||||
cutting_tool_blade_helix_angle = fields.Integer('刃部螺旋角(°)')
|
||||
cutting_tool_blade_type = fields.Char('刃部类型')
|
||||
cutting_tool_pitch = fields.Float('牙距(mm)')
|
||||
cutting_tool_blade_width = fields.Float('刃部宽度(mm)')
|
||||
cutting_tool_blade_depth = fields.Float('刃部深度(mm)')
|
||||
# cutting_tool_neck_length = fields.Float('颈部长度(mm)', digits=(6, 1))
|
||||
cutting_tool_neck_length = fields.Float(related='specification_id.neck_length', string='颈部长度(mm)', digits=(6, 1))
|
||||
# specification_id.neck_length
|
||||
# cutting_tool_neck_diameter = fields.Float('颈部直径(mm)', digits=(6, 1))
|
||||
cutting_tool_neck_diameter = fields.Float(related='specification_id.neck_diameter', string='颈部直径(mm)',
|
||||
digits=(6, 1))
|
||||
# specification_id.neck_diameter
|
||||
# cutting_tool_shank_diameter = fields.Float('柄部直径(mm)', digits=(6, 1))
|
||||
cutting_tool_shank_diameter = fields.Float(related='specification_id.handle_diameter', string='柄部直径(mm)', digits=(6, 1))
|
||||
# specification_id.handle_diameter
|
||||
# cutting_tool_blade_tip_diameter = fields.Float('刀尖直径(mm)', digits=(6, 1))
|
||||
cutting_tool_blade_tip_diameter = fields.Float(related='specification_id.blade_tip_diameter', string='刀尖直径(mm)',
|
||||
digits=(6, 1))
|
||||
# specification_id.blade_tip_diameter
|
||||
# cutting_tool_blade_tip_taper = fields.Integer('刀尖锥度(°)')
|
||||
cutting_tool_blade_tip_taper = fields.Integer(related='specification_id.blade_tip_taper', string='刀尖锥度(°)')
|
||||
# specification_id.blade_tip_taper
|
||||
# cutting_tool_blade_helix_angle = fields.Integer('刃部螺旋角(°)')
|
||||
cutting_tool_blade_helix_angle = fields.Integer(related='specification_id.blade_helix_angle', string='刃部螺旋角(°)')
|
||||
# specification_id.blade_helix_angle
|
||||
# cutting_tool_blade_type = fields.Char('刃部类型')
|
||||
cutting_tool_blade_type = fields.Char(related='cutting_tool_model_id.blade_type', string='刃部类型')
|
||||
# cutting_tool_pitch = fields.Float('牙距(mm)')
|
||||
cutting_tool_pitch = fields.Float(related='specification_id.pitch', string='牙距(mm)')
|
||||
# specification_id.pitch
|
||||
# cutting_tool_blade_width = fields.Float('刃部宽度(mm)')
|
||||
cutting_tool_blade_width = fields.Float(related='specification_id.blade_width', string='刃部宽度(mm)')
|
||||
# specification_id.blade_width
|
||||
# cutting_tool_blade_depth = fields.Float('刃部深度(mm)')
|
||||
cutting_tool_blade_depth = fields.Float(related='specification_id.blade_depth', string='刃部深度(mm)')
|
||||
# specification_id.blade_depth
|
||||
cutting_tool_cut_depth = fields.Float('切削深度(mm)')
|
||||
cutting_tool_cut_depth_max = fields.Float('最大切削深度(mm)')
|
||||
cutting_tool_coarse_medium_fine = fields.Selection([('粗', '粗'), ('中', '中'), ('精', '精')], '粗/中/精')
|
||||
cutting_tool_run_out_accuracy_max = fields.Float('端跳精度max', digits=(6, 1))
|
||||
cutting_tool_run_out_accuracy_min = fields.Float('端跳精度min', digits=(6, 1))
|
||||
cutting_tool_blade_tip_working_size = fields.Char('刀尖处理尺寸(R半径mm/倒角)', size=20)
|
||||
# cutting_tool_cut_depth_max = fields.Float('最大切削深度(mm)')
|
||||
cutting_tool_cut_depth_max = fields.Float(related='specification_id.cut_depth_max', string='最大切削深度(mm)')
|
||||
# specification_id.cut_depth_max
|
||||
# cutting_tool_coarse_medium_fine = fields.Selection([('粗', '粗'), ('中', '中'), ('精', '精')], '粗/中/精')
|
||||
cutting_tool_coarse_medium_fine = fields.Selection(related='cutting_tool_model_id.integral_coarse_medium_fine', string='粗/中/精')
|
||||
# cutting_tool_model_id.integral_coarse_medium_fine
|
||||
# cutting_tool_run_out_accuracy_max = fields.Float('端跳精度max', digits=(6, 1))
|
||||
cutting_tool_run_out_accuracy_max = fields.Char(related='cutting_tool_model_id.integral_run_out_accuracy_max', string='端跳精度max', digits=(6, 1))
|
||||
# cutting_tool_model_id.integral_run_out_accuracy_max
|
||||
# cutting_tool_run_out_accuracy_min = fields.Float('端跳精度min', digits=(6, 1))
|
||||
cutting_tool_run_out_accuracy_min = fields.Char(related='cutting_tool_model_id.integral_run_out_accuracy_min',
|
||||
string='端跳精度min', digits=(6, 1))
|
||||
# cutting_tool_model_id.integral_run_out_accuracy_min
|
||||
# cutting_tool_blade_tip_working_size = fields.Char('刀尖倒角度(°)', size=20)
|
||||
cutting_tool_blade_tip_working_size = fields.Char(related='specification_id.blade_tip_working_size',
|
||||
string='刀尖倒角度(°)', size=20)
|
||||
# specification_id.blade_tip_working_size
|
||||
# cutting_tool_blade_tip_r_size = fields.Float('刀尖R角(mm)')
|
||||
cutting_tool_blade_tip_r_size = fields.Float(related='specification_id.tip_r_size',
|
||||
string='刀尖R角(mm)')
|
||||
# specification_id.tip_r_size
|
||||
fit_blade_shape_id = fields.Many2one('maintenance.equipment.image',
|
||||
'适配刀片形状', domain=[('type', '=', '刀片形状')])
|
||||
suitable_machining_method_ids = fields.Many2many('maintenance.equipment.image',
|
||||
@@ -237,6 +300,7 @@ class ResProductMo(models.Model):
|
||||
self.cutting_tool_blade_tip_taper = self.specification_id.blade_tip_taper
|
||||
self.cutting_tool_blade_helix_angle = self.specification_id.blade_helix_angle
|
||||
self.cutting_tool_blade_tip_working_size = self.specification_id.blade_tip_working_size
|
||||
self.cutting_tool_blade_tip_r_size = self.specification_id.tip_r_size
|
||||
self.cutting_tool_pitch = self.specification_id.pitch
|
||||
self.cutting_tool_blade_width = self.specification_id.blade_width
|
||||
self.cutting_tool_blade_depth = self.specification_id.blade_depth
|
||||
@@ -427,44 +491,97 @@ class ResProductMo(models.Model):
|
||||
# if not self.cutting_direction_ids:
|
||||
# raise ValidationError("请选择走刀方向")
|
||||
|
||||
cutting_speed_ids = fields.One2many('sf.cutting.speed', 'product_template_id', string='切削速度Vc')
|
||||
# cutting_speed_ids = fields.One2many('sf.cutting.speed', 'product_template_id', string='切削速度Vc')
|
||||
cutting_speed_ids = fields.One2many(related='cutting_tool_model_id.cutting_speed_ids',
|
||||
string='切削速度Vc')
|
||||
# cutting_tool_model_id.cutting_speed_ids
|
||||
feed_per_tooth_ids = fields.One2many('sf.feed.per.tooth', 'product_template_id', string='每齿走刀量fz')
|
||||
cutting_tool_diameter = fields.Float('刀具直径(mm)')
|
||||
cutting_tool_rear_angle = fields.Integer('后角(°)')
|
||||
cutting_tool_main_included_angle = fields.Integer('主偏角(°)')
|
||||
# cutting_tool_rear_angle = fields.Integer('后角(°)')
|
||||
cutting_tool_rear_angle = fields.Integer(related='specification_id.relief_angle',
|
||||
string='后角(°)')
|
||||
# specification_id.relief_angle
|
||||
# cutting_tool_main_included_angle = fields.Integer('主偏角(°)')
|
||||
cutting_tool_main_included_angle = fields.Integer(related='specification_id.main_included_angle',
|
||||
string='主偏角(°)')
|
||||
# specification_id.main_included_angle
|
||||
# 适用夹头型号可以多选
|
||||
cutting_tool_chuck_id = fields.Many2one(
|
||||
'sf.cutting_tool.standard.library',
|
||||
domain="[('cutting_tool_type', '=', '夹头')]",
|
||||
string='适用夹头型号')
|
||||
# 刀片参数
|
||||
cutting_tool_cut_blade_length = fields.Float('切削刃长(mm)')
|
||||
cutting_tool_blade_tip_circular_arc_radius = fields.Char('刀尖圆弧半径(mm)', size=20)
|
||||
cutting_tool_top_angle = fields.Integer('顶角(°)')
|
||||
cutting_tool_inscribed_circle_diameter = fields.Float('内接圆直径(mm)')
|
||||
cutting_tool_install_aperture_diameter = fields.Float('安装孔直径(mm)')
|
||||
cutting_tool_chip_breaker_groove = fields.Selection([('无', '无'), ('单面', '单面'), ('双面', '双面')],
|
||||
string='有无断屑槽')
|
||||
cutting_tool_chip_breaker_type_code = fields.Char('断屑槽型代号')
|
||||
cutting_tool_bladed_teeth_model = fields.Selection(
|
||||
[('无', '无'), ('V牙型', 'V牙型'), ('米制全牙型', '米制全牙型'), ('美制全牙型', '美制全牙型'),
|
||||
('惠氏全牙型', '惠氏全牙型'), ('BSPT全牙型', 'BSPT全牙型'), ('NPT全牙型', 'NPT全牙型'),
|
||||
('UNJ全牙型', 'UNJ全牙型'), ('DIN405圆牙型', 'DIN405圆牙型'), ('ACME梯形', 'ACME梯形'),
|
||||
('石油管螺纹刀片', '石油管螺纹刀片'), ('矮牙ACME梯形', '矮牙ACME梯形'),
|
||||
('Trapeze30° 103', 'Trapeze30° 103')], string='刀片牙型', default='无')
|
||||
cutting_tool_blade_blade_number = fields.Selection(
|
||||
[('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'),
|
||||
('7', '7'), ('8', '8'), ('9', '9'), ('10', '10')],
|
||||
string='刀片的刃数(个)')
|
||||
# cutting_tool_cut_blade_length = fields.Float('切削刃长(mm)')
|
||||
cutting_tool_cut_blade_length = fields.Float(related='specification_id.cutting_blade_length',
|
||||
string='切削刃长(mm)')
|
||||
# specification_id.cutting_blade_length
|
||||
# cutting_tool_blade_tip_circular_arc_radius = fields.Char('刀尖圆弧半径(mm)', size=20)
|
||||
cutting_tool_blade_tip_circular_arc_radius = fields.Char(related='specification_id.blade_tip_circular_arc_radius',
|
||||
string='刀尖圆弧半径(mm)')
|
||||
# specification_id.blade_tip_circular_arc_radius
|
||||
# cutting_tool_top_angle = fields.Integer('顶角(°)')
|
||||
cutting_tool_top_angle = fields.Integer(related='specification_id.top_angle',
|
||||
string='顶角(°)')
|
||||
# specification_id.top_angle
|
||||
# cutting_tool_inscribed_circle_diameter = fields.Float('内接圆直径(mm)')
|
||||
cutting_tool_inscribed_circle_diameter = fields.Float(related='specification_id.inscribed_circle_diameter',
|
||||
string='内接圆直径(mm)')
|
||||
# specification_id.inscribed_circle_diameter
|
||||
# cutting_tool_install_aperture_diameter = fields.Float('安装孔直径(mm)')
|
||||
cutting_tool_install_aperture_diameter = fields.Float(related='specification_id.install_aperture_diameter',
|
||||
string='安装孔直径(mm)')
|
||||
# specification_id.install_aperture_diameter
|
||||
# cutting_tool_chip_breaker_groove = fields.Selection([('无', '无'), ('单面', '单面'), ('双面', '双面')],
|
||||
# string='有无断屑槽')
|
||||
cutting_tool_chip_breaker_groove = fields.Selection(related='specification_id.chip_breaker_groove',
|
||||
string='有无断屑槽')
|
||||
# specification_id.chip_breaker_groove
|
||||
# cutting_tool_chip_breaker_type_code = fields.Char('断屑槽型代号')
|
||||
cutting_tool_chip_breaker_type_code = fields.Char(related='specification_id.chip_breaker_type_code',
|
||||
string='断屑槽型代号')
|
||||
# specification_id.chip_breaker_type_code
|
||||
# cutting_tool_bladed_teeth_model = fields.Selection(
|
||||
# [('无', '无'), ('V牙型', 'V牙型'), ('米制全牙型', '米制全牙型'), ('美制全牙型', '美制全牙型'),
|
||||
# ('惠氏全牙型', '惠氏全牙型'), ('BSPT全牙型', 'BSPT全牙型'), ('NPT全牙型', 'NPT全牙型'),
|
||||
# ('UNJ全牙型', 'UNJ全牙型'), ('DIN405圆牙型', 'DIN405圆牙型'), ('ACME梯形', 'ACME梯形'),
|
||||
# ('石油管螺纹刀片', '石油管螺纹刀片'), ('矮牙ACME梯形', '矮牙ACME梯形'),
|
||||
# ('Trapeze30° 103', 'Trapeze30° 103')], string='刀片牙型', default='无')
|
||||
cutting_tool_bladed_teeth_model = fields.Selection(related='specification_id.blade_teeth_model',
|
||||
string='断屑槽型代号')
|
||||
# specification_id.blade_teeth_model
|
||||
# cutting_tool_blade_blade_number = fields.Selection(
|
||||
# [('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'),
|
||||
# ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10')],
|
||||
# string='刀片的刃数(个)')
|
||||
cutting_tool_blade_blade_number = fields.Selection(related='specification_id.blade_blade_number',
|
||||
string='刀片的刃数(个)')
|
||||
# specification_id.blade_blade_number
|
||||
|
||||
cutting_tool_thread_model = fields.Selection([('无', '无'), ('外螺纹', '外螺纹'), ('内螺纹', '内螺纹')],
|
||||
string='螺纹类型')
|
||||
cutting_tool_thread_num = fields.Float('每英寸螺纹数(tpi)')
|
||||
cutting_tool_blade_tip_height_tolerance = fields.Char('刀尖高度公差(mm)', size=20)
|
||||
cutting_tool_inscribed_circle_tolerance = fields.Char('内接圆公差(mm)', size=20)
|
||||
cutting_tool_thickness_tolerance = fields.Char('厚度公差(mm)', size=20)
|
||||
|
||||
cutting_tool_jump_accuracy = fields.Char('径跳精度(mm)')
|
||||
# cutting_tool_thread_model = fields.Selection([('无', '无'), ('外螺纹', '外螺纹'), ('内螺纹', '内螺纹')],
|
||||
# string='螺纹类型')
|
||||
cutting_tool_thread_model = fields.Selection(related='specification_id.thread_model',
|
||||
string='螺纹类型')
|
||||
# specification_id.thread_model
|
||||
# cutting_tool_thread_num = fields.Float('每英寸螺纹数(tpi)')
|
||||
cutting_tool_thread_num = fields.Float(related='specification_id.thread_num',
|
||||
string='每英寸螺纹数(tpi)')
|
||||
# specification_id.thread_num
|
||||
# cutting_tool_blade_tip_height_tolerance = fields.Char('刀尖高度公差(mm)', size=20)
|
||||
cutting_tool_blade_tip_height_tolerance = fields.Char(related='specification_id.blade_tip_height_tolerance',
|
||||
string='刀尖高度公差(mm)')
|
||||
# specification_id.blade_tip_height_tolerance
|
||||
# cutting_tool_inscribed_circle_tolerance = fields.Char('内接圆公差(mm)', size=20)
|
||||
cutting_tool_inscribed_circle_tolerance = fields.Char(related='specification_id.inscribed_circle_tolerance',
|
||||
string='内接圆公差(mm)')
|
||||
# specification_id.inscribed_circle_tolerance
|
||||
# cutting_tool_thickness_tolerance = fields.Char('厚度公差(mm)', size=20)
|
||||
cutting_tool_thickness_tolerance = fields.Char(related='specification_id.thickness_tolerance',
|
||||
string='厚度公差(mm)')
|
||||
# specification_id.thickness_tolerance
|
||||
# cutting_tool_jump_accuracy = fields.Char('径跳精度(mm)')
|
||||
cutting_tool_jump_accuracy = fields.Char(related='specification_id.run_out_accuracy',
|
||||
string='径跳精度(mm)')
|
||||
# specification_id.run_out_accuracy
|
||||
cutting_tool_working_hardness = fields.Integer('加工硬度(hrc)')
|
||||
cutting_tool_cutter_bar_ids = fields.Many2many(
|
||||
'sf.cutting_tool.standard.library',
|
||||
@@ -483,55 +600,153 @@ class ResProductMo(models.Model):
|
||||
string='适用刀盘型号' # 使用空列表作为默认值
|
||||
)
|
||||
# 刀杆/参数
|
||||
cutting_tool_knife_head_height = fields.Float('刀头高度(mm)')
|
||||
cutting_tool_knife_head_width = fields.Float('刀头宽度(mm)')
|
||||
cutting_tool_knife_head_length = fields.Float('刀头长度(mm)')
|
||||
cutting_tool_blade_diameter = fields.Float('刃径/刃部直径(mm)')
|
||||
cutting_tool_cutter_arbor_diameter = fields.Float('刀杆直径(mm)')
|
||||
cutting_tool_min_machining_aperture = fields.Integer('最小加工孔径(mm)')
|
||||
cutting_tool_install_blade_tip_num = fields.Integer('可装刀片数/齿数(个)')
|
||||
cutting_tool_installing_structure = fields.Char('安装结构', size=20)
|
||||
# cutting_tool_knife_head_height = fields.Float('刀头高度(mm)')
|
||||
cutting_tool_knife_head_height = fields.Float(related='specification_id.knife_head_height',
|
||||
string='刀头高度(mm)')
|
||||
# specification_id.knife_head_height
|
||||
# cutting_tool_knife_head_width = fields.Float('刀头宽度(mm)')
|
||||
cutting_tool_knife_head_width = fields.Float(related='specification_id.knife_head_width',
|
||||
string='刀头宽度(mm)')
|
||||
# cutting_tool_knife_head_length = fields.Float('刀头长度(mm)')
|
||||
cutting_tool_knife_head_length = fields.Float(related='specification_id.knife_head_length',
|
||||
string='刀头长度(mm)')
|
||||
# cutting_tool_blade_diameter = fields.Float('刃径/刃部直径(mm)')
|
||||
cutting_tool_blade_diameter = fields.Float(related='specification_id.blade_diameter',
|
||||
string='刃径/刃部直径(mm)')
|
||||
# specification_id.blade_diameter
|
||||
# cutting_tool_cutter_arbor_diameter = fields.Float('刀杆直径(mm)')
|
||||
cutting_tool_cutter_arbor_diameter = fields.Float(related='specification_id.cutter_arbor_diameter',
|
||||
string='刀杆直径(mm)')
|
||||
# specification_id.cutter_arbor_diameter
|
||||
# cutting_tool_min_machining_aperture = fields.Integer('最小加工孔径(mm)')
|
||||
cutting_tool_min_machining_aperture = fields.Integer(related='specification_id.min_machining_aperture',
|
||||
string='最小加工孔径(mm)')
|
||||
# specification_id.min_machining_aperture
|
||||
# cutting_tool_install_blade_tip_num = fields.Integer('可装刀片数/齿数(个)')
|
||||
cutting_tool_install_blade_tip_num = fields.Integer(related='specification_id.install_blade_tip_num',
|
||||
string='可装刀片数/齿数(个)')
|
||||
# specification_id.install_blade_tip_num
|
||||
# cutting_tool_installing_structure = fields.Char('安装结构', size=20)
|
||||
cutting_tool_installing_structure = fields.Char(related='specification_id.installing_structure',
|
||||
string='安装结构')
|
||||
# specification_id.installing_structure
|
||||
cutting_tool_blade_id = fields.Many2one(
|
||||
'sf.cutting_tool.standard.library',
|
||||
domain="[('cutting_tool_type', '=', '刀片')]",
|
||||
string='适用刀片型号' # 使用空列表作为默认值
|
||||
)
|
||||
cutting_tool_tool_shim = fields.Char('适配刀垫型号', size=50)
|
||||
cutting_tool_cotter_pin = fields.Char('适配销钉型号', size=50)
|
||||
cutting_tool_pressing_plate = fields.Char('适配压板型号', size=50)
|
||||
cutting_tool_screw = fields.Char('适配螺钉型号', size=50)
|
||||
cutting_tool_wrench = fields.Char('适配扳手型号')
|
||||
cutting_tool_is_cooling_hole = fields.Boolean('有无冷却孔', default=False)
|
||||
cutting_tool_locating_slot_code = fields.Char('定位槽代号', size=20)
|
||||
# cutting_tool_blade_id = fields.Many2one(related='specification_id.blade_id',
|
||||
# domain="[('cutting_tool_type', '=', '刀片')]",
|
||||
# string='适用刀片型号')
|
||||
# specification_id.blade_id
|
||||
# cutting_tool_tool_shim = fields.Char('适配刀垫型号', size=50)
|
||||
cutting_tool_tool_shim = fields.Char(related='specification_id.tool_shim',
|
||||
string='适配刀垫型号')
|
||||
# specification_id.tool_shim
|
||||
# cutting_tool_cotter_pin = fields.Char('适配销钉型号', size=50)
|
||||
cutting_tool_cotter_pin = fields.Char(related='specification_id.cotter_pin',
|
||||
string='适配销钉型号')
|
||||
# cutting_tool_pressing_plate = fields.Char('适配压板型号', size=50)
|
||||
cutting_tool_pressing_plate = fields.Char(related='specification_id.pressing_plate',
|
||||
string='适配压板型号')
|
||||
# cutting_tool_screw = fields.Char('适配螺钉型号', size=50)
|
||||
cutting_tool_screw = fields.Char(related='specification_id.screw',
|
||||
string='适配螺钉型号')
|
||||
# specification_id.screw
|
||||
# cutting_tool_wrench = fields.Char('适配扳手型号')
|
||||
cutting_tool_wrench = fields.Char(related='specification_id.spanner',
|
||||
string='适配扳手型号')
|
||||
# specification_id.spanner
|
||||
# cutting_tool_is_cooling_hole = fields.Boolean('有无冷却孔', default=False)
|
||||
cutting_tool_is_cooling_hole = fields.Boolean(related='specification_id.is_cooling_hole',
|
||||
string='有无冷却孔')
|
||||
# specification_id.is_cooling_hole
|
||||
# cutting_tool_locating_slot_code = fields.Char('定位槽代号', size=20)
|
||||
cutting_tool_locating_slot_code = fields.Char(related='specification_id.locating_slot_code',
|
||||
string='定位槽代号')
|
||||
# specification_id.locating_slot_code
|
||||
# 刀盘参数
|
||||
cutting_tool_cutter_head_diameter = fields.Float('刀盘直径(mm)')
|
||||
cutting_tool_interface_diameter = fields.Float('接口直径(mm)')
|
||||
|
||||
# cutting_tool_cutter_head_diameter = fields.Float('刀盘直径(mm)')
|
||||
cutting_tool_cutter_head_diameter = fields.Float(related='specification_id.cutter_head_diameter',
|
||||
string='刀盘直径(mm)')
|
||||
# specification_id.cutter_head_diameter
|
||||
# cutting_tool_interface_diameter = fields.Float('接口直径(mm)')
|
||||
cutting_tool_interface_diameter = fields.Float(related='specification_id.interface_diameter',
|
||||
string='接口直径(mm)')
|
||||
# specification_id.interface_diameter
|
||||
# 刀柄参数
|
||||
cutting_tool_clamping_diameter_max = fields.Float('最大夹持直径')
|
||||
cutting_tool_clamping_diameter_min = fields.Float('最小夹持直径')
|
||||
cutting_tool_flange_length = fields.Float('法兰柄长(mm)')
|
||||
cutting_tool_flange_diameter = fields.Float('法兰直径(mm)')
|
||||
cutting_tool_is_safety_lock = fields.Boolean('有无安全锁', default=False)
|
||||
cutting_tool_is_high_speed_cutting = fields.Boolean('可高速切削', default=False)
|
||||
cutting_tool_change_time = fields.Integer('换刀时间(s)')
|
||||
cutting_tool_clamping_way = fields.Char('夹持方式')
|
||||
cutting_tool_fit_chuck_size = fields.Char('适配夹头尺寸')
|
||||
cutting_tool_taper_shank_model = fields.Char('锥柄型号')
|
||||
# cutting_tool_clamping_diameter_max = fields.Float('最大夹持直径')
|
||||
cutting_tool_clamping_diameter_max = fields.Float(related='specification_id.max_clamping_diameter',
|
||||
string='最大夹持直径')
|
||||
# specification_id.max_clamping_diameter
|
||||
# cutting_tool_clamping_diameter_min = fields.Float('最小夹持直径')
|
||||
cutting_tool_clamping_diameter_min = fields.Float(related='specification_id.min_clamping_diameter',
|
||||
string='最小夹持直径')
|
||||
# specification_id.min_clamping_diameter
|
||||
# cutting_tool_flange_length = fields.Float('法兰柄长(mm)')
|
||||
cutting_tool_flange_length = fields.Float(related='specification_id.flange_shank_length',
|
||||
string='法兰柄长(mm)')
|
||||
# cutting_tool_flange_diameter = fields.Float('法兰直径(mm)')
|
||||
cutting_tool_flange_diameter = fields.Float(related='specification_id.flange_diameter',
|
||||
string='法兰直径(mm)')
|
||||
# cutting_tool_is_safety_lock = fields.Boolean('有无安全锁', default=False)
|
||||
cutting_tool_is_safety_lock = fields.Boolean(related='specification_id.is_safe_lock',
|
||||
string='有无安全锁')
|
||||
# cutting_tool_is_high_speed_cutting = fields.Boolean('可高速切削', default=False)
|
||||
cutting_tool_is_high_speed_cutting = fields.Boolean(related='specification_id.is_quick_cutting',
|
||||
string='可高速切削')
|
||||
# cutting_tool_change_time = fields.Integer('换刀时间(s)')
|
||||
cutting_tool_change_time = fields.Integer(related='specification_id.tool_changing_time',
|
||||
string='换刀时间(s)')
|
||||
# cutting_tool_clamping_way = fields.Char('夹持方式')
|
||||
cutting_tool_clamping_way = fields.Char(related='specification_id.clamping_mode',
|
||||
string='夹持方式')
|
||||
# cutting_tool_fit_chuck_size = fields.Char('适配夹头尺寸')
|
||||
cutting_tool_fit_chuck_size = fields.Char(related='specification_id.fit_chuck_size',
|
||||
string='适配夹头尺寸')
|
||||
# cutting_tool_taper_shank_model = fields.Char('锥柄型号')
|
||||
cutting_tool_taper_shank_model = fields.Char(related='specification_id.taper_shank_model',
|
||||
string='锥柄型号')
|
||||
cutting_tool_standard_speed = fields.Integer('标准转速(n/min)')
|
||||
cutting_tool_speed_max = fields.Integer('最大转速(n/min)')
|
||||
# cutting_tool_speed_max = fields.Integer('最大转速(n/min)')
|
||||
cutting_tool_speed_max = fields.Integer(related='specification_id.max_rotate_speed',
|
||||
string='最大转速(n/min)')
|
||||
# specification_id.max_rotate_speed
|
||||
cutting_tool_cooling_type = fields.Char('冷却类型')
|
||||
cutting_tool_dynamic_balance_class = fields.Char('动平衡等级')
|
||||
cutting_tool_fit_nut_model = fields.Char('适用锁紧螺母型号')
|
||||
|
||||
# cutting_tool_fit_nut_model = fields.Char('适用锁紧螺母型号')
|
||||
cutting_tool_fit_nut_model = fields.Char(related='specification_id.nut',
|
||||
string='适用锁紧螺母型号')
|
||||
# specification_id.nut
|
||||
# 夹头参数
|
||||
cutting_tool_taper = fields.Integer('锥度(°)')
|
||||
cutting_tool_top_diameter = fields.Float('顶部直径')
|
||||
cutting_tool_outer_diameter = fields.Float('外径(mm)')
|
||||
cutting_tool_inner_diameter = fields.Float('内径(mm)')
|
||||
cooling_suit_type_ids = fields.Char('适用冷却套型号')
|
||||
cutting_tool_max_load_capacity = fields.Float('最大负载能力(kg)')
|
||||
cutting_tool_er_size_model = fields.Char('尺寸型号')
|
||||
# cutting_tool_taper = fields.Integer('锥度(°)')
|
||||
cutting_tool_taper = fields.Integer(related='specification_id.taper',
|
||||
string='锥度(°)')
|
||||
# specification_id.taper
|
||||
# cutting_tool_top_diameter = fields.Float('顶部直径')
|
||||
cutting_tool_top_diameter = fields.Float(related='specification_id.top_diameter',
|
||||
string='顶部直径')
|
||||
# specification_id.top_diameter
|
||||
# cutting_tool_outer_diameter = fields.Float('外径(mm)')
|
||||
# specification_id.outer_diameter
|
||||
cutting_tool_outer_diameter = fields.Float(related='specification_id.outer_diameter',
|
||||
string='外径(mm)')
|
||||
# cutting_tool_inner_diameter = fields.Float('内径(mm)')
|
||||
cutting_tool_inner_diameter = fields.Float(related='specification_id.inner_diameter',
|
||||
string='内径(mm)')
|
||||
# specification_id.inner_diameter
|
||||
# cooling_suit_type_ids = fields.Char('适用冷却套型号')
|
||||
cooling_suit_type_ids = fields.Char(related='specification_id.cooling_jacket',
|
||||
string='适用冷却套型号')
|
||||
# specification_id.cooling_jacket
|
||||
# cutting_tool_max_load_capacity = fields.Float('最大负载能力(kg)')
|
||||
cutting_tool_max_load_capacity = fields.Float(related='specification_id.max_load_capacity',
|
||||
string='最大负载能力(kg)')
|
||||
# specification_id.max_load_capacity
|
||||
# cutting_tool_er_size_model = fields.Char('尺寸型号')
|
||||
cutting_tool_er_size_model = fields.Char(related='specification_id.er_size_model',
|
||||
string='尺寸型号')
|
||||
# specification_id.er_size_model
|
||||
# cutting_tool_handle_ids = fields.Many2many(
|
||||
# 'sf.cutting_tool.standard.library',
|
||||
# relation='product_cutting_tool_library_chuck_handle_rel',
|
||||
@@ -546,6 +761,10 @@ class ResProductMo(models.Model):
|
||||
domain="[('cutting_tool_type', '=', '刀柄')]",
|
||||
string='适用刀柄型号'
|
||||
)
|
||||
# cutting_tool_handle_id = fields.Many2one(related='cutting_tool_model_id.handle_id',
|
||||
# domain="[('cutting_tool_type', '=', '刀柄')]",
|
||||
# string='适用刀柄型号')
|
||||
# cutting_tool_model_id.handle_id
|
||||
|
||||
# 注册状态
|
||||
register_state = fields.Selection([('未注册', '未注册'), ('已注册', '已注册'), ('注册失败', '注册失败')],
|
||||
|
||||
@@ -4,19 +4,39 @@ from odoo import models, fields, api
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
agv_rcs_url = fields.Char(string='avg_rcs访问地址',
|
||||
default='http://172.16.10.114:8182/rcms/services/rest/hikRpcService/genAgvSchedulingTask')
|
||||
wbcode = fields.Char('地码')
|
||||
agv_code = fields.Char(string='agv编号')
|
||||
task_type_no = fields.Char('任务单类型编号')
|
||||
|
||||
is_agv_task_dispatch = fields.Boolean('是否下发AGV任务', default=False)
|
||||
# 是否重新获取检测文件
|
||||
is_get_detection_file = fields.Boolean(string='重新获取检测文件', default=False)
|
||||
|
||||
@api.model
|
||||
def get_values(self):
|
||||
values = super(ResConfigSettings, self).get_values()
|
||||
config = self.env['ir.config_parameter'].sudo()
|
||||
agv_rcs_url = config.get_param('agv_rcs_url', default='')
|
||||
wbcode = config.get_param('wbcode', default='')
|
||||
agv_code = config.get_param('agv_code', default='')
|
||||
is_agv_task_dispatch = config.get_param('is_agv_task_dispatch')
|
||||
is_get_detection_file = config.get_param('is_get_detection_file')
|
||||
values.update(
|
||||
agv_rcs_url=agv_rcs_url,
|
||||
wbcode=wbcode,
|
||||
agv_code=agv_code,
|
||||
is_agv_task_dispatch=is_agv_task_dispatch,
|
||||
is_get_detection_file=is_get_detection_file
|
||||
)
|
||||
return values
|
||||
|
||||
def set_values(self):
|
||||
super(ResConfigSettings, self).set_values()
|
||||
config = self.env['ir.config_parameter'].sudo()
|
||||
config.set_param("agv_rcs_url", self.agv_rcs_url or "")
|
||||
config.set_param("wbcode", self.wbcode or "")
|
||||
config.set_param("agv_code", self.agv_code or "")
|
||||
config.set_param("is_agv_task_dispatch", self.is_agv_task_dispatch or False)
|
||||
config.set_param("is_get_detection_file", self.is_get_detection_file or False)
|
||||
|
||||
@@ -269,8 +269,9 @@ class StockRule(models.Model):
|
||||
sale_order = self.env['sale.order'].sudo().search([('name', '=', production.origin)])
|
||||
# 根据销售订单号查询快速订单
|
||||
quick_easy_order = self.env['quick.easy.order'].sudo().search([('sale_order_id', '=', sale_order.id)])
|
||||
production.write({'part_number': quick_easy_order.part_drawing_number,
|
||||
'part_drawing': quick_easy_order.machining_drawings})
|
||||
if quick_easy_order:
|
||||
production.write({'part_number': quick_easy_order.part_drawing_number,
|
||||
'part_drawing': quick_easy_order.machining_drawings})
|
||||
if sale_order:
|
||||
# sale_order.write({'schedule_status': 'to schedule'})
|
||||
self.env['sf.production.plan'].sudo().with_company(company_id).create({
|
||||
|
||||
@@ -296,6 +296,10 @@
|
||||
</attribute>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//sheet//notebook//page[@name='operations']//field[@name='workorder_ids']" position="replace">
|
||||
<field name="workorder_ids" attrs="{'readonly': ['|', ('state', '!=', 'test_value'), '&', ('state', '=', 'done'), ('is_locked', '=', True)]}" context="{'tree_view_ref': 'mrp.mrp_production_workorder_tree_editable_view', 'default_product_uom_id': product_uom_id, 'from_manufacturing_order': True}"/>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//sheet//notebook//page[@name='operations']" position="after">
|
||||
<page string="检测结果" attrs="{'invisible': [('detection_result_ids', '=', [])]}">
|
||||
<field name="detection_result_ids" string="" readonly="0">
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
</field>
|
||||
<xpath expr="//field[@name='qty_remaining']" position="after">
|
||||
<field name="manual_quotation" optional="show"/>
|
||||
<field name='tag_type' widget="badge"
|
||||
decoration-danger="tag_type == '重新加工'"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='date_planned_start']" position="replace">
|
||||
<field name="date_planned_start" string="计划开始日期" optional="show"/>
|
||||
@@ -43,11 +45,11 @@
|
||||
<field name="date_planned_finished" string="计划结束日期" optional="hide"/>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='button_start']" position="attributes">
|
||||
<!-- <attribute name="attrs">{'invisible': ['|', '|', '|','|','|', ('production_state','in', ('draft',-->
|
||||
<!-- 'done',-->
|
||||
<!-- 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),-->
|
||||
<!-- ('is_user_working', '!=', False),("user_permissions","=",False),("name","=","CNC加工")]}-->
|
||||
<!-- </attribute>-->
|
||||
<!-- <attribute name="attrs">{'invisible': ['|', '|', '|','|','|', ('production_state','in', ('draft',-->
|
||||
<!-- 'done',-->
|
||||
<!-- 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),-->
|
||||
<!-- ('is_user_working', '!=', False),("user_permissions","=",False),("name","=","CNC加工")]}-->
|
||||
<!-- </attribute>-->
|
||||
<attribute name="attrs">{'invisible': ['|', '|', '|','|','|', ('production_state','in', ('draft',
|
||||
'done',
|
||||
'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),
|
||||
@@ -165,8 +167,8 @@
|
||||
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'not in', ('pending_processing', 'pending_cam', 'pending_era_cam')), ('state','!=','progress'), ('routing_type', 'not in', ('装夹预调', 'CNC加工', '解除装夹'))]}" -->
|
||||
<!-- groups="sf_base.group_sf_mrp_user" confirm="是否确认完工"/> -->
|
||||
|
||||
<!-- <button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"-->
|
||||
<!-- attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>-->
|
||||
<!-- <button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"-->
|
||||
<!-- attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>-->
|
||||
<button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"
|
||||
attrs="{'invisible': ['|', '|', '|', '|', '|', ('routing_type', '=', '装夹预调'), ('routing_type', '=', '解除装夹'), ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>
|
||||
<button name="button_start" type="object" string="开始" class="btn-success"
|
||||
@@ -191,8 +193,8 @@
|
||||
<!-- context="{'default_workcenter_id': workcenter_id}" class="btn-danger" -->
|
||||
<!-- groups="sf_base.group_sf_mrp_user" -->
|
||||
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked'),('state','=','done')]}"/> -->
|
||||
<!-- <button name="button_workpiece_delivery" type="object" string="工件配送" class="btn-primary"-->
|
||||
<!-- attrs="{'invisible': ['|','|','|','|',('routing_type','!=','装夹预调'),('is_delivery','=',True),('state','!=','done'),('is_rework','=',True),'&',('rfid_code','in',['',False]),('state','=','done')]}"/>-->
|
||||
<!-- <button name="button_workpiece_delivery" type="object" string="工件配送" class="btn-primary"-->
|
||||
<!-- attrs="{'invisible': ['|','|','|','|',('routing_type','!=','装夹预调'),('is_delivery','=',True),('state','!=','done'),('is_rework','=',True),'&',('rfid_code','in',['',False]),('state','=','done')]}"/>-->
|
||||
<button name="button_rework_pre" type="object" string="返工"
|
||||
class="btn-primary"
|
||||
attrs="{'invisible': ['|','|',('routing_type','!=','装夹预调'),('state','!=','progress'),('is_rework','=',True)]}"/>
|
||||
@@ -221,9 +223,12 @@
|
||||
<xpath expr="//label[1]" position="before">
|
||||
<field name='routing_type' readonly="1"/>
|
||||
<field name='process_state' attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/>
|
||||
<field name='tag_type' readonly="1" attrs='{"invisible": [("tag_type","=",False)]}'
|
||||
decoration-danger="tag_type == '重新加工'"/>
|
||||
<field name="rfid_code" force_save="1" readonly="1" cache="True"
|
||||
attrs="{'invisible': [('rfid_code_old', '!=', False)]}"/>
|
||||
<field name="rfid_code_old" readonly="1" attrs="{'invisible': [('rfid_code_old', '=', False)]}"/>
|
||||
|
||||
</xpath>
|
||||
<xpath expr="//label[1]" position="attributes">
|
||||
<attribute name="string">计划加工时间</attribute>
|
||||
@@ -479,10 +484,10 @@
|
||||
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<field name="data_state" invisible="1"/>
|
||||
<button type="object" class="oe_highlight" name="get_three_check_datas" string="获取数据"
|
||||
attrs='{"invisible": ["|", "|", "|", ("material_center_point","!=",False),("state","!=","progress"),("user_permissions","=",False), ("data_state", "=", True)]}'/>
|
||||
<button type="object" class="oe_highlight" name="getcenter" string="计算定位"
|
||||
attrs='{"invisible": ["|","|", "|",("material_center_point","!=",False),("state","!=","progress"),("user_permissions","=",False), ("data_state", "=", False)]}'/>
|
||||
<!-- <button type="object" class="oe_highlight" name="get_three_check_datas" string="获取数据" -->
|
||||
<!-- attrs='{"invisible": ["|", "|", "|", ("material_center_point","!=",False),("state","!=","progress"),("user_permissions","=",False), ("data_state", "=", True)]}'/> -->
|
||||
<!-- <button type="object" class="oe_highlight" name="getcenter" string="计算定位" -->
|
||||
<!-- attrs='{"invisible": ["|","|", "|",("material_center_point","!=",False),("state","!=","progress"),("user_permissions","=",False), ("data_state", "=", False)]}'/> -->
|
||||
</div>
|
||||
|
||||
<group>
|
||||
@@ -514,8 +519,8 @@
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//form//header" position="inside">
|
||||
<button type="object" class="oe_highlight" name="get_three_check_datas" string="获取数据"
|
||||
attrs='{"invisible": [("state","!=","progress")]}'/>
|
||||
<button type="object" class="oe_highlight jikimo_button_confirm" name="get_three_check_datas"
|
||||
string="获取数据" attrs='{"invisible": ["|", ("state","!=","progress"), ("routing_type","!=","装夹预调")]}'/>
|
||||
</xpath>
|
||||
|
||||
|
||||
@@ -666,7 +671,8 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree string="工件配送" class="center" create="0" delete="0" js_class="remove_focus_list_view">
|
||||
<header>
|
||||
<button name="button_delivery" type="object" string="工件配送" class="btn-primary jikimo_button_confirm" attrs="{'force_show':1}"/>
|
||||
<button name="button_delivery" type="object" string="工件配送"
|
||||
class="btn-primary jikimo_button_confirm" attrs="{'force_show':1}"/>
|
||||
</header>
|
||||
<field name="status" widget="badge"
|
||||
decoration-success="status == '已配送'"
|
||||
@@ -678,15 +684,15 @@
|
||||
<field name="production_id"/>
|
||||
<field name="type" readonly="1"/>
|
||||
<field name="production_line_id" options="{'no_create': True}" readonly="1"/>
|
||||
<!-- <field name="route_id" options="{'no_create': True}"-->
|
||||
<!-- domain="[('route_type','in',['上产线','下产线'])]"/>-->
|
||||
<!-- <field name="route_id" options="{'no_create': True}"-->
|
||||
<!-- domain="[('route_type','in',['上产线','下产线'])]"/>-->
|
||||
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
|
||||
<!-- <field name="feeder_station_destination_id" readonly="1" force_save="1"/>-->
|
||||
<!-- <field name="feeder_station_destination_id" readonly="1" force_save="1"/>-->
|
||||
<field name="is_cnc_program_down" readonly="1"/>
|
||||
<!-- <field name="rfid_code"/>-->
|
||||
<!-- <field name="task_delivery_time" readonly="1"/>-->
|
||||
<!-- <field name="task_completion_time" readonly="1"/>-->
|
||||
<!-- <field name="delivery_duration" widget="float_time"/>-->
|
||||
<!-- <field name="task_delivery_time" readonly="1"/>-->
|
||||
<!-- <field name="task_completion_time" readonly="1"/>-->
|
||||
<!-- <field name="delivery_duration" widget="float_time"/>-->
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -791,7 +797,7 @@
|
||||
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
|
||||
<!-- <field name="type" readonly="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"
|
||||
string="历史"/>
|
||||
</tree>
|
||||
@@ -853,10 +859,10 @@
|
||||
<field name="domain">[('type','in',['运送空料架']),('name','not ilike','WDO')]</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="mrp.menu_mrp_manufacturing"
|
||||
name="Operations"
|
||||
sequence="10"
|
||||
parent="mrp.menu_mrp_root"
|
||||
groups="sf_base.group_sf_order_user,sf_base.group_sf_mrp_manager,sf_base.group_sf_equipment_user"/>
|
||||
<menuitem id="mrp.menu_mrp_manufacturing"
|
||||
name="Operations"
|
||||
sequence="10"
|
||||
parent="mrp.menu_mrp_root"
|
||||
groups="sf_base.group_sf_order_user,sf_base.group_sf_mrp_manager,sf_base.group_sf_equipment_user"/>
|
||||
</odoo>
|
||||
|
||||
|
||||
@@ -6,6 +6,32 @@
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="base_setup.res_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[hasclass('app_settings_block')]/div" position="before">
|
||||
<div>
|
||||
<h2>AGV参数配置</h2>
|
||||
<div class="row mt16 o_settings_container" id="agv_config">
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<div class="o_setting_left_pane"/>
|
||||
<div class="o_setting_right_pane">
|
||||
<div class="text-muted">
|
||||
<label for="agv_rcs_url" string="访问地址"/>
|
||||
<field name="agv_rcs_url"/>
|
||||
</div>
|
||||
<div class="text-muted">
|
||||
<label for="agv_code" string="车辆编号"/>
|
||||
<field name="agv_code"/>
|
||||
</div>
|
||||
<div class="text-muted">
|
||||
<label for="wbcode"/>
|
||||
<field name="wbcode"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
|
||||
|
||||
<xpath expr="//div[@id='agv_config']/div" position="after">
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<group col="1">
|
||||
<field name="production_ids" readonly="1" widget="many2many_tags" string="制造订单号"/>
|
||||
<field name="delivery_type" readonly="1"/>
|
||||
<field name="feeder_station_start_id" options="{'no_create': True}" required="1"/>
|
||||
<field name="feeder_station_start_id" string="当前接驳站" options="{'no_create': True}" required="1"/>
|
||||
<field name="workcenter_id" options="{'no_create': True}"/>
|
||||
</group>
|
||||
<footer>
|
||||
|
||||
@@ -179,18 +179,15 @@ class WorkpieceDeliveryWizard(models.TransientModel):
|
||||
self.feeder_station_destination_id = self.route_id.end_site_id.id
|
||||
|
||||
def on_barcode_scanned(self, barcode):
|
||||
delivery_type = self.env.context.get('default_delivery_type')
|
||||
# 判断barcode是否是数字
|
||||
if not barcode.isdigit():
|
||||
# 判断是否是AGV接驳站名称
|
||||
agv_site = self.env['sf.agv.site'].search([('name', '=', barcode)])
|
||||
if agv_site:
|
||||
if not self.feeder_station_start_id:
|
||||
self.feeder_station_start_id = agv_site.id
|
||||
else:
|
||||
if self.feeder_station_start_id.id != agv_site.id:
|
||||
raise UserError('起点接驳站不匹配!')
|
||||
self.feeder_station_start_id = agv_site.id # 修正:移除 .id
|
||||
return
|
||||
delivery_type = self.env.context.get('default_delivery_type')
|
||||
|
||||
if delivery_type == '上产线':
|
||||
workorder = self.env['mrp.workorder'].search(
|
||||
[('production_line_state', '=', '待上产线'), ('rfid_code', '=', barcode),
|
||||
@@ -210,11 +207,14 @@ class WorkpieceDeliveryWizard(models.TransientModel):
|
||||
workorder.production_line_id.id != self.production_ids[0].production_line_id.id):
|
||||
raise UserError(f'该rfid对应的制造订单号为{workorder.production_id.name}的目的生产线不一致')
|
||||
|
||||
# 调用打印成品条码方法
|
||||
workorder.print_method()
|
||||
|
||||
# 将对象添加到对应的同模型且是多对多类型里
|
||||
self.production_ids |= workorder.production_id
|
||||
self.workorder_ids |= workorder
|
||||
|
||||
down_product_agv_scheduling = self.get_down_product_agv_scheduling()
|
||||
down_product_agv_scheduling = workorder.get_down_product_agv_scheduling()
|
||||
if down_product_agv_scheduling:
|
||||
if not self.feeder_station_start_id:
|
||||
self.feeder_station_start_id = down_product_agv_scheduling.end_site_id.id
|
||||
@@ -226,4 +226,11 @@ class WorkpieceDeliveryWizard(models.TransientModel):
|
||||
raise UserError('该rfid码对应的工单不存在')
|
||||
return
|
||||
|
||||
@api.onchange('feeder_station_start_id')
|
||||
def on_start_id_change(self):
|
||||
if self.delivery_type == '运送空料架' and len(self.workorder_ids) > 0:
|
||||
down_product_agv_scheduling = self.workorder_ids[0].get_down_product_agv_scheduling()
|
||||
if down_product_agv_scheduling and self.feeder_station_start_id \
|
||||
and down_product_agv_scheduling.end_site_id.id != self.feeder_station_start_id.id:
|
||||
raise UserError('当前接驳站不匹配!')
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
from . import models
|
||||
from . import models
|
||||
from . import controllers
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
""",
|
||||
'category': 'sf',
|
||||
'website': 'https://www.sf.jikimo.com',
|
||||
'depends': ['base', 'sf_base'],
|
||||
'depends': ['sale', 'purchase', 'sf_plan', 'jikimo_message_notify', 'stock'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'views/sf_message_template_view.xml',
|
||||
'data/bussiness_node.xml',
|
||||
# 'data/cron_data.xml',
|
||||
'data/template_data.xml',
|
||||
|
||||
],
|
||||
'test': [
|
||||
],
|
||||
|
||||
1
sf_message/controllers/__init__.py
Normal file
1
sf_message/controllers/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import main
|
||||
40
sf_message/controllers/main.py
Normal file
40
sf_message/controllers/main.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import json
|
||||
import requests
|
||||
import logging
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
from odoo.addons.sf_mrs_connect.controllers.controllers import Sf_Mrs_Connect
|
||||
from odoo.addons.sf_base.commons.common import Common
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class MessageSfMrsConnect(Sf_Mrs_Connect):
|
||||
|
||||
@http.route('/api/cnc_processing/create', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
||||
cors="*")
|
||||
def get_cnc_processing_create(self, **kw):
|
||||
res = super(MessageSfMrsConnect, self).get_cnc_processing_create(**kw)
|
||||
res = json.loads(res)
|
||||
if res.get('production_ids'):
|
||||
try:
|
||||
_logger.info('已编程的制造订单:%s' % res.get('production_ids'))
|
||||
productions = request.env['mrp.production'].sudo().search([('id', 'in', res.get('production_ids'))])
|
||||
# 过滤programming_state为已编程,tool_state为2的制造订单
|
||||
tool_state_valid_productions = productions.filtered(lambda x: x.programming_state == '已编程' and x.tool_state == '2')
|
||||
if tool_state_valid_productions:
|
||||
data = {
|
||||
'name': tool_state_valid_productions[0].programming_no
|
||||
}
|
||||
# 请求cloud接口,发送微信消息推送
|
||||
configsettings = request.env['res.config.settings'].sudo().get_values()
|
||||
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
|
||||
url = '/api/message/invalid_tool_state'
|
||||
config_url = configsettings['sf_url'] + url
|
||||
data['token'] = configsettings['token']
|
||||
ret = requests.post(config_url, json=data, headers=config_header)
|
||||
ret = ret.json()
|
||||
_logger.info('无效用刀异常消息推送接口:%s' % ret)
|
||||
except Exception as e:
|
||||
_logger.info('无效用刀异常消息推送接口:%s' % e)
|
||||
return json.JSONEncoder().encode(res)
|
||||
|
||||
89
sf_message/data/bussiness_node.xml
Normal file
89
sf_message/data/bussiness_node.xml
Normal file
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" ?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!--销售订单-->
|
||||
<record id="bussiness_pending_order" model="jikimo.message.bussiness.node">
|
||||
<field name="name">待接单</field>
|
||||
<field name="model">sale.order</field>
|
||||
</record>
|
||||
|
||||
<record id="bussiness_to_be_confirm" model="jikimo.message.bussiness.node">
|
||||
<field name="name">确认接单</field>
|
||||
<field name="model">sale.order</field>
|
||||
</record>
|
||||
|
||||
<!-- <record id="bussiness_sale_order_overdue_warning" model="jikimo.message.bussiness.node">-->
|
||||
<!-- <field name="name">销售订单逾期预警</field>-->
|
||||
<!-- <field name="model">sale.order</field>-->
|
||||
<!-- </record>-->
|
||||
|
||||
<!-- <record id="bussiness_sale_order_overdue" model="jikimo.message.bussiness.node">-->
|
||||
<!-- <field name="name">销售订单已逾期</field>-->
|
||||
<!-- <field name="model">sale.order</field>-->
|
||||
<!-- </record>-->
|
||||
|
||||
<record id="transfer_inventory" model="jikimo.message.bussiness.node">
|
||||
<field name="name">调拨入库</field>
|
||||
<field name="model">stock.picking</field>
|
||||
</record>
|
||||
<record id="tool_assembly" model="jikimo.message.bussiness.node">
|
||||
<field name="name">功能刀具组装</field>
|
||||
<field name="model">sf.functional.tool.assembly</field>
|
||||
</record>
|
||||
<record id="tool_dismantle" model="jikimo.message.bussiness.node">
|
||||
<field name="name">功能刀具寿命到期</field>
|
||||
<field name="model">sf.functional.tool.dismantle</field>
|
||||
</record>
|
||||
<record id="bussiness_material_purchase_remind" model="jikimo.message.bussiness.node">
|
||||
<field name="name">坯料采购提醒</field>
|
||||
<field name="model">purchase.order</field>
|
||||
</record>
|
||||
<record id="bussiness_material_picking_remind" model="jikimo.message.bussiness.node">
|
||||
<field name="name">坯料发料提醒</field>
|
||||
<field name="model">stock.picking</field>
|
||||
</record>
|
||||
|
||||
<!--工单-->
|
||||
<record id="bussiness_mrp_workorder_remind" 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">-->
|
||||
<!-- <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_cnc_overdue_warning" model="jikimo.message.bussiness.node">-->
|
||||
<!-- <field name="name">CNC工单逾期预警</field>-->
|
||||
<!-- <field name="model">mrp.workorder</field>-->
|
||||
<!-- </record>-->
|
||||
<!-- <record id="bussiness_mrp_workorder_cnc_overdue" model="jikimo.message.bussiness.node">-->
|
||||
<!-- <field name="name">CNC工单已逾期</field>-->
|
||||
<!-- <field name="model">mrp.workorder</field>-->
|
||||
<!-- </record>-->
|
||||
|
||||
<!-- <record id="bussiness_mrp_workorder_unclamp_overdue_warning" model="jikimo.message.bussiness.node">-->
|
||||
<!-- <field name="name">解除装夹工单逾期预警</field>-->
|
||||
<!-- <field name="model">mrp.workorder</field>-->
|
||||
<!-- </record>-->
|
||||
|
||||
<!-- <record id="bussiness_mrp_workorder_unclamp_overdue" model="jikimo.message.bussiness.node">-->
|
||||
<!-- <field name="name">解除装夹工单已逾期</field>-->
|
||||
<!-- <field name="model">mrp.workorder</field>-->
|
||||
<!-- </record>-->
|
||||
|
||||
<!-- <record id="bussiness_mrp_workorder_surface_overdue_warning" model="jikimo.message.bussiness.node">-->
|
||||
<!-- <field name="name">表面工艺工单逾期预警</field>-->
|
||||
<!-- <field name="model">mrp.workorder</field>-->
|
||||
<!-- </record>-->
|
||||
|
||||
<!-- <record id="bussiness_mrp_workorder_surface_overdue" model="jikimo.message.bussiness.node">-->
|
||||
<!-- <field name="name">表面工艺工单已逾期</field>-->
|
||||
<!-- <field name="model">mrp.workorder</field>-->
|
||||
<!-- </record>-->
|
||||
</data>
|
||||
</odoo>
|
||||
160
sf_message/data/cron_data.xml
Normal file
160
sf_message/data/cron_data.xml
Normal file
@@ -0,0 +1,160 @@
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record model="ir.cron" id="ir_cron_sale_order_overdue_warning">
|
||||
<field name="name">销售订单逾期预警</field>
|
||||
<field name="model_id" ref="model_sale_order"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model._overdue_warning_func()</field>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.cron" id="ir_cron_sale_order_overdue">
|
||||
<field name="name">销售订单已逾期</field>
|
||||
<field name="model_id" ref="model_sale_order"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model._overdue_func()</field>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.cron" id="ir_cron_mrp_workorder_overdue_warning">
|
||||
<field name="name">装夹预调工单逾期预警</field>
|
||||
<field name="model_id" ref="model_mrp_workorder"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model._overdue_warning_func()</field>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.cron" id="ir_cron_mrp_workorder_overdue">
|
||||
<field name="name">工单已逾期</field>
|
||||
<field name="model_id" ref="model_mrp_workorder"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model._overdue_func()</field>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
<!-- -->
|
||||
<!-- <record model="ir.cron" id="ir_cron_mrp_workorder_overdue_warning">-->
|
||||
<!-- <field name="name">工单逾期预警</field>-->
|
||||
<!-- <field name="model_id" ref="model_mrp_workorder"/>-->
|
||||
<!-- <field name="state">code</field>-->
|
||||
<!-- <field name="code">model._overdue_warning_func()</field>-->
|
||||
<!-- <field name="interval_number">1</field>-->
|
||||
<!-- <field name="interval_type">minutes</field>-->
|
||||
<!-- <field name="numbercall">-1</field>-->
|
||||
<!-- <field name="doall" eval="False"/>-->
|
||||
<!-- <field name="user_id" ref="base.user_root"/>-->
|
||||
<!-- <field name="active" eval="True"/>-->
|
||||
<!-- </record>-->
|
||||
|
||||
<!-- <record model="ir.cron" id="ir_cron_mrp_workorder_pre_overdue">-->
|
||||
<!-- <field name="name">工单已逾期</field>-->
|
||||
<!-- <field name="model_id" ref="model_mrp_workorder"/>-->
|
||||
<!-- <field name="state">code</field>-->
|
||||
<!-- <field name="code">model._overdue_func()</field>-->
|
||||
<!-- <field name="interval_number">1</field>-->
|
||||
<!-- <field name="interval_type">minutes</field>-->
|
||||
<!-- <field name="numbercall">-1</field>-->
|
||||
<!-- <field name="doall" eval="False"/>-->
|
||||
<!-- <field name="user_id" ref="base.user_root"/>-->
|
||||
<!-- <field name="active" eval="True"/>-->
|
||||
<!-- </record>-->
|
||||
<!-- -->
|
||||
<!-- <record model="ir.cron" id="ir_cron_mrp_workorder_pre_overdue_warning">-->
|
||||
<!-- <field name="name">工单逾期预警</field>-->
|
||||
<!-- <field name="model_id" ref="model_mrp_workorder"/>-->
|
||||
<!-- <field name="state">code</field>-->
|
||||
<!-- <field name="code">model._overdue_warning_func()</field>-->
|
||||
<!-- <field name="interval_number">1</field>-->
|
||||
<!-- <field name="interval_type">minutes</field>-->
|
||||
<!-- <field name="numbercall">-1</field>-->
|
||||
<!-- <field name="doall" eval="False"/>-->
|
||||
<!-- <field name="user_id" ref="base.user_root"/>-->
|
||||
<!-- <field name="active" eval="True"/>-->
|
||||
<!-- </record>-->
|
||||
|
||||
<!-- <record model="ir.cron" id="ir_cron_mrp_workorder_pre_overdue">-->
|
||||
<!-- <field name="name">工单已逾期</field>-->
|
||||
<!-- <field name="model_id" ref="model_mrp_workorder"/>-->
|
||||
<!-- <field name="state">code</field>-->
|
||||
<!-- <field name="code">model._overdue_func()</field>-->
|
||||
<!-- <field name="interval_number">1</field>-->
|
||||
<!-- <field name="interval_type">minutes</field>-->
|
||||
<!-- <field name="numbercall">-1</field>-->
|
||||
<!-- <field name="doall" eval="False"/>-->
|
||||
<!-- <field name="user_id" ref="base.user_root"/>-->
|
||||
<!-- <field name="active" eval="True"/>-->
|
||||
<!-- </record>-->
|
||||
<!-- -->
|
||||
<!-- -->
|
||||
<!-- <record model="ir.cron" id="ir_cron_mrp_workorder_pre_overdue_warning">-->
|
||||
<!-- <field name="name">工单逾期预警</field>-->
|
||||
<!-- <field name="model_id" ref="model_mrp_workorder"/>-->
|
||||
<!-- <field name="state">code</field>-->
|
||||
<!-- <field name="code">model._overdue_warning_func()</field>-->
|
||||
<!-- <field name="interval_number">1</field>-->
|
||||
<!-- <field name="interval_type">minutes</field>-->
|
||||
<!-- <field name="numbercall">-1</field>-->
|
||||
<!-- <field name="doall" eval="False"/>-->
|
||||
<!-- <field name="user_id" ref="base.user_root"/>-->
|
||||
<!-- <field name="active" eval="True"/>-->
|
||||
<!-- </record>-->
|
||||
|
||||
<!-- <record model="ir.cron" id="ir_cron_mrp_workorder_pre_overdue">-->
|
||||
<!-- <field name="name">工单已逾期</field>-->
|
||||
<!-- <field name="model_id" ref="model_mrp_workorder"/>-->
|
||||
<!-- <field name="state">code</field>-->
|
||||
<!-- <field name="code">model._overdue_func()</field>-->
|
||||
<!-- <field name="interval_number">1</field>-->
|
||||
<!-- <field name="interval_type">minutes</field>-->
|
||||
<!-- <field name="numbercall">-1</field>-->
|
||||
<!-- <field name="doall" eval="False"/>-->
|
||||
<!-- <field name="user_id" ref="base.user_root"/>-->
|
||||
<!-- <field name="active" eval="True"/>-->
|
||||
<!-- </record>-->
|
||||
<!-- -->
|
||||
<!-- <record model="ir.cron" id="ir_cron_mrp_workorder_pre_overdue_warning">-->
|
||||
<!-- <field name="name">工单逾期预警</field>-->
|
||||
<!-- <field name="model_id" ref="model_mrp_workorder"/>-->
|
||||
<!-- <field name="state">code</field>-->
|
||||
<!-- <field name="code">model._overdue_warning_func()</field>-->
|
||||
<!-- <field name="interval_number">1</field>-->
|
||||
<!-- <field name="interval_type">minutes</field>-->
|
||||
<!-- <field name="numbercall">-1</field>-->
|
||||
<!-- <field name="doall" eval="False"/>-->
|
||||
<!-- <field name="user_id" ref="base.user_root"/>-->
|
||||
<!-- <field name="active" eval="True"/>-->
|
||||
<!-- </record>-->
|
||||
|
||||
<!-- <record model="ir.cron" id="ir_cron_mrp_workorder_pre_overdue">-->
|
||||
<!-- <field name="name">工单已逾期</field>-->
|
||||
<!-- <field name="model_id" ref="model_mrp_workorder"/>-->
|
||||
<!-- <field name="state">code</field>-->
|
||||
<!-- <field name="code">model._overdue_func()</field>-->
|
||||
<!-- <field name="interval_number">1</field>-->
|
||||
<!-- <field name="interval_type">minutes</field>-->
|
||||
<!-- <field name="numbercall">-1</field>-->
|
||||
<!-- <field name="doall" eval="False"/>-->
|
||||
<!-- <field name="user_id" ref="base.user_root"/>-->
|
||||
<!-- <field name="active" eval="True"/>-->
|
||||
<!-- </record>-->
|
||||
</data>
|
||||
</odoo>
|
||||
110
sf_message/data/template_data.xml
Normal file
110
sf_message/data/template_data.xml
Normal file
@@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" ?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="template_pending_order" model="jikimo.message.template">
|
||||
<field name="name">待接单</field>
|
||||
<field name="model_id" ref="sale.model_sale_order"/>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="bussiness_node_id" ref="bussiness_pending_order"/>
|
||||
<field name="msgtype">markdown</field>
|
||||
<field name="urgency">normal</field>
|
||||
<field name="content">### 待接单提醒:
|
||||
单号:销售订单[{{name}}]({{url}})
|
||||
事项:请确认是否接单。
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="template_to_be_confirm" model="jikimo.message.template">
|
||||
<field name="name">确认接单</field>
|
||||
<field name="model_id" ref="sale.model_sale_order"/>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="bussiness_node_id" ref="bussiness_to_be_confirm"/>
|
||||
<field name="msgtype">markdown</field>
|
||||
<field name="urgency">normal</field>
|
||||
<field name="content">### 待排程提醒:
|
||||
单号:产品[{{product_id}}]({{url}})
|
||||
事项:{{mrp_production_count}}个制造订单待计划排程
|
||||
</field>
|
||||
</record>
|
||||
<record id="template_material_purchase_remind" model="jikimo.message.template">
|
||||
<field name="name">坯料采购提醒</field>
|
||||
<field name="model_id" ref="purchase.model_purchase_order"/>
|
||||
<field name="model">purchase.order</field>
|
||||
<field name="bussiness_node_id" ref="bussiness_material_purchase_remind"/>
|
||||
<field name="msgtype">markdown</field>
|
||||
<field name="urgency">normal</field>
|
||||
<field name="content">### 坯料采购通知:
|
||||
单号:采购单[{{name}}]({{request_url}})
|
||||
事项:请确认坯料采购单并处理</field>
|
||||
</record>
|
||||
|
||||
<record id="template_material_picking_remind" model="jikimo.message.template">
|
||||
<field name="name">坯料发料提醒</field>
|
||||
<field name="model_id" ref="stock.model_stock_picking"/>
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="bussiness_node_id" ref="bussiness_material_picking_remind"/>
|
||||
<field name="msgtype">markdown</field>
|
||||
<field name="urgency">normal</field>
|
||||
<field name="content">### 坯料发料提醒:
|
||||
单号:产品[{{product_id}}]({{request_url}})
|
||||
事项:共{{number}}个生产发料单待确认处理</field>
|
||||
</record>
|
||||
|
||||
<record id="template_mrp_workorder_remind" model="jikimo.message.template">
|
||||
<field name="name">工单已下发通知</field>
|
||||
<field name="model_id" ref="mrp_workorder.model_mrp_workorder"/>
|
||||
<field name="model">mrp.workorder</field>
|
||||
<field name="bussiness_node_id" ref="bussiness_mrp_workorder_remind"/>
|
||||
<field name="msgtype">markdown</field>
|
||||
<field name="urgency">normal</field>
|
||||
<field name="content">### 工单已下发通知:
|
||||
单号:产品[{{product_id}}]({{request_url}})
|
||||
事项:共{{number}}个工单已下发,请查收知悉</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
<record id="template_transfer_inventory_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="transfer_inventory"/>
|
||||
<field name="msgtype">markdown</field>
|
||||
<field name="urgency">normal</field>
|
||||
<field name="content">### 调拨入库通知:
|
||||
单号:调拨入库单[{{name}}]({{request_url}})
|
||||
事项:完成刀具物料上架入库</field>
|
||||
</record>
|
||||
|
||||
<record id="template_tool_expired_remind" model="jikimo.message.template">
|
||||
<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="name">功能刀具寿命到期</field>
|
||||
<field name="model_id" ref="sf_tool_management.model_sf_functional_tool_dismantle"/>
|
||||
<field name="model">sf.functional.tool.dismantle</field>
|
||||
<field name="bussiness_node_id" ref="tool_dismantle"/>
|
||||
<field name="msgtype">markdown</field>
|
||||
<field name="urgency">normal</field>
|
||||
<field name="content">### 功能刀具寿命到期提醒:
|
||||
单号:拆解单[{{code}}]({{request_url}})
|
||||
事项:{{functional_tool_id.tool_name_id.name}}寿命已到期,需拆解</field>
|
||||
</record>
|
||||
|
||||
<record id="template_tool_assembly_remind" model="jikimo.message.template">
|
||||
<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="name">功能刀具组装</field>
|
||||
<field name="model_id" ref="sf_tool_management.model_sf_functional_tool_assembly"/>
|
||||
<field name="model">sf.functional.tool.assembly</field>
|
||||
<field name="bussiness_node_id" ref="tool_assembly"/>
|
||||
<field name="msgtype">markdown</field>
|
||||
<field name="urgency">normal</field>
|
||||
<field name="content">### 功能刀具组装通知:
|
||||
单号:组装任务单[{{name}}]({{request_url}})
|
||||
事项:{{use_tool_time}}前完成组装</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1 +1,9 @@
|
||||
from . import sf_message_template
|
||||
from . import sf_message_sale
|
||||
from . import sf_message_plan
|
||||
from . import sf_message_stock_picking
|
||||
from . import sf_message_cam_program
|
||||
from . import sf_message_functional_tool_assembly
|
||||
from . import sf_message_purchase
|
||||
from . import sf_message_workorder
|
||||
from . import sf_message_functional_tool_dismantle
|
||||
|
||||
6
sf_message/models/sf_message_cam_program.py
Normal file
6
sf_message/models/sf_message_cam_program.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from odoo import models, fields, api, _
|
||||
|
||||
|
||||
class SFMessageCamProgram(models.Model):
|
||||
_name = 'sf.cam.work.order.program.knife.plan'
|
||||
_inherit = ['sf.cam.work.order.program.knife.plan', 'jikimo.message.dispatch']
|
||||
15
sf_message/models/sf_message_functional_tool_assembly.py
Normal file
15
sf_message/models/sf_message_functional_tool_assembly.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from odoo import models, fields, api, _
|
||||
|
||||
|
||||
class SFMessagefunctionalToolAssembly(models.Model):
|
||||
_name = 'sf.functional.tool.assembly'
|
||||
_description = "刀具组装单"
|
||||
_inherit = ['sf.functional.tool.assembly', 'jikimo.message.dispatch']
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals):
|
||||
result = super(SFMessagefunctionalToolAssembly, self).create(vals)
|
||||
for obj in result:
|
||||
if obj.loading_task_source == '0' and obj.assemble_status == '0':
|
||||
obj.add_queue('功能刀具组装')
|
||||
return result
|
||||
19
sf_message/models/sf_message_functional_tool_dismantle.py
Normal file
19
sf_message/models/sf_message_functional_tool_dismantle.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from odoo import models, api
|
||||
|
||||
|
||||
class SFMessagefunctionalToolDismantle(models.Model):
|
||||
_name = 'sf.functional.tool.dismantle'
|
||||
_description = "刀具拆解单"
|
||||
_inherit = ['sf.functional.tool.dismantle', 'jikimo.message.dispatch']
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
# 判断是否为web页面创建请求
|
||||
is_web_request = self.env.context.get('is_web_request', False)
|
||||
result = super(SFMessagefunctionalToolDismantle, self).create(vals)
|
||||
if is_web_request:
|
||||
return result
|
||||
for obj in result:
|
||||
if obj.dismantle_cause in ['寿命到期报废', '崩刀报废'] and obj.state == '待拆解':
|
||||
obj.add_queue('功能刀具寿命到期')
|
||||
return result
|
||||
25
sf_message/models/sf_message_plan.py
Normal file
25
sf_message/models/sf_message_plan.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models, fields, api, _
|
||||
|
||||
|
||||
class SFMessagePlan(models.Model):
|
||||
_name = 'sf.production.plan'
|
||||
_inherit = ['sf.production.plan', 'jikimo.message.dispatch']
|
||||
|
||||
# def create(self, vals_list):
|
||||
# res = super(SFMessagePlan, self).create(vals_list)
|
||||
# if res:
|
||||
# try:
|
||||
# res.add_queue('待排程')
|
||||
# except Exception as e:
|
||||
# logging.info('add_queue error:%s' % e)
|
||||
# return res
|
||||
#
|
||||
# def _get_message(self):
|
||||
# res = super(SFMessagePlan, self)._get_message()
|
||||
# if res:
|
||||
# try:
|
||||
# res.add_queue('待排程')
|
||||
# except Exception as e:
|
||||
# logging.info('_get_message error:%s' % e)
|
||||
# return res
|
||||
33
sf_message/models/sf_message_purchase.py
Normal file
33
sf_message/models/sf_message_purchase.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from odoo import models, fields, api, _
|
||||
from urllib.parse import urlencode
|
||||
|
||||
|
||||
class SFMessagePurchase(models.Model):
|
||||
_name = 'purchase.order'
|
||||
_inherit = ['purchase.order', 'jikimo.message.dispatch']
|
||||
|
||||
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
|
||||
url = self.request_url(int(message_queue_id.res_id))
|
||||
purchase_order_line = self.env['purchase.order'].search([('id', '=', int(message_queue_id.res_id))])
|
||||
content = content.replace('{{name}}', purchase_order_line.name).replace(
|
||||
'{{request_url}}', url)
|
||||
contents.append(content)
|
||||
return contents
|
||||
|
||||
def request_url(self, id):
|
||||
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||
action_id = self.env.ref('purchase.purchase_form_action').id
|
||||
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_website_payment')]).id
|
||||
# 查询参数
|
||||
params = {'id': id, 'menu_id': menu_id, 'action': action_id,
|
||||
'model': 'purchase.order',
|
||||
'view_type': 'form'}
|
||||
# 拼接查询参数
|
||||
query_string = urlencode(params)
|
||||
# 拼接URL
|
||||
full_url = url + "/web#" + query_string
|
||||
return full_url
|
||||
73
sf_message/models/sf_message_sale.py
Normal file
73
sf_message/models/sf_message_sale.py
Normal file
@@ -0,0 +1,73 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
from odoo import models, fields, api, _
|
||||
|
||||
|
||||
class SFMessageSale(models.Model):
|
||||
_name = 'sale.order'
|
||||
_inherit = ['sale.order', 'jikimo.message.dispatch']
|
||||
|
||||
def create(self, vals_list):
|
||||
res = super(SFMessageSale, self).create(vals_list)
|
||||
if res:
|
||||
try:
|
||||
res.add_queue('待接单')
|
||||
except Exception as e:
|
||||
logging.info('add_queue error:%s' % e)
|
||||
return res
|
||||
|
||||
# 确认接单
|
||||
def action_confirm(self):
|
||||
res = super(SFMessageSale, self).action_confirm()
|
||||
if res is True:
|
||||
try:
|
||||
self.add_queue('确认接单')
|
||||
picking_ids = self.mrp_production_ids
|
||||
purchase_order_id = []
|
||||
if picking_ids:
|
||||
for picking_id in picking_ids:
|
||||
purchase_order_ids = (
|
||||
picking_id.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id |
|
||||
picking_id.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id).ids
|
||||
purchase_order_id.extend(purchase_order_ids)
|
||||
if purchase_order_id:
|
||||
purchase_order_list = self.env['purchase.order'].search([('id', 'in', purchase_order_id)])
|
||||
for purchase_order_info in purchase_order_list:
|
||||
purchase_order_info.add_queue('坯料采购提醒')
|
||||
except Exception as e:
|
||||
logging.info('add_queue error:%s' % e)
|
||||
return res
|
||||
|
||||
# 继承并重写jikimo.message.dispatch的_get_message()
|
||||
def _get_message(self, message_queue_ids):
|
||||
contents = []
|
||||
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||
for item in message_queue_ids:
|
||||
# 待接单的处理
|
||||
if item.message_template_id.bussiness_node_id.name == '待接单':
|
||||
content = super(SFMessageSale, self)._get_message(item)
|
||||
action_id = self.env.ref('sale.action_quotations_with_onboarding').id
|
||||
url = f"{url}/web#id={item.res_id}&view_type=form&action={action_id}"
|
||||
content = content[0].replace('{{url}}', url)
|
||||
contents.append(content)
|
||||
# 确认接单的处理
|
||||
elif item.message_template_id.bussiness_node_id.name == '确认接单':
|
||||
content = super(SFMessageSale, self)._get_message(item)
|
||||
sale_order_line = self.env['sale.order.line'].search([('order_id', '=', int(item.res_id))])
|
||||
product = sale_order_line[0].product_id.name if len(sale_order_line) == 1 else '%s...' % \
|
||||
sale_order_line[
|
||||
0].product_id.name
|
||||
action_id = self.env.ref('sf_plan.sf_production_plan_action1').id
|
||||
url = f"{url}/web#view_type=list&action={action_id}"
|
||||
content = content[0].replace('{{product_id}}', product).replace('{{url}}', url)
|
||||
contents.append(content)
|
||||
return contents
|
||||
|
||||
# # 销售订单逾期预警
|
||||
# def _overdue_warning_func(self):
|
||||
# sale_order_
|
||||
# return 1
|
||||
#
|
||||
# # 销售订单已逾期
|
||||
# def _overdue_func(self):
|
||||
# return 1
|
||||
65
sf_message/models/sf_message_stock_picking.py
Normal file
65
sf_message/models/sf_message_stock_picking.py
Normal file
@@ -0,0 +1,65 @@
|
||||
import re
|
||||
from odoo import models, fields, api, _
|
||||
from urllib.parse import urlencode
|
||||
|
||||
|
||||
class SFMessageStockPicking(models.Model):
|
||||
_name = 'stock.picking'
|
||||
_description = "库存调拨"
|
||||
_inherit = ['stock.picking', 'jikimo.message.dispatch']
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals):
|
||||
result = super(SFMessageStockPicking, self).create(vals)
|
||||
for obj in result:
|
||||
if obj.location_id.name == '进货' and obj.location_dest_id.name == '刀具房':
|
||||
obj.add_queue('调拨入库')
|
||||
return result
|
||||
|
||||
@api.depends('move_type', 'immediate_transfer', 'move_ids.state', 'move_ids.picking_id')
|
||||
def _compute_state(self):
|
||||
super(SFMessageStockPicking, self)._compute_state()
|
||||
for record in self:
|
||||
if record.state == 'assigned' and record.check_in == 'PC':
|
||||
record.add_queue('坯料发料提醒')
|
||||
|
||||
def _get_message(self, message_queue_ids):
|
||||
contents = []
|
||||
product_id = []
|
||||
for message_queue_id in message_queue_ids:
|
||||
i = 0
|
||||
if message_queue_id.message_template_id.name == '坯料发料提醒':
|
||||
content = message_queue_id.message_template_id.content
|
||||
stock_picking_line = self.env['stock.picking'].search([('id', '=', int(message_queue_id.res_id))])
|
||||
mrp_production_info = self.env['mrp.production'].search(
|
||||
[('name', '=', stock_picking_line.origin)])
|
||||
mrp_production_list = self.env['mrp.production'].search(
|
||||
[('product_id', '=', mrp_production_info.product_id.id)])
|
||||
for mrp_production_line in mrp_production_list:
|
||||
picking_ids = mrp_production_line.picking_ids
|
||||
for picking_id in picking_ids:
|
||||
if picking_id.state == 'assigned' and picking_id.check_in == 'PC':
|
||||
i += 1
|
||||
if i > 0 and mrp_production_info.product_id.id not in product_id:
|
||||
url = self.request_url()
|
||||
content = content.replace('{{product_id}}', mrp_production_info.product_id.name).replace(
|
||||
'{{number}}', str(i)).replace('{{request_url}}', url)
|
||||
product_id.append(mrp_production_info.product_id.id)
|
||||
contents.append(content)
|
||||
return contents
|
||||
else:
|
||||
res = super(SFMessageStockPicking, self)._get_message(message_queue_id)
|
||||
return res
|
||||
|
||||
def request_url(self):
|
||||
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||
action_id = self.env.ref('stock.stock_picking_type_action').id
|
||||
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_theme_treehouse')]).id
|
||||
# 查询参数
|
||||
params = {'menu_id': menu_id, 'action': action_id, 'model': 'stock.picking',
|
||||
'view_type': 'kanban'}
|
||||
# 拼接查询参数
|
||||
query_string = urlencode(params)
|
||||
# 拼接URL
|
||||
full_url = url + "/web#" + query_string
|
||||
return full_url
|
||||
@@ -1,48 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models, fields, api
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
|
||||
class SfMessageTemplate(models.Model):
|
||||
_name = "sf.message.template"
|
||||
_description = u'消息模板'
|
||||
_inherit = "jikimo.message.template"
|
||||
|
||||
name = fields.Char(string=u"名称", required=True)
|
||||
type = fields.Selection([
|
||||
('待接单', '待接单'),
|
||||
('待排程', '待排程'),
|
||||
('坯料采购', '坯料采购'),
|
||||
('坯料发料', '坯料发料'),
|
||||
('待编程', '待编程'),
|
||||
('调拨入库', '调拨入库'),
|
||||
('功能刀具组装', '功能刀具组装'),
|
||||
('功能刀具寿命到期', '功能刀具寿命到期'),
|
||||
('程序用刀计划异常', '程序用刀计划异常'),
|
||||
('工单无CNC程序', '工单无CNC程序'),
|
||||
('生产线无功能刀具', '生产线无功能刀具'),
|
||||
('工单无定位数据', '工单无定位数据'),
|
||||
('工单FTP无文件', '工单FTP无文件'),
|
||||
('工单加工失败', '工单加工失败'),
|
||||
('设备故障及异常', '设备故障及异常'),
|
||||
('工单逾期预警', '工单逾期预警'),
|
||||
('工单已逾期', '工单已逾期'),
|
||||
('销售订单逾期', '销售订单逾期'),
|
||||
('销售订单已逾期', '销售订单已逾期'),
|
||||
('待质量判定', '待质量判定'),
|
||||
('生产完工待入库', '生产完工待入库'),
|
||||
('订单发货', '订单发货')
|
||||
], string='类型', required=True)
|
||||
description = fields.Char(string=u"描述")
|
||||
content = fields.Html(string=u"内容", render_engine='qweb', translate=True, prefetch=True, sanitize=False)
|
||||
msgtype = fields.Selection(
|
||||
[('text', u'文字'), ('markdown', u'Markdown')], u'消息类型',
|
||||
required=True, default='markdown')
|
||||
notification_department_id = fields.Many2one('hr.department', u'通知部门', required=True)
|
||||
notification_employee_ids = fields.Many2many('hr.employee', string=u'员工',
|
||||
domain="[('department_id', '=',notification_department_id)]",
|
||||
required=True)
|
||||
active = fields.Boolean(string=u"是否有效", default=True)
|
||||
|
||||
@api.onchange('notification_department_id')
|
||||
def _clear_employee_ids(self):
|
||||
if self.notification_department_id:
|
||||
self.notification_employee_ids = False
|
||||
def _get_message_model(self):
|
||||
res = super(SfMessageTemplate, self)._get_message_model()
|
||||
res.append("sale.order")
|
||||
res.append("stock.picking")
|
||||
res.append('sf.functional.tool.assembly')
|
||||
res.append('sf.functional.tool.dismantle')
|
||||
res.append('purchase.order')
|
||||
res.append('mrp.workorder')
|
||||
return res
|
||||
|
||||
54
sf_message/models/sf_message_workorder.py
Normal file
54
sf_message/models/sf_message_workorder.py
Normal file
@@ -0,0 +1,54 @@
|
||||
from odoo import models, fields, api, _
|
||||
import logging, json
|
||||
import requests
|
||||
from odoo.addons.sf_base.commons.common import Common
|
||||
from urllib.parse import urlencode
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class SFMessageWork(models.Model):
|
||||
_name = 'mrp.workorder'
|
||||
_inherit = ['mrp.workorder', 'jikimo.message.dispatch']
|
||||
|
||||
@api.depends('production_availability', 'blocked_by_workorder_ids.state')
|
||||
def _compute_state(self):
|
||||
super(SFMessageWork, self)._compute_state()
|
||||
for workorder in self:
|
||||
if workorder.state == 'ready' and workorder.routing_type == '装夹预调':
|
||||
jikimo_message_queue = self.env['jikimo.message.queue'].sudo().search(
|
||||
[('res_id', '=', workorder.id), ("message_status", "=", "pending")])
|
||||
if not jikimo_message_queue:
|
||||
workorder.add_queue('工单已下发通知')
|
||||
|
||||
def _get_message(self, message_queue_ids):
|
||||
contents = []
|
||||
product_id = []
|
||||
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_workorder_line = self.env['mrp.workorder'].search([('id', '=', int(message_queue_id.res_id))])
|
||||
mrp_workorder_list = self.env['mrp.workorder'].search(
|
||||
[('product_id', '=', mrp_workorder_line.product_id.id), ('state', '=', 'ready'),
|
||||
('routing_type', '=', '装夹预调')])
|
||||
if len(mrp_workorder_list) > 0 and mrp_workorder_line.product_id.id not in product_id:
|
||||
url = self.request_url()
|
||||
content = content.replace('{{product_id}}', mrp_workorder_line.product_id.name).replace(
|
||||
'{{number}}', str(len(mrp_workorder_list))).replace(
|
||||
'{{request_url}}', url)
|
||||
product_id.append(mrp_workorder_line.product_id.id)
|
||||
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('sf_manufacturing.mrp_workorder_action_tablet').id
|
||||
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_stock_dropshipping')]).id
|
||||
# 查询参数
|
||||
params = {'menu_id': menu_id, 'action': action_id, 'model': 'mrp.workorder',
|
||||
'view_type': 'list', 'active_id': 1}
|
||||
# 拼接查询参数
|
||||
query_string = urlencode(params)
|
||||
# 拼接URL
|
||||
full_url = url + "/web#" + query_string
|
||||
return full_url
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_sf_message_template_group_sale_salemanager,sf_message_template,model_sf_message_template,sf_base.group_sale_salemanager,1,1,1,0
|
||||
access_sf_message_template_group_purchase,sf_message_template,model_sf_message_template,sf_base.group_purchase,1,1,1,0
|
||||
access_sf_message_template_group_sf_stock_user,sf_message_template,model_sf_message_template,sf_base.group_sf_stock_user,1,1,1,0
|
||||
access_sf_message_template_group_sf_order_user,sf_message_template,model_sf_message_template,sf_base.group_sf_order_user,1,1,1,0
|
||||
access_sf_message_template_group_sf_tool_user,sf_message_template,model_sf_message_template,sf_base.group_sf_tool_user,1,1,1,0
|
||||
|
||||
access_jikimo_message_template_group_sale_salemanager,jikimo_message_template,model_jikimo_message_template,sf_base.group_sale_salemanager,1,1,1,0
|
||||
access_jikimo_message_template_group_purchase,jikimo_message_template,model_jikimo_message_template,sf_base.group_purchase,1,1,1,0
|
||||
access_jikimo_message_template_group_sf_stock_user,jikimo_message_template,model_jikimo_message_template,sf_base.group_sf_stock_user,1,1,1,0
|
||||
access_jikimo_message_template_group_sf_order_user,jikimo_message_template,model_jikimo_message_template,sf_base.group_sf_order_user,1,1,1,0
|
||||
access_jikimo_message_template_group_sf_tool_user,jikimo_message_template,model_jikimo_message_template,sf_base.group_sf_tool_user,1,1,1,0
|
||||
|
||||
access_jikimo_message_bussiness_node_group_sale_salemanager,jikimo_message_bussiness_node,model_jikimo_message_bussiness_node,sf_base.group_sale_salemanager,1,1,1,0
|
||||
access_jikimo_message_bussiness_node_group_purchase,jikimo_message_bussiness_node,model_jikimo_message_bussiness_node,sf_base.group_purchase,1,1,1,0
|
||||
access_jikimo_message_bussiness_node_group_sf_stock_user,jikimo_message_bussiness_node,model_jikimo_message_bussiness_node,sf_base.group_sf_stock_user,1,1,1,0
|
||||
access_jikimo_message_bussiness_node_group_sf_order_user,jikimo_message_bussiness_node,model_jikimo_message_bussiness_node,sf_base.group_sf_order_user,1,1,1,0
|
||||
access_jikimo_message_bussiness_node_group_sf_tool_user,jikimo_message_bussiness_node,model_jikimo_message_bussiness_node,sf_base.group_sf_tool_user,1,1,1,0
|
||||
|
||||
access_jikimo_message_queue_group_sale_salemanager,jikimo_message_queue,model_jikimo_message_queue,sf_base.group_sale_salemanager,1,1,1,0
|
||||
access_jikimo_message_queue_group_purchase,jikimo_message_queue,model_jikimo_message_queue,sf_base.group_purchase,1,1,1,0
|
||||
access_jikimo_message_queue_group_sf_stock_user,jikimo_message_queue,model_jikimo_message_queue,sf_base.group_sf_stock_user,1,1,1,0
|
||||
access_jikimo_message_queue_group_sf_order_user,jikimo_message_queue,model_jikimo_message_queue,sf_base.group_sf_order_user,1,1,1,0
|
||||
access_jikimo_message_queue_group_sf_tool_user,jikimo_message_queue,model_jikimo_message_queue,sf_base.group_sf_tool_user,1,1,1,0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
<record id="sf_message_template_view_form" model="ir.ui.view">
|
||||
<field name="name">sf.message.template.view.form</field>
|
||||
<field name="model">sf.message.template</field>
|
||||
<field name="model">message.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="消息模板">
|
||||
<sheet>
|
||||
@@ -18,12 +18,12 @@
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<field name="type"/>
|
||||
<!-- <field name="type"/>-->
|
||||
<field name="notify_model_id"/>
|
||||
<field name="content" widget="html" class="oe-bordered-editor"
|
||||
options="{'style-inline': true, 'codeview': true, 'dynamic_placeholder': true}"/>
|
||||
<field name="description"/>
|
||||
<field name="msgtype"/>
|
||||
<field name="type"/>
|
||||
<field name="notification_department_id"/>
|
||||
<field name="notification_employee_ids" widget="many2many_tags"/>
|
||||
</group>
|
||||
@@ -34,14 +34,13 @@
|
||||
|
||||
<record id="sf_message_template_view_tree" model="ir.ui.view">
|
||||
<field name="name">sf.message.template.view.tree</field>
|
||||
<field name="model">sf.message.template</field>
|
||||
<field name="model">message.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="消息模板">
|
||||
<field name="name"/>
|
||||
<field name="type"/>
|
||||
<!-- <field name="type"/>-->
|
||||
<field name="content"/>
|
||||
<field name="msgtype"/>
|
||||
<field name="type"/>
|
||||
<field name="notification_department_id"/>
|
||||
<field name="notification_employee_ids" widget="many2many_tags"/>
|
||||
<field name="description"/>
|
||||
@@ -51,11 +50,11 @@
|
||||
|
||||
<record id="sf_message_template_search_view" model="ir.ui.view">
|
||||
<field name="name">sf.message.template.search.view</field>
|
||||
<field name="model">sf.message.template</field>
|
||||
<field name="model">message.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="name" string="模糊搜索"
|
||||
filter_domain="['|','|',('name','like',self),('type','like',self),('description','like',self)]"/>
|
||||
filter_domain="['|','|',('name','like',self),('description','like',self)]"/>
|
||||
<field name="name"/>
|
||||
<filter name="filter_active" string="已归档" domain="[('active','=',False)]"/>
|
||||
</search>
|
||||
@@ -65,7 +64,7 @@
|
||||
<!--定义单证类型视图动作-->
|
||||
<record id="sf_message_template_action" model="ir.actions.act_window">
|
||||
<field name="name">消息模板</field>
|
||||
<field name="res_model">sf.message.template</field>
|
||||
<field name="res_model">message.template</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_id" ref="sf_message_template_view_tree"/>
|
||||
</record>
|
||||
|
||||
@@ -10,11 +10,12 @@
|
||||
""",
|
||||
'category': 'sf',
|
||||
'website': 'https://www.sf.cs.jikimo.com',
|
||||
'depends': ['sf_base', 'base_setup'],
|
||||
'depends': ['sf_base', 'base_setup','sf_bf_connect'],
|
||||
'data': [
|
||||
'data/ir_cron_data.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'views/res_config_settings_views.xml'
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/order_price.xml',
|
||||
],
|
||||
'demo': [
|
||||
],
|
||||
|
||||
@@ -62,8 +62,8 @@ class Sf_Mrs_Connect(http.Controller):
|
||||
if cnc_workorder_has.cnc_ids:
|
||||
cnc_workorder_has.cmm_ids.sudo().unlink()
|
||||
cnc_workorder_has.cnc_ids.sudo().unlink()
|
||||
request.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan(
|
||||
production)
|
||||
# request.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan(
|
||||
# production)
|
||||
cnc_workorder_has.write(
|
||||
{'cnc_ids': cnc_workorder_has.cnc_ids.sudo()._json_cnc_processing(panel, ret),
|
||||
'cmm_ids': cnc_workorder_has.cmm_ids.sudo()._json_cmm_program(panel, ret)})
|
||||
@@ -93,6 +93,9 @@ class Sf_Mrs_Connect(http.Controller):
|
||||
pre_workorder.write(
|
||||
{'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
|
||||
productions.write({'programming_state': '已编程', 'work_state': '已编程'})
|
||||
res.update({
|
||||
'production_ids': productions.ids
|
||||
})
|
||||
return json.JSONEncoder().encode(res)
|
||||
else:
|
||||
res = {'status': 0, 'message': '该制造订单暂未开始'}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from . import ftp_operate
|
||||
from . import res_config_setting
|
||||
from . import sync_common
|
||||
from . import order_price
|
||||
29
sf_mrs_connect/models/order_price.py
Normal file
29
sf_mrs_connect/models/order_price.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from odoo import fields, models, api
|
||||
|
||||
|
||||
class OrderPrice(models.Model):
|
||||
_name = 'order.price'
|
||||
_description = '订单价格对比'
|
||||
sale_order_id = fields.Many2one('sale.order', '销售订单')
|
||||
bfm_order_name = fields.Char(related="sale_order_id.default_code", string='bfm订单号')
|
||||
sale_order_name = fields.Char(related="sale_order_id.name", string='销售订单号')
|
||||
currency_id = fields.Many2one(
|
||||
related='sale_order_id.currency_id', string='货币', store=True)
|
||||
sale_order_amount_total = fields.Monetary(related="sale_order_id.amount_total", tracking=4, string='销售订单金额')
|
||||
bfm_amount_total = fields.Float(string='价格合计', compute='_compute_bfm_amount_total', store=True)
|
||||
|
||||
def is_float(self,value):
|
||||
try:
|
||||
float(value)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
@api.depends('sale_order_id.order_line.remark')
|
||||
def _compute_bfm_amount_total(self):
|
||||
for record in self:
|
||||
amount_total = 0
|
||||
for line in record.sale_order_id.order_line:
|
||||
# 判断remark是否存在并且是否是数字
|
||||
if line.remark and self.is_float(line.remark):
|
||||
amount_total += float(line.remark)
|
||||
record.bfm_amount_total = amount_total
|
||||
@@ -16,13 +16,10 @@ class ResConfigSettings(models.TransientModel):
|
||||
token = fields.Char(string='TOKEN', default='b811ac06-3f00-11ed-9aed-0242ac110003')
|
||||
sf_secret_key = fields.Char(string='密钥', default='wBmxej38OkErKhD6')
|
||||
sf_url = fields.Char(string='访问地址', default='https://sf.cs.jikimo.com')
|
||||
agv_rcs_url = fields.Char(string='avg_rcs访问地址',
|
||||
default='http://172.16.10.114:8182/rcms/services/rest/hikRpcService/genAgvSchedulingTask')
|
||||
center_control_url = fields.Char(string='中控访问地址',
|
||||
default='http://172.16.21.50:8001')
|
||||
center_control_Authorization = fields.Char(string='中控访问认证')
|
||||
wbcode = fields.Char('地码')
|
||||
agv_code = fields.Char(string='agv编号')
|
||||
|
||||
task_type_no = fields.Char('任务单类型编号')
|
||||
model_parser_url = fields.Char('特征识别路径')
|
||||
ftp_host = fields.Char(string='FTP的ip')
|
||||
@@ -103,9 +100,7 @@ class ResConfigSettings(models.TransientModel):
|
||||
token = config.get_param('token', default='')
|
||||
sf_secret_key = config.get_param('sf_secret_key', default='')
|
||||
sf_url = config.get_param('sf_url', default='')
|
||||
agv_rcs_url = config.get_param('agv_rcs_url', default='')
|
||||
wbcode = config.get_param('wbcode', default='')
|
||||
agv_code = config.get_param('agv_code', default='')
|
||||
|
||||
center_control_url = config.get_param('center_control_url', default='')
|
||||
center_control_Authorization = config.get_param('center_control_Authorization', default='')
|
||||
ftp_host = config.get_param('ftp_host', default='')
|
||||
@@ -118,9 +113,7 @@ class ResConfigSettings(models.TransientModel):
|
||||
token=token,
|
||||
sf_secret_key=sf_secret_key,
|
||||
sf_url=sf_url,
|
||||
agv_rcs_url=agv_rcs_url,
|
||||
wbcode=wbcode,
|
||||
agv_code=agv_code,
|
||||
|
||||
center_control_url=center_control_url,
|
||||
center_control_Authorization=center_control_Authorization,
|
||||
ftp_host=ftp_host,
|
||||
@@ -137,9 +130,7 @@ class ResConfigSettings(models.TransientModel):
|
||||
ir_config.set_param("token", self.token or "")
|
||||
ir_config.set_param("sf_secret_key", self.sf_secret_key or "")
|
||||
ir_config.set_param("sf_url", self.sf_url or "")
|
||||
ir_config.set_param("agv_rcs_url", self.agv_rcs_url or "")
|
||||
ir_config.set_param("wbcode", self.wbcode or "")
|
||||
ir_config.set_param("agv_code", self.agv_code or "")
|
||||
|
||||
ir_config.set_param("center_control_url", self.center_control_url or "")
|
||||
ir_config.set_param("center_control_Authorization", self.center_control_Authorization or "")
|
||||
ir_config.set_param("ftp_host", self.ftp_host or "")
|
||||
@@ -184,7 +175,10 @@ class ResConfigSettings(models.TransientModel):
|
||||
new_price = res_order_lines_map.get(str(index))
|
||||
if order_line:
|
||||
# 修改单价
|
||||
order_line.write({'remark': new_price})
|
||||
order_line.write({'remark': round(new_price*order_line.product_uom_qty,2)})
|
||||
order_price = self.env['order.price'].sudo().search([('sale_order_id', '=',need_change_sale_order.id )])
|
||||
if not order_price:
|
||||
self.env['order.price'].sudo().create({'sale_order_id':need_change_sale_order.id})
|
||||
else:
|
||||
logging.error('同步销售订单价格失败 {}'.format(response.text))
|
||||
raise UserError('同步销售订单价格失败')
|
||||
|
||||
@@ -2438,6 +2438,7 @@ class CuttingToolBasicParameters(models.Model):
|
||||
'handle_length': integral_tool_item['shank_length'],
|
||||
'blade_tip_diameter': integral_tool_item['tip_diameter'],
|
||||
'blade_tip_working_size': integral_tool_item['tip_handling_size'],
|
||||
'tip_r_size': integral_tool_item['tip_r_size'],
|
||||
'blade_tip_taper': integral_tool_item['knife_tip_taper'],
|
||||
'blade_helix_angle': integral_tool_item['blade_helix_angle'],
|
||||
'blade_width': integral_tool_item['blade_width'],
|
||||
@@ -2459,6 +2460,7 @@ class CuttingToolBasicParameters(models.Model):
|
||||
'handle_length': integral_tool_item['shank_length'],
|
||||
'blade_tip_diameter': integral_tool_item['tip_diameter'],
|
||||
'blade_tip_working_size': integral_tool_item['tip_handling_size'],
|
||||
'tip_r_size': integral_tool_item['tip_r_size'],
|
||||
'blade_tip_taper': integral_tool_item['knife_tip_taper'],
|
||||
'blade_helix_angle': integral_tool_item['blade_helix_angle'],
|
||||
'blade_width': integral_tool_item['blade_width'],
|
||||
@@ -2674,7 +2676,8 @@ class CuttingToolBasicParameters(models.Model):
|
||||
'interface_diameter': cutter_head_item['interface_diameter'],
|
||||
'total_length': cutter_head_item['total_length'],
|
||||
'blade_length': cutter_head_item['blade_length'],
|
||||
'cutting_depth': cutter_head_item['cutting_depth_max'],
|
||||
'cutting_blade_length': cutter_head_item['cutting_blade_length'],
|
||||
'cut_depth_max': cutter_head_item['cutting_depth_max'],
|
||||
'main_included_angle': cutter_head_item['edge_angle'],
|
||||
'installing_structure': cutter_head_item['mounting_structure'],
|
||||
'blade_id': False if not cutter_head_item['fit_blade_model_code'] else self.env[
|
||||
@@ -2696,7 +2699,8 @@ class CuttingToolBasicParameters(models.Model):
|
||||
'interface_diameter': cutter_head_item['interface_diameter'],
|
||||
'total_length': cutter_head_item['total_length'],
|
||||
'blade_length': cutter_head_item['blade_length'],
|
||||
'cutting_depth': cutter_head_item['cutting_depth_max'],
|
||||
'cutting_blade_length': cutter_head_item['cutting_blade_length'],
|
||||
'cut_depth_max': cutter_head_item['cutting_depth_max'],
|
||||
'main_included_angle': cutter_head_item['edge_angle'],
|
||||
'installing_structure': cutter_head_item['mounting_structure'],
|
||||
'blade_id': False if not cutter_head_item['fit_blade_model_code'] else self.env[
|
||||
@@ -2789,6 +2793,7 @@ class CuttingToolBasicParameters(models.Model):
|
||||
'handle_length': integral_tool_item['shank_length'],
|
||||
'blade_tip_diameter': integral_tool_item['tip_diameter'],
|
||||
'blade_tip_working_size': integral_tool_item['tip_handling_size'],
|
||||
'tip_r_size': integral_tool_item['tip_r_size'],
|
||||
'blade_tip_taper': integral_tool_item['knife_tip_taper'],
|
||||
'blade_helix_angle': integral_tool_item['blade_helix_angle'],
|
||||
'blade_width': integral_tool_item['blade_width'],
|
||||
@@ -2810,6 +2815,7 @@ class CuttingToolBasicParameters(models.Model):
|
||||
'handle_length': integral_tool_item['shank_length'],
|
||||
'blade_tip_diameter': integral_tool_item['tip_diameter'],
|
||||
'blade_tip_working_size': integral_tool_item['tip_handling_size'],
|
||||
'tip_r_size': integral_tool_item['tip_r_size'],
|
||||
'blade_tip_taper': integral_tool_item['knife_tip_taper'],
|
||||
'blade_helix_angle': integral_tool_item['blade_helix_angle'],
|
||||
'blade_width': integral_tool_item['blade_width'],
|
||||
@@ -3007,8 +3013,7 @@ class CuttingToolBasicParameters(models.Model):
|
||||
})
|
||||
if 'basic_parameters_cutter_head' in result['cutting_tool_basic_parameters_yesterday_list']:
|
||||
if result['cutting_tool_basic_parameters_yesterday_list']['basic_parameters_cutter_head']:
|
||||
basic_parameters_cutter_head_list = json.loads(
|
||||
result['cutting_tool_basic_parameters_yesterday_list']['basic_parameters_cutter_head'])
|
||||
basic_parameters_cutter_head_list = result['cutting_tool_basic_parameters_yesterday_list']['basic_parameters_cutter_head']
|
||||
if basic_parameters_cutter_head_list:
|
||||
for cutter_head_item in basic_parameters_cutter_head_list:
|
||||
cutter_head = self.search(
|
||||
@@ -3027,7 +3032,8 @@ class CuttingToolBasicParameters(models.Model):
|
||||
'interface_diameter': cutter_head_item['interface_diameter'],
|
||||
'total_length': cutter_head_item['total_length'],
|
||||
'blade_length': cutter_head_item['blade_length'],
|
||||
'cutting_depth': cutter_head_item['cutting_depth_max'],
|
||||
'cutting_blade_length': cutter_head_item['cutting_blade_length'],
|
||||
'cut_depth_max': cutter_head_item['cutting_depth_max'],
|
||||
'main_included_angle': cutter_head_item['edge_angle'],
|
||||
'installing_structure': cutter_head_item['mounting_structure'],
|
||||
'blade_id': False if not cutter_head_item['fit_blade_model_code'] else self.env[
|
||||
@@ -3049,7 +3055,8 @@ class CuttingToolBasicParameters(models.Model):
|
||||
'interface_diameter': cutter_head_item['interface_diameter'],
|
||||
'total_length': cutter_head_item['total_length'],
|
||||
'blade_length': cutter_head_item['blade_length'],
|
||||
'cutting_depth': cutter_head_item['cutting_depth_max'],
|
||||
'cutting_blade_length': cutter_head_item['cutting_blade_length'],
|
||||
'cut_depth_max': cutter_head_item['cutting_depth_max'],
|
||||
'main_included_angle': cutter_head_item['edge_angle'],
|
||||
'installing_structure': cutter_head_item['mounting_structure'],
|
||||
'blade_id': False if not cutter_head_item['fit_blade_model_code'] else self.env[
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_sf_static_resource_datasync,sf_static_resource_datasync,model_sf_static_resource_datasync,base.group_user,1,1,1,1
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
access_order_price,order.price,model_order_price,sf_base.group_sale_director,1,1,1,1
|
||||
|
27
sf_mrs_connect/views/order_price.xml
Normal file
27
sf_mrs_connect/views/order_price.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record model="ir.actions.act_window" id="order_price_tree_act">
|
||||
<field name="name">bfm订单价格对比</field>
|
||||
<field name="res_model">order.price</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
<menuitem sequence="22" name="销售订单bfm对比" id="menu_sale_order_bfm_price"
|
||||
action="order_price_tree_act"
|
||||
parent="sale.sale_order_menu"
|
||||
groups="sf_base.group_sale_director"
|
||||
/>
|
||||
|
||||
<record id="view_order_price_tree" model="ir.ui.view">
|
||||
<field name="name">order.price.list</field>
|
||||
<field name="model">order.price</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="订单计划">
|
||||
<field name="bfm_order_name"/>
|
||||
<field name="sale_order_name"/>
|
||||
<field name="sale_order_amount_total"/>
|
||||
<field name="bfm_amount_total"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -74,28 +74,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>AGV参数配置</h2>
|
||||
<div class="row mt16 o_settings_container" id="agv_config">
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<div class="o_setting_left_pane"/>
|
||||
<div class="o_setting_right_pane">
|
||||
<div class="text-muted">
|
||||
<label for="agv_rcs_url" string="访问地址"/>
|
||||
<field name="agv_rcs_url"/>
|
||||
</div>
|
||||
<div class="text-muted">
|
||||
<label for="agv_code" string="车辆编号"/>
|
||||
<field name="agv_code"/>
|
||||
</div>
|
||||
<div class="text-muted">
|
||||
<label for="wbcode"/>
|
||||
<field name="wbcode"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2>中控参数配置</h2>
|
||||
<div class="row mt16 o_settings_container">
|
||||
|
||||
@@ -70,6 +70,32 @@ class sf_production_plan(models.Model):
|
||||
sequence = fields.Integer(string='序号', copy=False, readonly=True, index=True)
|
||||
current_operation_name = fields.Char(string='当前工序名称', size=64, default='生产计划')
|
||||
|
||||
@api.onchange('date_planned_start')
|
||||
def date_planned_start_onchange(self):
|
||||
if self.date_planned_start:
|
||||
self.date_planned_finished = self.date_planned_start + timedelta(hours=1)
|
||||
|
||||
#处理计划状态非待排程,计划结束时间为空的数据处理
|
||||
def deal_no_date_planned_finished(self):
|
||||
plans = self.env['sf.production.plan'].search(
|
||||
[('date_planned_finished', '=', False), ('state', 'in', ['processing', 'done', 'finished'])])
|
||||
for item in plans:
|
||||
if item.date_planned_start:
|
||||
item.date_planned_finished = item.date_planned_start + timedelta(hours=1)
|
||||
|
||||
# 处理计划订单截止时间为空的数据
|
||||
def deal_no_order_deadline(self):
|
||||
plans = self.env['sf.production.plan'].sudo().search(
|
||||
[('order_deadline', '=', False)])
|
||||
for item in plans:
|
||||
if item.date_planned_start:
|
||||
item.order_deadline = item.date_planned_start + timedelta(days=7)
|
||||
@api.model
|
||||
def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None):
|
||||
|
||||
info = super(sf_production_plan, self).search_read(domain, fields, offset, limit, order)
|
||||
return info
|
||||
|
||||
# 计算实际加工时长
|
||||
@api.depends('actual_start_time', 'actual_end_time')
|
||||
def _compute_actual_process_time(self):
|
||||
@@ -192,7 +218,7 @@ class sf_production_plan(models.Model):
|
||||
|
||||
return num
|
||||
|
||||
def do_production_schedule(self, date_planned_start):
|
||||
def do_production_schedule(self):
|
||||
"""
|
||||
排程方法
|
||||
"""
|
||||
@@ -200,8 +226,7 @@ class sf_production_plan(models.Model):
|
||||
if not record.production_line_id:
|
||||
raise ValidationError("未选择生产线")
|
||||
else:
|
||||
|
||||
is_schedule = self.deal_processing_schedule(date_planned_start)
|
||||
is_schedule = self.deal_processing_schedule(record.date_planned_start)
|
||||
if not is_schedule:
|
||||
raise ValidationError("排程失败")
|
||||
workorder_id_list = record.production_id.workorder_ids.ids
|
||||
@@ -210,7 +235,6 @@ class sf_production_plan(models.Model):
|
||||
for item in record.production_id.workorder_ids:
|
||||
if item.name == 'CNC加工':
|
||||
item.date_planned_finished = datetime.now() + timedelta(days=100)
|
||||
# item.date_planned_start = record.date_planned_start
|
||||
item.date_planned_start = self.date_planned_start if self.date_planned_start else datetime.now()
|
||||
record.sudo().production_id.plan_start_processing_time = item.date_planned_start
|
||||
item.date_planned_finished = item.date_planned_start + timedelta(
|
||||
@@ -223,6 +247,8 @@ class sf_production_plan(models.Model):
|
||||
record.date_planned_start, record.date_planned_finished = \
|
||||
item.date_planned_start, item.date_planned_finished
|
||||
record.state = 'done'
|
||||
record.date_planned_finished = record.date_planned_start + timedelta(
|
||||
minutes=60) if not record.date_planned_finished else record.date_planned_finished
|
||||
# record.production_id.schedule_state = '已排'
|
||||
record.sudo().production_id.schedule_state = '已排'
|
||||
record.sudo().production_id.process_state = '待装夹'
|
||||
|
||||
@@ -88,8 +88,8 @@
|
||||
|
||||
<group string="加工信息">
|
||||
|
||||
<field name="date_planned_start" placeholder="如果不选择计划开始时间,会取当前时间来做排程"/>
|
||||
<field name="date_planned_finished"/>
|
||||
<field name="date_planned_start" placeholder="如果不选择计划开始时间,会取当前时间来做排程" required="1"/>
|
||||
<field name="date_planned_finished" required="1"/>
|
||||
<field name="actual_process_time"/>
|
||||
<field name="actual_start_time"/>
|
||||
<field name="actual_end_time"/>
|
||||
@@ -339,7 +339,7 @@
|
||||
name="空料架配送"
|
||||
sequence="11"
|
||||
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"
|
||||
/>
|
||||
<!-- <menuitem -->
|
||||
|
||||
@@ -6,6 +6,7 @@ from datetime import datetime
|
||||
from odoo import fields, models
|
||||
# from odoo.exceptions import ValidationError
|
||||
from odoo.exceptions import UserError
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -14,10 +15,15 @@ class Action_Plan_All_Wizard(models.TransientModel):
|
||||
_name = 'sf.action.plan.all.wizard'
|
||||
_description = u'排程向导'
|
||||
|
||||
def _get_date_planned_start(self):
|
||||
planned_start_date = datetime.now() + timedelta(minutes=10)
|
||||
logging.info('计划开始时间: %s', planned_start_date)
|
||||
return planned_start_date
|
||||
|
||||
# 选择生产线
|
||||
production_line_id = fields.Many2one('sf.production.line', string=u'生产线', required=True)
|
||||
date_planned_start = fields.Datetime(string='计划开始时间', index=True, copy=False,
|
||||
default=fields.Datetime.now)
|
||||
default=_get_date_planned_start)
|
||||
|
||||
# 接收传递过来的计划ID
|
||||
plan_ids = fields.Many2many('sf.production.plan', string=u'计划ID')
|
||||
@@ -36,7 +42,7 @@ class Action_Plan_All_Wizard(models.TransientModel):
|
||||
plan_obj = self.env['sf.production.plan'].browse(plan.id)
|
||||
plan_obj.production_line_id = self.production_line_id.id
|
||||
plan.date_planned_start = self.date_planned_start
|
||||
plan_obj.do_production_schedule(self.date_planned_start)
|
||||
plan_obj.do_production_schedule()
|
||||
# plan_obj.state = 'done'
|
||||
print('处理计划:', plan.id, '完成')
|
||||
|
||||
|
||||
@@ -58,6 +58,15 @@ class QuickEasyOrder(models.Model):
|
||||
|
||||
part_drawing_number = fields.Char('零件图号')
|
||||
machining_drawings = fields.Binary('2D加工图纸')
|
||||
machining_drawings_name = fields.Char('2D加工图纸名')
|
||||
|
||||
@api.onchange('machining_drawings_name')
|
||||
def _onchange_machining_drawings_name(self):
|
||||
for item in self:
|
||||
if item.machining_drawings_name:
|
||||
if not item.machining_drawings_name.lower().endswith(
|
||||
'.pdf'):
|
||||
raise ValidationError('文件格式上传有误,请检查文件后缀(不区分大小写)是否为pdf')
|
||||
|
||||
@api.onchange('parameter_ids')
|
||||
def _compute_parameter_ids(self):
|
||||
@@ -128,6 +137,10 @@ class QuickEasyOrder(models.Model):
|
||||
if len(item.upload_model_file) > 1:
|
||||
raise ValidationError('只允许上传一个文件')
|
||||
if item.upload_model_file:
|
||||
if not item.upload_model_file.name.lower().endswith(
|
||||
'.step') and not item.upload_model_file.name.lower().endswith(
|
||||
'.stp'):
|
||||
raise ValidationError('文件格式上传有误,请检查文件后缀(不区分大小写)是否为step、stp')
|
||||
file_attachment_id = item.upload_model_file[0]
|
||||
# 附件路径
|
||||
report_path = file_attachment_id._full_path(file_attachment_id.store_fname)
|
||||
|
||||
@@ -80,7 +80,8 @@
|
||||
<field name="unit_price"/>
|
||||
<field name="price" options="{'format': false}"/>
|
||||
<field name="part_drawing_number"/>
|
||||
<field name="machining_drawings" widget="pdf_viewer"/>
|
||||
<field name="machining_drawings" filename="machining_drawings_name" widget="pdf_viewer"/>
|
||||
<field name="machining_drawings_name" invisible="1"/>
|
||||
<field name="sale_order_id"
|
||||
attrs='{"invisible": [("sale_order_id","=",False)],"readonly": [("sale_order_id","!=",False)]}'/>
|
||||
</group>
|
||||
|
||||
@@ -74,6 +74,8 @@ class StockPicking(models.Model):
|
||||
|
||||
def send_to_bfm(self):
|
||||
skip_backorder = self.env.context.get('skip_backorder')
|
||||
cancel_backorder_ids = self.env.context.get('picking_ids_not_to_backorder')
|
||||
|
||||
# 下发发货到bfm
|
||||
config = self.env['res.config.settings'].get_values()
|
||||
move_ids, move_line_ids = self.deal_move_ids(self.move_ids, self.move_line_ids)
|
||||
@@ -92,13 +94,13 @@ class StockPicking(models.Model):
|
||||
'state': self.state,
|
||||
'backorder_id': self.deal_send_backorder_id(self.backorder_id),
|
||||
'backorder_ids': self.deal_send_backorder_id(self.backorder_ids),
|
||||
'cancel_backorder_ids': skip_backorder,
|
||||
'cancel_backorder_ids': True if skip_backorder and cancel_backorder_ids else False, # 没有欠单判断
|
||||
'move_type': self.move_type,
|
||||
},
|
||||
}
|
||||
url1 = config['bfm_url_new'] + '/api/stock/deliver_goods'
|
||||
json_str = json.dumps(data)
|
||||
print('json_str', json_str)
|
||||
logging.info('json_str= %s', json_str)
|
||||
r = requests.post(url1, json=data, data=None)
|
||||
if r.status_code == 200:
|
||||
result = json.loads(r.json()['result'])
|
||||
|
||||
@@ -185,6 +185,7 @@ class CAMWorkOrderProgramKnifePlan(models.Model):
|
||||
_description = 'CAM工单程序用刀计划'
|
||||
|
||||
name = fields.Char('工单任务编号')
|
||||
programming_no = fields.Char('编程单号')
|
||||
cam_procedure_code = fields.Char('程序名')
|
||||
filename = fields.Char('文件')
|
||||
cam_cutter_spacing_code = fields.Char('刀号')
|
||||
@@ -317,23 +318,33 @@ class CAMWorkOrderProgramKnifePlan(models.Model):
|
||||
"""
|
||||
根据传入的工单信息,查询是否有需要的功能刀具,如果没有则生成CAM工单程序用刀计划
|
||||
"""
|
||||
knife_plan = self.env['sf.cam.work.order.program.knife.plan'].sudo().create({
|
||||
'name': cnc_processing.workorder_id.production_id.name,
|
||||
'cam_procedure_code': cnc_processing.program_name,
|
||||
'filename': cnc_processing.cnc_id.name,
|
||||
'functional_tool_name': cnc_processing.cutting_tool_name,
|
||||
'cam_cutter_spacing_code': cnc_processing.cutting_tool_no,
|
||||
'process_type': cnc_processing.processing_type,
|
||||
'margin_x_y': float(cnc_processing.margin_x_y),
|
||||
'margin_z': float(cnc_processing.margin_z),
|
||||
'finish_depth': float(cnc_processing.depth_of_processing_z),
|
||||
'extension_length': float(cnc_processing.cutting_tool_extension_length),
|
||||
'shank_model': cnc_processing.cutting_tool_handle_type,
|
||||
'estimated_processing_time': cnc_processing.estimated_processing_time,
|
||||
})
|
||||
logging.info('CAM工单程序用刀计划创建成功!!!')
|
||||
# 创建装刀请求
|
||||
knife_plan.apply_for_tooling()
|
||||
|
||||
# 获取编程单号
|
||||
programming_no = cnc_processing.workorder_id.production_id.programming_no
|
||||
logging.info(f'编程单号:{programming_no}')
|
||||
cam_id = self.env['sf.cam.work.order.program.knife.plan'].sudo().search(
|
||||
[('programming_no', '=', programming_no),
|
||||
('functional_tool_name', '=', cnc_processing.cutting_tool_name)])
|
||||
logging.info(f'CAM装刀计划:{cam_id}')
|
||||
if not cam_id:
|
||||
knife_plan = self.env['sf.cam.work.order.program.knife.plan'].sudo().create({
|
||||
'name': cnc_processing.workorder_id.production_id.name,
|
||||
'programming_no': programming_no,
|
||||
'cam_procedure_code': cnc_processing.program_name,
|
||||
'filename': cnc_processing.cnc_id.name,
|
||||
'functional_tool_name': cnc_processing.cutting_tool_name,
|
||||
'cam_cutter_spacing_code': cnc_processing.cutting_tool_no,
|
||||
'process_type': cnc_processing.processing_type,
|
||||
'margin_x_y': float(cnc_processing.margin_x_y),
|
||||
'margin_z': float(cnc_processing.margin_z),
|
||||
'finish_depth': float(cnc_processing.depth_of_processing_z),
|
||||
'extension_length': float(cnc_processing.cutting_tool_extension_length),
|
||||
'shank_model': cnc_processing.cutting_tool_handle_type,
|
||||
'estimated_processing_time': cnc_processing.estimated_processing_time,
|
||||
})
|
||||
logging.info('CAM工单程序用刀计划创建成功!!!')
|
||||
# 创建装刀请求
|
||||
knife_plan.apply_for_tooling()
|
||||
|
||||
def unlink_cam_plan(self, production):
|
||||
for item in production:
|
||||
@@ -357,6 +368,8 @@ class FunctionalToolAssembly(models.Model):
|
||||
"""
|
||||
智能工厂组装单处扫码校验刀具物料
|
||||
"""
|
||||
if 'O-CMD' in barcode:
|
||||
return ''
|
||||
for record in self:
|
||||
tool_assembly_id = self.env['sf.functional.tool.assembly'].browse(self.ids)
|
||||
lot_ids = self.env['stock.lot'].sudo().search([('rfid', '=', barcode)])
|
||||
@@ -835,7 +848,7 @@ class FunctionalToolAssembly(models.Model):
|
||||
|
||||
if options == '刀柄+整体式刀具':
|
||||
if not integral_ids:
|
||||
raise ValidationError('功能刀具清单的BOM未配置[刀柄]信息,请先配置BOM再开始组装!')
|
||||
raise ValidationError('功能刀具清单的BOM未配置[整体式刀具]信息,请先配置BOM再开始组装!')
|
||||
return {'options': options, 'handle_ids': handle_ids, 'integral_ids': integral_ids}
|
||||
elif options == '刀柄+刀杆+刀片':
|
||||
if not blade_ids:
|
||||
|
||||
@@ -3,4 +3,4 @@ from odoo import models, fields
|
||||
|
||||
class SyncFunctionalCuttingToolModel(models.Model):
|
||||
_inherit = 'sf.functional.cutting.tool.model'
|
||||
cutting_tool_type_ids = fields.Many2many('sf.cutting.tool.type', string='适用刀具物料类型', required=True)
|
||||
cutting_tool_type_ids = fields.Many2many('sf.cutting.tool.type', string='适用刀具物料类型')
|
||||
@@ -30,13 +30,27 @@ class jikimo_bom(models.Model):
|
||||
return result
|
||||
|
||||
def check_types_in_list(self):
|
||||
# 统计每个元素的类型
|
||||
type_counts = Counter(item.cutting_tool_material_id.name for item in self.product_ids)
|
||||
return all(count > 0 for count in type_counts.values()) and len(type_counts) == self.options.split('+')
|
||||
"""
|
||||
检查产品列表中的元素是否包含了所有指定的类型,并且每种类型至少出现一次。
|
||||
:return: 如果条件满足返回True,否则返回False
|
||||
"""
|
||||
if not self.product_ids:
|
||||
return False
|
||||
try:
|
||||
# 统计每个类型的出现次数
|
||||
type_counts = Counter(item.cutting_tool_material_id.name for item in self.product_ids)
|
||||
|
||||
# 检查是否每种类型的出现次数都大于0,并且类型的数量与选项字符串中的数量相等
|
||||
return all(count > 0 for count in type_counts.values()) and len(type_counts) == len(self.options.split('+'))
|
||||
except AttributeError:
|
||||
# 如果出现属性错误,说明产品列表中的元素可能缺少必要的属性
|
||||
return False
|
||||
# type_counts = Counter(item.cutting_tool_material_id.name for item in self.product_ids)
|
||||
# return all(count > 0 for count in type_counts.values()) and len(type_counts) == self.options.split('+')
|
||||
|
||||
def write(self, vals):
|
||||
# 在更新模型时记录旧的 Many2many ID 列表
|
||||
if 'product_ids' in vals:
|
||||
if 'product_ids' in vals and not self.env.context.get('is_assembly_options'):
|
||||
old_product_counter = Counter(self.product_ids.ids)
|
||||
super(jikimo_bom, self).write(vals)
|
||||
new_product_counter = Counter(self.product_ids.ids)
|
||||
@@ -47,9 +61,15 @@ class jikimo_bom(models.Model):
|
||||
return True
|
||||
else:
|
||||
raise UserError('每种物料最少要有一个')
|
||||
return True
|
||||
return super(jikimo_bom, self).write(vals)
|
||||
|
||||
def bom_product_domains(self, assembly_options):
|
||||
"""
|
||||
根据装配选项生成产品域列表
|
||||
:param assembly_options: 装配选项字符串,各选项以'+'分隔
|
||||
:return: 动态生成的产品搜索条件
|
||||
"""
|
||||
self.options = assembly_options
|
||||
cutting_tool_materials = self.env['sf.cutting.tool.material'].search(
|
||||
[('name', 'in', assembly_options.split('+'))])
|
||||
@@ -64,12 +84,12 @@ class jikimo_bom(models.Model):
|
||||
|
||||
if option.name == '整体式刀具':
|
||||
domain = ['&'] + domain + [
|
||||
'|',
|
||||
'&',
|
||||
# 刀具直径
|
||||
('cutting_tool_blade_diameter', '=', self.tool_inventory_id.diameter),
|
||||
|
||||
# r角
|
||||
('cutting_tool_blade_tip_working_size', '=', self.tool_inventory_id.angle)]
|
||||
('cutting_tool_blade_tip_r_size', '=', self.tool_inventory_id.angle)]
|
||||
if option.name == '刀杆':
|
||||
domain = ['&'] + domain + [
|
||||
("cutting_tool_cutter_arbor_diameter", "=", self.tool_inventory_id.diameter)]
|
||||
@@ -82,23 +102,25 @@ class jikimo_bom(models.Model):
|
||||
domains = domains + domain
|
||||
if index != 0:
|
||||
domains = ['|'] + domains
|
||||
# wqwqwe = self.env['product.product'].search(ddd)
|
||||
# product = self.env['product.product'].search(domain)
|
||||
# if product:
|
||||
# products = products + product
|
||||
domains = domains + [('stock_move_count', '>', 0)]
|
||||
domains = domains + [('stock_move_ids', '!=',False)]
|
||||
return domains
|
||||
|
||||
def generate_bill_materials(self, assembly_options):
|
||||
"""
|
||||
生成物料清单
|
||||
|
||||
根据装配选项生成物料清单,首先获取产品领域,然后搜索相关产品,并设置产品ID。
|
||||
|
||||
:param assembly_options: 组装方式
|
||||
:type assembly_options: 装配选项字符串,各选项以'+'分隔
|
||||
"""
|
||||
domains = self.bom_product_domains(assembly_options)
|
||||
products = self.env['product.product'].search(domains)
|
||||
if products:
|
||||
self.product_ids = [Command.set(products.ids)]
|
||||
# if option.name == '刀盘':
|
||||
# hilt = self.env['product.product'].search(
|
||||
# [('cutting_tool_blade_diameter', '=', self.tool_inventory_id.diameter),
|
||||
# ('cutting_tool_material_id', '=', option.id)])
|
||||
# self.product_ids = [Command.set(hilt.ids)]k
|
||||
new_context = dict(self.env.context)
|
||||
new_context['is_assembly_options'] = True
|
||||
self.with_context(new_context).write({'product_ids': [Command.set(products.ids)]})
|
||||
# self.product_ids = [Command.set(products.ids)]
|
||||
|
||||
|
||||
class jikimo_bom_line(models.Model):
|
||||
@@ -111,15 +133,15 @@ class jikimo_bom_line(models.Model):
|
||||
class ProductProduct(models.Model):
|
||||
_inherit = 'product.product'
|
||||
_order = 'cutting_tool_material_id, cutting_tool_type_id'
|
||||
stock_move_count = fields.Integer(string='stock_move count', compute='_compute_stock_move_count', store=True)
|
||||
|
||||
@api.depends('stock_move_ids')
|
||||
def _compute_stock_move_count(self):
|
||||
for record in self:
|
||||
if record.stock_move_ids:
|
||||
record.stock_move_count = len(record.stock_move_ids)
|
||||
else:
|
||||
record.stock_move_count = 0
|
||||
# stock_move_count = fields.Integer(string='stock_move count', compute='_compute_stock_move_count')
|
||||
#
|
||||
# @api.depends('stock_move_ids')
|
||||
# def _compute_stock_move_count(self):
|
||||
# for record in self:
|
||||
# if record.stock_move_ids:
|
||||
# record.stock_move_count = len(record.stock_move_ids)
|
||||
# else:
|
||||
# record.stock_move_count = 0
|
||||
|
||||
def search(self, args, offset=0, limit=None, order=None, count=False):
|
||||
# 你可以在这里修改 `args` 以调整搜索条件
|
||||
|
||||
@@ -95,16 +95,20 @@ class CNCprocessing(models.Model):
|
||||
# tool_state_remark1 = f'{key}无效刀:{data1.get(key)}'
|
||||
# 无效刀处理逻辑
|
||||
# 1、创建制造订单无效刀检测结果记录
|
||||
logging.info('创建制造订单无效刀检测结果记录!')
|
||||
production_id.detection_result_ids.create({
|
||||
'production_id': production_id.id,
|
||||
'processing_panel': key,
|
||||
'routing_type': 'CNC加工',
|
||||
'rework_reason': 'programming', # 原因:编程(programming)
|
||||
'detailed_reason': '无效功能刀具',
|
||||
'test_results': '返工',
|
||||
'handle_result': '待处理'
|
||||
})
|
||||
if not production_id.detection_result_ids.filtered(
|
||||
lambda a: (a.processing_panel == key and a.detailed_reason == '无效功能刀具'
|
||||
and a.handle_result == '待处理' and a.routing_type == 'CNC加工'
|
||||
and a.rework_reason == 'programming' and a.test_results == '返工')):
|
||||
logging.info('创建制造订单无效刀检测结果记录!')
|
||||
production_id.detection_result_ids.create({
|
||||
'production_id': production_id.id,
|
||||
'processing_panel': key,
|
||||
'routing_type': 'CNC加工',
|
||||
'rework_reason': 'programming', # 原因:编程(programming)
|
||||
'detailed_reason': '无效功能刀具',
|
||||
'test_results': '返工',
|
||||
'handle_result': '待处理'
|
||||
})
|
||||
# 修改当前面装夹预调工单的 is_rework 为 True
|
||||
# work_ids = production_id.workorder_ids.filtered(
|
||||
# lambda a: a.routing_type == '装夹预调' and a.processing_panel == key and not a.is_rework)
|
||||
|
||||
@@ -298,8 +298,8 @@ class SfShelfLocationLot(models.Model):
|
||||
brand_id = fields.Many2one('sf.machine.brand', '品牌', related='product_id.brand_id')
|
||||
|
||||
cutting_tool_blade_diameter = fields.Float('刃部直径(mm)', related='product_id.cutting_tool_blade_diameter')
|
||||
cutting_tool_blade_tip_working_size = fields.Char('刀尖R角(mm)',
|
||||
related='product_id.cutting_tool_blade_tip_working_size')
|
||||
cutting_tool_blade_tip_working_size = fields.Float('刀尖R角(mm)',
|
||||
related='product_id.cutting_tool_blade_tip_r_size')
|
||||
cutting_tool_blade_radius = fields.Char('刀尖圆弧半径(mm)',
|
||||
related='product_id.cutting_tool_blade_tip_circular_arc_radius')
|
||||
cutting_tool_cutter_arbor_diameter = fields.Float('刀杆直径(mm)',
|
||||
|
||||
@@ -14,10 +14,7 @@ class ToolInventory(models.Model):
|
||||
self._bom_mainfest()
|
||||
return self.bom_mainfest()
|
||||
request.session['jikimo_bom_product'] = {'bom_id': int(self.jikimo_bom_ids)}
|
||||
# context = dict(self.env.context)
|
||||
# context.update({'jikimo_bom_product': self.jikimo_bom_ids.options})
|
||||
# if self.functional_cutting_tool_model_id.cutting_tool_type_ids:
|
||||
# context.update({'jikimo_bom_product_cutting_tool_type': self.functional_cutting_tool_model_id.cutting_tool_type_ids.ids})
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': '刀具组装清单',
|
||||
@@ -26,7 +23,6 @@ class ToolInventory(models.Model):
|
||||
'view_id': self.env.ref('sf_tool_management.view_jikimo_bom_form').id,
|
||||
'res_id': int(self.jikimo_bom_ids),
|
||||
'target': 'current', # Use 'new' to open in a new window/tab
|
||||
# {'jikimo_bom_product': self.jikimo_bom_ids.options}
|
||||
}
|
||||
|
||||
# 创建bom单
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<field name="name">jikimo.bom.form</field>
|
||||
<field name="model">jikimo.bom</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<form create="False">
|
||||
<header>
|
||||
<button type="action" name="%(action_jikimo_bom_wizard)d"
|
||||
class="btn btn-info" string="组装方式.." context="{'default_bom_id':id}"
|
||||
@@ -31,7 +31,7 @@
|
||||
<notebook colspan="4">
|
||||
<page string="物料清单">
|
||||
<field name="product_ids" context="{'jikimo_bom_product': True}">
|
||||
<tree>
|
||||
<tree create="False">
|
||||
<field name="name"/>
|
||||
<!-- <field name="categ_id"/>-->
|
||||
<field name="cutting_tool_material_id"/>
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree create="0" export_xlsx="0" delete="0">
|
||||
<header>
|
||||
<button string="确认" name="set_tool_material" type="object"
|
||||
class="treeHeaderBtn"/>
|
||||
<button string="确认" name="set_tool_material" type="object" class="treeHeaderBtn"/>
|
||||
</header>
|
||||
<field name="name"/>
|
||||
<field name="cutting_tool_type_id"/>
|
||||
@@ -62,6 +61,7 @@
|
||||
<field name="brand_id"/>
|
||||
<field name="shelf_location_id"/>
|
||||
<field name="lot_id"/>
|
||||
<field name="qty"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -86,6 +86,7 @@
|
||||
<field name="brand_id"/>
|
||||
<field name="shelf_location_id"/>
|
||||
<field name="lot_id"/>
|
||||
<field name="qty"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -111,6 +112,7 @@
|
||||
<field name="brand_id"/>
|
||||
<field name="shelf_location_id"/>
|
||||
<field name="lot_id"/>
|
||||
<field name="qty"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -135,6 +137,7 @@
|
||||
<field name="brand_id"/>
|
||||
<field name="shelf_location_id"/>
|
||||
<field name="lot_id"/>
|
||||
<field name="qty"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -283,21 +283,22 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree create="0">
|
||||
<field name="name" string="工单编码"/>
|
||||
<field name="cam_procedure_code"/>
|
||||
<field name="filename"/>
|
||||
<field name="programming_no"/>
|
||||
<field name="cam_procedure_code" optional="hide"/>
|
||||
<field name="filename" optional="hide"/>
|
||||
<field name="functional_tool_name" string="刀具名称"/>
|
||||
<field name="cam_cutter_spacing_code"/>
|
||||
<field name="diameter" optional="hide"/>
|
||||
<field name="tool_included_angle" optional="hide"/>
|
||||
<field name="process_type"/>
|
||||
<field name="process_type" optional="hide"/>
|
||||
<field name="margin_x_y"/>
|
||||
<field name="margin_z"/>
|
||||
<field name="finish_depth"/>
|
||||
<field name="extension_length" string="刀具伸出长度(mm)"/>
|
||||
<field name="shank_model"/>
|
||||
<field name="estimated_processing_time"/>
|
||||
<field name="need_knife_time"/>
|
||||
<field name="applicant_time"/>
|
||||
<field name="need_knife_time" optional="hide"/>
|
||||
<field name="applicant_time" optional="hide"/>
|
||||
<field name="plan_execute_status"/>
|
||||
|
||||
<field name="production_line_id" invisible="1"/>
|
||||
@@ -332,6 +333,7 @@
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="programming_no"/>
|
||||
<field name="cam_procedure_code"/>
|
||||
<field name="filename"/>
|
||||
<field name="production_line_id"/>
|
||||
@@ -388,6 +390,7 @@
|
||||
<field name="model">sf.cam.work.order.program.knife.plan</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="programming_no"/>
|
||||
<field name="name" string="工单编码"/>
|
||||
<field name="cam_procedure_code"/>
|
||||
<field name="filename"/>
|
||||
@@ -478,9 +481,9 @@
|
||||
class="btn-primary"/>
|
||||
<button string="确认组装" name="functional_tool_assembly" type="object"
|
||||
attrs="{'invisible': [('assemble_status', 'not in', ['01'])]}"
|
||||
class="btn-primary"/>
|
||||
class="btn-primary jikimo_button_confirm"/>
|
||||
<button name="get_tool_preset_parameter" string="获取测量值"
|
||||
type="object" class="btn-primary"
|
||||
type="object" class="btn-primary jikimo_button_flushed"
|
||||
attrs="{'invisible': [('assemble_status', 'in', ['0','1','2'])]}"
|
||||
/>
|
||||
<field name="assemble_status" widget="statusbar" statusbar_visible="0,01,1"/>
|
||||
@@ -1043,7 +1046,7 @@
|
||||
<field name="res_model">sf.functional.tool.dismantle</field>
|
||||
<field name="view_mode">tree,form,search</field>
|
||||
<field name="search_view_id" ref="sf_functional_tool_dismantle_search"/>
|
||||
<field name="context">{'search_default_no_dismantle_state':1}</field>
|
||||
<field name="context">{'search_default_no_dismantle_state':1,'is_web_request':True}</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -6,10 +6,32 @@
|
||||
<field name="inherit_id" ref="sf_base.view_tool_inventory_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='extension']" position="before">
|
||||
<field name="knife_handle_model" />
|
||||
<field name="knife_handle_model" class="o-sticky-header"/>
|
||||
<button name="bom_mainfest" string="bom清单" type="object" class="btn-link"
|
||||
icon="fa-refresh" />
|
||||
icon="fa-refresh"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_tool_inventory_inherit_search" model="ir.ui.view">
|
||||
<field name="name">sf.tool.inventory.inherit.search</field>
|
||||
<field name="model">sf.tool.inventory</field>
|
||||
<field name="inherit_id" ref="sf_base.view_cutting_tool_inventory_search"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='extension']" position="after">
|
||||
<searchpanel>
|
||||
<field name="functional_cutting_tool_model_id" enable_counters="1"/>
|
||||
<!-- <field name="job_id" enable_counters="1"/>-->
|
||||
<!-- <field name="department_id" enable_counters="1"/>-->
|
||||
<!-- <field name="company_id" enable_counters="1"/>-->
|
||||
</searchpanel>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<!-- <searchpanel>-->
|
||||
<!-- <field name="org_type_id_display" enable_counters="1"/>-->
|
||||
<!-- <!– <field name="job_id" enable_counters="1"/>–>-->
|
||||
<!-- <field name="department_id" enable_counters="1"/>-->
|
||||
<!-- <!– <field name="company_id" enable_counters="1"/>–>-->
|
||||
<!-- </searchpanel>-->
|
||||
</odoo>
|
||||
@@ -8,7 +8,7 @@
|
||||
<field name="inherit_id" ref="sf_base.view_cutter_function_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='name']" position="after">
|
||||
<field name="cutting_tool_type_ids" widget="many2many_tags"/>
|
||||
<field name="cutting_tool_type_ids" widget="many2many_tags" options="{'no_create': True}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -15,9 +15,24 @@ class JikimoBomWizard(models.TransientModel):
|
||||
('刀柄+刀杆+刀片', '刀柄+刀杆+刀片'),
|
||||
('刀柄+刀盘+刀片', '刀柄+刀盘+刀片')
|
||||
], string='组装方式', required=True)
|
||||
# assembly_options_ids = fields.Many2many('sf.cutting.tool.material', string="组装方式")
|
||||
is_ok = fields.Boolean('确认上述信息正确无误。')
|
||||
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
res = super(JikimoBomWizard, self).default_get(fields)
|
||||
# 根据某个字段的值设置默认选项
|
||||
if 'default_bom_id' in self.env.context:
|
||||
jikimo_bom = self.env['jikimo.bom'].browse(self.env.context['default_bom_id'])
|
||||
if not jikimo_bom:
|
||||
return res
|
||||
|
||||
if jikimo_bom.options:
|
||||
res['assembly_options'] = jikimo_bom.options
|
||||
# some_field_value = self.env.context.get('some_field')
|
||||
# if some_field_value == 'condition_value':
|
||||
# res['default_option'] = 'option2' # 设置为特定选项
|
||||
return res
|
||||
def submit(self):
|
||||
if not self.bom_id:
|
||||
raise UserError('缺少bom信息')
|
||||
|
||||
@@ -931,13 +931,6 @@ class SfStockMoveLine(models.Model):
|
||||
if not record.destination_location_id.product_id:
|
||||
record.destination_location_id.product_id = record.product_id.id
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
|
||||
records = super(SfStockMoveLine, self).create(vals_list)
|
||||
self.put_shelf_location(records)
|
||||
return records
|
||||
|
||||
|
||||
class SfStockPicking(models.Model):
|
||||
_inherit = 'stock.picking'
|
||||
@@ -1122,6 +1115,12 @@ class SfPickingType(models.Model):
|
||||
'sf_warehouse.group_sf_stock_manager'
|
||||
)
|
||||
|
||||
def _get_action(self, action_xmlid):
|
||||
action = super(SfPickingType, self)._get_action(action_xmlid)
|
||||
if not self.env.user.has_group('base.group_system'):
|
||||
action['context']['create'] = False
|
||||
return action
|
||||
|
||||
|
||||
class CustomStockMove(models.Model):
|
||||
_name = 'stock.move'
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
<record model="ir.actions.act_window" id="stock.stock_picking_type_action">
|
||||
<field name="context">{'search_default_groupby_code':1}</field>
|
||||
<field name="domain">[('name', '!=', '制造')]</field>
|
||||
</record>
|
||||
|
||||
<record id="view_location_form_sf_inherit" model="ir.ui.view">
|
||||
|
||||
@@ -77,18 +77,20 @@ class ShelfLocationWizard(models.TransientModel):
|
||||
|
||||
def confirm_the_change(self):
|
||||
if self.destination_barcode_id:
|
||||
stocks = []
|
||||
if self.lot_id:
|
||||
self.current_barcode_id.product_sn_id = False
|
||||
self.destination_barcode_id.product_sn_id = self.lot_id.id
|
||||
self.create_stock_moves(self.lot_id, 1)
|
||||
stocks = self.create_stock_moves(self.lot_id, 1)
|
||||
elif self.current_product_sn_ids:
|
||||
for current_product_sn_id in self.current_product_sn_ids:
|
||||
self.create_stock_moves(current_product_sn_id.lot_id, current_product_sn_id.qty_num)
|
||||
stocks = self.create_stock_moves(current_product_sn_id.lot_id, current_product_sn_id.qty_num)
|
||||
current_product_sn_id.write({
|
||||
'qty_num': 0
|
||||
})
|
||||
else:
|
||||
raise ValidationError('没有需要变更的批次/序列号!')
|
||||
self.env['stock.move.line'].sudo().put_shelf_location(stocks[-1])
|
||||
else:
|
||||
raise ValidationError('请选择目标货位编码!')
|
||||
|
||||
|
||||
138
sg_wechat_enterprise/.gitignore
vendored
138
sg_wechat_enterprise/.gitignore
vendored
@@ -1,138 +0,0 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
@@ -1,3 +0,0 @@
|
||||
from . import we_api
|
||||
from . import models
|
||||
from . import controllers
|
||||
@@ -1,52 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
{
|
||||
'name': '企业微信',
|
||||
'version': '0.1',
|
||||
'summary': '企业通讯录\消息处理\企业应用\无缝登录',
|
||||
'sequence': 30,
|
||||
"author": 'SmartGo Studio.,',
|
||||
'description': '''用于企业内部员工的管理,
|
||||
ER企业微信模块
|
||||
=====================================================
|
||||
主要针对odoo使用微信进行管理,包括以下功能:
|
||||
1) 公众号信息管理(企业号下多applicaiton管理)
|
||||
2) 接收消息处理
|
||||
3) 发送消息处理
|
||||
4) 自定义菜单处理
|
||||
|
||||
....
|
||||
本安装包使用了WechatEnterpriseSDK/wechat_sdk.py,在此表示感谢。
|
||||
源代码可以访问github.地址如下:https://github.com/facert/WechatEnterpriseSDK
|
||||
|
||||
''',
|
||||
'category': '基础信息',
|
||||
'website': 'https://www.smartgo.cn',
|
||||
'depends': ['base', 'mail','hr'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'views/we_config_view.xml',
|
||||
'views/we_app_view.xml',
|
||||
'views/we_send_message_view.xml',
|
||||
'views/we_receive_message_view.xml',
|
||||
'views/we_message_process_view.xml',
|
||||
'views/we_templates.xml',
|
||||
# "views/mail_view.xml",
|
||||
"views/res_users_view.xml",
|
||||
'views/menu_view.xml',
|
||||
# 'views/we_menu.xml',
|
||||
"data/data.xml"
|
||||
],
|
||||
'demo': [
|
||||
'demo/we_config_demo.xml',
|
||||
],
|
||||
'qweb': [
|
||||
# "static/src/xml/base.xml",
|
||||
# "static/src/xml/account_payment.xml",
|
||||
# "static/src/xml/account_report_backend.xml",
|
||||
],
|
||||
'installable': True,
|
||||
'application': True,
|
||||
'auto_install': False,
|
||||
# 'post_init_hook': '_auto_install_l10n',
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- encoding:utf-8 -*-
|
||||
|
||||
""" 对企业微信发送给企业后台的消息加解密示例代码.
|
||||
@copyright: Copyright (c) 1998-2014 Tencent Inc.
|
||||
|
||||
"""
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
import base64
|
||||
import string
|
||||
import random
|
||||
import hashlib
|
||||
import time
|
||||
import struct
|
||||
from Crypto.Cipher import AES
|
||||
import xml.etree.cElementTree as ET
|
||||
import socket
|
||||
from . import ierror
|
||||
|
||||
"""
|
||||
关于Crypto.Cipher模块,ImportError: No module named 'Crypto'解决方案
|
||||
请到官方网站 https://www.dlitz.net/software/pycrypto/ 下载pycrypto。
|
||||
下载后,按照README中的“Installation”小节的提示进行pycrypto安装。
|
||||
"""
|
||||
|
||||
|
||||
class FormatException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def throw_exception(message, exception_class=FormatException):
|
||||
"""my define raise exception function"""
|
||||
raise exception_class(message)
|
||||
|
||||
|
||||
class SHA1:
|
||||
"""计算企业微信的消息签名接口"""
|
||||
|
||||
def getSHA1(self, token, timestamp, nonce, encrypt):
|
||||
"""用SHA1算法生成安全签名
|
||||
@param token: 票据
|
||||
@param timestamp: 时间戳
|
||||
@param encrypt: 密文
|
||||
@param nonce: 随机字符串
|
||||
@return: 安全签名
|
||||
"""
|
||||
try:
|
||||
sortlist = [token, timestamp, nonce, encrypt]
|
||||
sortlist.sort()
|
||||
sha = hashlib.sha1()
|
||||
sort_str = "".join(sortlist)
|
||||
sha.update(sort_str.encode('utf-8'))
|
||||
return ierror.WXBizMsgCrypt_OK, sha.hexdigest()
|
||||
except Exception as e:
|
||||
return ierror.WXBizMsgCrypt_ComputeSignature_Error, None
|
||||
|
||||
|
||||
class XMLParse:
|
||||
"""提供提取消息格式中的密文及生成回复消息格式的接口"""
|
||||
|
||||
# xml消息模板
|
||||
AES_TEXT_RESPONSE_TEMPLATE = """<xml>
|
||||
<Encrypt><![CDATA[%(msg_encrypt)s]]></Encrypt>
|
||||
<MsgSignature><![CDATA[%(msg_signaturet)s]]></MsgSignature>
|
||||
<TimeStamp>%(timestamp)s</TimeStamp>
|
||||
<Nonce><![CDATA[%(nonce)s]]></Nonce>
|
||||
</xml>"""
|
||||
|
||||
def extract(self, xmltext):
|
||||
"""提取出xml数据包中的加密消息
|
||||
@param xmltext: 待提取的xml字符串
|
||||
@return: 提取出的加密消息字符串
|
||||
"""
|
||||
try:
|
||||
xml_tree = ET.fromstring(xmltext)
|
||||
encrypt = xml_tree.find("Encrypt")
|
||||
touser_name = xml_tree.find("ToUserName")
|
||||
return ierror.WXBizMsgCrypt_OK, encrypt.text, touser_name.text
|
||||
except Exception as e:
|
||||
return ierror.WXBizMsgCrypt_ParseXml_Error, None, None
|
||||
|
||||
def generate(self, encrypt, signature, timestamp, nonce):
|
||||
"""生成xml消息
|
||||
@param encrypt: 加密后的消息密文
|
||||
@param signature: 安全签名
|
||||
@param timestamp: 时间戳
|
||||
@param nonce: 随机字符串
|
||||
@return: 生成的xml字符串
|
||||
"""
|
||||
resp_dict = {
|
||||
'msg_encrypt': encrypt,
|
||||
'msg_signaturet': signature,
|
||||
'timestamp': timestamp,
|
||||
'nonce': nonce,
|
||||
}
|
||||
resp_xml = self.AES_TEXT_RESPONSE_TEMPLATE % resp_dict
|
||||
return resp_xml
|
||||
|
||||
|
||||
class PKCS7Encoder():
|
||||
"""提供基于PKCS7算法的加解密接口"""
|
||||
|
||||
block_size = 32
|
||||
|
||||
def encode(self, text):
|
||||
""" 对需要加密的明文进行填充补位
|
||||
@param text: 需要进行填充补位操作的明文
|
||||
@return: 补齐明文字符串
|
||||
"""
|
||||
text_length = len(text)
|
||||
# 计算需要填充的位数
|
||||
amount_to_pad = self.block_size - (text_length % self.block_size)
|
||||
if amount_to_pad == 0:
|
||||
amount_to_pad = self.block_size
|
||||
# 获得补位所用的字符
|
||||
pad = chr(amount_to_pad)
|
||||
return text + pad * amount_to_pad
|
||||
|
||||
def decode(self, decrypted):
|
||||
"""删除解密后明文的补位字符
|
||||
@param decrypted: 解密后的明文
|
||||
@return: 删除补位字符后的明文
|
||||
"""
|
||||
pad = ord(decrypted[-1])
|
||||
if pad < 1 or pad > 32:
|
||||
pad = 0
|
||||
return decrypted[:-pad]
|
||||
|
||||
|
||||
class Prpcrypt(object):
|
||||
"""提供接收和推送给企业微信消息的加解密接口"""
|
||||
|
||||
def __init__(self, key):
|
||||
|
||||
# self.key = base64.b64decode(key+"=")
|
||||
self.key = key
|
||||
# 设置加解密模式为AES的CBC模式
|
||||
self.mode = AES.MODE_CBC
|
||||
|
||||
def encrypt(self, text, corpid):
|
||||
"""对明文进行加密
|
||||
@param text: 需要加密的明文
|
||||
@return: 加密得到的字符串
|
||||
"""
|
||||
# 16位随机字符串添加到明文开头
|
||||
text = self.get_random_str() + str(struct.pack("I", socket.htonl(len(text))), encoding='utf8')\
|
||||
+ str(text, encoding='utf8') + corpid
|
||||
# 使用自定义的填充方式对明文进行补位填充
|
||||
pkcs7 = PKCS7Encoder()
|
||||
text = pkcs7.encode(text)
|
||||
# 加密
|
||||
cryptor = AES.new(self.key, self.mode, self.key[:16])
|
||||
try:
|
||||
ciphertext = cryptor.encrypt(text)
|
||||
# 使用BASE64对加密后的字符串进行编码
|
||||
return ierror.WXBizMsgCrypt_OK, base64.b64encode(ciphertext)
|
||||
except Exception as e:
|
||||
return ierror.WXBizMsgCrypt_EncryptAES_Error, None
|
||||
|
||||
def decrypt(self, text, corpid):
|
||||
"""对解密后的明文进行补位删除
|
||||
@param text: 密文
|
||||
@return: 删除填充补位后的明文
|
||||
"""
|
||||
try:
|
||||
cryptor = AES.new(self.key, self.mode, self.key[:16])
|
||||
# 使用BASE64对密文进行解码,然后AES-CBC解密
|
||||
plain_text = cryptor.decrypt(base64.b64decode(text))
|
||||
except Exception as e:
|
||||
return ierror.WXBizMsgCrypt_DecryptAES_Error, None
|
||||
try:
|
||||
pad = plain_text[-1]
|
||||
# 去掉补位字符串
|
||||
# pkcs7 = PKCS7Encoder()
|
||||
# plain_text = pkcs7.encode(plain_text)
|
||||
# 去除16位随机字符串
|
||||
content = plain_text[16:-pad]
|
||||
xml_len = socket.ntohl(struct.unpack("I", content[: 4])[0])
|
||||
xml_content = content[4: xml_len + 4]
|
||||
from_corpid = content[xml_len + 4:]
|
||||
except Exception as e:
|
||||
return ierror.WXBizMsgCrypt_IllegalBuffer, None
|
||||
if str(from_corpid, encoding="utf8") != corpid and len(ET.fromstring(xml_content).findall("SuiteId")) < 1:
|
||||
return ierror.WXBizMsgCrypt_ValidateCorpid_Error, None
|
||||
return 0, xml_content
|
||||
|
||||
def get_random_str(self):
|
||||
""" 随机生成16位字符串
|
||||
@return: 16位字符串
|
||||
"""
|
||||
rule = string.ascii_letters + string.digits
|
||||
str = random.sample(rule, 16)
|
||||
return "".join(str)
|
||||
|
||||
|
||||
class WXBizMsgCrypt(object):
|
||||
# 构造函数
|
||||
# @param sToken: 企业微信后台,开发者设置的Token
|
||||
# @param sEncodingAESKey: 企业微信后台,开发者设置的EncodingAESKey
|
||||
# @param sCorpId: 企业号的CorpId
|
||||
def __init__(self, sToken, sEncodingAESKey, sCorpId):
|
||||
try:
|
||||
self.key = base64.b64decode(sEncodingAESKey + "=")
|
||||
assert len(self.key) == 32
|
||||
except:
|
||||
throw_exception("[error]: EncodingAESKey unvalid !", FormatException)
|
||||
# return ierror.WXBizMsgCrypt_IllegalAesKey,None
|
||||
self.m_sToken = sToken
|
||||
self.m_sCorpid = sCorpId
|
||||
|
||||
# 验证URL
|
||||
# @param sMsgSignature: 签名串,对应URL参数的msg_signature
|
||||
# @param sTimeStamp: 时间戳,对应URL参数的timestamp
|
||||
# @param sNonce: 随机串,对应URL参数的nonce
|
||||
# @param sEchoStr: 随机串,对应URL参数的echostr
|
||||
# @param sReplyEchoStr: 解密之后的echostr,当return返回0时有效
|
||||
# @return:成功0,失败返回对应的错误码
|
||||
|
||||
def VerifyURL(self, sMsgSignature, sTimeStamp, sNonce, sEchoStr):
|
||||
sha1 = SHA1()
|
||||
ret, signature = sha1.getSHA1(self.m_sToken, sTimeStamp, sNonce, sEchoStr)
|
||||
if ret != 0:
|
||||
return ret, None
|
||||
if not signature == sMsgSignature:
|
||||
return ierror.WXBizMsgCrypt_ValidateSignature_Error, None
|
||||
pc = Prpcrypt(self.key)
|
||||
ret, sReplyEchoStr = pc.decrypt(sEchoStr, self.m_sCorpid)
|
||||
return ret, sReplyEchoStr
|
||||
|
||||
def EncryptMsg(self, sReplyMsg, sNonce, timestamp=None):
|
||||
# 将企业回复用户的消息加密打包
|
||||
# @param sReplyMsg: 企业号待回复用户的消息,xml格式的字符串
|
||||
# @param sTimeStamp: 时间戳,可以自己生成,也可以用URL参数的timestamp,如为None则自动用当前时间
|
||||
# @param sNonce: 随机串,可以自己生成,也可以用URL参数的nonce
|
||||
# sEncryptMsg: 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串,
|
||||
# return:成功0,sEncryptMsg,失败返回对应的错误码None
|
||||
pc = Prpcrypt(self.key)
|
||||
ret, encrypt = pc.encrypt(sReplyMsg, self.m_sCorpid)
|
||||
if ret != 0:
|
||||
return ret, None
|
||||
if timestamp is None:
|
||||
timestamp = str(int(time.time()))
|
||||
# 生成安全签名
|
||||
sha1 = SHA1()
|
||||
ret, signature = sha1.getSHA1(self.m_sToken, timestamp, sNonce, encrypt)
|
||||
if ret != 0:
|
||||
return ret, None
|
||||
xmlParse = XMLParse()
|
||||
return ret, xmlParse.generate(encrypt, signature, timestamp, sNonce)
|
||||
|
||||
def DecryptMsg(self, sPostData, sMsgSignature, sTimeStamp, sNonce):
|
||||
# 检验消息的真实性,并且获取解密后的明文
|
||||
# @param sMsgSignature: 签名串,对应URL参数的msg_signature
|
||||
# @param sTimeStamp: 时间戳,对应URL参数的timestamp
|
||||
# @param sNonce: 随机串,对应URL参数的nonce
|
||||
# @param sPostData: 密文,对应POST请求的数据
|
||||
# xml_content: 解密后的原文,当return返回0时有效
|
||||
# @return: 成功0,失败返回对应的错误码
|
||||
# 验证安全签名
|
||||
xmlParse = XMLParse()
|
||||
ret, encrypt, touser_name = xmlParse.extract(sPostData)
|
||||
if ret != 0:
|
||||
return ret, None
|
||||
sha1 = SHA1()
|
||||
ret, signature = sha1.getSHA1(self.m_sToken, sTimeStamp, sNonce, encrypt)
|
||||
if ret != 0:
|
||||
return ret, None
|
||||
if not signature == sMsgSignature:
|
||||
return ierror.WXBizMsgCrypt_ValidateSignature_Error, None
|
||||
pc = Prpcrypt(self.key)
|
||||
ret, xml_content = pc.decrypt(encrypt, self.m_sCorpid)
|
||||
return ret, xml_content
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
from . import wechat_enterprise
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#########################################################################
|
||||
# Author: jonyqin
|
||||
# Created Time: Thu 11 Sep 2014 01:53:58 PM CST
|
||||
# File Name: ierror.py
|
||||
# Description:定义错误码含义
|
||||
#########################################################################
|
||||
WXBizMsgCrypt_OK = 0
|
||||
WXBizMsgCrypt_ValidateSignature_Error = -40001
|
||||
WXBizMsgCrypt_ParseXml_Error = -40002
|
||||
WXBizMsgCrypt_ComputeSignature_Error = -40003
|
||||
WXBizMsgCrypt_IllegalAesKey = -40004
|
||||
WXBizMsgCrypt_ValidateCorpid_Error = -40005
|
||||
WXBizMsgCrypt_EncryptAES_Error = -40006
|
||||
WXBizMsgCrypt_DecryptAES_Error = -40007
|
||||
WXBizMsgCrypt_IllegalBuffer = -40008
|
||||
WXBizMsgCrypt_EncodeBase64_Error = -40009
|
||||
WXBizMsgCrypt_DecodeBase64_Error = -40010
|
||||
WXBizMsgCrypt_GenReturnXml_Error = -40011
|
||||
@@ -1,231 +0,0 @@
|
||||
import time
|
||||
import functools
|
||||
import base64
|
||||
from json import *
|
||||
|
||||
from odoo import http, fields
|
||||
from odoo.http import request
|
||||
from . import WXBizMsgCrypt
|
||||
from werkzeug.exceptions import abort
|
||||
import xml.etree.cElementTree as Et
|
||||
import requests as req
|
||||
|
||||
import logging
|
||||
from lxml import etree
|
||||
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def wechat_login(func):
|
||||
"""
|
||||
用来根据userid取得合作伙伴的id
|
||||
:return:
|
||||
"""
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kw):
|
||||
# now_time = time.time()
|
||||
# request.session['session_time'] = time.time()
|
||||
# if 'session_time' not in request.session:
|
||||
# request.session['session_time'] = 1
|
||||
# if now_time > request.session['session_time'] + 350:
|
||||
_logger.info(u"没进来之前的kw值为%s" % JSONEncoder().encode(kw))
|
||||
# if not request.session['login'] or not request.session['login'] or request.session[
|
||||
# 'login'] == "public":
|
||||
enterprise, agent_id = request.env['we.config'].sudo().get_odoo_wechat()
|
||||
if enterprise and agent_id:
|
||||
if 'code' in kw and 'state' in kw: # 检查是否取得了code
|
||||
if 'kw' in request.session.keys():
|
||||
_logger.info('code:%s' % kw['code'])
|
||||
account = enterprise.oauth.get_user_info(code=kw['code'])
|
||||
_logger.info('account:%s' % account)
|
||||
if account: # 是否取得了微信企业号通讯录的账号
|
||||
user_detail = enterprise.user.get_detail(account['user_ticket'])
|
||||
_logger.info('user_detail:%s' % user_detail)
|
||||
request.env['we.employee'].we_privacy_update(user_detail)
|
||||
user = request.env['res.users'].sudo().search(
|
||||
[('we_employee_id', '=', account['UserId'])])
|
||||
_logger.info('user:%s' % user)
|
||||
if user: # 是否取得了用户
|
||||
if 'state' in kw:
|
||||
state = base64.b64encode(kw['state'].encode('utf-8')).decode()
|
||||
kw['state'] = state
|
||||
uid = request.session.authenticate(request.session.db, user.login,
|
||||
account['UserId'])
|
||||
kw['user_id'] = uid
|
||||
request.session['session_time'] = time.time()
|
||||
request.session['login'] = user.login
|
||||
|
||||
_logger.info(u"进来之后的kw值为%s" % kw)
|
||||
return func(*args, **kw)
|
||||
else:
|
||||
_logger.warning(u'用户不存在.')
|
||||
return request.render('sg_wechat_enterprise.wechat_warning',
|
||||
{'title': u'警告', 'content': u'该员工的未配置登录用户.'})
|
||||
else:
|
||||
_logger.warning(u'微信企业号验证失败.')
|
||||
return request.render('sg_wechat_enterprise.wechat_warning',
|
||||
{'title': u'警告', 'content': u'微信企业号验证失败.'})
|
||||
else:
|
||||
# 返回时候进入的地方
|
||||
del kw['code']
|
||||
del kw['state']
|
||||
request.session['kw'] = base64.b64encode(JSONEncoder().encode(kw).encode('utf-8')).decode()
|
||||
if len(kw) == 0:
|
||||
base_url = request.httprequest.base_url
|
||||
else:
|
||||
base_url = request.httprequest.base_url + '?'
|
||||
for item, value in kw.items():
|
||||
base_url += item + '=' + value + "&"
|
||||
base_url = base_url[: -1]
|
||||
|
||||
url = enterprise.oauth.authorize_url(base_url,
|
||||
state=base64.b64encode(
|
||||
JSONEncoder().encode(kw).encode('utf-8')).decode(),
|
||||
agent_id=agent_id,
|
||||
scope='snsapi_privateinfo')
|
||||
_logger.warning(u"这是授权的url:" + url)
|
||||
value = {"url": url}
|
||||
return request.render("sg_wechat_enterprise.Transfer", value)
|
||||
else: # 开始微信企业号登录认证
|
||||
request.session['kw'] = base64.b64encode(JSONEncoder().encode(kw).encode('utf-8')).decode()
|
||||
|
||||
if len(kw) == 0:
|
||||
base_url = request.httprequest.base_url
|
||||
else:
|
||||
base_url = request.httprequest.base_url + '?'
|
||||
for item, value in kw.items():
|
||||
base_url += item + '=' + value + "&"
|
||||
base_url = base_url[: -1]
|
||||
url = enterprise.oauth.authorize_url(base_url,
|
||||
state=base64.b64encode(
|
||||
JSONEncoder().encode(kw).encode('utf-8')).decode(),
|
||||
agent_id=agent_id,
|
||||
scope='snsapi_privateinfo'
|
||||
)
|
||||
_logger.warning(u"这是授权的url:" + url)
|
||||
value = {"url": url}
|
||||
return request.render("sg_wechat_enterprise.Transfer", value)
|
||||
else:
|
||||
_logger.warning(u'微信企业号初始化失败.')
|
||||
return request.render('sg_wechat_enterprise.wechat_warning',
|
||||
{'title': u'警告', 'content': u'微信企业号初始化失败.'})
|
||||
|
||||
# return func(*args, **kw)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
class WechatEnterprise(http.Controller):
|
||||
"""
|
||||
用于接收微信发过来的任何消息,并转发给相应的业务类进行处理
|
||||
"""
|
||||
__check_str = 'NDOEHNDSY#$_@$JFDK:Q{!'
|
||||
BASE_URL = '/we'
|
||||
|
||||
@wechat_login
|
||||
@http.route(BASE_URL + '/auth', type='http', auth='none')
|
||||
def auth(self, *args, **kw):
|
||||
"""
|
||||
企业微信免登认证
|
||||
"""
|
||||
try:
|
||||
# user_id = (request.session['uid'])
|
||||
redirect1 = kw['redirect'] if 'redirect' in kw else None
|
||||
uid = kw['user_id'] if 'redirect' in kw else None
|
||||
_logger.info('user_id %s', uid)
|
||||
if uid is not False:
|
||||
request.params['login_success'] = True
|
||||
if not redirect1:
|
||||
redirect1 = '/web'
|
||||
redirect1 = redirect1.replace('-', '&').replace('?', '#')
|
||||
logging.info('url:%s' % redirect1)
|
||||
return request.redirect(redirect1)
|
||||
except Exception as ex:
|
||||
_logger.error('无有效的登录凭证.')
|
||||
_logger.warning('auth exceptions:%s' % ex)
|
||||
return request.render('sg_wechat_enterprise.wechat_warning',
|
||||
{'title': u'警告', 'content': u'无有效的登录凭证.'})
|
||||
|
||||
@http.route('/WechatEnterprise/<string:code>/api', type='http', auth="public", methods=["GET", "POST"], csrf=False)
|
||||
def process(self, code, **kwargs):
|
||||
"""
|
||||
处理从微信服务器发送过来的请求
|
||||
:param code: 自定义代码
|
||||
:param kwargs: 包含 (msg_signature, timestamp, nonce, echostr) 等参数
|
||||
:return:
|
||||
"""
|
||||
_logger.info(u'处理从微信服务器发送过来的请求code: %s, kwargs: %s' % (code, kwargs))
|
||||
app_id = request.env['we.app'].sudo().search([('code', '=', code)], limit=1)
|
||||
if not app_id:
|
||||
_logger.warning(u'Can not find wechat app by code: {code}')
|
||||
abort(403)
|
||||
corp_id = app_id.enterprise_id
|
||||
we_chat_cpt = WXBizMsgCrypt.WXBizMsgCrypt(app_id.Token, app_id.EncodingAESKey, corp_id.corp_id)
|
||||
signature, timestamp, nonce = kwargs['msg_signature'], kwargs['timestamp'], kwargs['nonce']
|
||||
if kwargs.get('echostr'):
|
||||
echo_string = kwargs['echostr']
|
||||
sort_list = [app_id.Token, timestamp, nonce, echo_string]
|
||||
if request.env['we.tools'].sudo(). \
|
||||
check_message_signature(message_list=sort_list, msg_signature=signature):
|
||||
ret, signature_echo_string = we_chat_cpt.VerifyURL(signature, timestamp, nonce, echo_string)
|
||||
if ret == 0:
|
||||
return str(signature_echo_string, encoding="utf8")
|
||||
body_text = request.httprequest.data
|
||||
ret, signature_message = we_chat_cpt.DecryptMsg(body_text, signature, timestamp, nonce)
|
||||
xml_tree = Et.fromstring(signature_message)
|
||||
if len(xml_tree.findall("SuiteId")) > 0:
|
||||
return "success"
|
||||
if len(xml_tree.find("ApprovalInfo")) > 0:
|
||||
xmlstr = etree.fromstring(signature_message)
|
||||
# data = xml2json_from_elementtree(xmlstr)
|
||||
return request.env['we.receive.message'].sudo().sys_approval_change(xml_tree.find("ApprovalInfo"))
|
||||
data = {
|
||||
'MsgType': xml_tree.find("MsgType").text,
|
||||
# 'AgentID': xml_tree.find("AgentID").text,
|
||||
'ToUserName': xml_tree.find("ToUserName").text,
|
||||
'FromUserName': xml_tree.find("FromUserName").text,
|
||||
'CreateTime': xml_tree.find("CreateTime").text
|
||||
}
|
||||
if xml_tree.find("AgentID") != None:
|
||||
data['AgentID'] = xml_tree.find("AgentID").text
|
||||
if data["MsgType"] == "text":
|
||||
data["Content"] = xml_tree.find("Content").text
|
||||
data["MsgId"] = xml_tree.find("MsgId").text
|
||||
if data["MsgType"] == "image":
|
||||
data["PicUrl"] = xml_tree.find("PicUrl").text
|
||||
data["MediaId"] = xml_tree.find("MediaId").text
|
||||
data["MsgId"] = xml_tree.find("MsgId").text
|
||||
if data["MsgType"] == "voice":
|
||||
data["MediaId"] = xml_tree.find("MediaId").text
|
||||
data["Format"] = xml_tree.find("Format").text
|
||||
data["MsgId"] = xml_tree.find("MsgId").text
|
||||
if data["MsgType"] == "video" or data["MsgType"] == "shortvideo":
|
||||
data["MediaId"] = xml_tree.find("MediaId").text
|
||||
data["ThumbMediaId"] = xml_tree.find("ThumbMediaId").text
|
||||
data["MsgId"] = xml_tree.find("MsgId").text
|
||||
if data["MsgType"] == "location":
|
||||
data["Location_X"] = xml_tree.find("Location_X").text
|
||||
data["Location_Y"] = xml_tree.find("Location_Y").text
|
||||
data["Scale"] = xml_tree.find("Scale").text
|
||||
data["Label"] = xml_tree.find("Label").text
|
||||
data["MsgId"] = xml_tree.find("MsgId").text
|
||||
if data["MsgType"] == "link":
|
||||
data["Title"] = xml_tree.find("Title").text
|
||||
data["Description"] = xml_tree.find("Description").text
|
||||
data["PicUrl"] = xml_tree.find("PicUrl").text
|
||||
data["MsgId"] = xml_tree.find("MsgId").text
|
||||
if data["MsgType"] == "event":
|
||||
if xml_tree.find("Event") == "subscribe" or xml_tree.find("Event") == "unsubscribe":
|
||||
data["Event"] = xml_tree.find("Event").text
|
||||
else:
|
||||
ret, signature_message = we_chat_cpt.EncryptMsg(signature_message, nonce, timestamp)
|
||||
return signature_message
|
||||
request.env['we.receive.message'].sudo().process_message(data)
|
||||
return ''
|
||||
|
||||
@http.route('/WechatEnterprise/transfer', type='http', auth="public", methods=["POST", "GET"], csrf=False)
|
||||
def transfer(self, url):
|
||||
value = {"url": url}
|
||||
return request.render('sg_wechat_enterprise.Transfer', value)
|
||||
@@ -1,16 +0,0 @@
|
||||
<odoo>
|
||||
<data>
|
||||
<record model="ir.cron" id="ir_cron_dingtalk_accesstoken">
|
||||
<field name="name">SNS Message To 企业微信</field>
|
||||
<field name="model_id" ref="mail.model_mail_message"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model.send_we_message()</field>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="main_wechat_enterprise" model="we.config">
|
||||
<field name="name">SmartGo</field>
|
||||
<field name="corp_id">wwad4f2c227d490637</field>
|
||||
<field name="corp_secret">kq_AzJN1FoPdWjyEwAQs_cqzJhALmKhmwYMBQyJzuEs</field>
|
||||
<field name="company_id">1</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
from . import we_app
|
||||
from . import we_conf
|
||||
from . import we_send_message
|
||||
from . import we_receive_message
|
||||
from . import we_receive_message_process
|
||||
from . import res_users
|
||||
from . import we_tools
|
||||
from . import mail
|
||||
from . import client
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user