Compare commits

..

301 Commits

Author SHA1 Message Date
马广威
4bff6e4355 Accept Merge Request #1889: (release/release_2.9 -> develop)
Merge Request: 调整名称与提示语

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1889?initial=true
2025-02-28 17:45:56 +08:00
mgw
98b4c53213 调整名称与提示语 2025-02-28 17:35:32 +08:00
廖丹龙
ec1da84da0 Accept Merge Request #1888: (feature/tool_standard_library_process -> 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/1888
2025-02-28 16:38:23 +08:00
liaodanlong
4c8cf5caf8 制造订单状态为返工时限制表面工艺工单状态 2025-02-28 16:36:16 +08:00
马广威
aa1517ebce Accept Merge Request #1887: (release/release_2.9 -> develop)
Merge Request: 取消销售订单时的下游单据清单【大类、单据名称、作业类型】显示错误

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1887?initial=true
2025-02-28 16:06:04 +08:00
mgw
c24f539197 取消销售订单时的下游单据清单【大类、单据名称、作业类型】显示错误 2025-02-28 16:04:49 +08:00
禹翔辉
72620236ab Accept Merge Request #1886: (feature/外协优化_1 -> develop)
Merge Request: Merge branch 'feature/外协优化' into feature/外协优化_1

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1886
2025-02-28 08:39:01 +08:00
yuxianghui
eb69e20f01 Merge branch 'feature/外协优化' into feature/外协优化_1 2025-02-28 08:37:41 +08:00
yuxianghui
76f9b5ef3e 处理特殊表面工艺外协入库单点击【验证】报错 2025-02-28 08:36:12 +08:00
胡尧
04f697ae7b Merge branch 'develop' into release/release_2.9 2025-02-27 17:08:59 +08:00
廖丹龙
d5e93b14be Accept Merge Request #1885: (feature/tool_standard_library_process -> 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/1885
2025-02-27 16:14:24 +08:00
liaodanlong
d4f42abfd8 隐藏发料出库的批量调拨按钮 2025-02-27 16:12:12 +08:00
mgw
07c07ef37c 完善下游单据问题 2025-02-27 16:00:29 +08:00
廖丹龙
8fd536324e Accept Merge Request #1884: (feature/tool_standard_library_process -> 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/1884
2025-02-27 15:11:21 +08:00
liaodanlong
22c5ee6806 隐藏收料入库的批量调拨按钮 2025-02-27 15:09:57 +08:00
liaodanlong
3bcc673d88 人工线下加工表面工艺逻辑还原 2025-02-27 15:09:14 +08:00
马广威
90a7a025b1 Accept Merge Request #1883: (feature/制造功能优化 -> develop)
Merge Request: 组件制造调拨外置

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1883?initial=true
2025-02-27 14:54:13 +08:00
mgw
95929fa01c Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-27 14:53:41 +08:00
mgw
19eb6519c0 组件制造调拨外置 2025-02-27 14:53:23 +08:00
马广威
188e1fc553 Accept Merge Request #1882: (feature/制造功能优化 -> develop)
Merge Request: 调整名称等

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1882?initial=true
2025-02-27 14:37:47 +08:00
mgw
197f1f65ea Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-27 14:37:21 +08:00
mgw
bae6d4d59b 调整名称等 2025-02-27 14:37:03 +08:00
马广威
211e9c58f1 Accept Merge Request #1881: (feature/制造功能优化 -> develop)
Merge Request: 处理空值情况

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1881?initial=true
2025-02-27 14:01:33 +08:00
mgw
46d1c9a83d Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-27 14:00:58 +08:00
mgw
92adde7909 处理空值情况 2025-02-27 14:00:41 +08:00
廖丹龙
a8f70c82d1 Accept Merge Request #1879: (feature/tool_standard_library_process -> 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/1879
2025-02-27 13:55:33 +08:00
马广威
1efb6be453 Accept Merge Request #1880: (feature/制造功能优化 -> develop)
Merge Request: 统一处理质检单

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1880?initial=true
2025-02-27 13:55:19 +08:00
mgw
b6165e333b Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-27 13:54:46 +08:00
mgw
ed17444f21 统一处理质检单 2025-02-27 13:54:28 +08:00
liaodanlong
20932f9d51 人工线下加工表面工艺逻辑修改 2025-02-27 13:46:30 +08:00
禹翔辉
43904c0249 Accept Merge Request #1878: (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/1878?initial=true
2025-02-27 13:14:24 +08:00
yuxianghui
919717ccc5 处理工单外协创建的外协出入库单没有质检单的问题 2025-02-27 13:13:22 +08:00
马广威
a4898297b7 Accept Merge Request #1877: (feature/制造功能优化 -> develop)
Merge Request: Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1877?initial=true
2025-02-27 11:33:19 +08:00
mgw
75bbbaa027 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-27 11:32:46 +08:00
mgw
b693252a7b Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-27 11:32:29 +08:00
马广威
4db8a29bc1 Accept Merge Request #1876: (feature/制造功能优化 -> develop)
Merge Request: 调整单据状态

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1876
2025-02-27 11:27:05 +08:00
mgw
88fceb016f 调整单据状态 2025-02-27 11:26:26 +08:00
马广威
712e1f3947 Accept Merge Request #1875: (feature/制造功能优化 -> develop)
Merge Request: 完善单据

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1875?initial=true
2025-02-27 11:00:19 +08:00
mgw
d1a71e1a05 完善单据 2025-02-27 10:58:06 +08:00
马广威
0c3e730619 Accept Merge Request #1874: (feature/制造功能优化 -> develop)
Merge Request: 取消列表单据只读

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1874?initial=true
2025-02-27 09:58:58 +08:00
mgw
0063a199f8 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-27 09:58:29 +08:00
mgw
7af9b25eee 取消列表单据只读 2025-02-27 09:58:12 +08:00
管欢
7f195f9e5f Accept Merge Request #1873: (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/1873
2025-02-27 09:54:51 +08:00
guanhuan
000cd9a13c 修复坯料发料提醒重复发送 2025-02-27 09:53:42 +08:00
廖丹龙
3771a62dae Accept Merge Request #1871: (feature/tool_standard_library_process -> 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/1871
2025-02-27 09:18:55 +08:00
马广威
172219a3e2 Accept Merge Request #1872: (feature/制造功能优化 -> develop)
Merge Request: 调整行号

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1872?initial=true
2025-02-27 09:18:15 +08:00
mgw
7bd17ea8da Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-27 09:17:06 +08:00
mgw
f234943104 调整行号 2025-02-27 09:16:50 +08:00
liaodanlong
9b9b0a30a3 批量调拨按钮隐藏 2025-02-27 09:10:07 +08:00
马广威
3552c11122 Accept Merge Request #1870: (feature/制造功能优化 -> develop)
Merge Request: 补全单据

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1870?initial=true
2025-02-27 09:05:59 +08:00
mgw
e0e5dc8f7e Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-27 09:04:58 +08:00
mgw
d608b78f8c 坯料外协单据 2025-02-27 09:04:38 +08:00
mgw
513daa7c2b 补全单据 2025-02-26 17:39:34 +08:00
黄焱
57342ec843 Accept Merge Request #1869: (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/1869?initial=true
2025-02-26 16:40:33 +08:00
hyyy
352dc89e91 还原序号处理 2025-02-26 16:29:19 +08:00
马广威
8dc6c1a617 Accept Merge Request #1868: (feature/制造功能优化 -> develop)
Merge Request: Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1868?initial=true
2025-02-26 14:48:43 +08:00
mgw
82cbd95b7d Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-26 14:47:45 +08:00
mgw
a4bfda0a41 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-26 14:47:28 +08:00
廖丹龙
6e0e00a5a1 Accept Merge Request #1867: (feature/tool_standard_library_process -> 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/1867
2025-02-26 14:38:34 +08:00
liaodanlong
0cb73b6d29 切割工单过滤掉回退按钮 2025-02-26 14:36:50 +08:00
廖丹龙
358ad68dc9 Accept Merge Request #1866: (feature/tool_standard_library_process -> 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/1866
2025-02-26 14:17:33 +08:00
liaodanlong
cd115ea411 过滤掉检测结果为合格的数据 2025-02-26 14:15:46 +08:00
廖丹龙
64fa908519 Accept Merge Request #1865: (feature/tool_standard_library_process -> 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/1865
2025-02-26 13:33:40 +08:00
liaodanlong
266c448c00 字段名称修改 2025-02-26 13:21:27 +08:00
马广威
1fb39e55c7 Accept Merge Request #1864: (feature/制造功能优化 -> develop)
Merge Request: 去除对切割工单的编程控制

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1864?initial=true
2025-02-26 13:09:45 +08:00
mgw
dbaf5342fc 去除对切割工单的编程控制 2025-02-26 13:09:15 +08:00
马广威
8bfb5b3b79 Accept Merge Request #1863: (feature/制造功能优化 -> develop)
Merge Request: Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1863?initial=true
2025-02-26 12:43:21 +08:00
mgw
897a10a500 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-26 12:42:58 +08:00
mgw
2035dd6857 切割工单隐藏编程相关 2025-02-26 12:42:42 +08:00
廖丹龙
4beaeffd46 Accept Merge Request #1862: (feature/tool_standard_library_process -> 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/1862
2025-02-26 11:32:02 +08:00
liaodanlong
cd78a39410 匹配规则修改 2025-02-26 11:30:14 +08:00
liaodanlong
2e47922086 匹配规则修改 2025-02-26 11:25:09 +08:00
廖丹龙
fd76863daa Accept Merge Request #1861: (feature/tool_standard_library_process -> 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/1861
2025-02-26 10:48:29 +08:00
liaodanlong
85c8e834cb 匹配规则修改 2025-02-26 10:46:02 +08:00
马广威
e4c729e8e0 Accept Merge Request #1860: (feature/制造功能优化 -> develop)
Merge Request: 调整判断逻辑

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1860?initial=true
2025-02-26 10:41:42 +08:00
mgw
e9226a87f5 调整判断逻辑 2025-02-26 10:41:05 +08:00
廖丹龙
22b0b02168 Accept Merge Request #1859: (feature/tool_standard_library_process -> 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/1859
2025-02-26 10:31:10 +08:00
liaodanlong
bff10de6b2 修改计算方法名称 2025-02-26 10:29:01 +08:00
廖丹龙
ebe8d56ffd Accept Merge Request #1854: (feature/tool_standard_library_process -> 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/1854
2025-02-26 10:22:03 +08:00
liaodanlong
d7e3ce2b1a 刀具标准库数据同步 2025-02-26 10:21:40 +08:00
马广威
ae7e902b4d Accept Merge Request #1858: (feature/制造功能优化 -> develop)
Merge Request: 修改变量名

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1858?initial=true
2025-02-26 10:13:57 +08:00
mgw
0be9afcdfe 修改变量名 2025-02-26 10:13:26 +08:00
马广威
02582470e5 Accept Merge Request #1857: (feature/制造功能优化 -> develop)
Merge Request: 调整判断逻辑

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1857?initial=true
2025-02-26 10:06:55 +08:00
mgw
e3b2dade15 调整判断逻辑 2025-02-26 10:06:27 +08:00
马广威
f6e36014c8 Accept Merge Request #1856: (feature/制造功能优化 -> develop)
Merge Request: 修改状态名称

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1856?initial=true
2025-02-26 09:58:39 +08:00
mgw
f89b52bad1 修改状态名称 2025-02-26 09:58:16 +08:00
马广威
157ce5e2dc Accept Merge Request #1855: (feature/制造功能优化 -> develop)
Merge Request: sf-工厂取消接单-取消列表展示数据问题

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1855?initial=true
2025-02-26 09:54:53 +08:00
mgw
478c54a44d Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-26 09:54:24 +08:00
mgw
f06951958d sf-工厂取消接单-取消列表展示数据问题 2025-02-26 09:54:08 +08:00
liaodanlong
8799fcddb2 还原代码 2025-02-26 09:41:17 +08:00
马广威
0d25eb5009 Accept Merge Request #1853: (feature/制造功能优化 -> develop)
Merge Request: sf-工厂取消接单-坯料的制造订单未取消

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1853?initial=true
2025-02-26 09:41:07 +08:00
mgw
92c3de98ba Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-26 09:40:39 +08:00
mgw
dca6f9fb53 sf-工厂取消接单-坯料的制造订单未取消 2025-02-26 09:40:18 +08:00
liaodanlong
c4c6386871 坯料的采购订单零件图号,零件名称显示 2025-02-26 09:37:56 +08:00
禹翔辉
4bdeec6e24 Accept Merge Request #1852: (feature/调拨质检消息优化_1 -> develop)
Merge Request: Merge branch 'feature/调拨质检消息优化' into feature/调拨质检消息优化_1

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1852
2025-02-26 09:25:26 +08:00
yuxianghui
b7331a811b Merge branch 'feature/调拨质检消息优化' into feature/调拨质检消息优化_1 2025-02-26 09:23:44 +08:00
yuxianghui
e5d1a18640 处理质检完成时消息通知重复问题 2025-02-26 09:22:50 +08:00
马广威
62e8634abf Accept Merge Request #1851: (feature/制造功能优化 -> develop)
Merge Request: 坯料自加工的切割工序状态也判断了编程状态

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1851
2025-02-26 09:11:10 +08:00
mgw
2a8791b9a9 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-26 09:10:04 +08:00
mgw
def9bd2c6e 坯料自加工的切割工序状态也判断了编程状态 2025-02-26 09:09:26 +08:00
胡尧
bf785328ac Accept Merge Request #1850: (feature/mrp_bug_fixed -> develop)
Merge Request: 工单返工完成不触发制造订单的完成事件

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1850?initial=true
2025-02-25 17:25:26 +08:00
胡尧
332dbce769 工单返工完成不触发制造订单的完成事件 2025-02-25 17:25:04 +08:00
廖丹龙
fa2f5552e2 Accept Merge Request #1849: (feature/tool_standard_library_process -> 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/1849
2025-02-25 16:40:12 +08:00
liaodanlong
c400553294 工艺确认时的工单空值处理 2025-02-25 16:35:40 +08:00
liaodanlong
7975fd2bd2 CNC回退后需开放送检按钮 2025-02-25 16:32:04 +08:00
liaodanlong
a0c5f9d15f 制造订单是返工状态时-上报返工的工单有【回退】入口-要隐藏问题处理 2025-02-25 16:31:18 +08:00
liaodanlong
2e648dea4e 成品入库单已经完成-工单还有【回退】按钮-还能回退成功 问题处理 2025-02-25 16:29:58 +08:00
黄焱
7056298420 Accept Merge Request #1848: (feature/前端样式修改 -> develop)
Merge Request: 去掉listRenderer含有hasSelectors时的row_no,因为td中没有序号

Created By: @黄焱
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @黄焱
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1848?initial=true
2025-02-25 16:28:26 +08:00
hyyy
3cd7a52900 去掉listRenderer含有hasSelectors时的row_no,因为td中没有序号 2025-02-25 16:15:11 +08:00
禹翔辉
5323f1076e Accept Merge Request #1847: (feature/调拨质检消息优化 -> develop)
Merge Request: Merge branch 'feature/库存优化_6' into feature/调拨质检消息优化

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1847
2025-02-25 15:09:55 +08:00
yuxianghui
5c5b8fb8fd Merge branch 'feature/库存优化_6' into feature/调拨质检消息优化 2025-02-25 15:06:38 +08:00
yuxianghui
fb4f08a24a 1、处理销售订单确认供货路线报错-坯料获取方式是自加工 问题;2、质检完成的消息通知添加超链接;3、添加 库存作业单据没有质检单时-会发送待处理消息通知-不用发送判断条件 2025-02-25 15:05:30 +08:00
禹翔辉
f3e5901474 Accept Merge Request #1846: (feature/库存优化_6 -> develop)
Merge Request: Merge branch 'feature/库存优化_5' into feature/库存优化_6

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1846
2025-02-25 11:30:39 +08:00
yuxianghui
247036209e Merge branch 'feature/库存优化_5' into feature/库存优化_6 2025-02-25 11:23:23 +08:00
yuxianghui
0fd33831f1 处理由客供料入库单生成的质检单没有批次序列号问题 2025-02-25 11:22:41 +08:00
禹翔辉
78b58d4fb8 Accept Merge Request #1845: (feature/库存优化_5 -> develop)
Merge Request: Merge branch 'feature/质检消息优化' into feature/库存优化_5

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1845
2025-02-25 10:33:39 +08:00
yuxianghui
6e94dc45a9 Merge branch 'feature/质检消息优化' into feature/库存优化_5 2025-02-25 10:31:00 +08:00
yuxianghui
7ecb28382a 1、库存序列号的更新序列号功能按钮优化;2、销售单默认显示字段优化; 2025-02-25 10:28:51 +08:00
禹翔辉
7a0b8f0c78 Accept Merge Request #1844: (feature/质检消息优化 -> develop)
Merge Request: Merge branch 'feature/库存优化_3' into feature/质检消息优化

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1844
2025-02-25 09:32:43 +08:00
yuxianghui
ee42b6ae70 Merge branch 'feature/库存优化_3' into feature/质检消息优化 2025-02-25 08:45:54 +08:00
马广威
e8c0011fbc Accept Merge Request #1843: (feature/制造功能优化 -> develop)
Merge Request: 编程单去重

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1843?initial=true
2025-02-24 17:01:31 +08:00
mgw
e2e9637dbf Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-24 16:59:33 +08:00
mgw
3745125990 编程单去重 2025-02-24 16:59:12 +08:00
yuxianghui
83fb8a6625 质检完成消息通知发送失败 优化 2025-02-24 16:53:43 +08:00
廖丹龙
014456f09f Accept Merge Request #1842: (feature/tool_standard_library_process -> 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/1842
2025-02-24 16:50:08 +08:00
liaodanlong
7a3534f8e4 当前工单下一张工单为表面工艺时不显示回退按钮问题处理 2025-02-24 16:39:10 +08:00
廖丹龙
08c7226add Accept Merge Request #1840: (feature/tool_standard_library_process -> 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/1840
2025-02-24 16:31:56 +08:00
禹翔辉
de43411e53 Accept Merge Request #1841: (feature/库存优化_3 -> develop)
Merge Request: Merge branch 'feature/库存功能优化' into feature/库存优化_3

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1841?initial=true
2025-02-24 16:29:40 +08:00
yuxianghui
8c14ea4a85 Merge branch 'feature/库存功能优化' into feature/库存优化_3 2025-02-24 16:27:49 +08:00
yuxianghui
eb11e71d0d 1、入库单据优化:单据就绪自动分配序列号;分配序列号时,流水号取值=MAX(产品序列号模型最大序号,预分配最大序号),界面优化。2、客供料入库单批量调拨有待质检的单子时-提示语显示重复了优化 2025-02-24 16:27:09 +08:00
liaodanlong
71e7ac9993 最后一个解除装夹未隐藏回退按钮 2025-02-24 16:16:00 +08:00
liaodanlong
749df39809 cnc工单没有回退按钮问题 2025-02-24 16:08:22 +08:00
马广威
332ac49229 Accept Merge Request #1839: (feature/制造功能优化 -> develop)
Merge Request: 调整工单名称、编程单去重

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1839?initial=true
2025-02-24 15:29:53 +08:00
mgw
fea58e6288 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-24 15:29:20 +08:00
mgw
28a16ca08e 调整工单名称、编程单去重 2025-02-24 15:29:02 +08:00
马广威
363169d9e4 Accept Merge Request #1838: (feature/制造功能优化 -> develop)
Merge Request: 改正语法问题

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1838?initial=true
2025-02-24 15:15:47 +08:00
mgw
74442c6539 改正语法问题 2025-02-24 15:15:27 +08:00
马广威
d247b25d55 Accept Merge Request #1837: (feature/制造功能优化 -> develop)
Merge Request: 调整领料单生成次数

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1837?initial=true
2025-02-24 15:13:09 +08:00
mgw
b2b6a57f40 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-24 15:12:40 +08:00
mgw
4e7bf40d91 调整领料单生成次数 2025-02-24 15:12:23 +08:00
马广威
2baf739eb2 Accept Merge Request #1836: (feature/制造功能优化 -> develop)
Merge Request: 调整行号取值

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1836?initial=true
2025-02-24 15:00:23 +08:00
mgw
7ab90143b6 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-24 14:59:53 +08:00
mgw
8bf788e39c 调整行号取值 2025-02-24 14:57:19 +08:00
马广威
2e83a22540 Accept Merge Request #1835: (feature/制造功能优化 -> develop)
Merge Request: 调整下游单据内容

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1835?initial=true
2025-02-24 14:47:28 +08:00
mgw
9e25657fea Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-24 14:46:35 +08:00
mgw
555802b271 调整下游单据内容 2025-02-24 14:46:18 +08:00
廖丹龙
4002588eec Accept Merge Request #1834: (feature/tool_standard_library_process -> develop)
Merge Request: sf-生产环境-客供料入库单-合并后的客供料入库单的零件图号显示名称不正确

Created By: @廖丹龙
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1834
2025-02-24 14:05:38 +08:00
liaodanlong
09ba001b06 sf-生产环境-客供料入库单-合并后的客供料入库单的零件图号显示名称不正确 2025-02-24 13:38:02 +08:00
廖丹龙
c208bac3a1 Accept Merge Request #1833: (feature/tool_standard_library_process -> 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/1833
2025-02-24 09:45:27 +08:00
liaodanlong
7efea85420 刀具标准库 基本参数修改字段默认隐藏,并可勾选展示 2025-02-24 09:30:27 +08:00
mgw
91061b1eec 调整调拨产品名称等 2025-02-21 17:34:57 +08:00
廖丹龙
3c349c3346 Accept Merge Request #1832: (feature/tool_standard_library_process -> 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/1832?initial=true
2025-02-21 16:47:25 +08:00
liaodanlong
e1185aa28b 子制造订单回退功能处理 处理解除装夹没有回退按钮 2025-02-21 15:22:00 +08:00
胡尧
e2edd38a8e Accept Merge Request #1831: (feature/mrp_bug_fixed -> develop)
Merge Request: 修复库存有两个驾驶舱菜单的bug

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1831?initial=true
2025-02-21 14:48:34 +08:00
胡尧
16b5ce300a 修复库存有两个驾驶舱菜单的bug 2025-02-21 14:48:01 +08:00
胡尧
70855a4936 Accept Merge Request #1830: (feature/mrp_bug_fixed -> develop)
Merge Request: 取消配送路线关联字段修改

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1830?initial=true
2025-02-21 14:29:46 +08:00
胡尧
1bbdf2bd44 取消配送路线关联字段修改 2025-02-21 14:29:15 +08:00
廖丹龙
3da67f27ec Accept Merge Request #1829: (feature/tool_standard_library_process -> 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/1829?initial=true
2025-02-21 10:30:16 +08:00
liaodanlong
d8e8babbd3 工单详情添加回退按钮 2025-02-21 10:28:12 +08:00
廖丹龙
41deefa4f3 Accept Merge Request #1828: (feature/tool_standard_library_process -> 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/1828
2025-02-21 09:00:21 +08:00
liaodanlong
acfc09596b 刀具编码修改 2025-02-20 17:21:44 +08:00
管欢
5d00c1018b Accept Merge Request #1827: (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/1827
2025-02-20 16:28:20 +08:00
廖丹龙
a438ba9c40 Accept Merge Request #1826: (feature/tool_standard_library_process -> 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/1826
2025-02-20 16:13:43 +08:00
liaodanlong
c3f5a890f1 工厂编码修改 2025-02-20 16:12:59 +08:00
liaodanlong
416a63138a Merge branch 'refs/heads/develop' into feature/tool_standard_library_process
# Conflicts:
#	sf_manufacturing/models/mrp_workorder.py
2025-02-20 15:48:22 +08:00
liaodanlong
75460c5df6 制造订单 工单回退 2025-02-20 15:38:37 +08:00
liaodanlong
162a1061de 制造订单 工单回退 2025-02-20 14:08:06 +08:00
黄焱
cb2777ba3e Accept Merge Request #1825: (feature/前端样式修改 -> develop)
Merge Request: sf-制造-功能刀具标准库字段样式调整

Created By: @黄焱
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @黄焱
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1825?initial=true
2025-02-20 11:37:34 +08:00
hyyy
e6ab17ca0d sf-制造-功能刀具标准库字段样式调整 2025-02-20 11:34:19 +08:00
guanhuan
8e27ee2434 修复取消订单状态已更新时的提示 2025-02-20 10:25:56 +08:00
禹翔辉
1ca61b2346 Accept Merge Request #1824: (feature/库存功能优化 -> develop)
Merge Request: Merge branch 'feature/调拨质检完成消息通知' into feature/库存功能优化

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1824?initial=true
2025-02-20 10:06:34 +08:00
yuxianghui
087896aa1c Merge branch 'feature/调拨质检完成消息通知' into feature/库存功能优化 2025-02-20 10:05:05 +08:00
yuxianghui
7ff1fed4b6 调拨单批量调拨功能,新增调拨单的质检单是否完成校验 2025-02-20 10:04:22 +08:00
禹翔辉
74c747b3ef Accept Merge Request #1823: (feature/调拨质检完成消息通知 -> develop)
Merge Request: Merge branch 'feature/质检返工优化' into feature/调拨质检完成消息通知

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1823
2025-02-19 16:24:26 +08:00
yuxianghui
996f25232b Merge branch 'feature/质检返工优化' into feature/调拨质检完成消息通知 2025-02-19 16:22:51 +08:00
yuxianghui
cddcec4225 添加调拨单的质检完成的消息通知 2025-02-19 16:22:00 +08:00
mgw
a30ee546bd Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-19 15:35:40 +08:00
马广威
045faeae89 Accept Merge Request #1822: (feature/制造功能优化 -> develop)
Merge Request: 调整名称

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1822?initial=true
2025-02-19 15:35:29 +08:00
mgw
064af3b884 调整名称 2025-02-19 15:34:42 +08:00
马广威
a783d54836 Accept Merge Request #1821: (feature/制造功能优化 -> develop)
Merge Request: 修复完善取消订单问题

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1821
2025-02-19 15:15:39 +08:00
mgw
55a118f19a Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-19 15:14:24 +08:00
mgw
0969967570 修复完善取消订单问题 2025-02-19 15:14:05 +08:00
管欢
85f19defa8 Accept Merge Request #1820: (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/1820
2025-02-19 14:50:15 +08:00
guanhuan
a628826b52 采购协议 2025-02-19 11:34:58 +08:00
guanhuan
bdccce07d4 采购协议 2025-02-19 11:31:46 +08:00
guanhuan
4dc19cab81 采购协议 2025-02-19 10:42:48 +08:00
黄焱
986b727d9b Accept Merge Request #1818: (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/1818?initial=true
2025-02-19 10:36:52 +08:00
hyyy
9e6fc3bacb 增加样式权重 2025-02-19 10:29:25 +08:00
黄焱
201bab304d Accept Merge Request #1817: (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/1817
2025-02-18 16:24:30 +08:00
hyyy
6984f31547 删除多余代码 2025-02-18 16:23:25 +08:00
hyyy
6f4f0ab0fb 增加刀具管理的展开列 2025-02-18 15:44:40 +08:00
马广威
a4b40bee16 Accept Merge Request #1816: (feature/制造功能优化 -> develop)
Merge Request: 增加状态映射

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1816?initial=true
2025-02-17 18:00:29 +08:00
mgw
dc29671f1a 增加状态映射 2025-02-17 17:59:38 +08:00
马广威
1ca68d9ff2 Accept Merge Request #1815: (feature/制造功能优化 -> develop)
Merge Request: 调整工单状态改变逻辑

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1815?initial=true
2025-02-17 17:35:57 +08:00
mgw
9fce93e1ef 调整工单状态改变逻辑 2025-02-17 17:34:54 +08:00
mgw
732c2089b9 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-17 17:30:21 +08:00
mgw
09f8f5c0fd 调整状态 2025-02-17 17:30:02 +08:00
禹翔辉
74290a060e Accept Merge Request #1814: (feature/质检返工优化 -> develop)
Merge Request: Merge branch 'feature/库存优化_2' into feature/质检返工优化

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1814
2025-02-17 17:17:18 +08:00
yuxianghui
6388072bed Merge branch 'feature/库存优化_2' into feature/质检返工优化 2025-02-17 17:16:11 +08:00
yuxianghui
71cbd7ac36 1、优化由质检返工生成的制造订单检测结果数据不全的问题;2、 发票账单:增加字段及优化 2025-02-17 17:15:30 +08:00
马广威
c77c6310a2 Accept Merge Request #1813: (feature/制造功能优化 -> develop)
Merge Request: 调整字段名称

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1813?initial=true
2025-02-17 16:21:54 +08:00
mgw
5d13c09468 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-17 16:21:20 +08:00
mgw
f24c5ff75a 调整字段名称 2025-02-17 16:21:01 +08:00
马广威
902857444b Accept Merge Request #1812: (feature/制造功能优化 -> develop)
Merge Request: 完善取消订单功能

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1812?initial=true
2025-02-17 16:16:14 +08:00
mgw
fafa12dd23 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-17 16:12:20 +08:00
mgw
8d9aee497a 完善取消订单功能 2025-02-17 16:11:48 +08:00
黄焱
27820837c4 Accept Merge Request #1811: (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/1811?initial=true
2025-02-17 16:06:47 +08:00
hyyy
69c5996db7 合并销售订单列,修改必填*展示方法, 2025-02-17 16:03:14 +08:00
马广威
1e3f8f005e Accept Merge Request #1810: (feature/制造功能优化 -> develop)
Merge Request: 完善人工编程功能

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1810?initial=true
2025-02-17 15:41:32 +08:00
mgw
5d2cadc3d0 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-17 15:37:58 +08:00
mgw
567ea84b00 完善人工编程功能 2025-02-17 15:37:30 +08:00
禹翔辉
006152edd9 Accept Merge Request #1809: (feature/库存优化_2 -> develop)
Merge Request: Merge branch 'feature/库存优化_1' into feature/库存优化_2

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1809
2025-02-17 15:19:55 +08:00
yuxianghui
60f525e228 Merge branch 'feature/库存优化_1' into feature/库存优化_2 2025-02-17 15:18:49 +08:00
yuxianghui
3d2e383330 1、处理 sf-制造订单-通过质量检查对装夹预调进行返工-制造订单未记录检测结果 问题;2、处理 手动创建调拨单右上角会关联坯料采购单和坯料委外单 问题 2025-02-17 15:17:54 +08:00
胡尧
d25022204b Accept Merge Request #1808: (feature/mrp_bug_fixed -> develop)
Merge Request: 修改配送类型的字段关联形式,修改工件配送列表页的默认筛选条件

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1808?initial=true
2025-02-17 13:24:31 +08:00
胡尧
8095a8a972 修改配送类型的字段关联形式,修改工件配送列表页的默认筛选条件 2025-02-17 13:23:32 +08:00
禹翔辉
781e1d616f Accept Merge Request #1807: (feature/库存优化_1 -> develop)
Merge Request: Merge branch 'feature/同步接口优化' into feature/库存优化_1

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1807?initial=true
2025-02-17 12:44:29 +08:00
yuxianghui
f556e45102 Merge branch 'feature/同步接口优化' into feature/库存优化_1 2025-02-17 12:42:55 +08:00
yuxianghui
fc4eae0488 完成 入库优化:自动确认序列号 需求 2025-02-17 12:42:07 +08:00
mgw
3e9b6f808d 优化取消接单功能 2025-02-17 10:00:40 +08:00
黄焱
3f1c745ece Accept Merge Request #1806: (feature/前端样式修改 -> develop)
Merge Request: 删除模块,移到efms里面;销售订单明细表的视图优化_1

