Compare commits

...

296 Commits

Author SHA1 Message Date
禹翔辉
3686c0e070 Merge branch refs/heads/develop into refs/heads/release/release_2.1 2024-07-25 15:25:07 +08:00
杨金灵
536d7e9e79 Accept Merge Request #1177: (feature/修复返工-工件配送 -> develop)
Merge Request: 修复返工-工件配送

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1177?initial=true
2024-07-25 15:24:59 +08:00
jinling.yang
9eddfe36d6 修复返工-工件配送 2024-07-25 15:22:48 +08:00
马广威
709f29185d Accept Merge Request #1175: (feature/优化制造功能 -> develop)
Merge Request: 设备日志、oee优化

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1175?initial=true
2024-07-25 14:06:38 +08:00
mgw
886de3195c Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化制造功能 2024-07-25 14:05:49 +08:00
mgw
d9e5ffa68d 设备日志、oee优化 2024-07-25 14:05:35 +08:00
jinling.yang
030a900580 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-25 10:39:10 +08:00
jinling.yang
4370236edf Merge branch 'feature/修复中控对接接口' into develop 2024-07-25 10:35:41 +08:00
杨金灵
e01209f343 Accept Merge Request #1174: (feature/修复中控对接接口 -> develop)
Merge Request: 修复中控对接接口

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1174?initial=true
2024-07-25 10:34:29 +08:00
jinling.yang
b4803f03fa 修复制造订单完成生产数量问题 2024-07-25 10:34:12 +08:00
jinling.yang
c9d4f0667a 修复中控对接接口 2024-07-25 10:27:07 +08:00
杨金灵
30c823189c Accept Merge Request #1172: (feature/修复更新程序 -> develop)
Merge Request: 修复更新程序

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1172?initial=true
2024-07-24 17:16:28 +08:00
jinling.yang
3ed503391e 修复更新程序 2024-07-24 17:10:35 +08:00
杨金灵
befe0c2b00 Accept Merge Request #1171: (feature/修复批量更新程序 -> develop)
Merge Request: 修复批量更新程序

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1171?initial=true
2024-07-24 16:40:48 +08:00
jinling.yang
44a9ad340b 修复批量更新程序 2024-07-24 16:39:38 +08:00
jinling.yang
55bac0020f Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-24 16:34:21 +08:00
禹翔辉
d3124bb9fe Accept Merge Request #1170: (feature/修复工单cnc校验bug_2 -> develop)
Merge Request: 根据制造订单功能刀具状态变更第一个装夹预调工单的工单状态

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1170
2024-07-24 16:24:13 +08:00
yuxianghui
64fd6ef5dc Merge branch 'feature/修复工单cnc校验bug_1' into feature/修复工单cnc校验bug_2 2024-07-24 16:21:40 +08:00
yuxianghui
001b2ead57 1、更具制造订单功能刀具状态变更第一个装夹预调工单的工单状态 2024-07-24 16:20:48 +08:00
mgw
d0be097a9b 设备加工参数等页面修改 2024-07-24 16:19:29 +08:00
jinling.yang
0be8d0fbb2 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-24 15:09:57 +08:00
jinling.yang
71faa8a835 Merge branch 'feature/修复工件配送按钮显示' into develop 2024-07-24 15:09:49 +08:00
杨金灵
12afc1ef83 Accept Merge Request #1169: (feature/修复工件配送按钮显示 -> develop)
Merge Request: 修复工件配送按钮显示

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1169?initial=true
2024-07-24 15:08:49 +08:00
jinling.yang
34624e6592 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/修复工件配送按钮显示 2024-07-24 15:07:05 +08:00
jinling.yang
a16effd8db Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-24 15:06:58 +08:00
jinling.yang
f59299d4d5 修复工件配送按钮显示 2024-07-24 15:06:50 +08:00
禹翔辉
c7cef27ad6 Accept Merge Request #1168: (feature/修复工单cnc校验bug_1 -> develop)
Merge Request: 工单开始时增加新校验

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1168?initial=true
2024-07-24 12:38:21 +08:00
yuxianghui
b88b0fc38a Merge branch 'feature/修复工单cnc校验bug' into feature/修复工单cnc校验bug_1
# Conflicts:
#	sf_tool_management/models/mrp_workorder.py
2024-07-24 12:35:31 +08:00
yuxianghui
4297d41dda 添加工单开始校验 2024-07-24 12:32:33 +08:00
马广威
3f85d058cc Accept Merge Request #1167: (feature/优化制造功能 -> develop)
Merge Request: 修复“同一个产品多个制造订单时-只有一张制造订单有组件”

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1167?initial=true
2024-07-24 12:18:54 +08:00
mgw
680bed9944 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化制造功能 2024-07-24 12:18:04 +08:00
mgw
d3e307d195 修复“同一个产品多个制造订单时-只有一张制造订单有组件” 2024-07-24 12:17:32 +08:00
jinling.yang
86a011343d Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-24 11:53:08 +08:00
jinling.yang
fe4bd3834e Merge branch 'feature/修复cnc工单按钮显示问题' into develop 2024-07-24 11:52:42 +08:00
杨金灵
d3992c2a2e Accept Merge Request #1166: (feature/修复cnc工单按钮显示问题 -> develop)
Merge Request: 修复cnc工单按钮显示问题

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1166
2024-07-24 11:52:12 +08:00
jinling.yang
e62ed636e7 还原注释代码 2024-07-24 11:42:34 +08:00
jinling.yang
f0fdeff699 修复cnc工单按钮显示问题 2024-07-24 11:40:16 +08:00
mgw
dec85dc92f Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化制造功能 2024-07-24 09:26:35 +08:00
mgw
f4f9591f35 设备机床信息tab调整,修复打印返回action问题 2024-07-24 09:26:19 +08:00
jinling.yang
efb7334486 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-23 17:20:59 +08:00
jinling.yang
b9c1224442 Merge branch 'feature/修复返工向导' into develop 2024-07-23 17:20:49 +08:00
杨金灵
ca1a3ff120 Accept Merge Request #1165: (feature/修复返工向导 -> develop)
Merge Request: 修复返工向导

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1165
2024-07-23 17:20:22 +08:00
jinling.yang
da8e543819 修复返工向导 2024-07-23 17:18:31 +08:00
禹翔辉
eb236fea11 Accept Merge Request #1164: (feature/修复工单cnc校验bug -> develop)
Merge Request: 1、修复feature/修复工单cnc校验bug

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1164?initial=true
2024-07-23 16:00:39 +08:00
yuxianghui
1d271d9167 1、修复feature/修复工单cnc校验bug 2024-07-23 15:46:15 +08:00
禹翔辉
c5cf614645 Accept Merge Request #1163: (feature/修复工单cnc校验bug -> develop)
Merge Request: 修复工单cnc校验bug

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1163?initial=true
2024-07-23 11:54:58 +08:00
yuxianghui
167c280fa4 修复工单cnc校验bug 2024-07-23 11:53:47 +08:00
杨金灵
69157a7b1f Accept Merge Request #1162: (feature/修复功能刀具 -> develop)
Merge Request: 修复功能刀具

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1162?initial=true
2024-07-23 11:48:47 +08:00
jinling.yang
2939e6bf82 修复功能刀具 2024-07-23 11:47:48 +08:00
杨金灵
96bbf3a677 Accept Merge Request #1161: (feature/修复返工(CNC加工) -> develop)
Merge Request: 修复返工(CNC加工)

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1161?initial=true
2024-07-23 10:48:29 +08:00
jinling.yang
e3580d9ac8 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/修复返工(CNC加工) 2024-07-23 10:33:37 +08:00
jinling.yang
ded1789813 修复返工(CNC加工) 2024-07-23 10:33:22 +08:00
禹翔辉
759f947fa7 Accept Merge Request #1160: (feature/NC程序和功能刀具校验_1 -> develop)
Merge Request: 完成  NC程序和功能刀具校验需求

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1160
2024-07-23 09:13:22 +08:00
yuxianghui
302aed16ec Merge branch 'feature/NC程序和功能刀具校验' into feature/NC程序和功能刀具校验_1 2024-07-23 09:08:33 +08:00
yuxianghui
7d1db793b1 Merge branch 'feature/NC程序和功能刀具校验' into feature/NC程序和功能刀具校验_1
# Conflicts:
#	sf_manufacturing/models/mrp_workorder.py
2024-07-23 09:06:57 +08:00
jinling.yang
83396f5434 修复返工(CNC加工) 2024-07-22 17:32:31 +08:00
禹翔辉
fe9baf08f0 Merge branch refs/heads/develop into refs/heads/feature/NC程序和功能刀具校验 2024-07-22 17:11:41 +08:00
杨金灵
b47cd0cdfe Accept Merge Request #1159: (feature/修复返工多面 -> develop)
Merge Request: 修复返工多面

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1159?initial=true
2024-07-22 15:10:13 +08:00
jinling.yang
251e289fd1 修复返工多面 2024-07-22 15:08:14 +08:00
jinling.yang
1d4ecb9895 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-22 14:47:12 +08:00
yuxianghui
f3a7dcc6a4 Merge branch 'feature/无效刀返工' into feature/NC程序和功能刀具校验
# Conflicts:
#	sf_manufacturing/models/mrp_workorder.py
2024-07-22 11:36:29 +08:00
jinling.yang
ebd56eb5e0 Merge branch 'feature/修复返工bug' into develop 2024-07-22 11:01:57 +08:00
杨金灵
4f99a162b5 Accept Merge Request #1157: (feature/修复返工bug -> develop)
Merge Request: 修复返工bug

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1157?initial=true
2024-07-22 11:01:41 +08:00
jinling.yang
28843b0733 修复返工bug 2024-07-22 10:57:43 +08:00
jinling.yang
8a0c968b10 修复返工bug 2024-07-19 17:25:51 +08:00
jinling.yang
9d3c4f8163 修复返工按钮 2024-07-19 09:17:35 +08:00
jinling.yang
08831812ae Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/修复返工bug
# Conflicts:
#	sf_manufacturing/models/mrp_production.py
2024-07-18 16:56:54 +08:00
jinling.yang
8b75658787 添加注释代码 2024-07-18 16:55:59 +08:00
yuxianghui
25ac346cb7 1、cnc编程单tree视图展示刀具状态字段;2、优化制造订单的功能刀具状态字段自动更新方法,同时当工单刀具状态变化时变更缺刀备注信息,添加状态为无效刀的备注字段;3、优化当功能刀具从【刀具房】到【制造前】时进行校验,校验是否为制造订单所缺的刀,如果是则修改cnc编程单刀具状态为正常;4、优化cnc用刀校验:添加对无效刀的处理(无效刀时触发返工流程,并生成检测结果数据),优化生成的待处理数据的数据结构; 2024-07-18 16:33:41 +08:00
jinling.yang
2d15782228 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-18 16:30:51 +08:00
jinling.yang
856237c4cb Merge branch 'feature/优化最新版报废' into develop 2024-07-18 16:30:41 +08:00
杨金灵
a47c751b04 Accept Merge Request #1155: (feature/优化最新版报废 -> develop)
Merge Request: 修复部分返工bug及优化部分报废

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1155
2024-07-18 16:29:58 +08:00
jinling.yang
9bec39f4e6 还原注释代码 2024-07-18 16:25:19 +08:00
jinling.yang
33bf88968c 修复返工bug 2024-07-18 16:17:19 +08:00
jinling.yang
7163e025d2 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化最新版报废
# Conflicts:
#	sf_manufacturing/security/ir.model.access.csv
2024-07-17 19:32:21 +08:00
杨金灵
11ec448e81 Accept Merge Request #1154: (feature/修复更新程序后制造订单状态 -> develop)
Merge Request: 修复更新程序后制造订单状态

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1154?initial=true
2024-07-17 19:30:45 +08:00
jinling.yang
6c2fb043c4 修复更新程序后制造订单状态 2024-07-17 19:29:21 +08:00
jinling.yang
1dcecea3d5 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-17 19:07:35 +08:00
jinling.yang
88f3ac3d4e Merge branch 'feature/修复多面返工工单状态变更错误' into develop 2024-07-17 19:06:58 +08:00
杨金灵
983618d798 Accept Merge Request #1153: (feature/修复多面返工工单状态变更错误 -> develop)
Merge Request: 修复多面返工工单状态变更错误

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1153?initial=true
2024-07-17 19:06:42 +08:00
jinling.yang
7a9c0ff163 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-17 19:03:14 +08:00
jinling.yang
77f7602b23 修复多面返工工单状态变更错误 2024-07-17 19:03:07 +08:00
yuxianghui
70b8521b90 Merge branch 'feature/销售订单、工单优化' into feature/无效刀返工
# Conflicts:
#	sf_manufacturing/models/mrp_production.py
#	sf_manufacturing/models/mrp_workorder.py
2024-07-17 16:57:11 +08:00
jinling.yang
1b89642dae Merge branch 'feature/修复检测报告添加权限' into develop 2024-07-17 15:39:00 +08:00
杨金灵
1968ee17cb Accept Merge Request #1152: (feature/修复检测报告添加权限 -> develop)
Merge Request: 修复检测报告添加权限

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1152
2024-07-17 15:37:57 +08:00
jinling.yang
5e83fb382f 还原rfid代码 2024-07-17 15:37:20 +08:00
jinling.yang
974671ee07 修复解除装夹 2024-07-17 15:33:49 +08:00
jinling.yang
d8b4797ffe 检测报告添加机床操作岗权限 2024-07-17 15:31:47 +08:00
jinling.yang
b07e7707c7 添加计划调度岗,机床操作岗 2024-07-17 15:30:38 +08:00
jinling.yang
8726f8cac3 添加报废向导;添加重新生成制造订单 2024-07-17 15:20:31 +08:00
杨金灵
cb7960387b Accept Merge Request #1151: (feature/修复返工向导 -> develop)
Merge Request: 修复返工向导

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1151?initial=true
2024-07-17 10:17:42 +08:00
jinling.yang
6eaf92b841 修复调用更新程序 2024-07-17 10:15:05 +08:00
jinling.yang
ec0b5e88b6 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-17 09:47:30 +08:00
jinling.yang
f1b8ee41bc Merge branch 'feature/优化最新版返工' into develop 2024-07-17 09:47:18 +08:00
杨金灵
97c0b95e2d Accept Merge Request #1150: (feature/优化最新版返工 -> develop)
Merge Request: 优化最新版返工

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1150
2024-07-17 09:47:03 +08:00
jinling.yang
7ac342bfd4 返工向导隐藏工序 2024-07-17 09:46:23 +08:00
jinling.yang
6c820ead44 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化最新版返工
# Conflicts:
#	sf_manufacturing/models/mrp_production.py
2024-07-16 17:25:24 +08:00
jinling.yang
c536dde2cc 去掉注释代码 2024-07-16 17:24:15 +08:00
马广威
801ff9e076 Accept Merge Request #1149: (feature/优化制造功能 -> develop)
Merge Request: 修复制造订单状态

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1149?initial=true
2024-07-16 16:32:11 +08:00
mgw
d0e049e881 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化制造功能 2024-07-16 16:31:33 +08:00
mgw
81158e2fb4 修复制造订单状态 2024-07-16 16:31:18 +08:00
yuxianghui
3404cccdc8 1、货架、货位添加循环货位标签;2、功能刀具拆解选取法人货位添加只能选择循环货位的过滤条件 2024-07-16 15:33:27 +08:00
禹翔辉
45bb78d7e9 Accept Merge Request #1148: (feature/表面工艺优化 -> develop)
Merge Request: 1、表面工艺及同步接口添加‘加工天数’、‘路途天数’字段;2、表面工艺可选参数及同步接口添加‘工艺描述’、‘加工天数’、‘路途天数’字段

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1148?initial=true
2024-07-16 14:11:57 +08:00
yuxianghui
83334e2915 1、表面工艺及同步接口添加‘加工天数’、‘路途天数’字段;2、表面工艺可选参数及同步接口添加‘工艺描述’、‘加工天数’、‘路途天数’字段 2024-07-16 14:10:14 +08:00
禹翔辉
d4c30f645b Accept Merge Request #1147: (feature/销售订单接口优化 -> develop)
Merge Request: 创建销售订单接口添加销售明细信息‘备注’字段

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1147?initial=true
2024-07-16 10:46:37 +08:00
yuxianghui
bd642812a9 创建销售订单接口添加销售明细信息‘备注’字段 2024-07-16 10:45:00 +08:00
yuxianghui
3eca74518a Merge branch 'feature/CNC功能刀具用刀校验' into feature/销售订单、工单优化 2024-07-16 10:41:56 +08:00
yuxianghui
37977be862 1、工单解绑时添加二次校验是否解绑成功;2、销售订单创建接口添加详情信息‘备注’字段; 2024-07-16 10:40:18 +08:00
jinling.yang
a0bd214118 修复返工操作时工单状态及制造订单更新程序 2024-07-15 17:34:18 +08:00
jinling.yang
7885794322 1.优化制造订单的检测结果:新增处理结果字段
2.优化制造订单的返工向导:加工面字段类型改为多对多,且该字段需根据处理结果为待处理的检测结果的加工面进行过滤
3.优化工单工件下产线接口:工单状态为待检测
4,.优化工单状态方法(_compute_state)
2024-07-12 17:32:53 +08:00
jinling.yang
7152b54017 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化最新版返工
# Conflicts:
#	sf_manufacturing/views/mrp_workorder_view.xml
2024-07-11 17:38:38 +08:00
jinling.yang
0fef714d61 1.注释mrp_workcenter.xml文件和res.config添加不要的字段:lost_agent_id
2.制造订单新增“重新编程次数”字段,新增更新程序方法及对应的接口,优化返工且编程中的制造订单定时获取Cloud编程单状态的方法
3.优化返工向导页面及新增“重复编程次数”和“编程状态”字段
2024-07-11 17:37:35 +08:00
禹翔辉
6a2ff72dce Accept Merge Request #1146: (feature/销售单优化 -> develop)
Merge Request: 1、在销售订单内产品列表内增加备注字段

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1146?initial=true
2024-07-11 16:40:39 +08:00
yuxianghui
3094379e86 1、在销售订单内产品列表内增加备注字段 2024-07-11 16:38:41 +08:00
yuxianghui
77815c45a0 1、当检测到缺刀工单需要的功能刀具从刀具房出库到线边刀架时,更改cnc编程单的功能刀具状态为正常; 2024-07-11 16:37:20 +08:00
马广威
dd7c69ce1b Accept Merge Request #1145: (feature/优化制造功能 -> develop)
Merge Request: 修改订单状态

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1145?initial=true
2024-07-11 11:23:05 +08:00
mgw
e02f792300 修改订单状态 2024-07-11 11:22:25 +08:00
jinling.yang
9aca786522 1.优化工单排序2.新增调取cloud编程单的状态方法3.制造订单添加状态为返工及编程状态为"已编程",“已编程未下发”的过滤条件4,新增返工且编程中的制造订单定时获取Cloud编程单状态的定时器 2024-07-10 17:36:26 +08:00
yuxianghui
134e23ea2f 1、重构工单工序的生成方法; 2024-07-10 16:25:18 +08:00
yuxianghui
129f8a4d7d 1、重构工单cnc编程用刀校验方法;制造订单、工单、cnc编程单添加刀具状态字段,并且根据cnc编程单刀具状态自动计算制造订单、工单刀具状态字段的值;制造订单form页新增展示功能刀具状态字段;2、功能刀具拆解单刀具物料选择目标货位添加过滤添加为只选择所属货区是刀具房的货位; 2024-07-10 16:23:24 +08:00
马广威
79e3006cdb Accept Merge Request #1144: (feature/优化制造功能 -> develop)
Merge Request: 优化工单模块,增加企微模块

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1144?initial=true
2024-07-10 16:05:49 +08:00
mgw
6b140fe6dd 优化工单模块,增加企微模块 2024-07-10 15:58:47 +08:00
jinling.yang
7533d23d3e 1.工单状态新增‘返工’,2,在制造订单上点击返工选择加工面确认后,新增加工面的对应工单及新增重置cloud的编程单的状态3.优化返工向导 2024-07-09 17:36:47 +08:00
马广威
e8512b23e4 Accept Merge Request #1143: (feature/优化制造功能 -> develop)
Merge Request: 增加工单模块

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1143?initial=true
2024-07-09 17:32:54 +08:00
mgw
b582bfbafa 增加工单模块 2024-07-09 17:32:03 +08:00
马广威
11907d4c5e Accept Merge Request #1142: (feature/优化制造功能 -> develop)
Merge Request: 解除装夹处打印成品二维码

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1142
2024-07-09 10:19:20 +08:00
mgw
ed4903b6f1 解除装夹处打印成品二维码 2024-07-09 10:18:13 +08:00
jinling.yang
b383a6d229 1.新增加工面模型2.制造订单详情页面的返工操作上的加工面根据产品的加工面板进行过滤 2024-07-08 17:33:04 +08:00
禹翔辉
27eb959a2a Accept Merge Request #1141: (feature/功能刀具拆解单库存优化 -> develop)
Merge Request: 1、功能刀具组装/拆解单库存优化;2、完成 SF材料材料型号缺陷优化 bug

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1141
2024-07-08 09:57:04 +08:00
yuxianghui
1672a3982e 1、完成 SF材料材料型号缺陷优化 bug 2024-07-05 17:09:32 +08:00
黄焱
40f8392bd3 Accept Merge Request #1140: (feature/前端样式修改 -> develop)
Merge Request: 去除 根据进度条设置水印 修复切换产品3D图bug 修改表格下拉框会被表格下面数据框覆盖的bug

Created By: @黄焱
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @黄焱
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1140?initial=true
2024-07-05 16:05:38 +08:00
hy
1c8644983b 去除 根据进度条设置水印
修复切换产品3D图bug
修改表格下拉框会被表格下面数据框覆盖的bug
2024-07-05 16:02:32 +08:00
马广威
566d9fce9c Accept Merge Request #1139: (feature/优化制造功能 -> develop)
Merge Request: 优化展示效果

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1139?initial=true
2024-07-05 11:37:24 +08:00
mgw
b071b14bbd 优化展示效果 2024-07-05 11:36:48 +08:00
马广威
9d95442720 Accept Merge Request #1138: (feature/优化制造功能 -> develop)
Merge Request: 工作中心的参数修改需增加sns模块("记录备注")

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1138?initial=true
2024-07-05 11:27:02 +08:00
mgw
fd225fa0e1 工作中心的参数修改需增加sns模块("记录备注") 2024-07-05 11:26:02 +08:00
jinling.yang
52fa229896 优化制造订单菜单 2024-07-04 17:31:03 +08:00
马广威
937efa5f0f Accept Merge Request #1137: (feature/优化制造功能 -> develop)
Merge Request: 前置检测文件命名增加加工面标识,避免检测文件覆盖

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1137?initial=true
2024-07-04 17:22:25 +08:00
mgw
87153fab3f 前置检测文件命名增加加工面标识,避免检测文件覆盖 2024-07-04 17:21:39 +08:00
马广威
21c33bb662 Accept Merge Request #1136: (feature/优化制造功能 -> develop)
Merge Request: 修复“已经排程的制造订单取消排程后状态没有回退”

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1136?initial=true
2024-07-04 16:19:46 +08:00
mgw
b0da7977f5 修复“已经排程的制造订单取消排程后状态没有回退” 2024-07-04 16:19:20 +08:00
马广威
ae6764495e Accept Merge Request #1135: (feature/优化制造功能 -> develop)
Merge Request: 修复”制造订单完工后对应的成品入库单还是等待状态-应该是就绪状态“

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1135?initial=true
2024-07-04 16:13:30 +08:00
mgw
869e3e4b4f 修复”制造订单完工后对应的成品入库单还是等待状态-应该是就绪状态“ 2024-07-04 16:12:27 +08:00
jinling.yang
050e82673a 优化cnc工单 2024-07-04 15:38:43 +08:00
杨金灵
1210625cfc Accept Merge Request #1134: (release/release_2.0 -> develop)
Merge Request: 修复工件多面工单上下产线状态

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1134?initial=true
2024-07-04 15:37:25 +08:00
马广威
804b401a87 Accept Merge Request #1133: (release/release_2.0 -> master)
Merge Request: 修复多面工单产线状态

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1133?initial=true
2024-07-03 21:33:49 +08:00
jinling.yang
a7a8e73616 新增返工向导 2024-07-03 17:35:29 +08:00
yuxianghui
d60fefb2ec 1 2024-07-03 17:33:29 +08:00
yuxianghui
4a86871039 1、功能刀具组装单添加对应调拨单跳转按钮;功能刀具拆解单添加调拨单跳转按钮和功能刀具移动跳转按钮;2、功能刀具拆解模型,form视图添加报废page页存放刀具物料报废单,优化拆解流程添加功能刀具拆解移动记录;3、功能刀具出入库列表视图添加拆解单跳转链接;4、功能刀具安全库存模型添加名称唯一验证,优化字段计算方法;5、优化功能刀具同步到cloud的同步接口,解决编码问题; 2024-07-03 17:17:48 +08:00
jinling.yang
93c3548b40 注释OCC代码 2024-07-03 11:05:55 +08:00
jinling.yang
6c6fe44d45 制造订单去掉上/下产线字段 2024-07-03 10:53:29 +08:00
jinling.yang
bb33a1d093 修复多面工单产线状态 2024-07-03 10:37:02 +08:00
杨金灵
396084a498 Accept Merge Request #1132: (feature/修复多面加工产线状态 -> develop)
Merge Request: 修复多面加工产线状态

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1132
2024-07-03 10:32:42 +08:00
jinling.yang
73cc244994 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/修复多面加工产线状态 2024-07-03 10:30:27 +08:00
jinling.yang
fa6303bbef 修复多面工单加工产线状态 2024-07-03 10:30:13 +08:00
杨金灵
5ca555c520 Accept Merge Request #1131: (feature/修复多面加工产线状态 -> develop)
Merge Request: 修复多面加工产线状态

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1131?initial=true
2024-07-03 10:00:29 +08:00
jinling.yang
487f6c2054 修复多面加工产线状态 2024-07-03 09:55:47 +08:00
jinling.yang
9ec37f5d5b 修复多面加工产线状态 2024-07-03 09:45:55 +08:00
yuxianghui
cbc8a41984 1、添加拆解的仓库管理地点;2、优化功能刀具拆解库存管理;3、代码迁移 2024-07-02 17:32:27 +08:00
yuxianghui
025ad213d6 Merge branch 'feature/功能刀具组装库存优化' into feature/功能刀具拆解单库存优化 2024-07-02 10:24:52 +08:00
yuxianghui
0b267cc88a 1 2024-07-02 09:32:50 +08:00
马广威
0bc4072a8f Accept Merge Request #1130: (release/release_1.9 -> master)
Merge Request: SPRINT-MES-2024-03迭代优化升级

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1130?initial=true
2024-07-01 22:35:53 +08:00
马广威
957c5583bd Accept Merge Request #1129: (feature/修复制造订单数量 -> develop)
Merge Request: 修复制造订单数量

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1129#mr-1129-review-164092
2024-07-01 20:13:10 +08:00
jinling.yang
9c4c2b85dd 修复制造订单数量 2024-07-01 20:08:46 +08:00
jinling.yang
56af286500 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-01 18:30:18 +08:00
jinling.yang
cc83029c72 Merge branch 'feature/修复工单完成接口' into develop 2024-07-01 18:30:06 +08:00
杨金灵
79ab1cc573 Accept Merge Request #1128: (feature/修复工单完成接口 -> develop)
Merge Request: 修复工单完成接口

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1128?initial=true
2024-07-01 18:29:29 +08:00
jinling.yang
bda3df7806 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-01 18:29:16 +08:00
马广威
150b684354 Accept Merge Request #1127: (feature/sf物流方面缺陷与优化 -> develop)
Merge Request: Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/sf物流方面缺陷与优化

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1127?initial=true
2024-07-01 18:28:37 +08:00
jinling.yang
d69dc20fd1 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/修复工单完成接口 2024-07-01 18:28:11 +08:00
jinling.yang
e9c9b77112 修复所有工单完成报库存问题 2024-07-01 18:27:59 +08:00
mgw
495f92bd80 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/sf物流方面缺陷与优化 2024-07-01 18:27:52 +08:00
马广威
8a97fd08e2 Accept Merge Request #1126: (feature/sf物流方面缺陷与优化 -> develop)
Merge Request: 状态补充条件

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1126?initial=true
2024-07-01 18:18:19 +08:00
mgw
1551ea4b12 状态补充条件 2024-07-01 18:17:48 +08:00
jinling.yang
75c5cca9a0 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-01 18:12:25 +08:00
jinling.yang
6b6be85057 去掉更改制造订单的状态等相关代码 2024-07-01 18:12:15 +08:00
jinling.yang
7fb3c32d07 修复工单完成接口 2024-07-01 17:48:45 +08:00
马广威
8203975142 Accept Merge Request #1125: (feature/sf物流方面缺陷与优化 -> develop)
Merge Request: 优化订单状态条件及物流的容错处理

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1125?initial=true
2024-07-01 17:28:45 +08:00
mgw
449cf5bfb1 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/sf物流方面缺陷与优化 2024-07-01 17:28:07 +08:00
mgw
d7d5ccc142 优化订单状态条件及物流的容错处理 2024-07-01 17:27:52 +08:00
jinling.yang
8710df6dc2 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-01 17:14:42 +08:00
jinling.yang
f5c1457707 Merge branch 'feature/优化解绑rfid' into develop 2024-07-01 17:13:57 +08:00
杨金灵
5155135cb4 Accept Merge Request #1124: (feature/优化解绑rfid -> develop)
Merge Request: 优化解绑rfid

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1124
2024-07-01 17:13:38 +08:00
jinling.yang
4421eed1d0 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-01 17:11:47 +08:00
yuxianghui
5be5e8b3ec 1、功能刀具组装完成时,自动生成内部调拨单以及所有的刀具物料的库存移动和移动历史记录,并自动完成该调拨单;优化功能刀具的刀具组装入库单的生成,简化入库步骤。 2024-07-01 17:11:44 +08:00
jinling.yang
46e3a23492 优化解绑rfid 2024-07-01 17:11:34 +08:00
马广威
675b09390b Accept Merge Request #1123: (feature/sf物流方面缺陷与优化 -> develop)
Merge Request: 优化订单状态

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1123?initial=true
2024-07-01 16:57:22 +08:00
mgw
34251b4027 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/sf物流方面缺陷与优化 2024-07-01 16:56:43 +08:00
mgw
c4621d36a3 优化订单状态 2024-07-01 16:56:19 +08:00
jinling.yang
7bdc4b463a Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-01 15:42:22 +08:00
jinling.yang
ea88b5418f Merge branch 'feature/优化cnc工单接口' into develop 2024-07-01 15:42:10 +08:00
杨金灵
f5a64caf81 Accept Merge Request #1122: (feature/优化cnc工单接口 -> develop)
Merge Request: 优化cnc工单接口

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1122?initial=true
2024-07-01 15:41:52 +08:00
jinling.yang
1e5f26d645 工单开始和结束接口增加rfid_code不为空的条件 2024-07-01 15:29:55 +08:00
jinling.yang
ace32d9490 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-01 14:03:23 +08:00
马广威
5db2f1fc70 Accept Merge Request #1121: (feature/sf物流方面缺陷与优化 -> develop)
Merge Request: 优化物流提示

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1121?initial=true
2024-07-01 11:51:37 +08:00
mgw
960c940505 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/sf物流方面缺陷与优化 2024-07-01 11:50:49 +08:00
mgw
ee1629b40a 优化物流提示 2024-07-01 11:47:51 +08:00
jinling.yang
06de5f6a2d Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-07-01 10:50:19 +08:00
jinling.yang
c1eff705c2 Merge branch 'feature/修复表面工艺' into develop 2024-07-01 10:49:57 +08:00
杨金灵
8e3c7f9732 Accept Merge Request #1120: (feature/修复表面工艺 -> develop)
Merge Request: 修复表面工艺

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1120?initial=true
2024-07-01 10:49:38 +08:00
jinling.yang
2591e627f0 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/修复表面工艺 2024-07-01 10:47:33 +08:00
jinling.yang
4c66d86ee6 修复表面工艺 2024-07-01 10:47:05 +08:00
禹翔辉
d4bc1c13ea Accept Merge Request #1119: (feature/工单优化-2 -> develop)
Merge Request: 优化工单扫描绑定托盘的提示信息

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1119
2024-07-01 09:55:21 +08:00
yuxianghui
4c845ee9fd Merge branch 'feature/工单优化-1' into feature/工单优化-2 2024-07-01 09:53:11 +08:00
yuxianghui
726c3aede1 1、优化工单扫描绑定托盘的提示信息 2024-07-01 09:52:16 +08:00
黄焱
871c406c09 Accept Merge Request #1118: (feature/前端样式修改 -> develop)
Merge Request: // 修复表格内容覆盖表头bug

Created By: @黄焱
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @黄焱
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1118?initial=true
2024-07-01 09:49:05 +08:00
hy
2ba9695c34 // 修复表格内容覆盖表头bug 2024-07-01 09:47:50 +08:00
杨金灵
e1db34f25d Accept Merge Request #1117: (feature/修复无外协单和程序单 -> develop)
Merge Request: 修复无外协单和程序单

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1117
2024-07-01 09:31:43 +08:00
jinling.yang
e496555ff3 去掉注释代码 2024-06-30 19:29:36 +08:00
jinling.yang
404a41294c 修复表面工艺外协单状态 2024-06-30 19:16:48 +08:00
jinling.yang
e124560089 修复无外协单和程序单 2024-06-30 17:52:02 +08:00
jinling.yang
fd3a363bf6 修复无外协单和程序单 2024-06-30 16:51:40 +08:00
杨金灵
9f6563186c Accept Merge Request #1116: (feature/优化工单页面 -> develop)
Merge Request: 优化工单页面

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1116
2024-06-30 14:47:44 +08:00
jinling.yang
1b174c5805 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-30 14:41:38 +08:00
jinling.yang
849b7be20d Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-30 14:09:34 +08:00
jinling.yang
9d32bf8363 Merge branch 'feature/修复制造订单状态和多个编程单问题' into develop 2024-06-30 12:46:48 +08:00
杨金灵
998daaa7f7 Accept Merge Request #1115: (feature/修复制造订单状态和多个编程单问题 -> develop)
Merge Request: 修复制造订单状态和多个编程单问题

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1115?initial=true
2024-06-30 12:45:54 +08:00
jinling.yang
b23cb9af6e 优化编程状态字段的样式 2024-06-30 12:09:09 +08:00
jinling.yang
56adaddefc 1.修复编程状态字段类型2.优化工单rfid排放位置 2024-06-30 12:01:48 +08:00
jinling.yang
4a648fbd7b 1,修复制造订单状态:第一张装夹工单未就绪是,状态需为:待加工;所有工单完成后,状态需为:完成;2.修复多个产品确认制造订单未生产多个编程单问题3.优化工单页面:去掉非必要的字段 2024-06-30 11:45:52 +08:00
mgw
a3bf451d83 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/sf物流方面缺陷与优化 2024-06-28 17:03:06 +08:00
mgw
2919a3c159 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/sf物流方面缺陷与优化 2024-06-28 17:02:47 +08:00
马广威
8f6e2bc13d Accept Merge Request #1114: (feature/sf物流方面缺陷与优化 -> develop)
Merge Request: 排程单甘特图界面订单的hover提示中订单交期字段没有翻译成日期格式

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1114?initial=true
2024-06-28 17:01:43 +08:00
mgw
909230c1cc 排程单甘特图界面订单的hover提示中订单交期字段没有翻译成日期格式 2024-06-28 17:00:50 +08:00
马广威
db8c338588 Accept Merge Request #1113: (feature/sf物流方面缺陷与优化 -> develop)
Merge Request: 工单增加默认产品分组,组内排序从小到大

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1113?initial=true
2024-06-28 16:02:09 +08:00
mgw
f319a9adf4 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/sf物流方面缺陷与优化 2024-06-28 16:01:05 +08:00
mgw
172e420939 增加分组内排序 2024-06-28 16:00:50 +08:00
mgw
0c3727540e 工单增加默认产品分组 2024-06-28 14:51:53 +08:00
禹翔辉
5668722109 Accept Merge Request #1112: (feature/工单优化-1 -> develop)
Merge Request: 工单优化

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1112
2024-06-28 14:25:01 +08:00
杨金灵
2d80355559 Accept Merge Request #1111: (feature/修复cmm程序接口 -> develop)
Merge Request: 修复cmm程序接口

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1111?initial=true
2024-06-28 14:24:23 +08:00
jinling.yang
d281026086 修复cmm程序接口 2024-06-28 14:23:03 +08:00
yuxianghui
5c35a8c38d Merge branch 'feature/添加权限、解决bug' into feature/工单优化-1 2024-06-28 14:22:35 +08:00
yuxianghui
3b24f90385 1、工单添加按Rfid搜索,隐藏多余的Rfid字段;2、优化程序创建日期; 2024-06-28 14:20:57 +08:00
杨金灵
14355606c7 Accept Merge Request #1110: (feature/优化ftp -> develop)
Merge Request: ftp优化

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1110
2024-06-28 12:03:05 +08:00
jinling.yang
5997cbcb0e download_program_file方法添加关闭ftp 2024-06-28 12:00:29 +08:00
jinling.yang
f07d7611d0 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-28 10:10:46 +08:00
马广威
b106d56f3f Accept Merge Request #1109: (feature/sf物流方面缺陷与优化 -> develop)
Merge Request: 修复隐藏项目

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1109?initial=true
2024-06-28 09:52:58 +08:00
mgw
3156052604 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/sf物流方面缺陷与优化 2024-06-28 09:51:50 +08:00
mgw
c8de75ceae 修复隐藏项目 2024-06-28 09:51:31 +08:00
马广威
d4b1f8f1a1 Accept Merge Request #1108: (feature/sf物流方面缺陷与优化 -> develop)
Merge Request: Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/sf物流方面缺陷与优化

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1108?initial=true
2024-06-28 09:45:59 +08:00
mgw
5378d62551 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/sf物流方面缺陷与优化 2024-06-28 09:44:24 +08:00
马广威
424f7196f8 Accept Merge Request #1107: (feature/sf物流方面缺陷与优化 -> develop)
Merge Request: 处理unlink传参问题2

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1107?initial=true
2024-06-28 09:19:58 +08:00
mgw
c2723d9d05 处理unlink传参问题2 2024-06-28 09:19:09 +08:00
jinling.yang
7bf001927a Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-28 09:17:56 +08:00
杨金灵
84d67d285e Accept Merge Request #1106: (feature/修复制造订单无采购单问题 -> develop)
Merge Request: 修复制造订单无采购单问题

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1106?initial=true
2024-06-28 09:16:32 +08:00
jinling.yang
a583b483e3 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/修复制造订单无采购单问题 2024-06-27 20:10:22 +08:00
jinling.yang
719a96f983 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-27 20:10:01 +08:00
jinling.yang
08fd0b01d3 去掉程序下发按钮 2024-06-27 20:09:45 +08:00
jinling.yang
db7fbed177 去掉重新编程下发相关代码,修复多制造订单无采购调拨单,去掉程序下发按钮 2024-06-27 20:09:28 +08:00
马广威
ad149a58ea Accept Merge Request #1105: (feature/sf物流方面缺陷与优化 -> develop)
Merge Request: 处理unlink()参数问题

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1105?initial=true
2024-06-27 17:32:18 +08:00
mgw
0d96cd070c Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/sf物流方面缺陷与优化 2024-06-27 17:31:21 +08:00
mgw
37f030c2db 处理unlink()参数问题 2024-06-27 17:31:03 +08:00
禹翔辉
e2a62325f8 Accept Merge Request #1104: (feature/添加权限、解决bug -> develop)
Merge Request: 添加权限、解决bug

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1104?initial=true
2024-06-27 17:26:20 +08:00
yuxianghui
0db103cfc7 Merge branch 'feature/调拨单优化1' into feature/添加权限、解决bug 2024-06-27 17:22:42 +08:00
yuxianghui
05f2cb553d 1、完成 采购/采购总监岗位权限增加 需求;2、完成 刀具出入库列表缺少单据跳转 需求;3、完成 设备详情的机内装刀时间比当前时间还晚 bug; 2024-06-27 17:21:37 +08:00
黄焱
c597471949 Accept Merge Request #1103: (feature/前端样式修改 -> develop)
Merge Request: 甘特修改背景颜色和不可拖拽模块

Created By: @黄焱
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @黄焱
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1103?initial=true
2024-06-27 10:14:10 +08:00
hy
70ffbafac4 甘特修改背景颜色和不可拖拽模块 2024-06-27 09:57:12 +08:00
jinling.yang
1cb7af072e 注释OCC代码 2024-06-27 09:52:20 +08:00
禹翔辉
4f6b1d0342 Accept Merge Request #1102: (feature/调拨单优化1 -> develop)
Merge Request: 1、完成 内部调拨单优化项 需求;2、解决 不同序列号调拨单选择同一个货位可以确认成功 bug;3、收货入库时扫描录入Rfid方法添加日志

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1102?initial=true
2024-06-26 17:10:25 +08:00
yuxianghui
9bdcf0dd92 Merge branch 'feature/调拨单优化' into feature/调拨单优化1 2024-06-26 17:08:39 +08:00
yuxianghui
bcb60425b2 1、完成 内部调拨单优化项 需求;2、解决 不同序列号调拨单选择同一个货位可以确认成功 bug;3、收货入库时扫描录入Rfid方法添加日志 2024-06-26 17:07:09 +08:00
jinling.yang
22643368fa Merge branch 'feature/修复工单完成报错库存问题' into develop 2024-06-26 16:41:23 +08:00
jinling.yang
aeb100923f Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/修复工单完成报错库存问题 2024-06-26 16:41:05 +08:00
杨金灵
53c6d6015e Accept Merge Request #1101: (feature/修复工单完成报错库存问题 -> develop)
Merge Request: 修复制造订单生成时没有采购单

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1101?initial=true
2024-06-26 16:40:40 +08:00
jinling.yang
131e9329de Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-26 16:37:05 +08:00
jinling.yang
dbd5c488f8 修复制造订单生成时没有采购单 2024-06-26 16:36:51 +08:00
马广威
f8ba414229 Accept Merge Request #1100: (feature/sf物流方面缺陷与优化 -> develop)
Merge Request: sf物流优化

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1100?initial=true
2024-06-26 16:34:52 +08:00
mgw
7fd3647afb sf物流优化 2024-06-26 16:34:04 +08:00
马广威
f5be9c6e65 Accept Merge Request #1099: (feature/制造订单列表页及详情页优化需求 -> develop)
Merge Request: 制造订单列表页及详情页优化需求

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1099?initial=true
2024-06-26 14:13:06 +08:00
mgw
c275a39a15 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造订单列表页及详情页优化需求 2024-06-26 14:11:45 +08:00
mgw
d07ab53aeb 制造订单列表页及详情页优化需求 2024-06-26 14:10:57 +08:00
jinling.yang
55accb714a Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-26 11:25:47 +08:00
jinling.yang
5bd3eddfd5 Merge branch 'feature/修复编程单下发ftp路径' into develop 2024-06-26 11:24:58 +08:00
杨金灵
c8bcd4d0f5 Accept Merge Request #1098: (feature/修复编程单下发ftp路径 -> develop)
Merge Request: 修复编程单下发ftp路径

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1098?initial=true
2024-06-26 11:24:40 +08:00
jinling.yang
2cbe5ed394 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/修复编程单下发ftp路径 2024-06-26 11:11:26 +08:00
jinling.yang
1f1cb17a40 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-26 11:11:08 +08:00
jinling.yang
2ff935f68d 修复编程单下发ftp路径 2024-06-26 11:10:54 +08:00
杨金灵
098745ec50 Accept Merge Request #1097: (feature/修复编程单下发ftp路径 -> develop)
Merge Request: 修复编程单下发ftp路径

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1097?initial=true
2024-06-26 10:21:25 +08:00
jinling.yang
94175f2039 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-26 10:17:27 +08:00
jinling.yang
1940116a35 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/修复编程单下发ftp路径 2024-06-26 10:17:14 +08:00
jinling.yang
f4eb378bea 修复制造订单:无工单时,工单page需隐藏 2024-06-26 10:17:00 +08:00
jinling.yang
dcbb60b088 修复编程单下发ftp路径:去掉注释 2024-06-25 18:11:45 +08:00
jinling.yang
88ef9c106d 修复编程单下发ftp路径 2024-06-25 17:54:08 +08:00
马广威
0f7f12008f Accept Merge Request #1096: (feature/【计划】模块的【排程单】页面优化需求 -> develop)
Merge Request: 处理"打印的二维码信息与界面的二维码信息不一致"等

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1096?initial=true
2024-06-25 16:51:47 +08:00
mgw
f44836705b 放出程序下载按钮 2024-06-25 16:50:09 +08:00
mgw
35ac51bc10 取消默认分组展示 2024-06-25 14:22:51 +08:00
mgw
eb2851391b 处理"打印的二维码信息与界面的二维码信息不一致" 2024-06-25 12:06:28 +08:00
jinling.yang
86f67556ce Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-25 11:07:41 +08:00
禹翔辉
e437875a92 Accept Merge Request #1095: (release/release_1.8 -> develop)
Merge Request: 功能刀具、工件状态优化

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1095
2024-06-25 09:53:14 +08:00
马广威
9e56aeda67 Accept Merge Request #1093: (release/release_1.8 -> master)
Merge Request: 功能刀具、工件状态优化

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1093?initial=true
2024-06-24 21:38:15 +08:00
jinling.yang
b4535e88c9 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-24 17:31:08 +08:00
jinling.yang
be94cd0d54 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-24 17:30:03 +08:00
jinling.yang
65e209fe2a Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-24 17:20:18 +08:00
jinling.yang
27ba002b51 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-24 16:54:28 +08:00
jinling.yang
2cd83c87e2 Merge branch 'feature/优化重新下发程序' into develop 2024-06-24 16:52:48 +08:00
jinling.yang
ba87d07af7 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-24 16:52:24 +08:00
221 changed files with 17148 additions and 1581 deletions

View File

@@ -5,7 +5,7 @@ import {patch} from '@web/core/utils/patch';
import {_t} from "@web/core/l10n/translation";
import {FormStatusIndicator} from "@web/views/form/form_status_indicator/form_status_indicator";
import {ListRenderer} from "@web/views/list/list_renderer";
import {StatusBarField} from "@web/views/fields/statusbar/statusbar_field";
// import {StatusBarField} from "@web/views/fields/statusbar/statusbar_field";
import {Field} from "@web/views/fields/field";
@@ -153,34 +153,34 @@ patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
// 根据进度条设置水印
const statusbar_params = {
'已完工': 'bg-primary',
'完成': 'bg-primary',
'采购订单': 'bg-primary',
'作废': 'bg-danger',
'封存(报废)': 'bg-danger',
}
patch(StatusBarField.prototype, 'jikimo_frontend.StatusBarField', {
setup() {
owl.onMounted(this.ribbons);
return this._super(...arguments);
},
ribbons() {
try {
const dom = $('.o_form_sheet.position-relative')
const status = statusbar_params[this.currentName]
if(status && dom.length) {
dom.prepend(`<div class="o_widget o_widget_web_ribbon">
<div class="ribbon ribbon-top-right">
<span class="bg-opacity-75 ${status}" title="">${this.currentName}</span>
</div>
</div>`)
}
} catch (e) {
console.log(e)
}
}
})
// const statusbar_params = {
// '已完工': 'bg-primary',
// '完成': 'bg-primary',
// '采购订单': 'bg-primary',
// '作废': 'bg-danger',
// '封存(报废)': 'bg-danger',
// }
// patch(StatusBarField.prototype, 'jikimo_frontend.StatusBarField', {
// setup() {
// owl.onMounted(this.ribbons);
// return this._super(...arguments);
// },
// ribbons() {
// try {
// const dom = $('.o_form_sheet.position-relative')
// const status = statusbar_params[this.currentName]
// if(status && dom.length) {
// dom.prepend(`<div class="o_widget o_widget_web_ribbon">
// <div class="ribbon ribbon-top-right">
// <span class="bg-opacity-75 ${status}" title="">${this.currentName}</span>
// </div>
// </div>`)
// }
// } catch (e) {
// console.log(e)
// }
// }
// })
$(function () {
document.addEventListener('click', function () {

View File

@@ -526,4 +526,15 @@ div:has(.o_required_modifier) > label::before {
// 设置表单页面label文本不换行
.o_form_view .o_group .o_wrap_label .o_form_label {
white-space: nowrap;
}
}
// 修复表格内容覆盖表头bug
.o_list_renderer .o_list_table tbody th {
position: unset;
}
// 修改表格下拉框会被表格下面数据框覆盖的bug
.tab-pane .o_field_widget {
position: relative;
z-index: 1;
}

View File

@@ -301,53 +301,27 @@ def unlink(self):
# This is used to restrict the access right to unlink a record
current_model_id = self.env['ir.model'].sudo().search(
[('model', '=', self._name)]).id
# access_right_rec = self.env['access.right'].sudo().search_read(
# [('model_id', '=', current_model_id)], ['model_id', 'is_delete',
# 'groups_id'])
# if access_right_rec and not self.env.is_admin():
# for rec in access_right_rec:
# group_name = self.env['ir.model.data'].sudo().search([
# ('model', '=', 'res.groups'),
# ('res_id', '=', rec['groups_id'][0])
# ]).name
# module_name = self.env['ir.model.data'].sudo().search([
# ('model', '=', 'res.groups'),
# ('res_id', '=', rec['groups_id'][0])
# ]).module
# group = module_name + "." + group_name
# if self.env.user.has_group(group):
# if rec['is_delete']:
# raise UserError(_('You are restricted from performing this'
# ' operation. Please contact the'
# ' administrator.'))
# 检查 'access.right' 模型是否存在于环境中
if 'access.right' in self.env:
# current_model_id = self.env['ir.model'].sudo().search([('model', '=', self._name)]).id
access_right_rec = self.env['access.right'].sudo().search_read(
[('model_id', '=', current_model_id)], ['model_id', 'is_delete', 'groups_id']
)
if access_right_rec and not self.env.is_admin():
for rec in access_right_rec:
group_data = self.env['ir.model.data'].sudo().search_read(
[('model', '=', 'res.groups'), ('res_id', '=', rec['groups_id'][0])],
['name', 'module']
)
if group_data:
group_name = group_data[0]['name']
module_name = group_data[0]['module']
group_xml_id = f"{module_name}.{group_name}"
if self.env.user.has_group(group_xml_id) and rec['is_delete']:
raise UserError(
_('You are restricted from performing this operation. Please contact the administrator.'))
else:
# 如果 'access.right' 模型不存在,可以在这里定义备选逻辑
pass
access_right_rec = self.env['access.right'].sudo().search_read(
[('model_id', '=', current_model_id)], ['model_id', 'is_delete',
'groups_id'])
if access_right_rec and not self.env.is_admin():
for rec in access_right_rec:
group_name = self.env['ir.model.data'].sudo().search([
('model', '=', 'res.groups'),
('res_id', '=', rec['groups_id'][0])
]).name
module_name = self.env['ir.model.data'].sudo().search([
('model', '=', 'res.groups'),
('res_id', '=', rec['groups_id'][0])
]).module
group = module_name + "." + group_name
if self.env.user.has_group(group):
if rec['is_delete']:
raise UserError(_('You are restricted from performing this'
' operation. Please contact the'
' administrator.'))
return True
BaseModel._create = _create
BaseModel.unlink = unlink
BaseModel.unlink = unlink

View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
from . import controllers
from . import models
from . import wizard

View File

@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
{
'name': "jikimo_system_order",
'summary': """
系统工单""",
'description': """
用于处理针对系统的工作任务;
员工可以通过系统工单发起申请,由维护人员处理以后,填写处理结果。
""",
'author': "机企猫",
'website': "http://www.jikimo.com",
# Categories can be used to filter modules in modules listing
# Check https://github.com/odoo/odoo/blob/master/odoo/addons/base/module/module_data.xml
# for the full list
'category': 'Uncategorized',
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['base','mail'],
# always loaded
'data': [
'security/account_security.xml',
'security/ir.model.access.csv',
'wizard/order_wizard.xml',
'views/notice_user_config.xml',
'views/yizuo_system_order_view.xml',
'views/work_order_number.xml',
'views/res_config_settings_views.xml',
],
# only loaded in demonstration mode
'demo': [
'demo/demo.xml',
],
}

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import controllers

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from odoo import http
# class TopSystemOrder(http.Controller):
# @http.route('/jikimo_system_order/jikimo_system_order/', auth='public')
# def index(self, **kw):
# return "Hello, world"
# @http.route('/jikimo_system_order/jikimo_system_order/objects/', auth='public')
# def list(self, **kw):
# return http.request.render('jikimo_system_order.listing', {
# 'root': '/jikimo_system_order/jikimo_system_order',
# 'objects': http.request.env['jikimo_system_order.jikimo_system_order'].search([]),
# })
# @http.route('/jikimo_system_order/jikimo_system_order/objects/<model("jikimo_system_order.jikimo_system_order"):obj>/', auth='public')
# def object(self, obj, **kw):
# return http.request.render('jikimo_system_order.object', {
# 'object': obj
# })

View File

@@ -0,0 +1,30 @@
<odoo>
<data>
<!-- -->
<!-- <record id="object0" model="jikimo_system_order.jikimo_system_order"> -->
<!-- <field name="name">Object 0</field> -->
<!-- <field name="value">0</field> -->
<!-- </record> -->
<!-- -->
<!-- <record id="object1" model="jikimo_system_order.jikimo_system_order"> -->
<!-- <field name="name">Object 1</field> -->
<!-- <field name="value">10</field> -->
<!-- </record> -->
<!-- -->
<!-- <record id="object2" model="jikimo_system_order.jikimo_system_order"> -->
<!-- <field name="name">Object 2</field> -->
<!-- <field name="value">20</field> -->
<!-- </record> -->
<!-- -->
<!-- <record id="object3" model="jikimo_system_order.jikimo_system_order"> -->
<!-- <field name="name">Object 3</field> -->
<!-- <field name="value">30</field> -->
<!-- </record> -->
<!-- -->
<!-- <record id="object4" model="jikimo_system_order.jikimo_system_order"> -->
<!-- <field name="name">Object 4</field> -->
<!-- <field name="value">40</field> -->
<!-- </record> -->
<!-- -->
</data>
</odoo>

View File

@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
from . import constant
from . import order_classify
from . import system_work_order
from . import work_order_template
from . import res_config_setting

View File

@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
# 工单状态
STATE_SELECTION = [('draft', u'草稿'), ('unconfirmed', u'待确认'), ('pending', u'待处理'),
('processed', u'已处理待评分'), ('completed', u'已完成'), ('closed', u'已关闭')]
GRADE = [('1', '1非常不满意'), ('2', '2不满意'), ('3', '3一般'), ('4', '4满意'), ('5', '5非常满意')]

View File

@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
from odoo.exceptions import ValidationError
class OrderClassify(models.Model):
_name = 'order.classify'
_order = 'sequence, name'
@api.constrains('name')
def check_base_name(self):
"""类型名称唯一"""
name_obj = self.env['order.classify'].search([('name', '=', self.name)])
if len(name_obj) >= 2:
raise ValidationError(u'该类型已存在')
# 名称
name = fields.Char(string=u'名称', size=20)
# 排序
sequence = fields.Integer(default=10)
# 是否有效
state = fields.Boolean(default=True, string='是否有效')

View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
import logging
from odoo import api, fields, models, _
_logger = logging.getLogger(__name__)
class ResModelWeConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
lost_agent_id = fields.Char('企微通知应用ID')
@api.model
def get_values(self):
"""
重载获取参数的方法,参数都存在系统参数中
:return:
"""
values = super(ResModelWeConfigSettings, self).get_values()
config = self.env['ir.config_parameter'].sudo()
lost_agent_id = config.get_param('lost_agent_id', default='')
values.update(
lost_agent_id=lost_agent_id,
)
return values
def set_values(self):
super(ResModelWeConfigSettings, self).set_values()
ir_config = self.env['ir.config_parameter'].sudo()
ir_config.set_param("lost_agent_id", self.lost_agent_id or "")

View File

@@ -0,0 +1,183 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
from odoo.exceptions import ValidationError
from odoo import exceptions
from .constant import STATE_SELECTION, GRADE
import datetime
import logging
class SystemWorkOrder(models.Model):
_name = 'system.work.order'
_inherit = ['mail.thread', 'mail.activity.mixin']
_order = 'date desc'
_description = u'系统工单'
_rec_name = 'order_number'
def get_is_technicist(self):
self._cr.execute(
"select u.id from res_users u left join res_groups_users_rel r on r.uid = u.id where r.gid in (select g.id from res_groups g where g.name = '技术员权限') and u.id ='%s'",
(self.env.user.id,))
hr = self._cr.dictfetchall()
if len(hr) > 0:
return True
else:
return False
# def get_user_department_id(self):
# """根据用户id系统员工id"""
# employee = self.env['hr.employee'].sudo().search([('user_id', '=', self.env.uid)], limit=1)
# if employee:
# if len(employee) > 0:
# if not employee.department_id:
# raise exceptions.Warning(u'您当前使用的用户没有所属部门')
# return employee.department_id
# else:
# return False
# else:
# raise exceptions.Warning(u'您当前使用的用户没有关联员工')
@api.onchange('order_template_id')
def get_title(self):
"""选择模板自动填充"""
if self.order_template_id:
self.title = self.order_template_id.title_template
self.text = self.order_template_id.text_template
# 工单编号
order_number = fields.Char(string=u'工单编号', default='/')
# 紧急程度
urgency_degree = fields.Selection([('0', u'0星'), ('1', u'一星'), ('2', u'二星'), ('3', u'三星'), ('4', u'四星'),
('5', u'五星')], string=u'紧急程度', help='五星为最紧急!', default='5')
# 工单分类(可以配置,并调整优先级)
order_type = fields.Many2one('order.classify', string=u'工单分类', domain=[('state', '=', True)])
# 发起人所属公司(res.company)
initiator_company_id = fields.Many2one('res.company', string=u'发起人所属公司', default=lambda self: self.env.user.company_id)
# 发起人部门(hr.department)
# initiator_department_id = fields.Many2one('hr.department', string=u'发起人部门', default=get_user_department_id)
# 发起人(hr.employee)
initiator_id = fields.Many2one('res.users', string=u'发起人', default=lambda self: self.env.user)
# 发起时间
date = fields.Datetime(string=u'发起时间', default=lambda self: fields.datetime.now())
# 确认人
confirm_id = fields.Many2one('res.users', string=u'确认人')
# 确认日期
confirmation_date = fields.Datetime(string=u'确认时间')
# 模板
order_template_id = fields.Many2one('work.order.template', string=u'模板', domain=[('state', '=', True)])
# 标题
title = fields.Char(string=u'标题')
# 正文
text = fields.Html(string=u'正文')
# 状态[草稿\待确认\待处理\已处理\已关闭]
state = fields.Selection(STATE_SELECTION, default='draft', string=u'状态')
# 关闭原因
close_cause = fields.Text(string=u'关闭问题原因')
# 关闭时间
close_time = fields.Datetime(string=u'关闭问题时间')
# 关闭人
close_user_id = fields.Many2one('res.users', string=u'关闭人')
# 解决人
solve_people_id = fields.Many2one('res.users', string=u'解决人')
# 用户实际问题
users_problem = fields.Text(string=u'用户实际问题')
# 最终解决方案
solution = fields.Text(string=u'最终解决方案')
# 判断是否为技术人员
# is_technicist = fields.Boolean(string=u'是否为技术人员', default=get_is_technicist)
# 打分
grade = fields.Selection(GRADE, string=u'评分')
# 评价按钮的显示
is_display = fields.Boolean('控制显示评价按钮', compute='compute_is_display')
def compute_is_display(self):
for item in self:
if item.state == 'processed' and self.env.user.id == item.initiator_id.id:
item.is_display = True
else:
item.is_display = False
@api.onchange('order_type')
def _onchange_order_type(self):
self.order_template_id = None
self.title = None
self.text = None
@api.model
def create(self, vals):
# 创建编号
if vals.get('order_number', '/') == '/':
vals['order_number'] = self.env['ir.sequence'].get('system.work.order') or '/'
return super(SystemWorkOrder, self).create(vals)
def do_draft(self, order=None):
"""状态草稿"""
bill = self
if order:
bill = order
if bill.state == 'unconfirmed':
state_remark = u'待确认 --> 草稿'
# bill.message_post(u'操作人:%s操作时间%s状态变更过程%s' % (self.env.user.name,
# (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
bill.state = 'draft'
def do_unconfirmed(self):
"""状态待确认"""
if self.state == 'draft':
state_remark = u'草稿 --> 待确认'
# self.message_post(u'操作人:%s操作时间%s状态变更过程%s' % (
# self.env.user.name,
# (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
self.state = 'unconfirmed'
# 获取通知人
objs = self.env['system.order.notice'].search([])
user_ids = objs.notice_user_ids.filtered(lambda item: item.we_employee_id not in ['', False])
we_employee_ids = user_ids.mapped('we_employee_id')
lost_agent_id = self.env['ir.config_parameter'].sudo().get_param('lost_agent_id')
wechat = self.env['we.config'].sudo().get_wechat(agent_id=lost_agent_id)
# agent_id, user_ids, content
content = """您有一张工单<font color=\"warning\">待处理</font>:**工单标题:{2}**
>创建人:{1}
>提交时间:{3}
>紧急程度:{0}
请查看工单消息,并及时处理!
""".format(self.urgency_degree,
self.initiator_id.name, self.title, (self.date + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M'))
for we_employee_id in we_employee_ids:
try:
wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_id, content=content)
except Exception as e:
logging.error('工单处理发送消息异常%s' % str(e))
return True
def do_pending(self):
"""状态待处理"""
if self.state == 'unconfirmed':
state_remark = u'待确认 --> 待处理'
# self.message_post(u'操作人:%s操作时间%s状态变更过程%s' % (
# self.env.user.name,
# (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
self.state = 'pending'
self.confirm_id = self.env.user
self.confirmation_date = fields.datetime.now()
return True
def urned_off(self):
"""状态关闭"""
if self.close_cause:
self.state = 'closed'
self.close_time = fields.datetime.now()
else:
raise ValidationError(u'请注明关闭原因')
return True
def unlink(self):
for item in self:
if item.state != "draft":
raise ValidationError(u'只能删除状态为【草稿】的工单。')
elif item.env.uid != item.initiator_id.id:
raise ValidationError(u'非本人不能删除')
else:
super(SystemWorkOrder, item).unlink()

View File

@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
class WorkOrderTemplate(models.Model):
_name = 'work.order.template'
_order = 'num'
# 编号
num = fields.Char(string=u'编号', default='/')
# 名称
name = fields.Char(string=u'模板名称', required="1")
# 分类
work_order_type = fields.Many2one('order.classify', string=u'系统工单分类', domain=[('state', '=', True)])
# 模板标题
title_template = fields.Char(string=u'模板标题')
# 模板正文
text_template = fields.Html(string=u'模板正文')
# 模板说明
template_explain = fields.Text(string=u'模板说明')
# 是否有效
state = fields.Boolean(default=True, string=u'是否有效')
@api.model
def create(self, vals):
# 创建编号
if vals.get('num', '/') == '/':
vals['num'] = self.env['ir.sequence'].get('work.order.template') or '/'
return super(WorkOrderTemplate, self).create(vals)
class SystemOrderNotice(models.Model):
_name = 'system.order.notice'
_description = '工单处理人设置'
notice_user_ids = fields.Many2many('res.users', string='工单处理人')

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="0"> <!-- noupdate表示当模块升级时是否更新本条数据-->
<!--运维权限组-->
<record id="group_operations_permissions_rwc" model="res.groups">
<field name="name">运维权限</field>
</record>
<record id="system_order_user_rule" model="ir.rule">
<field name="name">用户访问工单信息</field>
<field name="model_id" ref="model_system_work_order"/>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="domain_force">[('initiator_id', '=', user.id)]</field>
</record>
<record id="system_order_group_operations_rule" model="ir.rule">
<field name="name">运维访问工单信息</field>
<field name="model_id" ref="model_system_work_order"/>
<field name="groups" eval="[(4, ref('jikimo_system_order.group_operations_permissions_rwc'))]"/>
<field name="domain_force">[(1, '=', 1)]</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,16 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
inside_system_order_classify_r,jikimo_system_order.order_classify,model_order_classify,,1,1,1,1
inside_system_work_order_rc,jikimo_system_order.system_work_order,model_system_work_order,,1,1,1,1
inside_work_order_template_r,jikimo_system_order.work_order_template,model_work_order_template,,1,1,1,1
inside_system_order_classify_rwc,jikimo_system_order.order_classify,model_order_classify,group_operations_permissions_rwc,1,1,1,0
inside_system_work_order_rwc,jikimo_system_order.system_work_order,model_system_work_order,group_operations_permissions_rwc,1,1,1,0
inside_work_order_template_rwc,jikimo_system_order.work_order_template,model_work_order_template,group_operations_permissions_rwc,1,1,1,0
order_close_wizard_group_user,jikimo_system_order.order_close_wizard,model_order_close_wizard,base.group_user,1,1,1,1
order_other_wizard_group_user,jikimo_system_order.order_other_wizard,model_order_other_wizard,base.group_user,1,1,1,1
order_technician_wizard_group_user,jikimo_system_order.order_technician_wizard,model_order_technician_wizard,base.group_user,1,1,1,1
system_work_order_wizard_group_user,jikimo_system_order.system_work_order_wizard,model_system_work_order_wizard,base.group_user,1,1,1,1
system_order_notice_group_user,jikimo_system_order.system_order_notice,model_system_order_notice,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 inside_system_order_classify_r jikimo_system_order.order_classify model_order_classify 1 1 1 1
3 inside_system_work_order_rc jikimo_system_order.system_work_order model_system_work_order 1 1 1 1
4 inside_work_order_template_r jikimo_system_order.work_order_template model_work_order_template 1 1 1 1
5 inside_system_order_classify_rwc jikimo_system_order.order_classify model_order_classify group_operations_permissions_rwc 1 1 1 0
6 inside_system_work_order_rwc jikimo_system_order.system_work_order model_system_work_order group_operations_permissions_rwc 1 1 1 0
7 inside_work_order_template_rwc jikimo_system_order.work_order_template model_work_order_template group_operations_permissions_rwc 1 1 1 0
8 order_close_wizard_group_user jikimo_system_order.order_close_wizard model_order_close_wizard base.group_user 1 1 1 1
9 order_other_wizard_group_user jikimo_system_order.order_other_wizard model_order_other_wizard base.group_user 1 1 1 1
10 order_technician_wizard_group_user jikimo_system_order.order_technician_wizard model_order_technician_wizard base.group_user 1 1 1 1
11 system_work_order_wizard_group_user jikimo_system_order.system_work_order_wizard model_system_work_order_wizard base.group_user 1 1 1 1
12 system_order_notice_group_user jikimo_system_order.system_order_notice model_system_order_notice base.group_user 1 1 1 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
# ---------- 工单通知处理人设置 ------------
<record model="ir.ui.view" id="tree_system_order_notice_view">
<field name="name">tree.system.order.notice</field>
<field name="model">system.order.notice</field>
<field name="arch" type="xml">
<tree string="工单处理人设置" editable="top">
<field name="notice_user_ids" widget="many2many_tags" required="1" options="{'no_create': True, 'no_edit': True}"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="search_system_order_notice_view">
<field name="name">search.system.order.notice</field>
<field name="model">system.order.notice</field>
<field name="arch" type="xml">
<search string="工单处理人设置">
<field name="notice_user_ids" string="模糊搜索"
filter_domain="[('notice_user_ids', 'ilike', self)]"/>
<separator></separator>
<field name="notice_user_ids" string="处理人"/>
</search>
</field>
</record>
<record model="ir.actions.act_window" id="action_system_order_notice_view">
<field name="name">工单处理人</field>
<field name="res_model">system.order.notice</field>
<field name="view_mode">tree</field>
<field name="domain">[]</field>
<field name="context">{}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
[工单处理人] 还没有哦!点左上角的[创建]按钮,沙发归你了!
</p>
<p>
</p>
</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="res_config_settings_we_view_form_inherit" model="ir.ui.view">
<field name="name">res.config.settings.we.view.form.inherit.bpm</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="base.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[hasclass('app_settings_block')]/div[6]" position="after">
<div>
<h2>企微通知应用ID</h2>
<div class="row mt16 o_settings_container" id="jd_api">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane"/>
<div class="o_setting_right_pane">
<div class="text-muted">
<label for="lost_agent_id"/>
<field name="lost_agent_id"/>
</div>
</div>
</div>
</div>
</div>
</xpath>
</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="True">
<!-- 工单流水号 -->
<record id="seq_work_order" model="ir.sequence">
<field name="name">seq_work_order</field>
<field name="company_id"/>
<field name="code">system.work.order</field>
<field name="prefix">SO%(year)s%(month)s%(day)s</field>
<field name="padding">1</field>
</record>
<!-- 模板编号 -->
<record id="seq_order_template" model="ir.sequence">
<field name="name">seq_order_template</field>
<field name="company_id"/>
<field name="code">work.order.template</field>
<field name="prefix">TL</field>
<field name="padding">1</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,243 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<!--工单信息-->
<record model="ir.ui.view" id="work_order_tree">
<field name="name">工单信息</field>
<field name="model">system.work.order</field><!--对应表单名称-->
<field name="arch" type="xml">
<tree>
<field name="state" widget="badge" decoration-primary="state == 'draft'"
decoration-success="state in ('processed', 'completed')"
decoration-danger="state == 'pending'" decoration-warning="state in ('unconfirmed')"/>
<field name="order_number"/>
<field name="title"/>
<field name="initiator_id"/>
<field name="date"/>
</tree>
</field>
</record>
<!--新建系统工单-->
<record model="ir.ui.view" id="ork_order_form">
<field name="name">新建系统工单</field>
<field name="model">system.work.order</field>
<field name="arch" type="xml">
<form>
<header>
<field name="is_display" invisible="1"/>
<button string='提交' class="oe_highlight" states="draft"
type="object" name="do_unconfirmed"
attrs="{'invisible': [('state', '!=', 'draft')]}"/>
<button string='追回编辑' states="unconfirmed"
type="action" name="%(system_work_order_wizard_view_act_window)d"
context="{'explain':'确认要执行此操作吗?','object_name':'system.work.order','function_name':'do_draft','object_id':id}"/>
<button name="do_pending" states="unconfirmed"
string="确认可处理" type="object" class="oe_highlight"
groups="jikimo_system_order.group_operations_permissions_rwc"/>
<button string='处理工单' class="oe_highlight" states="pending"
type="action" name="%(launch_order_technician_wizard)d"
groups="jikimo_system_order.group_operations_permissions_rwc"/>
<button string='评价' class="oe_highlight" attrs="{'invisible': [('is_display', '=', False)]}"
type="action" name="%(launch_order_other_wizard)d" context="{'active_id':id}"/>
<button name="%(launch_order_close_wizard)d" string="关闭该工单"
attrs="{'invisible': ['|',('state', '=', 'draft'),'|',('state','=','completed'),('state','=','closed')]}"
type="action" context="{'active_id':id}"/>
<field name="state" widget="statusbar"/>
</header>
<sheet>
<group>
<!-- <label for="order_number" class="oe_edit_only"/>-->
<group>
<field name="order_number" required="True" readonly="1"/>
<field name="order_type" required="True" attrs="{'readonly': [('state', '!=', 'draft')]}" options="{'no_create': True}"/>
<field name="date" required="True" readonly="True"/>
<field name="order_template_id" attrs="{'readonly': [('state', '!=', 'draft')]}"
domain="[('work_order_type','=',order_type),('state','=',True)]" options="{'no_create': True}"/>
<field name="confirmation_date" readonly="True"/>
<field name="urgency_degree" required="True" attrs="{'readonly': [('state','!=','draft')]}" widget="priority"/>
</group>
<group>
<field name="initiator_company_id" required="True" readonly="True"/>
<!-- <field name="initiator_department_id" required="True" readonly="True"/>-->
<field name="initiator_id" required="True" readonly="True"/>
<field name="confirm_id" readonly="True"/>
<field name="solve_people_id" readonly="True"/>
<field name="close_user_id" readonly="True"/>
</group>
<group>
<field name="title" attrs="{'readonly': [('state', '!=', 'draft')]}" required="True"/>
</group>
</group>
<notebook>
<page string="工单内容">
<field name="text" attrs="{'readonly': [('state','!=','draft')]}" required="True"/>
</page>
<page string="解决方案">
<group>
<field name="users_problem" readonly="True"/>
<field name="solution" readonly="True"/>
</group>
</page>
<page string="其他">
<group>
<field name="close_cause" readonly="True"/>
<field name="close_time" readonly="True"/>
<field name="grade" readonly="True"/>
</group>
</page>
</notebook>
</sheet>
<!-- <div class="oe_chatter">-->
<!-- <field name="message_follower_ids" widget="mail_followers"/>-->
<!-- <field name="message_ids" widget="mail_thread"/>-->
<!-- </div>-->
</form>
</field>
</record>
<!-- 搜索工单 -->
<record model="ir.ui.view" id="restaurant_search">
<field name="name">搜索工单</field>
<field name="model">system.work.order</field>
<field name="arch" type="xml">
<search>
<field string='发起人' name="initiator_id" widget="char" required="True"/>
<field string='标题' name="title" widget="char"/>
<field string='正文' name="text" widget="html"/>
<field string='实际问题' name="users_problem" widget="text"/>
<field string='解决方案' name="solution" widget="text"/>
<filter name="today" string="今日工单" domain="[('date','=',time.strftime('%%Y-%%m-%%d'))]"/>
<filter name="yesterday" string="昨日工单"
domain="[('date', '=', (context_today() - relativedelta(days=1)).strftime('%Y-%m-%d'))]"/>
<filter name="month" string="本月工单"
domain="[('date','&gt;=', time.strftime('%Y-%m-01')),('date','&lt;', (context_today() + relativedelta(months=1)).strftime('%Y-%m-01'))]"/>
<filter name="last_month" string="上月工单"
domain="[('date','&lt;', time.strftime('%Y-%m-01')),('date','&gt;=', (context_today() - relativedelta(months=1)).strftime('%Y-%m-01'))]"/>
<filter name="unconfirmed" string="待确认" domain="[('state','=','unconfirmed')]"/>
<filter name="pending" string="待处理" domain="[('state','=','pending')]"/>
<filter name="processed" string="已处理"
domain="['|', ('state','=','processed'), ('state','=','closed')]"/>
<group>
<filter string='发起人' name="initiator_id" context='{"group_by":"initiator_id"}'/>
<filter string='工单分类' name="order_type" context='{"group_by":"order_type"}'/>
<filter string='模板' name="order_template_id" context='{"group_by":"order_template_id"}'/>
<filter string='状态' name="state" context='{"group_by":"state"}'/>
<filter string='紧急情况' name="state" context='{"group_by":"urgency_degree"}'/>
</group>
</search>
</field>
</record>
<record model="ir.ui.view" id="graph_tree">
<field name="name">工单图表</field>
<field name="model">system.work.order</field><!--对应表单名称-->
<field name="arch" type="xml">
<pivot>
<field name="date" type="row" interval="day"/>
<field name="order_type" type="col"/>
<field name="state" type="row"/>
</pivot>
</field>
</record>
<!-- 工单 -->
<record model="ir.actions.act_window" id="system_order">
<field name="name">工单</field>
<field name="res_model">system.work.order</field>
<field name="view_mode">tree,form,search,graph,pivot</field>
</record>
<!--工单模板信息-->
<record model="ir.ui.view" id="order_template_tree">
<field name="name">工单模板信息</field>
<field name="model">work.order.template</field><!--对应表单名称-->
<field name="arch" type="xml">
<tree>
<field name="num"/>
<field name="name"/>
<field name="work_order_type"/>
<field name="title_template"/>
<field name="template_explain"/>
<field name="state"/>
</tree>
</field>
</record>
<!--新建系统工单模板-->
<record model="ir.ui.view" id="order_template_form">
<field name="name">新建系统工单模板</field>
<field name="model">work.order.template</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="num" required="True" readonly="True"/>
<field name="name" required="True"/>
<field name="work_order_type" required="True"/>
<field name="template_explain" required="True" style="height: 50px;"/>
<field name="title_template" required="True"/>
<field name="state"/>
<field name="text_template" required="True"/>
</group>
</sheet>
</form>
</field>
</record>
<!-- 工单模板 -->
<record model="ir.actions.act_window" id="work_template">
<field name="name">工单模板</field>
<field name="res_model">work.order.template</field>
<field name="view_mode">tree,form</field>
</record>
<!--工单分类信息-->
<record model="ir.ui.view" id="order_type_tree">
<field name="name">工单分类信息</field>
<field name="model">order.classify</field><!--对应表单名称-->
<field name="arch" type="xml">
<tree>
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="state"/>
</tree>
</field>
</record>
<!--新建系统分类信息-->
<record model="ir.ui.view" id="order_type_form">
<field name="name">新建系统分类信息</field>
<field name="model">order.classify</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name" required="True"/>
<field name="sequence" invisible="True"/>
<field name="state"/>
</group>
</sheet>
</form>
</field>
</record>
<!-- 工单分类 -->
<record model="ir.actions.act_window" id="classify">
<field name="name">工单分类</field>
<field name="res_model">order.classify</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem name="系统工单" id="work_order_1_list" web_icon="jikimo_system_order,static/description/icon.png"/>
<menuitem name="工单" id="work_order" parent="work_order_1_list" action="system_order"/>
<menuitem name="工单模板" id="work_order_template" parent="work_order_1_list" action="work_template" groups="jikimo_system_order.group_operations_permissions_rwc"/>
<menuitem name="工单分类" id="work_order_type" parent="work_order_1_list" action="classify" groups="jikimo_system_order.group_operations_permissions_rwc"/>
<menuitem name="工单设置" id="system_order_notice_user_config" parent="work_order_1_list" action="action_system_order_notice_view" groups="jikimo_system_order.group_operations_permissions_rwc"/>
</data>
</odoo>

View File

@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from . import order_other_wizard
from . import order_technician_wizard
from . import order_close_wizard
from . import system_work_order_wizard

View File

@@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
from odoo.addons.jikimo_system_order.models.constant import STATE_SELECTION
from odoo.exceptions import ValidationError
import datetime, logging
class OrderCloseWizard(models.TransientModel):
_name = 'order.close.wizard'
def get_context(self):
if self._context.get('active_id'):
obj = self.env['system.work.order'].browse(self._context.get('active_id'))
if obj.initiator_id.id != self.env.user.id:
raise ValidationError(u'非本人无法操作')
return obj
order_id = fields.Many2one('system.work.order', string=u'工单ID',
default=lambda self: self.get_context().id)
# 关闭原因
close_cause = fields.Text(string=u'关闭问题原因', default=lambda self: self.get_context().close_cause)
# 关闭时间
close_time = fields.Datetime(string=u'关闭问题时间', default=fields.datetime.now())
# 状态
state = fields.Selection(STATE_SELECTION, default='closed', string=u'状态')
# 关闭人
close_user_id = fields.Many2one('res.users', string=u'关闭人', default=lambda self: self.env.user)
def sure(self):
self.order_id.close_cause = self.close_cause
self.order_id.close_time = self.close_time
if self.order_id.state == 'unconfirmed':
state_remark = u'待确认 --> 已关闭'
if self.order_id.state == 'pending':
state_remark = u'待处理 --> 已关闭'
if self.order_id.state == 'processed':
state_remark = u'已处理待评分 --> 已关闭'
# self.order_id.message_post(u'操作人:%s操作时间%s状态变更过程%s' % (
# self.env.user.name,
# (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
self.order_id.state = self.state
self.order_id.close_user_id = self.close_user_id
we_employee_ids = []
if self.order_id.initiator_id.we_employee_id:
we_employee_ids.append(self.order_id.initiator_id.we_employee_id)
lost_agent_id = self.env['ir.config_parameter'].sudo().get_param('lost_agent_id')
wechat = self.env['we.config'].sudo().get_wechat(agent_id=lost_agent_id)
# agent_id, user_ids, content
content = """您提交的工单-**工单标题:{0}**-<font color=\"#FF0000\">**已关闭**</font>
>提交时间:{1}
>处理时间:{2}
>处理人:{3}
如有问题,请联系系统管理员!
""".format(self.order_id.title,
(self.order_id.date + datetime.timedelta(hours=8)).strftime(
'%Y-%m-%d %H:%M'), (datetime.datetime.now() + datetime.timedelta(
hours=8)).strftime('%Y-%m-%d %H:%M'), self.env.user.name or '')
# wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_ids, content=content)
for we_employee_id in we_employee_ids:
try:
wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_id, content=content)
except Exception as e:
logging.error('工单关闭发送消息异常%s' % str(e))
return {}

View File

@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
from odoo.exceptions import ValidationError
from odoo.addons.jikimo_system_order.models.constant import STATE_SELECTION, GRADE
import datetime
class OrderOtherWizard(models.TransientModel):
_name = 'order.other.wizard'
def get_context(self):
if self._context.get('active_id'):
obj = self.env['system.work.order'].browse(self._context.get('active_id'))
if obj.initiator_id.id != self.env.user.id:
raise ValidationError(u'非本人无法操作')
return obj
order_id = fields.Many2one('system.work.order', string=u'工单ID',
default=lambda self: self.get_context().id)
# 关闭时间
close_time = fields.Datetime(string=u'关闭时间', default=fields.datetime.now())
# 状态
state = fields.Selection(STATE_SELECTION, default='completed', string=u'状态')
# 打分
grade = fields.Selection(GRADE, string=u'评分')
# 关闭人
close_user_id = fields.Many2one('res.users', string=u'关闭人', default=lambda self: self.env.user)
def sure(self):
self.order_id.close_time = self.close_time
self.order_id.grade = self.grade
if self.order_id.state == 'processed':
state_remark = u'已处理待评分 --> 已完成'
# self.order_id.message_post(u'操作人:%s操作时间%s状态变更过程%s' % (
# self.env.user.name,
# (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
self.order_id.state = self.state
self.order_id.close_user_id = self.close_user_id
return {}

View File

@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
from odoo.addons.jikimo_system_order.models.constant import STATE_SELECTION
import datetime
import logging
class OrderTechnicianWizard(models.TransientModel):
_name = 'order.technician.wizard'
order_id = fields.Many2one('system.work.order', string=u'工单ID',
default=lambda self: self.env.context.get('active_id'))
# 解决人
solve_people_id = fields.Many2one('res.users', string=u'解决人', default=lambda self: self.env.user)
# 用户实际问题
users_problem = fields.Text(string=u'用户实际问题')
# 最终解决方案
solution = fields.Text(string=u'最终解决方案')
# 状态
state = fields.Selection(STATE_SELECTION, default='processed', string=u'状态')
def sure(self):
self.order_id.solve_people_id = self.solve_people_id
self.order_id.users_problem = self.users_problem
self.order_id.solution = self.solution
if self.order_id.state == 'pending':
state_remark = u'待处理 --> 已处理待评分'
# self.order_id.message_post(u'操作人:%s操作时间%s状态变更过程%s' % (
# self.env.user.name,
# (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
self.order_id.state = self.state
# 获取通知人
# objs = self.env['system.order.notice'].search([])
# user_ids = objs.notice_user_ids.filtered(lambda item: item.we_employee_id not in ['', False])
# we_employee_ids = user_ids.mapped('we_employee_id')
we_employee_ids = []
if self.order_id.initiator_id.we_employee_id:
we_employee_ids.append(self.order_id.initiator_id.we_employee_id)
print(we_employee_ids)
lost_agent_id = self.env['ir.config_parameter'].sudo().get_param('lost_agent_id')
wechat = self.env['we.config'].sudo().get_wechat(agent_id=lost_agent_id)
# agent_id, user_ids, content
content = """您提交的工单-**工单标题:{0}**-<font color=\"info\">**已处理**</font>
>提交时间:{1}
>处理反馈:{4}
>处理时间:{2}
>处理人:{3}
如有问题,请联系系统管理员!
""".format(self.order_id.title,
(self.order_id.date + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M'), (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M'), self.env.user.name or '', self.solution or '')
# wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_ids, content=content)
for we_employee_id in we_employee_ids:
try:
wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_id, content=content)
except Exception as e:
logging.error('工单处理发送消息异常%s' % str(e))
return {}

View File

@@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<!-- 技术员向导form-->
<record model="ir.ui.view" id="wizard_technician_form_view">
<field name="name">技术员向导</field>
<field name="model">order.technician.wizard</field>
<field name="arch" type="xml">
<form string="技术员编辑">
<group>
<field name="order_id" required="1" readonly="1"/>
<field name="solve_people_id" required="1"/>
<field name="users_problem" required="1" style="height: 50px;"/>
<field name="solution" required="1" style="height: 50px;"/>
</group>
<footer>
<button name="sure" string="确定" type="object" class="oe_highlight"/>
or
<button string="取消" class="oe_link" special="cancel"/>
</footer>
</form>
</field>
</record>
<record model="ir.actions.act_window" id="launch_order_technician_wizard">
<field name="name">技术员编辑</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">order.technician.wizard</field>
<field name="view_mode">form</field>
<field name="view_id" ref="wizard_technician_form_view"/>
<field name="context">{'display_default_code':False}</field>
<field name="target">new</field>
</record>
<!-- 其它向导form-->
<record model="ir.ui.view" id="wizard_other_form_view">
<field name="name">其它向导</field>
<field name="model">order.other.wizard</field>
<field name="arch" type="xml">
<form string="其它编辑">
<group>
<field name="order_id" required="1" readonly="1"/>
<field name="close_time" required="1" readonly="1"/>
<field name="grade" required="1"/>
<field name="close_user_id" required="1" readonly="1"/>
</group>
<footer>
<button name="sure" string="确定" type="object" class="oe_highlight"/>
or
<button string="取消" class="oe_link" special="cancel"/>
</footer>
</form>
</field>
</record>
<record model="ir.actions.act_window" id="launch_order_other_wizard">
<field name="name">其它编辑</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">order.other.wizard</field>
<field name="view_mode">form</field>
<field name="view_id" ref="wizard_other_form_view"/>
<field name="context">{'display_default_code':False}</field>
<field name="target">new</field>
</record>
<!--关闭向导form-->
<record model="ir.ui.view" id="wizard_close_form_view">
<field name="name">关闭向导</field>
<field name="model">order.close.wizard</field>
<field name="arch" type="xml">
<form string="关闭工单">
<group>
<field name="order_id" required="1" readonly="1"/>
<field name="close_cause" required="1" style="height: 50px;"/>
<field name="close_time" required="1" readonly="1"/>
<field name="close_user_id" required="1" readonly="1"/>
</group>
<footer>
<button name="sure" string="确定" type="object" class="oe_highlight"/>
or
<button string="取消" class="oe_link" special="cancel"/>
</footer>
</form>
</field>
</record>
<record model="ir.actions.act_window" id="launch_order_close_wizard">
<field name="name">关闭工单</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">order.close.wizard</field>
<field name="view_mode">form</field>
<field name="view_id" ref="wizard_close_form_view"/>
<field name="context">{'display_default_code':False}</field>
<field name="target">new</field>
</record>
<record id="system_work_order_wizard_view" model="ir.ui.view">
<field name="name">system_work_order_wizard_view</field>
<field name="model">system.work.order.wizard</field>
<field name="arch" type="xml">
<form string="二次确认">
<field name="explain" readonly="1"/>
<footer>
<button name="sure" string="确定" type="object" class="oe_highlight"/>
or
<button string="取消" class="oe_link" special="cancel"/>
</footer>
</form>
</field>
</record>
<record model="ir.actions.act_window" id="system_work_order_wizard_view_act_window">
<field name="name">二次确认</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">system.work.order.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,42 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2017/12/12 9:46
# @Author : GuoXiang
# @Site :
# @File : system_work_order_wizard.py
# @Software: PyCharm
# @Desc :
# @license : Copyright©2018 www.dasmaster.com All Rights Reserved.
# @Contact : xg1230205321@163.com
from odoo import models, api, fields
from odoo.exceptions import ValidationError
class SystemWorkOrderWizard(models.TransientModel):
_name = "system.work.order.wizard"
_description = u"追回确认"
def _get_explain(self):
if self._context.get('object_id'):
obj = self.env['system.work.order'].browse(self._context.get('object_id'))
if obj.initiator_id.id != self.env.user.id:
raise ValidationError(u'非本人无法操作')
if self._context.get('explain'):
return self._context["explain"]
explain = fields.Char(default=_get_explain)
def sure(self):
"""
确认
:return:
"""
if self._context.get('object_id') and self._context.get('object_name') and self._context.get(
'explain') and self._context.get('function_name'):
work_sheet_obj = self.env[self._context["object_name"]].search([('id', '=', int(self._context["object_id"]))])
class_name = self._context.get('object_name') # 获得对象类名
method_name = self._context.get('function_name') # 获得对象的方法
obj_function = getattr(self.env[class_name], method_name)
obj_function(work_sheet_obj)

View File

@@ -61,12 +61,10 @@ class MrsMaterialModel(models.Model):
supplier_ids = fields.One2many('sf.supplier.sort', 'materials_model_id', string='供应商')
active = fields.Boolean('有效', default=True)
@api.onchange('gain_way')
def _check_gain_way(self):
if not self.gain_way:
raise UserError("请选择获取方式")
if self.gain_way in ['外协', '采购']:
if not self.supplier_ids:
@api.constrains("gain_way")
def _check_supplier_ids(self):
for item in self:
if item.gain_way in ('外协', '采购') and not item.supplier_ids:
raise UserError("请添加供应商")
@@ -94,8 +92,10 @@ class MrsProductionProcess(models.Model):
partner_process_ids = fields.Many2many('res.partner', 'process_ids', '加工工厂')
active = fields.Boolean('有效', default=True)
parameter_ids = fields.One2many('sf.production.process.parameter', 'process_id', string='可选参数')
category_id = fields.Many2one('sf.production.process.category')
category_id = fields.Many2one('sf.production.process.category', string='表面工艺类别')
# workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_process', required=True)
processing_day = fields.Float('加工天数/d')
travel_day = fields.Float('路途天数/d')
# class MrsProcessingTechnology(models.Model):
@@ -143,7 +143,10 @@ class MrsProductionProcessParameter(models.Model):
is_check = fields.Boolean(default=False)
# price = fields.Float('单价')
process_id = fields.Many2one('sf.production.process', string='表面工艺')
process_description = fields.Char(string='工艺描述')
materials_model_ids = fields.Many2many('sf.materials.model', 'applicable_material', string='适用材料')
processing_day = fields.Float('加工天数/d')
travel_day = fields.Float('路途天数/d')
active = fields.Boolean('有效', default=True)
def name_get(self):

View File

@@ -11,8 +11,8 @@ access_sf_machine_control_system,sf_machine_control_system,model_sf_machine_cont
access_sf_machine_control_system_admin,sf_machine_control_system_admin,model_sf_machine_control_system,base.group_system,1,1,1,0
access_sf_production_process_group_sale_director,sf_production_process_group_sale_director,model_sf_production_process,sf_base.group_sale_director,1,0,0,0
access_sf_production_process_group_sale_salemanager,sf_production_process_group_sale_salemanager,model_sf_production_process,sf_base.group_sale_salemanager,1,0,0,0
access_res_partner_category_group_sale_salemanager,res_partner_category_group_sale_salemanager,base.model_res_partner_category,sf_base.group_sale_salemanager,1,0,1,0
access_res_partner_category_group_sale_director,res_partner_category_group_sale_director,base.model_res_partner_category,sf_base.group_sale_director,1,0,1,0
access_res_partner_category_group_sale_salemanager,res_partner_category_group_sale_salemanager,base.model_res_partner_category,sf_base.group_sale_salemanager,1,1,1,0
access_res_partner_category_group_sale_director,res_partner_category_group_sale_director,base.model_res_partner_category,sf_base.group_sale_director,1,1,1,0
access_sf_production_process,sf_production_process,model_sf_production_process,base.group_user,1,1,1,0
access_sf_production_process_admin,sf_production_process_admin,model_sf_production_process,base.group_system,1,1,1,0
access_sf_production_materials,sf_production_materials,model_sf_production_materials,base.group_user,1,1,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
11 access_sf_machine_control_system_admin sf_machine_control_system_admin model_sf_machine_control_system base.group_system 1 1 1 0
12 access_sf_production_process_group_sale_director sf_production_process_group_sale_director model_sf_production_process sf_base.group_sale_director 1 0 0 0
13 access_sf_production_process_group_sale_salemanager sf_production_process_group_sale_salemanager model_sf_production_process sf_base.group_sale_salemanager 1 0 0 0
14 access_res_partner_category_group_sale_salemanager res_partner_category_group_sale_salemanager base.model_res_partner_category sf_base.group_sale_salemanager 1 0 1 1 0
15 access_res_partner_category_group_sale_director res_partner_category_group_sale_director base.model_res_partner_category sf_base.group_sale_director 1 0 1 1 0
16 access_sf_production_process sf_production_process model_sf_production_process base.group_user 1 1 1 0
17 access_sf_production_process_admin sf_production_process_admin model_sf_production_process base.group_system 1 1 1 0
18 access_sf_production_materials sf_production_materials model_sf_production_materials base.group_user 1 1 1 0

View File

@@ -27,9 +27,13 @@
<group>
<field name="code" readonly="1"/>
<field name="process_id" readonly="1"/>
<field name="process_description" readonly="1"/>
<field name="gain_way"/>
</group>
<group>
<field name="processing_day" readonly="1"/>
<field name="travel_day" readonly="1"/>
</group>
</group>
<notebook>
<page string="适用材料">
@@ -179,41 +183,46 @@
</div>
<group>
<group>
<field name="code"/>
<field name="code" readonly="1"/>
<field name="category_id" readonly="1"/>
</group>
<group>
<field name="processing_day" readonly="1"/>
<field name="travel_day" readonly="1"/>
</group>
<notebook>
<page string="可选参数">
<field name="parameter_ids">
<tree force_save="1">
<field name="code" readonly="1" force_save="1"/>
<field name="name"/>
<field name="gain_way"/>
<field name='process_id' default="default"/>
</tree>
<form>
<sheet>
<group>
<group>
<field name="code"/>
<field name="name" string="参数名"/>
</group>
<group>
<field name='process_id'/>
<field name="gain_way"/>
</group>
</group>
<notebook>
<page string="适用材料">
<field name="materials_model_ids"/>
</page>
</notebook>
</sheet>
</form>
</field>
</page>
</notebook>
</group>
<notebook>
<page string="可选参数">
<field name="parameter_ids">
<tree force_save="1">
<field name="code" readonly="1" force_save="1"/>
<field name="name"/>
<field name="gain_way"/>
<field name='process_id' default="default"/>
</tree>
<form>
<sheet>
<group>
<group>
<field name="code"/>
<field name="name" string="参数名"/>
</group>
<group>
<field name='process_id'/>
<field name="gain_way"/>
</group>
</group>
<notebook>
<page string="适用材料">
<field name="materials_model_ids"/>
</page>
</notebook>
</sheet>
</form>
</field>
</page>
</notebook>
<group>
<group>
<field name="remark"/>
@@ -251,7 +260,7 @@
<group>
<group>
<field name="materials_no" readonly="1" force_save="1"/>
<field name="gain_way" required="1"/>
<field name="gain_way" required="0"/>
<field name="tensile_strength" required="1"/>
<field name="hardness" required="1"/>
<field name="density" readonly="1"/>
@@ -270,9 +279,9 @@
<notebook>
<page string="供应商">
<field name='supplier_ids' class="supplier_ids_set_css">
<tree editable='bottom'>
<tree editable='bottom' delete="1">
<field name="sequence" widget="handle" string="序号"/>
<field name="partner_id" string="名称"/>
<field name="partner_id" string="名称" options="{'no_create': True}"/>
</tree>
</field>
</page>

View File

@@ -21,6 +21,7 @@ class Http(models.AbstractModel):
def _auth_method_sf_token(cls):
# 从headers.environ中获取对方传过来的token,timestamp,加密的校验字符串
datas = request.httprequest.headers.environ
_logger.info('datas:%s' % datas)
if 'HTTP_TOKEN' in datas:
_logger.info('token:%s' % datas['HTTP_TOKEN'])
# 查询密钥

View File

@@ -5,7 +5,7 @@ import requests
import cpca
# from odoo.exceptions import UserError
# from odoo.exceptions import ValidationError
from odoo import api, fields, models
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
_logger = logging.getLogger(__name__)
@@ -161,15 +161,28 @@ class JdEclp(models.Model):
url2 = config['bfm_url'] + '/api/get/jd/no'
response = requests.post(url2, json=json2, data=None)
# _logger.info('调用成功2', response.json()['result']['wbNo'])
self.carrier_tracking_ref = response.json()['result']['wbNo']
tem_ret = response.json().get('result')
if not tem_ret:
raise ValidationError('京东物流返回异常,请联系管理员')
self.carrier_tracking_ref = response.json()['result'].get('wbNo')
if not self.carrier_tracking_ref:
raise ValidationError('物流下单未成功,请联系管理员')
self.is_bill = True
self.logistics_status = '1'
# # 京东物流下单后,销售订单状态改为待收货
# self.env['sale.order'].search([('name', '=', self.origin)]).write({'scheduled_status': 'to receive'})
notification = {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': _('成功'),
'type': 'success',
'message': '物流下单成功',
'sticky': False,
'next': {'type': 'ir.actions.client', 'tag': 'reload'}
}
}
# else:
# raise UserError("选择京东物流才能下单呦")
return notification
def get_bill(self):
"""

View File

@@ -32,7 +32,7 @@
<field name="is_bill" invisible="True"/>
<field name="logistics_status" invisible="True"/>
<field name="logistics_way" invisible="True"/>
<button string="京东物流下单" name="create_order" type="object" confirm="是否确认物流下单" class="btn-primary"
<button string="物流下单" name="create_order" type="object" confirm="是否确认物流下单" class="btn-primary"
attrs="{'invisible': ['|', '|', '|', ('check_out', '!=', 'OUT'), ('state', '!=', 'assigned'), ('is_bill', '=', True), ('logistics_way', '=', '自提')]}"/>
<button string="获取物流面单" name="get_bill" type="object" confirm="是否获取物流面单" class="btn-primary"
attrs="{'invisible': ['|', '|', '|', '|', ('check_out', '!=', 'OUT'), ('state', '!=', 'assigned'), ('logistics_status', '=', '2'), ('is_bill', '=', False), ('logistics_way', '=', '自提')]}"/>
@@ -45,42 +45,50 @@
<field name="model">stock.picking</field>
<field name="inherit_id" ref="delivery.view_picking_withcarrier_out_form"/>
<field name="arch" type="xml">
<field name="location_id" position="after">
<field name="logistics_status" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="logistics_way" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
</field>
</field>
</record>
<xpath expr="//group//field[@name='carrier_id']" position="after">
<!-- <field name="senderNickName" domain="[('self.name', 'like', '%OUT%')]"/> -->
<field name="senderNickName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="expressItemName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="deliveryType" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverMobile" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverProvinceName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverCityName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverCountyName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverTownName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverCompany" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="remark" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="grossWeight" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="grossVolume" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="pickupBeginTime" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="bill_show" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="logistics_status"/>
<record id="tracking_only_view" model="ir.ui.view">
<field name="name">tracking only</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<xpath expr="//form//sheet//notebook//page[@name='operations']" position="after">
<page string="发货信息" name="tracking">
<group>
<group>
<field name="senderNickName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="expressItemName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="grossWeight" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="grossVolume" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="deliveryType" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverMobile" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverProvinceName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverCityName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverCountyName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverTownName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="receiverCompany" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="remark" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
<field name="pickupBeginTime" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
</group>
<group>
<field name="logistics_status"/>
<field name="carrier_id"/>
<field name="carrier_tracking_ref"/>
<field name="bill_show" attrs="{'invisible': [('check_out', '!=', 'OUT')]}" string='面单下载'/>
<field name="bill_show" widget="pdf_viewer" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
</group>
</group>
</page>
</xpath>
<xpath expr="//group//field[@name='group_id']" position="after">
<field name="bill_show" widget="pdf_viewer" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
</xpath>
<!-- <xpath expr="//group[@name='other_infos']" position="after"> -->
<!-- <div> -->
<!-- <button string="京东物流下单" name="create_order" type="object" confirm="是否确认物流下单" class="btn-primary"/> -->
<!-- </div> -->
<!-- <div> -->
<!-- <button string="获取物流面单" name="get_bill" type="object" confirm="是否获取物流面单" class="btn-primary"/> -->
<!-- </div> -->
<!-- </xpath> -->
</field>
</record>
</odoo>

View File

@@ -22,6 +22,16 @@
<field name="company_id" ref="base.main_company"/>
</record>
<record id="stock_location_tool_dismantle" model="stock.location">
<field name="name">拆解</field>
<field name="location_id" ref="stock.stock_location_locations_virtual"/>
<field name="usage">internal</field>
<field name="barcode">DJCJ</field>
<field name="scrap_location">true</field>
<field name="active">true</field>
<field name="company_id" ref="base.main_company"/>
</record>
<record id="after_assembly_picking_in" model="stock.picking.type">
<field name="name">刀具组装入库</field>

View File

@@ -88,7 +88,7 @@
</div>
<field name="model_volume" string="体积[mm³]"/>
<field name="product_model_type_id" string="模型类型"/>
<field name="model_processing_panel" placeholder="例如R,U" string="加工面板"/>
<field name="model_processing_panel" placeholder="例如R,U" string="加工面板" readonly="1"/>
<field name="model_machining_precision"/>
<field name="model_process_parameters_ids" string="表面工艺参数"
widget="many2many_tags"

View File

@@ -123,128 +123,161 @@ class Machine_ftp(models.Model):
# workorder_ids = fields.One2many('mrp.workorder', 'machine_tool_id', string='工单')
# 机床配置项目
# ftp相关
ftp_num = fields.Char('ftp账号')
ftp_pwd = fields.Char('ftp密码')
ftp_host = fields.Char('ftp地址')
ftp_port = fields.Integer('ftp端口')
ftp_remote_path = fields.Char('机床ftp路径')
# 补偿值写入相关
x_compensation_node = fields.Char('x补偿值节点')
y_compensation_node = fields.Char('y补偿值节点')
# 数采配置相关
machine_ip = fields.Char('机床IP')
machine_signed = fields.Char('机床刷新间隔')
machine_status = fields.Char('机床在线状态')
machine_cnc_type = fields.Char('机床CNC型号')
machine_axis_count = fields.Char('机床轴总数')
machine_run_status = fields.Char('机床运行状态')
machine_emg_status = fields.Char('机床急停状态')
machine_cut_status = fields.Char('机床当前切削状态')
machine_mode = fields.Char('机床当前操作模式')
machine_spindle_load = fields.Char('机床主轴负载')
machine_x_mach = fields.Char('机床X轴机械坐标')
machine_x_abs_mach = fields.Char('机床X轴当前位置')
machine_x_rel_mach = fields.Char('机床X轴相对工件坐标')
machine_x_dis_mach = fields.Char('机床X轴目标距离')
machine_x_axis_load = fields.Char('机床X轴伺服轴负载')
machine_y_mach = fields.Char('机床Y轴机械坐标')
machine_y_abs_mach = fields.Char('机床Y轴当前位置')
machine_y_rel_mach = fields.Char('机床Y轴相对工件坐标')
machine_y_dis_mach = fields.Char('机床Y轴目标距离')
machine_y_axis_load = fields.Char('机床Y轴伺服轴负载')
machine_z_mach = fields.Char('机床Z轴机械坐标')
machine_z_abs_mach = fields.Char('机床Z轴当前位置')
machine_z_rel_mach = fields.Char('机床Z轴相对工件坐标')
machine_z_dis_mach = fields.Char('机床Z轴目标距离')
machine_z_axis_load = fields.Char('机床Z轴伺服轴负载')
machine_tool_num = fields.Char('机床当前刀位号')
machine_program = fields.Char('机床主程序名称')
machine_current_prg = fields.Char('机床当前执行指令')
machine_prg_seq = fields.Char('机床当前执行语句号')
machine_spindle_speed_set = fields.Char('机床设定主轴速度')
machine_act_spindle_speed = fields.Char('机床实际主轴转速')
machine_feed_speed_set = fields.Char('机床设定进给速度')
machine_act_feed_speed = fields.Char('机床实际进给速度')
machine_spindle_feed = fields.Char('机床主轴倍率')
machine_feed_rate = fields.Char('机床进给倍率')
machine_rapid_feed = fields.Char('机床快速移动倍率')
machine_run_time = fields.Char('机床运行时间')
machine_cut_time = fields.Char('机床切削时间')
machine_keep_alive_time = fields.Char('机床上电时间')
machine_circle_time = fields.Char('机床循环时间')
machine_product_counts = fields.Char('机床加工件数')
machine_system_date = fields.Char('机床系统日期')
machine_system_time = fields.Char('机床系统时间')
machine_alarm_msg = fields.Char('机床系统报警')
# # 机床配置项目
# # ftp相关
# ftp_num = fields.Char('ftp账号')
# ftp_pwd = fields.Char('ftp密码')
# ftp_host = fields.Char('ftp地址')
# ftp_port = fields.Integer('ftp端口')
# ftp_remote_path = fields.Char('机床ftp路径')
# # 补偿值写入相关
# x_compensation_node = fields.Char('x补偿值节点')
# y_compensation_node = fields.Char('y补偿值节点')
# # 数采配置相关
# machine_ip = fields.Char('机床IP')
# machine_signed = fields.Char('机床刷新间隔')
# machine_status = fields.Char('机床在线状态')
# machine_cnc_type = fields.Char('机床CNC型号')
# machine_axis_count = fields.Char('机床轴总数')
# machine_run_status = fields.Char('机床运行状态')
# machine_emg_status = fields.Char('机床急停状态')
# machine_cut_status = fields.Char('机床当前切削状态')
# machine_mode = fields.Char('机床当前操作模式')
# machine_spindle_load = fields.Char('机床主轴负载')
# machine_x_mach = fields.Char('机床X轴机械坐标')
# machine_x_abs_mach = fields.Char('机床X轴当前位置')
# machine_x_rel_mach = fields.Char('机床X轴相对工件坐标')
# machine_x_dis_mach = fields.Char('机床X轴目标距离')
# machine_x_axis_load = fields.Char('机床X轴伺服轴负载')
# machine_y_mach = fields.Char('机床Y轴机械坐标')
# machine_y_abs_mach = fields.Char('机床Y轴当前位置')
# machine_y_rel_mach = fields.Char('机床Y轴相对工件坐标')
# machine_y_dis_mach = fields.Char('机床Y轴目标距离')
# machine_y_axis_load = fields.Char('机床Y轴伺服轴负载')
# machine_z_mach = fields.Char('机床Z轴机械坐标')
# machine_z_abs_mach = fields.Char('机床Z轴当前位置')
# machine_z_rel_mach = fields.Char('机床Z轴相对工件坐标')
# machine_z_dis_mach = fields.Char('机床Z轴目标距离')
# machine_z_axis_load = fields.Char('机床Z轴伺服轴负载')
# machine_tool_num = fields.Char('机床当前刀位号')
# machine_program = fields.Char('机床主程序名称')
# machine_current_prg = fields.Char('机床当前执行指令')
# machine_prg_seq = fields.Char('机床当前执行语句号')
# machine_spindle_speed_set = fields.Char('机床设定主轴速度')
# machine_act_spindle_speed = fields.Char('机床实际主轴转速')
# machine_feed_speed_set = fields.Char('机床设定进给速度')
# machine_act_feed_speed = fields.Char('机床实际进给速度')
# machine_spindle_feed = fields.Char('机床主轴倍率')
# machine_feed_rate = fields.Char('机床进给倍率')
# machine_rapid_feed = fields.Char('机床快速移动倍率')
# machine_run_time = fields.Char('机床运行时间')
# machine_cut_time = fields.Char('机床切削时间')
# machine_keep_alive_time = fields.Char('机床上电时间')
# machine_circle_time = fields.Char('机床循环时间')
# machine_product_counts = fields.Char('机床加工件数')
# machine_system_date = fields.Char('机床系统日期')
# machine_system_time = fields.Char('机床系统时间')
# machine_alarm_msg = fields.Char('机床系统报警')
# 刀位配置
tool_num1 = fields.Char('刀位1')
tool_num2 = fields.Char('刀位2')
tool_num3 = fields.Char('刀位3')
tool_num4 = fields.Char('刀位4')
tool_num5 = fields.Char('刀位5')
tool_num6 = fields.Char('刀位6')
tool_num7 = fields.Char('刀位7')
tool_num8 = fields.Char('刀位8')
tool_num9 = fields.Char('刀位9')
tool_num10 = fields.Char('刀位10')
tool_num11 = fields.Char('刀位11')
tool_num12 = fields.Char('刀位12')
tool_num13 = fields.Char('刀位13')
tool_num14 = fields.Char('刀位14')
tool_num15 = fields.Char('刀位15')
tool_num16 = fields.Char('刀位16')
tool_num17 = fields.Char('刀位17')
tool_num18 = fields.Char('刀位18')
tool_num19 = fields.Char('刀位19')
tool_num20 = fields.Char('刀位20')
tool_num21 = fields.Char('刀位21')
tool_num22 = fields.Char('刀位22')
tool_num23 = fields.Char('刀位23')
tool_num24 = fields.Char('刀位24')
# # 刀位配置
# tool_num1 = fields.Char('刀位1')
# tool_num2 = fields.Char('刀位2')
# tool_num3 = fields.Char('刀位3')
# tool_num4 = fields.Char('刀位4')
# tool_num5 = fields.Char('刀位5')
# tool_num6 = fields.Char('刀位6')
# tool_num7 = fields.Char('刀位7')
# tool_num8 = fields.Char('刀位8')
# tool_num9 = fields.Char('刀位9')
# tool_num10 = fields.Char('刀位10')
# tool_num11 = fields.Char('刀位11')
# tool_num12 = fields.Char('刀位12')
# tool_num13 = fields.Char('刀位13')
# tool_num14 = fields.Char('刀位14')
# tool_num15 = fields.Char('刀位15')
# tool_num16 = fields.Char('刀位16')
# tool_num17 = fields.Char('刀位17')
# tool_num18 = fields.Char('刀位18')
# tool_num19 = fields.Char('刀位19')
# tool_num20 = fields.Char('刀位20')
# tool_num21 = fields.Char('刀位21')
# tool_num22 = fields.Char('刀位22')
# tool_num23 = fields.Char('刀位23')
# tool_num24 = fields.Char('刀位24')
# 机床采集项目
timestamp = fields.Datetime('时间戳', readonly=True)
signed = fields.Integer('刷新间隔', readonly=True)
status = fields.Boolean('在线状态', readonly=True)
time_on = fields.Char('总在线时长', readonly=True)
time_on_now = fields.Char('本次在线时长', readonly=True)
tool_num = fields.Integer('当前刀具', readonly=True)
program = fields.Char('当前程序', readonly=True)
run_status = fields.Selection([('0', '空闲中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')], string='运行状态',
status = fields.Boolean('机床在线状态', readonly=True)
run_status = fields.Selection([('0', '空闲中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')], string='机床运行状态',
readonly=True, default='0')
run_time = fields.Char('运行时长', readonly=True)
cut_time = fields.Char('总切削时长', readonly=True)
cut_status = fields.Selection([('0', '未切削'), ('1', '切削中'), ('2', '切削中'), ('3', '切削中')], string='切削状态',
readonly=True, default='0')
spindle_speed = fields.Char('主轴转速', readonly=True)
run_time = fields.Char('机床累计运行时长', readonly=True)
# 机床系统日期
system_date = fields.Char('机床系统日期', readonly=True)
# 机床系统时间
system_time = fields.Char('机床系统时间', readonly=True)
tool_num_process_time1 = fields.Char('刀位1', readonly=True, default='0')
tool_num_process_time2 = fields.Char('刀位2', readonly=True, default='0')
tool_num_process_time3 = fields.Char('刀位3', readonly=True, default='0')
tool_num_process_time4 = fields.Char('刀位4', readonly=True, default='0')
tool_num_process_time5 = fields.Char('刀位5', readonly=True, default='0')
tool_num_process_time6 = fields.Char('刀位6', readonly=True, default='0')
tool_num_process_time7 = fields.Char('刀位7', readonly=True, default='0')
tool_num_process_time8 = fields.Char('刀位8', readonly=True, default='0')
tool_num_process_time9 = fields.Char('刀位9', readonly=True, default='0')
tool_num_process_time10 = fields.Char('刀位10', readonly=True, default='0')
tool_num_process_time11 = fields.Char('刀位11', readonly=True, default='0')
tool_num_process_time12 = fields.Char('刀位12', readonly=True, default='0')
tool_num_process_time13 = fields.Char('刀位13', readonly=True, default='0')
tool_num_process_time14 = fields.Char('刀位14', readonly=True, default='0')
tool_num_process_time15 = fields.Char('刀位15', readonly=True, default='0')
tool_num_process_time16 = fields.Char('刀位16', readonly=True, default='0')
tool_num_process_time17 = fields.Char('刀位17', readonly=True, default='0')
tool_num_process_time18 = fields.Char('刀位18', readonly=True, default='0')
tool_num_process_time19 = fields.Char('刀位19', readonly=True, default='0')
tool_num_process_time20 = fields.Char('刀位20', readonly=True, default='0')
tool_num_process_time21 = fields.Char('刀位21', readonly=True, default='0')
tool_num_process_time22 = fields.Char('刀位22', readonly=True, default='0')
tool_num_process_time23 = fields.Char('刀位23', readonly=True, default='0')
tool_num_process_time24 = fields.Char('刀位24', readonly=True, default='0')
cut_time = fields.Char('机床累计切削时间', readonly=True)
cut_status = fields.Selection([('0', '未切削'), ('1', '切削中'), ('2', '切削中'), ('3', '切削中')], string='机床当前切削状态',
readonly=True, default='0')
# 当前程序名
program = fields.Char('机床当前程序', readonly=True)
# 当前刀具号
tool_num = fields.Integer('机床当前刀具号', readonly=True)
# 机床通电开机时间, 机床加工件数, 机床当前操作模式, 开始加工时间, 结束加工时间, 加工程序开始时间, 加工程序结束时间, 待机开始时间,
# 待机结束时间, 机床离线开始时间, 机床离线结束时间, 机床急停状态, 机床主程序名称, 程序运行的状态, 机床当前执行指令, 机床当前执行语句号
# 机床X轴当前位置, 机床Y轴当前位置, 机床Z轴当前位置
machine_power_on_time = fields.Char('机床通电开机时间', readonly=True)
product_counts = fields.Char('机床加工件数', readonly=True)
mode = fields.Char('机床当前操作模式', readonly=True)
start_time = fields.Char('开始加工时间', readonly=True)
end_time = fields.Char('结束加工时间', readonly=True)
program_start_time = fields.Char('加工程序开始时间', readonly=True)
program_end_time = fields.Char('加工程序结束时间', readonly=True)
standby_start_time = fields.Char('待机开始时间', readonly=True)
standby_end_time = fields.Char('待机结束时间', readonly=True)
offline_start_time = fields.Char('机床离线开始时间', readonly=True)
offline_end_time = fields.Char('机床离线结束时间', readonly=True)
emg_status = fields.Char('机床急停状态', readonly=True)
program_name = fields.Char('机床主程序名称', readonly=True)
program_status = fields.Char('程序运行状态', readonly=True)
current_program = fields.Char('机床当前执行指令', readonly=True)
current_program_seq = fields.Char('机床当前执行语句号', readonly=True)
x_abs_pos = fields.Char('机床X轴当前位置(mm)', readonly=True)
y_abs_pos = fields.Char('机床Y轴当前位置(mm)', readonly=True)
z_abs_pos = fields.Char('机床Z轴当前位置(mm)', readonly=True)
# 机床设定进给速度, 机床实际进给速度, 机床设定主轴转速, 机床实际主轴转速, 机床主轴负载, 机床X轴伺服轴负载, 机床Y轴伺服轴负载
# 机床Z轴伺服轴负载, 机床快速移动倍率, 机床进给倍率, 机床X轴机械坐标, 机床Y轴机械坐标, 机床Z轴机械坐标, 机床X轴相对工件坐标
# 机床Y轴相对工件坐标, 机床Z轴相对工件坐标, 机床X轴目标距离, 机床Y轴目标距离, 机床Z轴目标距离
feed_speed_set = fields.Char('机床设定进给速度(mm/min)', readonly=True)
act_feed_speed = fields.Char('机床实际进给速度(mm/min)', readonly=True)
spindle_speed_set = fields.Char('机床设定主轴转速(r/min)', readonly=True)
act_spindle_speed = fields.Char('机床实际主轴转速(r/min)', readonly=True)
spindle_load = fields.Char('机床主轴负载(%)', readonly=True)
x_axis_load = fields.Char('机床X轴伺服轴负载(%)', readonly=True)
y_axis_load = fields.Char('机床Y轴伺服轴负载(%)', readonly=True)
z_axis_load = fields.Char('机床Z轴伺服轴负载(%)', readonly=True)
rapid_feed = fields.Char('机床快速移动倍率(%)', readonly=True)
feed_rate = fields.Char('机床进给倍率(%)', readonly=True)
x_mach_coord = fields.Char('机床X轴机械坐标(mm)', readonly=True)
y_mach_coord = fields.Char('机床Y轴机械坐标(mm)', readonly=True)
z_mach_coord = fields.Char('机床Z轴机械坐标(mm)', readonly=True)
x_rel_coord = fields.Char('机床X轴相对工件坐标(mm)', readonly=True)
y_rel_coord = fields.Char('机床Y轴相对工件坐标(mm)', readonly=True)
z_rel_coord = fields.Char('机床Z轴相对工件坐标(mm)', readonly=True)
x_dis_coord = fields.Char('机床X轴目标距离(mm)', readonly=True)
y_dis_coord = fields.Char('机床Y轴目标距离(mm)', readonly=True)
z_dis_coord = fields.Char('机床Z轴目标距离(mm)', readonly=True)
# 故障报警时间, 故障报警信息, 故障消除时间(复原时间)
alarm_time = fields.Char('故障报警时间', readonly=True)
alarm_msg = fields.Char('故障报警信息', readonly=True)
clear_time = fields.Char('故障消除时间(复原时间)', readonly=True)
# 当前程序名, 机床累计运行时间, 机床系统日期, 机床系统时间, 当前刀具号, 机床循环时间
class WorkCenterBarcode(models.Model):
@@ -259,39 +292,59 @@ class WorkCenterBarcode(models.Model):
button_compensation_state = fields.Boolean(string='是否已经补偿', readonly=True)
button_up_all_state = fields.Boolean(string='是否已经全部下发', readonly=True)
machine_tool_id = fields.Many2one('sf.machine_tool.type', string='机床')
machine_tool_name = fields.Char(string='机床名称', default='未知机床', compute='_run_info', readonly=True)
machine_tool_type_id = fields.Char(string='机床型号', default='未知型号', compute='_run_info', readonly=True)
machine_tool_status = fields.Boolean(string='在线状态', compute='_run_info', readonly=True)
# machine_tool_name = fields.Char(string='机床名称', default='未知机床', compute='_run_info', readonly=True)
# machine_tool_type_id = fields.Char(string='机床型号', default='未知型号', compute='_run_info', readonly=True)
# machine_tool_status = fields.Boolean(string='在线状态', compute='_run_info', readonly=True)
# machine_tool_run_status = fields.Selection([('0', '关机中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')],
# string='运行状态', compute='_run_info', readonly=True, default='0')
# machine_tool_timestamp = fields.Datetime('时间戳', compute='_run_info', readonly=True)
# machine_tool_time_on = fields.Char('总在线时长', compute='_run_info', readonly=True)
# machine_tool_time_on_now = fields.Char('本次在线时长', compute='_run_info', readonly=True)
# machine_tool_tool_num = fields.Integer('当前刀具', compute='_run_info', readonly=True)
# machine_tool_program = fields.Char('当前程序', compute='_run_info', readonly=True)
# machine_tool_machine_ip = fields.Char('机床IP', compute='_run_info', readonly=True)
# machine_tool_cut_status = fields.Selection([('0', '未切削'), ('1', '切削中'), ('2', '切削中'), ('3', '切削中')],
# string='切削状态', compute='_run_info', readonly=True, default='0')
# machine_tool_compensation_value_x = fields.Char('x补偿值', compute='_run_info', readonly=True)
# machine_tool_compensation_value_y = fields.Char('y补偿值', compute='_run_info', readonly=True)
machine_tool_name = fields.Char(string='机床名称', default='未知机床', readonly=True)
machine_tool_type_id = fields.Char(string='机床型号', default='未知型号', readonly=True)
machine_tool_status = fields.Boolean(string='在线状态', readonly=True)
machine_tool_run_status = fields.Selection([('0', '关机中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')],
string='运行状态', compute='_run_info', readonly=True, default='0')
machine_tool_timestamp = fields.Datetime('时间戳', compute='_run_info', readonly=True)
machine_tool_time_on = fields.Char('总在线时长', compute='_run_info', readonly=True)
machine_tool_time_on_now = fields.Char('本次在线时长', compute='_run_info', readonly=True)
machine_tool_tool_num = fields.Integer('当前刀具', compute='_run_info', readonly=True)
machine_tool_program = fields.Char('当前程序', compute='_run_info', readonly=True)
machine_tool_machine_ip = fields.Char('机床IP', compute='_run_info', readonly=True)
string='运行状态', readonly=True, default='0')
machine_tool_timestamp = fields.Datetime('时间戳', readonly=True)
machine_tool_time_on = fields.Char('总在线时长', readonly=True)
machine_tool_time_on_now = fields.Char('本次在线时长', readonly=True)
machine_tool_tool_num = fields.Integer('当前刀具', readonly=True)
machine_tool_program = fields.Char('当前程序', readonly=True)
machine_tool_machine_ip = fields.Char('机床IP', readonly=True)
machine_tool_cut_status = fields.Selection([('0', '未切削'), ('1', '切削中'), ('2', '切削中'), ('3', '切削中')],
string='切削状态', compute='_run_info', readonly=True, default='0')
machine_tool_compensation_value_x = fields.Char('x补偿值', compute='_run_info', readonly=True)
machine_tool_compensation_value_y = fields.Char('y补偿值', compute='_run_info', readonly=True)
string='切削状态', readonly=True, default='0')
machine_tool_compensation_value_x = fields.Char('x补偿值', readonly=True)
machine_tool_compensation_value_y = fields.Char('y补偿值', readonly=True)
# 工单状态
delivery_records = fields.One2many('delivery.record', 'workorder_id', string="下发记录")
@api.depends('equipment_id.timestamp')
def _run_info(self):
# self.machine_tool_name = '1号机床'
self.machine_tool_name = self.equipment_id.name
self.machine_tool_type_id = self.equipment_id.type_id.name
self.machine_tool_status = self.equipment_id.status
self.machine_tool_run_status = self.equipment_id.run_status
self.machine_tool_timestamp = self.equipment_id.timestamp
self.machine_tool_time_on = self.equipment_id.time_on
self.machine_tool_time_on_now = self.equipment_id.time_on_now
self.machine_tool_tool_num = self.equipment_id.tool_num
self.machine_tool_program = self.equipment_id.program
self.machine_tool_machine_ip = self.equipment_id.machine_ip
self.machine_tool_cut_status = self.equipment_id.cut_status
self.machine_tool_compensation_value_x = self.compensation_value_x
self.machine_tool_compensation_value_y = self.compensation_value_y
# self.machine_tool_name = self.equipment_id.name
# self.machine_tool_type_id = self.equipment_id.type_id.name
# self.machine_tool_status = self.equipment_id.status
# self.machine_tool_run_status = self.equipment_id.run_status
# self.machine_tool_timestamp = self.equipment_id.timestamp
# self.machine_tool_time_on = self.equipment_id.time_on
# self.machine_tool_time_on_now = self.equipment_id.time_on_now
# self.machine_tool_tool_num = self.equipment_id.tool_num
# self.machine_tool_program = self.equipment_id.program
# self.machine_tool_machine_ip = self.equipment_id.machine_ip
# self.machine_tool_cut_status = self.equipment_id.cut_status
# self.machine_tool_compensation_value_x = self.compensation_value_x
# self.machine_tool_compensation_value_y = self.compensation_value_y
pass
def compensation(self):

View File

@@ -27,6 +27,7 @@
</xpath>
<xpath expr="//field[@name='production_id']" position="before">
<field name="product_tmpl_name"/>
<field name="rfid_code"/>
</xpath>
</field>
</record>

View File

@@ -5,10 +5,10 @@
<field name="model">mrp.workorder</field>
<field name="inherit_id" ref="sf_manufacturing.view_mrp_production_workorder_tray_form_inherit_sf"/>
<field name="arch" type="xml">
<!-- <xpath expr="//header" position="inside">-->
<!-- <button string="程序下载" name="cnc_file_download" type="object" class="oe_highlight" attrs='{"invisible": ["|",-->
<!-- ("user_permissions","=",False),("routing_type","!=","CNC加工")]}'/>-->
<!-- </xpath>-->
<!-- <xpath expr="//header" position="inside">&ndash;&gt;-->
<!-- <button string="程序下载" name="cnc_file_download" type="object" class="oe_highlight" attrs='{"invisible": ["|",-->
<!-- ("user_permissions","=",False),("routing_type","!=","CNC加工")]}'/>-->
<!-- </xpath>-->
<xpath expr="//page//field[@name='cnc_ids']" position="before">
<group>
<group>
@@ -17,15 +17,15 @@
</group>
<div>
<div>
<field name="button_compensation_state" attrs='{"invisible": ["|",
("state","!=","progress"),("user_permissions","=",False)]}'/>
<!-- <field name="button_compensation_state" attrs='{"invisible": ["|",-->
<!-- ("state","!=","progress"),("user_permissions","=",False)]}'/>-->
<!-- <span>&#32;</span>-->
<button name="button_send_program_again" type="object" string="重新下发NC程序"
class="btn-primary"
confirm="是否确认重新下发NC程序"
groups="sf_base.group_sf_order_user,sf_base.group_sf_equipment_user"
attrs="{'invisible': ['|', '|', '|',('routing_type','!=','装夹预调'),('state','in',['done', 'cancel',
'progress']),('processing_drawing','=',False),('is_send_program_again','=',True)]}"/>
<!-- <button name="button_send_program_again" type="object" string="重新下发NC程序"-->
<!-- class="btn-primary"-->
<!-- confirm="是否确认重新下发NC程序"-->
<!-- groups="sf_base.group_sf_order_user,sf_base.group_sf_equipment_user"-->
<!-- attrs="{'invisible': ['|', '|', '|',('routing_type','!=','装夹预调'),('state','in',['done', 'cancel',-->
<!-- 'progress']),('processing_drawing','=',False),('is_send_program_again','=',True)]}"/>-->
<!-- <button string="一键补偿" name="compensation" type="object" confirm="是否确认下发补偿"-->
<!-- class="btn-primary" attrs='{"invisible": ["|",-->
<!-- ("state","!=","progress"),("user_permissions","=",False)]}'/>-->

View File

@@ -11,6 +11,7 @@
<field name="program_name"/>
<field name="functional_tool_type_id"/>
<field name="cutting_tool_name"/>
<field name="tool_state"/>
<field name="cutting_tool_no"/>
<field name="processing_type"/>
<field name="margin_x_y"/>

View File

@@ -19,7 +19,7 @@
<!-- attrs='{"invisible": ["|",("state","!=","progress"), -->
<!-- ("user_permissions","!=",True)]}'/> -->
<!-- <button string="下发" name="up" type="object" class="btn-primary" confirm="是否确认下发此程序" context="{'default_cnc_ids': cnc_ids}"/> -->
<button string="下发" name="up" type="object" class="btn-primary" confirm="是否确认下发此程序"
<!-- <button string="下发" name="up" type="object" class="btn-primary" confirm="是否确认下发此程序"-->
context="{'default_sf_cnc_processing_id': id}" attrs='{"invisible": ["|",("state","!=","progress"),
("user_permissions","!=",True)]}'/>
<!-- <button string="下发" name="up" type="object" class="btn-primary" confirm="是否确认下发此程序"-->

View File

@@ -13,294 +13,138 @@
<page string="机床运行状态" attrs="{'invisible': [('equipment_type', '!=', '机床')]}">
<group string='状态监控'>
<group>
<field name="timestamp"/>
<field name="signed"/>
<!-- <field name="timestamp"/> -->
<field name="status"/>
<field name="time_on"/>
<field name="time_on_now"/>
</group>
<group>
<field name="run_status"/>
<field name="run_time"/>
<field name="system_date"/>
</group>
<group>
<field name="cut_status"/>
<field name="cut_time"/>
<!-- <field name="cut_time"/> -->
<field name="program"/>
<field name="tool_num"/>
<field name="spindle_speed"/>
</group>
</group>
<!-- <group string='刀位统计'> -->
<!-- <group> -->
<!-- <group> -->
<!-- <field name="tool_num_process_time1"/> -->
<!-- <field name="tool_num_process_time5"/> -->
<!-- <field name="tool_num_process_time9"/> -->
<!-- <field name="tool_num_process_time13"/> -->
<!-- <field name="tool_num_process_time17"/> -->
<!-- <field name="tool_num_process_time21"/> -->
<!-- </group> -->
<!-- <group> -->
<!-- <field name="tool_num_process_time2"/> -->
<!-- <field name="tool_num_process_time6"/> -->
<!-- <field name="tool_num_process_time10"/> -->
<!-- <field name="tool_num_process_time14"/> -->
<!-- <field name="tool_num_process_time18"/> -->
<!-- <field name="tool_num_process_time22"/> -->
<!-- </group> -->
<!-- </group> -->
<!-- <group> -->
<!-- <group> -->
<!-- <field name="tool_num_process_time3"/> -->
<!-- <field name="tool_num_process_time7"/> -->
<!-- <field name="tool_num_process_time11"/> -->
<!-- <field name="tool_num_process_time15"/> -->
<!-- <field name="tool_num_process_time19"/> -->
<!-- <field name="tool_num_process_time23"/> -->
<!-- </group> -->
<!-- <group> -->
<!-- <field name="tool_num_process_time4"/> -->
<!-- <field name="tool_num_process_time8"/> -->
<!-- <field name="tool_num_process_time12"/> -->
<!-- <field name="tool_num_process_time16"/> -->
<!-- <field name="tool_num_process_time20"/> -->
<!-- <field name="tool_num_process_time24"/> -->
<!-- </group> -->
<!-- </group> -->
<!-- </group> -->
<!-- <div class="o_address_format"> -->
<!-- <label for="tool_num_process_time1" string="刀位1"/> -->
<!-- <field name="tool_num_process_time1" class="o_form_label"/> -->
<!-- <span>&amp;nbsp;</span> -->
<!-- <label for="tool_num_process_time2" string="刀位2"/> -->
<!-- <field name="tool_num_process_time2" class="o_form_label"/> -->
<!-- <span>&amp;nbsp;</span> -->
<!-- <label for="tool_num_process_time3" string="刀位3"/> -->
<!-- <field name="tool_num_process_time3" class="o_form_label"/> -->
<!-- <span>&amp;nbsp;</span> -->
<!-- <label for="tool_num_process_time4" string="刀位4"/> -->
<!-- <field name="tool_num_process_time4" class="o_form_label"/> -->
<!-- <div></div> -->
<!-- <label for="tool_num_process_time5" string="刀位5"/> -->
<!-- <field name="tool_num_process_time5" class="o_form_label"/> -->
<!-- <span>&amp;nbsp;</span> -->
<!-- <label for="tool_num_process_time6" string="刀位6"/> -->
<!-- <field name="tool_num_process_time6" class="o_form_label"/> -->
<!-- <span>&amp;nbsp;</span> -->
<!-- <label for="tool_num_process_time7" string="刀位7"/> -->
<!-- <field name="tool_num_process_time7" class="o_form_label"/> -->
<!-- <span>&amp;nbsp;</span> -->
<!-- <label for="tool_num_process_time8" string="刀位8"/> -->
<!-- <field name="tool_num_process_time8" class="o_form_label"/> -->
<!-- <div></div> -->
<!-- <label for="tool_num_process_time9" string="刀位9"/> -->
<!-- <field name="tool_num_process_time9" class="o_form_label"/> -->
<!-- <span>&amp;nbsp;</span> -->
<!-- <label for="tool_num_process_time10" string="刀位10"/> -->
<!-- <field name="tool_num_process_time10" class="o_form_label"/> -->
<!-- <span>&amp;nbsp;</span> -->
<!-- <label for="tool_num_process_time11" string="刀位11"/> -->
<!-- <field name="tool_num_process_time11" class="o_form_label"/> -->
<!-- <span>&amp;nbsp;</span> -->
<!-- <label for="tool_num_process_time12" string="刀位12"/> -->
<!-- <field name="tool_num_process_time12" class="o_form_label"/> -->
<!-- <div></div> -->
<!-- <label for="tool_num_process_time13" string="刀位13"/> -->
<!-- <field name="tool_num_process_time13" class="o_form_label"/> -->
<!-- <span>&amp;nbsp;</span> -->
<!-- <label for="tool_num_process_time14" string="刀位14"/> -->
<!-- <field name="tool_num_process_time14" class="o_form_label"/> -->
<!-- <span>&amp;nbsp;</span> -->
<!-- <label for="tool_num_process_time15" string="刀位15"/> -->
<!-- <field name="tool_num_process_time15" class="o_form_label"/> -->
<!-- <span>&amp;nbsp;</span> -->
<!-- <label for="tool_num_process_time16" string="刀位16"/> -->
<!-- <field name="tool_num_process_time16" class="o_form_label"/> -->
<!-- <div></div> -->
<!-- <label for="tool_num_process_time17" string="刀位17"/> -->
<!-- <field name="tool_num_process_time17" class="o_form_label"/> -->
<!-- <span>&amp;nbsp;</span> -->
<!-- <label for="tool_num_process_time18" string="刀位18"/> -->
<!-- <field name="tool_num_process_time18" class="o_form_label"/> -->
<!-- <span>&amp;nbsp;</span> -->
<!-- <label for="tool_num_process_time19" string="刀位19"/> -->
<!-- <field name="tool_num_process_time19" class="o_form_label"/> -->
<!-- <span>&amp;nbsp;</span> -->
<!-- <label for="tool_num_process_time20" string="刀位20"/> -->
<!-- <field name="tool_num_process_time20" class="o_form_label"/> -->
<!-- <div></div> -->
<!-- <label for="tool_num_process_time21" string="刀位21"/> -->
<!-- <field name="tool_num_process_time21" class="o_form_label"/> -->
<!-- <span>&amp;nbsp;</span> -->
<!-- <label for="tool_num_process_time22" string="刀位22"/> -->
<!-- <field name="tool_num_process_time22" class="o_form_label"/> -->
<!-- <span>&amp;nbsp;</span> -->
<!-- <label for="tool_num_process_time23" string="刀位23"/> -->
<!-- <field name="tool_num_process_time23" class="o_form_label"/> -->
<!-- <span>&amp;nbsp;</span> -->
<!-- <label for="tool_num_process_time24" string="刀位24"/> -->
<!-- <field name="tool_num_process_time24" class="o_form_label"/> -->
<!-- </div> -->
<!-- </group> -->
</page>
<page string="机床运行数据" attrs="{'invisible': [('equipment_type', '!=', '机床')]}">
<!-- <group string="机床配置">--> -->
<group string="ftp相关">
<group>
<field name="ftp_num"/>
<field name="ftp_pwd"/>
<field name="ftp_host"/>
</group>
<group>
<field name="ftp_port"/>
<field name="ftp_remote_path"/>
</group>
<group string="运行数据">
<group>
<field name="machine_power_on_time"/>
<field name="product_counts"/>
<field name="mode"/>
<field name="start_time"/>
<field name="end_time"/>
<field name="program_start_time"/>
<field name="program_end_time"/>
<field name="standby_start_time"/>
<field name="standby_end_time"/>
<field name="offline_start_time"/>
<field name="offline_end_time"/>
<field name="emg_status"/>
<field name="program_name"/>
<field name="program_status"/>
<field name="current_program"/>
<field name="current_program_seq"/>
<field name="x_abs_pos"/>
<field name="y_abs_pos"/>
<field name="z_abs_pos"/>
</group>
<group string="补偿值相关">
<group>
<field name="x_compensation_node"/>
</group>
<group>
<field name="y_compensation_node"/>
</group>
<group>
<field name="feed_speed_set"/>
<field name="act_feed_speed"/>
<field name="spindle_speed_set"/>
<field name="act_spindle_speed"/>
<field name="spindle_load"/>
<field name="x_axis_load"/>
<field name="y_axis_load"/>
<field name="z_axis_load"/>
<field name="rapid_feed"/>
<field name="feed_rate"/>
<field name="x_mach_coord"/>
<field name="y_mach_coord"/>
<field name="z_mach_coord"/>
<field name="x_rel_coord"/>
<field name="y_rel_coord"/>
<field name="z_rel_coord"/>
<field name="x_dis_coord"/>
<field name="y_dis_coord"/>
<field name="z_dis_coord"/>
</group>
<group string="数采相关">
<group>
<field name="machine_ip"/>
<field name="machine_signed"/>
<field name="machine_status"/>
<field name="machine_cnc_type"/>
<field name="machine_axis_count"/>
<field name="machine_run_status"/>
<field name="machine_emg_status"/>
<field name="machine_cut_status"/>
<field name="machine_mode"/>
<field name="machine_spindle_load"/>
<field name="machine_x_mach"/>
<field name="machine_x_abs_mach"/>
<field name="machine_x_rel_mach"/>
<field name="machine_x_dis_mach"/>
<field name="machine_x_axis_load"/>
<field name="machine_y_mach"/>
<field name="machine_y_abs_mach"/>
<field name="machine_y_rel_mach"/>
<field name="machine_y_dis_mach"/>
<field name="machine_y_axis_load"/>
<field name="machine_z_mach"/>
<field name="machine_z_abs_mach"/>
</group>
<group>
<field name="machine_z_rel_mach"/>
<field name="machine_z_dis_mach"/>
<field name="machine_z_axis_load"/>
<field name="machine_tool_num"/>
<field name="machine_program"/>
<field name="machine_current_prg"/>
<field name="machine_prg_seq"/>
<field name="machine_spindle_speed_set"/>
<field name="machine_act_spindle_speed"/>
<field name="machine_feed_speed_set"/>
<field name="machine_act_feed_speed"/>
<field name="machine_spindle_feed"/>
<field name="machine_feed_rate"/>
<field name="machine_rapid_feed"/>
<field name="machine_run_time"/>
<field name="machine_cut_time"/>
<field name="machine_keep_alive_time"/>
<field name="machine_circle_time"/>
<field name="machine_product_counts"/>
<field name="machine_system_date"/>
<field name="machine_system_time"/>
<field name="machine_alarm_msg"/>
</group>
</group>
<!-- </group> -->
</page>
<!-- <page string="刀位配置"> -->
<!-- <group string="刀位配置"> -->
</group>
<!-- <group string="ftp相关"> -->
<!-- <group> -->
<!-- <group> -->
<!-- <field name="tool_num1"/> -->
<!-- <field name="tool_num5"/> -->
<!-- <field name="tool_num9"/> -->
<!-- <field name="tool_num13"/> -->
<!-- <field name="tool_num17"/> -->
<!-- <field name="tool_num21"/> -->
<!-- </group> -->
<!-- <group> -->
<!-- <field name="tool_num2"/> -->
<!-- <field name="tool_num6"/> -->
<!-- <field name="tool_num10"/> -->
<!-- <field name="tool_num14"/> -->
<!-- <field name="tool_num18"/> -->
<!-- <field name="tool_num22"/> -->
<!-- </group> -->
<!-- <field name="ftp_num"/> -->
<!-- <field name="ftp_pwd"/> -->
<!-- <field name="ftp_host"/> -->
<!-- </group> -->
<!-- <group> -->
<!-- <group> -->
<!-- <field name="tool_num3"/> -->
<!-- <field name="tool_num7"/> -->
<!-- <field name="tool_num11"/> -->
<!-- <field name="tool_num15"/> -->
<!-- <field name="tool_num19"/> -->
<!-- <field name="tool_num23"/> -->
<!-- </group> -->
<!-- <group> -->
<!-- <field name="tool_num4"/> -->
<!-- <field name="tool_num8"/> -->
<!-- <field name="tool_num12"/> -->
<!-- <field name="tool_num16"/> -->
<!-- <field name="tool_num20"/> -->
<!-- <field name="tool_num24"/> -->
<!-- </group> -->
<!-- <field name="ftp_port"/> -->
<!-- <field name="ftp_remote_path"/> -->
<!-- </group> -->
<!-- </group> -->
<!-- </page> -->
<!-- </notebook> -->
<!-- <group string="补偿值相关"> -->
<!-- <group> -->
<!-- <field name="x_compensation_node"/> -->
<!-- </group> -->
<!-- <group> -->
<!-- <field name="y_compensation_node"/> -->
<!-- </group> -->
<!-- </group> -->
<!-- <group string="数采相关"> -->
<!-- <group> -->
<!-- <field name="machine_ip"/> -->
<!-- <field name="machine_signed"/> -->
<!-- <field name="machine_status"/> -->
<!-- <field name="machine_cnc_type"/> -->
<!-- <field name="machine_axis_count"/> -->
<!-- <field name="machine_run_status"/> -->
<!-- <field name="machine_emg_status"/> -->
<!-- <field name="machine_cut_status"/> -->
<!-- <field name="machine_mode"/> -->
<!-- <field name="machine_spindle_load"/> -->
<!-- <field name="machine_x_mach"/> -->
<!-- <field name="machine_x_abs_mach"/> -->
<!-- <field name="machine_x_rel_mach"/> -->
<!-- <field name="machine_x_dis_mach"/> -->
<!-- <field name="machine_x_axis_load"/> -->
<!-- <field name="machine_y_mach"/> -->
<!-- <field name="machine_y_abs_mach"/> -->
<!-- <field name="machine_y_rel_mach"/> -->
<!-- <field name="machine_y_dis_mach"/> -->
<!-- <field name="machine_y_axis_load"/> -->
<!-- <field name="machine_z_mach"/> -->
<!-- <field name="machine_z_abs_mach"/> -->
<!-- </group> -->
<!-- <group> -->
<!-- <field name="machine_z_rel_mach"/> -->
<!-- <field name="machine_z_dis_mach"/> -->
<!-- <field name="machine_z_axis_load"/> -->
<!-- <field name="machine_tool_num"/> -->
<!-- <field name="machine_program"/> -->
<!-- <field name="machine_current_prg"/> -->
<!-- <field name="machine_prg_seq"/> -->
<!-- <field name="machine_spindle_speed_set"/> -->
<!-- <field name="machine_act_spindle_speed"/> -->
<!-- <field name="machine_feed_speed_set"/> -->
<!-- <field name="machine_act_feed_speed"/> -->
<!-- <field name="machine_spindle_feed"/> -->
<!-- <field name="machine_feed_rate"/> -->
<!-- <field name="machine_rapid_feed"/> -->
<!-- <field name="machine_run_time"/> -->
<!-- <field name="machine_cut_time"/> -->
<!-- <field name="machine_keep_alive_time"/> -->
<!-- <field name="machine_circle_time"/> -->
<!-- <field name="machine_product_counts"/> -->
<!-- <field name="machine_system_date"/> -->
<!-- <field name="machine_system_time"/> -->
<!-- <field name="machine_alarm_msg"/> -->
<!-- </group> -->
<!-- </group> -->
</page>
</xpath>
</field>
</record>
<!-- 刀具寿命 -->
<!-- <record id="view_cutting_tool_inherited" model="ir.ui.view"> -->
<!-- <field name="name">sf_cutting_tool_extension</field> -->
<!-- <field name="model">sf.cutting_tool.type</field> -->
<!-- <field name="inherit_id" ref="sf_base.form_sf_machine_tool_type"/> -->
<!-- <field name="arch" type="xml"> -->
<!-- <xpath expr="//form//group" position="after"> -->
<!-- <group string='刀具寿命'> -->
<!-- <group> -->
<!-- <field name="total_cut_time"/> -->
<!-- <field name="tool_position"/> -->
<!-- </group> -->
<!-- <group> -->
<!-- <field name="predict_life_time"/> -->
<!-- </group> -->
<!-- <div> -->
<!-- <div> -->
<!-- <field name='is_connect_tool_position' invisible='1'/> -->
<!-- <button string="绑定刀位" name="tool_connect_machine" type="object" confirm="是否确认绑定此刀位" -->
<!-- class="btn-primary"/> -->
<!-- attrs='{"invisible": [("is_connect_tool_position","!=", -->
<!-- "False")]}' -->
<!-- <span>&#32;</span> -->
<!-- <button string="解绑刀位" name="tool_unconnect_machine" type="object" confirm="是否解绑此刀位" -->
<!-- class="btn-primary"/> -->
<!-- attrs='{"invisible": [("is_connect_tool_position","!=", -->
<!-- "False")]}' -->
<!-- </div> -->
<!-- </div> -->
<!-- </group> -->
<!-- </xpath> -->
<!-- </field> -->
<!-- </record> -->
</odoo>

View File

@@ -14,7 +14,7 @@
<!-- <field name="program_ids" domain="[('workorder_id', '=', workorder_id)]"/>-->
</group>
<footer>
<button string="合并下发" name="confirm_up" type="object" class="btn-primary" confirm="是否确认合并下发"/>
<!-- <button string="合并下发" name="confirm_up" type="object" class="btn-primary" confirm="是否确认合并下发"/>-->
<button string="取消" class="btn-secondary" special="cancel"/>
</footer>
</form>

View File

@@ -10,21 +10,21 @@ class SfMaintenanceLogs(models.Model):
name = fields.Char(string='名称')
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='设备')
maintenance_equipment_id = fields.Many2one('maintenance.equipment', string='机台号')
maintenance_equipment_oee_id = fields.Many2one('maintenance.equipment.oee', string='设备oee')
code_location = fields.Char(string='编码位置')
fault_type = fields.Selection(
[('电气类', '电气类'), ('机械类', '机械类'), ('程序类', '程序类'), ('系统类', '系统类')], string='故障类型')
fault_code = fields.Char(string='故障代码')
fault_alarm_info = fields.Char(string='故障报警信息')
fault_alarm_info = fields.Text(string='故障报警信息')
alarm_level = fields.Selection([('一级', '一级(严重)'), ('二级', '二级(中等)'), ('三级', '三级(轻微)')],
string='报警级别')
alarm_time = fields.Datetime(string='报警时间')
alarm_time = fields.Datetime(string='故障报警时间')
alarm_way = fields.Selection([('文本提示报警', '文本提示报警'), ('声光报警', '声光报警'), ('图文报警', '图文报警')],
string='报警方式')
fault_process = fields.Text(string='故障处理方法')
operator = fields.Many2one('res.users', string='处理人')
recovery_time = fields.Datetime(string='复原时间')
recovery_time = fields.Datetime(string='故障消除时间')
fault_duration = fields.Float(string='故障时长')
note = fields.Text(string='备注')
active = fields.Boolean('Active', default=True)

View File

@@ -7,20 +7,23 @@ class SfMaintenanceEquipmentOEE(models.Model):
_description = '设备OEE'
name = fields.Char('设备oee')
equipment_id = fields.Many2one('maintenance.equipment', '设备',
equipment_id = fields.Many2one('maintenance.equipment', '机台号',
domain="[('category_id.equipment_type', '=', '机床'),('state_zc', '=', '已注册')]")
type_id = fields.Many2one('sf.machine_tool.type', '型号', related='equipment_id.type_id')
machine_tool_picture = fields.Binary('设备图片', related='equipment_id.machine_tool_picture')
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('故障停机总时长(h)')
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('故障率')
# 设备故障日志
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='运行日志')
@@ -38,12 +41,46 @@ class SfMaintenanceEquipmentOEELog(models.Model):
_name = 'maintenance.equipment.oee.logs'
_description = '设备运行日志'
name = fields.Char('运行日志')
run_time = fields.Datetime('时间')
state = fields.Selection([("开机", "开机"), ("关机", "关机"), ("等待", "等待"), ("开始加工", "开始加工"),
("结束加工", "结束加工"), ("故障", "故障"),
("检修", "检修"), ("保养", "保养")], default="", string="事件/状态")
workorder_id = fields.Char('加工订单')
time = fields.Char('持续时长')
color = fields.Char('颜色', default=1)
equipment_id = fields.Many2one('maintenance.equipment', '机台号')
name = fields.Char('设备名称')
machine_tool_picture = fields.Binary('设备图片')
type_id = fields.Many2one('sf.machine_tool.type', '品牌型号')
state = fields.Selection([("加工", "加工"), ("关机", "关机"), ("待机", "待机"), ("故障", "故障"),
("检修", "检修"), ("保养", "保养")], default="", string="实时状态")
online_time = fields.Char('开机时长')
offline_time = fields.Char('关机时长')
offline_nums = fields.Integer('关机次数')
# 待机时长
idle_time = fields.Char('待机时长')
# 待机率
idle_rate = fields.Char('待机率')
work_time = fields.Char('加工时长')
work_rate = fields.Char('可用率')
fault_time = fields.Char('故障时长')
fault_rate = fields.Char('故障率')
fault_nums = fields.Integer('故障次数')
detail_ids = fields.One2many('maintenance.equipment.oee.log.detail', 'log_id', string='日志详情')
# maintenance_time = fields.Char('维保时长')
# work_nums = fields.Integer('加工件数')
equipment_oee_id = fields.Many2one('maintenance.equipment.oee', '设备OEE')
# 设备运行日志详情
class SfMaintenanceEquipmentOEELogDetail(models.Model):
_name = 'maintenance.equipment.oee.log.detail'
_description = '设备运行日志详情'
sequence = fields.Integer('序号')
time = fields.Datetime('时间')
state = fields.Selection([("加工", "加工"), ("关机", "关机"), ("待机", "待机"), ("故障", "故障"),
("检修", "检修"), ("保养", "保养")], default="", string="事件/状态")
production_id = fields.Many2one('mrp.production', '加工工单')
log_id = fields.Many2one('maintenance.equipment.oee.logs', '日志')

View File

@@ -67,3 +67,6 @@ 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,0
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,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
67
68
69
70
71
72

View File

@@ -14,6 +14,7 @@
<field name="utilization_rate"/>
<field name="fault_time"/>
<field name="fault_nums"/>
<field name="fault_rate"/>
</tree>
</field>
</record>
@@ -49,19 +50,19 @@
</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>
</page>
</notebook>
<!-- <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> -->
<!-- </page> -->
<!-- </notebook> -->
</sheet>
</form>
</field>

View File

@@ -7,22 +7,20 @@
<field name="model">sf.maintenance.logs</field>
<field name="arch" type="xml">
<tree>
<field name="code"/>
<field name="type" optional="hide"/>
<field name="brand"/>
<field name="maintenance_equipment_id"/>
<field name="code_location" optional="hide"/>
<field name="fault_type"/>
<field name="fault_code" optional="hide"/>
<field name="alarm_time"/>
<field name="fault_alarm_info"/>
<field name="alarm_level" optional="hide"/>
<field name="alarm_time"/>
<field name="alarm_way" optional="hide"/>
<field name="fault_process"/>
<field name="operator"/>
<field name="fault_process" optional="hide"/>
<field name="operator" optional="hide"/>
<field name="recovery_time"/>
<field name="fault_duration"/>
<field name="note"/>
<field name="note" optional="hide"/>
</tree>
</field>
</record>
@@ -45,25 +43,32 @@
<group>
<group>
<field name="name"/>
<field name="type" required="1" widget="radio" options="{'horizontal': true}"/>
<field name="brand"/>
<!-- <field name="name"/> -->
<!-- <field name="type" required="1" widget="radio" options="{'horizontal': true}"/> -->
<field name="maintenance_equipment_id"/>
<field name="code_location"/>
<field name="fault_type" required="1" widget="radio" options="{'horizontal': true}"/>
<field name="fault_code"/>
<field name="fault_process"/>
</group>
<group>
<field name="fault_alarm_info"/>
<field name="brand"/>
<field name="alarm_time"/>
<field name="alarm_way" required="1" widget="radio" options="{'horizontal': true}"/>
<field name="operator"/>
<field name="recovery_time"/>
<field name="fault_duration"/>
<field name="note"/>
<field name="fault_alarm_info"/>
<!-- <field name="code_location"/> -->
<!-- <field name="fault_type" required="1" widget="radio" options="{'horizontal': true}"/> -->
<!-- <field name="fault_code"/> -->
</group>
<group>
<field name="operator"/>
<field name="fault_process"/>
<!-- <field name="alarm_way" required="1" widget="radio" options="{'horizontal': true}"/> -->
<field name="recovery_time"/>
<field name="fault_duration"/>
</group>
</group>
<group>
<field name="note"/>
</group>
</sheet>
</form>
@@ -100,6 +105,233 @@
</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="type_id"/>
<field name="state"/>
</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="日志详情">
<field name="detail_ids">
<tree>
<field name="sequence"/>
<field name="time"/>
<field name="state"/>
<field name="production_id"/>
</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_id"/>
</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="设备运行日志详情">
<header>
<field name="sequence" readonly="1"/>
</header>
<sheet>
<div class="oe_title">
<h1>
<field name="time" readonly="1"/>
</h1>
</div>
<group>
<group>
<field name="state" readonly="1"/>
<field name="production_id" readonly="1"/>
</group>
<group>
<field name="log_id"/>
</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 -->

View File

@@ -14,9 +14,12 @@
'data': [
'data/stock_data.xml',
'data/empty_racks_data.xml',
'data/panel_data.xml',
'security/group_security.xml',
'security/ir.model.access.csv',
'wizard/workpiece_delivery_views.xml',
'wizard/rework_wizard_views.xml',
'wizard/production_wizard_views.xml',
'views/mrp_views_menus.xml',
'views/stock_lot_views.xml',
'views/mrp_production_addional_change.xml',

View File

@@ -8,7 +8,7 @@ from odoo.http import request
class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/GetWoInfo', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/GetWoInfo', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def get_Work_Info(self, **kw):
"""
@@ -25,7 +25,8 @@ class Manufacturing_Connect(http.Controller):
{'content': ret, 'name': 'AutoDeviceApi/GetWoInfo'})
logging.info('RfidCode:%s' % ret['RfidCode'])
if 'RfidCode' in ret:
workorder = request.env['mrp.workorder'].sudo().search([('rfid_code', '=', ret['RfidCode'])])
workorder = request.env['mrp.workorder'].sudo().search(
[('rfid_code', '=', ret['RfidCode']), ('state', '!=', 'rework')])
if workorder:
for item in workorder:
res['Datas'].append({
@@ -102,7 +103,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('get_ShiftPlan error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/QcCheck', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/QcCheck', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def get_qcCheck(self, **kw):
"""
@@ -122,7 +123,8 @@ class Manufacturing_Connect(http.Controller):
logging.info('RfidCode:%s' % ret['RfidCode'])
if 'RfidCode' in ret:
workorder = request.env['mrp.workorder'].sudo().search(
[('routing_type', '=', '装夹预调'), ('rfid_code', '=', ret['RfidCode'])], limit=1, order='id asc')
[('routing_type', '=', '装夹预调'), ('rfid_code', '=', ret['RfidCode']), ('state', '!=', 'rework')],
limit=1, order='id asc')
if workorder:
for item in workorder:
if item.material_center_point:
@@ -143,7 +145,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('get_qcCheck error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/FeedBackStart', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/FeedBackStart', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def button_Work_START(self, **kw):
"""
@@ -162,7 +164,8 @@ class Manufacturing_Connect(http.Controller):
routing_type = ret['CraftId']
equipment_id = ret["DeviceId"]
workorder = request.env['mrp.workorder'].sudo().search(
[('production_id', '=', production_id), ('routing_type', '=', routing_type)], limit=1)
[('production_id', '=', production_id), ('routing_type', '=', routing_type),
('rfid_code', '!=', False), ('state', '!=', 'rework')], limit=1)
if not workorder:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '该工单不存在'}
return json.JSONEncoder().encode(res)
@@ -190,7 +193,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('button_Work_START error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/FeedBackEnd', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/FeedBackEnd', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def button_Work_End(self, **kw):
"""
@@ -203,12 +206,14 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True, 'Datas': ['工单已结束']}
datas = request.httprequest.data
ret = json.loads(datas)
logging.info('button_Work_End:%s' % ret)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/FeedBackEnd'})
production_id = ret['BillId']
routing_type = ret['CraftId']
workorder = request.env['mrp.workorder'].sudo().search(
[('production_id', '=', production_id), ('routing_type', '=', routing_type)], limit=1)
[('production_id', '=', production_id), ('routing_type', '=', routing_type),
('rfid_code', '!=', False), ('state', '!=', 'rework')], limit=1)
if not workorder:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '该工单不存在'}
return json.JSONEncoder().encode(res)
@@ -217,28 +222,29 @@ class Manufacturing_Connect(http.Controller):
return json.JSONEncoder().encode(res)
# workorder.write({'date_finished': datetime.now()})
if ret['IsComplete'] is True:
workorder.button_finish()
# workorder.process_state = '待解除装夹'
# workorder.sudo().production_id.process_state = '待解除装夹'
workorder.write({'date_finished': datetime.now()})
# 根据工单的实际结束时间修改排程单的结束时间、状态,同时修改销售订单的状态
if workorder.date_finished:
request.env['sf.production.plan'].sudo().search([('production_id', '=', production_id)]).write(
{'actual_end_time': workorder.date_finished,
'state': 'finished'})
production_obj = request.env['mrp.production'].sudo().search([('name', '=', production_id)])
if production_obj:
production_obj.sudo().work_order_state = '已完成'
production_obj.write({'state': 'completed'})
# request.env['sale.order'].sudo().search(
# [('name', '=', production_obj.origin)]).write({'schedule_status': 'to deliver'})
# workorder.process_state = '待解除装夹'
# workorder.sudo().production_id.process_state = '待解除装夹'
# 根据工单的实际结束时间修改排程单的结束时间、状态,同时修改销售订单的状态
# if workorder.date_finished:
# request.env['sf.production.plan'].sudo().search([('production_id', '=', production_id)]).write(
# {'actual_end_time': workorder.date_finished,
# 'state': 'finished'})
# production_obj = request.env['mrp.production'].sudo().search([('name', '=', production_id)])
# if production_obj:
# production_obj.sudo().work_order_state = '已完成'
# production_obj.write({'state': 'done'})
# request.env['sale.order'].sudo().search(
# [('name', '=', production_obj.origin)]).write({'schedule_status': 'to deliver'})
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
logging.info('button_Work_End error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/PartQualityInspect', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/PartQualityInspect', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def PartQualityInspect(self, **kw):
"""
@@ -256,7 +262,8 @@ class Manufacturing_Connect(http.Controller):
production_id = ret['BillId']
routing_type = ret['CraftId']
workorder = request.env['mrp.workorder'].sudo().search(
[('production_id', '=', production_id), ('routing_type', '=', routing_type)], limit=1)
[('production_id', '=', production_id), ('routing_type', '=', routing_type), ('state', '!=', 'rework')],
limit=1)
if workorder:
# workorder.test_results = ret['Quality']
logging.info('制造订单:%s' % workorder.production_id.name)
@@ -296,7 +303,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('PartQualityInspect error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/CMMProgDolod', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/CMMProgDolod', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def CMMProgDolod(self, **kw):
"""
@@ -314,18 +321,18 @@ class Manufacturing_Connect(http.Controller):
if 'RfidCode' in ret:
logging.info('RfidCode:%s' % ret['RfidCode'])
workorder = request.env['mrp.workorder'].sudo().search(
[('rfid_code', '=', ret['RfidCode']), ('routing_type', '=', 'CNC加工')])
[('rfid_code', '=', ret['RfidCode']), ('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')])
if workorder:
for item in workorder.cmm_ids:
if item.program_date is not False:
program_date = item.program_date.strftime("%Y-%m-%d %H:%M:%S")
program_date_str = request.env['sf.sync.common'].sudo().get_add_time(program_date)
if item.program_create_date is not False:
program_create_date = item.program_create_date.strftime("%Y-%m-%d %H:%M:%S")
program_date_str = request.env['sf.sync.common'].sudo().get_add_time(program_create_date)
res['Datas'].append({
'CraftId': workorder.id,
'CraftKey': workorder.name,
'ProgramDate': '' if not item.program_date else program_date_str,
'ProgramDate': '' if not item.program_create_date else program_date_str,
'ProgramPath': item.program_path,
'PostProcessing': item.post_processing_name,
'PostProcessing': item.program_name,
})
else:
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '暂无工单及对应的CNC程序数据'}
@@ -336,7 +343,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('CMMProgDolod error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/NCProgDolod', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/NCProgDolod', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def NCProgDolod(self, **kw):
"""
@@ -354,7 +361,7 @@ class Manufacturing_Connect(http.Controller):
if 'RfidCode' in ret:
logging.info('RfidCode:%s' % ret['RfidCode'])
workorder = request.env['mrp.workorder'].sudo().search(
[('rfid_code', '=', ret['RfidCode']), ('routing_type', '=', 'CNC加工')])
[('rfid_code', '=', ret['RfidCode']), ('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')])
if workorder:
for item in workorder.cnc_ids:
res['Datas'].append({
@@ -377,7 +384,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('NCProgDolod error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/LocationChange', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/LocationChange', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def LocationChange(self, **kw):
"""
@@ -435,7 +442,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('LocationChange error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/AGVToProduct', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/AGVToProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def AGVToProduct(self, **kw):
"""
@@ -463,19 +470,25 @@ class Manufacturing_Connect(http.Controller):
if rfid_code is not None:
domain = [
('rfid_code', '=', rfid_code),
('routing_type', '=', 'CNC加工')
('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')
]
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
if workorder:
for order in workorder:
if order.production_id.production_line_state == '待上产线':
if order.production_line_state == '待上产线':
logging.info(
'制造订单产线状态:%s' % order.production_id.production_line_state)
order.production_id.write({'production_line_state': '已上产线'})
'单产线状态:%s' % order.production_line_state)
panel_workorder = request.env['mrp.workorder'].sudo().search(
[('rfid_code', '=', rfid_code), ('state', '!=', 'rework'),
('processing_panel', '=', order.processing_panel)])
if panel_workorder:
panel_workorder.write({'production_line_state': '已上产线'})
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
[
('rfid_code', '=', rfid_code), ('type', '=', '上产线'),
('production_id', '=', order.production_id.id)])
('production_id', '=', order.production_id.id),
('workorder_id', '=', order.id),
('workorder_state', '=', 'done')])
if workpiece_delivery.status == '待下发':
workpiece_delivery.write({'is_manual_work': True})
else:
@@ -490,7 +503,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('AGVToProduct error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/AGVDownProduct', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/AGVDownProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def AGVDownProduct(self, **kw):
"""
@@ -520,20 +533,28 @@ class Manufacturing_Connect(http.Controller):
if rfid_code is not None:
domain = [
('rfid_code', '=', rfid_code),
('routing_type', '=', 'CNC加工')
('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')
]
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
if workorder:
for order in workorder:
if order.production_id.production_line_state == '已上产线':
if order.production_line_state == '已上产线':
logging.info(
'制造订单产线状态:%s' % order.production_id.production_line_state)
order.production_id.write({'production_line_state': '已下产线'})
'单产线状态:%s' % order.production_line_state)
panel_workorder = request.env['mrp.workorder'].sudo().search(
[('rfid_code', '=', rfid_code), ('state', '!=', 'rework'),
('processing_panel', '=', order.processing_panel)])
if panel_workorder:
panel_workorder.write({'production_line_state': '已下产线'})
workorder.write({'state': 'to be detected'})
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
[
('rfid_code', '=', rfid_code), ('type', '=', '下产线'),
('production_id', '=', order.production_id.id)])
delivery_Arr.append(workpiece_delivery.id)
('production_id', '=', order.production_id.id),
('workorder_id', '=', order.id),
('workorder_state', '=', 'done')])
if workpiece_delivery:
delivery_Arr.append(workpiece_delivery.id)
else:
res = {'Succeed': False, 'ErrorCode': 204,
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="panel_zm" model="sf.processing.panel">
<field name="name">ZM</field>
</record>
<record id="panel_fm" model="sf.processing.panel">
<field name="name">FM</field>
</record>
<record id="panel_yc" model="sf.processing.panel">
<field name="name">YC</field>
</record>
<record id="panel_zc" model="sf.processing.panel">
<field name="name">ZC</field>
</record>
<record id="panel_qc" model="sf.processing.panel">
<field name="name">QC</field>
</record>
<record id="panel_hc" model="sf.processing.panel">
<field name="name">HC</field>
</record>
</data>
</odoo>

View File

@@ -1,6 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record model="ir.cron" id="ir_cron_mrp_production">
<field name="name">返工且编程中的制造订单定时获取Cloud编程单状态</field>
<field name="model_id" ref="model_mrp_production"/>
<field name="state">code</field>
<field name="code">model._cron_get_programming_state()</field>
<field name="interval_number">3</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
</record>
<record id="sequence_routing_workcenter" model="ir.sequence">
<field name="name">工序编码规则</field>
<field name="code">mrp.routing.workcenter</field>

View File

@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
import base64
import logging
import json
import os
import re
import requests
from itertools import groupby
@@ -24,22 +26,59 @@ class MrpProduction(models.Model):
work_order_state = fields.Selection([('未排', '未排'), ('已排', '已排'), ('已完成', '已完成')],
string='工单状态', default='未排')
detection_result_ids = fields.One2many('sf.detection.result', 'production_id', '检测报告')
tool_state = fields.Selection([('0', '正常'), ('1', '缺刀'), ('2', '无效刀')], string='功能刀具状态', default='0',
store=True, compute='_compute_tool_state')
tool_state_remark = fields.Text(string='功能刀具状态备注(缺刀)', readonly=True)
tool_state_remark2 = fields.Text(string='功能刀具状态备注(无效刀)', readonly=True)
@api.depends('workorder_ids.tool_state')
def _compute_tool_state(self):
# if self.workorder_ids:
for item in self:
if item.workorder_ids:
workorder_ids = item.workorder_ids.filtered(lambda a: a.state not in ('rework', '返工'))
if workorder_ids.filtered(lambda a: a.tool_state == '2'):
item.tool_state = '2'
elif workorder_ids.filtered(lambda a: a.tool_state == '1'):
tool_state_remark = ''
data = {}
# 获取所有缺刀工单加工面对应缺的刀
for work in workorder_ids.filtered(lambda a: a.tool_state == '1'):
if work.processing_panel not in list(data.keys()):
data.update({work.processing_panel: []})
for cnc in work.cnc_ids.filtered(lambda a: a.tool_state == '1'):
if cnc.cutting_tool_name not in data[work.processing_panel]:
data[work.processing_panel].append(cnc.cutting_tool_name)
# 按格式生成缺刀提示信息
for key in data:
if data.get(key) and not data.get(key):
if tool_state_remark != '':
tool_state_remark = f'{tool_state_remark}\n{key}缺刀:{data.get(key)}'
else:
tool_state_remark = f'{key}缺刀:{data.get(key)}'
item.tool_state = '1'
item.tool_state_remark = tool_state_remark
item.tool_state_remark2 = ''
else:
item.tool_state = '0'
item.tool_state_remark = ''
item.tool_state_remark2 = ''
# state = fields.Selection(selection_add=[
# ('pending_scheduling', '待排程'),
# ('pending_processing', '待加工'),
# ('completed', '已完工')
# ])
state = fields.Selection([
('draft', 'Draft'),
('confirmed', 'Confirmed'),
('progress', '排程'),
('pending_cam', '待装夹'),
('pending_processing', '待加'),
('pending_era_cam', '待解除装夹'),
('completed', '已完工'),
('draft', '草稿'),
('confirmed', '待排程'),
('pending_cam', '加工'),
('progress', '加工中'),
('rework', ''),
('to_close', 'To Close'),
('done', 'Done'),
('cancel', 'Cancelled')], string='State',
('cancel', '报废')], string='State',
compute='_compute_state', copy=False, index=True, readonly=True,
store=True, tracking=True,
help=" * Draft: The MO is not confirmed yet.\n"
@@ -53,13 +92,17 @@ class MrpProduction(models.Model):
active = fields.Boolean(string='已归档', default=True)
programming_no = fields.Char('编程单号')
work_state = fields.Char('业务状态')
programming_state = fields.Char('编程状态', tracking=True)
programming_state = fields.Selection(
[('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'), ('已下发', '已下发')],
string='编程状态',
tracking=True)
glb_file = fields.Binary("glb模型文件")
production_line_id = fields.Many2one('sf.production.line', string='生产线', tracking=True)
plan_start_processing_time = fields.Datetime('计划开始加工时间')
production_line_state = fields.Selection(
[('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')],
string='上/下产线', default='待上产线', tracking=True)
is_rework = fields.Boolean(string='是否返工', default=False)
# production_line_state = fields.Selection(
# [('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')],
# string='上/下产线', default='待上产线', tracking=True)
# 工序状态
# Todo 研究下用法
process_state = fields.Selection([
@@ -77,11 +120,11 @@ class MrpProduction(models.Model):
part_drawing = fields.Binary('零件图纸')
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
rework_production = fields.Many2one('mrp.production', string='返工的制造订单')
is_scrap = fields.Boolean('是否报废', default=False)
@api.depends(
'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state',
'workorder_ids.state', 'product_qty', 'qty_producing', 'schedule_state', 'process_state')
'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state', 'tool_state',
'workorder_ids.state', 'product_qty', 'qty_producing', 'schedule_state')
def _compute_state(self):
for production in self:
if not production.state or not production.product_uom_id:
@@ -109,22 +152,40 @@ class MrpProduction(models.Model):
production.state = 'progress'
elif any(not float_is_zero(move.quantity_done,
precision_rounding=move.product_uom.rounding or move.product_id.uom_id.rounding)
for move in production.move_raw_ids):
for move in production.move_raw_ids if move.product_id):
production.state = 'progress'
# 新添加的状态逻辑
if production.state == 'progress' and production.schedule_state == '已排' and production.process_state == '待装夹':
# production.state = 'pending_processing'
# # 新添加的状态逻辑
if (
production.state == 'to_close' or production.state == 'progress') and production.schedule_state == '未排':
production.state = 'confirmed'
elif production.state == 'pending_cam' and production.schedule_state == '未排':
production.state = 'confirmed'
elif production.state == 'to_close' and production.schedule_state == '已排':
production.state = 'pending_cam'
if production.state == 'progress' and production.schedule_state == '已排' and production.process_state == '待加工':
# if production.state == 'pending_cam' and production.process_state == '待加工':
production.state = 'pending_processing'
elif production.state == 'progress' and production.process_state == '待解除装夹':
production.state = 'pending_era_cam'
elif production.state == 'progress' and production.process_state == '已完工':
production.state = 'completed'
elif production.state == 'progress' and production.work_order_state == '已完成':
production.state = 'completed'
if production.state == 'progress':
if all(wo_state not in ('progress', 'done', 'rework') for wo_state in
production.workorder_ids.mapped('state')):
production.state = 'pending_cam'
if production.is_rework is True:
production.state = 'rework'
# if production.state == 'pending_cam':
# if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')):
# production.state = 'done'
if any(
(
wo.test_results == '返工' and wo.state == 'done' and production.programming_state in [
'已编程']) or (
wo.state == 'rework' and production.programming_state == '编程中') or (
wo.is_rework is True and wo.state == 'done' and production.programming_state in ['编程中',
'已编程'])
for wo in
production.workorder_ids):
production.state = 'rework'
# 如果制造订单的功能刀具为【无效刀】则制造订单状态改为返工
if production.tool_state == '2':
production.state = 'rework'
def action_check(self):
"""
@@ -155,27 +216,66 @@ class MrpProduction(models.Model):
for production in self:
production.maintenance_count = len(production.request_ids)
# 制造订单报废:编程单更新
def updateCNC(self):
# 获取cloud编程单的状态
def _cron_get_programming_state(self):
try:
res = {'production_no': self.name, 'programming_no': self.programming_no,
'order_no': self.origin}
if not self:
reproduction = self.env['mrp.production'].search(
[('state', '=', 'rework'), ('programming_state', '=', '编程中'), ('is_rework', '=', True)])
else:
reproduction = self
if reproduction:
programming_no_set = set([str(item.programming_no) for item in reproduction])
programming_no = list(programming_no_set)
programming_no_str = ','.join(programming_no)
res = {'programming_no': programming_no_str}
logging.info('res=%s:' % res)
configsettings = self.env['res.config.settings'].get_values()
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
url = '/api/intelligent_programming/get_state'
config_url = configsettings['sf_url'] + url
ret = requests.post(config_url, json=res, data=None, headers=config_header)
ret = ret.json()
result = json.loads(ret['result'])
logging.info('cron_get_programming_state-ret:%s' % result)
if result['status'] == 1:
for item in result['programming_list']:
if not self:
for rp in reproduction:
if rp.programming_no == item['programming_no']:
rp.write({'programming_state': '已编程未下发' if item[
'programming_state'] == '已编程' else '编程中'})
logging.info('rp:%s' % rp.name)
else:
return item
else:
raise UserError(ret['message'])
except Exception as e:
logging.info('cron_get_programming_state error:%s' % e)
# 编程单更新
def update_programming_state(self):
try:
res = {'programming_no': self.programming_no,
'manufacturing_type': 'rework' if self.is_scrap is False else 'scrap'}
logging.info('res=%s:' % res)
configsettings = self.env['res.config.settings'].get_values()
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
url = '/api/intelligent_programming/update_intelligent_programmings'
url = '/api/intelligent_programming/reset_state_again'
config_url = configsettings['sf_url'] + url
res['token'] = configsettings['token']
ret = requests.post(config_url, json={}, data=res, headers=config_header)
ret = requests.post(config_url, json=res, data=None, headers=config_header)
ret = ret.json()
logging.info('updateCNC-ret:%s' % ret)
if ret['status'] == 1:
self.write({'work_state': '已编程'})
result = json.loads(ret['result'])
logging.info('update_programming_state-ret:%s' % result)
if result['status'] == 1:
self.write({'is_rework': True})
else:
raise UserError(ret['message'])
except Exception as e:
logging.info('updateCNC error:%s' % e)
raise UserError("更新程单失败,请联系管理员")
logging.info('update_programming_state error:%s' % e)
raise UserError("更新程单状态失败,请联系管理员")
# cnc程序获取
def fetchCNC(self, production_names):
@@ -201,7 +301,7 @@ class MrpProduction(models.Model):
'material_type_code': self.env['sf.materials.model'].search(
[('id', '=', cnc.product_id.materials_type_id.id)]).materials_no,
'machining_processing_panel': cnc.product_id.model_processing_panel,
'machining_precision': cnc.product_id.model_machining_precision,
'machining_precision': '',
'embryo_long': cnc.product_id.bom_ids.bom_line_ids.product_id.length,
'embryo_height': cnc.product_id.bom_ids.bom_line_ids.product_id.height,
'embryo_width': cnc.product_id.bom_ids.bom_line_ids.product_id.width,
@@ -293,14 +393,6 @@ class MrpProduction(models.Model):
# 其他规则限制: 默认只分配给工作中心状态为非故障的工作中心;
def _create_workorder3(self, item):
# 根据product_id对self进行分组
grouped_product_ids = {k: list(g) for k, g in groupby(self, key=lambda x: x.product_id.id)}
# 初始化一个字典来存储每个product_id对应的生产订单名称列表
product_id_to_production_names = {}
# 对于每个product_id获取其所有生产订单的名称
for product_id, productions in grouped_product_ids.items():
# 为同一个product_id创建一个生产订单名称列表
product_id_to_production_names[product_id] = [production.name for production in productions]
for production in self:
if not production.bom_id or not production.product_id:
continue
@@ -347,6 +439,7 @@ class MrpProduction(models.Model):
# 表面工艺工序
# 获取表面工艺id
if production.product_id.model_process_parameters_ids:
logging.info('model_process_parameters_ids:%s' % production.product_id.model_process_parameters_ids)
surface_technics_arr = []
# 工序id
route_workcenter_arr = []
@@ -361,6 +454,7 @@ class MrpProduction(models.Model):
# 用filter刷选表面工艺id'是否存在工艺类别对象里
if production_process_category:
for p in production_process_category:
logging.info('production_process_category:%s' % p.name)
production_process = p.production_process_ids.filtered(
lambda pp: pp.id in surface_technics_arr)
if production_process:
@@ -414,7 +508,7 @@ class MrpProduction(models.Model):
else:
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
production_item)
production)
if sorted_workorders[i] in consecutive_workorders:
is_pick = True
consecutive_workorders = []
@@ -422,46 +516,24 @@ class MrpProduction(models.Model):
# 当前面的连续工序生成对应的外协出入库单再生成当前工序的外协出入库单
if is_pick is False:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i],
production_item)
production)
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
production_item)
production)
if sorted_workorders[i] in consecutive_workorders:
is_pick = True
consecutive_workorders = []
m = 0
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production_item)
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production)
if is_pick is False and m == 0:
if len(sorted_workorders) == 1:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production_item)
self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production)
else:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production_item)
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production)
for workorder in production.workorder_ids:
workorder.duration_expected = workorder._get_duration_expected()
# 在之前的销售单上重新生成制造订单
def create_production1_values(self, production):
production_values_str = {'origin': production.origin,
'product_id': production.product_id.id,
'product_description_variants': production.product_description_variants,
'product_qty': production.product_qty,
'product_uom_id': production.product_uom_id.id,
'location_src_id': production.location_src_id.id,
'location_dest_id': production.location_dest_id.id,
'bom_id': production.bom_id.id,
'date_deadline': production.date_deadline,
'date_planned_start': production.date_planned_start,
'date_planned_finished': production.date_planned_finished,
'procurement_group_id': False,
'propagate_cancel': production.propagate_cancel,
'orderpoint_id': production.orderpoint_id.id,
'picking_type_id': production.picking_type_id.id,
'company_id': production.company_id.id,
'move_dest_ids': production.move_dest_ids.ids,
'user_id': production.user_id.id}
return production_values_str
# 工单排序
def _reset_work_order_sequence1(self, k):
for rec in self:
@@ -530,70 +602,90 @@ class MrpProduction(models.Model):
def _reset_work_order_sequence(self):
for rec in self:
sequence_list = {}
workorder_ids = rec.workorder_ids.filtered(lambda item: item.state in ('返工', 'rework'))
# 产品模型类型
model_type_id = rec.product_id.product_model_type_id
# 产品加工面板
model_processing_panel = rec.product_id.model_processing_panel
if model_type_id:
if model_processing_panel:
tmpl_num = 1
panel_list = model_processing_panel.split(',')
for panel in panel_list:
panel_sequence_list = {}
# 成品工序
product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids
if product_routing_tmpl_ids:
for tmpl_id in product_routing_tmpl_ids:
panel_sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
tmpl_num += 1
sequence_list.update({panel: panel_sequence_list})
# 表面工艺工序
# 模型类型的表面工艺工序模版
surface_tmpl_ids = model_type_id.surface_technics_routing_tmpl_ids
# 产品选择的表面工艺
model_process_parameters_ids = rec.product_id.model_process_parameters_ids
process_dict = {}
if model_process_parameters_ids:
for process_parameters_id in model_process_parameters_ids:
process_id = process_parameters_id.process_id
for surface_tmpl_id in surface_tmpl_ids:
if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id:
surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name
process_dict.update({int(process_id.category_id.code): '%s-%s' % (
surface_tmpl_name, process_parameters_id.name)})
process_list = sorted(process_dict.keys())
for process_num in process_list:
sequence_list.update({process_dict.get(process_num): tmpl_num})
tmpl_num += 1
# 坯料工序
tmpl_num = 1
embryo_routing_tmpl_ids = model_type_id.embryo_routing_tmpl_ids
if embryo_routing_tmpl_ids:
for tmpl_id in embryo_routing_tmpl_ids:
sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
if not workorder_ids:
sequence_list = {}
if model_type_id:
if model_processing_panel:
tmpl_num = 1
panel_list = model_processing_panel.split(',')
for panel in panel_list:
panel_sequence_list = {}
# 成品工序
product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids
if product_routing_tmpl_ids:
for tmpl_id in product_routing_tmpl_ids:
panel_sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
tmpl_num += 1
sequence_list.update({panel: panel_sequence_list})
# 表面工艺工序
# 模型类型的表面工艺工序模版
surface_tmpl_ids = model_type_id.surface_technics_routing_tmpl_ids
# 产品选择的表面工艺
model_process_parameters_ids = rec.product_id.model_process_parameters_ids
process_dict = {}
if model_process_parameters_ids:
for process_parameters_id in model_process_parameters_ids:
process_id = process_parameters_id.process_id
for surface_tmpl_id in surface_tmpl_ids:
if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id:
surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name
process_dict.update({int(process_id.category_id.code): '%s-%s' % (
surface_tmpl_name, process_parameters_id.name)})
process_list = sorted(process_dict.keys())
for process_num in process_list:
sequence_list.update({process_dict.get(process_num): tmpl_num})
tmpl_num += 1
# 坯料工序
tmpl_num = 1
embryo_routing_tmpl_ids = model_type_id.embryo_routing_tmpl_ids
if embryo_routing_tmpl_ids:
for tmpl_id in embryo_routing_tmpl_ids:
sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
tmpl_num += 1
else:
raise ValidationError('该产品【加工面板】为空!')
else:
raise ValidationError('该产品【加工面板】为空!')
raise ValidationError('该产品没有选择【模版类型】!')
else:
raise ValidationError('该产品没有选择【模版类型】!')
for work in rec.workorder_ids:
if sequence_list.get(work.name):
work.sequence = sequence_list[work.name]
elif sequence_list.get(work.processing_panel):
processing_panel = sequence_list.get(work.processing_panel)
if processing_panel.get(work.name):
work.sequence = processing_panel[work.name]
for work in rec.workorder_ids:
if sequence_list.get(work.name):
work.sequence = sequence_list[work.name]
elif sequence_list.get(work.processing_panel):
processing_panel = sequence_list.get(work.processing_panel)
if processing_panel.get(work.name):
work.sequence = processing_panel[work.name]
else:
raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
else:
raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
else:
raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
# if work.name == '获取CNC加工程序':
# work.button_start()
# #work.fetchCNC()
# work.button_finish()
# 当单个面触发返工时,将新生成的工单插入到返工工单下方,并且后面的所以工单工序重排
elif rec.workorder_ids.filtered(lambda item: item.sequence == 0):
# 获取新增的返工工单
work_ids = rec.workorder_ids.filtered(lambda item: item.sequence == 0)
# 获取当前返工面最后一个工单工序
sequence_max = sorted(
rec.workorder_ids.filtered(lambda item: item.processing_panel == work_ids[0].processing_panel),
key=lambda item: item.sequence, reverse=True)[0].sequence
# 对当前返工工单之后的工单工序进行重排
work_order_ids = rec.workorder_ids.filtered(lambda item: item.sequence > sequence_max)
for work_id in work_order_ids:
work_id.sequence = work_id.sequence + 3
# 生成新增的返工工单的工序
# 成品工序
panel_sequence_list = {}
product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids
if product_routing_tmpl_ids:
for tmpl_id in product_routing_tmpl_ids:
sequence_max += 1
panel_sequence_list.update({tmpl_id.route_workcenter_id.name: sequence_max})
for work_id in work_ids:
if panel_sequence_list.get(work_id.name):
work_id.sequence = panel_sequence_list[work_id.name]
# 创建工单并进行排序
def _create_workorder(self, item):
@@ -641,6 +733,9 @@ class MrpProduction(models.Model):
})
for production in self:
logging.info('qty_produced:%s' % production.qty_produced)
if production.qty_produced == 0.0:
production.qty_produced = 1.0
production.write({
'date_finished': fields.Datetime.now(),
'product_qty': production.qty_produced,
@@ -669,6 +764,7 @@ class MrpProduction(models.Model):
if any(mo.show_allocation for mo in self):
action = self.action_view_reception_report()
return action
logging.info('last-product_qty:%s' % production.product_qty)
return True
context = self.env.context.copy()
context = {k: v for k, v in context.items() if not k.startswith('default_')}
@@ -692,3 +788,313 @@ class MrpProduction(models.Model):
'view_mode': 'tree,form',
})
return action
# 返工
def button_rework(self):
cloud_programming = None
if self.programming_state in ['已编程']:
cloud_programming = self._cron_get_programming_state()
logging.info('cloud_programming_state:%s' % cloud_programming['programming_state'])
logging.info('programming_state:%s' % self.programming_state)
return {
'name': _('返工'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'sf.rework.wizard',
'target': 'new',
'context': {
'default_production_id': self.id,
'default_reprogramming_num': cloud_programming['reprogramming_num'],
'default_programming_state': cloud_programming['programming_state'],
'default_is_reprogramming': True if cloud_programming['programming_state'] in ['已下发'] else False
}
}
# 更新程序
def do_update_program(self):
program_production = self
if len(program_production) >= 1:
same_product_id = None
is_not_same_product = 0
for item in program_production:
if same_product_id is None:
same_product_id = item.product_id
if item.product_id != same_product_id:
is_not_same_product += 1
if item.state != "rework" and item.programming_state != "已编程未下发":
raise UserError("请选择状态为返工且已编程未下发的制造订单")
if is_not_same_product >= 1:
raise UserError("您选择的记录中含有其他产品的制造订单,请选择同一产品的制造订单")
grouped_program_ids = {k: list(g) for k, g in groupby(program_production, key=lambda x: x.programming_no)}
program_to_production_names = {}
for programming_no, program_production in grouped_program_ids.items():
program_to_production_names[programming_no] = [production.name for production in program_production]
for production in self:
if production.programming_no in program_to_production_names:
productions_not_delivered = self.env['mrp.production'].search(
[('programming_no', '=', production.programming_no), ('programming_state', '=', '已编程未下发')])
rework_workorder = production.workorder_ids.filtered(lambda m: m.state == 'rework')
if rework_workorder:
for rework_item in rework_workorder:
pending_workorder = production.workorder_ids.filtered(
lambda m1: m1.state in [
'pending'] and m1.processing_panel == rework_item.processing_panel and m1.routing_type == 'CNC加工')
if not pending_workorder.cnc_ids:
production.get_new_program(rework_item.processing_panel)
# production.write({'state': 'progress', 'programming_state': '已编程', 'is_rework': False})
productions_not_delivered.write(
{'state': 'progress', 'programming_state': '已编程', 'is_rework': False})
# 从cloud获取重新编程过的最新程序
def get_new_program(self, processing_panel):
try:
res = {'programming_no': self.programming_no, 'processing_panel': processing_panel}
configsettings = self.env['res.config.settings'].get_values()
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
url = '/api/intelligent_programming/get_new_program'
config_url = configsettings['sf_url'] + url
r = requests.post(config_url, json=res, data=None, headers=config_header)
r = r.json()
result = json.loads(r['result'])
if result['status'] == 1:
program_path_tmp_panel = os.path.join('/tmp', result['folder_name'], 'return', processing_panel)
if os.path.exists(program_path_tmp_panel):
files_r = os.listdir(program_path_tmp_panel)
if files_r:
for file_name in files_r:
file_path = os.path.join(program_path_tmp_panel, file_name)
os.remove(file_path)
download_state = self.env['sf.cnc.processing'].download_file_tmp(result['folder_name'],
processing_panel)
if download_state is False:
raise UserError('编程单号为%s的CNC程序文件从FTP拉取失败' % (self.programming_no))
productions = self.env['mrp.production'].search(
[('programming_no', '=', self.programming_no), ('state', 'not in', ('cancel', 'done'))])
if productions:
for production in productions:
panel_workorder = production.workorder_ids.filtered(lambda
pw: pw.processing_panel == processing_panel and pw.routing_type == 'CNC加工' and pw.state not in (
'rework', 'done'))
if panel_workorder:
if panel_workorder.cmm_ids:
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)
# 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)
files_panel = os.listdir(program_path_tmp_panel)
if files_panel:
for file in files_panel:
file_extension = os.path.splitext(file)[1]
if file_extension.lower() == '.pdf':
panel_file_path = os.path.join(program_path_tmp_panel, file)
logging.info('panel_file_path:%s' % panel_file_path)
panel_workorder.write(
{'cnc_ids': panel_workorder.cnc_ids.sudo()._json_cnc_processing(processing_panel,
result),
'cmm_ids': panel_workorder.cmm_ids.sudo()._json_cmm_program(processing_panel, result),
'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
logging.info('len(cnc_worksheet):%s' % len(panel_workorder.cnc_worksheet))
pre_workorder = production.workorder_ids.filtered(lambda
ap: ap.routing_type == '装夹预调' and ap.processing_panel == processing_panel and ap.state not in (
'rework', 'done'))
if pre_workorder:
pre_workorder.write(
{'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
logging.info('更新程序完成:%s' % production.name)
else:
raise UserError(result['message'])
except Exception as e:
logging.info('get_new_program error:%s' % e)
raise UserError("从云平台获取最新程序失败,请联系管理员")
def recreateManufacturing(self):
"""
重新生成制造订单
"""
if self.is_scrap is True:
sale_order = self.env['sale.order'].sudo().search([('name', '=', productions.origin)])
values = self.env['mrp.production'].create_production1_values(self.production_id)
productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(
self.production_id.company_id).create(
values)
# self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
productions._create_workorder()
productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \
(
p.move_dest_ids.procure_method != 'make_to_order' and
not p.move_raw_ids and not p.workorder_ids)).action_confirm()
for production_item in productions:
process_parameter_workorder = self.env['mrp.workorder'].search(
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', production_item.id),
('is_subcontract', '=', True)])
if process_parameter_workorder:
is_pick = False
consecutive_workorders = []
m = 0
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.id)
for i in range(len(sorted_workorders) - 1):
if m == 0:
is_pick = False
if sorted_workorders[i].supplier_id.id == sorted_workorders[i + 1].supplier_id.id and \
sorted_workorders[i].is_subcontract == sorted_workorders[i + 1].is_subcontract and \
sorted_workorders[i].id == sorted_workorders[i + 1].id - 1:
if sorted_workorders[i] not in consecutive_workorders:
consecutive_workorders.append(sorted_workorders[i])
consecutive_workorders.append(sorted_workorders[i + 1])
m += 1
continue
else:
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
production_item)
if sorted_workorders[i] in consecutive_workorders:
is_pick = True
consecutive_workorders = []
m = 0
# 当前面的连续工序生成对应的外协出入库单再生成当前工序的外协出入库单
if is_pick is False:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i],
production_item)
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
production_item)
if sorted_workorders[i] in consecutive_workorders:
is_pick = True
consecutive_workorders = []
m = 0
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production_item)
if is_pick is False and m == 0:
if len(sorted_workorders) == 1:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production_item)
else:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production_item)
for production in productions:
origin_production = production.move_dest_ids and production.move_dest_ids[
0].raw_material_production_id or False
orderpoint = production.orderpoint_id
if orderpoint and orderpoint.create_uid.id == SUPERUSER_ID and orderpoint.trigger == 'manual':
production.message_post(
body=_('This production order has been created from Replenishment Report.'),
message_type='comment',
subtype_xmlid='mail.mt_note')
elif orderpoint:
production.message_post_with_view(
'mail.message_origin_link',
values={'self': production, 'origin': orderpoint},
subtype_id=self.env.ref('mail.mt_note').id)
elif origin_production:
production.message_post_with_view(
'mail.message_origin_link',
values={'self': production, 'origin': origin_production},
subtype_id=self.env.ref('mail.mt_note').id)
'''
创建生产计划
'''
# 工单耗时
workorder_duration = 0
for workorder in productions.workorder_ids:
workorder_duration += workorder.duration_expected
if sale_order:
sale_order.mrp_production_ids |= productions
# sale_order.write({'schedule_status': 'to schedule'})
self.env['sf.production.plan'].sudo().with_company(self.production_id.company_id).create({
'name': productions.name,
'order_deadline': sale_order.deadline_of_delivery,
'production_id': productions.id,
'date_planned_start': productions.date_planned_start,
'origin': productions.origin,
'product_qty': productions.product_qty,
'product_id': productions.product_id.id,
'state': 'draft',
})
# 在之前的销售单上重新生成制造订单
def create_production1_values(self, production, sale_order):
production_values_str = {'origin': production.origin,
'product_id': production.product_id.id,
'product_description_variants': production.product_description_variants,
'product_qty': production.product_qty,
'product_uom_id': production.product_uom_id.id,
'location_src_id': production.location_src_id.id,
'location_dest_id': production.location_dest_id.id,
'bom_id': production.bom_id.id,
'date_deadline': production.date_deadline,
'date_planned_start': production.date_planned_start,
'date_planned_finished': production.date_planned_finished,
'procurement_group_id': sale_order.id,
'propagate_cancel': production.propagate_cancel,
'orderpoint_id': production.orderpoint_id.id,
'picking_type_id': production.picking_type_id.id,
'company_id': production.company_id.id,
'move_dest_ids': production.move_dest_ids.ids,
'user_id': production.user_id.id}
return production_values_str
class sf_detection_result(models.Model):
_name = 'sf.detection.result'
_description = "检测结果"
production_id = fields.Many2one('mrp.production')
processing_panel = fields.Char('加工面')
routing_type = fields.Selection([
('装夹预调', '装夹预调'),
('CNC加工', 'CNC加工')], string="工序类型")
rework_reason = fields.Selection(
[("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"),
("operate computer", "操机"),
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因", tracking=True)
detailed_reason = fields.Text('详细原因')
test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")],
string="检测结果", tracking=True)
test_report = fields.Binary('检测报告', readonly=True)
handle_result = fields.Selection([("待处理", "待处理"), ("已处理", "已处理")], default='', string="处理结果",
tracking=True)
# 查看检测报告
def button_look_test_report(self):
return {
'res_model': 'sf.detection.result',
'type': 'ir.actions.act_window',
'res_id': self.id,
'views': [(self.env.ref('sf_manufacturing.sf_test_report_form').id, 'form')],
# 'view_mode': 'form',
# 'context': {
# 'default_id': self.id
# },
'target': 'new'
}
class sf_processing_panel(models.Model):
_name = 'sf.processing.panel'
_description = "加工面"
name = fields.Char('加工面')
active = fields.Boolean('有效', default=True)
# @api.model
# def name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
# if self.env.user.has_group('sf_base.group_sf_order_user'):
# if self._context.get('product_id'):
# product = self.env['product.product'].search([('id', '=', self._context.get('product_id'))])
# if product:
# panel = self.env['sf.processing.panel'].search([('name', 'ilike', 'ZM')])
# if panel:
# ids = [t.id for t in panel]
# domain = [('id', 'in', ids)]
# else:
# domain = [('id', '=', False)]
# return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
# return super()._name_search(name, args, operator, limit, name_get_uid)

View File

@@ -5,24 +5,49 @@ from odoo.addons.resource.models.resource import Intervals
class ResWorkcenter(models.Model):
_inherit = "mrp.workcenter"
_name = "mrp.workcenter"
_inherit = ['mrp.workcenter', 'mail.thread']
# 生产线显示
production_line_show = fields.Char(string='生产线名称')
equipment_id = fields.Many2one(
'maintenance.equipment', string="设备",
)
equipment_id = fields.Many2one('maintenance.equipment', string="设备", tracking=True)
production_line_id = fields.Many2one('sf.production.line', string='生产线',
related='equipment_id.production_line_id', store=True)
is_process_outsourcing = fields.Boolean('工艺外协')
users_ids = fields.Many2many("res.users", 'users_workcenter')
users_ids = fields.Many2many("res.users", 'users_workcenter', tracking=True)
def write(self, vals):
if 'users_ids' in vals:
old_users = self.users_ids
res = super(ResWorkcenter, self).write(vals)
new_users = self.users_ids
added_users = new_users - old_users
removed_users = old_users - new_users
if added_users or removed_users:
message = "增加 → %s ; 移除 → %s (可操作用户)" % (
# ','.join(added_users.mapped('name')), ','.join(removed_users.mapped('name')))
added_users.mapped('name'), removed_users.mapped('name'))
self.message_post(body=message)
return res
return super(ResWorkcenter, self).write(vals)
name = fields.Char('Work Center', related='resource_id.name', store=True, readonly=False, tracking=True)
time_efficiency = fields.Float('Time Efficiency', related='resource_id.time_efficiency', default=100, store=True,
readonly=False, tracking=True)
default_capacity = fields.Float(
'Capacity', default=1.0,
help="Default number of pieces (in product UoM) that can be produced in parallel (at the same time) at this work center. For example: the capacity is 5 and you need to produce 10 units, then the operation time listed on the BOM will be multiplied by two. However, note that both time before and after production will only be counted once.",
tracking=True)
oee_target = fields.Float(
string='OEE Target', help="Overall Effective Efficiency Target in percentage", default=90, tracking=True)
time_start = fields.Float('Setup Time', tracking=True)
time_stop = fields.Float('Cleanup Time', tracking=True)
costs_hour = fields.Float(string='Cost per hour', help='Hourly processing cost.', default=0.0, tracking=True)
equipment_status = fields.Selection(
[("正常", "正常"), ("故障停机", "故障停机"), ("计划维保", "计划维保"),("空闲", "空闲"),("封存(报废)", "封存(报废)")],
[("正常", "正常"), ("故障停机", "故障停机"), ("计划维保", "计划维保"), ("空闲", "空闲"), ("封存(报废)", "封存(报废)")],
string="设备状态", related='equipment_id.state')
# @api.depends('equipment_id')

View File

@@ -13,13 +13,13 @@ from dateutil.relativedelta import relativedelta
# import subprocess
from odoo import api, fields, models, SUPERUSER_ID, _
from odoo.addons.sf_base.commons.common import Common
from odoo.exceptions import UserError
from odoo.exceptions import UserError, ValidationError
from odoo.addons.sf_mrs_connect.models.ftp_operate import FtpController
class ResMrpWorkOrder(models.Model):
_inherit = 'mrp.workorder'
_order = 'sequence asc,create_date desc'
_order = 'sequence asc'
product_tmpl_name = fields.Char('坯料产品名称', related='production_bom_id.bom_line_ids.product_id.name')
@@ -47,9 +47,28 @@ class ResMrpWorkOrder(models.Model):
('切割', '切割'), ('表面工艺', '表面工艺')
], string="工序类型")
results = fields.Char('结果')
state = fields.Selection([
('pending', '等待其他工单'),
('waiting', '等待组件'),
('ready', '就绪'),
('progress', '进行中'),
('to be detected', "待检测"),
('done', '已完工'),
('rework', '返工'),
('cancel', '取消')], string='Status',
compute='_compute_state', store=True,
default='pending', copy=False, readonly=True, recursive=True, index=True, tracking=True)
# state = fields.Selection(selection_add=[('to be detected', "待检测"), ('rework', '返工')], tracking=True)
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
def _compute_working_users(self):
super()._compute_working_users()
for item in self:
if item.state == 'to be detected':
if self.env.user.has_group('sf_base.group_sf_equipment_user'):
item.is_user_working = True
@api.onchange('users_ids')
def get_user_permissions(self):
uid = self.env.uid
@@ -136,11 +155,6 @@ class ResMrpWorkOrder(models.Model):
supplier_id = fields.Many2one('res.partner', string='外协供应商')
equipment_id = fields.Many2one('maintenance.equipment', string='加工设备', tracking=True)
is_ok = fields.Boolean(string='是否合格')
# 加工人
processing_user_id = fields.Many2one('res.users', string='加工人')
# 检测人
inspection_user_id = fields.Many2one('res.users', string='检测人')
# 保存名称
save_name = fields.Char(string='检测文件保存名称', compute='_compute_save_name')
# 获取数据状态
@@ -163,13 +177,29 @@ class ResMrpWorkOrder(models.Model):
# 加工图纸
processing_drawing = fields.Binary(string='加工图纸')
# 功能刀具状态
tool_state = fields.Selection([('0', '正常'), ('1', '缺刀'), ('2', '无效刀')], string='功能刀具状态', default='0',
store=True, compute='_compute_tool_state')
@api.depends('cnc_ids.tool_state')
def _compute_tool_state(self):
for item in self:
if item:
if item.cnc_ids.filtered(lambda a: a.tool_state == '2'):
item.tool_state = '2'
elif item.cnc_ids.filtered(lambda a: a.tool_state == '1'):
item.tool_state = '1'
else:
item.tool_state = '0'
@api.depends('production_id')
def _compute_save_name(self):
"""
保存名称
"""
for record in self:
record.save_name = record.production_id.name.replace('/', '_')
tem_name = record.production_id.name.replace('/', '_')
record.save_name = tem_name + '_' + record.processing_panel
schedule_state = fields.Selection(related='production_id.schedule_state', store=True)
# 工件装夹信息
@@ -198,16 +228,19 @@ class ResMrpWorkOrder(models.Model):
production_line_id = fields.Many2one('sf.production.line', related='production_id.production_line_id',
string='生产线', store=True, tracking=True)
production_line_state = fields.Selection(related='production_id.production_line_state',
string='/下产线', store=True, tracking=True)
detection_report = fields.Binary('检测报告', readonly=True)
production_line_state = fields.Selection(
[('待上产线', '待上产线'), ('已上产线', '上产线'), ('已下产线', '已下产线')],
string='上/下产线', default='待上产线', tracking=True)
detection_report = fields.Binary('检测报告', readonly=False)
is_remanufacture = fields.Boolean(string='重新生成制造订单', default=False)
is_fetchcnc = fields.Boolean(string='重新获取NC程序', default=False)
reason = fields.Selection(
[("programming", "编程"), ("clamping", "返工"), ("cutter", "刀具"), ("operate computer", "操机"),
("technology", "工艺"), ("customer redrawing", "客户改图"), ("other", "其他"), ], string="原因", tracking=True)
[("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"), ("operate computer", "操机"),
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因", tracking=True)
detailed_reason = fields.Text('详细原因')
is_send_program_again = fields.Boolean(string='是否重新下发NC程序', default=False)
is_rework = fields.Boolean(string='是否返工', default=False)
# is_send_program_again = fields.Boolean(string='是否重新下发NC程序', default=False)
@api.onchange('rfid_code')
def _onchange(self):
@@ -222,7 +255,7 @@ class ResMrpWorkOrder(models.Model):
sql = """
SELECT *
FROM mrp_workorder
WHERE
WHERE state!='rework'
to_char(date_planned_start::timestamp + '8 hour','YYYY-MM-DD HH:mm:SS')>= %s
AND to_char(date_planned_finished::timestamp + '8 hour','YYYY-MM-DD HH:mm:SS')<= %s
"""
@@ -234,16 +267,6 @@ class ResMrpWorkOrder(models.Model):
ids = [t[0] for t in self.env.cr.fetchall()]
return [('id', 'in', ids)]
@api.onchange('is_ok')
def _onchange_inspection_user_id(self):
"""
检测is_ok(是否合格)被修改的话就将当前用户赋值给inspection_user_id
"""
if not self.inspection_user_id:
self.inspection_user_id = self.env.user.id
else:
self.inspection_user_id = False
@api.onchange('functional_fixture_id')
def _onchange_functional_fixture_id(self):
if self.functional_fixture_id:
@@ -443,6 +466,7 @@ class ResMrpWorkOrder(models.Model):
work.compensation_value_y = eval(self.material_center_point)[1]
# work.process_state = '待加工'
# self.sudo().production_id.process_state = '待加工'
# self.sudo().production_id.process_state = '待加工'
self.date_finished = datetime.now()
workorder.button_finish()
@@ -473,6 +497,20 @@ class ResMrpWorkOrder(models.Model):
else:
raise UserError(_("该工单暂未完成,无法进行工件配送"))
def button_rework_pre(self):
return {
'name': _('返工'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'sf.rework.wizard',
'target': 'new',
'context': {
'default_workorder_id': self.id,
'default_production_id': self.production_id.id,
# 'default_programming_state': self.production_id.programming_state,
'default_routing_type': self.routing_type
}}
# 拼接工单对象属性值
def json_workorder_str(self, k, production, route, item):
# 计算预计时长duration_expected
@@ -680,119 +718,6 @@ class ResMrpWorkOrder(models.Model):
# 'target':'new'
# }
def recreateManufacturingOrWorkerOrder(self):
"""
重新生成制造订单或者重新生成工单
"""
if self.test_results in ['返工', '报废']:
values = self.env['mrp.production'].create_production1_values(self.production_id)
productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(
self.production_id.company_id).create(
values)
# self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
productions._create_workorder()
productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \
(
p.move_dest_ids.procure_method != 'make_to_order' and
not p.move_raw_ids and not p.workorder_ids)).action_confirm()
for production_item in productions:
process_parameter_workorder = self.env['mrp.workorder'].search(
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', production_item.id),
('is_subcontract', '=', True)])
if process_parameter_workorder:
is_pick = False
consecutive_workorders = []
m = 0
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.id)
for i in range(len(sorted_workorders) - 1):
if m == 0:
is_pick = False
if sorted_workorders[i].supplier_id.id == sorted_workorders[i + 1].supplier_id.id and \
sorted_workorders[i].is_subcontract == sorted_workorders[i + 1].is_subcontract and \
sorted_workorders[i].id == sorted_workorders[i + 1].id - 1:
if sorted_workorders[i] not in consecutive_workorders:
consecutive_workorders.append(sorted_workorders[i])
consecutive_workorders.append(sorted_workorders[i + 1])
m += 1
continue
else:
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
production_item)
if sorted_workorders[i] in consecutive_workorders:
is_pick = True
consecutive_workorders = []
m = 0
# 当前面的连续工序生成对应的外协出入库单再生成当前工序的外协出入库单
if is_pick is False:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i],
production_item)
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
production_item)
if sorted_workorders[i] in consecutive_workorders:
is_pick = True
consecutive_workorders = []
m = 0
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production_item)
if is_pick is False and m == 0:
if len(sorted_workorders) == 1:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production_item)
else:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production_item)
for production in productions:
origin_production = production.move_dest_ids and production.move_dest_ids[
0].raw_material_production_id or False
orderpoint = production.orderpoint_id
if orderpoint and orderpoint.create_uid.id == SUPERUSER_ID and orderpoint.trigger == 'manual':
production.message_post(
body=_('This production order has been created from Replenishment Report.'),
message_type='comment',
subtype_xmlid='mail.mt_note')
elif orderpoint:
production.message_post_with_view(
'mail.message_origin_link',
values={'self': production, 'origin': orderpoint},
subtype_id=self.env.ref('mail.mt_note').id)
elif origin_production:
production.message_post_with_view(
'mail.message_origin_link',
values={'self': production, 'origin': origin_production},
subtype_id=self.env.ref('mail.mt_note').id)
'''
创建生产计划
'''
# 工单耗时
workorder_duration = 0
for workorder in productions.workorder_ids:
workorder_duration += workorder.duration_expected
sale_order = self.env['sale.order'].sudo().search([('name', '=', productions.origin)])
if sale_order:
sale_order.mrp_production_ids |= productions
# sale_order.write({'schedule_status': 'to schedule'})
self.env['sf.production.plan'].sudo().with_company(self.production_id.company_id).create({
'name': productions.name,
'order_deadline': sale_order.deadline_of_delivery,
'production_id': productions.id,
'date_planned_start': productions.date_planned_start,
'origin': productions.origin,
'product_qty': productions.product_qty,
'product_id': productions.product_id.id,
'state': 'draft',
})
# if self.test_results == '返工':
# productions = self.production_id
# # self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
# # self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
# productions._create_workorder2(self.processing_panel)
# else:
# self.results = '合格'
def json_workorder_str1(self, k, production, route):
workorders_values_str = [0, '', {
'product_uom_id': production.product_uom_id.id,
@@ -816,28 +741,124 @@ class ResMrpWorkOrder(models.Model):
}]
return workorders_values_str
# @api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state')
# def _compute_state(self):
# super(ResMrpWorkOrder, self)._compute_state()
# for item in self:
# print(item.name)
# print(item.state)
# print(item.is_remanufacture)
# scrap_workorder = self.env['mrp.workorder'].search(
# [('production_id', '=', item.production_id.id), ('routing_type', '=', 'CNC加工'),
# ('state', '=', 'done'), ('test_results', 'in', ['返工', '报废'])])
# print(scrap_workorder)
# # if item.routing_type == 'CNC加工' and item.state in ['done'] and item.test_results in ['返工', '报废']:
# if item.routing_type == '解除装夹':
# if scrap_workorder and item.state not in ['cancel']:
# item.state = 'cancel'
# elif item.routing_type == '表面工艺':
# if scrap_workorder:
# stock_move = self.env['stock.move'].search(
# [('origin', '=', item.production_id.name)])
# stock_move.write({'state': 'cancel'})
# item.picking_ids.write({'state': 'cancel'})
# item.state = 'cancel'
@api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state')
def _compute_state(self):
super()._compute_state()
for workorder in self:
re_work = self.env['mrp.workorder'].search([('production_id', '=', workorder.production_id.id),
('processing_panel', '=', workorder.processing_panel),
('is_rework', '=', True), ('state', 'in', ['done', 'rework'])])
cnc_workorder = self.env['mrp.workorder'].search(
[('production_id', '=', workorder.production_id.id),
('processing_panel', '=', workorder.processing_panel),
('routing_type', '=', 'CNC加工'), ('state', 'in', ['done', 'rework']),
('test_results', '=', '返工')])
cnc_workorder_pending = self.env['mrp.workorder'].search(
[('production_id', '=', workorder.production_id.id),
('processing_panel', '=', workorder.processing_panel),
('routing_type', '=', 'CNC加工'), ('state', 'in', ['pending'])])
if workorder.state not in ['cancel', 'progress', 'rework']:
if workorder.production_id.state == 'rework':
logging.info('len(re_work):%s' % len(re_work))
logging.info('len(cnc_workorder):%s' % len(cnc_workorder))
logging.info('len(cnc_workorder_pending):%s' % len(cnc_workorder_pending))
logging.info('工序:%s' % workorder.routing_type)
logging.info('状态:%s' % workorder.state)
logging.info('is_rework:%s' % workorder.is_rework)
logging.info('production_id.is_rework:%s' % workorder.production_id.is_rework)
logging.info('面:%s' % workorder.processing_panel)
logging.info('编程状态:%s' % workorder.production_id.programming_state)
logging.info('制造状态:%s' % workorder.production_id.state)
if workorder.routing_type == '装夹预调' and workorder.state not in ['done', 'rework',
'cancel']:
# # 有返工工单
# if re_work:
# 新工单
if workorder.is_rework is False:
if workorder.production_id.programming_state == '已编程' and workorder.production_id.is_rework is False:
if re_work or cnc_workorder:
workorder.state = 'ready'
else:
if workorder.production_id.is_rework is True:
if re_work or cnc_workorder:
workorder.state = 'waiting'
if workorder.production_id.tool_state in ['1', '2'] and workorder.state != 'pending':
workorder.state = 'waiting'
elif workorder.routing_type == 'CNC加工' and workorder.state not in ['done', 'rework', 'cancel']:
pre_workorder = self.env['mrp.workorder'].search(
[('production_id', '=', workorder.production_id.id),
('processing_panel', '=', workorder.processing_panel),
('routing_type', '=', '装夹预调'), ('state', '=', 'done')])
if pre_workorder:
if re_work:
workorder.state = 'waiting'
elif workorder.routing_type == '解除装夹' and workorder.state not in ['done', 'rework', 'cancel']:
if cnc_workorder:
if not cnc_workorder_pending:
workorder.state = 'waiting'
# else:
# if workorder.production_id.is_rework is True:
# workorder.state = 'waiting'
elif workorder.production_id.state == 'progress':
logging.info('len(re_work):%s' % len(re_work))
logging.info('len(cnc_workorder):%s' % len(cnc_workorder))
logging.info('len(cnc_workorder_pending):%s' % len(cnc_workorder_pending))
logging.info('工序:%s' % workorder.routing_type)
logging.info('状态:%s' % workorder.state)
logging.info('is_rework:%s' % workorder.is_rework)
logging.info('production_id.is_rework:%s' % workorder.production_id.is_rework)
logging.info('面:%s' % workorder.processing_panel)
logging.info('编程状态:%s' % workorder.production_id.programming_state)
logging.info('制造状态:%s' % workorder.production_id.state)
if workorder.routing_type == '装夹预调' and workorder.production_id.programming_state == '已编程' and \
workorder.is_rework is False and workorder.state not in [
'done', 'rework',
'cancel']:
if (re_work or cnc_workorder) and workorder.production_id.is_rework is False:
workorder.state = 'ready'
if workorder.routing_type == '表面工艺' and workorder.state not in ['done', 'progress']:
unclamp_workorder = self.env['mrp.workorder'].search(
[('production_id', '=', workorder.production_id.id),
('sequence', '=', workorder.sequence - 1),
('state', 'in', ['done'])])
if unclamp_workorder:
workorder.state = 'ready'
logging.info('工序:%s' % workorder.sequence)
logging.info('工单最终状态:%s' % workorder.state)
# else:
# if workorder.state not in ['cancel', 'rework']:
# workorder.state = 'rework'
# elif workorder.routing_type == 'CNC加工' and workorder.state not in ['done', 'cancel', 'progress',
# 'rework']:
# per_work = self.env['mrp.workorder'].search(
# [('routing_type', '=', '装夹预调'), ('production_id', '=', workorder.production_id.id),
# ('processing_panel', '=', workorder.processing_panel), ('is_rework', '=', True)])
# if per_work:
# workorder.state = 'waiting'
# if workorder.routing_type == 'CNC加工' and workorder.state == 'progress':
# workorder.state = 'to be detected'
# for workorder in self:
# if workorder.is_rework is True and workorder.state == 'done':
# cnc_work = self.env['mrp.workorder'].search([('routing_type','=','CNC加工'),('production_id','=',workorder.production_id.id)])
# if cnc_work:
# cnc_work.state = 'waiting'
# if workorder.state == 'pending':
# if all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]):
# workorder.state = 'ready' if workorder.production_id.reservation_state == 'assigned' else 'waiting'
# continue
# if workorder.state not in ('waiting', 'ready'):
# continue
# if not all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]):
# workorder.state = 'pending'
# continue
# if workorder.production_id.reservation_state not in ('waiting', 'confirmed', 'assigned'):
# continue
# if workorder.production_id.reservation_state == 'assigned' and workorder.state == 'waiting':
# workorder.state = 'ready'
# elif workorder.production_id.reservation_state != 'assigned' and workorder.state == 'ready':
# workorder.state = 'waiting'
# 重写工单开始按钮方法
def button_start(self):
@@ -856,12 +877,10 @@ class ResMrpWorkOrder(models.Model):
limit=1, order='id asc')
if not cnc_workorder.cnc_ids:
raise UserError(_('该制造订单还未下发CNC程序请稍后再试'))
# else:
# for item in cnc_workorder.cnc_ids:
# functional_cutting_tool = self.env['sf.functional.cutting.tool.entity'].search(
# [('tool_name_id.name', '=', item.cutting_tool_name)])
# if not functional_cutting_tool:
# raise UserError(_('该制造订单的CNC程序为%s没有对应的功能刀具' % item.cutting_tool_name))
else:
if self.production_id.tool_state in ['1', '2']:
raise UserError(
f'制造订单【{self.production_id.name}】功能刀具状态为【{self.production_id.tool_state}】!')
if self.routing_type == '解除装夹':
'''
记录开始时间
@@ -876,10 +895,9 @@ class ResMrpWorkOrder(models.Model):
('location_dest_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'VL-SPOC')]).id),
('origin', '=', self.production_id.name)])
purchase = self.env['purchase.order'].search([('origin', '=', self.production_id.name)])
if purchase and move_out:
if move_out:
move_out.write({'state': 'assigned'})
self.env['stock.move.line'].create(move_out.get_move_line(purchase, self))
self.env['stock.move.line'].create(move_out.get_move_line(self.production_id, self))
# move_out._action_assign()
if self.state == 'waiting' or self.state == 'ready' or self.state == 'progress':
@@ -946,7 +964,7 @@ class ResMrpWorkOrder(models.Model):
if record.routing_type == '装夹预调':
if not record.material_center_point and record.X_deviation_angle > 0:
raise UserError("请对前置三元检测定位参数进行计算定位")
if not record.rfid_code:
if not record.rfid_code and record.is_rework is False:
raise UserError("请扫RFID码进行绑定")
record.process_state = '待加工'
# record.write({'process_state': '待加工'})
@@ -955,6 +973,15 @@ class ResMrpWorkOrder(models.Model):
record.process_state = '待解除装夹'
# record.write({'process_state': '待加工'})
record.production_id.process_state = '待解除装夹'
record.production_id.write({'detection_result_ids': [(0, 0, {
'rework_reason': record.reason,
'detailed_reason': record.detailed_reason,
'processing_panel': record.processing_panel,
'routing_type': record.routing_type,
'handle_result': '待处理' if record.test_results == '返工' or record.is_rework is True else '',
'test_results': record.test_results,
'test_report': record.detection_report})],
'is_scrap': True if record.test_results == '报废' else False})
if record.routing_type == '解除装夹':
'''
记录结束时间
@@ -1003,40 +1030,46 @@ class ResMrpWorkOrder(models.Model):
record.write({
'date_planned_finished': tem_date_planned_finished # 保持原值
})
# if record.routing_type == 'CNC加工':
# record.write({
# 'date_finished': tem_date_finished # 保持原值
# })
# if record.routing_type == 'CNC加工' and record.test_results in ['返工', '报废']:
# record.production_id.action_cancel()
# record.production_id.workorder_ids.write({'rfid_code': False, 'rfid_code_old': record.rfid_code})
# if record.is_remanufacture is True:
# record.recreateManufacturingOrWorkerOrder()
is_production_id = True
for workorder in record.production_id.workorder_ids:
if workorder.state != 'done':
is_production_id = False
if record.routing_type == '解除装夹':
is_production_id = False
rework_workorder = record.production_id.workorder_ids.filtered(lambda p: p.state == 'rework')
done_workorder = record.production_id.workorder_ids.filtered(lambda p1: p1.state == 'done')
if (len(rework_workorder) + len(done_workorder) == len(record.production_id.workorder_ids)) or (
len(done_workorder) == len(record.production_id.workorder_ids)):
is_production_id = True
if record.routing_type in ['解除装夹'] or (
record.is_rework is True and record.routing_type in ['装夹预调']) or (
record.test_results in ['返工', '报废'] and record.routing_type in ['CNC加工']):
for workorder in record.production_id.workorder_ids:
if workorder.processing_panel == record.processing_panel:
rfid_code = workorder.rfid_code
workorder.write({'rfid_code_old': rfid_code,
'rfid_code': ''})
workorder.rfid_code_old = rfid_code
workorder.rfid_code = ''
'rfid_code': False})
if workorder.rfid_code:
raise ValidationError(f'{workorder.name}】工单解绑失败,请重新点击完成按钮!!!')
# workorder.rfid_code_old = rfid_code
# workorder.rfid_code = False
logging.info('workorder.rfid_code:%s' % workorder.rfid_code)
if is_production_id is True and record.routing_type in ['解除装夹', '表面工艺']:
logging.info('product_qty:%s' % record.production_id.product_qty)
for move_raw_id in record.production_id.move_raw_ids:
move_raw_id.quantity_done = move_raw_id.product_uom_qty
record.process_state = '已完工'
record.production_id.process_state = '已完工'
if record.routing_type in ['表面工艺']:
raw_move = self.env['stock.move'].sudo().search(
[('origin', '=', record.production_id.name), ('procure_method', '=', 'make_to_order'),
[('origin', '=', record.production_id.name),
('procure_method', 'in', ['make_to_order', 'make_to_stock']),
('state', '!=', 'done')])
if raw_move:
raw_move.write({'state': 'done'})
record.production_id.button_mark_done1()
# self.production_id.state = 'done'
# record.production_id.state = 'done'
# 将FTP的检测报告文件下载到临时目录
def download_reportfile_tmp(self, workorder, reportpath):
@@ -1078,32 +1111,15 @@ class ResMrpWorkOrder(models.Model):
workorder.detection_report = base64.b64encode(open(report_file_path, 'rb').read())
return True
# 重新下发nc程序
def button_send_program_again(self):
try:
res = {'programming_no': self.production_id.programming_no}
configsettings = self.env['res.config.settings'].get_values()
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
url = '/api/intelligent_programming/reset_state_again'
config_url = configsettings['sf_url'] + url
r = requests.post(config_url, json=res, data=None, headers=config_header)
r = r.json()
result = json.loads(r['result'])
if result['status'] == 1:
productions = self.env['mrp.production'].search(
[('programming_no', '=', self.production_id.programming_no), ('programming_state', '=', '已编程')])
if productions:
workorder = productions.workorder_ids.filtered(
lambda ap: ap.routing_type in ['装夹预调', 'CNC加工'] and ap.state not in ['done', 'cancel',
'progress'])
if workorder:
workorder.write({'is_send_program_again': True})
productions.write({'work_state': '编程中', 'programming_state': '编程中'})
else:
raise UserError(result['message'])
except Exception as e:
logging.info('button_send_program_again error:%s' % e)
raise UserError("重新下发nc程序失败,请联系管理员")
def print_method(self):
"""
解除装夹处调用关联制造订单的关联序列号的打印方法
"""
if self.production_id:
if self.production_id.lot_producing_id:
self.production_id.lot_producing_id.print_single_method()
else:
raise UserError("无关联制造订单或关联序列号,无法打印。请检查!")
class CNCprocessing(models.Model):
@@ -1132,6 +1148,8 @@ class CNCprocessing(models.Model):
program_path = fields.Char('程序文件路径')
program_create_date = fields.Datetime('程序创建日期')
tool_state = fields.Selection([('0', '正常'), ('1', '缺刀'), ('2', '无效刀')], string='刀具状态', default='0')
# mrs下发编程单创建CNC加工
def cnc_processing_create(self, cnc_workorder, ret, program_path, program_path_tmp):
cnc_processing = None
@@ -1165,24 +1183,25 @@ class CNCprocessing(models.Model):
def _json_cnc_processing(self, panel, ret):
cnc_processing = []
for item in ret['programming_list']:
if item['processing_panel'] == panel and item['ftp_path'].find('.dmi') == -1:
cnc_processing.append((0, 0, {
'sequence_number': item['sequence_number'],
'program_name': item['program_name'],
'cutting_tool_name': item['cutting_tool_name'],
'cutting_tool_no': item['cutting_tool_no'],
'processing_type': item['processing_type'],
'margin_x_y': item['margin_x_y'],
'margin_z': item['margin_z'],
'depth_of_processing_z': item['depth_of_processing_z'],
'cutting_tool_extension_length': item['cutting_tool_extension_length'],
'cutting_tool_handle_type': item['cutting_tool_handle_type'],
'estimated_processing_time': item['estimated_processing_time'],
'program_path': item['ftp_path'],
'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'),
'remark': item['remark']
}))
if ret is not False:
for item in ret['programming_list']:
if item['processing_panel'] == panel and item['ftp_path'].find('.dmi') == -1:
cnc_processing.append((0, 0, {
'sequence_number': item['sequence_number'],
'program_name': item['program_name'],
'cutting_tool_name': item['cutting_tool_name'],
'cutting_tool_no': item['cutting_tool_no'],
'processing_type': item['processing_type'],
'margin_x_y': item['margin_x_y'],
'margin_z': item['margin_z'],
'depth_of_processing_z': item['depth_of_processing_z'],
'cutting_tool_extension_length': item['cutting_tool_extension_length'],
'cutting_tool_handle_type': item['cutting_tool_handle_type'],
'estimated_processing_time': item['estimated_processing_time'],
'program_path': item['ftp_path'],
'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'),
'remark': item['remark']
}))
return cnc_processing
# 根据程序名和加工面匹配到ftp里对应的Nc程序名,可优化为根据cnc_processing.program_path进行匹配
@@ -1215,11 +1234,13 @@ class CNCprocessing(models.Model):
# 将FTP的多面的程序单文件下载到临时目录
def download_file_tmp(self, production_no, processing_panel):
remotepath = os.path.join('/NC', production_no, 'return', processing_panel)
remotepath = os.path.join('/home/ftp/ftp_root/NC', production_no, 'return', processing_panel)
serverdir = os.path.join('/tmp', production_no, 'return', processing_panel)
ftp_resconfig = self.env['res.config.settings'].get_values()
ftp = FtpController(str(ftp_resconfig['ftp_host']), int(ftp_resconfig['ftp_port']), ftp_resconfig['ftp_user'],
ftp_resconfig['ftp_password'])
if not ftp.file_exists_1(remotepath):
logging.info('目录不存在:%s' % remotepath)
download_state = ftp.download_program_file(remotepath, serverdir)
logging.info('download_state:%s' % download_state)
return download_state
@@ -1265,6 +1286,7 @@ class SfWorkOrderBarcodes(models.Model):
_inherit = ["mrp.workorder", "barcodes.barcode_events_mixin"]
def on_barcode_scanned(self, barcode):
logging.info('Rfid:%s' % barcode)
workorder = self.env['mrp.workorder'].browse(self.ids)
# workorder_preset = self.env['mrp.workorder'].search(
# [('routing_type', '=', '装夹预调'), ('rfid_code', '=', barcode)])
@@ -1308,7 +1330,7 @@ class SfWorkOrderBarcodes(models.Model):
self.process_state = '待检测'
self.date_start = datetime.now()
else:
raise UserError('托盘信息不存在')
raise UserError('没有找到Rfid为【%s】的托盘信息!!!' % barcode)
# stock_move_line = self.env['stock.move.line'].search([('lot_name', '=', barcode)])
# if stock_move_line.product_id.categ_type == '夹具':
# workorder.write({
@@ -1382,7 +1404,8 @@ class WorkPieceDelivery(models.Model):
[('上产线', '上产线'), ('下产线', '下产线'), ('运送空料架', '运送空料架')], string='类型')
delivery_duration = fields.Float('配送时长', compute='_compute_delivery_duration')
status = fields.Selection(
[('待下发', '待下发'), ('待配送', '待配送'), ('已配送', '已配送')], string='状态', default='待下发',
[('待下发', '待下发'), ('待配送', '待配送'), ('已配送', '已配送'), ('已取消', '已取消')], string='状态',
default='待下发',
tracking=True)
is_cnc_program_down = fields.Boolean('程序是否下发', default=False, tracking=True)
is_manual_work = fields.Boolean('人工操作', default=False)
@@ -1635,12 +1658,13 @@ class CMMprogram(models.Model):
def _json_cmm_program(self, panel, ret):
cmm_program = []
for item in ret['programming_list']:
if item['processing_panel'] == panel and item['ftp_path'].find('.dmi') != -1:
cmm_program.append((0, 0, {
'sequence_number': 1,
'program_name': item['program_name'],
'program_path': item['ftp_path'],
'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'),
}))
if ret is not False:
for item in ret['programming_list']:
if item['processing_panel'] == panel and item['ftp_path'].find('.dmi') != -1:
cmm_program.append((0, 0, {
'sequence_number': 1,
'program_name': item['program_name'],
'program_path': item['ftp_path'],
'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'),
}))
return cmm_program

View File

@@ -616,7 +616,7 @@ class ResProductMo(models.Model):
item['model_width'] + model_type.embryo_tolerance) * (
item['model_height'] + model_type.embryo_tolerance),
'product_model_type_id': model_type.id,
'model_processing_panel': 'R',
# 'model_processing_panel': 'R',
'model_machining_precision': item['model_machining_precision'],
'model_code': item['barcode'],
'length': item['model_long'],
@@ -634,6 +634,7 @@ class ResProductMo(models.Model):
'model_process_parameters_ids': [(6, 0, [])] if not item.get(
'process_parameters_code') else self.get_process_parameters_id(item['process_parameters_code']),
'model_remark': item['remark'],
'single_manufacturing': True,
'default_code': '%s-%s' % (order_number, i),
'manual_quotation': item['manual_quotation'] or False,
'part_number': item.get('part_number') or '',

View File

@@ -205,39 +205,18 @@ class StockRule(models.Model):
productions_values)
# self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \
(
p.move_dest_ids.procure_method != 'make_to_order' and not
p.move_raw_ids and not p.workorder_ids)).action_confirm()
# self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
'''
创建工单
'''
# productions._create_workorder()
#
grouped_product_ids = {k: list(g) for k, g in groupby(productions, key=lambda x: x.product_id.id)}
# 初始化一个字典来存储每个product_id对应的生产订单名称列表
product_id_to_production_names = {}
# 对于每个product_id获取其所有生产订单的名称
for product_id, productions in grouped_product_ids.items():
# 为同一个product_id创建一个生产订单名称列表
product_id_to_production_names[product_id] = [production.name for production in productions]
for production_item in productions:
if production_item.product_id.id in product_id_to_production_names:
# # 同一个产品多个制造订单对应一个编程单和模型库
# # 只调用一次fetchCNC并将所有生产订单的名称作为字符串传递
if not production_item.programming_no:
production_programming = self.env['mrp.production'].search(
[('product_id.id', '=', production_item.product_id.id),
('origin', '=', production_item.origin)],
limit=1, order='id asc')
if not production_programming.programming_no:
production_item.fetchCNC(
', '.join(product_id_to_production_names[production_item.product_id.id]))
else:
production_item.write({'programming_no': production_programming.programming_no,
'programming_state': '编程中'})
self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \
(
p.move_dest_ids.procure_method != 'make_to_order' and not
p.move_raw_ids and not p.workorder_ids)).action_confirm()
for production in productions:
'''
创建制造订单时生成序列号
@@ -283,6 +262,29 @@ class StockRule(models.Model):
'product_id': production.product_id.id,
'state': 'draft',
})
all_production = productions
grouped_product_ids = {k: list(g) for k, g in groupby(all_production, key=lambda x: x.product_id.id)}
# 初始化一个字典来存储每个product_id对应的生产订单名称列表
product_id_to_production_names = {}
# 对于每个product_id获取其所有生产订单的名称
for product_id, all_production in grouped_product_ids.items():
# 为同一个product_id创建一个生产订单名称列表
product_id_to_production_names[product_id] = [production.name for production in all_production]
for production_item in productions:
if production_item.product_id.id in product_id_to_production_names:
# # 同一个产品多个制造订单对应一个编程单和模型库
# # 只调用一次fetchCNC并将所有生产订单的名称作为字符串传递
if not production_item.programming_no:
production_programming = self.env['mrp.production'].search(
[('product_id.id', '=', production_item.product_id.id),
('origin', '=', production_item.origin)],
limit=1, order='id asc')
if not production_programming.programming_no:
production_item.fetchCNC(
', '.join(product_id_to_production_names[production_item.product_id.id]))
else:
production_item.write({'programming_no': production_programming.programming_no,
'programming_state': '编程中'})
return True
@@ -403,6 +405,7 @@ class ProductionLot(models.Model):
def print_single_method(self):
print('self.name========== %s' % self.name)
self.ensure_one()
qr_code_data = self.qr_code_image
if not qr_code_data:
@@ -486,10 +489,10 @@ class StockPicking(models.Model):
('location_dest_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'VL-SPOC')]).id),
('origin', '=', self.origin)])
if self.id == move_out.picking_id.id:
if move_out.move_line_ids.workorder_id.state not in ['progress']:
raise UserError(
_('该出库单里源单据内的单号为%s的工单还未开始,不能进行验证操作!' % move_out.move_line_ids.workorder_id.name))
# if self.id == move_out.picking_id.id:
# if move_out.move_line_ids.workorder_id.state not in ['progress']:
# raise UserError(
# _('该出库单里源单据内的单号为%s的工单还未开始,不能进行验证操作!' % move_out.move_line_ids.workorder_id.name))
# 入库单验证
move_in = self.env['stock.move'].search(
[('location_dest_id', '=', self.env['stock.location'].search(
@@ -505,18 +508,17 @@ class StockPicking(models.Model):
res = super().button_validate()
if res is True:
if self.id == move_out.picking_id.id:
if move_out.move_line_ids.workorder_id.state == 'progress':
move_in = self.env['stock.move'].search(
[('location_dest_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
('location_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'VL-SPOC')]).id),
('origin', '=', self.origin)])
# purchase = self.env['purchase.order'].search([('origin', '=', self.origin)])
if move_in:
move_in.write({'state': 'assigned'})
purchase = self.env['purchase.order'].search([('origin', '=', self.origin)])
self.env['stock.move.line'].create(move_in.get_move_line(purchase, None))
# if move_out.move_line_ids.workorder_id.state == 'progress':
move_in = self.env['stock.move'].search(
[('location_dest_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
('location_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'VL-SPOC')]).id),
('origin', '=', self.origin)])
production = self.env['mrp.production'].search([('name', '=', self.origin)])
if move_in:
move_in.write({'state': 'assigned'})
self.env['stock.move.line'].create(move_in.get_move_line(production, None))
return res
@@ -598,7 +600,7 @@ class ReStockMove(models.Model):
'state': 'confirmed',
}
def get_move_line(self, purchase, sorted_workorders):
def get_move_line(self, production_id, sorted_workorders):
return {
'move_id': self.id,
'product_id': self.product_id.id,
@@ -607,7 +609,7 @@ class ReStockMove(models.Model):
'location_dest_id': self.picking_id.location_dest_id.id,
'picking_id': self.picking_id.id,
'reserved_uom_qty': 1.0,
'lot_id': purchase.picking_ids.move_line_ids.lot_id.id,
'lot_id': production_id.move_line_raw_ids.lot_id.id,
'company_id': self.company_id.id,
# 'workorder_id': '' if not sorted_workorders else sorted_workorders.id,
# 'production_id': '' if not sorted_workorders else sorted_workorders.production_id.id,

View File

@@ -29,7 +29,7 @@ access_mrp_workorder_group_sf_mrp_user,mrp_workorder,model_mrp_workorder,sf_base
access_mrp_workorder_manager,mrp_workorder,model_mrp_workorder,sf_base.group_sf_mrp_manager,1,1,1,0
access_mrp_workcenter_group_sf_mrp_user,mrp_workcenter,model_mrp_workcenter,sf_base.group_sf_mrp_user,1,0,0,0
access_mrp_workcenter_manager,mrp_workcenter,model_mrp_workcenter,sf_base.group_sf_mrp_manager,1,1,1,0
access_mrp_workcenter_productivity_group_sf_mrp_user,mrp_workcenter_productivity,model_mrp_workcenter_productivity,sf_base.group_sf_mrp_user,1,0,0,0
access_mrp_workcenter_productivity_group_sf_equipment_user,mrp_workcenter_productivity,model_mrp_workcenter_productivity,sf_base.group_sf_equipment_user,1,1,1,0
access_mrp_workcenter_productivity_manager,mrp_workcenter_productivity,model_mrp_workcenter_productivity,sf_base.group_sf_mrp_manager,1,1,1,0
access_sf_workpiece_delivery_group_sf_order_user,sf_workpiece_delivery_group_sf_order_user,model_sf_workpiece_delivery,sf_base.group_sf_order_user,1,1,1,0
access_sf_workpiece_delivery_group_sf_equipment_user,sf_workpiece_delivery_group_sf_equipment_user,model_sf_workpiece_delivery,sf_base.group_sf_equipment_user,1,1,0,0
@@ -141,3 +141,14 @@ access_sf_model_type_group_sf_stock_manager,sf_model_type_group_sf_mrp_manager,m
access_mrp_bom_byproduct_group_sf_stock_user,mrp_bom_byproduct_group_sf_stock_user,mrp.model_mrp_bom_byproduct,sf_base.group_sf_stock_user,1,0,0,0
access_mrp_bom_byproduct_group_sf_stock_manager,mrp_bom_byproduct_group_sf_mrp_manager,mrp.model_mrp_bom_byproduct,sf_base.group_sf_stock_manager,1,0,0,0
access_sf_rework_wizard_group_sf_order_user,sf_rework_wizard_group_sf_order_user,model_sf_rework_wizard,sf_base.group_sf_order_user,1,1,1,0
access_sf_rework_wizard_group_plan_dispatch,sf_rework_wizard_group_plan_dispatch,model_sf_rework_wizard,sf_base.group_plan_dispatch,1,1,1,0
access_sf_detection_result_group_sf_order_user,sf_detection_result_group_sf_order_user,model_sf_detection_result,sf_base.group_sf_order_user,1,1,1,0
access_sf_detection_result_group_plan_dispatch,sf_detection_result_group_plan_dispatch,model_sf_detection_result,sf_base.group_plan_dispatch,1,1,1,0
access_sf_detection_result_group_sf_equipment_user,sf_detection_result_group_sf_equipment_user,model_sf_detection_result,sf_base.group_sf_equipment_user,1,1,1,0
access_sf_processing_panel_group_sf_order_user,sf_processing_panel_group_sf_order_user,model_sf_processing_panel,sf_base.group_sf_order_user,1,1,1,0
access_sf_production_wizard_group_sf_order_user,sf_production_wizard_group_sf_order_user,model_sf_production_wizard,sf_base.group_sf_order_user,1,1,1,0
access_sf_processing_panel_group_plan_dispatch,sf_processing_panel_group_plan_dispatch,model_sf_processing_panel,sf_base.group_plan_dispatch,1,1,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
29 access_mrp_workorder_manager mrp_workorder model_mrp_workorder sf_base.group_sf_mrp_manager 1 1 1 0
30 access_mrp_workcenter_group_sf_mrp_user mrp_workcenter model_mrp_workcenter sf_base.group_sf_mrp_user 1 0 0 0
31 access_mrp_workcenter_manager mrp_workcenter model_mrp_workcenter sf_base.group_sf_mrp_manager 1 1 1 0
32 access_mrp_workcenter_productivity_group_sf_mrp_user access_mrp_workcenter_productivity_group_sf_equipment_user mrp_workcenter_productivity model_mrp_workcenter_productivity sf_base.group_sf_mrp_user sf_base.group_sf_equipment_user 1 0 1 0 1 0
33 access_mrp_workcenter_productivity_manager mrp_workcenter_productivity model_mrp_workcenter_productivity sf_base.group_sf_mrp_manager 1 1 1 0
34 access_sf_workpiece_delivery_group_sf_order_user sf_workpiece_delivery_group_sf_order_user model_sf_workpiece_delivery sf_base.group_sf_order_user 1 1 1 0
35 access_sf_workpiece_delivery_group_sf_equipment_user sf_workpiece_delivery_group_sf_equipment_user model_sf_workpiece_delivery sf_base.group_sf_equipment_user 1 1 0 0
141 access_sf_processing_panel_group_plan_dispatch sf_processing_panel_group_plan_dispatch model_sf_processing_panel sf_base.group_plan_dispatch 1 1 1 0
142
143
144
145
146
147
148
149
150
151
152
153
154

View File

@@ -1,11 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="custom_mrp_production_tree_view" model="ir.ui.view">
<field name="name">custom.mrp.production.tree</field>
<field name="model">mrp.production</field>
<field name="inherit_id" ref="mrp.mrp_production_tree_view"/>
<field name="arch" type="xml">
<xpath expr="//button[@name='do_unreserve']" position="after">
<button name="do_update_program" type="object" string="更新程序"
groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="//field[@name='product_id']" position="replace"/>
<xpath expr="//field[@name='product_qty']" position="replace"/>
<xpath expr="//field[@name='product_uom_id']" position="replace"/>
@@ -65,31 +70,38 @@
<!-- <attribute name="statusbar_visible">draft,confirmed,progress,pending_processing,completed,done -->
<!-- </attribute> -->
<attribute name="statusbar_visible">
progress,pending_cam,pending_processing,pending_era_cam,completed,done
confirmed,pending_cam,progress,done
</attribute>
</xpath>
<xpath expr="//sheet//group//group[2]//label" position="before">
<!-- <field name="process_state"/> -->
<field name="state"/>
<field name="state" readonly="1"/>
<!-- <field name="process_state"/> -->
</xpath>
<xpath expr="//sheet//group//group//div[3]" position="after">
<field name="manual_quotation" readonly="1"/>
<field name="programming_no" readonly="1"/>
<field name="programming_state" readonly="1"
decoration-success="programming_state == '已编程'"
decoration-warning="programming_state =='编程中'"
decoration-danger="programming_state =='已编程未下发'"/>
<field name="work_state" invisible="1"/>
<field name="schedule_state" invisible='1'/>
<field name="manual_quotation" readonly="1"/>
<field name="programming_state" readonly="1"/>
<field name="is_scrap" invisible='1'/>
<field name="is_rework" invisible='1'/>
</xpath>
<xpath expr="//field[@name='user_id']" position="before">
<field name="plan_start_processing_time" readonly="1"/>
</xpath>
<xpath expr="//field[@name='user_id']" position="after">
<field name="production_line_id" readonly="1"/>
<field name="production_line_state" readonly="1"/>
<!-- <field name="production_line_state" readonly="1"/>-->
<field name="part_number" string="成品的零件图号"/>
<field name="part_drawing"/>
<field name="tool_state"/>
<field name="tool_state_remark" string="备注" attrs="{'invisible': [('tool_state', '!=', '1')]}"/>
<field name="tool_state_remark2" invisible="1"/>
</xpath>
<xpath expr="//header//button[@name='action_cancel']" position="replace">
<button name="action_cancel" type="object" string="取消" data-hotkey="z"
@@ -106,9 +118,20 @@
<xpath expr="(//header//button[@name='button_mark_done'])[2]" position="replace">
<button name="button_mark_done"
attrs="{'invisible': ['|', '|', ('state', 'in', ('draft', 'cancel', 'done', 'to_close')), ('qty_producing', '=', 0), ('move_raw_ids', '=', [])]}"
string="验证" type="object" class="oe_highlight" data-hotkey="g"
string="验证" type="object" data-hotkey="g"
groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="(//header//button[@name='button_scrap'])" position="replace">
<button name="button_scrap" invisible="1"/>
<button name="do_update_program" string="更新程序" type="object" groups="sf_base.group_sf_mrp_user"
confirm="是否确认更新程序"
attrs="{'invisible': ['|',('state', '!=', 'rework'),('programming_state', '!=', '已编程未下发')]}"/>
<button name="button_rework" string="返工" type="object" groups="sf_base.group_sf_mrp_user"
attrs="{'invisible': ['|',('state', '!=', 'rework') ,('programming_state', '!=', '已编程')]}"/>
<button name="%(sf_manufacturing.action_sf_production_wizard)d" string="报废" type="action"
groups="sf_base.group_sf_mrp_user"
attrs="{'invisible': [('is_scrap', '=', False)]}"/>
</xpath>
<xpath expr="(//header//button[@name='button_mark_done'])[3]" position="replace">
<button name="button_mark_done" attrs="{'invisible': [
'|',
@@ -138,11 +161,11 @@
attrs="{'invisible': ['|', ('is_planned', '=', False), ('state', '=', 'cancel')]}"
data-hotkey="x" groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="//header//button[@name='button_scrap']" position="replace">
<button name="button_scrap" type="object" string="报废"
attrs="{'invisible': [('state', 'in', ('cancel', 'draft'))]}" data-hotkey="y"
groups="sf_base.group_sf_mrp_user"/>
</xpath>
<!-- <xpath expr="//header//button[@name='button_scrap']" position="replace">-->
<!-- <button name="button_scrap" type="object" string="报废"-->
<!-- attrs="{'invisible': [('state', 'in', ('cancel', 'draft'))]}" data-hotkey="y"-->
<!-- groups="sf_base.group_sf_mrp_user"/>-->
<!-- </xpath>-->
<xpath expr="//header//button[@name='action_confirm']" position="replace">
@@ -255,7 +278,35 @@
</xpath>
<xpath expr="//sheet//notebook//page[@name='operations']" position="attributes">
<attribute name="attrs">{'invisible': [('schedule_state', '=', '未排')]}</attribute>
<attribute name="attrs">{'invisible': ['|',('schedule_state', '=', '未排'),('workorder_ids', '=',
[])]}
</attribute>
</xpath>
<xpath expr="//sheet//notebook//page[@name='operations']" position="after">
<page string="检测结果" attrs="{'invisible': [('detection_result_ids', '=', [])]}">
<field name="detection_result_ids" string="" readonly="1">
<tree sample="1">
<field name="production_id" invisible="1"/>
<field name="processing_panel"/>
<field name="routing_type"/>
<field name="rework_reason"/>
<field name="detailed_reason"/>
<field name="test_results"/>
<field name="handle_result"/>
<field name="test_report" invisible="1"/>
<button name="button_look_test_report" string="查看测试报告" type="object"
attrs="{'invisible': [('test_report', '=', False)]}"
class="btn btn-primary me-1"/>
</tree>
</field>
</page>
</xpath>
<xpath expr="//sheet//notebook//page[@name='components']" position="attributes">
<attribute name="string">投料</attribute>
</xpath>
<xpath expr="//field[@name='components_availability']" position="attributes">
<attribute name="string">投料状态</attribute>
</xpath>
</field>
</record>
@@ -278,11 +329,23 @@
<field name="model">mrp.workorder</field>
<field name="inherit_id" ref="mrp.mrp_production_workorder_tree_editable_view"/>
<field name="arch" type="xml">
<xpath expr="//tree" position="attributes">
<attribute name="default_order">sequence</attribute>
</xpath>
<xpath expr="//field[@name='state']" position="replace">
<field name="state" widget="badge" decoration-warning="state == 'progress'"
decoration-success="state == 'done'" decoration-danger="state in ('cancel','rework')"
decoration-muted="state == 'to be detected'"
decoration-info="state not in ('progress', 'done', 'cancel','rework','to be detected')"
attrs="{'invisible': [('production_state', '=', 'draft')], 'column_invisible': [('parent.state', '=', 'draft')]}"/>
</xpath>
<xpath expr="//tree//button[@name='button_start']" position="replace">
<field name="routing_type" invisible="True"/>
<button name="button_start" type="object" string="开始" class="btn-success"
attrs="{'invisible': ['|', '|', '|','|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')), ('is_user_working', '!=', False), ('routing_type', '=', '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')), ('is_user_working', '!=',
False), ('routing_type', '=', 'CNC加工')]}"
groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="//tree//button[@name='button_pending']" position="replace">
<button name="button_pending" type="object" string="Pause" class="btn-warning"
@@ -291,7 +354,7 @@
</xpath>
<xpath expr="//tree//button[@name='button_finish']" position="replace">
<button name="button_finish" type="object" string="Done" class="btn-success"
attrs="{'invisible': ['|', '|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False), ('routing_type', '=', 'CNC加工')]}"
attrs="{'invisible': ['|', '|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"
groups="sf_base.group_sf_mrp_user"
confirm="是否确认完成?"/>
</xpath>
@@ -361,7 +424,9 @@
<xpath expr="//header//button[@name='action_cancel']" position="replace">
<button name="action_cancel" type="object" string="取消" groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="//field[@name='state']" position="after">
<field name="tool_state" invisible="1"/>
</xpath>
</field>
</record>
@@ -393,6 +458,22 @@
<field name="model">mrp.production</field>
<field name="inherit_id" ref="mrp.view_mrp_production_filter"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='filter_in_progress']" position="before">
<filter string="返工" name="filter_rework" domain="[('state', '=', 'rework')]"/>
</xpath>
<xpath expr="//filter[@name='planning_issues']" position="before">
<separator/>
<filter string="返工且已编程" name="filter_rework_programmed"
domain="[('state', '=', 'rework'),('programming_state', '=', '已编程')]"/>
<separator/>
<filter name="filter_programming" string="编程中"
domain="[('programming_state', '=', '编程中')]"/>
<filter name="filter_programmed" string="已编程"
domain="[('programming_state', '=', '已编程')]"/>
<filter name="filter_programmed_not_delivered" string="已编程未下发"
domain="[('programming_state', '=', '已编程未下发')]"/>
<separator/>
</xpath>
<xpath expr="//search" position="inside">
<searchpanel class="account_root">
<field name="state" icon="fa-filter" enable_counters="1"/>
@@ -478,5 +559,72 @@
</field>
</record>
<record model="ir.ui.view" id="sf_test_report_form">
<field name="name">sf.detection.result</field>
<field name="model">sf.detection.result</field>
<field name="arch" type="xml">
<form>
<group>
<!-- <field name="handle_result"/>-->
<field name="test_report" readonly="1" widget="pdf_viewer"/>
</group>
</form>
</field>
</record>
<record id="action_test_report_form" model="ir.actions.act_window">
<field name="name">检测报告</field>
<field name="res_model">sf.detection.result</field>
<field name="view_mode">form</field>
</record>
<!-- <menuitem action="mrp.mrp_production_action"-->
<!-- id="mrp_production_action"-->
<!-- sequence="1" active="False"/>-->
<!-- &lt;!&ndash; <record id="mrp_production_action_sf" model="ir.actions.act_window">&ndash;&gt;-->
<!-- &lt;!&ndash; <field name="name">Manufacturing Orders</field>&ndash;&gt;-->
<!-- &lt;!&ndash; <field name="type">ir.actions.act_window</field>&ndash;&gt;-->
<!-- &lt;!&ndash; <field name="res_model">mrp.production</field>&ndash;&gt;-->
<!-- &lt;!&ndash; <field name="view_mode">tree,kanban,form,calendar,pivot,graph</field>&ndash;&gt;-->
<!-- &lt;!&ndash; <field name="search_view_id" ref="mrp.view_mrp_production_filter"/>&ndash;&gt;-->
<!-- &lt;!&ndash; &lt;!&ndash; <field name="context">{'search_default_todo': True, 'default_company_id': allowed_company_ids[0]}</field>&ndash;&gt;&ndash;&gt;-->
<!-- &lt;!&ndash; <field name="domain">[('picking_type_id.active', '=', True)]</field>&ndash;&gt;-->
<!-- &lt;!&ndash; </record>&ndash;&gt;-->
<!-- &lt;!&ndash; <menuitem action="mrp_production_action_sf"&ndash;&gt;-->
<!-- &lt;!&ndash; id="menu_mrp_production_action_sf"&ndash;&gt;-->
<!-- &lt;!&ndash; parent="mrp.menu_mrp_manufacturing"&ndash;&gt;-->
<!-- &lt;!&ndash; sequence="2"&ndash;&gt;-->
<!-- &lt;!&ndash; string="制造订单"/>&ndash;&gt;-->
<!-- <record id="mrp_production_action_sf" model="ir.actions.act_window">-->
<!-- <field name="name">Manufacturing Orders</field>-->
<!-- <field name="type">ir.actions.act_window</field>-->
<!-- <field name="res_model">mrp.production</field>-->
<!-- <field name="view_mode">tree,kanban,form,calendar,pivot,graph</field>-->
<!-- <field name="view_id" eval="False"/>-->
<!-- <field name="search_view_id" ref="mrp.view_mrp_production_filter"/>-->
<!-- <field name="context">{'search_default_todo': True, 'default_company_id':-->
<!-- allowed_company_ids[0],'search_default_filter_rework':1,'search_default_filter_programmed':1}-->
<!-- </field>-->
<!-- <field name="domain">[('picking_type_id.active', '=', True)]</field>-->
<!-- &lt;!&ndash; &lt;!&ndash; <field name="help" type="html">&ndash;&gt;,'search_default_filter_rework': 1,&ndash;&gt;-->
<!-- &lt;!&ndash; 'search_default_filter_programming': 1&ndash;&gt;-->
<!-- &lt;!&ndash; <p class="o_view_nocontent_smiling_face">&ndash;&gt;-->
<!-- &lt;!&ndash; No manufacturing order found. Let's create one.&ndash;&gt;-->
<!-- &lt;!&ndash; </p>&ndash;&gt;-->
<!-- &lt;!&ndash; <p>&ndash;&gt;-->
<!-- &lt;!&ndash; Consume <a name="%(product.product_template_action)d" type='action' tabindex="-1">components</a> and&ndash;&gt;-->
<!-- &lt;!&ndash; build finished products using&ndash;&gt;-->
<!-- &lt;!&ndash; <a name="%(mrp_bom_form_action)d" type='action' tabindex="-1">bills of materials</a>&ndash;&gt;-->
<!-- &lt;!&ndash; </p>&ndash;&gt;-->
<!-- &lt;!&ndash; </field>&ndash;&gt;-->
<!-- </record>-->
<!-- <menuitem action="sf_manufacturing.mrp_production_action_sf"-->
<!-- id="mrp_production_action"-->
<!-- parent="mrp.menu_mrp_manufacturing"-->
<!-- sequence="1"/>-->
</data>
</odoo>

View File

@@ -24,6 +24,20 @@
</field>
</record>
<record id="custom_model_form_view_inherit" model="ir.ui.view">
<field name="name">custom.model.form.view.inherit</field>
<field name="model">mrp.workcenter</field>
<field name="inherit_id" ref="mrp.mrp_workcenter_view"/>
<field name="arch" type="xml">
<xpath expr='//form//sheet' position="after">
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
</div>
</xpath>
</field>
</record>
<record id="mrp_workcenter_view_kanban_inherit_workorder" model="ir.ui.view">
<field name="name">mrp.workcenter.view.kanban.inherit.mrp.workorder</field>
<field name="model">mrp.workcenter</field>

View File

@@ -7,11 +7,10 @@
<field name="arch" type="xml">
<field name="name" position="replace">
<field name="is_subcontract" invisible="1"/>
<field name="name" decoration-success="is_subcontract" decoration-bf="is_subcontract"/>
</field>
<field name="name" position="before">
<field name="sequence" optional="hide"/>
<field name="sequence"/>
<field name='user_permissions' invisible="1"/>
</field>
<field name="name" position="after">
@@ -19,6 +18,7 @@
</field>
<field name="state" position="after">
<field name="work_state" optional="hide"/>
<field name="tool_state" invisible="1"/>
<field name="product_tmpl_name" invisible="1"/>
</field>
<field name="duration" position="replace">
@@ -43,7 +43,7 @@
<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加工程序")]}
('is_user_working', '!=', False),("user_permissions","=",False),("name","=","CNC加工")]}
</attribute>
</xpath>
<xpath expr="//button[@name='%(mrp.act_mrp_block_workcenter_wo)d']" position="attributes">
@@ -91,7 +91,7 @@
<!-- <field name="target">fullscreen</field>-->
<field name="target">current</field>
<field name="domain">[('state', '!=', 'cancel'),('schedule_state', '=', '已排')]</field>
<field name="context">{'search_default_workcenter_id': active_id}</field>
<field name="context">{'search_default_product': 1, 'search_default_workcenter_id': active_id}</field>
<field name="help" type="html">
<p class="o_view_nocontent_workorder">
没有工单要做!
@@ -113,6 +113,10 @@
<field name="model">mrp.workorder</field>
<field name="inherit_id" ref="mrp.mrp_production_workorder_form_view_inherit"/>
<field name="arch" type="xml">
<xpath expr="//header/field[@name='state']" position="replace">
<field name="state" widget="statusbar"
statusbar_visible="pending,waiting,ready,progress,to be detected,done,rework"/>
</xpath>
<xpath expr="//div[@name='button_box']" position="inside">
<button type="object" name="action_view_surface_technics_picking" class="oe_stat_button" icon="fa-truck"
groups="base.group_user,sf_base.group_sf_order_user"
@@ -121,10 +125,12 @@
</button>
</xpath>
<xpath expr="//field[@name='state']" position="before">
<field name='tool_state' invisible="1"/>
<field name='user_permissions' invisible="1"/>
<field name='name' invisible="1"/>
<field name='is_rework' invisible="1"/>
<field name='is_delivery' invisible="1"/>
<field name='is_send_program_again' invisible="1"/>
<!-- <field name='is_send_program_again' invisible="1"/>-->
<!-- 工单form页面的开始停工按钮等 -->
<!-- <button name="button_start" type="object" string="开始" class="btn-success" -->
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'not in', ('pending_processing', 'pending_cam', 'pending_era_cam')), ('state','!=','ready'), ('routing_type', 'not in', ('装夹预调', 'CNC加工', '解除装夹'))]}" -->
@@ -136,8 +142,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"
attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')), ('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_pending" type="object" string="暂停" class="btn-warning"
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>
<button name="button_finish" type="object" string="完成" class="btn-success" confirm="是否确认完工"
@@ -159,12 +165,12 @@
<!-- 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')]}"/>
<button name="button_send_program_again" type="object" string="重新下发NC程序" class="btn-primary"
confirm="是否确认重新下发NC程序"
groups="sf_base.group_sf_order_user,sf_base.group_sf_equipment_user"
attrs="{'invisible': ['|', '|', '|',('routing_type','!=','装夹预调'),('state','in',['done', 'cancel',
'progress']),('cnc_worksheet','=',False),('is_send_program_again','=',True)]}"/>
attrs="{'invisible': ['|','|','|','|',('routing_type','!=','装夹预调'),('is_delivery','=',True),('state','!=','done'),('is_rework','=',True),'&amp;',('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)]}"/>
<button name="print_method" type="object" string="打印二维码" class="btn-primary"
attrs="{'invisible': ['|',('routing_type','!=','解除装夹'),('state','!=','done')]}"/>
</xpath>
<xpath expr="//page[1]" position="before">
<page string="开料要求" attrs='{"invisible": [("routing_type","!=","切割")]}'>
@@ -183,8 +189,11 @@
</page>
</xpath>
<xpath expr="//label[1]" position="before">
<field name='routing_type'/>
<field name='routing_type' readonly="1"/>
<field name='process_state' attrs='{"invisible": [("routing_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>
@@ -215,11 +224,11 @@
attrs="{'invisible': [('routing_type', '!=', 'CNC加工')]}"/>
<field name="processing_panel" readonly="1"
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
<field name="equipment_id"
<field name="equipment_id" readonly="1"
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割","装夹预调"))]}'/>
<field name="production_line_id"
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
<field name="production_line_state"
<field name="production_line_state" readonly="1"
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割","装夹预调"))]}'/>
<!-- <field name="functional_fixture_id" -->
<!-- attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/> -->
@@ -227,9 +236,6 @@
<!-- attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/> -->
<!-- <field name="functional_fixture_type_id" -->
<!-- attrs='{"invisible": [("routing_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)]}"/>
</group>
<!-- <group>-->
<!-- <div>-->
@@ -242,23 +248,6 @@
<!-- <field name="processing_panel" readonly="1" attrs="{'invisible': [('routing_type', 'in', ('获取CNC加工程序','装夹','解除装夹',-->
<!-- '前置三元定位检测','后置三元质量检测','解除装夹'))]}"/>-->
</field>
<xpath expr="//page[1]" position="before">
<page string="获取CNC加工程序" attrs='{"invisible": [("routing_type","!=","获取CNC加工程序")]}'>
<group>
<field name="programming_no" readonly="1"
attrs='{"invisible": [("programming_no","=",False)]}'/>
<field name="programming_state" readonly="1"
attrs='{"invisible": [("programming_no","=",False)]}'/>
</group>
<!-- <group>-->
<!-- <div class="col-12 col-lg-6 o_setting_box" style="white-space: nowrap">-->
<!-- <button type="object" class="oe_highlight" name="fetchCNC" string="获取CNC程序代码"-->
<!-- />-->
<!-- </div>-->
<!-- </group>-->
</page>
</xpath>
<!-- <page string="Components" name="components">-->
<xpath expr="//page[1]" position="before">
<!-- <page string="装夹托盘" attrs='{"invisible": [("routing_type","!=","装夹")]}'>-->
@@ -483,7 +472,11 @@
<field name="production_line_id" readonly="1"/>
<field name="task_delivery_time" readonly="1"/>
<field name="task_completion_time" readonly="1"/>
<field name="status" readonly="1"/>
<field name="status" readonly="1" widget="badge"
decoration-success="status == '已配送'"
decoration-warning="status == '待下发'"
decoration-danger="status == '待配送'"
decoration-info="status== '已取消'"/>
</tree>
</field>
</page>
@@ -495,16 +488,18 @@
<field name="results" invisible="1"/>
<page string="后置三元检测" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
<group>
<field name="test_results" attrs='{"invisible":[("results","!=",False)]}'/>
<field name="test_results"
attrs='{"readonly":[("state","!=","to be detected")],"invisible":[("results","!=",False)]}'/>
<!-- <field name="is_remanufacture" attrs='{"invisible":[("test_results","!=","报废")]}'/>-->
<!-- <field name="is_fetchcnc"-->
<!-- attrs='{"invisible":["|",("test_results","=","合格"),("is_remanufacture","=",False)]}'/>-->
<!-- <field name="reason"-->
<!-- attrs='{"required":[("test_results","!=","合格")],"invisible":[("test_results","=","合格")]}'/>-->
<!-- <field name="detailed_reason" attrs='{"invisible":[("test_results","=","合格")]}'/>-->
<!-- <field name="results" readonly="1" attrs='{"invisible":[("results","!=","合格")]}'/>-->
<field name="reason"
attrs='{"required":[("test_results","!=","合格")],"invisible":[("test_results","=","合格")]}'/>
<field name="detailed_reason"
attrs='{"required":[("test_results","!=","合格")],"invisible":[("test_results","=","合格")]}'/>
<!-- <field name="results" readonly="1" attrs='{"invisible":[("results","!=","合格")]}'/>-->
<field name="detection_report" attrs='{"invisible":[("results","!=",False)]}'
widget="pdf_viewer"/>
widget="pdf_viewer" readonly="1"/>
</group>
<!-- <div class="col-12 col-lg-6 o_setting_box">-->
<!-- <button type="object" class="oe_highlight" name="recreateManufacturingOrWorkerOrder"-->
@@ -521,6 +516,7 @@
<field name="sequence_number"/>
<field name="program_name"/>
<field name="cutting_tool_name"/>
<field name="tool_state"/>
<field name="cutting_tool_no"/>
<field name="processing_type"/>
<field name="margin_x_y"/>
@@ -575,9 +571,6 @@
</div>
</xpath>
<xpath expr="//form//sheet//group//group//div[3]" position="after">
<field name="is_ok" attrs='{"invisible": [("routing_type","=","装夹预调")]}'/>
<field name="processing_user_id" attrs='{"invisible": [("routing_type","=","装夹预调")]}'/>
<field name="inspection_user_id" attrs='{"invisible": [("routing_type","=","装夹预调")]}'/>
<field name="save_name" widget="CopyClipboardChar"
attrs="{'invisible':[('routing_type','!=','装夹预调')]}"/>
<label for="material_length" string="物料尺寸"/>
@@ -592,9 +585,6 @@
<field name="material_height" class="o_address_zip"/>
</div>
<field name="part_number" string="成品的零件图号"/>
<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>
</field>
</record>
@@ -607,6 +597,9 @@
<field name="product_id" position="after">
<field name="part_number" string="成品零件图号"/>
</field>
<xpath expr="//filter[@name='progress']" position="after">
<filter string="待检测" name="state" domain="[('state','=','to be detected')]"/>
</xpath>
</field>
</record>
@@ -643,7 +636,9 @@
<field name="status" widget="badge"
decoration-success="status == '已配送'"
decoration-warning="status == '待下发'"
decoration-danger="status == '待配送'"/>
decoration-danger="status == '待配送'"
decoration-info="status == '已取消'"
/>
<field name="name"/>
<field name="production_id"/>
<field name="type" readonly="1"/>
@@ -729,7 +724,7 @@
</search>
</field>
</record>
<record id="sf_workpiece_delivery_act" model="ir.actions.act_window">
<field name="name">工件配送</field>
<field name="res_model">sf.workpiece.delivery</field>
@@ -739,7 +734,7 @@
</field>
<field name="view_mode">tree,form</field>
<field name="domain">
[('type','in',['上产线','下产线']),('workorder_state','=','done'),('is_manual_work','=',false)]
[('type','in',['上产线','下产线']),('workorder_state','in',['done','rework']),('is_manual_work','=',false)]
</field>
</record>

View File

@@ -100,7 +100,7 @@
<field name="model">sf.maintenance.logs</field>
<field name="inherit_id" ref="sf_maintenance.view_maintenance_logs_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='code']" position="after">
<xpath expr="//field[@name='type']" position="after">
<field name="production_line_id" optional="hide"/>
</xpath>
</field>

View File

@@ -1 +1,3 @@
from . import workpiece_delivery_wizard
from . import rework_wizard
from . import production_wizard

View File

@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Part of YiZuo. See LICENSE file for full copyright and licensing details.
import logging
from odoo.exceptions import UserError, ValidationError
from datetime import datetime
from odoo import models, api, fields, _
class ProductionWizard(models.TransientModel):
_name = 'sf.production.wizard'
_description = '制造订单向导'
production_id = fields.Many2one('mrp.production', string='制造订单号')
is_reprogramming = fields.Boolean(string='申请重新编程', default=True)
is_remanufacture = fields.Boolean(string='重新生成制造订单', default=True)
def confirm(self):
if self.is_reprogramming is True:
self.production_id.update_programming_state()
self.production_id.action_cancel()

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.ui.view" id="sf_production_wizard_form_view">
<field name="name">sf.production.wizard.form.view</field>
<field name="model">sf.production.wizard</field>
<field name="arch" type="xml">
<form>
<sheet>
<field name="production_id" invisible="True"/>
<div>
重新生成制造订单
<field name="is_remanufacture"/>
</div>
<div>
申请重新编程
<field name="is_reprogramming" attrs='{"invisible": [("is_remanufacture","=",False)]}'/>
</div>
<footer>
<button string="确认" name="confirm" type="object" class="oe_highlight" confirm="是否确认报废"/>
<button string="取消" class="btn btn-secondary" special="cancel"/>
</footer>
</sheet>
</form>
</field>
</record>
<record id="action_sf_production_wizard" model="ir.actions.act_window">
<field name="name">报废</field>
<field name="res_model">sf.production.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>

View File

@@ -0,0 +1,186 @@
# -*- coding: utf-8 -*-
# Part of YiZuo. See LICENSE file for full copyright and licensing details.
import logging
from odoo.exceptions import UserError, ValidationError
from datetime import datetime
from odoo import models, api, fields, _
class ReworkWizard(models.TransientModel):
_name = 'sf.rework.wizard'
_description = '返工向导'
workorder_id = fields.Many2one('mrp.workorder', string='工单')
product_id = fields.Many2one('product.product')
production_id = fields.Many2one('mrp.production', string='制造订单号')
rework_reason = fields.Selection(
[("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"),
("operate computer", "操机"),
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因")
detailed_reason = fields.Text('详细原因')
routing_type = fields.Selection([
('装夹预调', '装夹预调'),
('CNC加工', 'CNC加工')], string="工序类型")
# 根据工单的加工面来显示
processing_panel_id = fields.Many2many('sf.processing.panel', string="加工面")
is_reprogramming = fields.Boolean(string='申请重新编程', default=False)
is_reprogramming_readonly = fields.Boolean(string='申请重新编程(只读)', default=False)
reprogramming_num = fields.Integer('重新编程次数', default=0)
programming_state = fields.Selection(
[('待编程', '待编程'), ('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'),
('已下发', '已下发')],
string='编程状态')
def confirm(self):
if self.routing_type in ['装夹预调', 'CNC加工']:
self.workorder_id.is_rework = True
self.production_id.write({'detection_result_ids': [(0, 0, {
'rework_reason': self.rework_reason,
'detailed_reason': self.detailed_reason,
'processing_panel': self.workorder_id.processing_panel,
'routing_type': self.workorder_id.routing_type,
'handle_result': '待处理' if self.workorder_id.test_results == '返工' or self.workorder_id.is_rework is True else '',
'test_results': '返工' if not self.routing_type == 'CNC加工' else self.workorder_id.test_results,
'test_report': self.workorder_id.detection_report})]})
self.workorder_id.button_finish()
else:
if self.production_id.workorder_ids:
handle_result = self.production_id.detection_result_ids.filtered(
lambda dr: dr.handle_result == '待处理')
if handle_result:
processing_panels_to_handle = set(handle_item.processing_panel for handle_item in handle_result)
processing_panels_choice = set(dr_panel.name for dr_panel in self.processing_panel_id)
# 使用集合的差集操作找出那些待处理结果中有但实际可用加工面中没有的加工面
processing_panels_missing = processing_panels_to_handle - processing_panels_choice
# 存在不一致的加工面
if processing_panels_missing:
processing_panels_str = ','.join(processing_panels_missing)
raise UserError('您还有待处理的检测结果中为%s的加工面未选择' % processing_panels_str)
# processing_panels = set()
# for handle_item in handle_result:
# for dr_panel in self.processing_panel_id:
# if dr_panel.name == handle_item.processing_panel:
# processing_panels.add(dr_panel.name)
# if len(processing_panels) != len(handle_result):
# processing_panels_str = ','.join(processing_panels)
# return UserError(f'您还有待处理的检测结果中为{processing_panels_str}的加工面未选择')
for panel in self.processing_panel_id:
panel_workorder = self.production_id.workorder_ids.filtered(
lambda ap: ap.processing_panel == panel.name and ap.state != 'rework')
if panel_workorder:
panel_workorder.write({'state': 'rework'})
panel_workorder.filtered(
lambda wo: wo.routing_type == '装夹预调').workpiece_delivery_ids.filtered(
lambda wd: wd.status == '待下发').write({'status': '已取消'})
# workpiece = self.env['sf.workpiece.delivery'].search([('status', '=', '待下发'), (
# 'workorder_id', '=',
# panel_workorder.filtered(lambda wd: wd.routing_type == '装夹预调').id)])
product_routing_workcenter = self.env['sf.product.model.type.routing.sort'].search(
[('product_model_type_id', '=', self.production_id.product_id.product_model_type_id.id)],
order='sequence asc'
)
workorders_values = []
for route in product_routing_workcenter:
if route.is_repeat is True:
workorders_values.append(
self.env['mrp.workorder'].json_workorder_str(panel.name,
self.production_id, route, False))
if workorders_values:
self.production_id.write({'workorder_ids': workorders_values, 'is_rework': True})
self.production_id._reset_work_order_sequence()
self.production_id.detection_result_ids.filtered(
lambda ap1: ap1.processing_panel == panel.name and ap1.handle_result == '待处理').write(
{'handle_result': '已处理'})
if self.is_reprogramming is False:
if self.programming_state in ['已编程', '已下发']:
if self.reprogramming_num >= 1 and self.programming_state == '已编程':
self.production_id.get_new_program(panel.name)
if self.reprogramming_num >= 0 and self.programming_state == '已下发':
ret = {'programming_list': []}
cnc_rework = self.production_id.workorder_ids.filtered(
lambda crw: crw.processing_panel == panel.name and crw.state in (
'rework') and crw.routing_type == 'CNC加工')
if cnc_rework.cnc_ids:
for item_line in cnc_rework.cnc_ids:
vals = {
'sequence_number': item_line.sequence_number,
'program_name': item_line.program_name,
'cutting_tool_name': item_line.cutting_tool_name,
'cutting_tool_no': item_line.cutting_tool_no,
'processing_type': item_line.processing_type,
'margin_x_y': item_line.margin_x_y,
'margin_z': item_line.margin_z,
'depth_of_processing_z': item_line.depth_of_processing_z,
'cutting_tool_extension_length': item_line.cutting_tool_extension_length,
'estimated_processing_time': item_line.estimated_processing_time,
'cutting_tool_handle_type': item_line.cutting_tool_handle_type,
'program_path': item_line.program_path,
'ftp_path': item_line.program_path,
'processing_panel': panel.name,
'program_create_date': datetime.strftime(item_line.program_create_date,
'%Y-%m-%d %H:%M:%S'),
'remark': item_line.remark
}
ret['programming_list'].append(vals)
for cmm_line in cnc_rework.cmm_ids:
vals = {
'sequence_number': cmm_line.sequence_number,
'program_name': cmm_line.program_name,
'program_path': cmm_line.program_path,
'ftp_path': item_line.program_path,
'processing_panel': panel.name,
'program_create_date': datetime.strftime(
cmm_line.program_create_date,
'%Y-%m-%d %H:%M:%S')
}
ret['programming_list'].append(vals)
new_cnc_workorder = self.production_id.workorder_ids.filtered(
lambda ap1: ap1.processing_panel == panel.name and ap1.state not in (
'rework', 'done') and ap1.routing_type == 'CNC加工')
if not new_cnc_workorder.cnc_ids:
new_cnc_workorder.write({
'cnc_ids': new_cnc_workorder.cnc_ids.sudo()._json_cnc_processing(panel.name,
ret),
'cmm_ids': new_cnc_workorder.cmm_ids.sudo()._json_cmm_program(panel.name,
ret),
'cnc_worksheet': cnc_rework.cnc_worksheet})
new_pre_workorder = self.production_id.workorder_ids.filtered(lambda
p: p.routing_type == '装夹预调' and p.processing_panel == panel.name and p.state not in (
'rework', 'done'))
if new_pre_workorder:
pre_rework = self.production_id.workorder_ids.filtered(
lambda pr: pr.processing_panel == panel.name and pr.state in (
'rework') and pr.routing_type == '装夹预调')
new_pre_workorder.write(
{'processing_drawing': pre_rework.processing_drawing})
self.production_id.write({'state': 'progress', 'is_rework': False})
elif self.programming_state in ['待编程', '编程中']:
self.production_id.write(
{'programming_state': '编程中', 'work_state': '编程中', 'is_rework': True})
if self.is_reprogramming is True:
self.production_id.update_programming_state()
self.production_id.write(
{'programming_state': '编程中', 'work_state': '编程中'})
if self.production_id.state == 'progress':
self.production_id.write({'programming_state': '已编程', 'work_state': '已编程'})
@api.onchange('production_id')
def onchange_processing_panel_id(self):
for item in self:
domain = [('id', '=', False)]
production_id = item.production_id
if production_id:
if self.env.user.has_group('sf_base.group_sf_order_user'):
panel_ids = []
panel_arr = production_id.product_id.model_processing_panel
for p in production_id.detection_result_ids.filtered(
lambda ap1: ap1.handle_result == '待处理'):
if p.processing_panel not in panel_arr:
panel_arr += ','.join(p.processing_panel)
for item in panel_arr.split(','):
panel = self.env['sf.processing.panel'].search(
[('name', 'ilike', item)])
if panel:
panel_ids.append(panel.id)
domain = {'processing_panel_id': [('id', 'in', panel_ids)]}
return {'domain': domain}

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.ui.view" id="sf_rework_wizard_form_view">
<field name="name">sf.rework.wizard.form.view</field>
<field name="model">sf.rework.wizard</field>
<field name="arch" type="xml">
<form>
<sheet>
<field name="production_id" invisible="True"/>
<field name="workorder_id" invisible="True"/>
<field name="product_id" invisible="True"/>
<field name="routing_type" invisible="True"/>
<group>
<field name="processing_panel_id" options="{'no_create': True}"
attrs='{"invisible": [("routing_type","=","装夹预调")]}' widget="many2many_tags"/>
</group>
<div attrs='{"invisible": [("reprogramming_num","=",0)]}'>
注意: 该制造订单的产品已重复编程过<field
name="reprogramming_num" string=""
readonly="1"
style='color:red;'/>次,且当前编程状态为
<field name="programming_state" string=""
decoration-info="programming_state == '待编程'"
decoration-success="programming_state == '已下发'"
decoration-warning="programming_state =='编程中'"
decoration-danger="programming_state =='已编程'" readonly="1"/>
</div>
<div attrs='{"invisible": ["|",("routing_type","in",["装夹预调","CNC加工"]),("programming_state","not in",["已下发"])]}'>
<span style='font-weight:bold;'>申请重新编程
<field name="is_reprogramming" force_save="1"/>
</span>
</div>
<div attrs='{"invisible": ["|",("routing_type","in",["装夹预调","CNC加工"]),("programming_state","in",["已下发"])],"readonly": [("is_reprogramming_readonly","=",False)]}'>
<span style='font-weight:bold;'>申请重新编程
<field name="is_reprogramming_readonly"
attrs='{"readonly": [("is_reprogramming_readonly","=",False)]}'/>
</span>
</div>
<group>
<!-- <field name="is_reprogramming" force_save="1"-->
<!-- attrs='{"invisible": ["|",("routing_type","in",["装夹预调","CNC加工"]),("programming_state","not in",["已下发"])]}'/>-->
<!-- <field name="is_reprogramming_readonly" string="申请重新编程"-->
<!-- attrs='{"invisible": ["|",("routing_type","in",["装夹预调","CNC加工"]),("programming_state","in",["已下发"])],"readonly": [("is_reprogramming_readonly","=",False)]}'/>-->
<field name="rework_reason"
attrs='{"invisible": [("routing_type","not in",["装夹预调","CNC加工"])],"required": [("routing_type","in",["装夹预调"])]}'/>
<field name="detailed_reason"
attrs='{"invisible": [("routing_type","not in",["装夹预调","CNC加工"])],"required": [("routing_type","in",["装夹预调"])]}'/>
</group>
<footer>
<button string="确认" name="confirm" type="object" class="oe_highlight" confirm="是否确认返工"/>
<button string="取消" class="btn btn-secondary" special="cancel"/>
</footer>
</sheet>
</form>
</field>
</record>
<record id="action_sf_rework_wizard" model="ir.actions.act_window">
<field name="name">返工</field>
<field name="res_model">sf.rework.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>

View File

@@ -28,67 +28,92 @@ class Sf_Mrs_Connect(http.Controller):
request.env.ref("base.user_admin")).search(
[('programming_no', '=', ret['programming_no'])])
if productions:
# 拉取所有加工面的程序文件
# # 拉取所有加工面的程序文件
for r in ret['processing_panel'].split(','):
program_path_tmp_r = os.path.join('/tmp', ret['folder_name'], 'return', r)
files_r = os.listdir(program_path_tmp_r)
if files_r:
for file_name in files_r:
file_path = os.path.join(program_path_tmp_r, file_name)
os.remove(file_path)
download_state = request.env['sf.cnc.processing'].with_user(
request.env.ref("base.user_admin")).download_file_tmp(
ret['folder_name'], r)
if download_state == 0:
res['status'] = -2
res['message'] = '制造订单号为%s的CNC程序文件从FTP拉取失败' % (cnc_production.name)
return json.JSONEncoder().encode(res)
if os.path.exists(program_path_tmp_r):
files_r = os.listdir(program_path_tmp_r)
if files_r:
for file_name in files_r:
file_path = os.path.join(program_path_tmp_r, file_name)
os.remove(file_path)
download_state = request.env['sf.cnc.processing'].with_user(
request.env.ref("base.user_admin")).download_file_tmp(
ret['folder_name'], r)
if download_state is False:
res['status'] = -2
res['message'] = '编程单号为%s的CNC程序文件从FTP拉取失败' % (ret['programming_no'])
return json.JSONEncoder().encode(res)
for production in productions:
if not production.workorder_ids:
production.product_id.model_processing_panel = ret['processing_panel']
production._create_workorder(ret)
else:
for panel in ret['processing_panel'].split(','):
# 查询状态为进行中且工序类型为CNC加工的工单
cnc_workorder = production.workorder_ids.filtered(
lambda ac: ac.routing_type == 'CNC加工' and ac.state not in ['progress', 'done',
'cancel'] and ac.processing_panel == panel)
if cnc_workorder:
if cnc_workorder.cnc_ids:
cnc_workorder.cmm_ids.sudo().unlink()
cnc_workorder.cnc_ids.sudo().unlink()
request.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',
# panel)
program_path_tmp_panel = os.path.join('/tmp', ret['folder_name'], 'return', panel)
logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel)
files_panel = os.listdir(program_path_tmp_panel)
if files_panel:
for file in files_panel:
file_extension = os.path.splitext(file)[1]
logging.info('file_extension:%s' % file_extension)
if file_extension.lower() == '.pdf':
panel_file_path = os.path.join(program_path_tmp_panel, file)
logging.info('panel_file_path:%s' % panel_file_path)
cnc_workorder.write(
{'is_send_program_again': False,
'cnc_ids': cnc_workorder.cnc_ids.sudo()._json_cnc_processing(panel, ret),
'cmm_ids': cnc_workorder.cmm_ids.sudo()._json_cmm_program(panel, ret),
'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
pre_workorder = production.workorder_ids.filtered(
lambda ap: ap.routing_type == '装夹预调' and ap.state not in ['done',
'cancel'] and ap.processing_panel == panel)
if pre_workorder:
pre_workorder.write(
{'is_send_program_again': False,
'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
# else:
# for panel in ret['processing_panel'].split(','):
# # 查询状态为进行中且工序类型为CNC加工的工单
# cnc_workorder = production.workorder_ids.filtered(
# lambda ac: ac.routing_type == 'CNC加工' and ac.state not in ['progress', 'done',
# 'cancel'] and ac.processing_panel == panel)
# if cnc_workorder:
# if cnc_workorder.cnc_ids:
# cnc_workorder.cmm_ids.sudo().unlink()
# cnc_workorder.cnc_ids.sudo().unlink()
# request.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',
# # panel)
# program_path_tmp_panel = os.path.join('/tmp', ret['folder_name'], 'return', panel)
# logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel)
# files_panel = os.listdir(program_path_tmp_panel)
# if files_panel:
# for file in files_panel:
# file_extension = os.path.splitext(file)[1]
# logging.info('file_extension:%s' % file_extension)
# if file_extension.lower() == '.pdf':
# panel_file_path = os.path.join(program_path_tmp_panel, file)
# logging.info('panel_file_path:%s' % panel_file_path)
# cnc_workorder.write(
# {'cnc_ids': cnc_workorder.cnc_ids.sudo()._json_cnc_processing(panel, ret),
# 'cmm_ids': cnc_workorder.cmm_ids.sudo()._json_cmm_program(panel, ret),
# 'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
# pre_workorder = production.workorder_ids.filtered(
# lambda ap: ap.routing_type == '装夹预调' and ap.state not in ['done',
# 'cancel'] and ap.processing_panel == panel)
# if pre_workorder:
# pre_workorder.write(
# {'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
for panel in ret['processing_panel'].split(','):
# 查询状态为进行中且工序类型为CNC加工的工单
cnc_workorder = productions.workorder_ids.filtered(
lambda ac: ac.routing_type == 'CNC加工' and ac.state not in ['progress', 'done',
'cancel'] and ac.processing_panel == panel)
if cnc_workorder:
# program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test',
# panel)
program_path_tmp_panel = os.path.join('/tmp', ret['folder_name'], 'return', panel)
logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel)
files_panel = os.listdir(program_path_tmp_panel)
if files_panel:
for file in files_panel:
file_extension = os.path.splitext(file)[1]
logging.info('file_extension:%s' % file_extension)
if file_extension.lower() == '.pdf':
panel_file_path = os.path.join(program_path_tmp_panel, file)
logging.info('panel_file_path:%s' % panel_file_path)
cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
pre_workorder = productions.workorder_ids.filtered(
lambda ap: ap.routing_type == '装夹预调' and ap.state not in ['done',
'cancel'] and ap.processing_panel == panel)
if pre_workorder:
pre_workorder.write(
{'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
productions.write({'programming_state': '已编程', 'work_state': '已编程'})
cnc_program_ids = [item.id for item in productions]
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
[('production_id', 'in', cnc_program_ids)])
if workpiece_delivery:
workpiece_delivery.write({'is_cnc_program_down': True})
workpiece_delivery.write(
{'is_cnc_program_down': True, 'production_line_id': productions.production_line_id.id})
return json.JSONEncoder().encode(res)
else:
res = {'status': 0, 'message': '该制造订单暂未开始'}

View File

@@ -21,6 +21,23 @@ class FtpController():
except Exception:
logging.info("ftp连接失败")
def file_exists_1(self, path):
# 检查文件是否存在于FTP服务器上
try:
logging.info("path:%s" % path)
logging.info("dirname:%s" % os.path.dirname(path))
directories = os.path.normpath(path).split(os.path.sep)
# 切换到上级目录
for directory in directories:
if directory:
# 检查目录是否存在
if (directory in ['NC']) or (directory not in ['home', 'ftp', 'ftp_root', 'NC']):
self.ftp.cwd(directory)
return os.path.basename(path)
except Exception as e:
logging.error(f"Error checking file: {e}")
return False
def file_exists(self, path):
# 检查文件是否存在于FTP服务器上
try:
@@ -32,8 +49,25 @@ class FtpController():
logging.error(f"Error checking file: {e}")
return False
# 下载目录下的pdf文件(程序单)
def download_program_file(self, target_dir, serverdir):
if not os.path.exists(serverdir):
os.makedirs(serverdir)
try:
logging.info('FTP目录:%s' % target_dir)
logging.info("进入FTP目录 ")
remotenames = self.ftp.nlst()
logging.info('FTP目录文件:%s' % remotenames)
for file in remotenames:
server = os.path.join(serverdir, file)
if file.find(".pdf") != -1:
self.download_file(server, file)
return True
except:
return False
finally:
self.ftp.quit()
logging.info("ftp已关闭")
# # 检测字符串的编码
# def detect_encoding(self, s):

View File

@@ -326,6 +326,8 @@ class sfProductionProcess(models.Model):
production_process.name = item['name']
production_process.category_id = category.id
production_process.remark = item['remark']
production_process.processing_day = item['processing_day']
production_process.travel_day = item['travel_day']
production_process.active = item['active']
else:
self.create({
@@ -333,6 +335,8 @@ class sfProductionProcess(models.Model):
"category_id": category.id,
"code": item['code'],
"remark": item['remark'],
"processing_day": item['processing_day'],
"travel_day": item['travel_day'],
"active": item['active'],
})
else:
@@ -358,12 +362,16 @@ class sfProductionProcess(models.Model):
"category_id": category.id,
"code": item['code'],
"remark": item['remark'],
"processing_day": item['processing_day'],
"travel_day": item['travel_day'],
"active": item['active'],
})
else:
production_process.name = item['name']
production_process.category_id = category.id
production_process.remark = item['remark']
production_process.processing_day = item['processing_day']
production_process.travel_day = item['travel_day']
production_process.active = item['active']
else:
raise ValidationError("表面工艺认证未通过")
@@ -1073,6 +1081,9 @@ class sfProductionProcessParameter(models.Model):
[('code', '=', item['process_id_code'])])
if production_process_parameter:
production_process_parameter.name = item['name']
production_process_parameter.process_description = item['process_description']
production_process_parameter.processing_day = item['processing_day']
production_process_parameter.travel_day = item['travel_day']
production_process_parameter.active = item['active']
production_process_parameter.process_id = process.id
production_process_parameter.materials_model_ids = self.env['sf.materials.model'].search(
@@ -1080,6 +1091,9 @@ class sfProductionProcessParameter(models.Model):
else:
self.create({
"name": item['name'],
"process_description": item['process_description'],
"processing_day": item['processing_day'],
"travel_day": item['travel_day'],
"code": item['code'],
"active": item['active'],
"process_id": process.id,
@@ -1107,6 +1121,9 @@ class sfProductionProcessParameter(models.Model):
if not production_process_parameter:
self.create({
"name": item['name'],
"process_description": item['process_description'],
"processing_day": item['processing_day'],
"travel_day": item['travel_day'],
"code": item['code'],
"active": item['active'],
"process_id": process.id,
@@ -1115,6 +1132,9 @@ class sfProductionProcessParameter(models.Model):
})
else:
production_process_parameter.name = item['name']
production_process_parameter.process_description = item['process_description']
production_process_parameter.processing_day = item['processing_day']
production_process_parameter.travel_day = item['travel_day']
production_process_parameter.process_id = process.id
production_process_parameter.materials_model_ids = self.env['sf.materials.model'].search(
[('materials_no', 'in', item['materials_model_ids_codes'])])

View File

@@ -37,7 +37,7 @@ class sf_production_plan(models.Model):
_order = 'state_order asc, write_date desc'
name = fields.Char(string='制造订单')
active = fields.Boolean(string='已归档', default=True)
# active = fields.Boolean(string='已归档', default=True)
# selected = fields.Boolean(default=False)
# order_number = fields.Char(string='订单号')
order_deadline = fields.Datetime(string='订单交期')
@@ -101,17 +101,17 @@ class sf_production_plan(models.Model):
# return super(sf_production_plan, self.with_context(active_test=False))._search(
# args, offset, limit, order, count, access_rights_uid)
def archive(self):
"""
归档
"""
self.write({'active': False})
def unarchive(self):
"""
取消归档
"""
self.write({'active': True})
# def archive(self):
# """
# 归档
# """
# self.write({'active': False})
#
# def unarchive(self):
# """
# 取消归档
# """
# self.write({'active': True})
@api.model
def get_import_templates(self):

View File

@@ -63,7 +63,7 @@
</div>
<group>
<group string="基本信息">
<field name="active" invisible="1"/>
<!-- <field name="active" invisible="1"/> -->
<field name="production_id" widget="many2one_button"/>
<field name="product_id"/>
<field name="origin"/>
@@ -217,11 +217,11 @@
</li>
<li>
<strong>订单交期:</strong>
<t t-out="order_deadline"/>
<t t-out="order_deadline.format('L LTS')"/>
</li>
<li>
<strong>产品名称:</strong>
<t t-out="product_id"/>
<t t-out="product_id[1]"/>
</li>
<li>
<strong>数量:</strong>
@@ -268,7 +268,8 @@
<field name="type">ir.actions.act_window</field>
<field name="res_model">sf.production.plan</field>
<field name="view_mode">tree,gantt,form</field>
<field name="context">{'search_default_group_by_state': 1, 'search_default_draft': 1, 'display_complete': True}</field>
<!-- <field name="context">{'search_default_group_by_state': 1, 'search_default_draft': 1, 'display_complete': True}</field> -->
<field name="context">{'search_default_draft': 1, 'display_complete': True}</field>
</record>
<menuitem

View File

@@ -114488,10 +114488,21 @@ msgid "径跳精度(mm)"
msgstr ""
#. module: sf_manufacturing
#: model:ir.model.fields.selection,name:sf_manufacturing.selection__mrp_production__state__progress
#: model:ir.model.fields.selection,name:sf_manufacturing.selection__mrp_production__state__draft
msgid "待排程"
msgstr "待排程"
#. module: sf_manufacturing
#: model:ir.model.fields.selection,name:sf_manufacturing.selection__mrp_production__process_state__待加工
#: model:ir.model.fields.selection,name:sf_manufacturing.selection__mrp_production__state__confirmed
msgid "待加工"
msgstr "待加工"
#. module: sf_manufacturing
#: model:ir.model.fields.selection,name:sf_manufacturing.selection__mrp_production__state__progress
msgid "待排程"
msgstr "加工中"
#. module: sf_base
#: model:ir.model.fields,field_description:sf_base.field_sf_cutting_tool_model__jump_accuracy
msgid "径跳精度(um)"

View File

@@ -44,7 +44,6 @@ class ReSaleOrder(models.Model):
store=True, readonly=False, precompute=True, check_company=True, tracking=True,
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
remark = fields.Text('备注')
validity_date = fields.Date(
string="Expiration",
compute='_compute_validity_date',
@@ -113,6 +112,7 @@ class ReSaleOrder(models.Model):
'price_unit': product.list_price,
'product_uom_qty': item['number'],
'model_glb_file': base64.b64decode(item['model_file']),
'remark': item.get('remark')
}
return self.env['sale.order.line'].with_context(skip_procurement=True).create(vals)
@@ -151,6 +151,7 @@ class ResaleOrderLine(models.Model):
# # without modifying the related product_id when updated.
# domain=[('sale_ok', '=', True), ('categ_type', '=', '成品')])
check_status = fields.Selection(related='order_id.check_status')
remark = fields.Char('备注')
@api.depends('product_template_id')
def _compute_model_glb_file(self):
@@ -171,6 +172,11 @@ class ProductTemplate(models.Model):
class RePurchaseOrder(models.Model):
_inherit = 'purchase.order'
mrp_production_count = fields.Integer(
"Count of MO Source",
compute='_compute_mrp_production_count',
groups='mrp.group_mrp_user,sf_base.group_purchase,sf_base.group_purchase_director')
remark = fields.Text('备注')
user_id = fields.Many2one(
'res.users', string='买家', index=True, tracking=True,
@@ -251,33 +257,33 @@ class ResPartnerToSale(models.Model):
# if obj:
# raise UserError('该邮箱已存在,请重新输入')
@api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
if self._context.get('is_customer'):
if self.env.user.has_group('sf_base.group_sale_director'):
domain = [('customer_rank', '>', 0)]
elif self.env.user.has_group('sf_base.group_sale_salemanager'):
customer = self.env['res.partner'].search(
[('customer_rank', '>', 0), ('user_id', '=', self.env.user.id)])
if customer:
ids = [t.id for t in customer]
domain = [('id', 'in', ids)]
else:
domain = [('id', '=', False)]
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
elif self._context.get('is_supplier') or self.env.user.has_group('sf_base.group_purchase_director'):
if self.env.user.has_group('sf_base.group_purchase_director'):
domain = [('supplier_rank', '>', 0)]
elif self.env.user.has_group('sf_base.group_purchase'):
supplier = self.env['res.partner'].search(
[('supplier_rank', '>', 0), ('purchase_user_id', '=', self.env.user.id)])
if supplier:
ids = [t.id for t in supplier]
domain = [('id', 'in', ids)]
else:
domain = [('id', '=', False)]
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
return super()._name_search(name, args, operator, limit, name_get_uid)
# @api.model
# def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
# if self._context.get('is_customer'):
# if self.env.user.has_group('sf_base.group_sale_director'):
# domain = [('customer_rank', '>', 0)]
# elif self.env.user.has_group('sf_base.group_sale_salemanager'):
# customer = self.env['res.partner'].search(
# [('customer_rank', '>', 0), ('user_id', '=', self.env.user.id)])
# if customer:
# ids = [t.id for t in customer]
# domain = [('id', 'in', ids)]
# else:
# domain = [('id', '=', False)]
# return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
# elif self._context.get('is_supplier') or self.env.user.has_group('sf_base.group_purchase_director'):
# if self.env.user.has_group('sf_base.group_purchase_director'):
# domain = [('supplier_rank', '>', 0)]
# elif self.env.user.has_group('sf_base.group_purchase'):
# supplier = self.env['res.partner'].search(
# [('supplier_rank', '>', 0), ('purchase_user_id', '=', self.env.user.id)])
# if supplier:
# ids = [t.id for t in supplier]
# domain = [('id', 'in', ids)]
# else:
# domain = [('id', '=', False)]
# return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
# return super()._name_search(name, args, operator, limit, name_get_uid)
@api.onchange('user_id')
def _get_salesman(self):

View File

@@ -54,7 +54,7 @@
<record model="ir.rule" id="sale_order_rule_my">
<field name="name">销售经理查看自己的订单</field>
<field name="model_id" ref="model_sale_order"/>
<field name="domain_force">['|',('user_id','=',user.id),('create_uid', '=',user.id)]</field>
<field name="domain_force">['|','|',('user_id','=',user.id),('user_id', '=', False),('create_uid', '=',user.id)]</field>
<field name="groups" eval="[(4, ref('sf_base.group_sale_salemanager'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
@@ -74,7 +74,7 @@
<record model="ir.rule" id="inventory_purchase_order_rule_my">
<field name="name">采购岗查看自己的订单</field>
<field name="model_id" ref="purchase.model_purchase_order"/>
<field name="domain_force">['|',('user_id','=',user.id),('create_uid', '=',user.id)]</field>
<field name="domain_force">['|','|',('user_id','=',user.id),('user_id', '=', False),('create_uid', '=',user.id)]</field>
<field name="groups" eval="[(4, ref('sf_base.group_purchase'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="0"/>

View File

@@ -79,6 +79,13 @@
<xpath expr="//form/header/button[@name='button_done']" position="attributes">
<attribute name="groups">sf_base.group_purchase,sf_base.group_purchase_director</attribute>
</xpath>
<xpath expr="//form/sheet/div[@name='button_box']/button[@name='action_view_mrp_productions']"
position="attributes">
<attribute name="groups">mrp.group_mrp_user,sf_base.group_purchase,sf_base.group_purchase_director
</attribute>
</xpath>
<xpath expr="//field[@name='order_line']" position="attributes">
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}
</attribute>

View File

@@ -69,6 +69,9 @@
<field name="model_glb_file" widget="Viewer3D" optional="show"
string="模型文件" attrs="{'readonly': [('state', 'in', ['draft'])]}"/>
</xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='price_subtotal']" position="after">
<field name="remark"/>
</xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='product_template_id']" position="attributes">
<attribute name="options">{'no_create': True}</attribute>
<attribute name="context">{'is_sale_order_line': True }</attribute>

View File

@@ -313,42 +313,27 @@ class CAMWorkOrderProgramKnifePlan(models.Model):
'applicant': None,
'sf_functional_tool_assembly_id': None})
def create_cam_work_plan(self, cnc_processing_ids):
def create_cam_work_plan(self, cnc_processing):
"""
根据传入的工单信息查询是否有需要的功能刀具如果没有则生成CAM工单程序用刀计划
"""
for cnc_processing in cnc_processing_ids:
status = False
if cnc_processing.cutting_tool_name:
functional_tools = self.env['sf.real.time.distribution.of.functional.tools'].sudo().search(
[('name', '=', cnc_processing.cutting_tool_name)])
if functional_tools:
for functional_tool in functional_tools:
if functional_tool.on_tool_stock_num == 0:
if functional_tool.tool_stock_num == 0 and functional_tool.side_shelf_num == 0:
status = True
else:
status = True
if status:
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()
else:
logging.info('功能刀具【%s】满足CNC用刀需求' % cnc_processing.cutting_tool_name)
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()
def unlink_cam_plan(self, production):
for item in production:
@@ -669,6 +654,20 @@ class FunctionalToolAssembly(models.Model):
:return:
"""
picking_num = fields.Integer('调拨单数量', compute='compute_picking_num', store=True)
@api.depends('assemble_status')
def compute_picking_num(self):
for item in self:
picking_ids = self.env['stock.picking'].sudo().search([('origin', '=', item.assembly_order_code)])
item.picking_num = len(picking_ids)
def open_tool_stock_picking(self):
action = self.env.ref('stock.action_picking_tree_all')
result = action.read()[0]
result['domain'] = [('origin', '=', self.assembly_order_code)]
return result
@api.model_create_multi
def create(self, vals):
obj = super(FunctionalToolAssembly, self).create(vals)
@@ -758,7 +757,7 @@ class FunctionalToolDismantle(models.Model):
num = "%03d" % m
return 'GNDJ-CJD-%s-%s' % (datetime, num)
functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', required=True,
functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', required=True, tracking=True,
domain=[('functional_tool_status', '!=', '已拆除'),
('current_location', '=', '刀具房')])
tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', store=True,
@@ -776,8 +775,18 @@ class FunctionalToolDismantle(models.Model):
dismantle_person = fields.Char('拆解人', readonly=True)
image = fields.Binary('图片', readonly=True)
scrap_id = fields.Char('报废单号', readonly=True)
scrap_ids = fields.One2many('stock.scrap', 'functional_tool_dismantle_id', string='报废单号', readonly=True)
grinding_id = fields.Char('磨削单号', readonly=True)
picking_id = fields.Many2one('stock.picking', string='刀具物料调拨单')
picking_num = fields.Integer('调拨单数量', default=0, compute='compute_picking_num', store=True)
@api.depends('picking_id')
def compute_picking_num(self):
for item in self:
if item.picking_id:
item.picking_num = 1
else:
item.picking_num = 0
state = fields.Selection([('待拆解', '待拆解'), ('已拆解', '已拆解')], default='待拆解', tracking=True)
active = fields.Boolean('有效', default=True)
@@ -791,7 +800,14 @@ class FunctionalToolDismantle(models.Model):
handle_rfid = fields.Char(string='刀柄Rfid', compute='_compute_functional_tool_num', store=True)
handle_lot_id = fields.Many2one('stock.lot', string='刀柄序列号', compute='_compute_functional_tool_num',
store=True)
scrap_boolean = fields.Boolean(string='刀柄是否报废', default=False, tracking=True)
scrap_boolean = fields.Boolean(string='刀柄是否报废', default=False, tracking=True, compute='compute_scrap_boolean',
store=True)
@api.depends('dismantle_cause')
def compute_scrap_boolean(self):
for item in self:
if item.dismantle_cause not in ['寿命到期报废', '崩刀报废']:
item.scrap_boolean = False
# 整体式
integral_product_id = fields.Many2one('product.product', string='整体式刀具',
@@ -803,7 +819,7 @@ class FunctionalToolDismantle(models.Model):
integral_lot_id = fields.Many2one('stock.lot', string='整体式刀具批次', compute='_compute_functional_tool_num',
store=True)
integral_freight_id = fields.Many2one('sf.shelf.location', '整体式刀具目标货位',
domain="[('product_id', 'in', (integral_product_id, False))]")
domain="[('product_id', 'in', (integral_product_id, False)),('rotative_Boolean', '=', True)]")
# 刀片
blade_product_id = fields.Many2one('product.product', string='刀片', compute='_compute_functional_tool_num',
@@ -813,7 +829,7 @@ class FunctionalToolDismantle(models.Model):
blade_brand_id = fields.Many2one('sf.machine.brand', string='刀片品牌', related='blade_product_id.brand_id')
blade_lot_id = fields.Many2one('stock.lot', string='刀片批次', compute='_compute_functional_tool_num', store=True)
blade_freight_id = fields.Many2one('sf.shelf.location', '刀片目标货位',
domain="[('product_id', 'in', (blade_product_id, False))]")
domain="[('product_id', 'in', (blade_product_id, False)),('rotative_Boolean', '=', True)]")
# 刀杆
bar_product_id = fields.Many2one('product.product', string='刀杆', compute='_compute_functional_tool_num',
@@ -823,7 +839,7 @@ class FunctionalToolDismantle(models.Model):
bar_brand_id = fields.Many2one('sf.machine.brand', string='刀杆品牌', related='bar_product_id.brand_id')
bar_lot_id = fields.Many2one('stock.lot', string='刀杆批次', compute='_compute_functional_tool_num', store=True)
bar_freight_id = fields.Many2one('sf.shelf.location', '刀杆目标货位',
domain="[('product_id', 'in', (bar_product_id, False))]")
domain="[('product_id', 'in', (bar_product_id, False)),('rotative_Boolean', '=', True)]")
# 刀盘
pad_product_id = fields.Many2one('product.product', string='刀盘', compute='_compute_functional_tool_num',
@@ -833,7 +849,7 @@ class FunctionalToolDismantle(models.Model):
pad_brand_id = fields.Many2one('sf.machine.brand', string='刀盘品牌', related='pad_product_id.brand_id')
pad_lot_id = fields.Many2one('stock.lot', string='刀盘批次', compute='_compute_functional_tool_num', store=True)
pad_freight_id = fields.Many2one('sf.shelf.location', '刀盘目标货位',
domain="[('product_id', 'in', (pad_product_id, False))]")
domain="[('product_id', 'in', (pad_product_id, False)),('rotative_Boolean', '=', True)]")
# 夹头
chuck_product_id = fields.Many2one('product.product', string='夹头', compute='_compute_functional_tool_num',
@@ -843,7 +859,7 @@ class FunctionalToolDismantle(models.Model):
chuck_brand_id = fields.Many2one('sf.machine.brand', string='夹头品牌', related='chuck_product_id.brand_id')
chuck_lot_id = fields.Many2one('stock.lot', string='夹头批次', compute='_compute_functional_tool_num', store=True)
chuck_freight_id = fields.Many2one('sf.shelf.location', '夹头目标货位',
domain="[('product_id', 'in', (chuck_product_id, False))]")
domain="[('product_id', 'in', (chuck_product_id, False)),('rotative_Boolean', '=', True)]")
@api.onchange('functional_tool_id')
def _onchange_freight(self):
@@ -933,77 +949,50 @@ class FunctionalToolDismantle(models.Model):
self.rfid, self.functional_tool_id.current_location))
# 目标重复校验
self.location_duplicate_check()
location = self.env['stock.location'].search([('name', '=', '刀具组装位置')])
location_dest = self.env['stock.location'].search([('name', '=', '刀具房')])
datas = {'scrap': [], 'picking': []}
# =================刀柄是否[报废]拆解=======
location_dest_scrap_ids = self.env['stock.location'].search([('name', 'in', ('Scrap', '报废'))])
if self.handle_rfid:
lot = self.env['stock.lot'].sudo().search([('rfid', '=', self.handle_rfid)])
if not lot:
raise ValidationError('Rfid为【%s】的功能刀具序列号不存在!' % self.handle_rfid)
functional_tool_assembly = self.functional_tool_id.functional_tool_name_id
raise ValidationError('Rfid为【%s】的刀柄序列号不存在!' % self.handle_rfid)
if self.scrap_boolean:
# 刀柄报废 入库到Scrap
lot.create_stock_quant(location, location_dest_scrap_ids[-1], functional_tool_assembly.id, code,
functional_tool_assembly, functional_tool_assembly.tool_groups_id)
datas['scrap'].append({'lot_id': lot})
lot.tool_material_status = '报废'
else:
# 刀柄不报废 入库到刀具房
lot.create_stock_quant(location, location_dest, functional_tool_assembly.id, code,
functional_tool_assembly, functional_tool_assembly.tool_groups_id)
datas['picking'].append({'lot_id': lot, 'destination': self.env['sf.shelf.location']})
lot.tool_material_status = '可用'
# ==============功能刀具[报废]拆解================
if self.dismantle_cause in ['寿命到期报废', '崩刀报废']:
# 除刀柄外物料报废 入库到Scrap
if self.integral_product_id:
self.integral_product_id.dismantle_stock_moves(False, self.integral_lot_id, location,
location_dest_scrap_ids[-1], code)
datas['scrap'].append({'lot_id': self.integral_lot_id})
elif self.blade_product_id:
self.blade_product_id.dismantle_stock_moves(False, self.blade_lot_id, location,
location_dest_scrap_ids[-1], code)
datas['scrap'].append({'lot_id': self.blade_lot_id})
if self.bar_product_id:
self.bar_product_id.dismantle_stock_moves(False, self.bar_lot_id, location,
location_dest_scrap_ids[-1], code)
datas['scrap'].append({'lot_id': self.bar_lot_id})
elif self.pad_product_id:
self.pad_product_id.dismantle_stock_moves(False, self.pad_lot_id, location,
location_dest_scrap_ids[-1], code)
datas['scrap'].append({'lot_id': self.pad_lot_id})
if self.chuck_product_id:
self.chuck_product_id.dismantle_stock_moves(False, self.chuck_lot_id, location,
location_dest_scrap_ids[-1], code)
# ===========功能刀具[磨削]拆解==============
# elif self.dismantle_cause in ['刀具需磨削']:
# location_dest = self.env['stock.location'].search([('name', '=', '磨削房')])
# # 除刀柄外物料拆解 入库到具体库位
# if self.integral_product_id:
# self.integral_product_id.dismantle_stock_moves(False, location, location_dest)
# elif self.blade_product_id:
# self.blade_product_id.dismantle_stock_moves(False, location, location_dest)
# if self.bar_product_id:
# self.bar_product_id.dismantle_stock_moves(False, location, location_dest)
# elif self.pad_product_id:
# self.pad_product_id.dismantle_stock_moves(False, location, location_dest)
# if self.chuck_product_id:
# self.chuck_product_id.dismantle_stock_moves(False, location, location_dest)
datas['scrap'].append({'lot_id': self.chuck_lot_id})
# ==============功能刀具[更换,磨削]拆解==============
elif self.dismantle_cause in ['更换为其他刀具', '刀具需磨削']:
# 除刀柄外物料拆解 入库到具体货位
if self.integral_freight_id:
self.integral_product_id.dismantle_stock_moves(self.integral_freight_id, self.integral_lot_id, location,
location_dest, code)
datas['picking'].append({'lot_id': self.integral_lot_id, 'destination': self.integral_freight_id})
elif self.blade_freight_id:
self.blade_product_id.dismantle_stock_moves(self.blade_freight_id, self.blade_lot_id, location,
location_dest, code)
datas['picking'].append({'lot_id': self.blade_lot_id, 'destination': self.blade_freight_id})
if self.bar_freight_id:
self.bar_product_id.dismantle_stock_moves(self.bar_freight_id, self.bar_lot_id, location,
location_dest, code)
datas['picking'].append({'lot_id': self.bar_lot_id, 'destination': self.bar_freight_id})
elif self.pad_freight_id:
self.pad_product_id.dismantle_stock_moves(self.pad_freight_id, self.pad_lot_id, location,
location_dest, code)
datas['picking'].append({'lot_id': self.pad_lot_id, 'destination': self.pad_freight_id})
if self.chuck_freight_id:
self.chuck_product_id.dismantle_stock_moves(self.chuck_freight_id, self.chuck_lot_id, location,
location_dest, code)
# ===============删除功能刀具的Rfid字段的值 赋值给Rfid(已拆解)字段=====
datas['picking'].append({'lot_id': self.chuck_lot_id, 'destination': self.chuck_freight_id})
self.create_tool_picking_scrap(datas)
# ===============创建功能刀具拆解移动记录=====
self.env['stock.move'].create_functional_tool_stock_move(self)
# 修改功能刀具数据
self.functional_tool_id.write({
'rfid_dismantle': self.functional_tool_id.rfid,
'rfid': '',
@@ -1011,37 +1000,173 @@ class FunctionalToolDismantle(models.Model):
})
# 修改拆解单的值
self.write({
'rfid_dismantle': self.rfid,
'dismantle_data': fields.Datetime.now(),
'dismantle_person': self.env.user.name,
'rfid': '',
'rfid': '%s(已拆解)' % self.rfid,
'state': '已拆解'
})
logging.info('%s】刀具拆解成功!' % self.name)
def create_tool_picking_scrap(self, datas):
scrap_data = datas['scrap']
picking_data = datas['picking']
if scrap_data:
for data in scrap_data:
if data:
self.env['stock.scrap'].create_tool_dismantle_stock_scrap(data['lot_id'], self)
if picking_data:
picking_id = self.env['stock.picking'].create_tool_dismantle_picking(self)
self.picking_id = picking_id.id
self.env['stock.move'].create_tool_stock_move({'data': picking_data, 'picking_id': picking_id})
# 将刀具物料出库库单的状态更改为就绪
picking_id.action_confirm()
# 修改刀具物料出库移动历史记录
self.env['stock.move'].write_tool_stock_move_line({'data': picking_data, 'picking_id': picking_id})
# 设置数量,并验证完成
picking_id.action_set_quantities_to_reservation()
picking_id.button_validate()
class ProductProduct(models.Model):
_inherit = 'product.product'
def action_open_reference1(self):
self.ensure_one()
return {
'res_model': self._name,
'type': 'ir.actions.act_window',
'views': [[False, "form"]],
'res_id': self.id,
}
def dismantle_stock_moves(self, shelf_location_id, lot_id, location_id, location_dest_id, code):
# 创建功能刀具拆解单产品库存移动记录
stock_move_id = self.env['stock.move'].sudo().create({
'name': code,
'product_id': self.id,
def open_function_tool_stock_move_line(self):
action = self.env.ref('sf_tool_management.sf_inbound_and_outbound_records_of_functional_tools_view_act')
result = action.read()[0]
result['domain'] = [('functional_tool_dismantle_id', '=', self.id), ('qty_done', '>', 0)]
return result
def open_tool_stock_picking(self):
action = self.env.ref('stock.action_picking_tree_all')
result = action.read()[0]
result['domain'] = [('origin', '=', self.code)]
return result
class StockPicking(models.Model):
_inherit = 'stock.picking'
def create_tool_dismantle_picking(self, obj):
"""
创建刀具物料入库单
"""
# 获取名称为内部调拨的作业类型
picking_type_id = self.env['stock.picking.type'].sudo().search([('name', '=', '内部调拨')])
location_id = self.env['stock.location'].search([('name', '=', '刀具组装位置')])
location_dest_id = self.env['stock.location'].search([('name', '=', '刀具房')])
if not location_id:
raise ValidationError('缺少名称为【刀具组装位置】的仓库管理地点')
if not location_dest_id:
raise ValidationError('缺少名称为【刀具房】的仓库管理地点')
# 创建刀具物料出库单
picking_id = self.env['stock.picking'].create({
'name': self._get_name_stock1(picking_type_id),
'picking_type_id': picking_type_id.id,
'location_id': location_id.id,
'location_dest_id': location_dest_id.id,
'origin': obj.code
})
return picking_id
class StockMove(models.Model):
_inherit = 'stock.move'
def create_tool_stock_move(self, datas):
picking_id = datas['picking_id']
data = datas['data']
stock_move_ids = []
for res in data:
if res:
# 创建库存移动记录
stock_move_id = self.env['stock.move'].sudo().create({
'name': picking_id.name,
'picking_id': picking_id.id,
'product_id': res['lot_id'].product_id.id,
'location_id': picking_id.location_id.id,
'location_dest_id': picking_id.location_dest_id.id,
'product_uom_qty': 1.00,
'reserved_availability': 1.00
})
stock_move_ids.append(stock_move_id)
return stock_move_ids
def write_tool_stock_move_line(self, datas):
picking_id = datas['picking_id']
data = datas['data']
move_line_ids = picking_id.move_line_ids
for move_line_id in move_line_ids:
for res in data:
if move_line_id.lot_id.product_id == res['lot_id'].product_id:
move_line_id.write({
'destination_location_id': res.get('destination').id,
'lot_id': res.get('lot_id').id
})
return True
def create_functional_tool_stock_move(self, dismantle_id):
"""
对功能刀具拆解过程的功能刀具进行库存移动,以及创建移动历史
"""
location_dismantle_id = self.env['stock.location'].search([('name', '=', '拆解')])
if not location_dismantle_id:
raise ValidationError('缺少名称为【拆解】的仓库管理地点')
tool_id = dismantle_id.functional_tool_id
# 创建库存移动记录
stock_move_id = self.env['stock.move'].sudo().create({
'name': dismantle_id.code,
'product_id': tool_id.barcode_id.product_id.id,
'location_id': tool_id.current_location_id.id,
'location_dest_id': location_dismantle_id.id,
'product_uom_qty': 1.00,
'state': 'done'
})
# 创建移动历史记录
stock_move_line_id = self.env['stock.move.line'].sudo().create({
'product_id': self.id,
'lot_id': lot_id.id,
'product_id': tool_id.barcode_id.product_id.id,
'functional_tool_dismantle_id': dismantle_id.id,
'lot_id': tool_id.barcode_id.id,
'move_id': stock_move_id.id,
'destination_location_id': shelf_location_id.id if shelf_location_id else False,
'install_tool_time': fields.Datetime.now(),
'qty_done': 1.0,
'state': 'done',
'functional_tool_type_id': tool_id.sf_cutting_tool_type_id.id,
'diameter': tool_id.functional_tool_diameter,
'knife_tip_r_angle': tool_id.knife_tip_r_angle,
'code': tool_id.code,
'rfid': tool_id.rfid,
'functional_tool_name': tool_id.name,
'tool_groups_id': tool_id.tool_groups_id.id
})
return stock_move_id, stock_move_line_id
class CustomStockScrap(models.Model):
_inherit = 'stock.scrap'
functional_tool_dismantle_id = fields.Many2one('sf.functional.tool.dismantle', string="功能刀具拆解单")
def create_tool_dismantle_stock_scrap(self, lot, dismantle_id):
location_id = self.env['stock.location'].search([('name', '=', '刀具组装位置')])
scrap_location_id = self.env['stock.location'].search([('name', 'in', ('Scrap', '报废'))])
if not location_id:
raise ValidationError('缺少名称为【刀具组装位置】的仓库管理地点')
if not scrap_location_id:
raise ValidationError('缺少名称为【Scrap】或【Scrap】的仓库管理地点')
stock_scrap_id = self.create({
'product_id': lot.product_id.id,
'lot_id': lot.id,
'location_id': location_id.id,
'scrap_location_id': scrap_location_id.id,
'functional_tool_dismantle_id': dismantle_id.id,
'origin': dismantle_id.code
})
# 完成报废单
stock_scrap_id.action_validate()
return stock_scrap_id

View File

@@ -252,11 +252,19 @@ class FunctionalCuttingToolEntity(models.Model):
def open_safety_stock(self):
action = self.env.ref('sf_tool_management.sf_real_time_distribution_of_functional_tools_view_act')
result = action.read()[0]
result['domain'] = [('name', '=', self.name), ('diameter', '=', self.functional_tool_diameter),
('knife_tip_r_angle', '=', self.knife_tip_r_angle),
('coarse_middle_thin', '=', self.coarse_middle_thin)]
result['domain'] = [('id', '=', self.safe_inventory_id.id)]
return result
def cnc_function_tool_use_verify(self):
"""
cnc程序用刀可用校验校验是否是制造订单所缺刀
"""
if self.tool_name_id.name:
cnc_processing_ids = self.env['sf.cnc.processing'].search(
[('tool_state', '=', '1'), ('cutting_tool_name', '=', self.tool_name_id.name)])
if cnc_processing_ids:
cnc_processing_ids.sudo().write({'tool_state': '0'})
def tool_inventory_displacement_out(self):
"""
机床当前刀库实时信息接口,功能刀具出库
@@ -267,6 +275,7 @@ class FunctionalCuttingToolEntity(models.Model):
self.create_stock_move(stock_location_id, False)
self.current_location_id = stock_location_id.id
self.current_shelf_location_id = False
# self.barcode_id.create_stock_quant(location_inventory_id, stock_location_id,
# self.functional_tool_name_id.id, '机床装刀', self.functional_tool_name_id,
# self.functional_tool_name_id.tool_groups_id)
@@ -372,6 +381,7 @@ class StockMoveLine(models.Model):
_order = 'date desc'
functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具组装单')
functional_tool_dismantle_id = fields.Many2one('sf.functional.tool.dismantle', string='功能刀具拆解单')
functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', store=True,
group_expand='_read_group_functional_tool_type_id')
functional_tool_name = fields.Char('刀具名称')
@@ -392,6 +402,9 @@ class StockMoveLine(models.Model):
if self.functional_tool_name_id:
action = self.functional_tool_name_id.action_open_reference1()
return action
if self.functional_tool_dismantle_id:
action = self.functional_tool_dismantle_id.action_open_reference1()
return action
elif self.move_id:
action = self.move_id.action_open_reference()
if action['res_model'] != 'stock.move':
@@ -409,13 +422,14 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
_inherit = ['mail.thread']
_description = '功能刀具安全库存'
name = fields.Char('名称', readonly=True, compute='_compute_name', store=True)
name = fields.Char('名称', compute='_compute_num', store=True)
functional_name_id = fields.Many2one('sf.tool.inventory', string='功能刀具名称', required=True)
tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', readonly=False, required=True)
sf_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', readonly=False,
group_expand='_read_mrs_cutting_tool_type_ids', store=True)
diameter = fields.Float(string='刀具直径(mm)', readonly=False)
knife_tip_r_angle = fields.Float(string='尖R角(mm)', readonly=False)
tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', compute='_compute_num', store=True)
sf_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型',
compute='_compute_num', store=True,
group_expand='_read_mrs_cutting_tool_type_ids')
diameter = fields.Float(string='具直径(mm)', compute='_compute_num', store=True)
knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', compute='_compute_num', store=True)
tool_stock_num = fields.Integer(string='刀具房数量', compute='_compute_stock_num', store=True)
side_shelf_num = fields.Integer(string='线边刀库数量', compute='_compute_stock_num', store=True)
on_tool_stock_num = fields.Integer(string='机内刀库数量', compute='_compute_stock_num', store=True)
@@ -460,22 +474,18 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
active = fields.Boolean(string='已归档', default=True)
@api.onchange('functional_name_id')
def _onchange_num(self):
@api.depends('functional_name_id', 'functional_name_id.diameter', 'functional_name_id.angle',
'functional_name_id.functional_cutting_tool_model_id')
def _compute_num(self):
for item in self:
if item.functional_name_id:
item.tool_groups_id = item.functional_name_id.tool_groups_id.id
item.sf_cutting_tool_type_id = item.functional_name_id.functional_cutting_tool_model_id.id
item.diameter = item.functional_name_id.diameter
item.knife_tip_r_angle = item.functional_name_id.angle
@api.depends('functional_name_id')
def _compute_name(self):
for obj in self:
if obj.tool_groups_id:
obj.name = obj.functional_name_id.name
item.name = item.functional_name_id.name
else:
obj.sudo().name = ''
item.sudo().name = ''
@api.constrains('min_stock_num', 'max_stock_num')
def _check_stock_num(self):
@@ -583,4 +593,10 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
for vals in vals_list:
vals['status_create'] = False
records = super(RealTimeDistributionOfFunctionalTools, self).create(vals_list)
for item in records:
if item:
record = self.search([('functional_name_id', '=', item.functional_name_id.id)])
if len(record) > 1:
raise ValidationError(
'功能刀具名称为【%s】的安全库存已经存在,请勿重复创建!!!' % item.functional_name_id.name)
return records

View File

@@ -37,14 +37,10 @@ class ToolDatasync(models.Model):
def _cron_tool_datasync_all(self):
try:
self.env['stock.lot'].sudo().sync_enroll_tool_material_stock_all()
self.env['stock.lot'].sudo().sync_enroll_fixture_material_stock_all()
self.env['sf.tool.material.search'].sudo().sync_enroll_tool_material_all()
self.env['stock.lot'].sudo().sync_enroll_tool_material_stock_all()
self.env['sf.fixture.material.search'].sudo().sync_enroll_fixture_material_all()
self.env['stock.lot'].sudo().sync_enroll_fixture_material_stock_all()
self.env['sf.functional.cutting.tool.entity'].sudo().esync_enroll_functional_tool_entity_all()
logging.info("已全部同步完成!!!")
# self.env['sf.functional.tool.warning'].sudo().sync_enroll_functional_tool_warning_all()
@@ -106,7 +102,7 @@ class StockLot(models.Model):
logging.info("没有刀具物料序列号信息")
except Exception as e:
logging.info("刀具物料序列号同步失败:%s" % e)
class ToolMaterial(models.Model):
_inherit = 'sf.tool.material.search'
@@ -198,7 +194,7 @@ class FunctionalCuttingToolEntity(models.Model):
for item in objs_all:
val = {
'id': item.id,
'code': item.code,
'code': False if not item.code else item.code.split('-', 1)[1],
'name': item.name,
'rfid': item.rfid,
'tool_groups_name': item.tool_groups_id.name,

View File

@@ -3,6 +3,7 @@ import requests
import logging
from odoo import models, api, fields
from odoo.exceptions import ValidationError
from datetime import datetime, timedelta
from odoo.addons.sf_base.commons.common import Common
@@ -101,7 +102,7 @@ class SfMaintenanceEquipment(models.Model):
tool_install_time = {'Nomal': '正常', 'Warning': '报警'}
equipment_tool_id.write({
'functional_tool_name_id': functional_tool_id.id,
'tool_install_time': time
'tool_install_time': time - timedelta(hours=8)
})
if functional_tool_id.current_location != '机内刀库':
# 对功能刀具进行移动到生产线

View File

@@ -29,13 +29,102 @@ class CNCprocessing(models.Model):
# else:
# raise ValidationError("MES装刀指令发送失败")
def cnc_tool_checkout(self, cnc_processing_ids):
"""
根据传入的工单信息查询是否有需要的功能刀具如果没有则生成CAM工单程序用刀计划
"""
logging.info('开始进行工单cnc程序用刀校验')
logging.info(f'cnc_processing_ids:{cnc_processing_ids}')
if not cnc_processing_ids:
return False
cam_id = self.env['sf.cam.work.order.program.knife.plan']
production_ids = [] # 制造订单集
datas = {'缺刀': {}, '无效刀': {}} # 缺刀/无效刀集
for cnc_processing in cnc_processing_ids:
# ======创建字典: {'缺刀': {'制造订单1': {'加工面1': [], ...}, ...}, '无效刀': {'制造订单1': {'加工面1': [], ...}, ...}}======
production_name = cnc_processing.workorder_id.production_id.name # 制造订单
processing_panel = cnc_processing.workorder_id.processing_panel # 加工面
if production_name not in list(datas['缺刀'].keys()):
datas['缺刀'].update({production_name: {processing_panel: []}})
datas['无效刀'].update({production_name: {processing_panel: []}})
production_ids.append(cnc_processing.workorder_id.production_id)
else:
if processing_panel not in list(datas['缺刀'].get(production_name).keys()):
datas['缺刀'].get(production_name).update({processing_panel: []})
datas['无效刀'].get(production_name).update({processing_panel: []})
# ======================================
if cnc_processing.cutting_tool_name:
tool_name = cnc_processing.cutting_tool_name
# 检验CNC用刀是否是功能刀具清单中的刀具
tool_inventory_id = self.env['sf.tool.inventory'].sudo().search([('name', '=', tool_name)])
if not tool_inventory_id:
if cnc_processing.cutting_tool_name not in datas['无效刀'][production_name][processing_panel]:
datas['无效刀'][production_name][processing_panel].append(cnc_processing.cutting_tool_name)
cnc_processing.tool_state = '2'
logging.info(f'"无效刀"[{production_name}{processing_panel}{cnc_processing.cutting_tool_name}]')
# 跳过本次循环
continue
# 校验CNC用刀在系统是否存在
functional_tools = self.env['sf.functional.cutting.tool.entity'].sudo().search(
[('tool_name_id', '=', tool_inventory_id.id), ('functional_tool_status', '=', '正常')])
# 判断线边、机内是否有满足条件的刀
if not functional_tools.filtered(lambda p: p.current_location in ('线边刀库', '机内刀库')):
if cnc_processing.cutting_tool_name not in datas['缺刀'][production_name][processing_panel]:
datas['缺刀'][production_name][processing_panel].append(cnc_processing.cutting_tool_name)
cnc_processing.tool_state = '1'
logging.info(f'"缺刀"[{production_name}{processing_panel}{cnc_processing.cutting_tool_name}]')
# 判断是否有满足条件的刀
if not functional_tools:
# 创建CAM申请装刀记录
cam_id.create_cam_work_plan(cnc_processing)
logging.info('成功调用CAM工单程序用刀计划创建方法')
logging.info(f'datas:{datas}')
for production_id in production_ids:
logging.info(f'production_id: {production_id}')
if production_id:
data1 = datas['无效刀'].get(production_id.name) # data1: {'加工面1': [], ...}
data2 = datas['缺刀'].get(production_id.name) # data2: {'加工面1': [], ...}
# tool_state_remark1 = ''
tool_state_remark2 = ''
# 对无效刀信息进行处理
for key in data1:
if data1.get(key):
# if tool_state_remark1 != '':
# tool_state_remark1 = f'{tool_state_remark1}\n{key}无效刀:{data1.get(key)}'
# else:
# 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': '待处理'
})
# 对缺刀信息进行处理
for key in data2:
if data2.get(key):
if tool_state_remark2 != '':
tool_state_remark2 = f'{tool_state_remark2}\n{key}缺刀:{data2.get(key)}'
else:
tool_state_remark2 = f'{key}缺刀:{data2.get(key)}'
# 将备注信息存入制造订单功能刀具状态的备注字段
logging.info('修改制造订单功能刀具状态的备注字段')
production_id.write({
'tool_state_remark': tool_state_remark2,
# 'tool_state_remark2': tool_state_remark1
})
logging.info('工单cnc程序用刀校验已完成')
@api.model_create_multi
def create(self, vals):
obj = super(CNCprocessing, self).create(vals)
for item in obj:
# 调用CAM工单程序用刀计划创建方法
self.env['sf.cam.work.order.program.knife.plan'].create_cam_work_plan(item)
logging.info('成功调用CAM工单程序用刀计划创建方法')
# 调用CAM工单程序用刀计划创建方法
self.cnc_tool_checkout(obj)
return obj

View File

@@ -21,3 +21,40 @@ class ShelfLocation(models.Model):
continue
item.tool_rfid = ''
item.tool_name_id = False
class StockMoveLine(models.Model):
_inherit = 'stock.move.line'
@api.model_create_multi
def create(self, vals_list):
records = super(StockMoveLine, self).create(vals_list)
move_lines = records.filtered(lambda a: a.product_id.categ_id.name == '功能刀具' and a.state == 'done')
if move_lines: # 校验是否为功能刀具移动历史
self.button_function_tool_use_verify(move_lines)
return records
def button_function_tool_use_verify(self, move_lines):
"""
对所有从【刀具房】到【制造前】的功能刀具进行校验(校验是否为制造订单所缺的刀)
"""
location_id = self.env['stock.location'].search([('name', '=', '刀具房')])
location_dest_id = self.env['stock.location'].search([('name', '=', '制造前')])
line_ids = move_lines.filtered(
lambda a: a.location_id == location_id and a.location_dest_id == location_dest_id)
for line_id in line_ids:
if line_id.lot_id:
self.env['sf.functional.cutting.tool.entity'].sudo().search(
[('barcode_id', '=', line_id.lot_id.id),
('functional_tool_status', '=', '正常')]).cnc_function_tool_use_verify()
class StockPicking(models.Model):
_inherit = 'stock.picking'
def button_validate(self):
res = super().button_validate()
move_lines = self.move_line_ids.filtered(lambda a: a.product_id.categ_id.name == '功能刀具')
if move_lines:
self.env['stock.move.line'].sudo().button_function_tool_use_verify(move_lines)
return res

View File

@@ -25,7 +25,8 @@ class ToolMaterial(models.Model):
have_been_used_num = fields.Integer('在用数量', compute='_compute_number', store=True)
scrap_num = fields.Integer('报废数量', compute='_compute_number', store=True)
barcode_ids = fields.One2many('stock.lot', 'tool_material_search_id', string='序列号', readonly=True)
barcode_ids = fields.One2many('stock.lot', 'tool_material_search_id', string='序列号', readonly=True,
domain=[('tool_material_status', '!=', '未入库')])
@api.depends('product_id.stock_quant_ids.quantity')
def _compute_number(self):
@@ -46,8 +47,6 @@ class ToolMaterial(models.Model):
record.scrap_num = scrap_num
record.number = usable_num + have_been_used_num + scrap_num
@api.model
def _read_group_cutting_tool_material_id(self, categories, domain, order):
cutting_tool_material_id = categories._search([], order=order, access_rights_uid=SUPERUSER_ID)

View File

@@ -29,7 +29,6 @@
<field name="current_location" string="当前位置"/>
<field name="current_location_id" invisible="1"/>
<field name="current_location" optional="hide"/>
<field name="sf_cutting_tool_type_id" invisible="True"/>
</tree>
</field>
@@ -61,13 +60,9 @@
</button>
<button class="oe_stat_button" groups="sf_base.group_sf_mrp_user"
name="open_stock_move_line"
icon="fa-list-ul"
type="object">
<div class="o_field_widget o_stat_info">
<span>
出库入库记录
</span>
</div>
<i class="fa fa-fw o_button_icon fa-exchange"/>
<span>出库入库记录</span>
</button>
<button class="oe_stat_button" groups="sf_base.group_sf_mrp_user"
name="open_safety_stock"
@@ -174,7 +169,7 @@
<field name="cut_time" attrs="{'invisible': [('new_former','=','0')]}"/>
<field name="cut_length" attrs="{'invisible': [('new_former','=','0')]}"/>
<field name="cut_number" attrs="{'invisible': [('new_former','=','0')]}"/>
<field name="current_location_id" string="当前位置"/>
<field name="current_location_id" string="当前位置" invisible="1"/>
<field name="current_location" string="当前位置"/>
<field name="current_shelf_location_id" string="当前货位"
attrs="{'invisible': [('current_shelf_location_id', '=', False)]}"/>
@@ -482,18 +477,19 @@
<field name="reference" string="单据号"/>
<field name="lot_id" invisible="1"/>
<field name="rfid"/>
<field name="functional_tool_name_id" optional="hide"/>
<field name="functional_tool_name" string="功能刀具名称"/>
<field name="diameter"/>
<field name="knife_tip_r_angle"/>
<field name="install_tool_time"/>
<field name="location_id"/>
<field name="current_location_id"/>
<field name="location_dest_id"/>
<field name="destination_location_id"/>
<field name="date"/>
<field name="qty_done" string="数量"/>
<field name="functional_tool_type_id" invisible="True"/>
<field name="functional_tool_type_id" invisible="1"/>
<field name="functional_tool_name_id" invisible="1"/>
<field name="functional_tool_dismantle_id" invisible="1"/>
<field name="install_tool_time" invisible="1"/>
<!-- <button name="enroll_functional_tool_move" string="安全库存注册" type="object" class="btn-primary"/>-->
</tree>
</field>

View File

@@ -407,6 +407,7 @@
<field name="plan_execute_status"/>
<filter string="已归档" name="inactive" domain="[('active', '=', False)]"/>
<searchpanel>
<field name="plan_execute_status" string="状态" enable_counters="1" icon="fa-filter"/>
<field name="production_line_id" string="生产线" enable_counters="1" icon="fa-filter"/>
<field name="functional_tool_type_id" string="功能刀具类型" enable_counters="1"
icon="fa-filter"/>
@@ -476,6 +477,17 @@
<field name="assemble_status" widget="statusbar" statusbar_visible="0,1"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button class="oe_stat_button" name="open_tool_stock_picking" icon="fa-truck" type="object"
attrs="{'invisible': [('assemble_status', '!=', '1')]}">
<div name="delivery_count" class="o_field_widget o_readonly_modifier o_field_statinfo">
<span class="o_stat_info o_stat_value">
<field name="picking_num"/>
</span>
<span class="o_stat_text">调拨</span>
</div>
</button>
</div>
<div class="oe_title">
<h1>
<field name="assembly_order_code"/>
@@ -711,10 +723,14 @@
</searchpanel>
<group expand="0" string="Group By...">
<filter string="功能刀具名称" name="name" domain="[]" context="{'group_by': 'functional_tool_name'}"/>
<filter string="刀具组" name="tool_groups" domain="[]" context="{'group_by': 'tool_groups_id'}"/>
<filter string="任务来源" name="loading_task_source" domain="[]" context="{'group_by': 'loading_task_source'}"/>
<filter string="用刀时间" name="use_tool_time" domain="[]" context="{'group_by': 'use_tool_time'}"/>
<filter string="功能刀具名称" name="name" domain="[]"
context="{'group_by': 'functional_tool_name'}"/>
<filter string="刀具组" name="tool_groups" domain="[]"
context="{'group_by': 'tool_groups_id'}"/>
<filter string="任务来源" name="loading_task_source" domain="[]"
context="{'group_by': 'loading_task_source'}"/>
<filter string="用刀时间" name="use_tool_time" domain="[]"
context="{'group_by': 'use_tool_time'}"/>
</group>
</search>
</field>
@@ -759,10 +775,25 @@
<form>
<header>
<button string="确认拆解" name="confirmation_disassembly" type="object" class="btn-primary"
confirm="是否确认拆解" attrs="{'invisible': [('state', '=', '拆解')]}"/>
confirm="是否确认拆解" attrs="{'invisible': [('state', '!=', '拆解')]}"/>
<field name="state" widget="statusbar" statusbar_visible="待拆解,已拆解"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button class="oe_stat_button" name="open_tool_stock_picking" icon="fa-truck" type="object"
attrs="{'invisible':[('state', '!=', '已拆解')]}">
<div name="delivery_count" class="o_field_widget o_readonly_modifier o_field_statinfo">
<span class="o_stat_info o_stat_value">
<field name="picking_num"/>
</span>
<span class="o_stat_text">调拨</span>
</div>
</button>
<button class="oe_stat_button" name="open_function_tool_stock_move_line" icon="fa-exchange"
type="object">
<span>功能刀具移动</span>
</button>
</div>
<div class="oe_title">
<h1>
<field name="code"/>
@@ -773,12 +804,13 @@
<group>
<field name="functional_tool_id" placeholder="请选择将要拆解的功能刀具"
options="{'no_create': True}" attrs="{'readonly': [('state', '=', '已拆解')]}"/>
<field name="rfid" attrs="{'invisible': [('state', '=', '已拆解')]}"/>
<field name="rfid_dismantle" attrs="{'invisible': [('state', '!=', '已拆解')]}"/>
<field name="rfid" attrs="{'invisible': [('rfid', '=', '')]}"/>
<field name="rfid_dismantle" attrs="{'invisible': [('rfid_dismantle', '=', False)]}"/>
<field name="tool_type_id"/>
<field name="tool_groups_id"/>
<field name="diameter"/>
<field name="knife_tip_r_angle"/>
<field name="picking_id" invisible="1"/>
</group>
<group>
<field name="image"/>
@@ -791,8 +823,8 @@
attrs="{'readonly': [('state', '=', '已拆解')]}"/>
</group>
<group>
<field name="scrap_id"
attrs="{'invisible': [('dismantle_cause', 'not in', ['寿命到期报废','崩刀报废'])]}"/>
<!-- <field name="scrap_id"-->
<!-- attrs="{'invisible': [('dismantle_cause', 'not in', ['寿命到期报废','崩刀报废'])]}"/>-->
<field name="grinding_id"
attrs="{'invisible': [('dismantle_cause', 'not in', ['刀具需磨削'])]}"/>
</group>
@@ -879,6 +911,23 @@
</group>
</group>
</page>
<page string="报废"
attrs="{'invisible':[('dismantle_cause', 'not in', ['寿命到期报废','崩刀报废'])]}">
<field name="scrap_ids">
<tree>
<field name="name"/>
<field name="product_id"/>
<field name="lot_id"/>
<field name="location_id"/>
<field name="scrap_location_id"/>
<field name="scrap_qty"/>
<field name="product_uom_id"/>
<field name="date_done"/>
<field name="state" widget="badge" decoration-success="state == 'done'"
decoration-muted="state == 'draft'"/>
</tree>
</field>
</page>
<page string="其他">
<group>
<group>

View File

@@ -63,6 +63,7 @@
<field name="name"/>
<field name="rfid"/>
<field name="product_qty"/>
<field name="create_date" string="入库日期"/>
<field name="tool_material_status"/>
<!-- <button name="enroll_tool_material_stock" string="序列号注册" type="object" class="btn-primary"/>-->
</tree>

View File

@@ -621,26 +621,10 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
desc_1 = self.get_desc_1(stock_lot)
# 封装功能刀具数据,用于创建功能刀具记录
desc_2 = self.get_desc_2(stock_lot, functional_tool_assembly)
# 创建刀具组装入库单
self.env['stock.picking'].create_stocking_picking(stock_lot, functional_tool_assembly, self)
# 刀具物料出库
if self.handle_code_id:
product_id.tool_material_stock_moves(self.handle_code_id, self.assembly_order_code)
if self.integral_product_id:
self.integral_product_id.material_stock_moves(self.integral_freight_barcode_id,
self.integral_freight_lot_id, self.assembly_order_code)
if self.blade_product_id:
self.blade_product_id.material_stock_moves(self.blade_freight_barcode_id,
self.blade_freight_lot_id, self.assembly_order_code)
if self.bar_product_id:
self.bar_product_id.material_stock_moves(self.bar_freight_barcode_id,
self.bar_freight_lot_id, self.assembly_order_code)
if self.pad_product_id:
self.pad_product_id.material_stock_moves(self.pad_freight_barcode_id,
self.pad_freight_lot_id, self.assembly_order_code)
if self.chuck_product_id:
self.chuck_product_id.material_stock_moves(self.chuck_freight_barcode_id,
self.chuck_freight_lot_id, self.assembly_order_code)
# 创建功能刀具组装入库单
self.env['stock.picking'].create_tool_stocking_picking(stock_lot, functional_tool_assembly, self)
# 创建刀具物料出库
self.env['stock.picking'].create_tool_stocking_picking1(self)
# ============================创建功能刀具列表、安全库存记录===============================
# 创建功能刀具列表记录
@@ -786,9 +770,9 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
class StockPicking(models.Model):
_inherit = 'stock.picking'
def create_stocking_picking(self, stock_lot, functional_tool_assembly, obj):
def create_tool_stocking_picking(self, stock_lot, functional_tool_assembly, obj):
"""
创建刀具组装入库单
创建功能刀具组装入库单
"""
# 获取名称为刀具组装入库的作业类型
picking_type_id = self.env['stock.picking.type'].sudo().search([('name', '=', '刀具组装入库')])
@@ -807,6 +791,7 @@ class StockPicking(models.Model):
'location_id': picking_id.location_id.id,
'location_dest_id': picking_id.location_dest_id.id,
'lot_id': stock_lot.id,
'install_tool_time': fields.Datetime.now(),
'qty_done': 1,
'functional_tool_name_id': functional_tool_assembly.id,
'functional_tool_type_id': obj.functional_tool_type_id.id,
@@ -836,6 +821,101 @@ class StockPicking(models.Model):
num = "%03d" % m
return name + str(num)
def create_tool_stocking_picking1(self, obj):
"""
创建刀具物料出库单
"""
# 获取名称为内部调拨的作业类型
picking_type_id = self.env['stock.picking.type'].sudo().search([('name', '=', '内部调拨')])
# 创建刀具物料出库单
picking_id = self.env['stock.picking'].create({
'name': self._get_name_stock1(picking_type_id),
'picking_type_id': picking_type_id.id,
'location_id': self.env['stock.location'].search([('name', '=', '刀具房')]).id,
'location_dest_id': self.env['stock.location'].search([('name', '=', '刀具组装位置')]).id,
'origin': obj.assembly_order_code
})
# =============刀具物料出库===================
stock_move_id = self.env['stock.move']
datas = {'data': [], 'picking_id': picking_id}
if obj.handle_code_id:
datas['data'].append(
{'current_location_id': self.env['sf.shelf.location'], 'lot_id': obj.handle_code_id})
if obj.integral_product_id:
datas['data'].append(
{'current_location_id': obj.integral_freight_barcode_id, 'lot_id': obj.integral_freight_lot_id.lot_id})
if obj.blade_product_id:
datas['data'].append(
{'current_location_id': obj.blade_freight_barcode_id, 'lot_id': obj.blade_freight_lot_id.lot_id})
if obj.bar_product_id:
datas['data'].append(
{'current_location_id': obj.bar_freight_barcode_id, 'lot_id': obj.bar_freight_lot_id.lot_id})
if obj.pad_product_id:
datas['data'].append(
{'current_location_id': obj.pad_freight_barcode_id, 'lot_id': obj.pad_freight_lot_id.lot_id})
if obj.chuck_product_id:
datas['data'].append(
{'current_location_id': obj.chuck_freight_barcode_id, 'lot_id': obj.chuck_freight_lot_id.lot_id})
# 创建刀具物料出库库存移动记录
stock_move_id.create_tool_material_stock_moves(datas)
# 将刀具物料出库库单的状态更改为就绪
picking_id.action_confirm()
# 修改刀具物料出库移动历史记录
stock_move_id.write_tool_material_stock_move_lines(datas)
# 设置数量,并验证完成
picking_id.action_set_quantities_to_reservation()
picking_id.button_validate()
def _get_name_stock1(self, picking_type_id):
name = picking_type_id.sequence_id.prefix
stock_id = self.env['stock.picking'].sudo().search(
[('name', 'like', name), ('picking_type_id', '=', picking_type_id.id)],
limit=1,
order="id desc"
)
if not stock_id:
num = "%05d" % 1
else:
m = int(stock_id.name[-3:]) + 1
num = "%05d" % m
return name + str(num)
class StockMove(models.Model):
_inherit = 'stock.move'
def create_tool_material_stock_moves(self, datas):
picking_id = datas['picking_id']
data = datas['data']
stock_move_ids = []
for res in data:
if res:
# 创建库存移动记录
stock_move_id = self.env['stock.move'].sudo().create({
'name': picking_id.name,
'picking_id': picking_id.id,
'product_id': res['lot_id'].product_id.id,
'location_id': picking_id.location_id.id,
'location_dest_id': picking_id.location_dest_id.id,
'product_uom_qty': 1.00,
'reserved_availability': 1.00
})
stock_move_ids.append(stock_move_id)
return stock_move_ids
def write_tool_material_stock_move_lines(self, datas):
picking_id = datas['picking_id']
data = datas['data']
move_line_ids = picking_id.move_line_ids
for move_line_id in move_line_ids:
for res in data:
if move_line_id.lot_id.product_id == res['lot_id'].product_id:
move_line_id.write({
'current_location_id': res.get('current_location_id').id,
'lot_id': res.get('lot_id').id
})
return True
class ProductProduct(models.Model):
_inherit = 'product.product'
@@ -853,13 +933,6 @@ class ProductProduct(models.Model):
'product_id': product_id[0].id,
'company_id': self.env.company.id
})
# 获取位置对象
location_inventory_ids = self.env['stock.location'].search([('name', 'in', ('Production', '生产'))])
stock_location_id = self.env['stock.location'].search([('name', '=', '组装后')])
# 创建功能刀具该批次/序列号 库存移动和移动历史
stock_lot.create_stock_quant(location_inventory_ids[-1], stock_location_id, functional_tool_assembly.id,
obj.assembly_order_code, obj, obj.after_tool_groups_id)
return stock_lot
def get_stock_lot_name(self, obj):
@@ -877,87 +950,3 @@ class ProductProduct(models.Model):
m = int(stock_lot_id.name[-3:]) + 1
num = "%03d" % m
return '%s-%s' % (code, num)
def tool_material_stock_moves(self, tool_material, assembly_order_code):
"""
对刀具物料进行库存移动到 刀具组装位置
"""
# 获取位置对象
location_inventory_id = tool_material.quant_ids.location_id[-1]
stock_location_id = self.env['stock.location'].search([('name', '=', '刀具组装位置')])
# 创建功能刀具该批次/序列号 库存移动和移动历史
tool_material.create_stock_quant(location_inventory_id, stock_location_id, None, assembly_order_code, False,
False)
def material_stock_moves(self, shelf_location_barcode_id, lot_id, assembly_order_code):
# 创建库存移动记录
stock_move_id = self.env['stock.move'].sudo().create({
'name': assembly_order_code,
'product_id': self.id,
'location_id': self.env['stock.location'].search([('name', '=', '刀具房')]).id,
'location_dest_id': self.env['stock.location'].search([('name', '=', '刀具组装位置')]).id,
'product_uom_qty': 1.00,
'state': 'done'
})
# 创建移动历史记录
stock_move_line_id = self.env['stock.move.line'].sudo().create({
'product_id': self.id,
'move_id': stock_move_id.id,
'lot_id': lot_id.lot_id.id,
'current_location_id': shelf_location_barcode_id.id,
'install_tool_time': fields.Datetime.now(),
'qty_done': 1.0,
'state': 'done',
})
return stock_move_id, stock_move_line_id
class StockLot(models.Model):
_inherit = 'stock.lot'
def create_stock_quant(self, location_inventory_id, stock_location_id, functional_tool_assembly_id, name, obj,
tool_groups_id):
"""
对功能刀具组装过程的功能刀具和刀具物料进行库存移动,以及创建移动历史
"""
# 创建库存移动记录
stock_move_id = self.env['stock.move'].sudo().create({
'name': name,
'product_id': self.product_id.id,
'location_id': location_inventory_id.id,
'location_dest_id': stock_location_id.id,
'product_uom_qty': 1.00,
'state': 'done'
})
# 创建移动历史记录
stock_move_line_id = self.env['stock.move.line'].sudo().create({
'product_id': self.product_id.id,
'functional_tool_name_id': functional_tool_assembly_id,
'lot_id': self.id,
'move_id': stock_move_id.id,
'install_tool_time': fields.Datetime.now(),
'qty_done': 1.0,
'state': 'done',
'functional_tool_type_id': False if not obj else obj.functional_tool_type_id.id,
'diameter': None if not obj else obj.after_assembly_functional_tool_diameter,
'knife_tip_r_angle': None if not obj else obj.after_assembly_knife_tip_r_angle,
'code': '' if not obj else obj.code,
'rfid': '' if not obj else obj.rfid,
'functional_tool_name': '' if not obj else obj.after_assembly_functional_tool_name,
'tool_groups_id': False if not tool_groups_id else tool_groups_id.id
})
return stock_move_id, stock_move_line_id
# class StockQuant(models.Model):
# _inherit = 'stock.quant'
#
# @api.model_create_multi
# def create(self, vals_list):
# records = super(StockQuant, self).create(vals_list)
# for record in records:
# if record.lot_id.product_id.categ_id.name == '刀具':
# record.lot_id.enroll_tool_material_stock()
# return records

View File

@@ -254,6 +254,7 @@ class SfShelf(models.Model):
shelf_height = fields.Float(string='货架高度(m)')
shelf_layer = fields.Integer(string='货架层数')
layer_capacity = fields.Integer(string='层数容量')
shelf_rotative_Boolean = fields.Boolean('循环货架', default=False)
# 是否有货位
is_there_area = fields.Boolean(string='是否有货位', compute='_compute_is_there_area', default=False, store=True)
@@ -361,6 +362,7 @@ class ShelfLocation(models.Model):
name = fields.Char('货位名称', required=True, size=20)
barcode = fields.Char('货位编码', copy=False, size=50)
rotative_Boolean = fields.Boolean('循环货位', related='shelf_id.shelf_rotative_Boolean', store=True)
qr_code = fields.Binary(string='二维码', compute='_compute_location_qr_code', store=True)
# 货架
@@ -416,19 +418,19 @@ class ShelfLocation(models.Model):
host = printer_config.printer_id.ip_address
port = printer_config.printer_id.port
self.print_qr_code(barcode, host, port)
# 获取当前wizard的视图ID或其他标识信息
view_id = self.env.context.get('view_id')
# 构造返回wizard页面的action字典
action = {
'type': 'ir.actions.act_window',
'name': '返回 Wizard',
'res_model': 'sf.shelf', # 替换为你的wizard模型名称
'view_mode': 'form',
'view_id': view_id, # 如果需要基于特定的视图返回
'target': 'new', # 如果需要在新的窗口或标签页打开
'res_id': self.shelf_id, # 如果你想要返回当前记录的视图
}
return action
# # 获取当前wizard的视图ID或其他标识信息
# view_id = self.env.context.get('view_id')
# # 构造返回wizard页面的action字典
# action = {
# 'type': 'ir.actions.act_window',
# 'name': '返回 Wizard',
# 'res_model': 'sf.shelf', # 替换为你的wizard模型名称
# 'view_mode': 'form',
# 'view_id': view_id, # 如果需要基于特定的视图返回
# 'target': 'new', # 如果需要在新的窗口或标签页打开
# 'res_id': self.shelf_id, # 如果你想要返回当前记录的视图
# }
# return action
# # 仓库类别selection库区、库位、货位
# location_type = fields.Selection([
@@ -557,8 +559,10 @@ class SfStockMoveLine(models.Model):
_name = 'stock.move.line'
_inherit = ['stock.move.line', 'printing.utils']
stock_lot_name = fields.Char('序列号名称', related='lot_id.name')
current_location_id = fields.Many2one(
'sf.shelf.location', string='当前货位', compute='_compute_current_location_id', store=True)
'sf.shelf.location', string='当前货位', compute='_compute_current_location_id', store=True, readonly=False,
domain="[('product_id', '=', product_id),'|',('product_sn_id.name', '=', stock_lot_name),('product_sn_ids.lot_id.name','=',stock_lot_name)]")
# location_dest_id = fields.Many2one('stock.location', string='目标库位')
location_dest_id_product_type = fields.Many2many(related='location_dest_id.product_type')
location_dest_id_value = fields.Integer(compute='_compute_location_dest_id_value', store=True)
@@ -622,6 +626,11 @@ class SfStockMoveLine(models.Model):
if not qr_code_data:
raise UserError("没有找到二维码数据。")
lot_name = self.lot_name
# 增加"当为坯料时,只打印序列号的前面部分"
if self.lot_name: # 确保 lot_name 存在
if self.product_id.categ_id.name == '坯料':
lot_name = lot_name.split('[', 1)[0]
# host = "192.168.50.110" # 可以根据实际情况修改
# port = 9100 # 可以根据实际情况修改
@@ -851,7 +860,9 @@ class SfStockMoveLine(models.Model):
def compute_destination_location_id(self):
for record in self:
obj = self.env['sf.shelf.location'].search([('name', '=',
self.destination_location_id.name)])
record.destination_location_id.name)])
if obj and obj.product_id and obj.product_id != record.product_id:
raise ValidationError('目标货位【%s】已被【%s】产品占用!' % (obj.code, obj.product_id))
if record.lot_id:
if record.product_id.tracking == 'serial':
shelf_location_obj = self.env['sf.shelf.location'].search(
@@ -864,7 +875,7 @@ class SfStockMoveLine(models.Model):
if obj:
obj.product_sn_id = record.lot_id.id
elif record.product_id.tracking == 'lot':
self.put_shelf_location(record)
record.put_shelf_location(record)
if not obj.product_id:
obj.product_id = record.product_id.id
else:
@@ -975,15 +986,26 @@ class SfStockPicking(models.Model):
# 调用入库方法进行入库刀货位
line.compute_destination_location_id()
else:
# 对除刀柄之外的刀具物料进行 目标货位必填校验
# 对除刀柄之外的刀具物料入库到刀具房进行 目标货位必填校验
if self.location_dest_id.name == '刀具房' and line.product_id.cutting_tool_material_id.name not in (
'刀柄', False):
raise ValidationError('请选择【%s】产品的目标货位!' % line.product_id.name)
if line.current_location_id:
# 对货位的批次产品进行出货
line.put_shelf_location(line)
if line.current_location_id:
# 按序列号管理的产品
if line.current_location_id.product_sn_id:
line.current_location_id.product_sn_id = False
# line.current_location_id.location_status = '空闲'
line.current_location_id.product_num = 0
line.current_location_id.product_id = False
else:
# 对除刀柄之外的刀具物料从刀具房出库进行 当前货位必填校验
if self.location_id.name == '刀具房' and line.product_id.cutting_tool_material_id.name not in (
'刀柄', False):
raise ValidationError('请选择【%s】产品的当前货位!' % line.product_id.name)
# 对入库作业的刀柄和托盘进行Rfid绑定校验
for move in self.move_ids:
@@ -1110,6 +1132,7 @@ class CustomStockMove(models.Model):
采购入库扫码绑定Rfid码
"""
for record in self:
logging.info('Rfid%s' % barcode)
if record:
lot = self.env['stock.lot'].sudo().search([('rfid', '=', barcode)])
if lot:
@@ -1121,7 +1144,9 @@ class CustomStockMove(models.Model):
'该Rfid【%s】已经被序列号为【%s】的【%s】物料所占用!' % (barcode, lot.name, material))
if '刀柄' in (record.product_id.cutting_tool_material_id.name or '') or '托盘' in (
record.product_id.fixture_material_id.name or ''):
logging.info('开始录入Rfid:%s' % record.move_line_nosuggest_ids)
for move_line_nosuggest_id in record.move_line_nosuggest_ids:
logging.info('录入的记录%s , Rfid:%s' % (move_line_nosuggest_id, move_line_nosuggest_id.rfid))
if move_line_nosuggest_id.rfid:
if move_line_nosuggest_id.rfid == barcode:
if record.product_id.cutting_tool_material_id.name:
@@ -1130,7 +1155,9 @@ class CustomStockMove(models.Model):
raise ValidationError('该托盘的Rfid已经录入请勿重复录入')
else:
line_id = int(re.sub(r"\D", "", str(move_line_nosuggest_id.id)))
self.env['stock.move.line'].sudo().search([('id', '=', line_id)]).write({'rfid': barcode})
res = self.env['stock.move.line'].sudo().search([('id', '=', line_id)]).write(
{'rfid': barcode})
logging.info('Rfid是否录入:%s' % res)
move_line_nosuggest_id.rfid = barcode
break
else:
@@ -1184,6 +1211,12 @@ class CustomStockMove(models.Model):
# todo 待控制
if not lot_name:
raise ValidationError("请先分配序列号")
# 增加"当为坯料时,只打印序列号的前面部分"
if record.lot_name: # 确保 lot_name 存在
if record.product_id.categ_id.name == '坯料':
lot_name = lot_name.split('[', 1)[0]
# host = "192.168.50.110" # 可以根据实际情况修改
# port = 9100 # 可以根据实际情况修改

View File

@@ -7,16 +7,18 @@
<field name="inherit_id" ref="stock.view_stock_move_line_detailed_operation_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='location_id'][2]" position="after">
<field name="current_location_id" force_save="1"/>
<field name="stock_lot_name" invisible="1"/>
<field name="current_location_id" force_save="1"
options="{'no_create': True,'no_create_edit':True}"/>
</xpath>
<xpath expr="//field[@name='location_dest_id'][2]" position="after">
<field name="current_product_id" invisible="1"/>
<field name="there_is_no_sn" invisible="1"/>
<!-- <field name="destination_location_id" domain="[('location_id', '=', location_dest_id_value), -->
<!-- '|', -->
<!-- ('location_status', '=', '空闲'), -->
<!-- ('location_status', '=', '占用'), ('product_id', '=', current_product_id) -->
<!-- ]"/> -->
<!-- <field name="destination_location_id" domain="[('location_id', '=', location_dest_id_value), -->
<!-- '|', -->
<!-- ('location_status', '=', '空闲'), -->
<!-- ('location_status', '=', '占用'), ('product_id', '=', current_product_id) -->
<!-- ]"/> -->
<field name="destination_location_id" domain="[('location_id', '=', location_dest_id_value), '|',
('location_status', '=', '空闲'), ('product_id', '=', current_product_id), ('product_sn_id',
'=', there_is_no_sn)]" options="{'no_create': True,'no_create_edit':True}"/>
@@ -55,6 +57,7 @@
<field name="inherit_id" ref="stock.view_move_line_form"/>
<field name="arch" type="xml">
<xpath expr="//form//sheet//group//group//field[@name='location_id']" position="after">
<field name="stock_lot_name" invisible="1"/>
<field name="current_location_id" options="{'no_create': False}" force_save="1"/>
</xpath>
<xpath expr="//form//sheet//group//group//field[@name='location_dest_id']" position="after">
@@ -99,11 +102,11 @@
attrs="{'invisible': ['|', '|', '|', ('picking_type_code', '=', 'incoming'), ('immediate_transfer', '=', True), '&amp;', ('state', '!=', 'assigned'), ('move_type', '!=', 'one'), '&amp;', ('state', 'not in', ('assigned', 'confirmed')), ('move_type', '=', 'one')]}"
data-hotkey="w"/>
</xpath>
<xpath expr="//form//header//button[@name='button_scrap']" position="replace">
<button name="button_scrap" groups="sf_base.group_sf_stock_user" type="object" string="报废"
attrs="{'invisible': ['|', '&amp;', ('picking_type_code', '=', 'incoming'), ('state', '!=', 'done'), '&amp;', ('picking_type_code', '=', 'outgoing'), ('state', '=', 'done')]}"
data-hotkey="y"/>
</xpath>
<!-- <xpath expr="//form//header//button[@name='button_scrap']" position="replace">-->
<!-- <button name="button_scrap" groups="sf_base.group_sf_stock_user" type="object" string="报废"-->
<!-- attrs="{'invisible': ['|', '&amp;', ('picking_type_code', '=', 'incoming'), ('state', '!=', 'done'), '&amp;', ('picking_type_code', '=', 'outgoing'), ('state', '=', 'done')]}"-->
<!-- data-hotkey="y"/>-->
<!-- </xpath>-->
<xpath expr="//form//header//button[@name='action_assign']" position="replace">
<button name="action_assign" attrs="{'invisible': [('show_check_availability', '=', False)]}"
string="检查可用量" type="object" class="oe_highlight"

View File

@@ -23,6 +23,7 @@
<field name="shelf_height" string="货架高度(m)"/>
<field name="shelf_layer" string="货架层数"/>
<field name="layer_capacity" string="层数容量"/>
<field name="shelf_rotative_Boolean"/>
</group>
<notebook>
<page string="货位">
@@ -169,6 +170,7 @@
<group>
<field name="barcode" readonly="1"/>
<field name="name" readonly="1"/>
<field name="rotative_Boolean" invisible="1"/>
<field name="shelf_id" readonly="1"/>
<field name="location_id" readonly="1"/>
<field name="product_id"/>

138
sg_wechat_enterprise/.gitignore vendored Normal file
View File

@@ -0,0 +1,138 @@
# 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/

View File

@@ -0,0 +1,3 @@
from . import we_api
from . import models
from . import controllers

View File

@@ -0,0 +1,52 @@
# -*- 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',
}

View File

@@ -0,0 +1,273 @@
#!/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成功0sEncryptMsg,失败返回对应的错误码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

View File

@@ -0,0 +1,3 @@
from . import wechat_enterprise

View File

@@ -0,0 +1,20 @@
#!/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

View File

@@ -0,0 +1,231 @@
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)

View File

@@ -0,0 +1,16 @@
<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>

View File

@@ -0,0 +1,13 @@
<?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>

View File

@@ -0,0 +1,9 @@
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