Created By: @黄焱
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @黄焱
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1806?initial=true
2025-02-17 08:39:25 +08:00
hyyy
b117fde8c3 删除模块,移到efms里面,修改样式 2025-02-14 17:31:19 +08:00
mgw
e98a9a3788 增加取消订单其他功能 2025-02-14 17:29:33 +08:00
mgw
3ac096e9a7 取消接单需求进行中 2025-02-13 17:41:18 +08:00
胡尧
3130ef4983 增加工单的开始时间已经leave_id字段的监控,异常则发送企业微信消息 2025-02-13 15:48:41 +08:00
禹翔辉
5463bff63b Accept Merge Request #1805: (feature/同步接口优化 -> develop)
Merge Request: Merge branch 'feature/库存优化' into feature/同步接口优化

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1805
2025-02-13 15:15:28 +08:00
yuxianghui
ea96ae2c55 Merge branch 'feature/库存优化' into feature/同步接口优化 2025-02-13 15:00:25 +08:00
yuxianghui
a05885936d 刀具标准库及关联模型同步接口优化 2025-02-13 14:26:01 +08:00
胡尧
32ead7fa07 去掉设置tree字段为隐藏的代码 2025-02-13 13:29:54 +08:00
胡尧
cf9095b51b Accept Merge Request #1804: (feature/mrp_bug_fixed -> develop)
Merge Request: 增加测试数据清理模块

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1804
2025-02-13 13:04:40 +08:00
胡尧
827311a146 质量检查列表修改3个字段的隐藏默认值 2025-02-13 13:04:16 +08:00
胡尧
941b8c0be7 增加测试数据清理模块 2025-02-13 11:31:35 +08:00
胡尧
f0525355c4 Accept Merge Request #1803: (feature/mrp_bug_fixed -> develop)
Merge Request: 质量检查列表增加默认筛选项

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1803?initial=true
2025-02-13 10:27:43 +08:00
胡尧
bb1012a15d 质量检查列表增加默认筛选项 2025-02-13 10:26:58 +08:00
胡尧
68e9537cdb Accept Merge Request #1802: (feature/mrp_bug_fixed -> develop)
Merge Request: 修改客供料的供货路线不能选择外购

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1802?initial=true
2025-02-12 15:34:34 +08:00
胡尧
2411d8334c 修改客供料的供货路线不能选择外购 2025-02-12 15:33:57 +08:00
胡尧
97d4e03ef8 Accept Merge Request #1801: (feature/mrp_bug_fixed -> develop)
Merge Request: 修改质量检查列表页

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1801?initial=true
2025-02-12 15:23:19 +08:00
胡尧
b7a2a763ac 修改质量检查列表页 2025-02-12 15:14:27 +08:00
禹翔辉
7e0f392550 Accept Merge Request #1800: (feature/库存优化 -> develop)
Merge Request: Merge branch 'feature/销售视图优化' into feature/库存优化

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1800?initial=true
2025-02-12 11:32:03 +08:00
yuxianghui
c223869bbd Merge branch 'feature/销售视图优化' into feature/库存优化 2025-02-12 11:30:27 +08:00
yuxianghui
7a6538bcc1 完成 库存单据列表增加字段【需求数量】 需求 2025-02-12 11:29:46 +08:00
mgw
d3cfec2f35 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-12 09:49:54 +08:00
mgw
6b9428c8bf 多种状态下显示拒绝接单/取消按钮 2025-02-10 16:38:34 +08:00
马广威
f25d06e08f Accept Merge Request #1799: (feature/制造功能优化 -> develop)
Merge Request: 调整工单处人工编程可见

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1799?initial=true
2025-02-10 13:35:06 +08:00
mgw
62314d38b3 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-10 13:34:06 +08:00
mgw
027be2b021 调整工单处人工编程可见 2025-02-10 13:33:46 +08:00
马广威
372dfd99c4 Accept Merge Request #1798: (feature/制造功能优化 -> develop)
Merge Request: 人工线下加工工单标识

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1798?initial=true
2025-02-10 13:15:01 +08:00
mgw
c702d1f3be Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-10 13:14:25 +08:00
mgw
7e33ca44d0 人工线下加工工单标识 2025-02-10 13:13:57 +08:00
马广威
c7d9b15624 Accept Merge Request #1797: (feature/制造功能优化 -> develop)
Merge Request: 增加人工线下加工工单的查找

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1797
2025-02-10 11:30:52 +08:00
mgw
a4d640acb5 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-10 11:29:56 +08:00
mgw
523cc4ac5e 增加人工线下加工工单的查找 2025-02-10 11:29:37 +08:00
马广威
9aa2bd5d54 Accept Merge Request #1796: (feature/制造功能优化 -> develop)
Merge Request: 人工线下加工编程方式设置

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1796
2025-02-10 11:00:17 +08:00
mgw
ae23edb9d5 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-10 10:58:19 +08:00
mgw
9695997542 人工线下加工编程方式设置 2025-02-10 10:58:01 +08:00
管欢
90243a7d2f Accept Merge Request #1795: (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/1795
2025-02-10 09:53:46 +08:00
guanhuan
5dc5602e3d 月结取消订单 2025-02-10 09:47:42 +08:00
胡尧
cbf58fd3fb Accept Merge Request #1794: (feature/mrp_bug_fixed -> develop)
Merge Request: 解决采购总监选择销售员报错的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1794?initial=true
2025-02-10 09:30:49 +08:00
马广威
0ada4933ba Accept Merge Request #1793: (feature/制造功能优化 -> develop)
Merge Request: 人工线下加工默认加工面ZM

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1793?initial=true
2025-02-10 09:14:33 +08:00
mgw
b1261bb91b Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-10 09:13:16 +08:00
mgw
ffde591b3c 人工线下加工默认加工面ZM 2025-02-10 09:12:58 +08:00
胡尧
356e1e55e4 解决采购总监选择销售员报错的问题 2025-02-10 09:03:13 +08:00
马广威
b6ce462171 Accept Merge Request #1792: (feature/制造功能优化 -> develop)
Merge Request: 增加人工线下加工程序写入

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1792?initial=true
2025-02-08 16:49:34 +08:00
mgw
e8b5ff0b75 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-08 16:48:59 +08:00
mgw
ac3b50c534 增加人工线下加工程序写入 2025-02-08 16:48:36 +08:00
yuxianghui
bc4806b303 1、销售订单详情界面列表优化;2、完成 人工线下加工制造订单提交返工时-建议隐藏【保留装夹预调测量数据】 需求 2025-02-08 16:47:06 +08:00
马广威
e7bc434d60 Accept Merge Request #1791: (feature/制造功能优化 -> develop)
Merge Request: 调整程序写入逻辑

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1791?initial=true
2025-02-08 16:01:11 +08:00
mgw
2de501b61d Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-08 16:00:25 +08:00
mgw
976d75973d 调整程序写入逻辑 2025-02-08 16:00:07 +08:00
马广威
e20bf3816a Accept Merge Request #1790: (feature/制造功能优化 -> develop)
Merge Request: 人工线下加工工单也需要走cloud编程流程的优化需求

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1790?initial=true
2025-02-08 14:10:19 +08:00
mgw
44f29d001e 调整人工线下加工页面内容 2025-02-08 14:08:53 +08:00
mgw
aff81f0b3a Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-08 13:32:59 +08:00
mgw
88840abf9f 制造订单编程状态搜索的优化需求 2025-02-08 13:32:40 +08:00
胡尧
332276752a Accept Merge Request #1789: (feature/mrp_bug_fixed -> develop)
Merge Request: 修复bug

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1789?initial=true
2025-02-07 10:54:56 +08:00
胡尧
d852cf54df 修复bug 2025-02-07 10:49:17 +08:00
管欢
6756cc201c Accept Merge Request #1788: (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/1788
2025-02-06 11:14:52 +08:00
guanhuan
06dd9c12e0 人工线下加工工单下发通知修改 2025-02-06 10:43:11 +08:00
mgw
978e427734 调整制造订单集合 2025-02-05 17:44:32 +08:00
mgw
dd35a6ae5f Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-02-05 17:42:10 +08:00
mgw
0a8cc050b8 人工线下加工工单也需要走cloud编程流程的优化需求 2025-02-05 17:41:52 +08:00
管欢
eb47e4c963 Accept Merge Request #1787: (feature/工单返工优化 -> 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/1787
2025-01-23 16:48:24 +08:00
guanhuan
5636681ee6 CNC工单返工 2025-01-23 16:42:16 +08:00
guanhuan
9b49edb290 装夹工单返工 2025-01-23 13:55:35 +08:00
liaodanlong
b604209df4 刀具标准库列表视图添加创建时间与修改时间 2025-01-21 16:23:34 +08:00
liaodanlong
a9b4c5d91b 保持智能工厂刀具标准库与cloud一致 2025-01-21 16:04:16 +08:00
胡尧
39b29960e3 Accept Merge Request #1786: (feature/验证合并的逻辑 -> develop)
Merge Request: Merge branch 'develop' into feature/验证合并的逻辑

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1786?initial=true
2025-01-21 10:40:27 +08:00
胡尧
6abb237491 Merge branch 'develop' into feature/验证合并的逻辑 2025-01-21 10:39:41 +08:00
胡尧
b0c043676c Accept Merge Request #1785: (release/release_2.8 -> develop)
Merge Request: 优化工件下产线,修改CNC加工工单状态为待检测时,同步修改对应质检单状态为待处理

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1785?initial=true
2025-01-21 10:39:19 +08:00
yuxianghui
8f9b7b2fb0 优化工件下产线,修改CNC加工工单状态为待检测时,同步修改对应质检单状态为待处理 2025-01-20 15:39:08 +08:00
mgw
32255af3a3 申请编程状态 2025-01-20 14:53:26 +08:00
yuxianghui
955b6a6213 工单优化 2025-01-20 14:47:09 +08:00
yuxianghui
8e788d3745 处理CNC加工工单通过接口上产线下产线变更为待检测时,对应质检单还是等待状态问题 2025-01-20 14:18:02 +08:00
guanhuan
dc843588e9 制造列表新增编程状态显示 2025-01-20 14:09:23 +08:00
马广威
8bfe5eca03 Accept Merge Request #1783: (feature/制造功能优化 -> develop)
Merge Request: 申请编程状态控制

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1783?initial=true
2025-01-20 14:04:27 +08:00
mgw
dbcf8b1089 申请编程状态控制 2025-01-20 14:02:54 +08:00
yuxianghui
0bf701e743 质检单状态优化 2025-01-20 12:55:12 +08:00
yuxianghui
a96a9f5b75 1、修改工单检测结果只读条件;2、修改质检单完成时同步完成工单判断条件;3、去除只有配置后置三元检测的工单才能看到质检单对应字段、页签的判断条件 2025-01-20 12:47:11 +08:00
yuxianghui
9e2704f726 新增工单送检按钮显示的时候隐藏完成按钮 2025-01-20 10:34:32 +08:00
胡尧
c436bbea46 Merge branch 'release/release_2.8' into feature/验证合并的逻辑 2025-01-17 16:12:27 +08:00
胡尧
47c73ae66e 处理订单行参数说明的精度问题 2025-01-17 15:20:50 +08:00
胡尧
19e1b16122 屏蔽将制造订单的补货组修改为不同的代码 2025-01-17 09:27:38 +08:00
85 changed files with 2242 additions and 1233 deletions

View File

@@ -20,7 +20,7 @@
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['base', 'account'],
'depends': ['base', 'account', 'l10n_cn'],
# always loaded
'data': [

View File

@@ -1,4 +1,4 @@
from odoo import models, fields, api
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
@@ -7,6 +7,14 @@ class CustomAccountMoveLine(models.Model):
_inherit = 'account.move'
_description = "account move line"
fapiao = fields.Char(string='发票号', size=20, copy=False, tracking=True, required=True)
@api.constrains('fapiao')
def _check_fapiao(self):
for record in self:
if record.fapiao and (len(record.fapiao) != 20 or not record.fapiao.isdecimal()):
raise ValidationError(_("Fapiao number is an 20-digit number. Please enter a correct one."))
@api.model_create_multi
def create(self, vals):
for val in vals:

View File

@@ -21,8 +21,8 @@
'web.assets_qweb': [
],
'web.assets_backend': [
'jikimo_frontend/static/src/fields/custom_many2many_checkboxes/*',
'jikimo_frontend/static/src/fields/Many2OneRadioField/*',
# 'jikimo_frontend/static/src/fields/custom_many2many_checkboxes/*',
# 'jikimo_frontend/static/src/fields/Many2OneRadioField/*',
# 移除odoo相关标识
'jikimo_frontend/static/src/bye_odoo/*',
'jikimo_frontend/static/src/scss/custom_style.scss',

View File

@@ -1,3 +1,8 @@
.o_list_renderer .o_list_table tbody > tr > td:not(.o_list_record_selector):not(.o_handle_cell):not(.o_list_button):not(.o_list_record_remove){
border:1px solid #dee2e6 !important;
}
.custom_required_add::before{
content: '*';
color: red;
}

View File

@@ -1,3 +0,0 @@
.many2one_radio_field {
display: inline-block;
}

View File

@@ -1,53 +0,0 @@
/** @odoo-module **/
import { RadioField } from "@web/views/fields/radio/radio_field"; // 导入单选按钮组件
import { registry } from "@web/core/registry";
export class Many2OneRadioField extends RadioField {
// 你可以重写或者添加一些方法和属性
// 例如你可以重写setup方法来添加一些事件监听器或者初始化一些变量
setup() {
super.setup(); // 调用父类的setup方法
// 你自己的代码
}
onImageClick(event) {
// 放大图片逻辑
// 获取图片元素
const img = event.target;
const close = img.nextSibling;
// 实现放大图片逻辑
// 比如使用 CSS 放大
img.parentElement.classList.add('zoomed');
close.classList.add('img_close');
}
onCloseClick(event) {
const close = event.target;
const img = close.previousSibling;
img.parentElement.classList.remove('zoomed');
close.classList.remove('img_close');
}
get items() {
return Many2OneRadioField.getItems(this.props.name, this.props.record);
}
static getItems(fieldName, record) {
switch (record.fields[fieldName].type) {
case "selection":
return record.fields[fieldName].selection;
case "many2one": {
const value = record.preloadedData[fieldName] || [];
return value.map((item) => [item.id, item.display_name, item.image]);
}
default:
return [];
}
}
}
Many2OneRadioField.template = "jikimo_frontend.Many2OneRadioField";
// MyCustomWidget.supportedTypes = ['many2many'];
registry.category("fields").add("many2one_radio", Many2OneRadioField);

View File

@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="jikimo_frontend.Many2OneRadioField" owl="1">
<div
role="radiogroup"
t-attf-class="o_{{ props.orientation }}"
t-att-aria-label="string"
>
<t t-foreach="items" t-as="item" t-key="item[0]">
<div class="form-check o_radio_item many2one_radio_field" aria-atomic="true">
<input
type="radio"
class="form-check-input o_radio_input"
t-att-checked="item[0] === value"
t-att-disabled="props.readonly"
t-att-name="id"
t-att-data-value="item[0]"
t-att-data-index="item_index"
t-att-id="`${id}_${item[0]}`"
t-on-change="() => this.onChange(item)"
/>
<label class="form-check-label o_form_label" t-att-for="`${id}_${item[0]}`" t-esc="item[1]" />
<div t-on-dblclick="onImageClick">
<t>
<img t-att-src="item[2]" width="50" height="50"/>
<div class="close" t-on-click="onCloseClick">×</div>
</t>
</div>
</div>
</t>
</div>
</t>
</templates>

View File

@@ -1,100 +0,0 @@
.processing-capabilities-grid {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 10px;
width: 100%;
}
.grid-item {
display: flex;
align-items: center;
}
.item-content {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
/*控制图片大小*/
.item-icon {
width: 50px;
height: 50px;
margin-bottom: 5px;
margin-top: 15px;
}
.item-label {
font-size: 12px;
word-break: break-word;
}
@media (max-width: 1200px) {
.processing-capabilities-grid {
grid-template-columns: repeat(4, 1fr);
}
}
@media (max-width: 768px) {
.processing-capabilities-grid {
grid-template-columns: repeat(3, 1fr);
}
}
@media (max-width: 480px) {
.processing-capabilities-grid {
grid-template-columns: repeat(2, 1fr);
}
}
.image-preview-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.9);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
opacity: 0;
transition: opacity 0.3s ease;
}
.image-preview-container.show {
opacity: 1;
}
.image-preview {
max-width: 90%;
max-height: 90%;
object-fit: contain;
box-shadow: 0 0 20px rgba(255, 255, 255, 0.2);
border-radius: 5px;
transform: scale(0.9);
transition: transform 0.3s ease;
}
.image-preview-container.show .image-preview {
transform: scale(1);
}
.image-preview-close {
position: absolute;
top: 20px;
right: 30px;
color: #fff;
font-size: 40px;
font-weight: bold;
transition: 0.3s;
cursor: pointer;
opacity: 0.7;
}
.image-preview-close:hover,
.image-preview-close:focus {
opacity: 1;
text-decoration: none;
cursor: pointer;
}

View File

@@ -1,60 +0,0 @@
/** @odoo-module **/
import {Many2ManyCheckboxesField} from "@web/views/fields/many2many_checkboxes/many2many_checkboxes_field";
import {registry} from "@web/core/registry";
export class MyCustomWidget extends Many2ManyCheckboxesField {
setup() {
super.setup();
}
onImageClick(event, src) {
event.preventDefault();
event.stopPropagation();
// 创建预览框
const previewContainer = document.createElement('div');
previewContainer.className = 'image-preview-container';
const previewImg = document.createElement('img');
previewImg.src = src;
previewImg.className = 'image-preview';
// 设置放大的预览图片大小
previewImg.style.width = '600px';
previewImg.style.height = 'auto'; // 保持宽高比
const closeButton = document.createElement('span');
closeButton.innerHTML = '&times;';
closeButton.className = 'image-preview-close';
previewContainer.appendChild(previewImg);
previewContainer.appendChild(closeButton);
document.body.appendChild(previewContainer);
// 添加关闭预览的事件监听器
const closePreview = () => {
previewContainer.classList.remove('show');
setTimeout(() => {
document.body.removeChild(previewContainer);
}, 300);
};
closeButton.addEventListener('click', closePreview);
// 点击预览框外部也可以关闭
previewContainer.addEventListener('click', (e) => {
if (e.target === previewContainer) {
closePreview();
}
});
// 使用 setTimeout 来触发过渡效果
setTimeout(() => {
previewContainer.classList.add('show');
}, 10);
}
}
MyCustomWidget.template = "jikimo_frontend.MyCustomWidget";
registry.category("fields").add("custom_many2many_checkboxes", MyCustomWidget);

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="jikimo_frontend.MyCustomWidget" owl="1">
<div aria-atomic="true" class="many2many_flex processing-capabilities-grid">
<t t-foreach="items" t-as="item" t-key="item[0]">
<div class="grid-item">
<CheckBox
value="isSelected(item)"
disabled="props.readonly"
onChange="(ev) => this.onChange(item[0], ev)"
>
<div class="item-content">
<img t-att-src="item[2]" class="item-icon" t-on-click="(ev) => this.onImageClick(ev, item[2])"/>
<span class="item-label"><t t-esc="item[1]"/></span>
</div>
</CheckBox>
</div>
</t>
</div>
</t>
</templates>

View File

@@ -6,8 +6,9 @@ import {_t} from "@web/core/l10n/translation";
import {FormStatusIndicator} from "@web/views/form/form_status_indicator/form_status_indicator";
import {ListRenderer} from "@web/views/list/list_renderer";
// import {StatusBarField} from "@web/views/fields/statusbar/statusbar_field";
import {FormLabel} from "@web/views/form/form_label";
import { fieldVisualFeedback } from "@web/views/fields/field";
import {Field} from "@web/views/fields/field";
var Dialog = require('web.Dialog');
// var {patch} = require("web.utils") 这句话也行
@@ -51,7 +52,6 @@ const tableRequiredList = [
'product_template_id', 'product_uom_qty', 'price_unit','product_id','product_qty',
'name', 'fault_type', 'maintenance_standards', 'Period'
]
patch(FormStatusIndicator.prototype, 'jikimo_frontend.FormStatusIndicator', {
setup() {
owl.onMounted(() => {
@@ -107,33 +107,7 @@ patch(FormStatusIndicator.prototype, 'jikimo_frontend.FormStatusIndicator', {
}
);
patch(Field.prototype, 'jikimo_frontend.Field', {
setup() {
owl.onMounted(this.setRequired);
return this._super(...arguments);
},
setRequired() {
const id = this.props.id
const isRequired = filedRequiredList[id]
if(id == 'number_of_axles') {
console.log(isRequired)
}
if(isRequired) {
let dom;
dom = $(`label[for=${id}]`)
if(isRequired.multiple && dom.length > 1) {
dom = dom.eq(-1)
dom = dom.parent().parent().next().find('label')
}
if(isRequired.noLabel) {
dom = dom.parent().parent()
}
let t = dom.html()
t = '<i class="c* r" style="color: red;margin-left: -4px">*</i>' + t
dom.html(t)
}
}
})
patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
setup(){
owl.onMounted(() => {
@@ -191,7 +165,33 @@ patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
}
}
})
patch(FormLabel.prototype, 'jikimo_frontend.FormLabel', {
get className() {
const { invalid, empty, readonly } = fieldVisualFeedback(
this.props.fieldInfo.FieldComponent,
this.props.record,
this.props.fieldName,
this.props.fieldInfo
);
const classes = this.props.className ? [this.props.className] : [];
const otherRequired = filedRequiredList[this.props.fieldName]
if(this.props.fieldInfo?.rawAttrs?.class?.indexOf('custom_required') >= 0 || otherRequired) {
classes.push('custom_required_add')
}
if (invalid) {
classes.push("o_field_invalid");
}
if (empty) {
classes.push("o_form_label_empty");
}
if (readonly) {
classes.push("o_form_label_readonly");
}
return classes.join(" ");
}
})
// 根据进度条设置水印
// const statusbar_params = {
@@ -231,7 +231,6 @@ $(function () {
clearInterval(timer)
timer = setInterval(() => {
timer_count++
const dom = $('.custom_required')
let tableDom = $('.table_custom_required')
if (tableDom.length) {
tableDom = tableDom.eq(0).parents('tr').children('.table_custom_required')
@@ -243,17 +242,6 @@ $(function () {
})
clearInterval(timer)
}
if (dom.length) {
dom.each(function () {
const requiredDom = $(this).parent().prev().find('label')
let t = requiredDom.html()
if (t && t.indexOf('c*') < 0) {
t = '<i class="c*" style="color: red;margin-left: -4px">*</i>' + t
}
requiredDom.html(t)
})
clearInterval(timer)
}
if (timer_count == 20) {
clearInterval(timer)
}

View File

@@ -10,7 +10,6 @@
</t>
<!-- 暂存,同一份文件中有问题,拆分后正常工作 -->
<!-- <t t-name="og.web.ListRenderer.Rows" t-inherit="web.ListRenderer.Rows" t-inherit-mode="extension"> -->

View File

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

View File

@@ -24,9 +24,6 @@
# always loaded
'data': [
'security/ir.model.access.csv',
'data/documents_data.xml',
'wizards/upload_file_wizard_view.xml',
'views/views.xml',
],
# only loaded in demonstration mode

View File

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

View File

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

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- 创建采购合同文件夹 -->
<record id="documents_purchase_contracts_folder" model="documents.folder">
<field name="name">采购合同</field>
<field name="description">存放采购合同相关文件</field>
<field name="sequence">10</field>
</record>
</data>
</odoo>

View File

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

View File

@@ -21,12 +21,8 @@ class jikimo_purchase_tier_validation(models.Model):
def button_confirm(self):
for record in self:
# if record.need_validation and record.validation_status != 'validated':
# raise ValidationError(_('此操作需要至少对一条记录进行审批。\n请发起审批申请。'))
if record.state in ['to approve']:
raise ValidationError(_('请先完成审批。'))
# if record.state == 'approved':
# record.state = 'purchase'
res = super(jikimo_purchase_tier_validation, self).button_confirm()
for record in self:
if record.state == 'approved':
@@ -39,45 +35,8 @@ class jikimo_purchase_tier_validation(models.Model):
record.message_subscribe([record.partner_id.id])
return res
# def button_confirm(self):
# self = self.with_context(skip_validation=True)
# return super().button_confirm()
#
# def _check_state_conditions(self, vals):
# self.ensure_one()
# if self._context.get('skip_validation'):
# return False
# return (
# self._check_state_from_condition()
# and vals.get(self._state_field) in self._state_to
# )
def request_validation(self):
for record in self:
error_messages = []
# 检查必填字段
required_fields = {
'partner_ref': '合同名称',
'contract_number': '合同编号'
}
missing_fields = [
name for field, name in required_fields.items()
if not record[field]
]
if missing_fields:
error_messages.append('* 如下字段要求必须填写:%s' % ''.join(missing_fields))
# 检查合同文件
if not record.contract_document_id:
error_messages.append('* 必须点击上传合同文件')
# 如果有任何错误,一次性显示所有错误信息
if error_messages:
raise ValidationError('\n'.join(error_messages))
# 添加通知消息
if hasattr(record, 'message_post'):
current_user = self.env.user.name

View File

@@ -1,2 +0,0 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_ir_attachment_wizard,ir.attachment.wizard,model_ir_attachment_wizard,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_ir_attachment_wizard ir.attachment.wizard model_ir_attachment_wizard base.group_user 1 1 1 1

View File

@@ -1,24 +0,0 @@
<odoo>
<data>
<!--
<template id="listing">
<ul>
<li t-foreach="objects" t-as="object">
<a t-attf-href="#{ root }/objects/#{ object.id }">
<t t-esc="object.display_name"/>
</a>
</li>
</ul>
</template>
<template id="object">
<h1><t t-esc="object.display_name"/></h1>
<dl>
<t t-foreach="object._fields" t-as="field">
<dt><t t-esc="field"/></dt>
<dd><t t-esc="object[field]"/></dd>
</t>
</dl>
</template>
-->
</data>
</odoo>

View File

@@ -23,76 +23,10 @@
<xpath expr="//header/field[@name='state']" position="replace">
<field name="state" widget="statusbar" statusbar_visible="draft,sent,to approve, approved, purchase" readonly="1"/>
</xpath>
<xpath expr="//header/button[last()]" position="after">
<button name="button_cancel" states="draft,to approve,sent,purchase" string="取消" type="object" data-hotkey="x" />
</xpath>
<xpath expr="//header/button[@name='action_rfq_send'][1]" position="before">
<field name="validation_status" invisible="1"/>
<field name="is_upload_contract_file" invisible="1"/>
<button name="upload_contract_file" string="上传合同" type="object" class="oe_highlight" attrs="{'invisible': ['|', '|', ('validation_status', '!=', 'no'), ('is_upload_contract_file', '=', True), ('state', 'not in', ['draft', 'sent'])]}"/>]}"/>
<button name="delete_contract_file" string="删除合同" type="object" class="oe_highlight" attrs="{'invisible': ['|', ('validation_status', '!=', 'no'), ('is_upload_contract_file', '=', False)]}"/>
</xpath>
<xpath expr="//notebook/page[1]" position="before">
<page string="合同" name="contract_documents"
attrs="{'invisible': [('contract_document_id', '=', False)]}"
autofocus="autofocus">
<group>
<group>
<field name="contract_document_id" invisible="1"/>
<field name="contract_file_name" invisible="1"/>
<field name="contract_file"
widget="adaptive_viewer"
filename="contract_file_name"/>
</group>
</group>
</page>
</xpath>
</field>
</record>
<!-- actions opening views on models -->
<!--
<record model="ir.actions.act_window" id="jikimo_purchase_tier_validation.action_window">
<field name="name">jikimo_purchase_tier_validation window</field>
<field name="res_model">jikimo_purchase_tier_validation.jikimo_purchase_tier_validation</field>
<field name="view_mode">tree,form</field>
</record>
-->
<!-- server action to the one above -->
<!--
<record model="ir.actions.server" id="jikimo_purchase_tier_validation.action_server">
<field name="name">jikimo_purchase_tier_validation server</field>
<field name="model_id" ref="model_jikimo_purchase_tier_validation_jikimo_purchase_tier_validation"/>
<field name="state">code</field>
<field name="code">
action = {
"type": "ir.actions.act_window",
"view_mode": "tree,form",
"res_model": model._name,
}
</field>
</record>
-->
<!-- Top menu item -->
<!--
<menuitem name="jikimo_purchase_tier_validation" id="jikimo_purchase_tier_validation.menu_root"/>
-->
<!-- menu categories -->
<!--
<menuitem name="Menu 1" id="jikimo_purchase_tier_validation.menu_1" parent="jikimo_purchase_tier_validation.menu_root"/>
<menuitem name="Menu 2" id="jikimo_purchase_tier_validation.menu_2" parent="jikimo_purchase_tier_validation.menu_root"/>
-->
<!-- actions -->
<!--
<menuitem name="List" id="jikimo_purchase_tier_validation.menu_1_list" parent="jikimo_purchase_tier_validation.menu_1"
action="jikimo_purchase_tier_validation.action_window"/>
<menuitem name="Server to list" id="jikimo_purchase_tier_validation" parent="jikimo_purchase_tier_validation.menu_2"
action="jikimo_purchase_tier_validation.action_server"/>
-->
</data>
</odoo>

View File

@@ -1,2 +1 @@
from . import upload_file_wizard
from . import comment_wizard
from . import comment_wizard

View File

@@ -1,114 +0,0 @@
from odoo import models, fields, api, _
class IrAttachmentWizard(models.TransientModel):
_name = 'ir.attachment.wizard'
_description = '文件上传向导'
attachment = fields.Binary(string='选择文件', required=True)
filename = fields.Char(string='文件名')
res_model = fields.Char()
res_id = fields.Integer()
# def action_upload_file(self):
# self.ensure_one()
# # 首先创建 ir.attachment
# attachment = self.env['ir.attachment'].create({
# 'name': self.filename,
# 'type': 'binary',
# 'datas': self.attachment,
# 'res_model': self.res_model,
# 'res_id': self.res_id,
# })
#
# # 获取默认的文档文件夹
# workspace = self.env['documents.folder'].search([('name', '=', '采购合同')], limit=1)
#
# # 创建 documents.document 记录
# document = self.env['documents.document'].create({
# 'name': self.filename,
# 'attachment_id': attachment.id,
# 'folder_id': workspace.id,
# 'res_model': self.res_model,
# 'res_id': self.res_id,
# })
#
# return {
# 'type': 'ir.actions.client',
# 'tag': 'display_notification',
# 'params': {
# 'title': _('成功'),
# 'message': _('文件上传成功'),
# 'type': 'success',
# }
# }
def action_upload_file(self):
self.ensure_one()
# 获取当前用户的 partner_id
current_partner = self.env.user.partner_id
# 首先创建 ir.attachment
attachment = self.env['ir.attachment'].create({
'name': self.filename,
'type': 'binary',
'datas': self.attachment,
'res_model': self.res_model,
'res_id': self.res_id,
})
# 获取默认的文档文件夹
workspace = self.env['documents.folder'].search([('name', '=', '采购合同')], limit=1)
# 创建 documents.document 记录
document = self.env['documents.document'].create({
'name': self.filename,
'attachment_id': attachment.id,
'folder_id': workspace.id,
'res_model': self.res_model,
'res_id': self.res_id,
'partner_id': current_partner.id,
})
# 更新采购订单的合同文档字段
purchase_order = self.env['purchase.order'].browse(self.res_id)
purchase_order.write({
'contract_document_id': document.id,
'is_upload_contract_file': True
})
# 显示成功消息并关闭向导
message = {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': _('成功'),
'message': _('文件上传成功'),
'type': 'success',
'sticky': False, # 自动消失
'next': {
'type': 'ir.actions.act_window_close'
}
}
}
return message
# def action_upload_file(self):
# self.ensure_one()
# attachment = self.env['ir.attachment'].create({
# 'name': self.filename,
# 'type': 'binary',
# 'datas': self.attachment,
# 'res_model': self.res_model,
# 'res_id': self.res_id,
# })
# return {
# 'type': 'ir.actions.client',
# 'tag': 'display_notification',
# 'params': {
# 'title': _('成功'),
# 'message': _('文件上传成功'),
# 'type': 'success',
# }
# }

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_upload_file_wizard_form" model="ir.ui.view">
<field name="name">ir.attachment.wizard.form</field>
<field name="model">ir.attachment.wizard</field>
<field name="arch" type="xml">
<form string="上传文件">
<group>
<field name="attachment" widget="binary" filename="filename" options="{'accepted_file_extensions': '.pdf,.doc,.docx,.jpg,.jpeg,.png'}"/>
<field name="filename" invisible="1"/>
<field name="res_model" invisible="1"/>
<field name="res_id" invisible="1"/>
</group>
<footer>
<button name="action_upload_file" string="确认上传" type="object" class="btn-primary"/>
<button string="取消" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>
</record>
</odoo>

View File

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

View File

@@ -0,0 +1,32 @@
{
'name': '机企猫 测试助手',
'version': '16.0.1.0.0',
'category': 'Technical',
'summary': '测试数据初始化工具',
'description': """
用于初始化测试环境数据的工具模块
""",
'author': 'Jikimo',
'website': 'www.jikimo.com',
'depends': [
'base',
'sale_management',
'purchase',
'mrp',
'stock',
'account'
],
'data': [
'security/ir.model.access.csv',
'wizards/jikimo_data_clean_wizard.xml',
],
'assets': {
'web.assets_backend': [
'jikimo_test_assistant/static/src/js/data_clean_confirm.js',
],
},
'installable': True,
'application': False,
'auto_install': False,
'license': 'LGPL-3',
}

View File

@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import main

View File

@@ -0,0 +1,86 @@
from odoo import http
import logging
import os
import json
import sys
_logger = logging.getLogger(__name__)
class Main(http.Controller):
@http.route('/api/pdf2image', type='http', auth='public', methods=['POST'], csrf=False)
def convert_pdf_to_image(self, **kwargs):
"""将PDF文件转换为图片文件
Returns:
dict: 包含转换后图片url的字典
"""
res = {}
try:
# 检查poppler是否可用
# if sys.platform.startswith('win'):
# if not os.environ.get('POPPLER_PATH'):
# return {
# 'code': 400,
# 'msg': '请先配置POPPLER_PATH环境变量'
# }
# else:
# import shutil
# if not shutil.which('pdftoppm'):
# return {
# 'code': 400,
# 'msg': '请先安装poppler-utils'
# }
# 获取上传的PDF文件
pdf_file = kwargs.get('file')
if not pdf_file:
res = {'code': 400, 'msg': '未找到上传的PDF文件'}
# 检查文件类型
if not pdf_file.filename.lower().endswith('.pdf'):
res = {'code': 400, 'msg': '请上传PDF格式的文件'}
# 读取PDF文件内容
pdf_content = pdf_file.read()
# 使用pdf2image转换
from pdf2image import convert_from_bytes
import tempfile
# 转换PDF
with tempfile.TemporaryDirectory() as path:
images = convert_from_bytes(pdf_content)
image_urls = []
# 保存每一页为图片
for i, image in enumerate(images):
image_path = os.path.join(path, f'page_{i+1}.jpg')
image.save(image_path, 'JPEG')
# 将图片保存到ir.attachment
with open(image_path, 'rb') as img_file:
attachment = http.request.env['ir.attachment'].sudo().create({
'name': f'page_{i+1}.jpg',
'datas': img_file.read(),
'type': 'binary',
'access_token': kwargs.get('access_token') or '123'
})
image_urls.append({
'page': i+1,
'url': f'/web/content/{attachment.id}'
})
res = {
'code': 200,
'msg': '转换成功',
'data': image_urls
}
except Exception as e:
_logger.error('PDF转换失败: %s', str(e))
res = {
'code': 500,
'msg': f'转换失败: {str(e)}'
}
return json.JSONEncoder().encode(res)

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_jikimo_data_clean_wizard,jikimo_test_assistant.jikimo_data_clean_wizard,model_jikimo_data_clean_wizard,base.group_system,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_jikimo_data_clean_wizard jikimo_test_assistant.jikimo_data_clean_wizard model_jikimo_data_clean_wizard base.group_system 1 1 1 1

View File

@@ -0,0 +1,50 @@
odoo.define('jikimo_test_assistant.action_clean_data_confirm', function (require) {
const core = require('web.core');
const ajax = require('web.ajax');
const Dialog = require('web.Dialog');
var rpc = require('web.rpc');
var _t = core._t;
async function action_clean_data_confirm(parent, {params}) {
let message = "确认清理数据?<br/>"
message += "日期:"+ params.date + "以前<br/>"
message += "模型:" + params.model_names.join('')
const dialog = new Dialog(parent, {
title: "确认",
$content: $('<div>').append(message),
buttons: [
{ text: "确认", classes: 'btn-primary jikimo_button_confirm', close: true, click: () => actionCleanDataConfirm(parent, params) },
{ text: "取消", close: true },
],
});
dialog.open();
async function actionCleanDataConfirm(parent, params) {
rpc.query({
model: 'jikimo.data.clean.wizard',
method: 'action_clean_data',
args: [params.active_id],
kwargs: {
context: params.context,
}
}).then(res => {
parent.services.action.doAction({
'type': 'ir.actions.client',
'tag': 'display_notification',
'target': 'new',
'params': {
'message': '数据清理成功!',
'type': 'success',
'sticky': false,
'next': {'type': 'ir.actions.act_window_close'},
}
});
})
}
}
core.action_registry.add('action_clean_data_confirm', action_clean_data_confirm);
return action_clean_data_confirm;
});

View File

@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import jikimo_data_clean_wizard

View File

@@ -0,0 +1,99 @@
from odoo import models, fields, api
from datetime import datetime
import logging
_logger = logging.getLogger(__name__)
class JikimoDataCleanWizard(models.TransientModel):
_name = 'jikimo.data.clean.wizard'
_description = '业务数据清理'
date = fields.Date(string='截止日期', required=True, default=fields.Date.context_today)
model_ids = fields.Many2many('ir.model', string='业务模型', domain=[
('model', 'in', [
'sale.order', # 销售订单
'purchase.order', # 采购订单
'mrp.production', # 生产订单
'stock.picking', # 库存调拨
'account.move', # 会计凭证
])
])
def action_clean_data(self):
self.ensure_one()
model_list = self.model_ids.mapped('model')
# 销售订单清理(排除已交付,已锁定,已取消)
if 'sale.order' in model_list:
self.model_cancel('sale.order', except_states=['delivered', 'done', 'cancel'])
# 采购订单清理(排除采购订单,已锁定,已取消)
if 'purchase.order' in model_list:
self.model_cancel('purchase.order', except_states=['purchase', 'done', 'cancel'])
# 生产订单清理(排除返工,报废,完成,已取消)
if 'mrp.production' in model_list:
self.model_cancel('mrp.production', except_states=['rework', 'scrap', 'done', 'cancel'])
# 工单清理 (排除返工,完成,已取消)
if 'mrp.workorder' in model_list:
self.model_cancel('mrp.production', except_states=['rework', 'done', 'cancel'])
# 排程单清理 (排除已完成,已取消)
if 'mrp.workorder' in model_list:
self.model_cancel('mrp.production', except_states=['finished', 'cancel'])
# 工单库存移动 (排除完成,已取消)
if 'stock.move' in model_list:
self.model_cancel('stock.move')
# 库存调拨清理 (排除完成,已取消)
if 'stock.picking' in model_list:
self.model_cancel('stock.picking')
# 会计凭证清理 (排除已过账,已取消)
if 'account.move' in model_list:
self.model_cancel('account.move', except_states=['posted', 'cancel'])
return True
def model_cancel(self, model_name, state_field='state', to_state='cancel',except_states=('done', 'cancel')):
table = self.env[model_name]._table
if isinstance(except_states, list):
except_states = tuple(except_states)
sql = """
UPDATE
%s SET %s = '%s'
WHERE
create_date < '%s'
AND state NOT IN %s
""" % (table, state_field, to_state, self.date.strftime('%Y-%m-%d'), except_states)
self.env.cr.execute(sql)
self.env.cr.commit()
@api.model
def get_confirm_message(self):
date_str = self.date.strftime('%Y-%m-%d') if self.date else ''
model_names = ', '.join([model.name for model in self.model_ids])
return {
'date': date_str,
'model_names': model_names
}
def action_clean_data_confirm(self):
model_names = self.model_ids.mapped('display_name')
return {
'type': 'ir.actions.client',
'tag': 'action_clean_data_confirm',
'params': {
'model_names': model_names,
'date': self.date,
'active_id': self.id,
'context': self.env.context
}
}

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Form View -->
<record id="view_jikimo_data_clean_form" model="ir.ui.view">
<field name="name">jikimo.data.clean.wizard.form</field>
<field name="model">jikimo.data.clean.wizard</field>
<field name="arch" type="xml">
<form string="业务数据清理">
<sheet>
<group>
<field name="date"/>
<field name="model_ids" widget="many2many_tags"/>
</group>
</sheet>
<footer>
<button name="action_clean_data_confirm"
string="确认清理"
type="object"
class="btn-primary"/>
<button special="cancel"
string="取消"
class="btn-secondary"/>
</footer>
</form>
</field>
</record>
<!-- Action -->
<record id="action_jikimo_data_clean" model="ir.actions.act_window">
<field name="name">业务数据清理</field>
<field name="res_model">jikimo.data.clean.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<!-- Menu -->
<menuitem id="menu_test_root"
name="测试"
parent="base.menu_custom"
sequence="100"/>
<menuitem id="menu_jikimo_data_clean"
name="业务数据清理"
parent="menu_test_root"
action="action_jikimo_data_clean"
sequence="10"/>
</odoo>

View File

@@ -4,6 +4,7 @@ import json
import logging
from odoo.addons.sf_mrs_connect.controllers.controllers import Sf_Mrs_Connect
from odoo.addons.sf_manufacturing.controllers.controllers import Manufacturing_Connect
from datetime import datetime
_logger = logging.getLogger(__name__)
@@ -30,6 +31,7 @@ class WorkorderExceptionConroller(http.Controller):
workorder = request.env['mrp.workorder'].sudo().search([
('rfid_code', '=', ret['RfidCode']),
('routing_type', '=', 'CNC加工'),
('state', '!=', 'rework')
])
if not workorder:
res = {'Succeed': False, 'ErrorCode': 401, 'Error': '无效的工单'}
@@ -41,7 +43,10 @@ class WorkorderExceptionConroller(http.Controller):
'exception_code': ret.get('coding'),
'exception_content': ret.get('Error', '')
})
# 申请重新编程
workorder.production_id.update_programming_state(trigger_time=datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
reprogramming_reason=ret.get('Error', ''))
workorder.production_id.write({'programming_state': '编程中', 'work_state': '编程中', 'is_rework': False})
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
_logger.info('workder_exception error:%s' % e)

View File

@@ -187,7 +187,7 @@ class CuttingToolModel(models.Model):
def _get_ids(self, cutting_tool_type_code, factory_short_name):
cutting_tool_type_ids = []
for item in cutting_tool_type_code:
cutting_tool_type = self.search([('code', '=', item.replace("JKM", factory_short_name))])
cutting_tool_type = self.search([('code', '=', item)])
if cutting_tool_type:
cutting_tool_type_ids.append(cutting_tool_type.id)
return [(6, 0, cutting_tool_type_ids)]

View File

@@ -9,6 +9,7 @@ function getDomData() {
table.hide()
const thead = customTable.children('thead')
const tbody = customTable.children('tbody')
const tfooter = customTable.children('tfoot')
const tableData = []
const tbody_child = tbody.children()
@@ -16,30 +17,29 @@ function getDomData() {
for (let v = 0; v < tbody_child_len; v++) { // 将数据取出来到tableData里面
const data = tbody_child[v].innerText.split('\t')
// console.log('dom data',data)
const [index, deep, name, Φ, value] = data
tableData.push({index, deep, name, Φ, value})
tableData.push({ index, deep, name, Φ, value })
}
const ΦList = [...new Set(tableData.map(_ => _.name))] // ΦList去重
const ΦList = [...new Set(tableData.map(_ => _.Φ))] // ΦList去重
const newTableData = {}
tableData.forEach(_ => {
const key = _.deep + '|' + _.Φ
!newTableData[key] ? newTableData[key] = {i: _.index} : '';
const key = _.deep + '|' + _.name
!newTableData[key] ? newTableData[key] = { i: _.index } : '';
if (_.Φ) { // 去除没有Φ的脏数据
newTableData[key]['Φ' + _.Φ] = _.value
newTableData[key]['Φ' + _.Φ + 'i'] = _.index
}
})
// console.log('qwdh',tableData, ΦList, newTableData);
// console.log(tableData, ΦList, newTableData);
if (ΦList.filter(_ => _).length == 0) return;
handleThead(thead, ΦList)
handleThead(thead, ΦList, tfooter)
handleTbody(tbody, newTableData, ΦList, table)
handleTbody(tbody, newTableData, ΦList, table )
}
// 重新设置表头、
function handleThead(thead, ΦList) {
function handleThead(thead, ΦList, tfooter) {
const dom = thead.children().eq(0).children()
const len = dom.length
dom.eq(0).attr('rowspan', 2)
@@ -47,7 +47,11 @@ function handleThead(thead, ΦList) {
len == 5 ? dom.eq(2).attr('rowspan', 2) : ''
dom.eq(-2).attr('colspan', ΦList.length)
dom.eq(-1).remove()
if(tfooter && tfooter.length) {
tfooter.children().each(function () {
$(this).children().eq(-1).remove()
})
}
const tr = document.createElement('tr')
for (let v = 0; v < ΦList.length; v++) {
const th = document.createElement('th')
@@ -68,7 +72,6 @@ function handleTbody(tbody, newTableData, ΦList, table) {
// b = b.split('=')[1].split('%')[0]
// return a - b
// })
// console.log('wqoqw ',ΦList)
data.forEach(_ => {
i++
const tr = $('<tr class="o_data_row"></tr>')
@@ -98,61 +101,6 @@ function handleTbody(tbody, newTableData, ΦList, table) {
// // }
tbody.append(tr)
})
// $(document).click(function (e) {
// if ($(e.target).attr('coustomTd')) {
// const orginV = $('[coustomInput=1]').children('input').val()
// $('[coustomInput=1]').parent().html(orginV)
// const v = $(e.target).attr('val')
// console.log($(e.target));
// $(e.target).html('')
// const input = $('<div coustomInput="1" name="feed_per_tooth" class="o_field_widget o_field_char"><input class="o_input" type="text" autocomplete="off" maxlength="20"></div>')
// input.children('input').val(v)
// $(e.target).append(input)
// input.children('input').focus()
// input.children('input').select()
// } else if ($(e.target).attr('coustomInput')) {
//
// } else {
// const orginV = $('[coustomInput=1]').children('input').val()
// $('[coustomInput=1]').parent().html(orginV)
// const v = $(e.target).attr('val')
// }
// })
// $(document).off('change') // 防止重复绑定
// $(document).on('change', '[coustomInput] input', function () {
// $(this).parents('td').attr('val', $(this).val());
// var eve1 = new Event('change');
// var eve2 = new Event('input');
// var eve3 = new Event('click');
// const i = $(this).parents('td').attr('col');
// let patchDom = table.find('tbody').children('tr').eq(i - 1);
//
// if (patchDom.length === 0) {
// console.error('No such row found');
// return;
// }
//
// patchDom = patchDom.children().eq(-1);
//
// setTimeout(() => {
// if (patchDom.length === 0) {
// console.error('No such cell found');
// return;
// }
// patchDom[0].dispatchEvent(eve3); // Simulate click event
//
// setTimeout(() => {
// patchDom = patchDom.find('input');
// if (patchDom.length === 0) {
// console.error('No input found in the target cell');
// return;
// }
// patchDom.val($(this).val());
// patchDom[0].dispatchEvent(eve2);
// patchDom[0].dispatchEvent(eve1);
// }, 200);
// }, 500);
// });
}

View File

@@ -24,4 +24,18 @@
.o_search_panel.account_root {
flex: unset !important;
}
.multi-line {
display: flex;
flex-wrap: nowrap;
> label.o_form_label {
width: 52px;
}
> span {
flex: 1;
}
> div {
flex: 2
}
}

View File

@@ -112,6 +112,8 @@
<field name="cutting_tool_material_id"/>
<field name="cutting_tool_type_id"/>
<field name="brand_id"/>
<field name="create_date" optional="hide"/>
<field name="write_date" string="修改时间" optional="hide"/>
</tree>
</field>
</record>
@@ -138,7 +140,7 @@
<field name="brand_id" required="1"/>
<label for="integral_run_out_accuracy_min" string="端跳精度"
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')]}"/>
<div class="o_address_format"
<div class="o_address_format multi-line"
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')]}">
<label for="integral_run_out_accuracy_min" string="最小"/>
<field name="integral_run_out_accuracy_min" class="o_address_zip"
@@ -177,33 +179,33 @@
</group>
<group string="适配刀片形状"
attrs="{'invisible': [('cutting_tool_type', 'in', ('刀柄','夹头','整体式刀具',False))]}">
<field name="fit_blade_shape_id" string="" widget="many2one_radio"/>
<field name="fit_blade_shape_id" string="" widget="many2one_radio" attrs="{'showExpand': True}"/>
</group>
<group string="适合加工方式"
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
<field name="suitable_machining_method_ids" string=""
widget="custom_many2many_checkboxes"/>
widget="custom_many2many_checkboxes" attrs="{'showExpand': True}"/>
</group>
<group string="刀尖特征"
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
<field name="blade_tip_characteristics_id" string=""
widget="many2one_radio"/>
widget="many2one_radio" attrs="{'showExpand': True}"/>
</group>
<group attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
<group string="柄部类型" attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')]}">
<field name="handle_type_id" string="" widget="many2one_radio"/>
<field name="handle_type_id" string="" widget="many2one_radio" attrs="{'showExpand': True}"/>
</group>
<group string="压紧方式"
attrs="{'invisible': [('cutting_tool_type', 'not in', ('刀杆','刀盘'))]}">
<field name="compaction_way_id" string="" widget="many2one_radio"/>
<field name="compaction_way_id" string="" widget="many2one_radio" attrs="{'showExpand': True}"/>
</group>
</group>
<group attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀杆','刀盘','刀片'))]}">
<group string="走刀方向">
<field name="cutting_direction_ids" string="" widget="custom_many2many_checkboxes"/>
<field name="cutting_direction_ids" string="" widget="custom_many2many_checkboxes" attrs="{'showExpand': True}"/>
</group>
<group string="适合冷却方式">
<field name="suitable_coolant_ids" string="" widget="custom_many2many_checkboxes"/>
<field name="suitable_coolant_ids" string="" widget="custom_many2many_checkboxes" attrs="{'showExpand': True}" />
</group>
</group>
<notebook>
@@ -317,28 +319,28 @@
<field name="knife_handle_basic_parameters_ids"
attrs="{'invisible': [('cutting_tool_type', '!=', '刀柄')]}">
<tree editable="bottom" class="center" delete="1">
<tree editable="bottom" delete="1">
<field name="cutting_tool_type" invisible="1"/>
<field name="name"/>
<field name="taper_shank_model"/>
<field name="total_length"/>
<field name="shank_length"/>
<field name="shank_diameter" class="diameter"/>
<field name="flange_shank_length"/>
<field name="flange_diameter"/>
<field name="diameter_slip_accuracy"/>
<field name="dynamic_balance_class"/>
<field name="flange_shank_length" optional="hide"/>
<field name="flange_diameter" optional="hide"/>
<field name="diameter_slip_accuracy" optional="hide"/>
<field name="dynamic_balance_class" optional="hide"/>
<field name="min_clamping_diameter" class="diameter"/>
<field name="max_clamping_diameter" class="diameter"/>
<field name="max_rotate_speed"/>
<field name="max_rotate_speed" optional="hide"/>
<field name="fit_chuck_size"/>
<field name="nut"/>
<field name="spanner" string="适配锁紧扳手型号"/>
<field name="clamping_mode"/>
<field name="tool_changing_time"/>
<field name="cooling_model"/>
<field name="is_quick_cutting"/>
<field name="is_safe_lock"/>
<field name="nut" optional="hide"/>
<field name="spanner" string="适配锁紧扳手型号" optional="hide"/>
<field name="clamping_mode" optional="hide"/>
<field name="tool_changing_time" optional="hide"/>
<field name="cooling_model" optional="hide"/>
<field name="is_quick_cutting" optional="hide"/>
<field name="is_safe_lock" optional="hide"/>
</tree>
</field>
<field name="chuck_basic_parameters_ids"

View File

@@ -132,6 +132,26 @@ class Sf_Bf_Connect(http.Controller):
request.cr.rollback()
return json.JSONEncoder().encode(res)
@http.route('/api/bfm_cancel_order', type='http', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
def get_bfm_cancel_order(self, **kw):
"""
业务平台取消销售订单
:param kw:
:return:
"""
res = {'status': 1, 'message': '工厂取消销售订单成功'}
logging.info('get_bfm_cancel_order:%s' % kw['order_number'])
try:
sale_order_info = request.env['sale.order'].sudo().search([('name', '=', kw['order_number'])])
sale_order_info._action_cancel()
return json.JSONEncoder().encode(res)
except Exception as e:
logging.error('get_bfm_cancel_order error: %s' % e)
res['status'] = -1
res['message'] = '工厂取消销售订单失败,请联系管理员'
return json.JSONEncoder().encode(res)
class jdElcp(http.Controller):

View File

@@ -72,7 +72,7 @@ class StatusChange(models.Model):
logging.info('函数已经执行=============')
# 使用super()来调用原始方法(在本例中为'sale.order'模型的'action_cancel'方法)
res = super(StatusChange, self).action_cancel()
res = super(StatusChange, self.with_context(disable_cancel_warning=True)).action_cancel()
# 原有方法执行后进行额外的操作如调用外部API
logging.info('函数已经执行=============2')

View File

@@ -27,6 +27,7 @@
'wizard/production_technology_re_adjust_wizard_views.xml',
'wizard/mrp_workorder_batch_replan_wizard_views.xml',
'wizard/sf_programming_reason_views.xml',
'wizard/sale_order_cancel_views.xml',
'views/mrp_views_menus.xml',
'views/agv_scheduling_views.xml',
'views/stock_lot_views.xml',

View File

@@ -596,6 +596,9 @@ class Manufacturing_Connect(http.Controller):
if panel_workorder:
panel_workorder.write({'production_line_state': '已下产线'})
workorder.write({'state': 'to be detected'})
workorder.check_ids.filtered(
lambda ch: ch.quality_state == 'waiting').write(
{'quality_state': 'none'})
else:
res = {'Succeed': False, 'ErrorCode': 204,
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}

View File

@@ -19,12 +19,10 @@ class AgvScheduling(models.Model):
_order = 'id desc'
name = fields.Char('任务单号', index=True, copy=False)
agv_route_id = fields.Many2one('sf.agv.task.route', '任务路线')
def _get_agv_route_type_selection(self):
return self.env['sf.agv.task.route'].fields_get(['route_type'])['route_type']['selection']
agv_route_type = fields.Selection(selection=_get_agv_route_type_selection, string='任务类型', required=True)
agv_route_id = fields.Many2one('sf.agv.task.route', '任务路线')
start_site_id = fields.Many2one('sf.agv.site', '起点接驳站', required=True)
end_site_id = fields.Many2one('sf.agv.site', '终点接驳站', tracking=True)
site_state = fields.Selection([

View File

@@ -1,27 +1,20 @@
# -*- coding: utf-8 -*-
import asyncio
import base64
import cProfile
import concurrent
import datetime
import io
import logging
import json
import os
import pstats
import re
import threading
import time
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import requests
from itertools import groupby
from collections import defaultdict, namedtuple
from odoo import api, fields, models, SUPERUSER_ID, _, tools
from odoo import api, fields, models, SUPERUSER_ID, _
from odoo.exceptions import UserError, ValidationError
from odoo.addons.sf_base.commons.common import Common
from odoo.tools import float_compare, float_round, float_is_zero, format_datetime
class MrpProduction(models.Model):
_inherit = 'mrp.production'
_description = "制造订单"
@@ -242,7 +235,7 @@ class MrpProduction(models.Model):
programming_no = fields.Char('编程单号')
work_state = fields.Char('业务状态')
programming_state = fields.Selection(
[('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'), ('已下发', '已下发')],
[('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'), ('已下发', '已下发'), ('已取消', '已取消')],
string='编程状态',
tracking=True)
glb_file = fields.Binary("glb模型文件")
@@ -272,6 +265,23 @@ class MrpProduction(models.Model):
part_name = fields.Char(string='零件名称', related='product_id.part_name', readonly=True)
# 判断制造的产品类型
production_product_type = fields.Selection([
('成品', '成品'),
('坯料', '坯料'),
('其他', '其他')
], string='产品类型', compute='_compute_production_product_type')
@api.depends('product_id')
def _compute_production_product_type(self):
for record in self:
if record.product_id.categ_id.name == '成品':
record.production_product_type = '成品'
elif record.product_id.categ_id.name == '坯料':
record.production_product_type = '坯料'
else:
record.production_product_type = '其他'
@api.depends('product_id.manual_quotation')
def _compute_manual_quotation(self):
for item in self:
@@ -584,16 +594,19 @@ class MrpProduction(models.Model):
# 编程单更新
# 增加触发时间参数
def update_programming_state(self, trigger_time=None):
def update_programming_state(self, trigger_time=None, reprogramming_reason=None):
try:
manufacturing_type = 'rework'
manufacturing_type = None
if self.is_scrap:
manufacturing_type = 'scrap'
elif self.tool_state == '2':
manufacturing_type = 'invalid_tool_rework'
elif self.is_rework:
manufacturing_type = 'rework'
res = {'programming_no': self.programming_no,
'manufacturing_type': manufacturing_type,
'trigger_time': trigger_time}
'trigger_time': trigger_time,
'reprogramming_reason': reprogramming_reason}
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'])
@@ -650,6 +663,28 @@ class MrpProduction(models.Model):
logging.info('update_programming_state error:%s' % e)
raise UserError("更新编程单状态失败,请联系管理员")
# 修改编程单状态
def _change_programming_state(self):
try:
res = {"programming_no": self.programming_no, "state": "已取消"}
logging.info('res=%s:' % res)
configsettings = self.env['res.config.settings'].get_values()
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
url = '/api/intelligent_programming/set_state'
config_url = configsettings['sf_url'] + url
ret = requests.post(config_url, json=res, data=None, headers=config_header)
ret = ret.json()
result = json.loads(ret['result'])
logging.info('change_programming_state-ret:%s' % result)
if result['status'] == 1:
self.write({'programming_state': '已取消'})
else:
raise UserError(ret['message'])
except Exception as e:
logging.info('change_programming_state error:%s' % e)
raise UserError("修改编程单状态失败,请联系管理员")
# cnc程序获取
def fetchCNC(self, production_names):
cnc = self.env['mrp.production'].search([('id', '=', self.id)])
@@ -662,6 +697,8 @@ class MrpProduction(models.Model):
programme_way = 'manual operation'
else:
programme_way = 'auto'
if cnc.production_type == '人工线下加工':
programme_way = 'manual operation'
if quick_order:
programme_way = 'manual operation'
try:
@@ -754,11 +791,11 @@ class MrpProduction(models.Model):
self.ensure_one()
iot_code = self.env['stock.lot']._get_next_serial(self.company_id, self.product_id) or self.env[
'ir.sequence'].next_by_code('stock.lot.serial')
iot_code_name = re.sub('[\u4e00-\u9fa5]', "", iot_code)
# iot_code_name = re.sub('[\u4e00-\u9fa5]', "", iot_code)
self.lot_producing_id = self.env['stock.lot'].create({
'product_id': self.product_id.id,
'company_id': self.company_id.id,
'name': iot_code_name,
'name': iot_code,
})
if self.move_finished_ids.filtered(lambda m: m.product_id == self.product_id).move_line_ids:
self.move_finished_ids.filtered(
@@ -766,105 +803,64 @@ class MrpProduction(models.Model):
# if self.product_id.tracking == 'serial':
# self._set_qty_producing()
# 重载根据工序生成工单的程序如果产品BOM中没有工序时
# 根据产品对应的模板类型中工序,去生成工单;
# CNC加工工序的选取规则
# 如果自动报价有带过来预分配的机床,
# 则根据设备找到工作中心;否则采用前面描述的工作中心分配机制;
# 其他规则限制: 默认只分配给工作中心状态为非故障的工作中心;
def process_production(self):
workorders_values = []
# production = self.env['mrp.production'].browse(production_id)
product_qty = self.product_uom_id._compute_quantity(self.product_qty,
self.bom_id.product_uom_id)
exploded_boms, dummy = self.bom_id.explode(self.product_id,
product_qty / self.bom_id.product_qty,
picking_type=self.bom_id.picking_type_id)
for bom, bom_data in exploded_boms:
# If the operations of the parent BoM and phantom BoM are the same, don't recreate work orders.
if not (bom.operation_ids and (not bom_data['parent_line'] or bom_data[
'parent_line'].bom_id.operation_ids != bom.operation_ids)):
continue
for operation in bom.operation_ids:
if operation._skip_operation_line(bom_data['product']):
continue
workorders_values += [{
'name': operation.name,
'production_id': self.id,
'workcenter_id': operation.workcenter_id.id,
'product_uom_id': self.product_uom_id.id,
'operation_id': operation.id,
'state': 'pending',
}]
if self.product_id.categ_id.type in ['成品', '坯料']:
# # 根据工序设计生成工单
technology_design_ids = sorted(self.technology_design_ids, key=lambda x: x.sequence)
for route in technology_design_ids:
workorder_has = self.env['mrp.workorder'].search(
[('technology_design_id', '=', route.id), ('production_id', '=', self.id)])
if not workorder_has:
if route.route_id.routing_type not in ['表面工艺']:
workorders_values.append(
self.env['mrp.workorder'].json_workorder_str(self, route))
else:
product_production_process = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', route.process_parameters_id.id)])
workorders_values.append(
self.env[
'mrp.workorder']._json_workorder_surface_process_str(
self, route, product_production_process.seller_ids[0].partner_id.id))
return workorders_values
def _set_workorder_duration_expected(self):
try:
# 在每个线程中创建独立的 ORM 环境
with api.Environment.manage():
new_cr = self.pool.cursor()
self = self.with_env(self.env(cr=new_cr))
# program_ids = self.sudo().env['loyalty.program'].browse(program_ids.ids)
# 使用独立的环境来处理数据库事务
# 在独立的环境中对 workorder 进行操作
production = self.sudo().env['mrp.production'].browse(self.id)
workorders_values = production.process_production()
production.write({'workorder_ids': workorders_values})
for workorder in production.workorder_ids:
workorder.duration_expected = workorder._get_duration_expected()
# 可以进行其他与工作单相关的操作
# workorder_env.write({'...'})
return production
except Exception as e:
logging.error(f"Error processing workorder {workorder.id}: {e}")
# workorders_values = self.process_production(self)
# print('_set_workorder_duration_expected wqio ', self)
# self.write({'workorder_ids': workorders_values})
# for workorder in self.workorder_ids:
# workorder.duration_expected = workorder._get_duration_expected()
def _create_workorder3(self, item):
with ThreadPoolExecutor(max_workers=os.cpu_count()) as executor:
futures = []
for production in self:
if not production.bom_id or not production.product_id:
for production in self:
if not production.bom_id or not production.product_id:
continue
workorders_values = []
product_qty = production.product_uom_id._compute_quantity(production.product_qty,
production.bom_id.product_uom_id)
exploded_boms, dummy = production.bom_id.explode(production.product_id,
product_qty / production.bom_id.product_qty,
picking_type=production.bom_id.picking_type_id)
for bom, bom_data in exploded_boms:
# If the operations of the parent BoM and phantom BoM are the same, don't recreate work orders.
if not (bom.operation_ids and (not bom_data['parent_line'] or bom_data[
'parent_line'].bom_id.operation_ids != bom.operation_ids)):
continue
# 提交每个生产任务到线程池
futures.append(executor.submit(production._set_workorder_duration_expected))
# 等待所有线程完成任务
results = []
for future in futures:
try:
result = future.result()
if result:
results.append(result)
except Exception as e:
logging.error(f"Error processing production: {e}")
return results
for operation in bom.operation_ids:
if operation._skip_operation_line(bom_data['product']):
continue
workorders_values += [{
'name': operation.name,
'production_id': production.id,
'workcenter_id': operation.workcenter_id.id,
'product_uom_id': production.product_uom_id.id,
'operation_id': operation.id,
'state': 'pending',
}]
if production.product_id.categ_id.type in ['成品', '坯料']:
# # 根据工序设计生成工单
technology_design_ids = sorted(production.technology_design_ids, key=lambda x: x.sequence)
for route in technology_design_ids:
workorder_has = self.env['mrp.workorder'].search(
[('technology_design_id', '=', route.id), ('production_id', '=', production.id)])
if not workorder_has:
if route.route_id.routing_type not in ['表面工艺']:
workorders_values.append(
self.env['mrp.workorder'].json_workorder_str(production, route))
else:
product_production_process = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', route.process_parameters_id.id)])
workorders_values.append(
self.env[
'mrp.workorder']._json_workorder_surface_process_str(
production, route, product_production_process.seller_ids[0].partner_id.id))
production.workorder_ids = workorders_values
for workorder in production.workorder_ids:
workorder.duration_expected = workorder._get_duration_expected()
# 外协出入库单处理
def get_subcontract_pick_purchase(self,productions):
production_all = productions.sorted(lambda x: x.id)
def get_subcontract_pick_purchase(self):
production_all = self.sorted(lambda x: x.id)
product_id_to_production_names = {}
grouped_product_ids = {k: list(g) for k, g in
groupby(production_all, key=lambda x: x.product_id.id)}
@@ -873,10 +869,9 @@ class MrpProduction(models.Model):
sorted_workorders = None
for production in production_all:
proc_workorders = []
process_parameter_workorder=production.workorder_ids.filtered(lambda w: w.surface_technics_parameters_id and w.is_subcontract and w.state!='cancel')
# process_parameter_workorder = self.env['mrp.workorder'].search(
# [('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id),
# ('is_subcontract', '=', True), ('state', '!=', 'cancel')], order='sequence asc')
process_parameter_workorder = self.env['mrp.workorder'].search(
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id),
('is_subcontract', '=', True), ('state', '!=', 'cancel')], order='sequence asc')
if process_parameter_workorder:
# 将这些特殊表面工艺工单的采购单与调拨单置为失效
for workorder in process_parameter_workorder:
@@ -991,49 +986,48 @@ class MrpProduction(models.Model):
if purchase_order_line:
line.unlink()
def _process_reset_work_order_sequence(self,rec):
workorder_ids = rec.workorder_ids
technology_design_ids = rec.technology_design_ids
if workorder_ids.filtered(lambda item: item.state in ('返工', 'rework')):
# 获取返工后新生成的工单
work_ids = workorder_ids.filtered(lambda item: item.sequence == 0)
# 对工单进行逐个插入
for work_id in work_ids:
order_rework_ids = rec.workorder_ids.filtered(
lambda item: (item.sequence > 0 and work_id.name == item.name
and work_id.processing_panel == item.processing_panel))
order_rework_ids = sorted(order_rework_ids, key=lambda item: item.sequence, reverse=True)
work_id.sequence = order_rework_ids[0].sequence + 1
# 对该工单之后的工单工序进行加一
work_order_ids = rec.workorder_ids.filtered(
lambda item: item.sequence >= work_id.sequence and item.id != work_id.id)
for work in work_order_ids:
work.sequence = work.sequence + 1
else:
# 将工艺设计生成的工单序号赋值给工单的序号
for work in workorder_ids:
td_ids = technology_design_ids.filtered(
lambda item: (item.route_id.name in work.name and item.process_parameters_id
and item.process_parameters_id == work.surface_technics_parameters_id) or
(item.route_id.name == work.name and item.panel
and item.panel == work.processing_panel))
if work.name == '人工线下加工':
td_ids = technology_design_ids.filtered(lambda item: (item.route_id.name in work.name))
if td_ids:
work.sequence = td_ids[0].sequence
cancel_work_ids = workorder_ids.filtered(lambda item: item.state in ('已取消', 'cancel'))
if cancel_work_ids:
sequence = max(workorder_ids.filtered(lambda item: item.state not in ('已取消', 'cancel')),
key=lambda w: w.sequence).sequence
for cw in cancel_work_ids:
cw.sequence = sequence + 1
def _reset_work_order_sequence(self,productions):
def _reset_work_order_sequence(self):
"""
工单工序排序方法(新)
"""
for rec in productions:
self._process_reset_work_order_sequence(rec)
for rec in self:
workorder_ids = rec.workorder_ids
technology_design_ids = rec.technology_design_ids
if workorder_ids.filtered(lambda item: item.state in ('返工', 'rework')):
# 获取返工后新生成的工单
work_ids = workorder_ids.filtered(lambda item: item.sequence == 0)
# 对工单进行逐个插入
for work_id in work_ids:
order_rework_ids = rec.workorder_ids.filtered(
lambda item: (item.sequence > 0 and work_id.name == item.name
and work_id.processing_panel == item.processing_panel))
order_rework_ids = sorted(order_rework_ids, key=lambda item: item.sequence, reverse=True)
work_id.sequence = order_rework_ids[0].sequence + 1
# 对该工单之后的工单工序进行加一
work_order_ids = rec.workorder_ids.filtered(
lambda item: item.sequence >= work_id.sequence and item.id != work_id.id)
for work in work_order_ids:
work.sequence = work.sequence + 1
else:
# 将工艺设计生成的工单序号赋值给工单的序号
for work in workorder_ids:
td_ids = technology_design_ids.filtered(
lambda item: (item.route_id.name in work.name and item.process_parameters_id
and item.process_parameters_id == work.surface_technics_parameters_id) or
(item.route_id.name == work.name and item.panel
and item.panel == work.processing_panel))
if work.name == '人工线下加工':
td_ids = technology_design_ids.filtered(lambda item: (item.route_id.name in work.name))
if td_ids:
work.sequence = td_ids[0].sequence
cancel_work_ids = workorder_ids.filtered(lambda item: item.state in ('已取消', 'cancel'))
if cancel_work_ids:
sequence = max(workorder_ids.filtered(lambda item: item.state not in ('已取消', 'cancel')),
key=lambda w: w.sequence).sequence
for cw in cancel_work_ids:
cw.sequence = sequence + 1
def _reset_work_order_sequence_1(self):
"""
工单工序排序方法(旧)
@@ -1129,9 +1123,9 @@ class MrpProduction(models.Model):
# 创建工单并进行排序
def _create_workorder(self, item):
productions = self._create_workorder3(item)
self._reset_work_order_sequence(productions)
return productions
self._create_workorder3(item)
self._reset_work_order_sequence()
return True
def production_process(self, pro_plan):
type_map = {'装夹预调': False, 'CNC加工': False, '解除装夹': False}
@@ -1304,6 +1298,7 @@ class MrpProduction(models.Model):
'target': 'new',
'context': {
'default_production_id': self.id,
'default_is_clamping': True if self.workorder_ids.filtered(lambda wk: wk.routing_type == '装夹预调') else False,
'default_workorder_ids': workorder_ids.ids if workorder_ids.ids != [] else self.workorder_ids.ids,
'default_hidden_workorder_ids': ','.join(map(str, work_id_list)) if work_id_list != [] else '',
'default_reprogramming_num': cloud_programming.get('reprogramming_num') if cloud_programming else '',
@@ -1342,7 +1337,7 @@ class MrpProduction(models.Model):
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加工')
'pending'] and m1.processing_panel == rework_item.processing_panel and m1.routing_type in ['CNC加工', '人工线下加工'])
if not pending_workorder.cnc_ids:
production.get_new_program(rework_item.processing_panel)
# production.write({'state': 'progress', 'programming_state': '已编程', 'is_rework': False})
@@ -1352,6 +1347,7 @@ class MrpProduction(models.Model):
# 对制造订单所以面的cnc工单的程序用刀进行校验
try:
logging.info(f'已更新制造订单:{productions_not_delivered}')
productions = productions.filtered(lambda p: p.production_type == '自动化产线加工')
productions.production_cnc_tool_checkout()
except Exception as e:
logging.info(f'对cnc工单的程序用刀进行校验报错{e}')
@@ -1384,7 +1380,7 @@ class MrpProduction(models.Model):
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 (
pw: pw.processing_panel == processing_panel and pw.routing_type in ['CNC加工', '人工线下加工'] and pw.state not in (
'rework', 'done'))
if panel_workorder:
if panel_workorder.cmm_ids:
@@ -1410,7 +1406,7 @@ class MrpProduction(models.Model):
'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 (
ap: ap.routing_type in ['装夹预调', '人工线下加工'] and ap.processing_panel == processing_panel and ap.state not in (
'rework', 'done'))
if pre_workorder:
pre_workorder.write(
@@ -1724,13 +1720,13 @@ class MrpProduction(models.Model):
url = '/api/intelligent_programming/reset_state_again'
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('update_programming_state-ret:%s' % result)
if result['status'] == 1:
self.write({'is_rework': True})
else:
raise UserError(ret['message'])
# ret = ret.json()
# 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('update_programming_state error:%s' % e)
raise UserError("更新编程单状态失败,请联系管理员")
@@ -1748,7 +1744,7 @@ class sf_programming_record(models.Model):
programming_method = fields.Selection([
('auto', '自动'),
('manual operation', '人工')], string="编程方式")
current_programming_count = fields.Integer('当前编程次数')
current_programming_count = fields.Integer('重新编程次数')
target_production_id = fields.Char('目标制造单号')
apply_time = fields.Datetime('申请时间')
send_time = fields.Datetime('下发时间')
@@ -1806,5 +1802,3 @@ class sf_processing_panel(models.Model):
name = fields.Char('加工面')
active = fields.Boolean('有效', default=True)

View File

@@ -17,10 +17,10 @@ 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'
_description = '工单'
product_tmpl_name = fields.Char('坯料产品名称', related='production_bom_id.bom_line_ids.product_id.name')
@@ -69,6 +69,68 @@ class ResMrpWorkOrder(models.Model):
delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效',
tracking=True)
back_button_display = fields.Boolean(default=False, compute='_compute_back_button_display', store=True)
@api.depends('state')
def _compute_back_button_display(self):
for record in self:
sorted_workorders = record.production_id.workorder_ids.filtered(lambda w: w.state != 'cancel').sorted(
key=lambda w: w.sequence)
if not sorted_workorders:
continue
position = next((idx for idx, workorder in enumerate(sorted_workorders) if workorder.id == record.id), -1)
cur_workorder = sorted_workorders[position]
if position == len(sorted_workorders) - 1:
picking_ids = cur_workorder.production_id.sale_order_id.picking_ids
finished_product_area = picking_ids.filtered(
lambda picking: picking.location_dest_id.name == '成品存货区' and picking.state == 'done'
)
if finished_product_area:
moves = self.env['stock.move'].search([
('name', '=', cur_workorder.production_id.name),
('state', '!=', 'cancel')
])
finish_move = next((move for move in moves if move.location_dest_id.name == '制造后'), None)
if not finish_move and not cur_workorder.is_subcontract and not cur_workorder.routing_type == '解除装夹':
record.back_button_display = True
else:
record.back_button_display = any(
finish_move.move_dest_ids.ids not in move.ids and record.state == 'done'
for picking in finished_product_area
for move in picking.move_ids
)
else:
if record.state == 'done':
record.back_button_display = True
else:
record.back_button_display = False
# tag_type
if cur_workorder.is_subcontract or cur_workorder.routing_type == '解除装夹' or cur_workorder.routing_type == '切割' or any(
detection_result.processing_panel == cur_workorder.processing_panel and
detection_result.routing_type == cur_workorder.routing_type and
cur_workorder.tag_type !='重新加工' and
detection_result.test_results != '合格'
for detection_result in cur_workorder.production_id.detection_result_ids
):
record.back_button_display = False
else:
next_workorder = sorted_workorders[position + 1]
next_state = next_workorder.state
if (next_state == 'ready' or (
next_workorder.state == 'waiting' and next_workorder.is_subcontract)) and cur_workorder.state == 'done':
record.back_button_display = True
else:
record.back_button_display = False
if cur_workorder.is_subcontract or cur_workorder.routing_type == '解除装夹' or cur_workorder.routing_type == '切割' or any(
detection_result.processing_panel == cur_workorder.processing_panel and
detection_result.routing_type == cur_workorder.routing_type and
cur_workorder.tag_type !='重新加工' and
detection_result.test_results != '合格'
for detection_result in cur_workorder.production_id.detection_result_ids
):
record.back_button_display = False
date_planned_start = fields.Datetime(tracking=True)
@api.depends('processing_panel')
def _compute_processing_panel_selection(self):
@@ -85,6 +147,82 @@ class ResMrpWorkOrder(models.Model):
manual_quotation = fields.Boolean('人工编程', default=False, compute=_compute_manual_quotation, store=True)
def button_back(self):
if self.production_id.state == 'rework':
raise UserError('制造订单为返工时不能进行工单回退')
sorted_workorders = self.production_id.workorder_ids.filtered(lambda w: w.state != 'cancel').sorted(
key=lambda w: w.sequence)
position = next((idx for idx, workorder in enumerate(sorted_workorders) if workorder.id == self.id), -1)
cur_workorder = sorted_workorders[position]
if position == len(sorted_workorders) - 1:
# 末工序
picking_ids = cur_workorder.production_id.sale_order_id.picking_ids
finished_product_area = picking_ids.filtered(
lambda picking: picking.location_dest_id.name == '成品存货区' and picking.state == 'done'
)
moves = self.env['stock.move'].search([
('name', '=', cur_workorder.production_id.name),
('state', '!=', 'cancel')
])
finish_move = next((move for move in moves if move.location_dest_id.name == '制造后'), None) or []
if any(
finish_move.move_dest_ids.ids in move.ids
for picking in finished_product_area
for move in picking.move_ids
):
raise UserError('已入库,无法回退')
else:
moves = self.env['stock.move'].search([
('name', '=', cur_workorder.production_id.name),
('state', '!=', 'cancel')
])
move_lines = self.env['stock.move.line'].search([
('reference', '=', cur_workorder.production_id.name),
('state', '!=', 'cancel')
])
moves.state = 'assigned'
external_assistance = move_lines.filtered(
lambda picking: picking.location_id.name != '外协线边仓'
)
external_assistance.state = 'assigned'
# move_lines.state = 'assigned'
self.time_ids.date_end = None
cur_workorder.state = 'progress'
cur_workorder.production_id.state = 'progress'
quality_check = self.env['quality.check'].search(
[('workorder_id', '=', self.id)])
for check_order in quality_check:
if check_order.point_id.is_inspect:
check_order.quality_state = 'waiting'
else:
check_order.quality_state = 'none'
# move_dest_ids
finished_quants = moves.mapped('move_line_ids.lot_id.quant_ids')
finished_quants.quantity = 0
finish_move = next((move for move in moves if move.location_dest_id.name == '制造后'), None)
finish_move.move_dest_ids.reserved_availability = 0
finish_move.move_dest_ids.move_line_ids.state = 'draft'
finish_move.move_dest_ids.move_line_ids.unlink()
# finish_move.move_dest_ids.move_line_ids.reserved_uom_qty = 0
else:
next_workorder = sorted_workorders[position + 1]
next_state = next_workorder.state
if next_state not in ['pending', 'waiting', 'ready']:
raise UserError('下工序已经开始,无法回退')
if next_workorder.is_subcontract:
next_workorder.picking_ids.write({'state': 'waiting'})
next_workorder.state = 'pending'
self.time_ids.date_end = None
cur_workorder.state = 'progress'
cur_workorder.production_id.state = 'progress'
quality_check = self.env['quality.check'].search(
[('workorder_id', '=', self.id)])
for check_order in quality_check:
if check_order.point_id.is_inspect:
check_order.quality_state = 'waiting'
else:
check_order.quality_state = 'none'
def _compute_working_users(self):
super()._compute_working_users()
for item in self:
@@ -155,7 +293,7 @@ class ResMrpWorkOrder(models.Model):
surface_technics_parameters_id = fields.Many2one('sf.production.process.parameter', string="表面工艺可选参数")
picking_ids = fields.Many2many('stock.picking', string='外协出入库单',
compute='_compute_surface_technics_picking_ids')
compute='_compute_surface_technics_picking_ids', store=True)
purchase_id = fields.Many2many('purchase.order', string='外协采购单')
surface_technics_picking_count = fields.Integer("外协出入库", compute='_compute_surface_technics_picking_ids')
@@ -456,6 +594,33 @@ class ResMrpWorkOrder(models.Model):
detailed_reason = fields.Text('详细原因')
is_rework = fields.Boolean(string='是否返工', default=False)
# rework_flag = fields.Boolean(string='返工标志', compute='_compute_rework_flag')
#
# @api.depends('state', 'production_line_state')
# def _compute_rework_flag(self):
# for record in self:
# if record.state == 'done' and record.routing_type == '装夹预调':
# next_workorder = record.production_id.workorder_ids.filtered(
# lambda w: w.sequence == record.sequence + 1)
# if next_workorder and next_workorder.routing_type == 'CNC加工' and next_workorder.state in ['ready',
# 'waiting',
# 'pending'] and next_workorder.production_line_state == '待上产线':
# record.rework_flag = False
# elif next_workorder and next_workorder.routing_type == '表面工艺' and next_workorder.state in ['ready',
# 'waiting',
# 'pending']:
# record.rework_flag = False
# else:
# record.rework_flag = True
# else:
# record.rework_flag = True
#
# def button_rework(self):
# for item in self:
# item.state = 'progress'
# for time_id in item.time_ids:
# time_id.write({'date_end': None})
def button_change_env(self):
self.is_test_env = not self.is_test_env
@@ -1061,7 +1226,11 @@ class ResMrpWorkOrder(models.Model):
and workorder.production_id.schedule_state == '已排'
and len(workorder.production_id.picking_ids.filtered(
lambda w: w.state not in ['done', 'cancel'])) == 0):
# and workorder.production_id.programming_state == '已编程'
if workorder.is_subcontract is True:
if workorder.production_id.state == 'rework':
workorder.state = 'waiting'
continue
purchase_orders_id = self._get_surface_technics_purchase_ids()
if purchase_orders_id.state == 'purchase':
workorder.state = 'ready'
@@ -1076,6 +1245,9 @@ class ResMrpWorkOrder(models.Model):
else:
workorder.state = 'waiting'
continue
elif workorder.routing_type == '人工线下加工':
if workorder.production_id.programming_state == '已编程':
workorder.state = 'ready'
else:
workorder.state = 'ready'
continue
@@ -1117,6 +1289,7 @@ class ResMrpWorkOrder(models.Model):
mo.get_move_line(workorder.production_id, workorder))
else:
workorder.state = 'waiting'
# 重写工单开始按钮方法
def button_start(self):
# 判断工单状态是否为等待组件
@@ -1283,7 +1456,8 @@ class ResMrpWorkOrder(models.Model):
'detailed_reason': record.detailed_reason,
'processing_panel': record.processing_panel,
'routing_type': record.routing_type,
'handle_result': '待处理' if record.test_results in ['返工', '报废'] or record.is_rework is True else '',
'handle_result': '待处理' if record.test_results in ['返工',
'报废'] 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
@@ -1300,7 +1474,8 @@ class ResMrpWorkOrder(models.Model):
raise UserError('请先完成该工单的工艺外协再进行操作')
# 表面工艺外协,最后一张工单
workorders = self.production_id.workorder_ids
subcontract_workorders = workorders.filtered(lambda wo: wo.is_subcontract == True and wo.state != 'cancel').sorted('sequence')
subcontract_workorders = workorders.filtered(
lambda wo: wo.is_subcontract == True and wo.state != 'cancel').sorted('sequence')
if self == subcontract_workorders[-1]:
# 给下一个库存移动就绪
self.move_subcontract_workorder_ids[0].move_dest_ids._action_done()
@@ -1357,7 +1532,8 @@ class ResMrpWorkOrder(models.Model):
# ('state', '!=', 'done')])
# if raw_move:
# raw_move.write({'state': 'done'})
record.production_id.button_mark_done1()
if record.production_id.state != 'rework':
record.production_id.button_mark_done1()
# record.production_id.state = 'done'
# ============工单完成,修改对应[质检单]的值=====================
@@ -1523,6 +1699,7 @@ class ResMrpWorkOrder(models.Model):
# 根据工单对应的【作业_个性化记录】配置页签
if any(item.code == 'PTD' for item in mw.routing_workcenter_id.individuation_page_ids):
mw.individuation_page_PTD = True
# =============================================================================================
is_inspect = fields.Boolean('需送检', compute='_compute_is_inspect', store=True, default=False)
@@ -1543,7 +1720,8 @@ class ResMrpWorkOrder(models.Model):
# 修改工单状态
self.write({'state': 'to be detected'})
# 若关联的【质量检查_需送检】=true则质量检查单的状态从“等待”更新为“待处理”
self.check_ids.filtered(lambda ch: ch.is_inspect is True).write({'quality_state': 'none'})
self.check_ids.filtered(lambda ch: ch.is_inspect is True and ch.quality_state == 'waiting').write(
{'quality_state': 'none'})
class CNCprocessing(models.Model):
@@ -1831,10 +2009,9 @@ class WorkPieceDelivery(models.Model):
feeder_station_destination_id = fields.Many2one('sf.agv.site', '目的接驳站')
task_delivery_time = fields.Datetime('任务下发时间')
task_completion_time = fields.Datetime('任务完成时间')
def _get_agv_route_type_selection(self):
return self.env['sf.agv.task.route'].fields_get(['route_type'])['route_type']['selection']
type = fields.Selection(selection=_get_agv_route_type_selection, string='类型')
delivery_duration = fields.Float('配送时长', compute='_compute_delivery_duration')
status = fields.Selection(

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import re
from collections import defaultdict
from odoo import api, fields, models, _
@@ -109,15 +109,37 @@ class PurchaseOrder(models.Model):
class PurchaseOrderLine(models.Model):
_inherit = 'purchase.order.line'
part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True)
part_number = fields.Char('零件图号', store=True, compute='_compute_related_product')
part_name = fields.Char('零件名称', store=True,
compute='_compute_related_product')
related_product = fields.Many2one('product.product', string='关联产品',
help='经此产品工艺加工成的成品')
# @api.depends('order_id.origin')
# def _compute_related_product(self):
# for record in self:
# if record.product_id.detailed_type:
# production_id = self.env['mrp.production'].search([('name', '=', record.order_id.origin)])
# record.related_product = production_id.product_id if production_id else False
# else:
# record.related_product = False
@api.depends('product_id')
def _compute_related_product(self):
for record in self:
if record.product_id.categ_id.name == '坯料':
product_name = ''
match = re.search(r'(S\d{5}-\d)', record.product_id.name)
# 如果匹配成功,提取结果
if match:
product_name = match.group(0)
sale_order_name = ''
match_sale = re.search(r'S(\d+)', record.product_id.name)
if match_sale:
sale_order_name = match_sale.group(0)
sale_order = self.env['sale.order'].sudo().search(
[('name', '=', sale_order_name)])
if sale_order:
filtered_order_line = sale_order.order_line.filtered(
lambda order_line: re.search(f'{product_name}$', order_line.product_id.name)
)
record.part_number = filtered_order_line.product_id.part_number if filtered_order_line else None
record.part_name = filtered_order_line.product_id.part_name if filtered_order_line else None
else:
record.part_number = record.product_id.part_number
record.part_name = record.product_id.part_name
# if record.product_id.detailed_type:
# production_id = self.env['mrp.production'].search([('name', '=', record.order_id.origin)])
# record.related_product = production_id.product_id if production_id else False
# else:
# record.related_product = False

View File

@@ -3,5 +3,6 @@ from odoo import fields, models, api
class QualityCheck(models.Model):
_inherit = "quality.check"
_description = "质量检查"
is_inspect = fields.Boolean('需送检')

View File

@@ -1,6 +1,6 @@
import logging
import json
from odoo import models, fields, api
from odoo import models, fields, api, _
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
@@ -24,6 +24,8 @@ class SaleOrder(models.Model):
self.state = 'supply method'
def action_confirm(self):
if self._get_forbidden_state_confirm() & set(self.mapped('state')):
raise UserError(_('订单状态已发生变化,请刷新当前页面'))
# 判断是否所有产品都选择了供货方式
filter_line = self.order_line.filtered(lambda line: not line.supply_method)
if filter_line:
@@ -149,6 +151,23 @@ class SaleOrder(models.Model):
product_bom_purchase.with_user(self.env.ref("base.user_admin")).bom_create_line_has(
purchase_embryo)
return super(SaleOrder, self).action_confirm()
def action_show_cancel_wizard(self):
wizard = self.env['sf.sale.order.cancel.wizard'].create({
'order_id': self.id,
})
# 创建关联单据行
self.env['sf.sale.order.cancel.line'].create_from_order(wizard.id, self)
return {
'name': '取消销售订单',
'type': 'ir.actions.act_window',
'res_model': 'sf.sale.order.cancel.wizard',
'view_mode': 'form',
'target': 'new',
'res_id': wizard.id,
}
class SaleOrderLine(models.Model):
_inherit = 'sale.order.line'
@@ -166,4 +185,6 @@ class SaleOrderLine(models.Model):
for line in self:
if vals['supply_method'] == 'automation' and line.manual_quotation:
raise UserError('当前(%s)产品为人工编程产品,不能选择自动化产线加工' % ','.join(line.mapped('product_id.name')))
if vals['supply_method'] == 'purchase' and line.is_incoming_material:
raise UserError('当前(%s)产品为客供料,不能选择外购' % ','.join(line.mapped('product_id.name')))
return super(SaleOrderLine, self).write(vals)

View File

@@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-
import base64
import random
import re
import qrcode
from itertools import groupby
from collections import defaultdict, namedtuple
@@ -180,14 +183,14 @@ class StockRule(models.Model):
productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create(
productions_values)
# 将这一批制造订单的采购组根据成品设置为不同的采购组
product_group_id = {}
for index, production in enumerate(productions):
if production.product_id.id not in product_group_id.keys():
product_group_id[production.product_id.id] = production.procurement_group_id.id
else:
productions_values[index].update({'name': production.name})
procurement_group_vals = production._prepare_procurement_group_vals(productions_values[index])
production.procurement_group_id = self.env["procurement.group"].create(procurement_group_vals).id
# product_group_id = {}
# for index, production in enumerate(productions):
# if production.product_id.id not in product_group_id.keys():
# product_group_id[production.product_id.id] = production.procurement_group_id.id
# else:
# productions_values[index].update({'name': production.name})
# procurement_group_vals = production._prepare_procurement_group_vals(productions_values[index])
# production.procurement_group_id = self.env["procurement.group"].create(procurement_group_vals).id
# self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
# self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
@@ -197,7 +200,7 @@ class StockRule(models.Model):
'''
# productions._create_workorder()
#
self.env['stock.move'].sudo().create(productions._get_moves_finished_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
@@ -289,7 +292,7 @@ class StockRule(models.Model):
if production_item.product_id.id in product_id_to_production_names:
# 同一个产品多个制造订单对应一个编程单和模型库
# 只调用一次fetchCNC并将所有生产订单的名称作为字符串传递
if not production_item.programming_no and production_item.production_type == '自动化产线加工':
if not production_item.programming_no and production_item.production_type in ['自动化产线加工', '人工线下加工']:
if not production_programming.programming_no:
production_item.fetchCNC(
', '.join(product_id_to_production_names[production_item.product_id.id]))
@@ -314,6 +317,11 @@ class StockRule(models.Model):
i += 1
technology_design_values.append(
self.env['sf.technology.design'].json_technology_design_str(k, route, i, False))
elif production_item.production_type == '人工线下加工':
for route in product_routing_workcenter:
i += 1
technology_design_values.append(
self.env['sf.technology.design'].json_technology_design_str('ZM', route, i, False))
else:
for route in product_routing_workcenter:
i += 1
@@ -444,25 +452,15 @@ class ProductionLot(models.Model):
"""Return the next serial number to be attributed to the product."""
if product.tracking == "serial":
last_serial = self.env['stock.lot'].search(
[('company_id', '=', company.id), ('product_id', '=', product.id)],
[('company_id', '=', company.id), ('product_id', '=', product.id), ('name', 'ilike', product.name)],
limit=1, order='name desc')
if last_serial:
if product.categ_id.name == '刀具':
return self.env['stock.lot'].get_tool_generate_lot_names1(company, product)
else:
# 对last_serial的name进行检测如果不是以产品名称+数字的形式的就重新搜索
if product.name.split('[')[0] not in last_serial.name:
last_serial = self.env['stock.lot'].search(
[('company_id', '=', company.id), ('product_id', '=', product.id),
('name', 'ilike', product.name.split('[')[0])],
limit=1, order='name desc')
if not last_serial:
return "%s-%03d" % (product.name, 1)
return self.env['stock.lot'].generate_lot_names1(product.name, last_serial.name, 2)[1]
now = datetime.now().strftime("%Y%m%d")
if product.cutting_tool_model_id:
split_codes = product.cutting_tool_model_id.code.split('-')
return "%s-T-%s-%s-%03d" % (split_codes[0], now, product.specification_id.name, 1)
move_line_id = self.env['stock.move.line'].sudo().search(
[('company_id', '=', company.id), ('product_id', '=', product.id), ('lot_name', 'ilike', product.name)],
limit=1, order='lot_name desc')
if last_serial or move_line_id:
return self.env['stock.lot'].generate_lot_names1(product.name, last_serial.name if (
not move_line_id or
(last_serial and last_serial.name > move_line_id.lot_name)) else move_line_id.lot_name, 2)[1]
return "%s-%03d" % (product.name, 1)
qr_code_image = fields.Binary(string='二维码', compute='_generate_qr_code')
@@ -606,6 +604,18 @@ class StockPicking(models.Model):
return sequence_id
def button_validate(self):
# 校验“收料入库单、客供料入库单”是否已经分配序列号,如果没有分配则自动分配
if self.picking_type_id.use_existing_lots is False and self.picking_type_id.use_create_lots is True:
for move in self.move_ids:
if not move.move_line_nosuggest_ids:
move.action_show_details()
else:
# 对已经生成的序列号做唯一性校验,如果重复则重新生成新的序列号
line_lot_name = [line_id.lot_name for line_id in move.move_line_nosuggest_ids]
lot_ids = self.env['stock.lot'].sudo().search([('name', 'in', line_lot_name)])
if lot_ids:
move.action_clear_lines_show_details()
move.action_show_details()
res = super().button_validate()
picking_type_in = self.env.ref('sf_manufacturing.outcontract_picking_in').id
if res is True and self.picking_type_id.id == picking_type_in:
@@ -615,12 +625,12 @@ class StockPicking(models.Model):
workorder = move_in.subcontract_workorder_id
workorders = workorder.production_id.workorder_ids
subcontract_workorders = workorders.filtered(lambda wo: wo.is_subcontract == True and wo.state!='cancel').sorted('sequence')
if workorder == subcontract_workorders[-1]:
self.env['stock.quant']._update_reserved_quantity(
move_in.product_id, move_in.location_dest_id, move_in.product_uom_qty,
lot_id=move_in.move_line_ids.lot_id,
package_id=False, owner_id=False, strict=False
)
# if workorder == subcontract_workorders[-1]:
# self.env['stock.quant']._update_reserved_quantity(
# move_in.product_id, move_in.location_dest_id, move_in.product_uom_qty,
# lot_id=move_in.move_line_ids.lot_id,
# package_id=False, owner_id=False, strict=False
# )
workorder.button_finish()
picking_type_out = self.env.ref('sf_manufacturing.outcontract_picking_out').id
if res and self.picking_type_id.id == picking_type_out:
@@ -636,6 +646,16 @@ class StockPicking(models.Model):
stock_picking = stock_picking_list.filtered(lambda p: p.state not in ("done", "cancel"))
if sale_id and not stock_picking:
sale_id.write({'state': 'delivered'})
if self.location_dest_id.name == '成品存货区' and self.state == 'done':
for move in self.move_ids:
for production in self.sale_order_id.mrp_production_ids:
moves = self.env['stock.move'].search([
('name', '=', production.name),
('state', '!=', 'cancel')
])
finish_move = next((move for move in moves if move.location_dest_id.name == '制造后'), None)
if finish_move.id in move.move_orig_ids.ids and finish_move.state == 'done':
production.workorder_ids.write({'back_button_display': False})
return res
# 创建 外协出库入单
@@ -674,8 +694,8 @@ class StockPicking(models.Model):
picking_in = self.create(
moves_in._get_new_picking_values_Res(item, workorder, 'WH/OCIN/'))
# pick_ids.append(picking_in.id)
moves_in.write(
{'picking_id': picking_in.id, 'state': 'waiting'})
moves_in.write({'picking_id': picking_in.id})
moves_in._action_confirm()
moves_in._assign_picking_post_process(new=new_picking)
# self.env.context.get('default_production_id')
moves_out = self.env['stock.move'].sudo().with_context(context).create(
@@ -685,8 +705,8 @@ class StockPicking(models.Model):
picking_out = self.create(
moves_out._get_new_picking_values_Res(item, workorder, 'WH/OCOUT/'))
# pick_ids.append(picking_out.id)
moves_out.write(
{'picking_id': picking_out.id, 'state': 'waiting'})
moves_out.write({'picking_id': picking_out.id})
moves_out._action_confirm()
moves_out._assign_picking_post_process(new=new_picking)
@api.depends('move_type', 'immediate_transfer', 'move_ids.state', 'move_ids.picking_id')
@@ -704,6 +724,20 @@ class StockPicking(models.Model):
'draft', 'sent']:
picking.state = 'waiting'
@api.constrains('state', 'move_ids_without_package')
def _check_move_ids_without_package(self):
"""
凡库存调拨单的【作业类型】=“收料入库、客供料入库”且其产品行的【产品_库存_追溯】="按唯一序列号/按批次”的,当调拨单的【状态】=就绪时
自动生成预分配序列号
"""
for sp in self:
if (sp.picking_type_id.use_existing_lots is False and sp.picking_type_id.use_create_lots is True
and sp.state == 'assigned'):
if sp.move_ids_without_package:
for move_id in sp.move_ids_without_package:
if move_id.product_id.tracking in ['serial', 'lot'] and not move_id.move_line_nosuggest_ids:
move_id.action_show_details()
class ReStockMove(models.Model):
_inherit = 'stock.move'
@@ -721,25 +755,27 @@ class ReStockMove(models.Model):
move.part_number = move.product_id.part_number
move.part_name = move.product_id.part_name
elif move.product_id.categ_id.type == '坯料':
if move.origin:
origin = move.origin.split(',')[0] if ',' in move.origin else move.origin
mrp_productio_info = self.env['mrp.production'].sudo().search(
[('name', '=', origin)])
if mrp_productio_info:
move.part_number = mrp_productio_info.part_number
move.part_name = mrp_productio_info.part_name
else:
purchase_order_info = self.env['purchase.order'].sudo().search(
[('name', '=', origin)])
if purchase_order_info:
mrp_production_ids = purchase_order_info._get_mrp_productions().ids
if mrp_production_ids:
mrp_productio_info = self.env['mrp.production'].sudo().search(
[('id', '=', mrp_production_ids[0])])
if mrp_productio_info:
move.part_number = mrp_productio_info.part_number
move.part_name = mrp_productio_info.part_name
product_name = ''
match = re.search(r'(S\d{5}-\d)', move.product_id.name)
# 如果匹配成功,提取结果
if match:
product_name = match.group(0)
if move.picking_id.sale_order_id:
sale_order = move.picking_id.sale_order_id
else:
sale_order_name = ''
match = re.search(r'(S\d+)', move.product_id.name)
if match:
sale_order_name = match.group(0)
sale_order = self.env['sale.order'].sudo().search(
[('name', '=', sale_order_name)])
filtered_order_line = sale_order.order_line.filtered(
lambda production: re.search(f'{product_name}$', production.product_id.name)
)
if filtered_order_line:
move.part_number = filtered_order_line.part_number
move.part_name = filtered_order_line.part_name
def _get_stock_move_values_Res(self, item, picking_type_id, group_id, move_dest_ids=False):
route_id = self.env.ref('sf_manufacturing.route_surface_technology_outsourcing').id
stock_rule = self.env['stock.rule'].sudo().search(
@@ -839,10 +875,12 @@ class ReStockMove(models.Model):
self.next_serial = self._get_tool_next_serial(self.company_id, self.product_id, self.origin)
else:
self.next_serial = self.env['stock.lot']._get_next_serial(self.company_id, self.product_id)
if self.picking_type_id.sequence_code == 'DL' and not self.move_line_nosuggest_ids:
self.action_assign_serial_show_details()
if (self.picking_type_id.use_existing_lots is False
and self.picking_type_id.use_create_lots is True and not self.move_line_nosuggest_ids):
self.action_assign_serial_show_details()
elif self.product_id.tracking == "lot":
self._put_tool_lot(self.company_id, self.product_id, self.origin)
if self.product_id.categ_id.name == '刀具':
self._put_tool_lot(self.company_id, self.product_id, self.origin)
return {
'name': _('Detailed Operations'),
@@ -867,37 +905,22 @@ class ReStockMove(models.Model):
),
}
def put_move_line(self):
"""
确认订单时,自动分配序列号
"""
if self.product_id.tracking == "serial":
if self.product_id.categ_id.name == '刀具':
self.next_serial = self._get_tool_next_serial(self.company_id, self.product_id, self.origin)
else:
self.next_serial = self.env['stock.lot']._get_next_serial(self.company_id, self.product_id)
self._generate_serial_numbers()
for item in self.move_line_nosuggest_ids:
if item.lot_name:
lot_name = item.lot_name
if item.product_id.categ_id.name == '坯料':
lot_name = lot_name.split('[', 1)[0]
item.lot_qr_code = self.compute_lot_qr_code(lot_name)
def _put_tool_lot(self, company, product, origin):
if product.tracking == "lot" and self.product_id.categ_id.name == '刀具':
if not self.move_line_nosuggest_ids:
lot_code = '%s-%s-%s' % ('%s-T-DJWL-%s' % (
product.cutting_tool_model_id.code.split('-')[0], product.cutting_tool_material_id.code),
datetime.now().strftime("%Y%m%d"), origin)
move_line_ids = self.env['stock.move.line'].sudo().search([('lot_name', 'like', lot_code)], limit=1,
order='id desc')
move_line_ids = self.env['stock.move.line'].sudo().search(
[('company_id', '=', company.id), ('lot_name', 'like', lot_code)], limit=1, order='id desc')
if not move_line_ids:
lot_code = '%s-001' % lot_code
else:
lot_code = '%s-%03d' % (lot_code, int(move_line_ids.lot_name[-3:]) + 1)
lot_names = self.env['stock.lot'].generate_lot_names(lot_code, 1)
move_lines_commands = self._generate_serial_move_line_commands_tool_lot(lot_names)
for move_lines_command in move_lines_commands:
move_lines_command[2]['qty_done'] = self.product_uom_qty
self.write({'move_line_nosuggest_ids': move_lines_commands})
for item in self.move_line_nosuggest_ids:
if item.lot_name:
@@ -925,10 +948,16 @@ class ReStockMove(models.Model):
last_serial = self.env['stock.lot'].search(
[('company_id', '=', company.id), ('product_id', '=', product.id), ('name', 'ilike', origin)],
limit=1, order='id DESC')
move_line_id = self.env['stock.move.line'].sudo().search(
[('company_id', '=', company.id), ('product_id', '=', product.id), ('lot_name', 'ilike', origin)],
limit=1, order='lot_name desc')
split_codes = product.cutting_tool_model_id.code.split('-')
if last_serial:
if last_serial or move_line_id:
return "%s-T-%s-%s-%03d" % (
split_codes[0], origin, product.specification_id.name, int(last_serial.name[-3:]) + 1)
split_codes[0], origin, product.specification_id.name,
int(last_serial.name[-3:] if (not move_line_id or
(last_serial and last_serial.name > move_line_id.lot_name))
else move_line_id.lot_name[-3:]) + 1)
else:
return "%s-T-%s-%s-%03d" % (split_codes[0], origin, product.specification_id.name, 1)
@@ -1024,6 +1053,15 @@ class ReStockMove(models.Model):
subcontract_workorder_id = fields.Many2one('mrp.workorder', '外协工单组件', check_company=True,
index='btree_not_null')
def button_update_the_sequence_number(self):
"""
更新序列号 功能按钮
"""
self.move_line_nosuggest_ids.unlink()
if self.state != 'assigned':
self.state = 'assigned'
return self.action_show_details()
class ReStockQuant(models.Model):
_inherit = 'stock.quant'

View File

@@ -192,3 +192,5 @@ access_sf_programming_reason,sf_programming_reason,model_sf_programming_reason,b
access_sf_programming_record,sf_programming_record,model_sf_programming_record,base.group_user,1,1,1,0
access_sf_work_individuation_page,sf_work_individuation_page,model_sf_work_individuation_page,sf_base.group_sf_mrp_user,1,1,1,0
access_sf_work_individuation_page_group_plan_dispatch,sf_work_individuation_page_group_plan_dispatch,model_sf_work_individuation_page,sf_base.group_plan_dispatch,1,1,0,0
access_sf_sale_order_cancel_wizard,sf_sale_order_cancel_wizard,model_sf_sale_order_cancel_wizard,sf_base.group_sf_order_user,1,1,1,0
access_sf_sale_order_cancel_line,sf_sale_order_cancel_line,model_sf_sale_order_cancel_line,sf_base.group_sf_order_user,1,0,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
192
193
194
195
196

View File

@@ -74,7 +74,9 @@
<xpath expr="//field[@name='production_real_duration']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<xpath expr="//field[@name='state']" position="after">
<field name="programming_state" optional="hide"/>
</xpath>
</field>
</record>
@@ -114,12 +116,13 @@
</xpath>
<xpath expr="//sheet//group//group//div[3]" position="after">
<field name="production_type" readonly="1"/>
<field name="production_product_type" invisible="1"/>
<field name="manual_quotation" readonly="1"
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"/>
attrs="{'invisible': ['|', ('production_type', 'not in', ['自动化产线加工', '人工线下加工']), ('production_product_type', '!=', '成品')]}"/>
<field name="programming_no" readonly="1"
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"/>
attrs="{'invisible': ['|', ('production_type', 'not in', ['自动化产线加工', '人工线下加工']), ('production_product_type', '!=', '成品')]}"/>
<field name="programming_state" readonly="1"
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"
attrs="{'invisible': ['|', ('production_type', 'not in', ['自动化产线加工', '人工线下加工']), ('production_product_type', '!=', '成品')]}"
decoration-success="programming_state == '已编程'"
decoration-warning="programming_state =='编程中'"
decoration-danger="programming_state =='已编程未下发'"/>
@@ -414,7 +417,9 @@
<span class="o_stat_text">子MO</span>
</xpath>
<xpath expr="//sheet//notebook//page[last()]" position="after">
<page string="编程记录">
<page string="编程记录" attrs="{'invisible': ['|', ('production_type', 'not in', ['自动化产线加工', '人工线下加工']), ('production_product_type', '!=', '成品')]}">
<field name="production_type" invisible="1"/>
<field name="production_product_type" invisible="1"/>
<field name="programming_record_ids" widget="one2many" attrs="{'readonly': [('id', '!=', False)]}">
<tree>
<field name="number"/>
@@ -630,6 +635,8 @@
<field name="state" icon="fa-filter" enable_counters="1"/>
<field name="delivery_status" icon="fa-filter" enable_counters="1"/>
<field name="production_type" icon="fa-filter" enable_counters="1"/>
<field name="programming_state" icon="fa-filter" enable_counters="1"/>
</searchpanel>
</xpath>
<filter name='todo' position="replace"/>
@@ -791,7 +798,7 @@
groups="sf_base.group_plan_dispatch,sf_base.group_sf_mrp_manager"
sequence="1"/>
<menuitem id="stock_picking_type_menu"
<menuitem id="stock.stock_picking_type_menu"
name="驾驶舱"
parent="stock.menu_stock_root"
action="stock.stock_picking_type_action"

View File

@@ -48,6 +48,7 @@
</xpath>
<xpath expr="//field[@name='date_planned_finished']" position="replace">
<field name="date_planned_finished" string="计划结束日期" optional="hide"/>
<field name="back_button_display" invisible="1"/>
</xpath>
<xpath expr="//button[@name='button_start']" position="attributes">
<!-- <attribute name="attrs">{'invisible': ['|', '|', '|','|','|', ('production_state','in', ('draft',-->
@@ -58,6 +59,11 @@
<attribute name="attrs">{'invisible': [('state', '!=', 'ready')]}
</attribute>
</xpath>
<xpath expr="//button[@name='button_start']" position="after">
<button name="button_back" string="回退" type="object" class="btn-primary"
attrs="{'invisible': [('back_button_display', '=', False)]}" confirm="是否确认回退"/>
</xpath>
<xpath expr="//button[@name='%(mrp.act_mrp_block_workcenter_wo)d']" position="attributes">
<attribute name="attrs">{'invisible':
['|',("user_permissions","=",False),("name","=","获取CNC加工程序")]}
@@ -163,6 +169,8 @@
<field name='is_delivery' invisible="1"/>
<field name="is_trayed" invisible="1"/>
<field name="is_inspect" invisible="1"/>
<field name="back_button_display" invisible="1"/>
<!-- <field name="rework_flag" invisible="1"/>-->
<!-- <field name='is_send_program_again' invisible="1"/>-->
<!-- 工单form页面的开始停工按钮等 -->
<!-- <button name="button_start" type="object" string="开始" class="btn-success" -->
@@ -177,12 +185,14 @@
<!-- <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_back" string="回退" type="object" class="btn-primary"
attrs="{'invisible': [('back_button_display', '=', False)]}" confirm="是否确认回退"/>
<button name="button_start" type="object" string="开始" class="btn-success"
attrs="{'invisible': [('state', '!=', 'ready')]}"/>
<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="是否确认完工"
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>
attrs="{'invisible': ['|', '|', '|',('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False),'&amp;','&amp;',('state', 'in', ('progress')), ('is_inspect', '=', True), ('routing_type','!=','CNC加工')]}"/>
<button name="%(mrp.act_mrp_block_workcenter_wo)d" type="action" string="阻塞"
context="{'default_workcenter_id': workcenter_id}" class="btn-danger"
@@ -192,10 +202,11 @@
attrs="{'invisible': ['|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked')]}"/>
<button name="do_inspect" type="object" string="送检" class="btn-success" confirm="是否确认送检"
attrs="{'invisible': ['|', '|', ('state', 'not in', ('progress')), ('is_inspect', '=', False), ('routing_type','=','CNC加工')]}"/>
<!-- <button name="%(mrp.act_mrp_block_workcenter_wo)d" type="action" string="停工" -->
<!-- context="{'default_workcenter_id': workcenter_id}" class="btn-danger" -->
<!-- groups="sf_base.group_sf_mrp_user" -->
<button name="do_inspect" type="object" string="送检" class="btn-success" confirm="是否确认送检"
attrs="{'invisible': ['|', '|', ('state', 'not in', ('progress')), ('is_inspect', '=', False), ('production_line_state','!=','已下产线')]}"/>
<!-- <button name="%(mrp.act_mrp_block_workcenter_wo)d" type="action" string="停工" -->
<!-- context="{'default_workcenter_id': workcenter_id}" class="btn-danger" -->
<!-- groups="sf_base.group_sf_mrp_user" -->
<!-- attrs="{'invisible': ['|', ('production_state', '!=', 'pending_processing'), ('state','!=','progress')]}"/> -->
<!-- <button name="button_unblock" type="object" string="Unblock" -->
<!-- context="{'default_workcenter_id': workcenter_id}" class="btn-danger" -->
@@ -211,9 +222,13 @@
attrs="{'invisible': ['|', '|', '|', ('routing_type','!=','装夹预调'),('state','!=','progress'), ('is_trayed', '=', False), ('state', 'in', ('done'))]}"/>
<button name="print_method" type="object" string="打印二维码" class="btn-primary"
attrs="{'invisible': ['|',('routing_type','!=','解除装夹'),('state','!=','done')]}"/>
<!-- <button type="object" class="oe_highlight jikimo_button_confirm" name="button_rework"-->
<!-- string="返工"-->
<!-- attrs='{"invisible": [("rework_flag","=",True)]}' confirm="是否返工"/>-->
</xpath>
<xpath expr="//page[1]" position="before">
<page string="开料要求" attrs='{"invisible": [("routing_type","not in",("切割", "线切割", "人工线下加工"))]}'>
<page string="开料要求"
attrs='{"invisible": [("routing_type","not in",("切割", "线切割", "人工线下加工"))]}'>
<group>
<group>
<field name="product_tmpl_id_materials_id" widget="many2one"/>
@@ -239,7 +254,7 @@
invisible="1" sum="real duration"/>
<field name="glb_file" readonly="1" widget="Viewer3D" string="加工模型"/>
<field name="manual_quotation" readonly="1"
attrs="{'invisible': [('routing_type', '!=', 'CNC加工')]}"/>
attrs="{'invisible': [('routing_type', 'not in', ['CNC加工', '人工线下加工'])]}"/>
<field name="processing_panel" readonly="1"
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
<field name="equipment_id" readonly="1"
@@ -254,8 +269,8 @@
<field name='tag_type' readonly="1" attrs='{"invisible": [("tag_type","=",False)]}'
decoration-danger="tag_type == '重新加工'"/>
<field name="is_test_env" invisible="1"/>
<field name="rfid_code" force_save="1" readonly="1" cache="True"
attrs="{'invisible': [('rfid_code_old', '!=', False)]}" widget="qrcode_widget"/>
<field name="rfid_code" force_save="1" readonly="1" cache="True"
attrs="{'invisible': [('rfid_code_old', '!=', False)]}" widget="qrcode_widget"/>
<field name="rfid_code" string="RFID码(手动输入框)" force_save="1" readonly="0" cache="True"
attrs="{'invisible': ['|',('rfid_code_old', '!=', False), ('is_test_env', '=', False)]}"/>
<field name="rfid_code_old" readonly="1" attrs="{'invisible': [('rfid_code_old', '=', False)]}"/>
@@ -312,7 +327,7 @@
<xpath expr="//page[1]" position="before">
<page string="工件装夹" attrs='{"invisible": [("routing_type","!=","装夹预调")]}'>
<group>
<!-- <field name="_barcode_scanned" widget="barcode_handler"/> -->
<!-- <field name="_barcode_scanned" widget="barcode_handler"/> -->
<group string="托盘">
<field name="tray_serial_number" readonly="1" string="序列号"/>
</group>
@@ -489,7 +504,8 @@
</group>
</page>
<page string="2D加工图纸" attrs="{'invisible': [('routing_type','!=','装夹预调')]}">
<page string="2D加工图纸"
attrs='{"invisible": [("routing_type","not in",["装夹预调", "人工线下加工"])]}'>
<field name="machining_drawings" widget="adaptive_viewer"/>
</page>
@@ -533,7 +549,7 @@
<page string="后置三元检测" attrs='{"invisible": [("individuation_page_PTD", "=", False)]}'>
<group>
<field name="test_results"
attrs='{"readonly":[("state","!=","to be detected"), "|",("routing_type","=","CNC加工"),("is_inspect", "=", True)],
attrs='{"readonly":["&amp;","|",("state","!=","to be detected"), "|",("routing_type","=","CNC加工"),("is_inspect", "=", True),("state","in",["done","rework"])],
"invisible":[("results","!=",False)]}'/>
<!-- <field name="is_remanufacture" attrs='{"invisible":[("test_results","!=","报废")]}'/>-->
<!-- <field name="is_fetchcnc"-->
@@ -561,7 +577,7 @@
</page>
</xpath>
<xpath expr="//page[1]" position="before">
<page string="CNC程序" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
<page string="CNC程序" attrs='{"invisible": [("routing_type","not in",["CNC加工", "人工线下加工"])]}'>
<field name="cnc_ids" widget="one2many" string="工作程序" default_order="sequence_number,id"
readonly="0">
<tree>
@@ -585,7 +601,7 @@
</field>
</page>
<page string="CMM程序" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
<page string="CMM程序" attrs='{"invisible": [("routing_type","not in",["CNC加工", "人工线下加工"])]}'>
<field name="cmm_ids" widget="one2many" string="CMM程序" readonly="1">
<tree>
<field name="sequence_number"/>
@@ -638,7 +654,8 @@
<field name="inherit_id" ref="sf_manufacturing.view_mrp_production_workorder_tray_form_inherit_sf"/>
<field name="arch" type="xml">
<xpath expr="//form//sheet//group//group[2]" position="replace">
<group string="装夹图纸" attrs="{'invisible': [('routing_type', '!=', '装夹预调')]}">
<group string="装夹图纸"
attrs="{'invisible': [('routing_type', 'not in', ['装夹预调', '人工线下加工'])]}">
<!-- 隐藏加工图纸字段名 -->
<field name="processing_drawing" widget="pdf_viewer" string="" readonly="1"/>
<!-- <field name="production_id" invisible="0"/>-->
@@ -781,6 +798,10 @@
<filter name="filter_to_be_issued" string="待下发" domain="[('status', 'in', ['待下发'])]"/>
<filter name="filter_issued" string="已下发" domain="[('status', 'in', ['已下发'])]"/>
<filter name="filter_delivered" string="已配送" domain="[('status', 'in', ['已配送'])]"/>
<separator/>
<filter name="filter_type_to_production_line" string="上产线" domain="[('type', '=', '上产线')]"/>
<filter name="filter_type_to_empty_racks" string="运送空料架" domain="[('type', '=', '运送空料架')]"/>
<filter name="filter_type_production_line_back" string="下产线" domain="[('type', '=', '下产线')]"/>
<field name="rfid_code"/>
<field name="production_id"/>
<field name="feeder_station_start_id"/>
@@ -803,7 +824,7 @@
<field name="res_model">sf.workpiece.delivery</field>
<field name="search_view_id" ref="sf_workpiece_delivery_search"/>
<field name="context">{'search_default_filter_to_be_issued': 1,
'search_default_filter_issued': 1}
'search_default_filter_type_to_production_line': 1}
</field>
<field name="view_mode">tree,form</field>
<field name="domain">

View File

@@ -19,18 +19,34 @@
<field name="supply_method" attrs="{'invisible': [('state', '=', 'draft')], 'required': [('state', '=', 'supply method')]}" />
</xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='model_glb_file']" position="before">
<field name="part_number" optional="show"/>
<field name="part_number" optional="show" class="section_and_note_text"/>
</xpath>
<!-- <xpath expr="//header/button[@name='action_cancel']" position="attributes"> -->
<!-- <attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute> -->
<!-- </xpath> -->
<xpath expr="//header/button[@name='action_cancel']" position="attributes">
<attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute>
</xpath>
<xpath expr="//header/button[@name='action_cancel']" position="attributes">
<attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute>
<attribute name="attrs">{'invisible': [('state', 'not in', ['draft', 'supply method'])]}</attribute>
<attribute name="confirm">警告:取消操作将不可逆,是否确定要取消该单据?</attribute>
</xpath>
<xpath expr="//header/button[@name='action_quotation_send'][5]" position="attributes">
<attribute name="attrs">{'invisible': ['|','&amp;',('check_status', '!=', 'approved'),('state', 'in', ['draft','cancel','supply method']),'&amp;',('check_status', '=', 'approved'),('state', 'in', ['sale','cancel','supply method'])]}</attribute>
</xpath>
<xpath expr="//header/button[@name='action_cancel']" position="after">
<button
name="action_show_cancel_wizard"
string="取消"
type="object"
attrs="{'invisible': [('state', 'not in', ['sale', 'processing'])]}"
/>
<button
name="action_show_cancel_wizard"
string="取消清单"
type="object"
attrs="{'invisible': [('state', 'not in', ['cancel'])]}"
/>
</xpath>
</field>
</record>

View File

@@ -74,5 +74,30 @@
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('stock.vpicktree')})]"/>
</record>
<record id="sf_view_stock_move_operations" model="ir.ui.view">
<field name="name">sf.stock.move.operations.form</field>
<field name="model">stock.move</field>
<field name="inherit_id" ref="stock.view_stock_move_operations"/>
<field name="arch" type="xml">
<xpath expr="//form//field[@name='next_serial']" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//form//field[@name='next_serial_count']" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//form//button[@name='action_assign_serial_show_details']" position="after">
<button name="button_update_the_sequence_number" type="object" class="btn-link" data-hotkey="k" title="Assign Serial Numbers">
<span>更新序列号</span>
</button>
</xpath>
<xpath expr="//form//button[@name='action_assign_serial_show_details']" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//form//button[@name='action_clear_lines_show_details']" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
</field>
</record>
</data>
</odoo>

View File

@@ -5,3 +5,4 @@ from . import production_technology_wizard
from . import production_technology_re_adjust_wizard
from . import mrp_workorder_batch_replan_wizard
from . import sf_programming_reason
from . import sale_order_cancel

View File

@@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
# Part of YiZuo. See LICENSE file for full copyright and licensing details.
import cProfile
import io
import pstats
from concurrent.futures import ThreadPoolExecutor
import logging
from itertools import groupby
from odoo import models, api, fields, _
from odoo.exceptions import UserError
class ProductionTechnologyWizard(models.TransientModel):
@@ -15,71 +14,6 @@ class ProductionTechnologyWizard(models.TransientModel):
origin = fields.Char(string='源单据')
is_technology_confirm = fields.Boolean(default=True)
def _process_production(self,productions,technology_designs):
for production in productions:
# self._process_production_special_design(production,technology_designs)
with ThreadPoolExecutor(max_workers=4) as executor:
executor.submit(self._process_production_special_design, production,technology_designs)
def _process_production_special_design(self,production,technology_designs):
if production != self.production_id:
self.env['sf.technology.design'].sudo().unified_procedure_multiple_work_orders(technology_designs,
production)
# 特殊表面工艺
special_design = self.env['sf.technology.design'].sudo().search(
[('routing_tag', '=', 'special'), ('production_id', '=', production.id),
('is_auto', '=', False), ('active', 'in', [True, False])])
for special in special_design:
workorders_values = []
if special.active is False:
# is_cancel = False
# 工单采购单外协出入库单皆需取消
domain = [('production_id', '=', special.production_id.id)]
if special.process_parameters_id:
domain += [('surface_technics_parameters_id', '=', special.process_parameters_id.id),
('state', '!=', 'cancel')]
else:
domain += [('technology_design_id', '=', special.id), ('state', '!=', 'cancel')]
workorder = self.env['mrp.workorder'].search(domain)
# previous_workorder = self.env['mrp.workorder'].search(
# [('sequence', '=', workorder.sequence - 1), ('routing_type', '=', '表面工艺'),
# ('production_id', '=', workorder.production_id.id)])
# if previous_workorder:
# if previous_workorder.supplier_id != workorder.supplier_id:
# is_cancel = True
# if workorder.state != 'cancel' and is_cancel is True:
workorder.write({'state': 'cancel'})
workorder.picking_ids.write({'state': 'cancel'})
workorder.picking_ids.move_ids.write({'state': 'cancel'})
purchase_order = self.env['purchase.order'].search(
[('origin', '=', workorder.production_id.name), ('purchase_type', '=', 'consignment')])
for line in purchase_order.order_line:
if line.product_id.server_product_process_parameters_id == workorder.surface_technics_parameters_id:
purchase_order.write({'state': 'cancel'})
else:
if special.production_id.workorder_ids:
workorder = self.env['mrp.workorder'].search(
[('technology_design_id', '=', special.id), ('production_id', '=', special.production_id.id),
('state', '!=', 'cancel')])
if not workorder:
if special.route_id.routing_type == '表面工艺':
product_production_process = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', special.process_parameters_id.id)])
workorders_values.append(
self.env[
'mrp.workorder']._json_workorder_surface_process_str(special.production_id, special,
product_production_process.seller_ids[
0].partner_id.id))
else:
workorders_values.append(
self.env['mrp.workorder'].json_workorder_str(special.production_id, special))
special.production_id.write({'workorder_ids': workorders_values})
else:
if len(workorder.blocked_by_workorder_ids) > 1:
if workorder.sequence == 1:
workorder.blocked_by_workorder_ids = None
else:
if workorder.blocked_by_workorder_ids:
workorder.blocked_by_workorder_ids = blocked_by_workorder_ids[0]
def confirm(self):
if self.is_technology_confirm is True and self.production_id.product_id.categ_id.type in ['成品', '坯料']:
domain = [('origin', '=', self.origin), ('state', '=', 'technology_to_confirmed'),
@@ -90,24 +24,75 @@ class ProductionTechnologyWizard(models.TransientModel):
[('production_id', '=', self.production_id.id), ('active', 'in', [True, False])])
# technology_designs = self.production_id.technology_design_ids
productions = self.env['mrp.production'].search(domain)
pr = cProfile.Profile()
pr.enable()
self._process_production(productions,technology_designs)
productions = productions._create_workorder(False)
for production in productions:
if production != self.production_id:
self.env['sf.technology.design'].sudo().unified_procedure_multiple_work_orders(technology_designs,
production)
# 特殊表面工艺
special_design = self.env['sf.technology.design'].sudo().search(
[('routing_tag', '=', 'special'), ('production_id', '=', production.id),
('is_auto', '=', False), ('active', 'in', [True, False])])
for special in special_design:
workorders_values = []
if special.active is False:
# is_cancel = False
# 工单采购单外协出入库单皆需取消
domain = [('production_id', '=', special.production_id.id)]
if special.process_parameters_id:
domain += [('surface_technics_parameters_id', '=', special.process_parameters_id.id), ('state', '!=', 'cancel')]
else:
domain += [('technology_design_id', '=', special.id), ('state', '!=', 'cancel')]
workorder = self.env['mrp.workorder'].search(domain)
# previous_workorder = self.env['mrp.workorder'].search(
# [('sequence', '=', workorder.sequence - 1), ('routing_type', '=', '表面工艺'),
# ('production_id', '=', workorder.production_id.id)])
# if previous_workorder:
# if previous_workorder.supplier_id != workorder.supplier_id:
# is_cancel = True
# if workorder.state != 'cancel' and is_cancel is True:
workorder.write({'state': 'cancel'})
workorder.picking_ids.write({'state': 'cancel'})
workorder.picking_ids.move_ids.write({'state': 'cancel'})
purchase_order = self.env['purchase.order'].search(
[('origin', '=', workorder.production_id.name), ('purchase_type', '=', 'consignment')])
for line in purchase_order.order_line:
if line.product_id.server_product_process_parameters_id == workorder.surface_technics_parameters_id:
purchase_order.write({'state': 'cancel'})
else:
if special.production_id.workorder_ids:
workorder = self.env['mrp.workorder'].search(
[('technology_design_id', '=', special.id), ('production_id', '=', special.production_id.id), ('state', '!=', 'cancel')])
if not workorder:
if special.route_id.routing_type == '表面工艺':
product_production_process = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', special.process_parameters_id.id)])
workorders_values.append(
self.env[
'mrp.workorder']._json_workorder_surface_process_str(special.production_id, special,
product_production_process.seller_ids[
0].partner_id.id))
else:
workorders_values.append(
self.env['mrp.workorder'].json_workorder_str(special.production_id, special))
special.production_id.write({'workorder_ids': workorders_values})
else:
if len(workorder.blocked_by_workorder_ids) > 1:
if workorder.sequence == 1:
workorder.blocked_by_workorder_ids = None
else:
if workorder.blocked_by_workorder_ids:
workorder.blocked_by_workorder_ids = blocked_by_workorder_ids[0]
productions._create_workorder(False)
if self.production_id.product_id.categ_id.type == '成品':
self.production_id.get_subcontract_pick_purchase(productions)
productions.get_subcontract_pick_purchase()
productions.is_adjust = False
for item in productions:
item.is_adjust = False
workorder = item.workorder_ids.filtered(lambda wo: wo.state not in ('cancel')).sorted(
key=lambda a: a.sequence)
if workorder[0].state in ['pending']:
if workorder[0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程':
workorder[0].state = 'waiting'
pr.disable() # 停止性能分析
# 将结果输出到 StringIO
s = io.StringIO()
ps = pstats.Stats(pr, stream=s)
ps.strip_dirs().sort_stats('cumulative').print_stats()
analysis_output = s.getvalue()
first_element = workorder[0] if workorder else None
if not first_element:
raise UserError('工艺确认后,工单未生成,请检查配置')
if first_element.state in ['pending']:
if first_element.production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程':
first_element.state = 'waiting'
return productions

View File

@@ -35,6 +35,7 @@ class ReworkWizard(models.TransientModel):
is_reprogramming = fields.Boolean(string='申请重新编程', default=False)
is_reprogramming_readonly = fields.Boolean(string='申请重新编程(只读)', default=False)
is_clamp_measure = fields.Boolean(string='保留装夹测量数据', default=True)
is_clamping = fields.Boolean(string='制造订单是否存在装夹预调工单')
reprogramming_num = fields.Integer('重新编程次数', default=0)
programming_state = fields.Selection(
[('待编程', '待编程'), ('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'),

View File

@@ -13,6 +13,7 @@
<field name="routing_type" invisible="True"/>
<field name="processing_panel_id" invisible="1"/>
<field name="hidden_workorder_ids" class="css_not_available_msg"/>
<field name="is_clamping" invisible="1"/>
<group>
<field name="hidden_workorder_ids" invisible="1"/>
<field options="{'no_create': True,'no_open': True}" readonly="1" name="workorder_ids"
@@ -26,7 +27,7 @@
</tree>
</field>
</group>
<div attrs='{"invisible": [("routing_type","=","装夹预调")]}'>
<div attrs='{"invisible": ["|", ("routing_type","=","装夹预调"), ("is_clamping", "=", False)]}'>
<span style='font-weight:bold;'>保留装夹测量数据
<field name="is_clamp_measure" force_save="1"/>
</span>

View File

@@ -0,0 +1,659 @@
from odoo import models, fields, api
from odoo.exceptions import UserError
class SFSaleOrderCancelWizard(models.TransientModel):
_name = 'sf.sale.order.cancel.wizard'
_description = '销售订单取消向导'
order_id = fields.Many2one('sale.order', string='销售订单')
related_docs = fields.One2many('sf.sale.order.cancel.line', 'wizard_id', string='相关单据')
has_movement = fields.Boolean(compute='_compute_has_movement', string='是否有异动')
display_message = fields.Char(compute='_compute_display_message', string='显示消息')
@api.model
def default_get(self, fields_list):
defaults = super().default_get(fields_list)
if self._context.get('active_id'):
order = self.env['sale.order'].browse(self._context.get('active_id'))
defaults['order_id'] = order.id
# 创建向导时自动创建关联单据行
wizard = self.create(defaults)
self.env['sf.sale.order.cancel.line'].create_from_order(wizard.id, order)
defaults['related_docs'] = wizard.related_docs.ids
return defaults
@api.depends('related_docs.cancel_reason')
def _compute_has_movement(self):
for wizard in self:
docs_has_movement = any(doc.cancel_reason for doc in wizard.related_docs)
order_canceled = wizard.order_id.state == 'cancel'
wizard.has_movement = docs_has_movement or order_canceled
@api.depends('has_movement', 'related_docs', 'related_docs.doc_state')
def _compute_display_message(self):
for wizard in self:
# 如果没有相关记录,显示为空
if not wizard.related_docs:
wizard.display_message = '无下游单据'
continue
# 检查是否所有记录都是已取消状态
all_canceled = all(doc.doc_state == '已取消' for doc in wizard.related_docs)
if all_canceled:
wizard.display_message = '取消的下游单据如下:'
else:
wizard.display_message = '部分或全部下游单据存在异动,无法取消,详情如下:' if wizard.has_movement else '确认所有下游单据全部取消?'
def action_confirm_cancel(self):
self.ensure_one()
# 删除现有关联单据行
self.related_docs.unlink()
# 重新生成最新关联单据行
self.env['sf.sale.order.cancel.line'].create_from_order(self.id, self.order_id)
# 强制重新计算校验字段
self._compute_has_movement()
self._compute_display_message()
# 检查是否存在异动
if self.has_movement:
raise UserError(
"存在下游单据异动,无法取消订单!\n"
"请关闭向导重新进入,以查看最新状态!"
)
# 取消销售订单关联的采购单
purchase_orders = self.env['purchase.order'].search([
('origin', '=', self.order_id.name)
])
if purchase_orders:
purchase_orders.write({'state': 'cancel'})
# 取消销售订单
result = self.order_id.action_cancel()
# 取消关联的制造订单及其采购单
manufacturing_orders = self.env['mrp.production'].search([
('origin', '=', self.order_id.name)
])
for mo in manufacturing_orders:
# 取消制造订单关联的采购单,但保持关联关系
mo_purchase_orders = self.env['purchase.order'].search([
('origin', '=', mo.name)
])
if mo_purchase_orders:
mo_purchase_orders.write({'state': 'cancel'})
# 取消制造订单的质检单
mo_quality_checks = self.env['quality.check'].search([
('production_id', '=', mo.id)
])
if mo_quality_checks:
mo_quality_checks.write({'quality_state': 'cancel'})
# 取消制造订单的子制造订单
child_mo_ids = self.env['mrp.production'].search([
('origin', '=', mo.name)
])
if child_mo_ids:
# child_mo_ids |= mo.child_ids
# for child_mo in child_mo_ids:
for child_mo in child_mo_ids:
child_mo.action_cancel()
# 取消工单的外协单
for workorder in mo.workorder_ids:
if workorder.picking_ids:
for pkd in workorder.picking_ids:
pkd.write({'state': 'cancel'})
# 取消制造订单
mo.action_cancel()
# 取消制造订单关联的编程单
mo._change_programming_state()
# 取消组件的制造单关联的采购单
for comp_mo in self.env['mrp.production'].search([
('origin', '=', mo.name)
]):
comp_purchase_orders = self.env['purchase.order'].search([
('origin', '=', comp_mo.name)
])
if comp_purchase_orders:
comp_purchase_orders.button_cancel()
return result
class SFSaleOrderCancelLine(models.TransientModel):
_name = 'sf.sale.order.cancel.line'
_description = '销售订单取消行'
wizard_id = fields.Many2one('sf.sale.order.cancel.wizard')
sequence = fields.Integer('序号')
category = fields.Char('大类')
doc_name = fields.Char('单据名称')
operation_type = fields.Char('作业类型')
doc_number = fields.Char('单据编号')
line_number = fields.Char('行号')
product_name = fields.Char('产品名称')
quantity = fields.Float('数量')
doc_state = fields.Char('单据状态')
cancel_reason = fields.Char('禁止取消原因')
quantity_str = fields.Char(
string="数量(字符串)",
compute="_compute_quantity_str",
store=False, # 默认不存储,除非需要搜索/排序
)
@api.depends("quantity")
def _compute_quantity_str(self):
for record in self:
# 处理所有可能的 False/0 情况
record.quantity_str = str(int(record.quantity)) if record.quantity not in [False, 0] else ""
@api.model
def create_from_order(self, wizard_id, order):
sequence = 1
lines = []
map_dict = {
'waiting': '等待其他作业',
'to approve': '待批准',
'technology_to_confirmed': '待工艺确认',
'confirmed': '已确认',
'pending': '等待其他工单',
'none': '待处理',
'draft': '询价',
'cancel': '已取消',
'pass': '通过的',
'fail': '失败的',
'done': '已完成',
'rework': '返工',
'purchase': '采购订单',
'ready': '就绪',
'approved': '已批准',
'pending_cam': '待加工',
'progress': '加工中',
'assigned': '就绪'
}
module_name_dict = {
'purchase': '采购',
'quality': '质量',
'mrp': '制造',
'stock': '库存',
'account': '会计',
'hr': '员工',
'project': '项目',
'crm': '销售',
'point_of_sale': '销售',
'website': '网站',
'sf_plan': '计划',
}
# 检查销售订单
if order.invoice_ids:
a = 0
for invoice in order.invoice_ids:
a += 1
vals = {
'wizard_id': wizard_id,
'sequence': sequence,
'category': '销售',
'doc_name': '销售订单',
'operation_type': '',
'doc_number': invoice.name,
'line_number': a,
'product_name': invoice.product_id.name,
'quantity': invoice.quantity,
'doc_state': invoice.state,
'cancel_reason': '已有异动' if invoice.state != 'draft' else ''
}
lines.append(self.create(vals))
sequence += 1
# 检查交货单
if order.picking_ids:
for picking in order.picking_ids:
b = 0
for move in picking.move_ids:
b += 1
vals = {
'wizard_id': wizard_id,
'sequence': sequence,
# 'category': '库存',
'category': module_name_dict[picking._original_module],
# 'doc_name': '交货单',
'doc_name': picking._description,
'operation_type': picking.picking_type_id.name,
'doc_number': picking.name,
'line_number': b,
'product_name': f'[{move.product_id.default_code}] {move.product_id.name}' if move else '',
# 'quantity': picking.product_qty if hasattr(picking, 'product_qty') else 0,
'quantity': move.product_uom_qty,
'doc_state': map_dict.get(picking.state, picking.state),
'cancel_reason': '已有异动' if picking.state not in ['draft', 'cancel', 'waiting'] else ''
}
lines.append(self.create(vals))
sequence += 1
# # 成品质检单
# fin_quality_checks = self.env['quality.check'].search([
# ('picking_id', '=', picking.id)
# ])
# if fin_quality_checks:
# b1 = 0
# for fin_qc in fin_quality_checks:
# b1 += 1
# vals = {
# 'wizard_id': wizard_id,
# 'sequence': sequence,
# 'category': '制造',
# 'doc_name': '质检单',
# 'operation_type': '',
# 'doc_number': fin_qc.name,
# 'line_number': b1,
# 'product_name': f'[{fin_qc.product_id.default_code}] {fin_qc.product_id.name}',
# 'quantity': 1,
# 'doc_state': map_dict.get(fin_qc.quality_state, fin_qc.quality_state),
# 'cancel_reason': '已有异动' if fin_qc.quality_state not in ['none', 'cancel', 'waiting'] else ''
# }
# lines.append(self.create(vals))
# 检查所有的质检单
quality_checks = self.env['quality.check'].search([
('product_id.name', 'like', f'%{order.name}%')])
if quality_checks:
b1 = 0
for quality_check in quality_checks:
b1 += 1
vals = {
'wizard_id': wizard_id,
'sequence': sequence,
'category': module_name_dict[quality_check._original_module],
'doc_name': quality_check._description,
'operation_type': '',
'doc_number': f'{quality_check.name}-{quality_check.title}',
'line_number': 1,
'product_name': f'[{quality_check.product_id.default_code}] {quality_check.product_id.name}' if quality_check.product_id.default_code else quality_check.product_id.name,
'quantity': 1,
'doc_state': map_dict.get(quality_check.quality_state, quality_check.quality_state),
'cancel_reason': '已有异动' if quality_check.quality_state not in ['none', 'cancel', 'waiting'] else ''
}
lines.append(self.create(vals))
# 检查组件的制造单
# component_mos = self.env['mrp.production'].search([
# ('origin', '=', mo.name)])
component_mos = self.env['mrp.production'].search([
('product_id.name', 'like', f'%R-{order.name}%')])
h = 0
if component_mos:
for comp_mo in component_mos:
h += 1
vals = {
'wizard_id': wizard_id,
'sequence': sequence,
'category': module_name_dict[comp_mo._original_module],
'doc_name': comp_mo._description,
'operation_type': '',
'doc_number': comp_mo.name,
'line_number': h,
'product_name': f'{comp_mo.product_id.name}',
'quantity': comp_mo.product_qty,
'doc_state': map_dict.get(comp_mo.state, comp_mo.state),
'cancel_reason': '已有异动' if comp_mo.state not in ['technology_to_confirmed',
'cancel'] else ''
}
lines.append(self.create(vals))
sequence += 1
for pinking_id in comp_mo.picking_ids:
y = 0
for move in pinking_id.move_ids:
y += 1
vals = {
'wizard_id': wizard_id,
'sequence': sequence,
'category': module_name_dict[pinking_id._original_module],
'doc_name': pinking_id._description,
'doc_number': f'{comp_mo.name}-{pinking_id.name}',
'line_number': y,
'operation_type': pinking_id.picking_type_id.name,
'product_name': move.product_id.name if move.product_id else '',
'quantity': move.product_uom_qty,
'doc_state': map_dict.get(pinking_id.state, pinking_id.state),
'cancel_reason': '已有异动' if pinking_id.state not in ['cancel', 'waiting',
'assigned'] else ''
}
lines.append(self.create(vals))
# 检查销售订单直接关联的采购单
purchase_orders = self.env['purchase.order'].search([
('origin', 'like', f'%{order.name}%')
])
if purchase_orders:
c = 0
for po in purchase_orders:
for order_line in po.order_line:
c += 1
vals = {
'wizard_id': wizard_id,
'sequence': sequence,
'category': module_name_dict[po._original_module],
'doc_name': po._description,
'operation_type': '',
'doc_number': po.name,
'line_number': c,
'product_name': f'[{order_line.product_id.default_code}] {order_line.product_id.name}',
'quantity': order_line.product_qty if order_line else 0,
'doc_state': map_dict.get(po.state, po.state),
'cancel_reason': '已有异动' if po.state not in ['draft', 'cancel'] else ''
}
lines.append(self.create(vals))
sequence += 1
# 客供料的入库单
for pod in purchase_orders:
pkds = self.env['stock.picking'].search([
('origin', '=', pod.name)
])
if pkds:
for pkd in pkds:
x3 = 0
for move in pkd.move_ids:
x3 += 1
vals = {
'wizard_id': wizard_id,
'sequence': sequence,
'category': module_name_dict[pkd._original_module],
'doc_name': pkd._description,
'doc_number': pkd.name,
'line_number': x3,
'operation_type': pkd.picking_type_id.name,
'product_name': f'[{move.product_id.default_code}] {move.product_id.name}',
'quantity': move.product_uom_qty,
'doc_state': map_dict.get(pkd.state, pkd.state),
'cancel_reason': '已有异动' if pkd.state not in ['waiting', 'cancel', 'confirmed'] else ''
}
lines.append(self.create(vals))
#
for child_pkd in self.env['stock.picking'].search([
('origin', '=', pkd.name)
]):
x4 = 0
for child_move in child_pkd.move_ids:
x4 += 1
vals = {
'wizard_id': wizard_id,
'sequence': sequence,
'category': module_name_dict[child_pkd._original_module],
'doc_name': child_pkd._description,
'doc_number': child_pkd.name,
'line_number': x4,
'operation_type': child_pkd.picking_type_id.name,
'product_name': child_move.product_id.name if child_move.product_id else '',
'quantity': child_move.product_uom_qty,
'doc_state': map_dict.get(child_pkd.state, child_pkd.state),
'cancel_reason': '已有异动' if child_pkd.state not in ['waiting',
'cancel', 'confirmed'] else ''
}
lines.append(self.create(vals))
# 检查制造订单
manufacturing_orders = self.env['mrp.production'].search([
('origin', '=', order.name)
])
d = 0
# 在领料单处只进行一次
flag = True
program_list = []
for mo in manufacturing_orders:
# 添加制造订单本身
d += 1
vals = {
'wizard_id': wizard_id,
'sequence': sequence,
'category': module_name_dict[mo._original_module],
'doc_name': mo._description,
'doc_number': mo.name,
'operation_type': '',
'line_number': d,
'product_name': f'[{mo.product_id.default_code}] {mo.product_id.name}',
'quantity': mo.product_qty,
'doc_state': map_dict.get(mo.state, mo.state),
'cancel_reason': '已有异动' if mo.state not in ['technology_to_confirmed', 'cancel'] else ''
}
lines.append(self.create(vals))
sequence += 1
# 检查制造订单关联的采购单
purchase_orders = self.env['purchase.order'].search([
('origin', 'like', f'%{mo.name}%')
])
if purchase_orders:
e = 0
for po in purchase_orders:
for order_line in po.order_line:
e += 1
vals = {
'wizard_id': wizard_id,
'sequence': sequence,
'category': module_name_dict[po._original_module],
'doc_name': po._description,
'doc_number': po.name,
'line_number': e,
'operation_type': '',
'product_name': order_line.product_id.name if order_line else '',
'quantity': order_line.product_qty if order_line else 0,
'doc_state': map_dict.get(po.state, po.state),
'cancel_reason': '已有异动' if po.state not in ['draft', 'cancel'] else ''
}
lines.append(self.create(vals))
sequence += 1
# 制造询价单的入库单
for pod in purchase_orders:
pkds = self.env['stock.picking'].search([
('origin', '=', pod.name)
])
if pkds:
for pkd in pkds:
x1 = 0
for move in pkd.move_ids:
x1 += 1
vals = {
'wizard_id': wizard_id,
'sequence': sequence,
'category': module_name_dict[pkd._original_module],
'doc_name': pkd._description,
'doc_number': pkd.name,
'line_number': x1,
'operation_type': pkd.picking_type_id.name,
'product_name': move.product_id.name if move.product_id else '',
'quantity': move.product_uom_qty,
'doc_state': map_dict.get(pkd.state, pkd.state),
'cancel_reason': '已有异动' if pkd.state not in ['draft', 'cancel'] else ''
}
lines.append(self.create(vals))
#
for child_pkd in self.env['stock.picking'].search([
('origin', '=', pkd.name)
]):
x2 = 0
for child_move in child_pkd.move_ids:
x2 += 1
vals = {
'wizard_id': wizard_id,
'sequence': sequence,
'category': module_name_dict[child_pkd._original_module],
'doc_name': child_pkd._description,
'doc_number': child_pkd.name,
'line_number': x2,
'operation_type': child_pkd.picking_type_id.name,
'product_name': child_move.product_id.name if child_move.product_id else '',
'quantity': child_move.product_uom_qty,
'doc_state': map_dict.get(child_pkd.state, child_pkd.state),
'cancel_reason': '已有异动' if child_pkd.state not in ['draft', 'cancel'] else ''
}
lines.append(self.create(vals))
# 检查制造订单的领料单
if mo.picking_ids and flag:
for picking in mo.picking_ids:
f = 0
for move in picking.move_ids:
f += 1
is_changed = False
if picking.state not in ['draft', 'cancel', 'waiting']:
is_changed = True
if picking.picking_type_id.name == '客供料入库' and picking.state in ['cancel', 'assigned']:
is_changed = False
vals = {
'wizard_id': wizard_id,
'sequence': sequence,
'category': module_name_dict[picking._original_module],
'doc_name': picking._description,
'doc_number': picking.name,
'line_number': f,
'operation_type': picking.picking_type_id.name,
'product_name': move.product_id.name if move.product_id else '',
'quantity': move.product_uom_qty,
'doc_state': map_dict.get(picking.state, picking.state),
'cancel_reason': '已有异动' if is_changed else ''
}
lines.append(self.create(vals))
sequence += 1
flag = False
# 检查制造订单的工单
if mo.workorder_ids:
g = 0
for workorder in mo.workorder_ids:
g += 1
vals = {
'wizard_id': wizard_id,
'sequence': sequence,
'category': module_name_dict[workorder._original_module],
'doc_name': workorder._description,
'doc_number': f'{mo.name}-{workorder.processing_panel}-{workorder.name}' if workorder.processing_panel else f'{mo.name}-{workorder.name}',
'line_number': g,
'operation_type': '',
'product_name': f'[{mo.product_id.default_code}] {mo.product_id.name}',
'quantity': workorder.qty_production,
'doc_state': map_dict.get(workorder.state, workorder.state),
'cancel_reason': '已有异动' if workorder.state not in ['draft', 'cancel', 'pending',
'waiting'] else ''
}
lines.append(self.create(vals))
sequence += 1
# 工艺外协处理
if workorder.picking_ids:
for pkd in workorder.picking_ids:
z = 0
for move in pkd.move_ids:
z += 1
vals = {
'wizard_id': wizard_id,
'sequence': sequence,
'category': module_name_dict[pkd._original_module],
'doc_name': pkd._description,
'doc_number': f'{mo.name}-{workorder.name}-{pkd.name}',
'line_number': z,
'operation_type': pkd.picking_type_id.name,
'product_name': move.product_id.name if move.product_id else '',
'quantity': move.product_uom_qty,
'doc_state': map_dict.get(pkd.state, pkd.state),
'cancel_reason': '已有异动' if pkd.state not in ['cancel', 'waiting'] else ''
}
lines.append(self.create(vals))
# # 检查制造订单组件的采购单和制造单
# for move in mo.move_raw_ids:
# # 检查组件的采购单
# component_pos = self.env['purchase.order'].search([
# ('origin', '=', mo.name),
# ('order_line.product_id', '=', move.product_id.id)
# ])
# for po in component_pos:
# vals = {
# 'wizard_id': wizard_id,
# 'sequence': sequence,
# 'category': '制造',
# 'doc_name': '组件采购单',
# 'operation_type': '组件采购',
# 'doc_number': po.name,
# 'product_name': move.product_id.name,
# 'quantity': po.order_line[0].product_qty if po.order_line else 0,
# 'doc_state': po.state,
# 'cancel_reason': '已有异动' if po.state not in ['draft', 'cancel'] else ''
# }
# lines.append(self.create(vals))
# sequence += 1
# # 检查制造订单的质检单
# quality_checks = self.env['quality.check'].search([
# ('production_id', '=', mo.id)
# ])
# if quality_checks:
# i = 0
# for check in quality_checks:
# i += 1
# vals = {
# 'wizard_id': wizard_id,
# 'sequence': sequence,
# 'category': '制造',
# 'doc_name': '质检单',
# 'operation_type': '',
# 'doc_number': check.name,
# 'line_number': i,
# 'product_name': f'[{check.product_id.default_code}] {check.product_id.name}',
# 'quantity': 1,
# 'doc_state': map_dict.get(check.quality_state, check.quality_state),
# 'cancel_reason': '已有异动' if check.quality_state not in ['none', 'cancel', 'waiting'] else ''
# }
# lines.append(self.create(vals))
# sequence += 1
# 检查制造订单的编程单
cloud_programming = mo._cron_get_programming_state()
if cloud_programming:
programming_no = cloud_programming['programming_no']
# 检查当前lines中是否已存在相同doc_number的记录
if not any(line.doc_number == programming_no for line in lines):
vals = {
'wizard_id': wizard_id,
'sequence': sequence,
'category': '编程',
'doc_name': '编程单',
'operation_type': '',
'doc_number': programming_no, # 直接使用变量
'line_number': 1,
'product_name': '',
'quantity': 0,
'doc_state': cloud_programming['programming_state'],
'cancel_reason': ''
}
lines.append(self.create(vals))
return lines
# unique_lines = {}
# for line in lines:
# doc_number = line.doc_number
# if doc_number not in unique_lines:
# unique_lines[doc_number] = line
#
# # 返回去重后的记录列表
# return list(unique_lines.values())

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_sf_sale_order_cancel_wizard" model="ir.ui.view">
<field name="name">sf.sale.order.cancel.wizard.form</field>
<field name="model">sf.sale.order.cancel.wizard</field>
<field name="arch" type="xml">
<form string="下游单据清单">
<group>
<field name="order_id" invisible="1"/>
<field name="has_movement" invisible="1"/>
</group>
<div class="alert alert-warning" role="alert">
<field name="display_message" readonly="1" nolabel="1"/>
</div>
<field name="related_docs">
<tree string="下游单据" create="false" edit="false" delete="false">
<!-- <field name="sequence" string="序号"/> -->
<field name="category" string="大类"/>
<field name="doc_name" string="单据名称"/>
<field name="operation_type" string="作业类型"/>
<field name="doc_number" string="单据编号"/>
<field name="line_number" string="行号"/>
<field name="product_name" string="产品名称"/>
<field name="quantity_str" string="数量"/>
<field name="doc_state" string="单据状态"/>
<field name="cancel_reason" string="禁止取消原因"/>
</tree>
</field>
<footer>
<button name="action_confirm_cancel"
string="确认取消"
type="object"
class="btn-primary"
attrs="{'invisible': [('has_movement', '=', True)]}"/>
<button string="关闭"
class="btn-secondary"
special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="view_sf_sale_order_cancel_line" model="ir.ui.view">
<field name="name">sf.sale.order.cancel.line.form</field>
<field name="model">sf.sale.order.cancel.line</field>
<field name="arch" type="xml">
<form string="下游单据明细">
<group>
<group>
<field name="category"/>
<field name="doc_name"/>
<field name="operation_type"/>
<field name="quantity_str"/>
<field name="cancel_reason"/>
</group>
<group>
<field name="line_number"/>
<field name="doc_number"/>
<field name="product_name"/>
<field name="doc_state"/>
</group>
</group>
</form>
</field>
</record>
</odoo>

View File

@@ -56,7 +56,6 @@ class WorkpieceDeliveryWizard(models.TransientModel):
def _get_agv_route_type_selection(self):
return self.env['sf.agv.task.route'].fields_get(['route_type'])['route_type']['selection']
delivery_type = fields.Selection(selection=_get_agv_route_type_selection, string='类型')
def dispatch_confirm(self):

View File

@@ -68,6 +68,11 @@
<field name="model">stock.picking</field>
</record>
<record id="order_quality_done" model="jikimo.message.bussiness.node">
<field name="name">调拨单质检完成提醒</field>
<field name="model">stock.picking</field>
</record>
<record id="bussiness_mrp_workorder_pre_overdue_warning" model="jikimo.message.bussiness.node">
<field name="name">装夹预调工单逾期预警</field>
<field name="model">mrp.workorder</field>
@@ -156,4 +161,12 @@
<field name="model">product.product</field>
</record>
</data>
<data noupdate="1">
<record id="bussiness_plan_data_tracking" model="jikimo.message.bussiness.node">
<field name="name">计划数据异常跟踪</field>
<field name="model">mrp.workorder</field>
</record>
</data>
</odoo>

View File

@@ -252,6 +252,18 @@
<field name="content">### 订单发货提醒:
单号:发料出库单[{{name}}]({{request_url}})
事项:销售订单{{sale_order_name}}已全部产出并入库,请及时发货</field>
</record>
<record id="template_order_quality_done" model="jikimo.message.template">
<field name="name">调拨单质检完成提醒</field>
<field name="model_id" ref="stock.model_stock_picking"/>
<field name="model">stock.picking</field>
<field name="bussiness_node_id" ref="order_quality_done"/>
<field name="msgtype">markdown</field>
<field name="urgency">normal</field>
<field name="content">### {{picking_type_name}}待处理提醒:
单号:[{{name}}]({{request_url}})
事项:质量检查已完成</field>
</record>
<record id="template_quality_cnc_test" model="jikimo.message.template">
@@ -402,4 +414,19 @@
事项:有{{num}}个质检单需要处理。</field>
</record>
</data>
<data noupdate="1">
<record id="template_plan_data_tracking" model="jikimo.message.template">
<field name="name">计划数据异常跟踪</field>
<field name="model_id" ref="mrp.model_mrp_workorder"/>
<field name="model">mrp.workorder</field>
<field name="bussiness_node_id" ref="bussiness_plan_data_tracking"/>
<field name="msgtype">markdown</field>
<field name="urgency">normal</field>
<field name="content">### 工单计划数据异常删除:
工单号:{{name}}
异动时间:{{write_date}}
</field>
</record>
</data>
</odoo>

View File

@@ -16,6 +16,7 @@ class SFMessageProduct(models.Model):
mrp_production_list = self.env['mrp.production'].sudo().search(
[('product_id', '=', product_product.id)])
production_num = 0
routing_type = None
for mrp_production_info in mrp_production_list:
routing_type = '人工线下加工' if mrp_production_info.production_type == '人工线下加工' else '装夹预调'
mrp_production_ready = mrp_production_info.workorder_ids.filtered(
@@ -23,7 +24,7 @@ class SFMessageProduct(models.Model):
if mrp_production_ready:
production_num += 1
if production_num >= 1:
url = self.get_request_url()
url = self.get_request_url(routing_type)
content = content.replace('{{product_id}}', product_product.name).replace(
'{{number}}', str(production_num)).replace(
'{{request_url}}', url)
@@ -42,11 +43,15 @@ class SFMessageProduct(models.Model):
contents.append(content)
return contents, message_queue_ids
def get_request_url(self):
def get_request_url(self, routing_type):
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
action_id = self.env.ref('sf_message.mrp_workorder_issued_action').id
menu_id = self.env.ref('mrp.menu_mrp_root').id
active_id = self.env['mrp.workcenter'].sudo().search([('name', '=', '工件装夹中心')]).id
if routing_type == '人工线下加工':
routing_name = '线下工作中心'
else:
routing_name = '工件装夹中心'
active_id = self.env['mrp.workcenter'].sudo().search([('name', '=', routing_name)]).id
# 查询参数
params = {'menu_id': menu_id, 'action': action_id, 'model': 'mrp.workorder',
'view_type': 'list', 'active_id': active_id}

View File

@@ -9,6 +9,8 @@ class SFMessageStockPicking(models.Model):
_description = "库存调拨"
_inherit = ['stock.picking', 'jikimo.message.dispatch']
quality_check_ids = fields.One2many('quality.check', 'picking_id', '质量检测单')
@api.model_create_multi
def create(self, vals):
result = super(SFMessageStockPicking, self).create(vals)
@@ -20,14 +22,17 @@ class SFMessageStockPicking(models.Model):
logging.info('add_queue调拨入库 error:%s' % e)
return result
@api.depends('move_type', 'immediate_transfer', 'move_ids.state', 'move_ids.picking_id')
@api.depends('move_type', 'immediate_transfer', 'move_ids.state', 'move_ids.picking_id',
'quality_check_ids.quality_state')
def _compute_state(self):
super(SFMessageStockPicking, self)._compute_state()
try:
for record in self:
if (record.state == 'assigned' and record.picking_type_id.sequence_code == 'PC'
and record.product_id.categ_id.type == '坯料'):
record.add_queue('坯料发料提醒')
jikimo_message_queue = record.get_message_queue(record.id)
if not jikimo_message_queue:
record.add_queue('坯料发料提醒')
if record.picking_type_id.sequence_code == 'SFP' and record.state == 'done':
stock_picking_sfp = record.env['stock.picking'].search(
@@ -48,6 +53,14 @@ class SFMessageStockPicking(models.Model):
all_ready_or_done = all(picking.state in ['assigned', 'done'] for picking in stock_picking_list)
if all_ready_or_done:
mrp_production.add_queue('工序外协发料通知')
if record.quality_check_ids and all(
qc.quality_state in ['pass', 'fail'] for qc in record.quality_check_ids):
message_template_id = self.env["jikimo.message.template"].sudo().search(
[('name', '=', '调拨单质检完成提醒')])
stock_picking_send = self.env["jikimo.message.queue"].sudo().search(
[('res_id', '=', record.id), ('message_template_id', '=', message_template_id.id)])
if not stock_picking_send:
record.add_queue('调拨单质检完成提醒')
except Exception as e:
logging.info('add_queue_compute_state error:%s' % e)
@@ -83,6 +96,17 @@ class SFMessageStockPicking(models.Model):
content = self.deal_stock_picking_sfp(message_queue_id)
if content:
contents.append(content)
elif message_queue_id.message_template_id.name == '调拨单质检完成提醒':
content = message_queue_id.message_template_id.content
stock_picking_line = self.env['stock.picking'].sudo().search(
[('id', '=', int(message_queue_id.res_id))])
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
action_id = self.env.ref('stock.action_picking_tree_ready').id
menu_id = self.env.ref('stock.menu_stock_root').id
url_with_id = f"{url}/web#view_type=form&action={action_id}&menu_id={menu_id}&id={stock_picking_line.id}"
content = content.replace('{{picking_type_name}}', stock_picking_line.picking_type_id.name).replace(
'{{name}}', stock_picking_line.name).replace('{{request_url}}', url_with_id)
contents.append(content)
return contents, message_queue_ids
def get_special_url(self, id, tmplate_name, special_name, model_id):
@@ -107,3 +131,14 @@ class SFMessageStockPicking(models.Model):
# 拼接URL
full_url = url + "/web#" + query_string
return full_url
def get_message_queue(self, res_id):
business_node_id = self.env.ref('sf_message.bussiness_material_picking_remind').id
message_template = self.env["jikimo.message.template"].sudo().search([
("model", "=", self._name),
("bussiness_node_id", "=", business_node_id)
], limit=1)
jikimo_message_queue = self.env['jikimo.message.queue'].sudo().search(
[('res_id', '=', res_id), ("message_status", "in", ("pending", "sent")),
('message_template_id', '=', message_template.id)])
return jikimo_message_queue

View File

@@ -188,3 +188,10 @@ class SFMessageWork(models.Model):
])
if message_queue_ids:
message_queue_ids.write({'message_status': 'cancel'})
def write(self, vals):
res = super(SFMessageWork, self).write(vals)
if ('leave_id' in vals and vals['leave_id'] is False or 'date_planned_start' in vals and vals['date_planned_start'] is False) \
and self.schedule_state != '未排':
self.add_queue('计划数据异常跟踪')
return res

View File

@@ -61,7 +61,7 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
for panel in ret['processing_panel'].split(','):
# 查询状态为进行中且工序类型为CNC加工的工单
cnc_workorder_has = production.workorder_ids.filtered(
lambda ach: ach.routing_type == 'CNC加工' and ach.state not in ['progress', 'done',
lambda ach: ach.routing_type in ['CNC加工', '人工线下加工'] and ach.state not in ['progress', 'done',
'rework',
'cancel'] and ach.processing_panel == panel)
if cnc_workorder_has:
@@ -76,7 +76,7 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
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', 'rework'
lambda ac: ac.routing_type in ['CNC加工', '人工线下加工'] and ac.state not in ['progress', 'done', 'rework'
'cancel'] and ac.processing_panel == panel)
if cnc_workorder:
# program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test',
@@ -91,19 +91,21 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
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', 'rework'
lambda ap: ap.routing_type in ['装夹预调', '人工线下加工'] and ap.state not in ['done', 'rework'
'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': '已编程'})
productions.filtered(lambda p: p.production_type == '人工线下加工').write({'manual_quotation': True})
logging.info('已更新制造订单编程状态:%s' % productions.ids)
# 对制造订单所有面的cnc工单的程序用刀进行校验
try:
logging.info(f'已更新制造订单:{productions}')
re_tool_chekout = False
re_tool_chekout = productions.production_cnc_tool_checkout()
productions_temp = productions.filtered(lambda p: p.production_type == '自动化产线加工')
re_tool_chekout = productions_temp.production_cnc_tool_checkout()
if re_tool_chekout:
return json.JSONEncoder().encode({'status': -3, 'message': '对cnc工单的程序用刀进行校验失败'})
except Exception as e:
@@ -200,6 +202,17 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
'send_time': ret['send_time'],
})
logging.info('已创建无效功能刀具的编程记录:%s' % production.name)
elif ret['reprogramming_reason']:
production.programming_record_ids.create({
'number': len(production.programming_record_ids) + 1,
'production_id': production.id,
'reason': ret['reprogramming_reason'],
'programming_method': ret['programme_way'],
'current_programming_count': ret['reprogramming_num'],
'target_production_id': productions_reprogram,
'apply_time': ret['trigger_time'],
'send_time': ret['send_time'],
})
else:
logging.info('无对应状态,不需更新编程记录')

View File

@@ -1968,8 +1968,7 @@ class CuttingSpeed(models.Model):
self.create({
'name': item['name'],
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', item['standard_library_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', item['standard_library_code'])]).id,
'execution_standard_id': self.env['sf.international.standards'].search(
[('code', '=', item['execution_standard_code'])]).id,
'material_name_id': self.env['sf.materials.model'].search(
@@ -1988,6 +1987,8 @@ class CuttingSpeed(models.Model):
})
else:
cutting_speed.write({
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', item['standard_library_code'])]).id,
'execution_standard_id': self.env['sf.international.standards'].search(
[('code', '=', item['execution_standard_code'])]).id,
'material_name_id': self.env['sf.materials.model'].search(
@@ -2020,8 +2021,7 @@ class CuttingSpeed(models.Model):
self.create({
'name': item['name'],
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', item['standard_library_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', item['standard_library_code'])]).id,
'execution_standard_id': self.env['sf.international.standards'].search(
[('code', '=', item['execution_standard_code'])]).id,
'material_name_id': self.env['sf.materials.model'].search(
@@ -2040,6 +2040,8 @@ class CuttingSpeed(models.Model):
})
else:
cutting_speed.write({
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', item['standard_library_code'])]).id,
'execution_standard_id': self.env['sf.international.standards'].search(
[('code', '=', item['execution_standard_code'])]).id,
'material_name_id': self.env['sf.materials.model'].search(
@@ -2118,8 +2120,7 @@ class CuttingSpeed(models.Model):
self.create({
'name': item['name'],
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', item['standard_library_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', item['standard_library_code'])]).id,
'materials_type_id': self.env['sf.materials.model'].search(
[('materials_no', '=', item['materials_type_code'])]).id,
'cutting_width_depth_id': self.env['sf.cutting.width.depth'].search(
@@ -2130,6 +2131,8 @@ class CuttingSpeed(models.Model):
})
else:
feed_per_tooth.write({
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', item['standard_library_code'])]).id,
'materials_type_id': self.env['sf.materials.model'].search(
[('materials_no', '=', item['materials_type_code'])]).id,
'cutting_width_depth_id': self.env['sf.cutting.width.depth'].search(
@@ -2156,8 +2159,7 @@ class CuttingSpeed(models.Model):
self.create({
'name': item['name'],
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', item['standard_library_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', item['standard_library_code'])]).id,
'materials_type_id': self.env['sf.materials.model'].search(
[('materials_no', '=', item['materials_type_code'])]).id,
'cutting_width_depth_id': self.env['sf.cutting.width.depth'].search(
@@ -2168,6 +2170,8 @@ class CuttingSpeed(models.Model):
})
else:
feed_per_tooth.write({
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', item['standard_library_code'])]).id,
'materials_type_id': self.env['sf.materials.model'].search(
[('materials_no', '=', item['materials_type_code'])]).id,
'cutting_width_depth_id': self.env['sf.cutting.width.depth'].search(
@@ -2195,7 +2199,7 @@ class Cutting_tool_standard_library(models.Model):
if result['status'] == 1:
for item in result['cutting_tool_standard_library_yesterday_list']:
cutting_tool_standard_library = self.search(
[("code", '=', item['code'].replace("JKM", result['factory_short_name'])),
[("code", '=', item['code']),
('active', 'in', [True, False])])
cutting_tool_type = self.env['sf.cutting.tool.type'].search(
[("code", '=', item['cutting_tool_type_code'])])
@@ -2206,7 +2210,7 @@ class Cutting_tool_standard_library(models.Model):
brand = self.env['sf.machine.brand'].search([("code", '=', item['brand_code'])])
if not cutting_tool_standard_library:
self.create({
"code": item['code'].replace("JKM", result['factory_short_name']),
"code": item['code'],
"name": item['name'],
"cutting_tool_material_id": cutting_tool_material.id,
"cutting_tool_type_id": cutting_tool_type.id,
@@ -2228,9 +2232,9 @@ class Cutting_tool_standard_library(models.Model):
'maintenance.equipment.image'].search(
[('name', '=', item['fit_blade_shape'])]).id,
"chuck_id": False if not item['chuck_code'] else self.search(
[('code', '=', item['chuck_code'].replace("JKM", result['factory_short_name']))]).id,
[('code', '=', item['chuck_code'])]).id,
"handle_id": False if not item['handle_code'] else self.search(
[('code', '=', item['handle_code'].replace("JKM", result['factory_short_name']))]).id,
[('code', '=', item['handle_code'])]).id,
"suitable_machining_method_ids": [(6, 0, [])] if not item.get(
'suitable_machining_methods') else self.env['maintenance.equipment.image']._get_ids(
item['suitable_machining_methods']),
@@ -2270,9 +2274,9 @@ class Cutting_tool_standard_library(models.Model):
'maintenance.equipment.image'].search(
[('name', '=', item['fit_blade_shape'])]).id,
"chuck_id": False if not item['chuck_code'] else self.search(
[('code', '=', item['chuck_code'].replace("JKM", result['factory_short_name']))]).id,
[('code', '=', item['chuck_code'])]).id,
"handle_id": False if not item['handle_code'] else self.search(
[('code', '=', item['handle_code'].replace("JKM", result['factory_short_name']))]).id,
[('code', '=', item['handle_code'])]).id,
"suitable_machining_method_ids": [(6, 0, [])] if not item.get(
'suitable_machining_methods') else self.env['maintenance.equipment.image']._get_ids(
item['suitable_machining_methods']),
@@ -2302,7 +2306,7 @@ class Cutting_tool_standard_library(models.Model):
if result['status'] == 1:
for item in result['cutting_tool_standard_library_all_list']:
cutting_tool_standard_library = self.search(
[("code", '=', item['code'].replace("JKM", result['factory_short_name'])),
[("code", '=', item['code']),
("active", 'in', [True, False])])
cutting_tool_type = self.env['sf.cutting.tool.type'].search(
[("code", '=', item['cutting_tool_type_code'])])
@@ -2313,7 +2317,7 @@ class Cutting_tool_standard_library(models.Model):
brand = self.env['sf.machine.brand'].search([("code", '=', item['brand_code'])])
if not cutting_tool_standard_library:
self.create({
"code": item['code'].replace("JKM", result['factory_short_name']),
"code": item['code'],
"name": item['name'],
"cutting_tool_material_id": cutting_tool_material.id,
"cutting_tool_type_id": cutting_tool_type.id,
@@ -2335,9 +2339,9 @@ class Cutting_tool_standard_library(models.Model):
'maintenance.equipment.image'].search(
[('name', '=', item['fit_blade_shape'])]).id,
"chuck_id": False if not item['chuck_code'] else self.search(
[('code', '=', item['chuck_code'].replace("JKM", result['factory_short_name']))]).id,
[('code', '=', item['chuck_code'])]).id,
"handle_id": False if not item['handle_code'] else self.search(
[('code', '=', item['handle_code'].replace("JKM", result['factory_short_name']))]).id,
[('code', '=', item['handle_code'])]).id,
"suitable_machining_method_ids": [(6, 0, [])] if not item.get(
'suitable_machining_method') else self.env['maintenance.equipment.image']._get_ids(
item['suitable_machining_method']),
@@ -2377,12 +2381,12 @@ class Cutting_tool_standard_library(models.Model):
'maintenance.equipment.image'].search(
[('name', '=', item['fit_blade_shape'])]).id,
"chuck_id": False if not item['chuck_code'] else self.search(
[('code', '=', item['chuck_code'].replace("JKM", result['factory_short_name']))]).id,
[('code', '=', item['chuck_code'])]).id,
"handle_id": False if not item['handle_code'] else self.search(
[('code', '=', item['handle_code'].replace("JKM", result['factory_short_name']))]).id,
[('code', '=', item['handle_code'])]).id,
"suitable_machining_method_ids": [(6, 0, [])] if not item.get(
'suitable_machining_methods') else self.env['maintenance.equipment.image']._get_ids(
item['suitable_machining_methods']),
'suitable_machining_method') else self.env['maintenance.equipment.image']._get_ids(
item['suitable_machining_method']),
"blade_tip_characteristics_id": self.env['maintenance.equipment.image'].search(
[('name', '=', item['blade_tip_characteristics'])]).id,
"handle_type_id": self.env['maintenance.equipment.image'].search(
@@ -2430,8 +2434,7 @@ class CuttingToolBasicParameters(models.Model):
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[(
'code', '=',
integral_tool_item['standard_library_code'].replace("JKM", result[
'factory_short_name']))]).id,
integral_tool_item['standard_library_code'])]).id,
'total_length': integral_tool_item['total_length'],
'blade_diameter': integral_tool_item['blade_diameter'],
'blade_length': integral_tool_item['blade_length'],
@@ -2454,6 +2457,10 @@ class CuttingToolBasicParameters(models.Model):
else:
self.search([('code', '=', integral_tool_item['code'])]).write({
'name': integral_tool_item['name'],
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[(
'code', '=',
integral_tool_item['standard_library_code'])]).id,
'total_length': integral_tool_item['total_length'],
'blade_diameter': integral_tool_item['blade_diameter'],
'blade_length': integral_tool_item['blade_length'],
@@ -2486,8 +2493,7 @@ class CuttingToolBasicParameters(models.Model):
'code': blade_item['code'],
'cutting_tool_type': '刀片',
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', blade_item['standard_library_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', blade_item['standard_library_code'])]).id,
'length': blade_item['length'],
'thickness': blade_item['thickness'],
'cutting_blade_length': blade_item['cutting_blade_length'],
@@ -2516,6 +2522,8 @@ class CuttingToolBasicParameters(models.Model):
else:
self.search([('code', '=', blade_item['code'])]).write({
'name': blade_item['name'],
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', blade_item['standard_library_code'])]).id,
'length': blade_item['length'],
'thickness': blade_item['thickness'],
'cutting_blade_length': blade_item['cutting_blade_length'],
@@ -2554,8 +2562,7 @@ class CuttingToolBasicParameters(models.Model):
'code': chuck_item['code'],
'cutting_tool_type': '夹头',
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', chuck_item['standard_library_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', chuck_item['standard_library_code'])]).id,
'er_size_model': chuck_item['size_model'],
'min_clamping_diameter': chuck_item['clamping_diameter_min'],
'max_clamping_diameter': chuck_item['clamping_diameter_max'],
@@ -2573,6 +2580,8 @@ class CuttingToolBasicParameters(models.Model):
else:
self.search([('code', '=', chuck_item['code'])]).write({
'name': chuck_item['name'],
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', chuck_item['standard_library_code'])]).id,
'er_size_model': chuck_item['size_model'],
'min_clamping_diameter': chuck_item['clamping_diameter_min'],
'max_clamping_diameter': chuck_item['clamping_diameter_max'],
@@ -2601,8 +2610,7 @@ class CuttingToolBasicParameters(models.Model):
'code': cutter_arbor_item['code'],
'cutting_tool_type': '刀杆',
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', cutter_arbor_item['standard_library_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', cutter_arbor_item['standard_library_code'])]).id,
'height': cutter_arbor_item['height'],
'width': cutter_arbor_item['width'],
'total_length': cutter_arbor_item['total_length'],
@@ -2620,8 +2628,7 @@ class CuttingToolBasicParameters(models.Model):
'installing_structure': cutter_arbor_item['mounting_structure'],
'blade_id': False if not cutter_arbor_item['fit_blade_model_code'] else self.env[
'sf.cutting_tool.standard.library'].search(
[('code', '=', cutter_arbor_item['fit_blade_model_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', cutter_arbor_item['fit_blade_model_code'])]).id,
'tool_shim': cutter_arbor_item['fit_knife_pad_model'],
'cotter_pin': cutter_arbor_item['fit_pin_model'],
'pressing_plate': cutter_arbor_item['fit_plate_model'],
@@ -2632,6 +2639,8 @@ class CuttingToolBasicParameters(models.Model):
else:
self.search([('code', '=', cutter_arbor_item['code'])]).write({
'name': cutter_arbor_item['name'],
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', cutter_arbor_item['standard_library_code'])]).id,
'height': cutter_arbor_item['height'],
'width': cutter_arbor_item['width'],
'total_length': cutter_arbor_item['total_length'],
@@ -2649,8 +2658,7 @@ class CuttingToolBasicParameters(models.Model):
'installing_structure': cutter_arbor_item['mounting_structure'],
'blade_id': False if not cutter_arbor_item['fit_blade_model_code'] else self.env[
'sf.cutting_tool.standard.library'].search(
[('code', '=', cutter_arbor_item['fit_blade_model_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', cutter_arbor_item['fit_blade_model_code'])]).id,
'tool_shim': cutter_arbor_item['fit_knife_pad_model'],
'cotter_pin': cutter_arbor_item['fit_pin_model'],
'pressing_plate': cutter_arbor_item['fit_plate_model'],
@@ -2672,8 +2680,7 @@ class CuttingToolBasicParameters(models.Model):
'code': cutter_head_item['code'],
'cutting_tool_type': '刀盘',
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', cutter_head_item['standard_library_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', cutter_head_item['standard_library_code'])]).id,
'install_blade_tip_num': cutter_head_item['number_blade_installed'],
'blade_diameter': cutter_head_item['blade_diameter'],
'cutter_head_diameter': cutter_head_item['cutter_diameter'],
@@ -2686,8 +2693,7 @@ class CuttingToolBasicParameters(models.Model):
'installing_structure': cutter_head_item['mounting_structure'],
'blade_id': False if not cutter_head_item['fit_blade_model_code'] else self.env[
'sf.cutting_tool.standard.library'].search(
[('code', '=', cutter_head_item['fit_blade_model_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', cutter_head_item['fit_blade_model_code'])]).id,
'screw': cutter_head_item['fit_screw_model'],
'spanner': cutter_head_item['fit_wrench_model'],
'is_cooling_hole': cutter_head_item['is_cooling_hole'],
@@ -2697,6 +2703,8 @@ class CuttingToolBasicParameters(models.Model):
else:
self.search([('code', '=', cutter_head_item['code'])]).write({
'name': cutter_head_item['name'],
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', cutter_head_item['standard_library_code'])]).id,
'install_blade_tip_num': cutter_head_item['number_blade_installed'],
'blade_diameter': cutter_head_item['blade_diameter'],
'cutter_head_diameter': cutter_head_item['cutter_diameter'],
@@ -2709,8 +2717,7 @@ class CuttingToolBasicParameters(models.Model):
'installing_structure': cutter_head_item['mounting_structure'],
'blade_id': False if not cutter_head_item['fit_blade_model_code'] else self.env[
'sf.cutting_tool.standard.library'].search(
[('code', '=', cutter_head_item['fit_blade_model_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', cutter_head_item['fit_blade_model_code'])]).id,
'screw': cutter_head_item['fit_screw_model'],
'spanner': cutter_head_item['fit_wrench_model'],
'is_cooling_hole': cutter_head_item['is_cooling_hole'],
@@ -2727,6 +2734,8 @@ class CuttingToolBasicParameters(models.Model):
[('code', '=', knife_handle_item['code']), ('active', 'in', [True, False])])
val = {
'name': knife_handle_item['name'],
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', knife_handle_item['standard_library_code'])]).id,
'taper_shank_model': knife_handle_item['taper_shank_model'],
'total_length': knife_handle_item['total_length'],
'flange_shank_length': knife_handle_item['flange_length'],
@@ -2751,9 +2760,6 @@ class CuttingToolBasicParameters(models.Model):
if not knife_handle:
val['code'] = knife_handle_item['code']
val['cutting_tool_type'] = '刀柄'
val['standard_library_id'] = self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', knife_handle_item['standard_library_code'].replace("JKM", result[
'factory_short_name']))]).id
self.create(val)
else:
self.search([('code', '=', knife_handle_item['code'])]).write(val)
@@ -2785,8 +2791,7 @@ class CuttingToolBasicParameters(models.Model):
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[(
'code', '=',
integral_tool_item['standard_library_code'].replace("JKM", result[
'factory_short_name']))]).id,
integral_tool_item['standard_library_code'])]).id,
'total_length': integral_tool_item['total_length'],
'blade_diameter': integral_tool_item['blade_diameter'],
'blade_length': integral_tool_item['blade_length'],
@@ -2809,6 +2814,10 @@ class CuttingToolBasicParameters(models.Model):
else:
integral_tool.write({
'name': integral_tool_item['name'],
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[(
'code', '=',
integral_tool_item['standard_library_code'])]).id,
'total_length': integral_tool_item['total_length'],
'blade_diameter': integral_tool_item['blade_diameter'],
'blade_length': integral_tool_item['blade_length'],
@@ -2841,8 +2850,7 @@ class CuttingToolBasicParameters(models.Model):
'code': blade_item['code'],
'cutting_tool_type': '刀片',
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', blade_item['standard_library_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', blade_item['standard_library_code'])]).id,
'length': blade_item['length'],
'thickness': blade_item['thickness'],
'cutting_blade_length': blade_item['cutting_blade_length'],
@@ -2871,6 +2879,8 @@ class CuttingToolBasicParameters(models.Model):
else:
blade.write({
'name': blade_item['name'],
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', blade_item['standard_library_code'])]).id,
'length': blade_item['length'],
'thickness': blade_item['thickness'],
'cutting_blade_length': blade_item['cutting_blade_length'],
@@ -2909,8 +2919,7 @@ class CuttingToolBasicParameters(models.Model):
'code': chuck_item['code'],
'cutting_tool_type': '夹头',
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', chuck_item['standard_library_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', chuck_item['standard_library_code'])]).id,
'er_size_model': chuck_item['size_model'],
'min_clamping_diameter': chuck_item['clamping_diameter_min'],
'max_clamping_diameter': chuck_item['clamping_diameter_max'],
@@ -2928,6 +2937,8 @@ class CuttingToolBasicParameters(models.Model):
else:
chuck.write({
'name': chuck_item['name'],
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', chuck_item['standard_library_code'])]).id,
'er_size_model': chuck_item['size_model'],
'min_clamping_diameter': chuck_item['clamping_diameter_min'],
'max_clamping_diameter': chuck_item['clamping_diameter_max'],
@@ -2956,8 +2967,7 @@ class CuttingToolBasicParameters(models.Model):
'code': cutter_arbor_item['code'],
'cutting_tool_type': '刀杆',
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', cutter_arbor_item['standard_library_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', cutter_arbor_item['standard_library_code'])]).id,
'height': cutter_arbor_item['height'],
'width': cutter_arbor_item['width'],
'total_length': cutter_arbor_item['total_length'],
@@ -2975,8 +2985,7 @@ class CuttingToolBasicParameters(models.Model):
'installing_structure': cutter_arbor_item['mounting_structure'],
'blade_id': False if not cutter_arbor_item['fit_blade_model_code'] else self.env[
'sf.cutting_tool.standard.library'].search(
[('code', '=', cutter_arbor_item['fit_blade_model_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', cutter_arbor_item['fit_blade_model_code'])]).id,
'tool_shim': cutter_arbor_item['fit_knife_pad_model'],
'cotter_pin': cutter_arbor_item['fit_pin_model'],
'pressing_plate': cutter_arbor_item['fit_plate_model'],
@@ -2987,6 +2996,8 @@ class CuttingToolBasicParameters(models.Model):
else:
cutter_arbor.write({
'name': cutter_arbor_item['name'],
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', cutter_arbor_item['standard_library_code'])]).id,
'height': cutter_arbor_item['height'],
'width': cutter_arbor_item['width'],
'total_length': cutter_arbor_item['total_length'],
@@ -3006,8 +3017,7 @@ class CuttingToolBasicParameters(models.Model):
self.env[
'sf.cutting_tool.standard.library'].search(
[('code', '=',
cutter_arbor_item['fit_blade_model_code'].replace("JKM", result[
'factory_short_name']))]).id,
cutter_arbor_item['fit_blade_model_code'])]).id,
'tool_shim': cutter_arbor_item['fit_knife_pad_model'],
'cotter_pin': cutter_arbor_item['fit_pin_model'],
'pressing_plate': cutter_arbor_item['fit_plate_model'],
@@ -3028,8 +3038,7 @@ class CuttingToolBasicParameters(models.Model):
'code': cutter_head_item['code'],
'cutting_tool_type': '刀盘',
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', cutter_head_item['standard_library_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', cutter_head_item['standard_library_code'])]).id,
'install_blade_tip_num': cutter_head_item['number_blade_installed'],
'blade_diameter': cutter_head_item['blade_diameter'],
'cutter_head_diameter': cutter_head_item['cutter_diameter'],
@@ -3042,8 +3051,7 @@ class CuttingToolBasicParameters(models.Model):
'installing_structure': cutter_head_item['mounting_structure'],
'blade_id': False if not cutter_head_item['fit_blade_model_code'] else self.env[
'sf.cutting_tool.standard.library'].search(
[('code', '=', cutter_head_item['fit_blade_model_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', cutter_head_item['fit_blade_model_code'])]).id,
'screw': cutter_head_item['fit_screw_model'],
'spanner': cutter_head_item['fit_wrench_model'],
'is_cooling_hole': cutter_head_item['is_cooling_hole'],
@@ -3053,6 +3061,8 @@ class CuttingToolBasicParameters(models.Model):
else:
cutter_head.write({
'name': cutter_head_item['name'],
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', cutter_head_item['standard_library_code'])]).id,
'install_blade_tip_num': cutter_head_item['number_blade_installed'],
'blade_diameter': cutter_head_item['blade_diameter'],
'cutter_head_diameter': cutter_head_item['cutter_diameter'],
@@ -3066,8 +3076,7 @@ class CuttingToolBasicParameters(models.Model):
'blade_id': False if not cutter_head_item['fit_blade_model_code'] else self.env[
'sf.cutting_tool.standard.library'].search(
[('code', '=',
cutter_head_item['fit_blade_model_code'].replace("JKM", result[
'factory_short_name']))]).id,
cutter_head_item['fit_blade_model_code'])]).id,
'screw': cutter_head_item['fit_screw_model'],
'spanner': cutter_head_item['fit_wrench_model'],
'is_cooling_hole': cutter_head_item['is_cooling_hole'],
@@ -3088,8 +3097,7 @@ class CuttingToolBasicParameters(models.Model):
'code': knife_handle_item['code'],
'cutting_tool_type': '刀柄',
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', knife_handle_item['standard_library_code'].replace("JKM", result[
'factory_short_name']))]).id,
[('code', '=', knife_handle_item['standard_library_code'])]).id,
'total_length': knife_handle_item['total_length'],
'taper_shank_model': knife_handle_item['taper_shank_model'],
'flange_shank_length': knife_handle_item['flange_length'],
@@ -3114,6 +3122,8 @@ class CuttingToolBasicParameters(models.Model):
else:
knife_handle.write({
'name': knife_handle_item['name'],
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', knife_handle_item['standard_library_code'])]).id,
'total_length': knife_handle_item['total_length'],
'taper_shank_model': knife_handle_item['taper_shank_model'],
'flange_shank_length': knife_handle_item['flange_length'],

View File

@@ -14,7 +14,8 @@ class QualityCheck(models.Model):
('waiting', '等待'),
('none', '待处理'),
('pass', '通过的'),
('fail', '失败的')], string='状态', tracking=True, store=True,
('fail', '失败的'),
('cancel', '已取消'), ], string='状态', tracking=True, store=True,
default='none', copy=False, compute='_compute_quality_state')
individuation_page_PTD = fields.Boolean('个性化记录(是否显示后置三元检测[PTD]页签)', related='workorder_id.individuation_page_PTD')
@@ -39,6 +40,14 @@ class QualityCheck(models.Model):
operation_id = fields.Many2one('mrp.routing.workcenter', '作业', store=True, compute='_compute_operation_id')
is_inspect = fields.Boolean('需送检', related='point_id.is_inspect')
lot_name = fields.Char('批次/序列号 名称', compute='_compute_lot_name', store=True)
@api.depends('move_line_id', 'move_line_id.lot_name')
def _compute_lot_name(self):
for qc in self:
if qc.move_line_id:
qc.lot_name = qc.move_line_id.lot_name
@api.depends('point_id.operation_id')
def _compute_operation_id(self):
for qc in self:
@@ -48,7 +57,7 @@ class QualityCheck(models.Model):
@api.depends('point_id.is_inspect')
def _compute_quality_state(self):
for qc in self:
if qc.point_id.is_inspect and qc.quality_state == 'none':
if qc.point_id.is_inspect and qc.quality_state == 'none' and qc.workorder_id.state != 'to be detected':
qc.quality_state = 'waiting'
elif not qc.point_id.is_inspect and qc.quality_state == 'waiting':
qc.quality_state = 'none'
@@ -62,7 +71,9 @@ class QualityCheck(models.Model):
def do_pass(self):
self.ensure_one()
super().do_pass()
if self.workorder_id and self.individuation_page_PTD is True:
if self.workorder_id:
if self.workorder_id.state in ('pending', 'waiting'):
raise ValidationError('工单未就绪!')
# 1将页签“判定结果”的检测结果值同步到【工单_后置三元检测_检测结果】
if self.test_results in ['返工', '报废']:
raise ValidationError('请重新选择【判定结果】-【检测结果】')
@@ -74,12 +85,25 @@ class QualityCheck(models.Model):
def do_fail(self):
self.ensure_one()
super().do_fail()
if self.workorder_id and self.individuation_page_PTD is True:
if self.workorder_id:
if self.workorder_id.state in ('pending', 'waiting'):
raise ValidationError('工单未就绪!')
# 1将页签“判定结果”的检测结果值同步到【工单_后置三元检测_检测结果】
if not self.test_results:
raise ValidationError('请填写【判定结果】里的信息')
if self.test_results == '合格':
raise ValidationError('请重新选择【判定结果】-【检测结果】')
if self.workorder_id.routing_type != 'CNC加工' and self.workorder_id.individuation_page_PTD is False:
self.workorder_id.production_id.write({'detection_result_ids': [(0, 0, {
'rework_reason': self.reason,
'detailed_reason': self.detailed_reason,
'processing_panel': self.workorder_id.processing_panel,
'routing_type': self.workorder_id.routing_type,
'handle_result': '待处理',
'test_results': self.test_results,
'test_report': self.workorder_id.detection_report})],
'is_scrap': True if self.test_results == '报废' else False
})
if self.workorder_id.state not in ['done']:
self.workorder_id.write(
{'test_results': self.test_results, 'reason': self.reason, 'detailed_reason': self.detailed_reason})
@@ -106,4 +130,3 @@ class QualityCheck(models.Model):
return "零件特采发送成功"
else:
raise ValidationError("零件特采发送失败")

View File

@@ -9,22 +9,24 @@
<field name="production_id" invisible="1"/>
<field name="work_state" invisible="1"/>
<field name="individuation_page_PTD" invisible="1"/>
<field name="production_line_id" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"/>
<field name="equipment_id" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"/>
<field name="production_line_id" attrs="{'invisible': [('production_id', '=', False)]}"/>
<field name="equipment_id" attrs="{'invisible': [('production_id', '=', False)]}"/>
<field name="model_file" widget="Viewer3D" string="模型" readonly="1" force_save="1"
attrs="{'invisible': ['|', '|',('model_file', '=', False), ('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"/>
attrs="{'invisible': ['|',('model_file', '=', False), ('production_id', '=', False)]}"/>
</xpath>
<xpath expr="//field[@name='partner_id']" position="after">
<field name="processing_panel" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"/>
<field name="processing_panel" attrs="{'invisible': [('production_id', '=', False)]}"/>
<!-- <field name="production_id" string="制造订单" readonly="1"-->
<!-- attrs="{'invisible': [('production_id', '=', False)]}"/>-->
<field name="workorder_id" string="工单号" readonly="1"
attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"/>
<field name="is_inspect" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"/>
attrs="{'invisible': [('production_id', '=', False)]}"/>
<field name="is_inspect" attrs="{'invisible': [('production_id', '=', False)]}"/>
</xpath>
<xpath expr="//page[@name='notes']" position="before">
<page string="检测报告" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}">
<page string="检测报告" attrs="{'invisible': [('production_id', '=', False)]}">
<field name="detection_report" string="" widget="pdf_viewer"/>
</page>
<page string="判定结果" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}">
<page string="判定结果" attrs="{'invisible': [('production_id', '=', False)]}">
<group>
<group>
<field name="test_results" attrs="{'readonly': [('quality_state','in', ['pass', 'fail'])]}"/>
@@ -35,25 +37,33 @@
</group>
</group>
</page>
<page string="2D图纸" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}">
<page string="2D图纸" attrs="{'invisible': [('production_id', '=', False)]}">
<field name="machining_drawings" string="" widget="adaptive_viewer"/>
</page>
<page string="客户质量标准" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}">
<page string="客户质量标准" attrs="{'invisible': [('production_id', '=', False)]}">
<field name="quality_standard" string="" widget="adaptive_viewer"/>
</page>
<page string="其他"
attrs="{'invisible': ['|','|', ('quality_state', 'not in', ['pass', 'fail']), ('production_id', '=', False),('individuation_page_PTD', '=', False)]}">
attrs="{'invisible': ['|',('quality_state', 'not in', ['pass', 'fail']), ('production_id', '=', False)]}">
<group>
<field name="write_uid" widget='many2one_avatar_user' string="判定人" readonly="1"/>
<field name="write_date" string="判定时间" readonly="1"/>
</group>
</page>
</xpath>
<xpath expr="//header//button[@name='do_pass'][1]" position="attributes">
<attribute name="string">合格</attribute>
</xpath>
<xpath expr="//header//button[@name='do_pass'][2]" position="attributes">
<attribute name="attrs">{'invisible': ['|',('quality_state', '!=', 'fail'),('work_state','in', ('done', 'rework'))]}</attribute>
<attribute name="string">合格</attribute>
</xpath>
<xpath expr="//header//button[@name='do_fail'][1]" position="attributes">
<attribute name="string">不合格</attribute>
</xpath>
<xpath expr="//header//button[@name='do_fail'][2]" position="attributes">
<attribute name="attrs">{'invisible': ['|',('quality_state', '!=', 'pass'),('work_state','in', ('done', 'rework'))]}</attribute>
<attribute name="string">不合格</attribute>
</xpath>
</field>
</record>
@@ -64,6 +74,7 @@
<field name="inherit_id" ref="quality_control.quality_check_view_tree"/>
<field name="arch" type="xml">
<xpath expr="//tree//field[@name='name']" position="after">
<field name="title" string="标准名"/>
<field name="operation_id" invisible="1"/>
</xpath>
</field>
@@ -79,8 +90,15 @@
</xpath>
<xpath expr="//field[@name='product_id']" position="after">
<field name="production_id"/>
</xpath>
</field>
</record>
<record id="quality_control.quality_check_action_main" model="ir.actions.act_window">
<field name="context">{
'is_web_request': True,
'search_default_progress': 1,
'search_default_passed': 1,
'search_default_failed': 1,
}</field>
</record>
</odoo>

View File

@@ -28,6 +28,7 @@
'web.assets_backend': [
'sf_sale/static/js/setTableWidth.js',
'sf_sale/static/src/css/purchase_list.css',
'sf_sale/static/lib/*',
]
},
'demo': [

View File

@@ -130,7 +130,10 @@ class ReSaleOrder(models.Model):
'order_id': self.id,
'product_id': product.id,
'name': '%s/%s/%s/%s/%s/%s' % (
product.model_long, product.model_width, product.model_height, product.model_volume,
self.format_float(product.model_long),
self.format_float(product.model_width),
self.format_float(product.model_height),
self.format_float(product.model_volume),
machining_accuracy_name,
product.materials_id.name),
'price_unit': product.list_price,
@@ -143,6 +146,20 @@ class ReSaleOrder(models.Model):
}
return self.env['sale.order.line'].with_context(skip_procurement=True).create(vals)
def format_float(self, value):
# 将浮点数转换为字符串
value_str = str(value)
# 检查小数点的位置
if '.' in value_str:
# 获取小数部分
decimal_part = value_str.split('.')[1]
# 判断小数位数是否超过2位
if len(decimal_part) > 2:
# 超过2位则保留2位小数
return "{:.2f}".format(value)
# 否则保持原来的位数
return float(value_str)
@api.constrains('order_line')
def check_order_line(self):
for item in self:
@@ -384,12 +401,6 @@ class RePurchaseOrder(models.Model):
def button_confirm(self):
result = super(RePurchaseOrder, self).button_confirm()
for item in self:
# 确认订单时,自动分配序列号
if item.picking_ids:
for picking_id in item.picking_ids:
if picking_id.move_ids:
for move_id in picking_id.move_ids:
move_id.put_move_line()
for line in item.order_line:
if line.product_id.categ_type == '表面工艺':
if item.origin:
@@ -500,9 +511,10 @@ class ResUserToSale(models.Model):
@api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
domain = []
if self._context.get('is_sale'):
if self.env.user.has_group('sf_base.group_sale_director'):
domain = []
pass
elif self.env.user.has_group('sf_base.group_sale_salemanager'):
if self.id != self.env.user.id:
domain = [('id', '=', self.id)]
@@ -511,7 +523,7 @@ class ResUserToSale(models.Model):
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
elif self._context.get('supplier_rank'):
if self.env.user.has_group('sf_base.group_purchase_director'):
domain = []
pass
elif self.env.user.has_group('sf_base.group_purchase'):
if self.id != self.env.user.id:
domain = [('id', '=', self.id)]

View File

@@ -0,0 +1,18 @@
/** @odoo-module */
import { Component } from "@odoo/owl";
import { registry } from "@web/core/registry";
export class MergeField extends Component {
get mergeValue() {
const data = this.props.record.data;
const v = data?.product_uom_qty
const unit = data?.product_uom[1]
return `${v} ${unit}`
}
}
MergeField.template = "jikimo_sf.MergeField";
registry.category("fields").add("merge_field", MergeField);

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="jikimo_sf.MergeField" owl="1">
<span t-esc="mergeValue"/>
</t>
</templates>

View File

@@ -1,3 +1,11 @@
.purchase_order_list_name {
min-width: 62px !important;
}
.o_list_renderer .o_list_table .o_data_row td.o_data_cell.o_field_cell.o_list_char.section_and_note_text, .section_and_note_text span{
white-space: wrap!important;
overflow: auto!important;
text-overflow: unset!important;
word-wrap: break-word;
word-break: break-all;
}

View File

@@ -139,7 +139,7 @@
</field>
<xpath expr="//field[@name='date_order']" position="after">
<field name="payment_term_id" attrs="{'readonly': ['|', ('invoice_status','=', 'invoiced'), ('state', '=', 'done')]}" options="{'no_create': True}"/>
<field name="contract_summary"/>
<!-- <field name="contract_summary"/>-->
</xpath>
<field name="partner_ref" position="attributes">
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}

View File

@@ -102,7 +102,7 @@
</field>
<xpath expr="//field[@name='order_line']/tree/field[@name='name']" position="before">
<field name="model_glb_file" widget="Viewer3D" optional="show"
string="模型文件" attrs="{'readonly': [('state', 'in', ['draft'])]}"/>
string="模型文件" attrs="{'readonly': [('state', 'in', ['draft'])], 'isInList': True}"/>
<field name="part_name" optional="hide"/>
</xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='price_subtotal']" position="after">
@@ -112,6 +112,7 @@
<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>
<attribute name="class">section_and_note_text</attribute>
</xpath>
<xpath expr="//field[@name='order_line']" position="attributes">
<attribute name="attrs">{'readonly': [('state', 'in', ['cancel','sale'])]}</attribute>
@@ -122,6 +123,16 @@
<field name="manual_quotation" readonly="1"/>
<field name="is_incoming_material" readonly="1"/>
</xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='product_uom_qty']" position="replace">
<field name="product_uom_qty" string="数量" widget="merge_field" optional="show" />
</xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='product_uom'][2]" position="attributes">
<attribute name="optional">hide</attribute>
</xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='product_uom']" position="attributes">
<attribute name="optional">hide</attribute>
</xpath>
<field name="user_id" position="attributes">
<attribute name="attrs">{'readonly': [('state', 'in', ['cancel','sale'])]}</attribute>
</field>
@@ -228,6 +239,18 @@
</field>
</record>
<record id="view_order_form_inherit_sale_stock_qty_sf" model="ir.ui.view">
<field name="name">sale.order.line.tree.sale.stock.qty.sf</field>
<field name="inherit_id" ref="sale_stock.view_order_form_inherit_sale_stock_qty"/>
<field name="model">sale.order</field>
<field name="arch" type="xml">
<xpath expr="//page/field[@name='order_line']/form/group/group/div[@name='ordered_qty']/widget[@name='qty_at_date_widget']" position="replace">
</xpath>
<xpath expr="//page/field[@name='order_line']/tree/widget[@name='qty_at_date_widget']" position="replace">
</xpath>
</field>
</record>
<record id="view_quotation_with_onboarding_tree_inherit_sf" model="ir.ui.view">
<field name="name">sale.order.quotation.tree.inherit.sf</field>
<field name="model">sale.order</field>

View File

@@ -18,7 +18,7 @@ class StockPicking(models.Model):
@api.depends('name')
def _compute_pro_purchase_count(self):
for sp in self:
if sp:
if sp.name and sp.name != '/':
po_ids = self.env['purchase.order'].sudo().search([
('origin', 'like', sp.name), ('purchase_type', '=', 'standard')])
if po_ids:
@@ -52,7 +52,7 @@ class StockPicking(models.Model):
@api.depends('name')
def _compute_pro_out_purchase_count(self):
for sp in self:
if sp:
if sp.name and sp.name != '/':
po_ids = self.env['purchase.order'].sudo().search([
('origin', 'like', sp.name), ('purchase_type', '=', 'outsourcing')])
if po_ids:

View File

@@ -935,11 +935,28 @@ class SfStockPicking(models.Model):
_inherit = 'stock.picking'
check_in = fields.Char(string='查询是否为入库单', compute='_check_is_in')
product_uom_qty_sp = fields.Float('需求数量', compute='_compute_product_uom_qty_sp', store=True)
@api.depends('move_ids_without_package', 'move_ids_without_package.product_uom_qty')
def _compute_product_uom_qty_sp(self):
for sp in self:
if sp.move_ids_without_package:
sp.product_uom_qty_sp = 0
for move_id in sp.move_ids_without_package:
sp.product_uom_qty_sp += move_id.product_uom_qty
else:
sp.product_uom_qty_sp = 0
def batch_stock_move(self):
"""
批量调拨,非就绪状态的会被忽略,完成后有通知提示
"""
# 对所以调拨单的质检单进行是否完成校验
sp_ids = [sp.id for sp in self]
qc_ids = self.env['quality.check'].sudo().search(
[('picking_id', 'in', sp_ids), ('quality_state', 'in', ['waiting', 'none'])])
if qc_ids:
raise ValidationError(f'单据{list(set(qc.picking_id.name for qc in qc_ids))}未完成质量检查,完成后再试。')
for record in self:
if record.state != 'assigned':
continue

View File

@@ -79,6 +79,9 @@
<xpath expr="//field[@name='lot_name']" position="after">
<field name="rfid"/>
</xpath>
<xpath expr="//field[@name='lot_name']" position="attributes">
<attribute name="readonly">True</attribute>
</xpath>
<xpath expr="//field[@name='product_uom_id']" position="after">
<field name="lot_qr_code" widget="image"/>
<button name="print_single_method" string="打印编码" type="object" class="oe_highlight"/>
@@ -154,7 +157,24 @@
groups="sf_base.group_sf_stock_user"/>
</xpath>
<xpath expr="//header" position="inside">
<button name="batch_stock_move" type='object' string="批量调拨"/>
<button name="batch_stock_move" type='object' string="批量调拨"
invisible="context.get('stock_type','') in ('发料出库')"
/>
</xpath>
<xpath expr="//field[@name='location_dest_id']" position="after">
<field name="product_uom_qty_sp"/>
</xpath>
</field>
</record>
<record id="view_stock_picking_type_kanban_inherit" model="ir.ui.view">
<field name="name">stock.picking.type.kanban.view.inherit</field>
<field name="model">stock.picking.type</field>
<field name="inherit_id" ref="stock.stock_picking_type_kanban"/>
<field name="arch" type="xml">
<!-- 找到按钮所在位置并添加 context -->
<xpath expr="//button[@name='get_action_picking_tree_ready']" position="attributes">
<attribute name="context">{'stock_type': name}</attribute>
</xpath>
</field>
</record>

View File

@@ -0,0 +1,3 @@
.model-viewer-in-list {
width: 150px;
}

View File

@@ -63,11 +63,16 @@ StepViewer.supportedTypes = ["binary"];
StepViewer.props = {
...standardFieldProps,
url: {type: String, optional: true},
isInList: {type: Boolean, optional: true},
};
StepViewer.extractProps = ({attrs}) => {
const modifiedAttrs = JSON.parse(attrs.modifiers || '{}');
return {
url: attrs.options.url,
isInList: modifiedAttrs.isInList,
};
};

View File

@@ -5,6 +5,7 @@
<t t-if="props.value">
<model-viewer
t-att-class="props.isInList ? 'model-viewer-in-list' : ''"
t-att-src='props.url'
name="3D model"
alt="3D model"