Compare commits

...

153 Commits

Author SHA1 Message Date
liaodanlong
0fd64aee38 Merge branch 'refs/heads/develop' into feature/customer_material_optimization 2024-12-28 08:45:25 +08:00
马广威
a1d4e74056 Accept Merge Request #1655: (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/1655?initial=true
2024-12-28 08:26:44 +08:00
mgw
3c8fbcf9f9 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2024-12-28 08:24:55 +08:00
mgw
c0bb07efb2 修改提示方式 2024-12-28 08:24:31 +08:00
廖丹龙
ead54cfc87 Accept Merge Request #1654: (feature/part_number -> 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/1654
2024-12-27 17:08:40 +08:00
liaodanlong
e990f24615 两张外协工单报错问题 2024-12-27 16:21:36 +08:00
胡尧
dc871e0a1d Accept Merge Request #1653: (feature/sale_order_route_pick -> develop)
Merge Request: 修改消息通知

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1653
2024-12-27 13:57:06 +08:00
胡尧
f71246e9fb 解决冲突 2024-12-27 13:56:41 +08:00
胡尧
133c1a87f2 修改消息通知 2024-12-27 13:53:10 +08:00
管欢
6f14a39347 Accept Merge Request #1652: (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/1652
2024-12-26 17:18:09 +08:00
guanhuan
6e2ee60616 工单已下发提醒 2024-12-26 17:15:48 +08:00
马广威
3efb30cece Accept Merge Request #1651: (feature/制造功能优化 -> develop)
Merge Request: 修改确认订单时的判定逻辑,调整提示词

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1651?initial=true
2024-12-26 16:35:57 +08:00
mgw
e56df3d7b9 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2024-12-26 16:34:11 +08:00
mgw
253a06ceaf 修改确认订单时的判定逻辑,调整提示词 2024-12-26 16:33:43 +08:00
禹翔辉
28c6c841be Accept Merge Request #1650: (feature/销售、采购页面优化 -> develop)
Merge Request: 1、订单详情字段显示优化;2、采购单页面优化

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1650?initial=true
2024-12-26 16:32:56 +08:00
yuxianghui
f948bf445a Merge branch 'feature/无效刀自动返工优化' into feature/销售、采购页面优化 2024-12-26 16:29:27 +08:00
yuxianghui
1b278a1432 1、订单详情字段显示优化;2、采购单页面优化 2024-12-26 16:28:39 +08:00
马广威
3fcb3b250f Accept Merge Request #1649: (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/1649?initial=true
2024-12-26 15:44:28 +08:00
mgw
57c689b03b Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2024-12-26 15:43:51 +08:00
mgw
158ca95dbb 去掉对审批的跳过 2024-12-26 15:42:43 +08:00
guanhuan
e059d9266d 坯料发料提醒 2024-12-26 15:26:12 +08:00
马广威
8de2a59b88 Accept Merge Request #1648: (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/1648?initial=true
2024-12-26 14:48:01 +08:00
mgw
9ac60ae283 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2024-12-26 14:47:20 +08:00
mgw
07356b5bb2 sf-询价单-【发起审批】按钮,及必填字段问题 2024-12-26 14:46:17 +08:00
禹翔辉
96c79a78ab Accept Merge Request #1647: (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/1647?initial=true
2024-12-26 13:55:49 +08:00
yuxianghui
b03eafcf94 取消自动返工时修改制造订单状态 2024-12-26 13:54:59 +08:00
禹翔辉
0b7d7959c5 Accept Merge Request #1646: (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/1646
2024-12-26 13:25:05 +08:00
yuxianghui
40865b7c6c 取消制造订单功能刀具状态为无效刀时自动返工修改制造订单的状态; 2024-12-26 13:24:04 +08:00
马广威
8abb6447d2 Accept Merge Request #1645: (feature/制造功能优化 -> develop)
Merge Request: 调整按钮位置

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1645?initial=true
2024-12-26 13:17:33 +08:00
mgw
394c3f9870 调整按钮位置 2024-12-26 13:16:04 +08:00
马广威
b254624d68 Accept Merge Request #1644: (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/1644
2024-12-26 11:28:47 +08:00
mgw
87b2c0b0f8 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2024-12-26 11:22:53 +08:00
mgw
b0e993dd53 完善采购合同上传功能 2024-12-26 11:22:16 +08:00
廖丹龙
79183761ae Accept Merge Request #1642: (feature/part_number -> 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/1642
2024-12-26 10:20:43 +08:00
liaodanlong
c448088906 采购单关联产品 2024-12-26 10:18:17 +08:00
禹翔辉
a7a1779e61 Accept Merge Request #1643: (feature/销售订单优化 -> develop)
Merge Request: 1、采购入库,选取目标货位去重校验优化;2、添加功能刀具自动计算当前目标货位的值;

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1643?initial=true
2024-12-26 10:03:13 +08:00
yuxianghui
06842c6df9 1、采购入库,选取目标货位去重校验优化;2、添加功能刀具自动计算当前目标货位的值; 2024-12-26 10:01:42 +08:00
liaodanlong
6532af500f 质量检测添加零件图号与零件名称 2024-12-26 08:57:31 +08:00
liaodanlong
1fb88960db Merge branch 'refs/heads/develop' into feature/part_number 2024-12-25 17:29:48 +08:00
liaodanlong
4c8071fea2 多制造订单生成工艺有误 2024-12-25 17:16:30 +08:00
禹翔辉
4aac3d42b6 Accept Merge Request #1641: (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/1641?initial=true
2024-12-25 17:14:44 +08:00
yuxianghui
0f4d25abb4 Merge branch 'feature/工单按钮优化' into feature/销售订单优化 2024-12-25 17:13:24 +08:00
yuxianghui
14cc285ee2 1、销售订单供货方式为委外加工时生成的采购单问题优化 2024-12-25 17:12:38 +08:00
廖丹龙
54b896004b Accept Merge Request #1640: (feature/part_number -> 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/1640?initial=true
2024-12-25 15:16:26 +08:00
liaodanlong
7a1237ddf8 时间获取修改 2024-12-25 15:14:07 +08:00
廖丹龙
f80aaf978b Accept Merge Request #1639: (feature/part_number -> 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/1639
2024-12-25 14:57:16 +08:00
liaodanlong
64f35b5633 工艺确认统一工艺设计 2024-12-25 14:49:05 +08:00
禹翔辉
9e887076f2 Accept Merge Request #1638: (feature/工单按钮优化 -> develop)
Merge Request: 1、制造订单状态优化

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1638?initial=true
2024-12-25 14:20:31 +08:00
yuxianghui
78fff46452 1、制造订单状态优化 2024-12-25 14:16:34 +08:00
禹翔辉
a6d4f99710 Accept Merge Request #1637: (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/1637?initial=true
2024-12-25 13:22:25 +08:00
yuxianghui
05f1f1a983 去除工单多余的开始按钮 2024-12-25 13:13:38 +08:00
禹翔辉
f719135abb Accept Merge Request #1636: (feature/制造、采购优化 -> develop)
Merge Request: 1、处理制造订单返工确认后,状态还是返工的bug;2、隐藏已取消状态的采购单【确认订单】按钮

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1636?initial=true
2024-12-25 13:05:14 +08:00
yuxianghui
82983871fc Merge branch 'feature/制造订单优化' into feature/制造、采购优化 2024-12-25 13:03:44 +08:00
yuxianghui
7f17ff8b78 1、处理制造订单返工确认后,状态还是返工的bug;2、隐藏已取消状态的采购单【确认订单】按钮 2024-12-25 13:02:49 +08:00
胡尧
9efece315f Accept Merge Request #1635: (feature/sale_order_route_pick -> develop)
Merge Request: 解决手工创建表面工艺采购单由于没有源单据报错

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1635?initial=true
2024-12-25 11:32:36 +08:00
胡尧
d82cb1e64c 解决手工创建表面工艺采购单由于没有源单据报错 2024-12-25 11:31:27 +08:00
mgw
7ab2894ae8 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2024-12-25 11:02:29 +08:00
mgw
0991888494 采购合同上传基础功能 2024-12-25 11:02:00 +08:00
禹翔辉
42937bcf76 Accept Merge Request #1634: (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/1634
2024-12-25 09:43:29 +08:00
yuxianghui
32fb6eadbc 1 2024-12-25 09:43:04 +08:00
yuxianghui
5a1ebabc87 处理没有工单时,制造订单就已经完成的问题 2024-12-25 09:41:07 +08:00
马广威
57f51d7cfa Accept Merge Request #1633: (feature/制造功能优化 -> develop)
Merge Request: 询价单字段排版及增加逻辑验证

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1633?initial=true
2024-12-24 20:42:18 +08:00
mgw
d2ec0cacc3 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2024-12-24 17:27:36 +08:00
mgw
aaf3b20e65 询价单字段排版及增加逻辑验证 2024-12-24 17:27:08 +08:00
禹翔辉
5912a170f1 Accept Merge Request #1632: (feature/制造订单优化 -> develop)
Merge Request: 1、制造订单隐藏不必的取消按钮、添加取消按钮的隐藏条件;2、添加返工完成后制造订单状态置为加工中

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1632
2024-12-24 17:21:36 +08:00
yuxianghui
ed3feaf2a0 优化制造订单状态方法 2024-12-24 17:17:53 +08:00
yuxianghui
d953f4e4a0 Merge branch 'feature/采购、外协优化' into feature/制造订单优化 2024-12-24 16:48:19 +08:00
yuxianghui
c2d346282c 1、制造订单隐藏不必的取消按钮、添加取消按钮的隐藏条件;2、添加返工完成后制造订单状态置为加工中 2024-12-24 16:47:10 +08:00
管欢
9d85f56af3 Accept Merge Request #1631: (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/1631
2024-12-24 15:00:25 +08:00
guanhuan
78eea6aad0 消息提醒 2024-12-24 14:49:17 +08:00
liaodanlong
cbfa9d517a 调试 2024-12-24 14:13:47 +08:00
liaodanlong
fd9e028c5a Merge branch 'refs/heads/develop' into feature/customer_material_optimization 2024-12-24 09:11:48 +08:00
廖丹龙
5306e45f83 Accept Merge Request #1630: (feature/part_number -> 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/1630?initial=true
2024-12-24 09:08:58 +08:00
liaodanlong
4bd40aeeb0 工艺确认报错问题解决 2024-12-24 09:07:11 +08:00
禹翔辉
f6d69ea193 Accept Merge Request #1629: (feature/采购、外协优化 -> develop)
Merge Request: 1、采购单的采购类型优化,外协优化

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1629?initial=true
2024-12-23 17:15:43 +08:00
yuxianghui
fe5990ff9a Merge branch 'feature/制造、采购、工单优化' into feature/采购、外协优化 2024-12-23 17:14:00 +08:00
yuxianghui
6db5e72752 1、采购单的采购类型优化,外协优化 2024-12-23 17:13:12 +08:00
廖丹龙
62fcc93a21 Accept Merge Request #1628: (feature/part_number -> 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/1628?initial=true
2024-12-23 15:44:40 +08:00
liaodanlong
d6c8120b7c 工艺确认创建工单通过工艺设计排序再进行数据生成 2024-12-23 15:42:07 +08:00
管欢
20994e6656 Accept Merge Request #1627: (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/1627
2024-12-23 14:27:24 +08:00
禹翔辉
7a64851761 Accept Merge Request #1626: (feature/制造、采购、工单优化 -> develop)
Merge Request: 1、优化销售订单状态;2、优化夹具物料查询的托盘可用状态;3、优化工单开始按键;4、优化采购订单的采购类型,根据销售订单的供货方式和工艺确认时是否外协自动选择对应采购类型;

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1626
2024-12-23 14:22:58 +08:00
guanhuan
88f3155132 Merge remote-tracking branch 'refs/remotes/origin/develop' into feature/消息提醒优化
# Conflicts:
#	sf_sale/views/purchase_order_view.xml
2024-12-23 14:06:11 +08:00
yuxianghui
8bd2250d45 Merge branch 'feature/工单rfid优化' into feature/制造、采购、工单优化 2024-12-23 13:47:22 +08:00
yuxianghui
0e7e7af85a 1、优化销售订单状态;2、优化夹具物料查询的托盘可用状态;3、优化工单开始按键;4、优化采购订单的采购类型,根据销售订单的供货方式和工艺确认时是否外协自动选择对应采购类型; 2024-12-23 13:46:30 +08:00
liaodanlong
0bff8669ee Merge branch 'refs/heads/develop' into feature/customer_material_optimization 2024-12-23 13:24:14 +08:00
yuxianghui
098e8c2518 工单工序优化 2024-12-20 17:22:56 +08:00
guanhuan
5a06811732 发送消息 2024-12-20 16:12:30 +08:00
guanhuan
c4b02f7717 发送消息 2024-12-20 15:31:55 +08:00
廖丹龙
a2f29766e6 Accept Merge Request #1625: (feature/part_number -> 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/1625
2024-12-20 15:25:27 +08:00
liaodanlong
091b9940ef 零件图号零件名称 2024-12-20 14:59:26 +08:00
liaodanlong
c0db279414 Merge branch 'refs/heads/develop' into feature/part_number
# Conflicts:
#	sf_manufacturing/models/purchase_order.py
2024-12-20 14:56:46 +08:00
liaodanlong
41096c9c65 零件图号零件名称 2024-12-20 14:55:04 +08:00
guanhuan
bf227e3e92 交期状态 2024-12-20 14:33:23 +08:00
胡尧
fa9ed5885b Accept Merge Request #1624: (feature/sale_order_route_pick -> develop)
Merge Request: 修复 工艺外协工序采购单已创建账单并且过账,退回调整删除后报错的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1624?initial=true
2024-12-20 14:16:59 +08:00
胡尧
c8df5ed179 修复 工艺外协工序采购单已创建账单并且过账,退回调整删除后报错的问题 2024-12-20 14:16:25 +08:00
guanhuan
9d883d112c 消息提醒 2024-12-20 13:51:11 +08:00
胡尧
142b94139c Accept Merge Request #1623: (feature/sale_order_route_pick -> develop)
Merge Request: 选择工序时,排除已经选择的工序

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1623?initial=true
2024-12-20 13:02:37 +08:00
胡尧
2a20d54530 选择工序时,排除已经选择的工序 2024-12-20 13:02:10 +08:00
胡尧
f2e8d0175b 选择工序时,排除已经选择的工序 2024-12-20 12:51:42 +08:00
禹翔辉
690e8ca1b2 Accept Merge Request #1622: (feature/工单rfid优化 -> develop)
Merge Request: 1、处理工单返工时就解除rfid绑定问题;2、同步bfm的订单状态到销售订单 

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1622?initial=true
2024-12-20 11:09:07 +08:00
yuxianghui
18073cb726 Merge branch 'feature/制造、工单优化' into feature/工单rfid优化 2024-12-20 11:07:48 +08:00
yuxianghui
bdac1e7460 1、处理工单返工时就解除rfid绑定问题;2、同步bfm的订单状态到销售订单 2024-12-20 11:06:41 +08:00
廖丹龙
8cfccfb2e0 Accept Merge Request #1621: (feature/delivery_status -> 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/1621?initial=true
2024-12-20 10:36:46 +08:00
liaodanlong
791d6b7fb1 客供料优化 2024-12-20 09:16:19 +08:00
禹翔辉
283950db39 Accept Merge Request #1620: (feature/制造、工单优化 -> develop)
Merge Request: 1、隐藏返工状态工单的开始按钮;2、优化工单状态方法;3、优化制造订单状态

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1620
2024-12-19 17:22:40 +08:00
yuxianghui
9cb1cff671 Merge branch 'feature/销售、制造、返工优化' into feature/制造、工单优化 2024-12-19 17:21:04 +08:00
yuxianghui
03dbeaf988 1、隐藏返工状态工单的开始按钮;2、优化工单状态方法;3、优化制造订单状态 2024-12-19 17:14:48 +08:00
胡尧
bef8ba46cd Accept Merge Request #1619: (feature/sale_order_route_pick -> develop)
Merge Request: 增加agv配送重新下发与取消

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1619?initial=true
2024-12-19 17:02:34 +08:00
胡尧
23afbc14c9 增加agv配送重新下发与取消 2024-12-19 17:00:40 +08:00
guanhuan
72bf8ad011 消息提醒 2024-12-19 14:36:16 +08:00
禹翔辉
93b87ec8ff Accept Merge Request #1618: (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/1618?initial=true
2024-12-19 13:47:26 +08:00
yuxianghui
d4fb6ef049 Merge branch 'feature/返工、工序优化' into feature/销售、制造、返工优化 2024-12-19 13:46:01 +08:00
yuxianghui
97facdaf9c 1、销售订单状态添加【加工中】、【物流中】、【已交付】三种状态;2、优化 获取mrs下发的编程单 接口,添加对返工重新编程的处理 2024-12-19 13:45:10 +08:00
guanhuan
5eda4427df 消息提醒 2024-12-19 13:09:20 +08:00
马广威
6b2b792df8 Accept Merge Request #1617: (feature/制造功能优化 -> develop)
Merge Request: sf-创建员工时提示员工已存在

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1617?initial=true
2024-12-18 16:58:50 +08:00
mgw
aad68b23ac Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2024-12-18 16:56:53 +08:00
mgw
ff91aa1ad6 sf-创建员工时提示员工已存在 2024-12-18 16:56:26 +08:00
yuxianghui
15eb36a5bc 1、优化销售订单列表页、详情页字段显示;2、优化新增采购订单列表页、详情页字段显示; 2024-12-18 16:39:54 +08:00
guanhuan
79d188fb5c 消息提醒 2024-12-18 16:26:28 +08:00
guanhuan
42444e6c83 消息提醒 2024-12-17 17:21:36 +08:00
guanhuan
d7a25580d1 消息提醒 2024-12-17 14:42:15 +08:00
guanhuan
1da3989fa3 消息提醒 2024-12-17 11:34:24 +08:00
yuxianghui
202fafac84 采购模块界面优化、采购类型新增【工序外协】、【外购订单】选项 2024-12-16 17:22:39 +08:00
yuxianghui
2b9a44761f 1、采购单详情页添加销售订单号关联字段,关联跳转至对应销售订单;2、采购页面优化 2024-12-13 17:03:45 +08:00
管欢
d24b0e8af8 Accept Merge Request #1616: (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/1616
2024-12-13 16:29:45 +08:00
guanhuan
b551777d49 逾期工单不能批量重新安排的优化需求 2024-12-13 16:24:25 +08:00
yuxianghui
3253b26818 1、报价、销售订单列表视图添加平台订单号字段。2、销售订单详情的采购链接按采购类型拆分为委外加工和采购的跳转链接; 2024-12-13 13:38:22 +08:00
liaodanlong
9f67dbe9aa 工序外协工单没有自动触发工单开始、结束的优化需求处理 2024-12-12 16:06:55 +08:00
guanhuan
01fa7bbe87 批量重新安排 2024-12-12 14:39:10 +08:00
廖丹龙
e8644962c5 Accept Merge Request #1614: (feature/delivery_status -> 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/1614?initial=true
2024-12-12 09:32:37 +08:00
guanhuan
c793089131 工艺确认弹框优化 2024-12-12 09:29:48 +08:00
禹翔辉
8fe7a47367 Accept Merge Request #1615: (feature/返工、工序优化 -> develop)
Merge Request: 1、制造订单点击返工按钮添加新的过滤条件;2、优化工单工序逻辑;3、返工添加校验限制条件;

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1615
2024-12-11 17:27:19 +08:00
yuxianghui
b7e35ce5e5 Merge branch 'feature/制造、销售、采购优化' into feature/返工、工序优化 2024-12-11 17:25:24 +08:00
yuxianghui
7c17241882 1、制造订单点击返工按钮添加新的过滤条件;2、优化工单工序逻辑;3、返工添加校验限制条件; 2024-12-11 17:24:32 +08:00
liaodanlong
ab32f881c2 制造订单退回调整删除表面工艺再次确认后-没被删除的表面工艺的外协出入库单变成了草稿状态问题处理 2024-12-11 17:09:51 +08:00
廖丹龙
b06becead4 Accept Merge Request #1613: (feature/delivery_status -> 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/1613
2024-12-11 11:23:34 +08:00
liaodanlong
f32ca13dff 修改日志的使用库 2024-12-11 11:05:25 +08:00
禹翔辉
83028b3b3d Accept Merge Request #1612: (feature/制造、销售、采购优化 -> develop)
Merge Request: 1、去除工单页面的时效状态自动筛选项;2、坯料制造订单添加批量确认加工工艺功能;3、处理返工新生成的工单工序错误问题;4、采购、销售页面优化

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1612
2024-12-11 10:36:24 +08:00
yuxianghui
ad74fd989b Merge branch 'feature/返工入口' into feature/制造、销售、采购优化 2024-12-11 10:34:09 +08:00
yuxianghui
179ce86bef 1、去除工单页面的时效状态自动筛选项;2、坯料制造订单添加批量确认加工工艺功能;3、处理返工新生成的工单工序错误问题;4、采购、销售页面优化 2024-12-11 10:33:10 +08:00
廖丹龙
1b321b424e Accept Merge Request #1611: (feature/delivery_status -> 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/1611
2024-12-11 10:16:19 +08:00
liaodanlong
d1ba2cc3d4 Merge branch 'refs/heads/develop' into feature/delivery_status
# Conflicts:
#	sf_manufacturing/models/mrp_production.py
2024-12-11 10:07:34 +08:00
liaodanlong
4738b03a45 销售订单与制造订单关联 2024-12-11 10:03:20 +08:00
guanhuan
83e57b63ec 报废新生成的制造订单没关联新采购单 2024-12-10 17:18:33 +08:00
禹翔辉
e14e750bd9 Accept Merge Request #1610: (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/1610?initial=true
2024-12-09 16:08:52 +08:00
yuxianghui
1c4a6ca85e 开放返工入口 2024-12-09 16:07:34 +08:00
管欢
c922ae1571 Accept Merge Request #1609: (feature/manufactur_order -> 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/1609
2024-12-09 09:48:12 +08:00
guanhuan
ed6189a963 Merge remote-tracking branch 'origin/feature/manufactur_order' into feature/manufactur_order 2024-12-09 09:36:08 +08:00
guanhuan
a6979b213b 手动创建的采购单生成的内部调拨单没有源单据 2024-12-09 09:35:41 +08:00
马广威
f873ebe079 Accept Merge Request #1608: (release/release_2.6 -> 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/1608
2024-12-05 18:26:29 +08:00
马广威
e50b601ccf Accept Merge Request #1607: (feature/制造功能优化 -> develop)
Merge Request: 对合并后的采购单修改匹配逻辑

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1607?initial=true
2024-12-05 18:15:09 +08:00
mgw
37c47b0a54 对合并后的采购单修改匹配逻辑 2024-12-05 18:14:19 +08:00
liaodanlong
73ab43f16a 人工线下加工 2024-11-28 10:05:17 +08:00
90 changed files with 2432 additions and 444 deletions

View File

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

View File

@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
{
'name': "机企猫 采购审批流程",
'summary': """
Short (1 phrase/line) summary of the module's purpose, used as
subtitle on modules listing or apps.openerp.com""",
'description': """
Long description of module's purpose
""",
'author': "My Company",
'website': "https://www.yourcompany.com",
# Categories can be used to filter modules in modules listing
# Check https://github.com/odoo/odoo/blob/16.0/odoo/addons/base/data/ir_module_category_data.xml
# for the full list
'category': 'Uncategorized',
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['purchase', 'base_tier_validation', 'documents'],
# 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
'demo': [
'demo/demo.xml',
],
}

View File

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

View File

@@ -0,0 +1,21 @@
# -*- 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

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

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

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

View File

@@ -0,0 +1,154 @@
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
import logging
_logger = logging.getLogger(__name__)
class jikimo_purchase_tier_validation(models.Model):
_name = 'purchase.order'
_inherit = ['purchase.order', 'tier.validation']
_tier_validation_buttons_xpath = "/form/header/button[@id='draft_confirm'][1]"
contract_document_id = fields.Many2one('documents.document', string='合同文件')
contract_file = fields.Binary(related='contract_document_id.datas', string='合同文件内容')
contract_file_name = fields.Char(related='contract_document_id.attachment_id.name', string='文件名')
# 是否已上传合同文件
is_upload_contract_file = fields.Boolean(string='是否已上传合同文件', default=False)
def button_confirm(self):
for record in self:
if record.need_validation and record.validation_status != 'validated':
raise ValidationError(_('请先完成审批!'))
return super().button_confirm()
# 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))
return super(jikimo_purchase_tier_validation, self).request_validation()
# 上传合同文件
def upload_contract_file(self):
print('upload_contract_file===========================')
# self.ensure_one()
# return {
# 'name': _('上传合同文件'),
# 'type': 'ir.actions.act_window',
# 'res_model': 'ir.attachment',
# 'view_mode': 'form',
# 'view_type': 'form',
# 'target': 'new',
# 'context': {
# 'default_res_model': self._name,
# 'default_res_id': self.id,
# 'default_type': 'binary',
# 'default_mimetype': 'application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,image/jpeg,image/png',
# }
# }
self.ensure_one()
action = {
'type': 'ir.actions.act_window',
'name': _('上传合同文件'),
'res_model': 'ir.attachment.wizard', # 我们需要创建一个新的向导模型
'view_mode': 'form',
'target': 'new',
'context': {
'default_res_model': self._name,
'default_res_id': self.id,
}
}
return action
# 删除合同文件
def delete_contract_file(self):
self.ensure_one()
if self.contract_document_id:
try:
document = self.contract_document_id
# 清空关联
self.write({
'contract_document_id': False,
'contract_file': False,
'contract_file_name': False
})
# 删除文档
if document:
document.with_context(no_attachment=True).sudo().unlink()
self.is_upload_contract_file = False
# 返回视图动作来刷新当前表单
return {
'type': 'ir.actions.act_window',
'res_model': 'purchase.order',
'res_id': self.id,
'view_mode': 'form',
'view_type': 'form',
'target': 'current',
'flags': {'mode': 'readonly'},
}
except Exception as e:
_logger.error('删除合同文件时出错: %s', str(e))
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': _('错误'),
'message': _('删除文件时出现错误'),
'type': 'danger',
'sticky': True,
}
}
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': _('提示'),
'message': _('没有需要删除的合同文件'),
'type': 'warning',
'sticky': False,
}
}

View File

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

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

@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<record model="ir.ui.view" id="tier_validation_view_purchase_order_form_inherit">
<field name="name">tier_validation_view_purchase_order_form_inherit</field>
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.purchase_order_form"/>
<field name="arch" type="xml">
<xpath expr="//header/button[@name='button_cancel']" position="replace">
</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)]}"/>
<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

@@ -0,0 +1 @@
from . import upload_file_wizard

View File

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

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

@@ -51,11 +51,11 @@ class JikimoWorkorderException(models.Model):
return res
def _get_message(self, message_queue_ids):
contents = super(JikimoWorkorderException, self)._get_message(message_queue_ids)
contents, _ = super(JikimoWorkorderException, self)._get_message(message_queue_ids)
url = self.env['ir.config_parameter'].get_param('web.base.url')
action_id = self.env.ref('mrp.mrp_production_action').id
for index, content in enumerate(contents):
exception_id = self.env['jikimo.workorder.exception'].browse(message_queue_ids[index].res_id)
url = url + '/web#id=%s&view_type=form&action=%s' % (exception_id.workorder_id.production_id.id, action_id)
contents[index] = content.replace('{{url}}', url)
return contents
return contents, message_queue_ids

View File

@@ -138,7 +138,7 @@ if env.user.has_group('mrp.group_mrp_workorder_dependencies'):
<button name="openMenuPopup" t-att-disabled="isBlocked" class="btn btn-secondary o_workorder_icon_btn fa fa-bars" type="workorder_event" title="menu"/>
<span groups="mrp_workorder.group_mrp_wo_tablet_timer">
<button name="button_pending" type="object" class="btn btn-secondary" attrs="{'invisible': ['|', ('is_user_working', '=', False), ('working_state', '=', 'blocked')]}" barcode_trigger="pause" string="PAUSE"/>
<button name="button_start" type="object" class="btn btn-warning" attrs="{'invisible': ['|', '|', ('is_user_working', '=', True), ('working_state', '=', 'blocked'), ('state', '=', ('done', 'cancel'))]}" barcode_trigger="pause" string="CONTINUE"/>
<button name="button_start" type="object" class="btn btn-warning" attrs="{'invisible': ['|', '|', ('is_user_working', '=', True), ('working_state', '=', 'blocked'), ('state', '=', ('done','rework', 'cancel'))]}" barcode_trigger="pause" string="CONTINUE"/>
<button name="button_unblock" type="object" class="btn btn-secondary btn-danger o_unblock" attrs="{'invisible': [('working_state', '!=', 'blocked')]}">Unblock</button>
<field name="duration" widget="mrp_timer" class="ms-1" readonly="1"/>
</span>

View File

@@ -7,6 +7,7 @@ from datetime import datetime
import random
from odoo import api, models, fields, _
from odoo.api import depends
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, float_round
from odoo.osv.expression import OR
@@ -122,7 +123,13 @@ class QualityPoint(models.Model):
class QualityCheck(models.Model):
_inherit = "quality.check"
part_name = fields.Char('零件名称', compute='_compute_part_name_number', readonly=True)
part_number = fields.Char('零件图号', compute='_compute_part_name_number', readonly=True)
@depends('product_id')
def _compute_part_name_number(self):
for record in self:
record.part_number = record.product_id.part_number
record.part_name = record.product_id.part_name
failure_message = fields.Html(related='point_id.failure_message', readonly=True)
measure = fields.Float('Measure', default=0.0, digits='Quality Tests', tracking=True)
measure_success = fields.Selection([

View File

@@ -389,6 +389,8 @@
<field name="name" decoration-bf="1"/>
<field name="measure_on" optional="show"/>
<field name='product_id' optional="show"/>
<field name="part_name" optional="hide"/>
<field name='part_number' optional="show"/>
<field name="lot_id" invisible="context.get('show_lots_text')"/>
<field name="lot_name" invisible="not context.get('show_lots_text')"/>
<field name="picking_id" optional="hide" string="Transfer"/>

View File

@@ -151,6 +151,12 @@ class JdEclp(models.Model):
_logger.info('准备调接口1')
url1 = config['bfm_url_new'] + '/api/create/jd/order'
requests.post(url1, json=json1, data=None)
# ===============修改销售订单状态为【物流中】===================
item = self.env['sale.order'].sudo().search([('name', '=', self.origin)])
if not item:
raise ValidationError('没有查询到订单号为【%s】的销售订单!' % self.origin)
else:
item.write({'state': 'physical_distribution'})
_logger.info('调用成功1')
_logger.info('准备调接口2')
json2 = {

View File

@@ -36,6 +36,8 @@ class StatusChange(models.Model):
# 使用super()来调用原始方法(在本例中为'sale.order'模型的'action_confirm'方法)
try:
res = super(StatusChange, self).action_confirm()
# 修改销售订单状态为【加工中】
self.write({'state': 'processing'})
logging.info('原生方法返回结果:%s' % res)
# 原有方法执行后进行额外的操作如调用外部API
process_start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

View File

@@ -9,9 +9,9 @@ class StockRuleInherit(models.Model):
@api.model
def _run_buy(self, procurements):
# 判断补货组的采购类型
procurements_group = {'standard': [], 'consignment': []}
procurements_group = {'standard': [], 'outsourcing': []}
for procurement, rule in procurements:
is_consignment = False
is_outsourcing = False
product = procurement.product_id
# 获取主 BOM
bom = self.env['mrp.bom'].search([('product_tmpl_id', '=', product.product_tmpl_id.id)], limit=1)
@@ -24,17 +24,17 @@ class StockRuleInherit(models.Model):
for route in raw_material.route_ids:
# print('route.name:', route.name)
if route.name == '按订单补给外包商':
is_consignment = True
is_outsourcing = True
if is_consignment:
procurements_group['consignment'].append((procurement, rule))
if is_outsourcing:
procurements_group['outsourcing'].append((procurement, rule))
else:
procurements_group['standard'].append((procurement, rule))
for key, value in procurements_group.items():
super(StockRuleInherit, self)._run_buy(value)
if key == 'consignment':
if key == 'outsourcing':
for procurement, rule in value:
supplier = procurement.values.get('supplier')
if supplier:
@@ -49,7 +49,7 @@ class StockRuleInherit(models.Model):
], limit=1)
logging.info("po=: %s", po)
if po:
po.write({'purchase_type': 'consignment'})
po.write({'purchase_type': 'outsourcing'})
# # 首先调用父类的 _run_buy 方法,以保留原有逻辑
# super(StockRuleInherit, self)._run_buy(procurements)
@@ -83,5 +83,5 @@ class StockRuleInherit(models.Model):
# ], limit=1)
# logging.info("po=: %s", po)
# if po:
# po.write({'purchase_type': 'consignment'})
# po.write({'purchase_type': 'outsourcing'})
# break

View File

@@ -23,6 +23,16 @@ class JkmPracticeEmployee(models.Model):
vals["we_id"] = self._get_we_id(vals.get('work_email'))
return super(JkmPracticeEmployee, self).write(vals)
def unlink(self):
for record in self:
res_partner_obj = record.env['res.partner'].sudo().search([('email', '=', record.work_email)])
if res_partner_obj:
res_partner_obj.unlink()
res = super(JkmPracticeEmployee, self).unlink()
return res
@api.depends('work_contact_id', 'work_contact_id.mobile', 'work_contact_id.email')
def _compute_work_contact_details(self):
for employee in self:

View File

@@ -24,6 +24,7 @@
'wizard/production_wizard_views.xml',
'wizard/production_technology_wizard_views.xml',
'wizard/production_technology_re_adjust_wizard_views.xml',
'wizard/mrp_workorder_batch_replan_wizard_views.xml',
'views/mrp_views_menus.xml',
'views/agv_scheduling_views.xml',
'views/stock_lot_views.xml',
@@ -38,6 +39,8 @@
'views/sf_maintenance_equipment.xml',
'views/res_config_settings_views.xml',
'views/sale_order_views.xml',
'views/mrp_workorder_batch_replan.xml',
'views/purchase_order_view.xml',
],
'assets': {
@@ -50,6 +53,8 @@
'sf_manufacturing/static/src/scss/kanban_change.scss',
'sf_manufacturing/static/src/xml/button_show_on_tree.xml',
'sf_manufacturing/static/src/js/workpiece_delivery_wizard_confirm.js',
'sf_manufacturing/static/src/js/agv_scheduling_resend_confirm.js',
'sf_manufacturing/static/src/js/agv_scheduling_cancel_confirm.js',
'sf_manufacturing/static/src/js/qr.js',
'sf_manufacturing/static/src/xml/qr.xml',
]

View File

@@ -27,7 +27,7 @@ class JikimoSaleRoutePicking(Sf_Bf_Connect):
bfm_process_order_list = json.loads(kw['bfm_process_order_list'])
order_id = request.env['sale.order'].with_user(request.env.ref("base.user_admin")).sale_order_create(
company_id, kw['delivery_name'], kw['delivery_telephone'], kw['delivery_address'],
kw['delivery_end_date'], kw['payments_way'], kw['pay_way'], state='draft')
kw['delivery_end_date'], kw['payments_way'], kw['pay_way'], kw['order_number'], state='draft')
i = 1
# 给sale_order的default_code字段赋值
# aa = request.env['sale.order'].sudo().search([('name', '=', order_id.name)])

View File

@@ -15,3 +15,4 @@ from . import sf_technology_design
from . import sf_production_common
from . import sale_order
from . import quick_easy_order
from . import purchase_order

View File

@@ -1,6 +1,7 @@
import logging
import requests
from datetime import timedelta
from odoo import models, fields, api, _
from odoo.exceptions import UserError
@@ -210,9 +211,18 @@ class AgvScheduling(models.Model):
def button_cancel(self):
# 弹出二次确认窗口后执行
for rec in self:
if rec.state != '待下发':
raise UserError('只有待下发状态的AGV调度任务才能取消')
rec.state = '已取消'
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'target': 'new',
'params': {
'message': '任务取消成功!',
'type': 'success',
'sticky': False,
'next': {'type': 'ir.actions.act_window_close'},
}
}
def finish_scheduling(self):
"""
@@ -232,7 +242,7 @@ class AgvScheduling(models.Model):
agv_route sf.agv.task.route对象
"""
for rec in self:
if rec.state != '待下发':
if rec.state not in ['待下发', '配送中']:
return False
_logger.info('AGV任务调度下发调度任务路线为%s' % agv_task_route)
rec.state = '配送中'
@@ -264,7 +274,45 @@ class AgvScheduling(models.Model):
'task_delivery_time': fields.Datetime.now()
})
return super().write(vals)
def button_cancel_confirm(self):
if self.task_delivery_time > fields.Datetime.now() - timedelta(minutes=10):
return {
'type': 'ir.actions.client',
'tag': 'agv_scheduling_cancel_confirm',
'params': {
'agv_scheduling_id': self.id,
}
}
else:
return self.button_cancel()
def button_resend_confirm(self):
if self.task_delivery_time > fields.Datetime.now() - timedelta(minutes=10):
return {
'type': 'ir.actions.client',
'tag': 'agv_scheduling_resend_confirm',
'params': {
'agv_scheduling_id': self.id,
'context': self.env.context,
}
}
else:
return self.button_resend()
def button_resend(self):
self.dispatch_scheduling(self.agv_route_id)
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'target': 'new',
'params': {
'message': '任务重新下发成功!',
'type': 'success',
'sticky': False,
'next': {'type': 'ir.actions.act_window_close'},
}
}
class ResMrpWorkOrder(models.Model):
_inherit = 'mrp.workorder'

View File

@@ -75,7 +75,7 @@ class ManualProductModelTypeRoutingSort(models.Model):
_description = '成品工序排序(人工线下加工)'
sequence = fields.Integer('Sequence')
route_workcenter_id = fields.Many2one('mrp.routing.workcenter')
route_workcenter_id = fields.Many2one('mrp.routing.workcenter', domain=[('routing_type', 'in', ['人工线下加工'])])
is_repeat = fields.Boolean('重复', related='route_workcenter_id.is_repeat')
routing_type = fields.Selection(string="工序类型", related='route_workcenter_id.routing_type')
workcenter_ids = fields.Many2many('mrp.workcenter', required=False, related='route_workcenter_id.workcenter_ids')

View File

@@ -8,6 +8,7 @@ import re
import requests
from itertools import groupby
from collections import defaultdict, namedtuple
from odoo import api, fields, models, SUPERUSER_ID, _
from odoo.exceptions import UserError, ValidationError
from odoo.addons.sf_base.commons.common import Common
@@ -18,6 +19,7 @@ class MrpProduction(models.Model):
_inherit = 'mrp.production'
_description = "制造订单"
_order = 'create_date desc'
sale_order_id = fields.Many2one('sale.order', string='销售订单', compute='_compute_sale_order_id', store=True)
deadline_of_delivery = fields.Date('订单交期', tracking=True, compute='_compute_deadline_of_delivery')
# tray_ids = fields.One2many('sf.tray', 'production_id', string="托盘")
maintenance_count = fields.Integer(compute='_compute_maintenance_count', string="Number of maintenance requests")
@@ -34,6 +36,29 @@ class MrpProduction(models.Model):
tool_state_remark = fields.Text(string='功能刀具状态备注(缺刀)', compute='_compute_tool_state_remark', store=True)
tool_state_remark2 = fields.Text(string='功能刀具状态备注(无效刀)', readonly=True)
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
def _compute_sale_order_id(self):
for production in self:
# 初始化 sale_order_id 为 False
sale_order_id = False
# 使用正则表达式查找产品名称中的 'S' 开头的字母数字字符串
match = re.search(r'S\d+', production.product_id.with_context(lang='zh_CN').name) # 从字符串开始匹配
if match:
result = match.group(0)
try:
# 查找与匹配的字符串相符的销售订单
sale_order = self.env['sale.order'].search(
[('name', '=', result)], limit=1, order='id asc'
)
if sale_order:
production.sale_order_id = sale_order.id
else:
logging.warning("No sale order found for production {} with product {} (name match: {})".format(
production.id, production.product_id.name, result))
except Exception as e:
logging.error("Error while fetching sale order for production {}: {}".format(production.id, str(e)))
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
def _compute_deadline_of_delivery(self):
for production in self:
@@ -338,24 +363,18 @@ class MrpProduction(models.Model):
# if production.state == 'pending_cam':
# if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')):
# production.state = 'done'
if any(
(
wo.test_results == '返工' and wo.state == 'done' and production.programming_state in [
'已编程']) or (
wo.state == 'rework' and production.programming_state == '编程中') or (
wo.is_rework is True and wo.state == 'done' and production.programming_state in ['编程中',
'已编程'])
for wo in
production.workorder_ids):
if any((wo.test_results == '返工' and wo.state == 'done' and production.programming_state in ['已编程'])
or (wo.is_rework is True and wo.state == 'done' and production.programming_state in ['编程中', '已编程'])
for wo in production.workorder_ids):
production.state = 'rework'
if any(wo.test_results == '报废' and wo.state == 'done' for wo in production.workorder_ids):
production.state = 'scrap'
if any(dr.test_results == '报废' and dr.handle_result == '已处理' for dr in
production.detection_result_ids):
production.state = 'cancel'
# 如果制造订单的功能刀具为【无效刀】则制造订单状态改为返工
if production.tool_state == '2':
production.state = 'rework'
if production.workorder_ids and all(wo_state in ('done', 'rework', 'cancel') for wo_state in production.workorder_ids.mapped('state')):
if production.state not in ['scrap', 'rework', 'cancel']:
production.state = 'done'
# 退回调整
def technology_back_adjust(self):
@@ -378,22 +397,55 @@ class MrpProduction(models.Model):
if process_parameters:
raise UserError(_("【工艺设计】-【参数】为%s的在【产品】中不存在,请先创建", ", ".join(process_parameters)))
if production_confirmed:
return {
'name': _('退回调整'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'sf.production.technology.re_adjust.wizard',
'target': 'new',
'context': {
'default_production_id': self.id,
'default_origin': self.origin,
}}
production_count = self.env['mrp.production'].search_count([
('origin', '=', self.origin),
('product_id', '=', self.product_id.id),
('state', '=', 'confirmed')
])
if production_count > 1:
return {
'name': _('退回调整'),
'type': 'ir.actions.act_window',
'views': [(self.env.ref(
'sf_manufacturing.sf_production_technology_re_adjust_wizard_form_view').id,
'form')],
'res_model': 'sf.production.technology.re_adjust.wizard',
'target': 'new',
'context': {
'default_production_id': self.id,
'default_origin': self.origin,
}}
else:
return {
'name': _('退回调整'),
'type': 'ir.actions.act_window',
'views': [(self.env.ref(
'sf_manufacturing.sf_production_technology_re_adjust_wizard_confirm_form_view').id,
'form')],
'res_model': 'sf.production.technology.re_adjust.wizard',
'target': 'new',
'context': {
'default_production_id': self.id,
'default_origin': self.origin,
}}
# 工艺确认
def technology_confirm(self):
process_parameters = []
account_moves = []
parameters_not = []
# 获取原有的工单对应的工序
origin_designs = self.workorder_ids.technology_design_id
# 获取已删除的工序
deleted_designs = origin_designs - self.technology_design_ids
if deleted_designs:
for deleted_design in deleted_designs:
workorder = self.env['mrp.workorder'].search([('technology_design_id', '=', deleted_design.id)])
purchase = workorder._get_surface_technics_purchase_ids()
account = self.env['account.move'].search([('id', 'in', purchase.invoice_ids.ids)])
if account.state not in ['cancel', False]:
if purchase.name not in account_moves:
account_moves.append(purchase.name)
special_design = self.technology_design_ids.filtered(
lambda a: a.routing_tag == 'special' and a.is_auto is False)
for special in special_design:
@@ -405,11 +457,7 @@ class MrpProduction(models.Model):
if not product_production_process:
if special.process_parameters_id not in process_parameters:
process_parameters.append(special.process_parameters_id.display_name)
purchase = self.env['purchase.order'].search([('origin', '=', special.production_id.name)])
account = self.env['account.move'].search([('id', 'in', purchase.invoice_ids)])
if account.state not in ['cancel', False]:
if purchase.name not in account_moves:
account_moves.append(purchase.name)
if account_moves:
raise UserError(_("请联系工厂生产经理对采购订单为%s生成的账单进行取消", ", ".join(account_moves)))
if parameters_not:
@@ -441,16 +489,37 @@ class MrpProduction(models.Model):
error_panel.append(design.panel)
else:
if not error_panel and not process_parameters:
return {
'name': _('工艺确认'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'sf.production.technology.wizard',
'target': 'new',
'context': {
'default_production_id': self.id,
'default_origin': self.origin,
}}
production_count = self.env['mrp.production'].search_count([
('origin', '=', self.origin),
('product_id', '=', self.product_id.id),
('state', '=', 'technology_to_confirmed')
])
if production_count > 1:
return {
'name': _('工艺确认'),
'type': 'ir.actions.act_window',
'views': [(self.env.ref(
'sf_manufacturing.sf_production_technology_wizard_form_view').id,
'form')],
'res_model': 'sf.production.technology.wizard',
'target': 'new',
'context': {
'default_production_id': self.id,
'default_origin': self.origin,
}}
else:
return {
'name': _('工艺确认'),
'type': 'ir.actions.act_window',
'views': [(self.env.ref(
'sf_manufacturing.sf_production_technology_wizard_confirm_form_view').id,
'form')],
'res_model': 'sf.production.technology.wizard',
'target': 'new',
'context': {
'default_production_id': self.id,
'default_origin': self.origin,
}}
if error_panel:
raise UserError(_("【加工面】为%s的标准工序顺序有误,请调整后重试", ", ".join(error_panel)))
return True
@@ -694,7 +763,8 @@ class MrpProduction(models.Model):
}]
if production.product_id.categ_id.type in ['成品', '坯料']:
# # 根据工序设计生成工单
for route in production.technology_design_ids:
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:
@@ -733,47 +803,48 @@ class MrpProduction(models.Model):
workorder.move_subcontract_workorder_ids.picking_id.write({'state': 'cancel'})
consecutive_workorders = []
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.sequence)
for i, workorder in enumerate(sorted_workorders):
# 检查当前工作订单和下一个工作订单是否连续,并且供应商相同
if i == 0:
consecutive_workorders.append(workorder)
elif workorder.sequence == sorted_workorders[
i - 1].sequence + 1 and workorder.supplier_id.id == sorted_workorders[i - 1].supplier_id.id:
consecutive_workorders.append(workorder)
else:
# 处理连续组,如果它不为空
if consecutive_workorders:
proc_workorders.append(consecutive_workorders)
# 创建外协出入库单和采购订单
# self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production, sorted_workorders)
# self.env['purchase.order'].get_purchase_order(consecutive_workorders, production,
# product_id_to_production_names)
if i < len(sorted_workorders) - 1:
# 重置连续组,并添加当前工作订单
consecutive_workorders = [workorder]
else:
# 判断最后一笔:
if workorder.sequence == sorted_workorders[
i - 1].sequence and workorder.supplier_id.id == sorted_workorders[
i - 1].supplier_id.id:
consecutive_workorders = [workorder]
else:
proc_workorders.append([workorder])
# 立即创建外协出入库单和采购订单
# self.env['stock.picking'].create_outcontract_picking(workorder, production)
# self.env['purchase.order'].get_purchase_order(workorder, production,
# product_id_to_production_names)
consecutive_workorders = []
# 处理最后一个组,即使它可能只有一个工作订单
if consecutive_workorders:
proc_workorders.append(consecutive_workorders)
# for i, workorder in enumerate(sorted_workorders):
# # 检查当前工作订单和下一个工作订单是否连续,并且供应商相同
# if i == 0:
# consecutive_workorders.append(workorder)
# elif workorder.sequence == sorted_workorders[
# i - 1].sequence + 1 and workorder.supplier_id.id == sorted_workorders[i - 1].supplier_id.id:
# consecutive_workorders.append(workorder)
# else:
# # 处理连续组,如果它不为空
# if consecutive_workorders:
# proc_workorders.append(consecutive_workorders)
# # 创建外协出入库单和采购订单
# # self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production, sorted_workorders)
# # self.env['purchase.order'].get_purchase_order(consecutive_workorders, production,
# # product_id_to_production_names)
# if i < len(sorted_workorders) - 1:
# # 重置连续组,并添加当前工作订单
# consecutive_workorders = [workorder]
# else:
# # 判断最后一笔:
# if workorder.sequence == sorted_workorders[
# i - 1].sequence and workorder.supplier_id.id == sorted_workorders[
# i - 1].supplier_id.id:
# consecutive_workorders = [workorder]
# else:
# proc_workorders.append([workorder])
# # 立即创建外协出入库单和采购订单
# # self.env['stock.picking'].create_outcontract_picking(workorder, production)
# # self.env['purchase.order'].get_purchase_order(workorder, production,
# # product_id_to_production_names)
# consecutive_workorders = []
#
# # 处理最后一个组,即使它可能只有一个工作订单
# if consecutive_workorders:
# proc_workorders.append(consecutive_workorders)
# self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production)
# self.env['purchase.order'].get_purchase_order(consecutive_workorders, production,
# product_id_to_production_names)
for workorders in reversed(proc_workorders):
for workorders in reversed(sorted_workorders):
self.env['stock.picking'].create_outcontract_picking(workorders, production, sorted_workorders)
self.env['purchase.order'].get_purchase_order(workorders, production, product_id_to_production_names)
# 工单排序
def _reset_work_order_sequence1(self, k):
for rec in self:
@@ -887,10 +958,11 @@ class MrpProduction(models.Model):
work_ids = workorder_ids.filtered(lambda item: item.sequence == 0)
# 对工单进行逐个插入
for work_id in work_ids:
for order_id in rec.workorder_ids.filtered(lambda item: item.sequence > 0):
if work_id.name == order_id.name:
work_id.sequence = order_id.sequence + 1
break
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)
@@ -1170,7 +1242,9 @@ class MrpProduction(models.Model):
lambda wk: (wk.name == result_id.routing_type and wk.processing_panel == result_id.processing_panel
and wk.state == 'done')).id
for result_id in result_ids]
workorder_ids = self.workorder_ids.filtered(
lambda wk: wk.technology_design_id.routing_tag == 'standard' and wk.state not in ['rework', 'cancel'])
logging.info('标准工艺工单【%s' % workorder_ids)
return {
'name': _('返工'),
'type': 'ir.actions.act_window',
@@ -1179,7 +1253,7 @@ class MrpProduction(models.Model):
'target': 'new',
'context': {
'default_production_id': self.id,
'default_workorder_ids': self.workorder_ids.ids,
'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['reprogramming_num'],
'default_programming_state': cloud_programming['programming_state'],
@@ -1435,6 +1509,26 @@ class MrpProduction(models.Model):
for production in self:
production.production_type = '自动化产线加工' if not production.product_id.is_manual_processing else '人工线下加工'
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
def _compute_sale_order_count(self):
for production in self:
if production.sale_order_id:
production.sale_order_count = 1
else:
production.sale_order_count = 0
def action_view_sale_orders(self):
if self.sale_order_id:
action = {
'res_model': 'sale.order',
'type': 'ir.actions.act_window',
}
action.update({
'view_mode': 'form',
'res_id': self.sale_order_id.id,
})
return action
@api.model_create_multi
def create(self, vals_list):
"""
@@ -1484,18 +1578,26 @@ class MrpProduction(models.Model):
first_order = self.env['mrp.production'].search(
[('origin', '=', order.origin), ('product_id', '=', order.product_id.id)], limit=1, order='id asc')
order.picking_ids = self.env['stock.picking'].search([
('group_id', '=', first_order.procurement_group_id.id), ('group_id', '!=', False),
('group_id', '!=', False), ('state', '!=', 'cancel'),
'|', # 表示“或”的开始
('group_id', '=', order.procurement_group_id.id),
('related_group_ids', '=', order.procurement_group_id.id)
])
order.delivery_count = len(first_order.picking_ids)
else:
order.picking_ids = self.env['stock.picking'].search([
('group_id', '=', order.procurement_group_id.id), ('group_id', '!=', False),
('group_id', '!=', False),('state', '!=', 'cancel'),
'|', # 表示“或”的开始
('group_id', '=', order.procurement_group_id.id),
('related_group_ids', '=', order.procurement_group_id.id)
])
order.delivery_count = len(order.picking_ids)
def action_view_purchase_orders(self):
self.ensure_one()
if self.product_id.product_tmpl_id.single_manufacturing == True:
if self.is_remanufacture:
production = self
elif self.product_id.product_tmpl_id.single_manufacturing == True:
production = self.env['mrp.production'].search(
[('origin', '=', self.origin), ('product_id', '=', self.product_id.id)], limit=1, order='id asc')
else:

View File

@@ -94,4 +94,10 @@ class ResMrpRoutingWorkcenter(models.Model):
route_ids.append(t.route_id.surface_technics_id.id)
domain = [('id', 'not in', route_ids), ('routing_tag', '=', 'special')]
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
if self._context.get('is_duplicate') and self._context.get('model_name'):
# 查询出已经选择的工序
model_type = self.env[self._context.get('model_name')].search_read([],['route_workcenter_id'])
route_workcenter_ids = [item['route_workcenter_id'][0] if item['route_workcenter_id'] else False for item in model_type]
domain = args + [('id', 'not in', route_workcenter_ids)]
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
return super()._name_search(name, args, operator, limit, name_get_uid)

View File

@@ -36,7 +36,7 @@ class ResMrpWorkOrder(models.Model):
store=True, check_company=True, string="材料")
product_tmpl_id_materials_type_id = fields.Many2one(related='production_id.product_tmpl_id.materials_type_id',
readonly=True, store=True, check_company=True, string="型号")
workcenter_id = fields.Many2one('mrp.workcenter', string='工作中心', required=False)
# workcenter_id = fields.Many2one('mrp.workcenter', string='工作中心', required=False)
users_ids = fields.Many2many("res.users", 'users_workorder', related="workcenter_id.users_ids")
processing_panel = fields.Char('加工面')
sequence = fields.Integer(string='工序')
@@ -129,7 +129,7 @@ class ResMrpWorkOrder(models.Model):
Y10_axis = fields.Float(default=0)
Z10_axis = fields.Float(default=0)
X_deviation_angle = fields.Integer(string="X轴偏差度", default=0)
test_results = fields.Selection([("合格", "合格")], default='合格',
test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")], default='合格',
string="检测结果", tracking=True)
cnc_ids = fields.One2many("sf.cnc.processing", 'workorder_id', string="CNC加工程序")
cmm_ids = fields.One2many("sf.cmm.program", 'workorder_id', string="CMM程序")
@@ -138,7 +138,8 @@ class ResMrpWorkOrder(models.Model):
is_subcontract = fields.Boolean(string='是否外协')
surface_technics_parameters_id = fields.Many2one('sf.production.process.parameter', string="表面工艺可选参数")
picking_ids = fields.Many2many('stock.picking', string='外协出入库单', compute='_compute_surface_technics_picking_ids')
picking_ids = fields.Many2many('stock.picking', string='外协出入库单',
compute='_compute_surface_technics_picking_ids')
purchase_id = fields.Many2many('purchase.order', string='外协采购单')
surface_technics_picking_count = fields.Integer("外协出入库", compute='_compute_surface_technics_picking_ids')
@@ -327,7 +328,7 @@ class ResMrpWorkOrder(models.Model):
'view_mode': 'form',
}
return result
def _get_surface_technics_purchase_ids(self):
domain = [('origin', '=', self.production_id.name), ('purchase_type', '=', 'consignment')]
purchase_orders = self.env['purchase.order'].search(domain)
@@ -1114,9 +1115,7 @@ class ResMrpWorkOrder(models.Model):
# ================= 如果制造订单刀具状态为[无效刀、缺刀] 或者 制造订单状态为[返工]==========================
if (workorder.production_id.tool_state in ['1', '2'] or workorder.production_id.state == 'rework'
or workorder.production_id.schedule_state != '已排'
or workorder.production_id.reservation_state not in ['assigned']
or workorder.production_id.workorder_ids.filtered(
lambda wk: wk.sequence == workorder.sequence - 1).test_results in ['报废', '返工']):
or workorder.production_id.reservation_state not in ['assigned']):
if workorder.state != 'waiting':
workorder.state = 'waiting'
continue
@@ -1425,13 +1424,14 @@ class ResMrpWorkOrder(models.Model):
len(done_workorder) == len(record.production_id.workorder_ids)):
is_production_id = True
if record.routing_type in ['解除装夹'] or (
record.is_rework is True and record.routing_type in ['装夹预调']) or (
record.test_results in ['返工', '报废'] and record.routing_type in ['CNC加工']):
record.is_rework is True and record.routing_type in ['装夹预调']):
for workorder in record.production_id.workorder_ids:
if workorder.processing_panel == record.processing_panel:
rfid_code = workorder.rfid_code
workorder.write({'rfid_code_old': rfid_code,
'rfid_code': False})
self.env['stock.lot'].sudo().search([('rfid', '=', rfid_code)]).write(
{'tool_material_status': '可用'})
if workorder.rfid_code:
raise ValidationError(f'{workorder.name}】工单解绑失败,请重新点击完成按钮!!!')
# workorder.rfid_code_old = rfid_code
@@ -1570,7 +1570,7 @@ class ResMrpWorkOrder(models.Model):
'default_confirm_button': '确认解除',
# 'default_feeder_station_start_id': feeder_station_start_id,
}}
move_subcontract_workorder_ids = fields.One2many('stock.move', 'subcontract_workorder_id', string='组件')
@@ -1781,6 +1781,7 @@ class SfWorkOrderBarcodes(models.Model):
if workorder_rfid:
for item in workorder_rfid:
item.write({'rfid_code': barcode})
lot.sudo().write({'tool_material_status': '在用'})
logging.info("Rfid[%s]绑定成功!!!" % barcode)
else:
raise UserError('该Rfid【%s】绑定的是【%s】, 不是托盘!!!' % (barcode, lot.product_id.name))

View File

@@ -774,11 +774,10 @@ class ResProductMo(models.Model):
# bfm下单
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
part_number = fields.Char(string='零件图号', readonly=True)
machining_drawings = fields.Binary('2D加工图纸', readonly=True)
quality_standard = fields.Binary('质检标准', readonly=True)
part_name = fields.Char(string='零件名称', readonly=True)
part_number = fields.Char(string='零件图号', readonly=True)
@api.constrains('tool_length')
def _check_tool_length_size(self):
if self.tool_length > 1000000:
@@ -892,7 +891,7 @@ class ResProductMo(models.Model):
'machining_drawings': '' if not item['machining_drawings'] else base64.b64decode(
item['machining_drawings']),
'quality_standard': '' if not item['quality_standard'] else base64.b64decode(item['quality_standard']),
'part_name': item['part_name'],
'part_name': item.get('part_name') or '',
}
tax_id = self.env['account.tax'].sudo().search(
[('type_tax_use', '=', 'sale'), ('amount', '=', item.get('tax')), ('price_include', '=', 'True')])

View File

@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from odoo import api, fields, models, _
from odoo.tools import OrderedSet
# _get_surface_technics_purchase_ids
class PurchaseOrder(models.Model):
_inherit = 'purchase.order'
def button_confirm(self):
super().button_confirm()
workorders = self.env['mrp.workorder'].search([('purchase_id', '=', self.id)])
for workorder in workorders:
if workorder.routing_type == '表面工艺' and workorder.is_subcontract is True:
move_out = workorder.move_subcontract_workorder_ids[1]
# move_out = self.env['stock.move'].search(
# [('location_id', '=', self.env['stock.location'].search(
# [('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
# ('location_dest_id', '=', self.env['stock.location'].search(
# [('barcode', 'ilike', 'VL-SPOC')]).id),
# ('origin', '=', self.production_id.name), ('state', 'not in', ['cancel', 'done'])])
for mo in move_out:
if mo.state != 'done':
mo.write({'state': 'assigned', 'production_id': False})
if not mo.move_line_ids:
self.env['stock.move.line'].create(mo.get_move_line(workorder.production_id, workorder))
return True
class PurchaseOrderLine(models.Model):
_inherit = 'purchase.order.line'
part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True)
related_product = fields.Many2one('product.product',compute='_compute_related_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

View File

@@ -13,6 +13,9 @@ class SaleOrder(models.Model):
('sent', "报价已发送"),
('supply method', "供货方式待确认"),
('sale', "销售订单"),
('processing', "加工中"),
('physical_distribution', "物流中"),
('delivered', "已交付"),
('done', "已锁定"),
('cancel', "已取消"),
])
@@ -145,11 +148,64 @@ class SaleOrder(models.Model):
self.env.ref("base.user_admin")).bom_create(product, bom_type, 'product')
product_bom_purchase.with_user(self.env.ref("base.user_admin")).bom_create_line_has(
purchase_embryo)
return super(SaleOrder, self).action_confirm()
result = super(SaleOrder, self).action_confirm()
self.merge_picking()
return result
def merge_picking(self):
"""
合并多个stock.picking为一个新的stock.picking。
:param picking_ids: 需要合并的stock.picking记录ID列表
:return: 合并后的新的stock.picking对象
"""
picking_idss = self.env['stock.picking'].search([('retrospect_ref', 'ilike','%'+ self.name +'%')])
for pick in picking_idss:
print('qfwowio',pick)
picking_ids = self.env['stock.picking'].search([('retrospect_ref', '=', self.default_code)])
if not picking_ids:
return
# 获取需要合并的 stock.picking 记录
# 创建一个新的 stock.picking 作为合并结果
group = self.env['procurement.group'].create({
'name': self.name,
'partner_id': self.partner_id.id,
})
new_picking = self.env['stock.picking'].create({
'partner_id': picking_ids[0].partner_id.id,
'location_id': picking_ids[0].location_id.id,
'location_dest_id': picking_ids[0].location_dest_id.id,
'move_ids': False,
'sale_id': self.id,
'picking_type_id': picking_ids[0].picking_type_id.id,
'origin': '合并自: ' + ','.join([p.origin for p in picking_ids]),
'retrospect_ref':self.default_code,
'person_of_delivery':picking_ids[0].person_of_delivery,
'telephone_of_delivery': picking_ids[0].telephone_of_delivery,
'address_of_delivery': picking_ids[0].address_of_delivery,
'group_id':picking_ids[0].group_id.id
})
# 合并所有 move_lines
for picking in picking_ids:
for move in picking.move_ids:
# 复制 move_lines 到新的 picking
new_move_vals = move.copy_data()[0]
new_move_vals['picking_id']=new_picking.id
self.env['stock.move'].create(new_move_vals)
for pick in picking_idss:
print('qfwowio',pick)
# 处理合并后的配送单状态
new_picking.action_confirm() # 确认新的配送单
for pick in picking_idss:
print('qfwowio',pick)
new_picking.action_assign() # 分配新的配送单
# 删除原有的配送单
picking_ids.write({'state': 'cancel'}) # 将原配送单状态更改为取消
return new_picking
class SaleOrderLine(models.Model):
_inherit = 'sale.order.line'
part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True)
# 供货方式
supply_method = fields.Selection([
('automation', "自动化产线加工"),

View File

@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
from collections import Counter
from odoo import fields, models, api, _
from odoo.exceptions import ValidationError
@@ -6,7 +8,7 @@ from odoo.exceptions import ValidationError
class sf_technology_design(models.Model):
_name = 'sf.technology.design'
_description = "工艺设计"
group_uniq_id = fields.Integer('同一制造订单唯一id',default=0)
sequence = fields.Integer('序号')
route_id = fields.Many2one('mrp.routing.workcenter', '工序')
process_parameters_id = fields.Many2one('sf.production.process.parameter', string='表面工艺参数')
@@ -17,6 +19,11 @@ class sf_technology_design(models.Model):
is_auto = fields.Boolean('是否自动生成', default=False)
active = fields.Boolean('有效', default=True)
# @api.depends('production_id')
# def _compute_group_uniq_id(self):
# for record in self:
def json_technology_design_str(self, k, route, i, process_parameter):
workorders_values_str = [0, '', {
'route_id': route.id if route.routing_type in ['表面工艺'] else route.route_workcenter_id.id,
@@ -28,6 +35,9 @@ class sf_technology_design(models.Model):
'is_auto': True}]
return workorders_values_str
def write(self, vals):
print('qwfojkqwfkio')
return super(sf_technology_design, self).write(vals)
def unlink_technology_design(self):
self.active = False
@@ -37,4 +47,64 @@ class sf_technology_design(models.Model):
for vals in vals_list:
if not vals.get('route_id'):
raise ValidationError(_("工序不能为空"))
return super(sf_technology_design, self).create(vals_list)
result = super(sf_technology_design, self).create(vals_list)
for res in result:
record = self.search([('production_id', '=', res.production_id.id), ('active', 'in', [True, False])], order='group_uniq_id desc', limit=1)
res.group_uniq_id=record.group_uniq_id + 1
return result
def get_duplicates_with_inactive(self,technology_designs):
# 统计每个 'sequence' 出现的次数
sequence_count = Counter(technology_design.sequence for technology_design in technology_designs)
# 筛选出 'sequence' 重复且 'active' 为 False 的元素
result = [
technology_design for technology_design in technology_designs
if sequence_count[technology_design.sequence] > 1 and technology_design.active is False
]
return result
# def rearrange_numbering(self,self_technology_designs):
# inactive_designs = self.get_duplicates_with_inactive(self_technology_designs)
# if inactive_designs:
# max_design = max(self_technology_designs, key=lambda x: x.sequence)
# max_sequence = max_design.sequence if max_design else 0
# for designs in inactive_designs:
# max_sequence += 1
# designs.sequence = max_sequence
# self_technology_designs.sorted(key=lambda techology_design:techology_design.sequence)
# return self_technology_designs
def get_technology_design(self):
return {
'sequence':self.sequence,
'route_id': self.route_id.id,
'process_parameters_id': self.process_parameters_id.id,
'panel': self.panel,
'routing_tag': self.routing_tag,
'time_cycle_manual': self.time_cycle_manual,
'is_auto': self.is_auto,
'active': self.active,
'group_uniq_id':self.group_uniq_id,
}
def sync_technology_designs(self,production_technology_designs, self_technology_designs):
production_id = production_technology_designs[0].production_id.id
self_technology_design_dict = {item.group_uniq_id:item for item in self_technology_designs}
production_technology_designs_dict = {item.group_uniq_id:item for item in production_technology_designs}
for technology_design in production_technology_designs:
if not self_technology_design_dict.get(technology_design.group_uniq_id):
technology_design.write({'production_id': False})
else:
technology_design.write(self_technology_design_dict.get(technology_design.group_uniq_id).get_technology_design())
for technology_design in self_technology_designs:
if not production_technology_designs_dict.get(technology_design.group_uniq_id):
technology_design = technology_design.get_technology_design()
technology_design.update({'production_id': production_id})
technology_design.pop('group_uniq_id')
self.env['sf.technology.design'].create(technology_design)
def unified_procedure_multiple_work_orders(self,self_technology_designs,production_item):
technology_designs = self.env['sf.technology.design'].sudo().search(
[('production_id', '=', production_item.id), ('active', 'in', [True, False])])
self.sync_technology_designs(self_technology_designs=self_technology_designs,production_technology_designs=technology_designs)

View File

@@ -587,16 +587,28 @@ class StockPicking(models.Model):
telephone_of_delivery = fields.Char('电话号码', compute='_compute_move_ids', store=True)
address_of_delivery = fields.Char('联系地址', compute='_compute_move_ids', store=True)
retrospect_ref = fields.Char('追溯参考', compute='_compute_move_ids', store=True)
retrospect_ref = fields.Char('平台订单号', compute='_compute_move_ids', store=True)
sale_name = fields.Char('销售订单', compute='_compute_move_ids', store=True)
picking_type_sequence_code = fields.Char(related='picking_type_id.sequence_code')
related_group_ids = fields.Many2many(
'procurement.group', string='关联捕获组',
readonly=True, compute='_compute_related_group_ids', store=True)
@api.depends('origin')
def _compute_related_group_ids(self):
for record in self:
if not record.origin:
continue
names = record.origin.split(',')
related_group_ids = self.env['procurement.group'].search([('name', 'in', names)])
record.related_group_ids = [(6, 0, related_group_ids.ids)]
@api.depends('move_ids', 'move_ids.product_id')
def _compute_move_ids(self):
for item in self:
if item.move_ids:
if item.picking_type_id.sequence_code == 'DL':
sale_name = item.move_ids[0].product_id.name.split('-')[1]
sale_name = item.move_ids[0].product_id.with_context(lang='zh_CN').name.split('-')[1]
item.sale_name=sale_name
if 'S' in sale_name:
sale_id = self.env['sale.order'].sudo().search([('name', '=', sale_name)])
item.person_of_delivery = sale_id.person_of_delivery
@@ -644,20 +656,27 @@ 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).sorted('sequence')
if workorder == subcontract_workorders[-1]:
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,
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:
move_out = self.move_ids
if move_out:
workorder = move_out.subcontract_workorder_id
workorder.button_start()
return res
# 创建 外协出库入单
def create_outcontract_picking(self, workorders, item, sorted_workorders):
for workorder in workorders:
if workorder.move_subcontract_workorder_ids:
workorder.move_subcontract_workorder_ids.write({'state': 'draft'})
workorder.move_subcontract_workorder_ids.picking_id.write({'state': 'draft'})
workorder.move_subcontract_workorder_ids.write({'state': 'waiting'})
workorder.move_subcontract_workorder_ids.picking_id.write({'state': 'waiting'})
else:
# 创建一个新的补货组
procurement_group_id = self.env['procurement.group'].create({
@@ -665,20 +684,21 @@ class StockPicking(models.Model):
'partner_id': self.partner_id.id,
})
move_dest_id = False
# 如果当前工单是是制造订单的最后一个工单
if workorder == item.workorder_ids[-1]:
# 如果当前工单是是制造订单的最后一个工艺外协工
if workorder == next((workorder for workorder in reversed(item.workorder_ids) if workorder.is_subcontract), None):
move_dest_id = item.move_raw_ids[0].id
else:
# 从sorted_workorders中找到上一工单的move
if sorted_workorders.index(workorder) > 0:
move_dest_id = sorted_workorders[sorted_workorders.index(workorder) - 1].move_subcontract_workorder_ids[1].id
if len(sorted_workorders) > 1:
move_dest_id = sorted_workorders[sorted_workorders.index(workorder)+1].move_subcontract_workorder_ids[1].id
new_picking = True
outcontract_picking_type_in = self.env.ref(
'sf_manufacturing.outcontract_picking_in').id,
outcontract_picking_type_out = self.env.ref(
'sf_manufacturing.outcontract_picking_out').id,
moves_in = self.env['stock.move'].sudo().create(
self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_in, procurement_group_id.id, move_dest_id))
self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_in,
procurement_group_id.id, move_dest_id))
picking_in = self.create(
moves_in._get_new_picking_values_Res(item, workorder, 'WH/OCIN/'))
# pick_ids.append(picking_in.id)
@@ -686,7 +706,8 @@ class StockPicking(models.Model):
{'picking_id': picking_in.id, 'state': 'waiting'})
moves_in._assign_picking_post_process(new=new_picking)
moves_out = self.env['stock.move'].sudo().create(
self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_out, procurement_group_id.id, moves_in.id))
self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_out,
procurement_group_id.id, moves_in.id))
workorder.write({'move_subcontract_workorder_ids': [(6, 0, [moves_in.id, moves_out.id])]})
picking_out = self.create(
moves_out._get_new_picking_values_Res(item, workorder, 'WH/OCOUT/'))
@@ -694,8 +715,7 @@ class StockPicking(models.Model):
moves_out.write(
{'picking_id': picking_out.id, 'state': 'waiting'})
moves_out._assign_picking_post_process(new=new_picking)
@api.depends('move_type', 'immediate_transfer', 'move_ids.state', 'move_ids.picking_id')
def _compute_state(self):
super(StockPicking, self)._compute_state()
@@ -706,7 +726,9 @@ class StockPicking(models.Model):
if picking.move_ids:
workorder = picking.move_ids[0].subcontract_workorder_id
if picking.state == 'assigned':
if workorder.state in ['pending', 'waiting'] or workorder._get_surface_technics_purchase_ids().state in ['draft', 'sent']:
if workorder.state in ['pending',
'waiting'] or workorder._get_surface_technics_purchase_ids().state in [
'draft', 'sent']:
picking.state = 'waiting'
@@ -719,7 +741,8 @@ class ReStockMove(models.Model):
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([('route_id', '=', route_id), ('picking_type_id', '=', picking_type_id)])
stock_rule = self.env['stock.rule'].sudo().search(
[('route_id', '=', route_id), ('picking_type_id', '=', picking_type_id)])
move_values = {
'name': '',
'company_id': item.company_id.id,
@@ -993,11 +1016,13 @@ class ReStockMove(models.Model):
production = self.env['mrp.production'].search([('name', '=', self[0].origin)], limit=1, order='id asc')
productions = self.env['mrp.production'].search(
[('origin', '=', production.origin), ('product_id', '=', production.product_id.id)])
res['origin'] = ','.join(productions.mapped('name'))
if productions.mapped('name'):
res['origin'] = ','.join(productions.mapped('name'))
res['retrospect_ref'] = production.product_id.name
return res
subcontract_workorder_id = fields.Many2one('mrp.workorder', '外协工单组件', check_company=True, index='btree_not_null')
subcontract_workorder_id = fields.Many2one('mrp.workorder', '外协工单组件', check_company=True,
index='btree_not_null')
class ReStockQuant(models.Model):

View File

@@ -182,3 +182,5 @@ access_sf_manual_product_model_type_routing_sort_group_sf_mrp_user,sf_manual_pro
access_sf_manual_product_model_type_routing_sort_manager,sf_manual_product_model_type_routing_sort,model_sf_manual_product_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,1
access_sf_manual_product_model_type_routing_sort_group_plan_dispatch,sf_manual_product_model_type_routing_sort_group_plan_dispatch,model_sf_manual_product_model_type_routing_sort,sf_base.group_plan_dispatch,1,0,0,0
access_sf_detection_result_manager,sf_detection_result_manager,model_sf_detection_result,,1,1,1,1
access_mrp_workorder_batch_replan_wizard_group_plan_dispatch,mrp_workorder_batch_replan_wizard_group_plan_dispatch,model_mrp_workorder_batch_replan_wizard,sf_base.group_plan_dispatch,1,1,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
182
183
184
185
186

View File

@@ -0,0 +1,50 @@
odoo.define('sf_manufacturing.agv_scheduling_button_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 agv_scheduling_cancel_confirm(parent, {params}) {
const dialog = new Dialog(parent, {
title: "确认",
$content: $('<div>').append("工件正在配送中,确定取消"),
buttons: [
{ text: "确认", classes: 'btn-primary', close: true, click: () => dispatchConfirmed(parent, params) },
{ text: "取消", close: true },
],
});
dialog.open();
async function dispatchConfirmed(parent, params) {
rpc.query({
model: 'sf.agv.scheduling',
method: 'button_cancel',
args: [params.agv_scheduling_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'},
}
});
parent.services.action.doAction({
'type': 'ir.actions.client',
'tag': 'reload',
});
})
}
}
core.action_registry.add('agv_scheduling_cancel_confirm', agv_scheduling_cancel_confirm);
return agv_scheduling_cancel_confirm;
});

View File

@@ -0,0 +1,50 @@
odoo.define('sf_manufacturing.agv_scheduling_resend_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 agv_scheduling_resend_confirm(parent, {params}) {
const dialog = new Dialog(parent, {
title: "确认",
$content: $('<div>').append("工件正在配送中,确定重新下发"),
buttons: [
{ text: "确认", classes: 'btn-primary', close: true, click: () => dispatchConfirmed(parent, params) },
{ text: "取消", close: true },
],
});
dialog.open();
async function dispatchConfirmed(parent, params) {
rpc.query({
model: 'sf.agv.scheduling',
method: 'button_resend',
args: [params.agv_scheduling_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'},
}
});
parent.services.action.doAction({
'type': 'ir.actions.client',
'tag': 'reload',
});
})
}
}
core.action_registry.add('agv_scheduling_resend_confirm', agv_scheduling_resend_confirm );
return agv_scheduling_resend_confirm;
});

View File

@@ -22,8 +22,7 @@ odoo.define('sf_manufacturing.action_dispatch_confirm', function (require) {
rpc.query({
model: 'sf.workpiece.delivery.wizard',
method: 'confirm',
args: [params.active_id]
,
args: [params.active_id],
kwargs: {
context: params.context,
}

View File

@@ -33,6 +33,20 @@
class="btn-danger"
confirm="你确定要取消这条记录吗?"
/>
<button
name="button_cancel_confirm"
string="取消" type="object"
attrs="{'invisible': [('state', '!=', '配送中')]}"
icon="fa-times"
class="btn-danger"
/>
<button
name="button_resend_confirm"
string="重新下发" type="object"
attrs="{'invisible': [('state', '!=', '配送中')]}"
icon="fa-circle-o-notch"
class="btn-success "
/>
</tree>
</field>
</record>

View File

@@ -3,7 +3,7 @@
<data>
#------------------模型类型------------------
<record model="ir.ui.view" id="search_sf_model_type_view">
<record model="ir.ui.view" id="search_sf_model_name_view">
<field name="name">search.sf.model.type</field>
<field name="model">sf.model.type</field>
<field name="arch" type="xml">
@@ -14,7 +14,7 @@
</field>
</record>
<record model="ir.ui.view" id="tree_sf_model_type_view">
<record model="ir.ui.view" id="tree_sf_model_name_view">
<field name="name">tree.sf.model.type</field>
<field name="model">sf.model.type</field>
<field name="arch" type="xml">
@@ -24,7 +24,7 @@
</field>
</record>
<record model="ir.ui.view" id="form_sf_model_type">
<record model="ir.ui.view" id="form_sf_model_name">
<field name="name">form.sf.model.type</field>
<field name="model">sf.model.type</field>
<field name="arch" type="xml">
@@ -37,7 +37,7 @@
<field name='product_routing_tmpl_ids'>
<tree editable='bottom'>
<field name="sequence" widget="handle" string="序号"/>
<field name="route_workcenter_id" string="工序" options="{'no_create': True}"/>
<field name="route_workcenter_id" string="工序" options="{'no_create': True}" context="{'is_duplicate': True, 'model_name': 'sf.product.model.type.routing.sort'}"/>
<field name="routing_type" string="类型"/>
<field name="is_repeat" string="重复"/>
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
@@ -48,7 +48,7 @@
<field name='manual_product_routing_tmpl_ids'>
<tree editable='bottom'>
<field name="sequence" widget="handle" string="序号"/>
<field name="route_workcenter_id" string="工序" options="{'no_create': True}"/>
<field name="route_workcenter_id" string="工序" options="{'no_create': True}" context="{'is_duplicate': True, 'model_name': 'sf.manual.product.model.type.routing.sort'}"/>
<field name="routing_type" string="类型"/>
<field name="is_repeat" string="重复"/>
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
@@ -59,7 +59,7 @@
<field name='embryo_routing_tmpl_ids'>
<tree editable='bottom'>
<field name="sequence" widget="handle" string="序号"/>
<field name="route_workcenter_id" string="工序" options="{'no_create': True}"/>
<field name="route_workcenter_id" string="工序" options="{'no_create': True}" context="{'is_duplicate': True, 'model_name': 'sf.embryo.model.type.routing.sort'}"/>
<field name="routing_type" string="类型"/>
<field name="is_repeat" string="重复"/>
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
@@ -70,7 +70,7 @@
<field name='surface_technics_routing_tmpl_ids' style="white-space: pre-wrap;">
<tree editable='bottom'>
<field name="sequence" widget="handle" string="序号"/>
<field name="route_workcenter_id" string="工序" options="{'no_create': True}"/>
<field name="route_workcenter_id" string="工序" options="{'no_create': True}" context="{'is_duplicate': True, 'model_name': 'sf.surface_technics.model.type.routing.sort'}"/>
<field name="routing_type" string="类型"/>
<field name="is_repeat" string="重复"/>
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
@@ -81,7 +81,7 @@
</field>
</record>
<record id="action_sf_model_type" model="ir.actions.act_window">
<record id="action_sf_model_name" model="ir.actions.act_window">
<field name="name">模型类型</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sf.model.type</field>
@@ -96,11 +96,11 @@
</record>
<menuitem
id="menu_sf_model_type"
id="menu_sf_model_name"
name="模型类型"
parent="mrp.menu_mrp_configuration"
sequence="9"
action="action_sf_model_type"
action="action_sf_model_name"
/>
</data>
</odoo>

View File

@@ -18,9 +18,12 @@
<xpath expr="//field[@name='date_deadline']" position="replace"/>
<xpath expr="//field[@name='name']" position="after">
<field name="product_id" readonly="1" optional="show"/>
<field name="part_name" readonly="1" optional="hide"/>
<field name="part_number" readonly="1" optional="show"/>
</xpath>
<xpath expr="//field[@name='product_id']" position="after">
<field name="product_qty" sum="Total Qty" string="数量" readonly="1" optional="show"/>
<field name="deadline_of_delivery" optional="show"/>
</xpath>
<xpath expr="//field[@name='product_qty']" position="after">
<field name="product_uom_id" string="计量单位" options="{'no_open':True,'no_create':True}"
@@ -43,6 +46,7 @@
<field name="activity_ids" string="下一个活动" widget="list_activity" optional="hide"/>
</xpath>
<xpath expr="//field[@name='origin']" position="replace">
<field name="sale_order_id" optional="show"/>
<field name="origin" optional="hide"/>
</xpath>
<xpath expr="//field[@name='components_availability']" position="replace">
@@ -123,14 +127,10 @@
<field name="tool_state"
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"/>
<field name="tool_state_remark" string="备注" attrs="{'invisible': [('tool_state', '!=', '1')]}"/>
<field name="sale_order_id" readonly="1"/>
<field name="deadline_of_delivery" readonly="1"/>
<field name="tool_state_remark2" invisible="1"/>
</xpath>
<xpath expr="//header//button[@name='action_cancel']" position="replace">
<button name="action_cancel" type="object" string="取消" data-hotkey="z"
attrs="{'invisible': ['|', '|', ('id', '=', False), ('state', 'in', ('done', 'cancel')), ('confirm_cancel', '=', True)]}"
groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="(//header//button[@name='button_mark_done'])[1]" position="replace">
<button name="button_mark_done"
attrs="{'invisible': ['|', '|', ('state', 'in', ('draft', 'cancel', 'done', 'to_close')), ('qty_producing', '=', 0), ('move_raw_ids', '!=', [])]}"
@@ -255,13 +255,6 @@
type="object" groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="//header//button[@name='action_cancel']" position="replace">
<button name="action_cancel" type="object" string="Cancel" data-hotkey="z"
attrs="{'invisible': ['|', '|', ('id', '=', False), ('state', 'in', ('done', 'cancel')), ('confirm_cancel', '=', False)]}"
confirm="Some product moves have already been confirmed, this manufacturing order can't be completely cancelled. Are you still sure you want to process ?"
groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="//header//button[@name='button_unbuild']" position="replace">
<button name="button_unbuild" type="object" string="拆单"
attrs="{'invisible': [('state', '!=', 'done')]}" data-hotkey="shift+v"
@@ -305,12 +298,14 @@
type="object" groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="//header//button[@name='action_cancel']" position="replace">
<xpath expr="//header//button[@name='action_cancel'][2]" position="replace">
<button name="action_cancel" type="object" string="取消" data-hotkey="z"
attrs="{'invisible': ['|', '|', ('id', '=', False), ('state', 'in', ('done', 'cancel')), ('confirm_cancel', '=', False)]}"
attrs="{'invisible': ['|', '|', ('id', '=', False), ('state', 'in', ('done', 'rework', 'cancel')), ('confirm_cancel', '=', False)]}"
confirm="Some product moves have already been confirmed, this manufacturing order can't be completely cancelled. Are you still sure you want to process ?"
groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="//header//button[@name='action_cancel'][1]" position="replace"></xpath>
<xpath expr="//header//button[@name='button_unbuild']" position="replace">
<button name="button_unbuild" type="object" string="拆单"
attrs="{'invisible': [('state', '!=', 'done')]}" data-hotkey="shift+v"
@@ -439,9 +434,7 @@
<xpath expr="//tree//button[@name='button_start']" position="replace">
<field name="routing_type" invisible="True"/>
<button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始?"
attrs="{'invisible': ['|', '|', '|','|', ('production_state','in', ('draft', 'done', 'cancel')),
('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')), ('is_user_working', '!=',
False), ('routing_type', '=', 'CNC加工')]}"
attrs="{'invisible': [('state', '!=', 'ready')]}"
groups="sf_base.group_sf_mrp_user"/>
</xpath>
<xpath expr="//tree//button[@name='button_pending']" position="replace">
@@ -584,6 +577,7 @@
<searchpanel>
<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"/>
</searchpanel>
</xpath>
<filter name='todo' position="replace"/>

View File

@@ -0,0 +1,18 @@
<odoo>
<data>
<record id="mrp_production_workorder_tree_editable_view_inherit" model="ir.ui.view">
<field name="name">>mrp.workorder.tree.editable.inherit</field>
<field name="model">mrp.workorder</field>
<field name="inherit_id" ref="mrp.mrp_production_workorder_tree_editable_view"/>
<field name="arch" type="xml">
<xpath expr="//tree/field[1]" position="before">
<header>
<button string="重新安排" name="%(sf_manufacturing.mrp_workorder_batch_replan_wizard)d" type="action"
class="treeHeaderBtn"
invisible="context.get('workorder_type') not in ('工件装夹中心','1#自动生产线','工件拆卸中心')"/>
</header>
</xpath>
</field>
</record>
</data>
</odoo>

View File

@@ -29,6 +29,8 @@
</field>
<field name="product_id" position="after">
<field name="equipment_id" optional="hide"/>
<field name="part_name" optional="hide"/>
<field name="part_number" optional="show"/>
</field>
<xpath expr="//field[@name='qty_remaining']" position="after">
<field name="manual_quotation" optional="show"/>
@@ -53,10 +55,7 @@
<!-- 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),-->
<!-- ('is_user_working', '!=', False),("user_permissions","=",False),("name","=","CNC加工")]}-->
<!-- </attribute>-->
<attribute name="attrs">{'invisible': ['|', '|', '|','|','|', ('production_state','in', ('draft',
'done',
'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),
('is_user_working', '!=', False),("user_permissions","=",False),("name","in",("CNC加工","解除装夹"))]}
<attribute name="attrs">{'invisible': [('state', '!=', 'ready')]}
</attribute>
</xpath>
<xpath expr="//button[@name='%(mrp.act_mrp_block_workcenter_wo)d']" position="attributes">
@@ -104,8 +103,7 @@
<!-- <field name="target">fullscreen</field>-->
<field name="target">current</field>
<field name="domain">[('state', '!=', 'cancel'),('schedule_state', '=', '已排')]</field>
<field name="context">{'search_default_product': 1, 'search_default_workcenter_id':
active_id,'search_default_filter_order_warning':1,'search_default_filter_order_overdue':1,'search_default_filter_order_normal':1}
<field name="context">{'search_default_product': 1, 'search_default_workcenter_id': active_id}
</field>
<field name="help" type="html">
<p class="o_view_nocontent_workorder">
@@ -175,9 +173,7 @@
<!-- <button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"-->
<!-- attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>-->
<button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"
attrs="{'invisible': ['|', '|', '|', '|', '|', ('routing_type', '=', '装夹预调'), ('routing_type', '=', '解除装夹'), ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>
<button name="button_start" type="object" string="开始" class="btn-success"
attrs="{'invisible': ['|', '|', '|', '|', ('routing_type', '!=', '装夹预调'), ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>
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="是否确认完工"
@@ -200,7 +196,7 @@
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked'),('state','=','done')]}"/> -->
<!-- <button name="button_workpiece_delivery" type="object" string="工件配送" class="btn-primary"-->
<!-- attrs="{'invisible': ['|','|','|','|',('routing_type','!=','装夹预调'),('is_delivery','=',True),('state','!=','done'),('is_rework','=',True),'&amp;',('rfid_code','in',['',False]),('state','=','done')]}"/>-->
<button name="button_rework_pre" type="object" string="异常反馈" invisible="1"
<button name="button_rework_pre" type="object" string="异常反馈"
class="btn-primary"
attrs="{'invisible': ['|','|',('routing_type','!=','装夹预调'),('state','!=','progress'),('is_rework','=',True)]}"/>
<button name="unbind_tray" type="object" string="解绑托盘"

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<record model="ir.ui.view" id="view_purchase_order_line_form_inherit_sf1">
<field name="name">purchase.order.form.inherit.sf</field>
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.purchase_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='order_line']/tree/field[@name='name']" position="after">
<field name="related_product" optional="show"/>
<field name="part_number" optional="show"/>
</xpath>
</field>
</record>
</data>
</odoo>

View File

@@ -13,11 +13,15 @@
<button name="action_confirm" string="供货方式确认" type="object" attrs="{'invisible': [('state', '!=', 'supply method')]}" confirm="确认供货方式"/>
</xpath>
<xpath expr="//header/field[@name='state']" position="attributes">
<attribute name="statusbar_visible">draft,sent,supply method,sale</attribute>
<attribute name="statusbar_visible">supply method,sale,processing,physical_distribution,delivered</attribute>
</xpath>
<xpath expr="//page/field[@name='order_line']/tree/field[@name='remark']" position="before">
<field name="supply_method" attrs="{'invisible': [('state', '=', 'draft')], 'required': [('state', '=', 'supply method')]}" />
</xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='model_glb_file']" position="before">
<field name="part_number" optional="show"/>
</xpath>
<xpath expr="//header/button[@name='action_cancel']" position="attributes">
<attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute>
</xpath>

View File

@@ -35,6 +35,7 @@
<field name="inherit_id" ref="stock.vpicktree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='origin']" position="after">
<field name="sale_name"/>
<field name="retrospect_ref"/>
</xpath>
</field>

View File

@@ -3,3 +3,4 @@ from . import rework_wizard
from . import production_wizard
from . import production_technology_wizard
from . import production_technology_re_adjust_wizard
from . import mrp_workorder_batch_replan_wizard

View File

@@ -0,0 +1,70 @@
# -*- coding: utf-8 -*-
import logging
from datetime import datetime, timedelta
from odoo import models, api, fields, _
class MrpWorkorderBatchReplanWizard(models.TransientModel):
_name = 'mrp.workorder.batch.replan.wizard'
_description = '制造订单批量重新安排向导'
def _get_date_planned_start(self):
planned_start_date = datetime.now() + timedelta(hours=2)
logging.info('计划开始加工时间: %s', planned_start_date)
return planned_start_date
def _get_default_workorder_count(self):
active_ids = self.env.context.get('active_ids', [])
return len(active_ids)
def _get_default_workorder_type(self):
active_ids = self.env.context.get('active_ids', [])
if active_ids:
workorders = self.env['mrp.workorder'].browse(active_ids)
if workorders:
routing_type = set(workorders.mapped('routing_type'))
return '/'.join(sorted(routing_type)) if routing_type else None
return None
workorder_type = fields.Char(string='工单类型', default=_get_default_workorder_type, readonly=True)
workorder_count = fields.Integer(string='工单数量',
default=_get_default_workorder_count,
readonly=True)
workorder_id = fields.Many2many('mrp.workorder', string=u'工单')
def confirm(self):
routing_type = set(self.workorder_id.mapped('routing_type'))
if len(routing_type) > 1:
raise models.ValidationError("批量重新安排工单类型必须一致。")
show_json_popover = self.workorder_id.mapped('show_json_popover')
if any(not value for value in show_json_popover):
raise models.ValidationError("所选工单必须都为逾期状态")
failed_workorders = {}
for workorder_info in self.workorder_id:
try:
workorder_info.action_replan()
except Exception as e:
reason = str(e)
if reason in failed_workorders:
failed_workorders[reason].append(
workorder_info.production_id.name)
else:
failed_workorders[reason] = [workorder_info.production_id.name]
if failed_workorders:
error_messages = "\n".join(
[f"制造订单: {', '.join(workorder_names)}, 原因: {reason}" for reason, workorder_names in
failed_workorders.items()])
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'message': f"以下工单重新安排失败:\n{error_messages}",
'sticky': False,
'color': 'red',
'next': {
'type': 'ir.actions.act_window_close'
}
},
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="mrp_workorder_batch_replan_wizard_form" model="ir.ui.view">
<field name="name">mrp.workorder.batch.replan.wizard.form.view</field>
<field name="model">mrp.workorder.batch.replan.wizard</field>
<field name="arch" type="xml">
<form>
<group>
<field name="workorder_type"/>
<field name="workorder_count"/>
</group>
<footer>
<button string="确认" name="confirm" type="object" class="oe_highlight"/>
<button string="取消" class="btn-primary" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="mrp_workorder_batch_replan_wizard" model="ir.actions.act_window">
<field name="name">重新安排工单</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mrp.workorder.batch.replan.wizard</field>
<field name="view_mode">form</field>
<field name="view_id" ref="mrp_workorder_batch_replan_wizard_form"/>
<field name="target">new</field>
<field name="context">{'default_workorder_id': active_ids}</field>
</record>
</odoo>

View File

@@ -10,7 +10,7 @@ class ProductionTechnologyReAdjustWizard(models.TransientModel):
production_id = fields.Many2one('mrp.production', string='制造订单号')
origin = fields.Char(string='源单据')
is_technology_re_adjust = fields.Boolean(default=False)
is_technology_re_adjust = fields.Boolean(default=True)
def confirm(self):
if self.is_technology_re_adjust is True:
@@ -24,38 +24,39 @@ class ProductionTechnologyReAdjustWizard(models.TransientModel):
for production_item in productions:
# 该制造订单的其他同一销售订单的制造订单的工艺设计处理
if production_item != self.production_id:
for td_other in production_item.technology_design_ids:
if td_other.is_auto is False:
td_del = technology_designs.filtered(lambda tdo: tdo.route_id.id == td_other.route_id.id)
if not td_del or td_del.active is False:
td_other.write({'active': False})
for td_main in technology_designs:
route_other = production_item.technology_design_ids.filtered(
lambda td: td.route_id.id == td_main.route_id.id)
if not route_other and td_main.active is True:
production_item.write({'technology_design_ids': [(0, 0, {
'route_id': td_main.route_id.id,
'process_parameters_id': False if td_main.process_parameters_id is False else
self.env[
'sf.production.process.parameter'].search(
[('id', '=', td_main.process_parameters_id.id)]).id,
'sequence': td_main.sequence,
'is_auto': td_main.is_auto})]})
else:
for ro in route_other:
domain = [('production_id', '=', self.production_id.id),
('active', 'in', [True, False]),
('route_id', '=', ro.route_id.id)]
if ro.route_id.routing_type == '表面工艺':
domain += [('process_parameters_id', '=', ro.process_parameters_id.id)]
elif ro.route_id.routing_tag == 'special' and ro.is_auto is False:
# display_name = ro.route_id.display_name
domain += [('id', '=', ro.id)]
elif ro.panel is not False:
domain += [('panel', '=', ro.panel)]
td_upd = self.env['sf.technology.design'].sudo().search(domain)
if td_upd:
ro.write({'sequence': td_upd.sequence, 'active': td_upd.active})
self.env['sf.technology.design'].sudo().unified_procedure_multiple_work_orders(technology_designs, production_item)
# for td_other in production_item.technology_design_ids:
# # if td_other.is_auto is False:
# # td_del = technology_designs.filtered(lambda tdo: tdo.route_id.id == td_other.route_id.id)
# # if not td_del or td_del.active is False:
# # td_other.write({'active': False})
# for td_main in technology_designs:
# route_other = production_item.technology_design_ids.filtered(
# lambda td: td.route_id.id == td_main.route_id.id)
# if not route_other and td_main.active is True:
# production_item.write({'technology_design_ids': [(0, 0, {
# 'route_id': td_main.route_id.id,
# 'process_parameters_id': False if td_main.process_parameters_id is False else
# self.env[
# 'sf.production.process.parameter'].search(
# [('id', '=', td_main.process_parameters_id.id)]).id,
# 'sequence': td_main.sequence,
# 'is_auto': td_main.is_auto})]})
# else:
# for ro in route_other:
# domain = [('production_id', '=', self.production_id.id),
# ('active', 'in', [True, False]),
# ('route_id', '=', ro.route_id.id)]
# if ro.route_id.routing_type == '表面工艺':
# domain += [('process_parameters_id', '=', ro.process_parameters_id.id)]
# elif ro.route_id.routing_tag == 'special' and ro.is_auto is False:
# # display_name = ro.route_id.display_name
# domain += [('id', '=', ro.id)]
# elif ro.panel is not False:
# domain += [('panel', '=', ro.panel)]
# td_upd = self.env['sf.technology.design'].sudo().search(domain)
# if td_upd:
# ro.write({'sequence': td_upd.sequence, 'active': td_upd.active})
special_design = self.env['sf.technology.design'].sudo().search(
[('routing_tag', '=', 'special'), ('production_id', '=', production_item.id),
('is_auto', '=', False), ('active', 'in', [True, False])])
@@ -124,3 +125,4 @@ class ProductionTechnologyReAdjustWizard(models.TransientModel):
if workorders[
0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程':
workorders[0].state = 'waiting'

View File

@@ -21,6 +21,26 @@
</field>
</record>
<record model="ir.ui.view" id="sf_production_technology_re_adjust_wizard_confirm_form_view">
<field name="name">sf.production.technology.re_adjust.wizard.form.view</field>
<field name="model">sf.production.technology.re_adjust.wizard</field>
<field name="arch" type="xml">
<form>
<sheet>
<field name="production_id" invisible="1"/>
<field name="origin" invisible="1"/>
<div>
是否确认退回调整
</div>
<footer>
<button string="确认" name="confirm" type="object" class="oe_highlight"/>
<button string="取消" class="btn btn-secondary" special="cancel"/>
</footer>
</sheet>
</form>
</field>
</record>
<record id="action_sf_production_technology_re_adjust_wizard" model="ir.actions.act_window">
<field name="name">工艺退回调整</field>
<field name="res_model">sf.production.technology.re_adjust.wizard</field>

View File

@@ -11,48 +11,52 @@ class ProductionTechnologyWizard(models.TransientModel):
production_id = fields.Many2one('mrp.production', string='制造订单号')
origin = fields.Char(string='源单据')
is_technology_confirm = fields.Boolean(default=False)
is_technology_confirm = fields.Boolean(default=True)
def confirm(self):
if self.is_technology_confirm is True and self.production_id.product_id.categ_id.type == '成品':
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'),
('product_id', '=', self.production_id.product_id.id)]
else:
domain = [('id', '=', self.production_id.id)]
technology_designs = self.production_id.technology_design_ids
technology_designs = self.env['sf.technology.design'].sudo().search(
[('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)
for production in productions:
if production != self.production_id:
for td_other in production.technology_design_ids:
if td_other.is_auto is False:
td_del = technology_designs.filtered(lambda tdo: tdo.route_id.id == td_other.route_id.id)
if not td_del or td_del.active is False:
td_other.write({'active': False})
for td_main in technology_designs:
route_other = production.technology_design_ids.filtered(
lambda td: td.route_id.id == td_main.route_id.id)
if not route_other and td_main.active is True:
production.write({'technology_design_ids': [(0, 0, {
'route_id': td_main.route_id.id,
'process_parameters_id': False if td_main.process_parameters_id is False else self.env[
'sf.production.process.parameter'].search(
[('id', '=', td_main.process_parameters_id.id)]).id,
'sequence': td_main.sequence})]})
else:
for ro in route_other:
domain = [('production_id', '=', self.production_id.id),
('active', 'in', [True, False]),
('route_id', '=', ro.route_id.id)]
if ro.route_id.routing_type == '表面工艺':
domain += [('process_parameters_id', '=', ro.process_parameters_id.id)]
elif ro.route_id.routing_tag == 'special' and ro.is_auto is False:
# display_name = ro.route_id.display_name
domain += [('id', '=', ro.id)]
elif ro.panel is not False:
domain += [('panel', '=', ro.panel)]
td_upd = self.env['sf.technology.design'].sudo().search(domain)
if td_upd:
ro.write({'sequence': td_upd.sequence, 'active': td_upd.active})
self.env['sf.technology.design'].sudo().unified_procedure_multiple_work_orders(technology_designs,
production)
# for td_other in production.technology_design_ids:
# if td_other.is_auto is False:
# td_del = technology_designs.filtered(lambda tdo: tdo.route_id.id == td_other.route_id.id)
# if not td_del or td_del.active is False:
# td_other.write({'active': False})
# for td_main in technology_designs:
# route_other = production.technology_design_ids.filtered(
# lambda td: td.route_id.id == td_main.route_id.id)
# if not route_other and td_main.active is True:
# production.write({'technology_design_ids': [(0, 0, {
# 'route_id': td_main.route_id.id,
# 'process_parameters_id': False if td_main.process_parameters_id is False else self.env[
# 'sf.production.process.parameter'].search(
# [('id', '=', td_main.process_parameters_id.id)]).id,
# 'sequence': td_main.sequence})]})
# else:
# for ro in route_other:
# domain = [('production_id', '=', self.production_id.id),
# ('active', 'in', [True, False]),
# ('route_id', '=', ro.route_id.id)]
# if ro.route_id.routing_type == '表面工艺':
# domain += [('process_parameters_id', '=', ro.process_parameters_id.id)]
# elif ro.route_id.routing_tag == 'special' and ro.is_auto is False:
# # display_name = ro.route_id.display_name
# domain += [('id', '=', ro.id)]
# elif ro.panel is not False:
# domain += [('panel', '=', ro.panel)]
# td_upd = self.env['sf.technology.design'].sudo().search(domain)
# if td_upd:
# ro.write({'sequence': td_upd.sequence, 'active': td_upd.active})
special_design = self.env['sf.technology.design'].sudo().search(
[('routing_tag', '=', 'special'), ('production_id', '=', production.id),
('is_auto', '=', False), ('active', 'in', [True, False])])
@@ -116,3 +120,4 @@ class ProductionTechnologyWizard(models.TransientModel):
if workorder[0].state in ['pending']:
if workorder[0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程':
workorder[0].state = 'waiting'
return productions

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.ui.view" id="sf_production_technology_wizard_form_view">
<record model="ir.ui.view" id="sf_production_technology_wizard_form_view">
<field name="name">sf.production.technology.wizard.form.view</field>
<field name="model">sf.production.technology.wizard</field>
<field name="arch" type="xml">
@@ -13,7 +13,28 @@
对当前制造订单,同一销售订单相同产品所生成的制造订单统一进行工艺调整与确认
</div>
<footer>
<button string="确认" name="confirm" type="object" class="oe_highlight" confirm="是否确认工艺调整"/>
<button string="确认" name="confirm" type="object" class="oe_highlight"
confirm="是否确认工艺调整"/>
<button string="取消" class="btn btn-secondary" special="cancel"/>
</footer>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="sf_production_technology_wizard_confirm_form_view">
<field name="name">sf.production.technology.wizard.form.view</field>
<field name="model">sf.production.technology.wizard</field>
<field name="arch" type="xml">
<form>
<sheet>
<field name="production_id" invisible="1"/>
<field name="origin" invisible="1"/>
<div>
是否确认工艺调整
</div>
<footer>
<button string="确认" name="confirm" type="object" class="oe_highlight"/>
<button string="取消" class="btn btn-secondary" special="cancel"/>
</footer>
</sheet>

View File

@@ -62,6 +62,37 @@ class ReworkWizard(models.TransientModel):
hidden_workorder_list.sort()
item.hidden_workorder_ids = ','.join(hidden_workorder_list)
def efficacy_rework_wo(self, wk_ids):
"""限制判断 """
# 判断检测结果待处理所对应的工单是否勾选
result_ids = self.production_id.detection_result_ids.filtered(lambda dr: dr.handle_result == '待处理')
work_id_list = []
if result_ids:
work_id_list = [self.workorder_ids.filtered(
lambda wk: (wk.name == result_id.routing_type and wk.processing_panel == result_id.processing_panel
and wk.state == 'done')).id
for result_id in result_ids]
if len(wk_ids.filtered(lambda wk: wk.id in work_id_list)) != len(work_id_list):
raise ValidationError('存在【检测结果】为【待处理】所对应的工单未进行勾选!!!')
# 获取已完成的标准工单
grouped_rw_ids = {key: list(group) for key, group in groupby(wk_ids, key=lambda w: w.processing_panel)}
for panel, panel_rw_ids in grouped_rw_ids.items():
# 1、当制造订单内ZM面的工单都已完成时返工勾选工序时只能勾选上ZM面的所有工序进行返工
work_ids = self.workorder_ids.filtered(lambda w: w.state == 'done' and w.processing_panel == panel)
if len(work_ids) == 3 and len(panel_rw_ids) != 3:
raise ValidationError(
'因为[%s]面的工单已全部完成,如果要对[%s]面的工单进行返工,请勾选这个面的所有工单。' % (panel, panel))
# 2、当FM工单在CNC工单进行选择返工并将已全部完成的ZM面工序全部勾选上时FM工单上所有的已完成的工单装夹预调工单也必须进行勾选
if not wk_ids.filtered(lambda wk: wk.name == '装夹预调' and wk.processing_panel == panel):
if wk_ids.filtered(lambda wk: wk.name == 'CNC加工' and wk.processing_panel == panel):
sequence_max = wk_ids.filtered(
lambda wk: wk.name == 'CNC加工' and wk.processing_panel == panel).sequence
for wk_id in wk_ids.filtered(lambda wk: wk.sequence < sequence_max):
if len(wk_ids.filtered(lambda wk: wk.processing_panel == wk_id.processing_panel)) == 3:
raise ValidationError(
'由于在[%s]面之前存在整个面进行了勾选的情况,所以在勾选了[%s]面的【CNC加工】工单的时请勾选[%s]面的装夹预调工单!' % (
panel, panel, panel))
def confirm(self):
if self.routing_type in ['装夹预调', 'CNC加工']:
self.is_clamp_measure = False
@@ -79,36 +110,22 @@ class ReworkWizard(models.TransientModel):
if self.hidden_workorder_ids:
hidden_workorder_list = self.hidden_workorder_ids.split(',')
rework_workorder_ids = self.workorder_ids.filtered(lambda w: str(w.id) in hidden_workorder_list)
# 限制判断
# 1、当制造订单内ZM面的工单都已完成时返工勾选工序时只能勾选上ZM面的所有工序进行返工
# 2、当FM工单在CNC工单进行选择返工并将已全部完成的ZM面工序全部勾选上时FM工单上所有的已完成的工单装夹预调工单也必须进行勾选
# 获取已完成的标准工单
# done_normative_workorder_ids = self.workorder_ids.filtered(
# lambda w: w.state == 'done' and w.processing_panel is not False)
# # 获取需要返工的标准工单
# rework_normative_workorder_ids = rework_workorder_ids.filtered(
# lambda w: w.processing_panel is not False)
# if rework_normative_workorder_ids:
# for rw in rework_normative_workorder_ids:
# if len(done_normative_workorder_ids.filtered(
# lambda w: w.processing_panel == rw.processing_panel)) == 3:
# pass
# 调用效验方法
self.efficacy_rework_wo(rework_workorder_ids)
else:
raise ValidationError('请选择返工工单!!!')
if rework_workorder_ids:
clamp_workorder_ids = None
if rework_workorder_ids:
# 限制
# 1、单独返工CNC工单则不解绑托盘RFID如单独返工装夹预调工单则自动解绑托盘RFID
# 2、返工CNC工单和装夹预调工单则自动解绑RFID
clamp_workorder_ids = rework_workorder_ids.filtered(lambda rp: rp.routing_type == '装夹预调')
if clamp_workorder_ids:
for clamp_workorder_id in clamp_workorder_ids:
self.production_id.workorder_ids.filtered(
lambda wk: wk.processing_panel == clamp_workorder_id.processing_panel).write(
{'rfid_code': False})
# 返工工单状态设置为【返工】
rework_workorder_ids.write({'state': 'rework'})
# 限制
# 1、单独返工CNC工单则不解绑托盘RFID如单独返工装夹预调工单则自动解绑托盘RFID
# 2、返工CNC工单和装夹预调工单则自动解绑RFID
clamp_workorder_ids = rework_workorder_ids.filtered(lambda rp: rp.routing_type == '装夹预调')
if clamp_workorder_ids:
for clamp_workorder_id in clamp_workorder_ids:
self.production_id.workorder_ids.filtered(
lambda wk: wk.processing_panel == clamp_workorder_id.processing_panel).write(
{'rfid_code': None})
# 返工工单状态设置为【返工】
rework_workorder_ids.write({'state': 'rework'})
# 查询返工工单对应的工艺设计记录,并调用方法拼接数据,用于创建新的工单
workorders_values = []
for work in rework_workorder_ids:
@@ -119,7 +136,7 @@ class ReworkWizard(models.TransientModel):
and item.panel == work.processing_panel))
if route:
work_list = self.env['mrp.workorder'].json_workorder_str(self.production_id, route[0])
work_list[2].update({'tag_type': '重新加工'})
work_list[2].update({'tag_type': '重新加工', 'sequence': 0})
workorders_values.append(work_list)
# 创建新工单,并进行返工配置的相关操作
if workorders_values:
@@ -129,6 +146,11 @@ class ReworkWizard(models.TransientModel):
new_pre_workorder_ids = self.production_id.workorder_ids.filtered(
lambda kw: kw.routing_type == '装夹预调' and kw.sequence == 0)
self.production_id._reset_work_order_sequence()
# ====新工单绑定rfid===
for new_work_id in new_work_ids:
if new_work_id.routing_type in ['CNC加工', '解除装夹']:
new_work_id.write({'rfid_code': self.production_id.workorder_ids.filtered(
lambda wk: wk.sequence == new_work_id.sequence - 1).rfid_code})
self.production_id.detection_result_ids.filtered(
lambda ap1: ap1.handle_result == '待处理').write({'handle_result': '已处理'})
panels = [] # 返工的加工面
@@ -249,11 +271,7 @@ class ReworkWizard(models.TransientModel):
elif self.programming_state in ['待编程', '编程中']:
self.production_id.write(
{'programming_state': '编程中', 'work_state': '编程中', 'is_rework': True})
# ==================申请重新编程=======================
if self.is_reprogramming is True:
self.production_id.update_programming_state()
self.production_id.write(
{'programming_state': '编程中', 'work_state': '编程中'})
# =================================================
if self.production_id.state == 'progress':
self.production_id.write({'programming_state': '已编程', 'work_state': '已编程'})
if self.reprogramming_num >= 1 and self.programming_state == '已编程':
@@ -264,6 +282,13 @@ class ReworkWizard(models.TransientModel):
productions_not_delivered.write(
{'state': 'progress', 'programming_state': '已编程', 'work_state': '已编程',
'is_rework': False})
# ==================申请重新编程=======================
if self.is_reprogramming is True:
self.production_id.update_programming_state()
self.production_id.write(
{'programming_state': '编程中', 'work_state': '编程中', 'state': 'progress'})
# ================= 返工完成,制造订单状态置为加工中 ==============
self.production_id.write({'state': 'progress', 'is_rework': False})
@api.onchange('production_id')
def onchange_processing_panel_id(self):

View File

@@ -14,7 +14,7 @@
<field name="processing_panel_id" invisible="1"/>
<field name="hidden_workorder_ids" class="css_not_available_msg"/>
<group>
<field name="hidden_workorder_ids"/>
<field name="hidden_workorder_ids" invisible="1"/>
<field options="{'no_create': True,'no_open': True}" readonly="1" name="workorder_ids"
widget="jikimo_subtree_selector_field"
jikimo_selector="True" replace_context="hidden_workorder_ids" string="工序"

View File

@@ -11,13 +11,15 @@
""",
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
'depends': ['sale', 'purchase', 'sf_plan', 'jikimo_message_notify', 'stock', 'sf_quality', 'mrp'],
'depends': ['sale', 'purchase', 'sf_plan', 'jikimo_message_notify', 'stock', 'sf_quality', 'mrp',
'sf_manufacturing','product'],
'data': [
'data/bussiness_node.xml',
'data/cron_data.xml',
'data/template_data.xml',
'security/ir.model.access.csv',
'views/mrp_workorder_views.xml',
'views/purchase_order_view.xml',
],
'test': [
],

View File

@@ -4,10 +4,12 @@ import logging
from odoo import http
from odoo.http import request
from odoo.addons.sf_mrs_connect.controllers.controllers import Sf_Mrs_Connect
from odoo.addons.sf_bf_connect.controllers.controllers import Sf_Bf_Connect
from odoo.addons.sf_base.commons.common import Common
_logger = logging.getLogger(__name__)
class MessageSfMrsConnect(Sf_Mrs_Connect):
@http.route('/api/cnc_processing/create', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
@@ -39,7 +41,8 @@ class MessageSfMrsConnect(Sf_Mrs_Connect):
_logger.info('无效用刀异常消息推送接口:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/api/maintenance_logs/notify', type='json', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
@http.route('/api/maintenance_logs/notify', type='json', auth='public', methods=['GET', 'POST'], csrf=False,
cors="*")
def maintenance_logs_notify(self, **kw):
res = {'code': 200, 'message': '设备故障日志信息推送成功'}
datas = request.httprequest.data
@@ -51,10 +54,26 @@ class MessageSfMrsConnect(Sf_Mrs_Connect):
try:
if not isinstance(log_id, list):
log_id = [log_id]
maintenance_logs = request.env['sf.maintenance.logs'].sudo().search([('id', 'in', [int(id) for id in log_id])])
maintenance_logs = request.env['sf.maintenance.logs'].sudo().search(
[('id', 'in', [int(id) for id in log_id])])
if maintenance_logs:
maintenance_logs.add_queue('设备故障')
except Exception as e:
res = {'code': 400, 'message': '设备故障信息推送失败', 'error': str(e)}
return json.JSONEncoder().encode(res)
class MessageSfBfConnect(Sf_Bf_Connect):
@http.route('/api/bfm_process_order/list', type='http', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
def get_bfm_process_order_list(self, **kw):
res = super(MessageSfBfConnect, self).get_bfm_process_order_list(**kw)
response_data = json.loads(res.data.decode('utf-8'))
if response_data['status'] == 1:
try:
_logger.info('已进入待接单消息推送:%s' % response_data)
sale_order = request.env['sale.order'].sudo().search([('name', '=', response_data['factory_order_no'])])
sale_order.add_queue('待接单')
except Exception as e:
logging.info('add_queue error:%s' % e)
return res

View File

@@ -22,6 +22,15 @@
<field name="model">sale.order</field>
</record>
<record id="bussiness_supply_method" model="jikimo.message.bussiness.node">
<field name="name">待确认供货方式</field>
<field name="model">sale.order</field>
</record>
<record id="bussiness_technology_to_confirmed" model="jikimo.message.bussiness.node">
<field name="name">待确认加工路线</field>
<field name="model">mrp.production</field>
</record>
<record id="transfer_inventory" model="jikimo.message.bussiness.node">
<field name="name">调拨入库</field>
@@ -47,7 +56,7 @@
<!--工单-->
<record id="bussiness_mrp_workorder_remind" model="jikimo.message.bussiness.node">
<field name="name">工单已下发通知</field>
<field name="model">mrp.workorder</field>
<field name="model">product.product</field>
</record>
<!--发货调拨-->
<record id="production_completed_warehouse_reminder" model="jikimo.message.bussiness.node">
@@ -106,5 +115,40 @@
<field name="name">设备故障</field>
<field name="model">sf.maintenance.logs</field>
</record>
<record id="bussiness_state_draft" model="jikimo.message.bussiness.node">
<field name="name">待排程</field>
<field name="model">mrp.production</field>
</record>
<record id="bussiness_outsourcing" model="jikimo.message.bussiness.node">
<field name="name">委外加工采购单提醒</field>
<field name="model">purchase.order</field>
</record>
<record id="bussiness_purchase" model="jikimo.message.bussiness.node">
<field name="name">外购订单采购单提醒</field>
<field name="model">purchase.order</field>
</record>
<record id="bussiness_process_outsourcing" model="jikimo.message.bussiness.node">
<field name="name">工序外协采购单通知</field>
<field name="model">purchase.order</field>
</record>
<record id="bussiness_outsourced_outbound" model="jikimo.message.bussiness.node">
<field name="name">工序外协发料通知</field>
<field name="model">mrp.production</field>
</record>
<record id="bussiness_purchase_order_warning" model="jikimo.message.bussiness.node">
<field name="name">采购订单预警提醒</field>
<field name="model">purchase.order</field>
</record>
<record id="bussiness_purchase_order_overdue" model="jikimo.message.bussiness.node">
<field name="name">采购单已逾期提醒</field>
<field name="model">purchase.order</field>
</record>
</data>
</odoo>

View File

@@ -10,7 +10,7 @@
<field name="msgtype">markdown</field>
<field name="urgency">normal</field>
<field name="content">### 待接单提醒:
单号:销售订单[{{name}}]({{url}})
单号:询价单[{{name}}]({{url}})
事项:请确认是否接单。
</field>
</record>
@@ -72,14 +72,14 @@
<field name="msgtype">markdown</field>
<field name="urgency">normal</field>
<field name="content">### 坯料发料提醒:
单号:产品[{{product_id}}]({{request_url}})
事项:共{{number}}个生产发料单待确认处理</field>
单号:发料单[{{name}}]({{request_url}})
事项:请确认坯料发料单并处理</field>
</record>
<record id="template_mrp_workorder_remind" model="jikimo.message.template">
<field name="name">工单已下发通知</field>
<field name="model_id" ref="mrp_workorder.model_mrp_workorder"/>
<field name="model">mrp.workorder</field>
<field name="model_id" ref="product.model_product_product"/>
<field name="model">product.product</field>
<field name="bussiness_node_id" ref="bussiness_mrp_workorder_remind"/>
<field name="msgtype">markdown</field>
<field name="urgency">normal</field>
@@ -275,5 +275,119 @@
机台号:[{{maintenance_equipment_id.name}}]({{url}})
事项:{{create_date}}故障报警</field>
</record>
<record id="template_supply_method" model="jikimo.message.template">
<field name="name">待确认供货方式</field>
<field name="model_id" ref="sale.model_sale_order"/>
<field name="model">sale.order</field>
<field name="bussiness_node_id" ref="bussiness_supply_method"/>
<field name="msgtype">markdown</field>
<field name="urgency">normal</field>
<field name="content">### 待确认供货方式提醒:
单号:销售订单[{{name}}]({{url}})
事项:请确认供货方式。
</field>
</record>
<record id="template_technology_to_confirmed" model="jikimo.message.template">
<field name="name">待确认加工路线</field>
<field name="model_id" ref="mrp.model_mrp_production"/>
<field name="model">mrp.production</field>
<field name="bussiness_node_id" ref="bussiness_technology_to_confirmed"/>
<field name="msgtype">markdown</field>
<field name="urgency">normal</field>
<field name="content">### 待确认加工路线提醒:
单号:产品[{{name}}]({{url}})
事项:有{{production_num}}个制造订单需要确认加工路线。
</field>
</record>
<record id="template_state_draft" model="jikimo.message.template">
<field name="name">待排程</field>
<field name="model_id" ref="mrp.model_mrp_production"/>
<field name="model">mrp.production</field>
<field name="bussiness_node_id" ref="bussiness_state_draft"/>
<field name="msgtype">markdown</field>
<field name="urgency">normal</field>
<field name="content">### 待排程提醒:
单号:产品[{{name}}]({{url}})
事项:有{{production_num}}个制造订单待排程。
</field>
</record>
<record id="template_outsourcing_remind" model="jikimo.message.template">
<field name="name">委外加工采购单提醒</field>
<field name="model_id" ref="purchase.model_purchase_order"/>
<field name="model">purchase.order</field>
<field name="bussiness_node_id" ref="bussiness_outsourcing"/>
<field name="msgtype">markdown</field>
<field name="urgency">normal</field>
<field name="content">### 委外加工采购通知:
单号:委外加工采购单[{{name}}]({{request_url}})
事项:请确认委外采购单并处理。</field>
</record>
<record id="template_purchase_remind" model="jikimo.message.template">
<field name="name">外购订单采购单提醒</field>
<field name="model_id" ref="purchase.model_purchase_order"/>
<field name="model">purchase.order</field>
<field name="bussiness_node_id" ref="bussiness_purchase"/>
<field name="msgtype">markdown</field>
<field name="urgency">normal</field>
<field name="content">### 外购订单采购通知:
单号:外购采购单[{{name}}]({{request_url}})
事项:请确认外购采购单并处理。</field>
</record>
<record id="template_process_outsourcing_remind" model="jikimo.message.template">
<field name="name">工序外协采购单通知</field>
<field name="model_id" ref="purchase.model_purchase_order"/>
<field name="model">purchase.order</field>
<field name="bussiness_node_id" ref="bussiness_process_outsourcing"/>
<field name="msgtype">markdown</field>
<field name="urgency">normal</field>
<field name="content">### 工序外协采购单通知:
单号:工序外协采购,产品[{{name}}]({{url}})
事项:请确认{{num}}个工序外协采购单并处理。</field>
</record>
<record id="template_outsourced_outbound_remind" model="jikimo.message.template">
<field name="name">工序外协发料通知</field>
<field name="model_id" ref="mrp.model_mrp_production"/>
<field name="model">mrp.production</field>
<field name="bussiness_node_id" ref="bussiness_outsourced_outbound"/>
<field name="msgtype">markdown</field>
<field name="urgency">normal</field>
<field name="content">### 工序外协发料提醒:
单号:产品[{{name}}]({{url}})发料单
事项:请确认{{num}}个工序外协发料单并发料处理。</field>
</record>
<record id="template_purchase_order_warning" model="jikimo.message.template">
<field name="name">采购订单预警提醒</field>
<field name="model_id" ref="purchase.model_purchase_order"/>
<field name="model">purchase.order</field>
<field name="bussiness_node_id" ref="bussiness_purchase_order_warning"/>
<field name="msgtype">markdown</field>
<field name="send_type">timing</field>
<field name="urgency">normal</field>
<field name="content">### 采购订单预警提醒
事项:[共有{{num}}个采购订单有逾期风险]({{url}})
</field>
</record>
<record id="template_purchase_order_overdue" model="jikimo.message.template">
<field name="name">采购单已逾期提醒</field>
<field name="model_id" ref="purchase.model_purchase_order"/>
<field name="model">purchase.order</field>
<field name="bussiness_node_id" ref="bussiness_purchase_order_overdue"/>
<field name="msgtype">markdown</field>
<field name="send_type">timing</field>
<field name="urgency">normal</field>
<field name="content">### 采购单已逾期提醒
事项:[共有{{num}}个采购订单已逾期]({{url}})
</field>
</record>
</data>
</odoo>

View File

@@ -10,3 +10,6 @@ from . import sf_message_functional_tool_dismantle
from . import sf_message_mrp_production
from . import sf_message_quality_cnc_test
from . import sf_message_maintenance_logs
from . import sf_message_mrp_production_wizard
from . import sf_message_mrp_production_adjust_wizard
from . import sf_message_product

View File

@@ -13,11 +13,11 @@ class SFMessageMaintenanceLogs(models.Model):
return res
def _get_message(self, message_queue_ids):
contents = super(SFMessageMaintenanceLogs, self)._get_message(message_queue_ids)
contents, _ = super(SFMessageMaintenanceLogs, self)._get_message(message_queue_ids)
url = self.env['ir.config_parameter'].get_param('web.base.url')
action_id = self.env.ref('sf_maintenance.action_maintenance_logs').id
for index, content in enumerate(contents):
maintenance_logs_id = self.env['sf.maintenance.logs'].browse(message_queue_ids[index].res_id)
url = url + '/web#id=%s&view_type=form&action=%s' % (maintenance_logs_id.id, action_id)
contents[index] = content.replace('{{url}}', url)
return contents
return contents, message_queue_ids

View File

@@ -14,19 +14,24 @@ class SFMessageMrpProduction(models.Model):
'workorder_ids.state', 'product_qty', 'qty_producing')
def _compute_state(self):
super(SFMessageMrpProduction, self)._compute_state()
for record in self:
if record.state in ['scrap', 'done']:
# 查询制造订单下的所有未完成的生产订单
mrp_production = record.env['mrp.production'].search(
[('origin', '=', record.origin), ('state', 'not in', ['scrap', 'done'])])
if not mrp_production:
mrp_production_queue = self.env["jikimo.message.queue"].search([('res_id', '=', record.id)])
if not mrp_production_queue:
record.add_queue('生产完工入库提醒')
try:
for record in self:
if record.state in ['scrap', 'done']:
# 查询制造订单下的所有未完成的生产订单
mrp_production = record.env['mrp.production'].search(
[('origin', '=', record.origin), ('state', 'not in', ['scrap', 'done'])])
if not mrp_production:
mrp_production_queue = self.env["jikimo.message.queue"].search([('res_id', '=', record.id)])
if not mrp_production_queue:
record.add_queue('生产完工入库提醒')
except Exception as e:
logging.info('add_queue生产完工入库提醒 error:%s' % e)
# 获取发送消息内容
def _get_message(self, message_queue_ids):
contents = []
unique_products = set()
technology_to_confirmed = set()
for message_queue_id in message_queue_ids:
if message_queue_id.message_template_id.name == '生产完工入库提醒':
content = message_queue_id.message_template_id.content
@@ -40,7 +45,57 @@ class SFMessageMrpProduction(models.Model):
content = content.replace('{{name}}', stock_picking_sfp.name).replace(
'{{sale_order_name}}', mrp_production.origin).replace('{{request_url}}', url)
contents.append(content)
if message_queue_id.message_template_id.name == '待确认加工路线':
content = message_queue_id.message_template_id.content
mrp_production = self.env['mrp.production'].sudo().search([('id', '=', int(message_queue_id.res_id))])
technology_to_confirmed.add(mrp_production.product_id.id)
if message_queue_id.message_template_id.name == '待排程':
content = message_queue_id.message_template_id.content
mrp_production = self.env['mrp.production'].sudo().search([('id', '=', int(message_queue_id.res_id))])
unique_products.add(mrp_production.product_id.id)
if message_queue_id.message_template_id.name == '工序外协发料通知':
content = message_queue_id.message_template_id.content
mrp_production = self.env['mrp.production'].sudo().search([('id', '=', int(message_queue_id.res_id))])
mrp_production_list = self.env['mrp.production'].sudo().search(
[('product_id', '=', mrp_production.product_id.id)])
mrp_production_names = mrp_production_list.mapped('name')
stock_picking_num = self.env['stock.picking'].sudo().search_count(
[('origin', 'in', mrp_production_names), ('state', '=', 'assigned')])
if stock_picking_num >= 1:
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
action_id = self.env.ref('stock.action_picking_tree_ready').id
url_with_id = f"{url}/web#view_type=list&action={action_id}"
content = content.replace('{{name}}', mrp_production.product_id.name).replace('{{url}}',
url_with_id).replace(
'{{num}}', str(stock_picking_num))
contents.append(content)
if unique_products:
action_id = self.env.ref('sf_plan.sf_production_plan_action1').id
unique_products_contents = self.get_production_info(content, unique_products, 'confirmed',
action_id)
contents.extend(unique_products_contents)
if technology_to_confirmed:
action_id = self.env.ref('mrp.mrp_production_action').id
technology_to_confirmed_contents = self.get_production_info(content, technology_to_confirmed,
'technology_to_confirmed',
action_id)
contents.extend(technology_to_confirmed_contents)
logging.info('生产完工入库提醒: %s' % contents)
return contents, message_queue_ids
def get_production_info(self, content, product_ids, state, action_id):
contents = []
for products_id in product_ids:
product_name = self.env['product.product'].sudo().search([('id', '=', products_id)]).name
production_num = self.env['mrp.production'].sudo().search_count(
[('product_id', '=', products_id), ('state', '=', state)])
if production_num >= 1:
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
url_with_id = f"{url}/web#view_type=list&action={action_id}"
new_content = (content.replace('{{name}}', product_name)
.replace('{{url}}', url_with_id)
.replace('{{production_num}}', str(production_num)))
contents.append(new_content)
return contents
def request_url(self, id):
@@ -48,7 +103,7 @@ class SFMessageMrpProduction(models.Model):
action_id = self.env.ref('stock.action_picking_tree_all').id
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_theme_treehouse')]).id
# 查询参数
params = {'id': id, 'menu_id': menu_id, 'action': action_id, 'model': 'mrp.production',
params = {'id': id, 'menu_id': menu_id, 'action': action_id, 'model': 'mrp.production',
'view_type': 'form'}
# 拼接查询参数
query_string = urlencode(params)

View File

@@ -0,0 +1,17 @@
import logging
from odoo import models, fields, api, _
class SFMessageMrpProductionAdjustWizard(models.TransientModel):
_name = 'sf.production.technology.re_adjust.wizard'
_description = "制造订单工艺调整"
_inherit = ['sf.production.technology.re_adjust.wizard', 'jikimo.message.dispatch']
def confirm(self):
super(SFMessageMrpProductionAdjustWizard, self).confirm()
try:
for production_info in self.production_id:
if production_info.state == 'technology_to_confirmed':
production_info.add_queue('待确认加工路线')
except Exception as e:
logging.info('add_queue待确认加工路线 error:%s' % e)

View File

@@ -0,0 +1,23 @@
import logging
from odoo import models, fields, api, _
class SFMessageMrpProductionWizard(models.TransientModel):
_name = 'sf.production.technology.wizard'
_description = "制造订单工艺确认向导"
_inherit = ['sf.production.technology.wizard', 'jikimo.message.dispatch']
def confirm(self):
productions = super(SFMessageMrpProductionWizard, self).confirm()
try:
for production_info in self.production_id:
if production_info.state == 'confirmed' and production_info.product_id.categ_id.type == '成品':
production_info.add_queue('待排程')
for production_id in productions:
workorder_ids = production_id.workorder_ids.filtered(
lambda p: p.routing_type == '表面工艺' and p.state != 'cancel')
for workorder_id in workorder_ids:
purchase_orders_id = workorder_id._get_surface_technics_purchase_ids()
purchase_orders_id.add_queue('工序外协采购单通知')
except Exception as e:
logging.info('add_queue待排程 error:%s' % e)

View File

@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
from urllib.parse import urlencode
class SFMessagePlan(models.Model):
_name = 'product.product'
_inherit = ['product.product', 'jikimo.message.dispatch']
def _get_message(self, message_queue_ids):
contents = []
for message_queue_id in message_queue_ids:
if message_queue_id.message_template_id.name == '工单已下发通知':
content = message_queue_id.message_template_id.content
product_product = self.env['product.product'].sudo().search([('id', '=', int(message_queue_id.res_id))])
mrp_production_list = self.env['mrp.production'].sudo().search(
[('product_id', '=', product_product.id)])
production_num = 0
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(
lambda w: w.routing_type == routing_type and w.state == 'ready')
if mrp_production_ready:
production_num += 1
if production_num >= 1:
url = self.get_request_url()
content = content.replace('{{product_id}}', product_product.name).replace(
'{{number}}', str(production_num)).replace(
'{{request_url}}', url)
contents.append(content)
return contents
def get_request_url(self):
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
# 查询参数
params = {'menu_id': menu_id, 'action': action_id, 'model': 'mrp.workorder',
'view_type': 'list', 'active_id': active_id}
# 拼接查询参数
query_string = urlencode(params)
# 拼接URL
full_url = url + "/web#" + query_string
return full_url

View File

@@ -8,20 +8,62 @@ class SFMessagePurchase(models.Model):
def _get_message(self, message_queue_ids):
contents = []
process_outsourcing = set()
for message_queue_id in message_queue_ids:
if message_queue_id.message_template_id.name == '坯料采购提醒':
if message_queue_id.message_template_id.name in (
'坯料采购提醒', '委外加工采购单提醒', '外购订单采购单提醒'):
content = message_queue_id.message_template_id.content
url = self.request_url(int(message_queue_id.res_id))
purchase_order_line = self.env['purchase.order'].search([('id', '=', int(message_queue_id.res_id))])
content = content.replace('{{name}}', purchase_order_line.name).replace(
'{{request_url}}', url)
contents.append(content)
return contents
if message_queue_id.message_template_id.name == '工序外协采购单通知':
content = message_queue_id.message_template_id.content
purchase_order_line = self.env['purchase.order'].sudo().search(
[('id', '=', int(message_queue_id.res_id))])
mrp_production = self.env['mrp.production'].sudo().search([('name', '=', purchase_order_line.origin)])
process_outsourcing.add(mrp_production.product_id.id)
if message_queue_id.message_template_id.name in ('采购订单预警提醒', '采购单已逾期提醒'):
content = message_queue_id.message_template_id.content
if message_queue_id.message_template_id.name == '采购订单预警提醒':
domain = [('delivery_warning', '=', 'warning')]
action_id = self.env.ref("sf_message.purchase_form_warning_action").id
else:
domain = [('delivery_warning', '=', 'overdue')]
action_id = self.env.ref("sf_message.purchase_form_overdue_action").id
purchase_order_num = self.env['purchase.order'].sudo().search_count(domain)
if purchase_order_num >= 1:
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
menu_id = self.env.ref('purchase.menu_purchase_form_action').id
url_with_id = f"{url}/web#view_type=list&action={action_id}&menu_id={menu_id}"
content = content.replace('{{url}}', url_with_id).replace('{{num}}', str(purchase_order_num))
contents.append(content)
if process_outsourcing:
content_info = content
for products_id in process_outsourcing:
production_num = 0
product_name = self.env['product.product'].sudo().search([('id', '=', products_id)]).name
production_list = self.env['mrp.production'].sudo().search(
[('product_id', '=', products_id), ('state', '=', 'confirmed')])
for production_info in production_list:
workorder_ids = len(production_info.workorder_ids.filtered(
lambda p: p.routing_type == '表面工艺' and p.state != 'cancel'))
production_num += workorder_ids
if production_num >= 1:
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
action_id = self.env.ref('purchase.purchase_form_action').id
url_with_id = f"{url}/web#view_type=list&action={action_id}"
new_content = (content_info.replace('{{name}}', product_name)
.replace('{{url}}', url_with_id)
.replace('{{num}}', str(production_num)))
contents.append(new_content)
return contents, message_queue_ids
def request_url(self, id):
url = self.env['ir.config_parameter'].get_param('web.base.url')
action_id = self.env.ref('purchase.purchase_form_action').id
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_website_payment')]).id
menu_id = self.env.ref('purchase.menu_purchase_form_action').id
# 查询参数
params = {'id': id, 'menu_id': menu_id, 'action': action_id,
'model': 'purchase.order',
@@ -31,3 +73,27 @@ class SFMessagePurchase(models.Model):
# 拼接URL
full_url = url + "/web#" + query_string
return full_url
def _overdue_or_warning_func(self):
last_overdue_order, last_warning_order = super(SFMessagePurchase, self)._overdue_or_warning_func()
if last_overdue_order:
business_node_id = self.env.ref('sf_message.template_purchase_order_overdue').id
purchase_order_has = self._get_message_queue(business_node_id)
if not purchase_order_has:
last_overdue_order.add_queue("采购单已逾期提醒")
if last_warning_order:
business_node_id = self.env.ref('sf_message.template_purchase_order_warning').id
purchase_order_has = self._get_message_queue(business_node_id)
if not purchase_order_has:
last_warning_order.add_queue("采购订单预警提醒")
def _get_message_queue(self, business_node_id):
message_template = self.env["jikimo.message.template"].sudo().search([
("model", "=", self._name),
("bussiness_node_id", "=", business_node_id)
], limit=1)
purchase_order_has = self.env['jikimo.message.queue'].sudo().search([
('message_status', '=', 'pending'),
('message_template_id', '=', message_template.id)
])
return purchase_order_has

View File

@@ -33,4 +33,4 @@ class SFMessageQualityCncTest(models.Model):
content_template = content.replace('{{judge_num}}', str(i))
content_template = content_template.replace('{{url}}', url_with_id)
contents.append(content_template)
return contents
return contents, message_queue_ids

View File

@@ -8,17 +8,6 @@ class SFMessageSale(models.Model):
_name = 'sale.order'
_inherit = ['sale.order', 'jikimo.message.dispatch']
@api.model_create_multi
def create(self, vals_list):
res = super(SFMessageSale, self).create(vals_list)
if res:
try:
logging.info('add_queue res:%s' % res)
res.add_queue('待接单')
except Exception as e:
logging.info('add_queue error:%s' % e)
return res
# 确认接单
def action_confirm(self):
res = super(SFMessageSale, self).action_confirm()
@@ -29,19 +18,34 @@ class SFMessageSale(models.Model):
purchase_order_id = []
if picking_ids:
for picking_id in picking_ids:
picking_id.add_queue('待确认加工路线')
purchase_order_ids = (
picking_id.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id |
picking_id.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id).ids
purchase_order_id.extend(purchase_order_ids)
if purchase_order_id:
purchase_order_list = self.env['purchase.order'].sudo().search([('id', 'in', purchase_order_id)])
purchase_order_list = self.env['purchase.order'].sudo().search(
[('id', 'in', purchase_order_id)])
for purchase_order_info in purchase_order_list:
purchase_order_info.add_queue('坯料采购提醒')
purchase_order_ids = self.order_line.purchase_line_ids.order_id | self.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id | self.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id
for purchase_order_id in purchase_order_ids:
if purchase_order_id.purchase_type == 'outsourcing':
purchase_order_id.add_queue('委外加工采购单提醒')
if purchase_order_id.purchase_type == 'standard':
purchase_order_id.add_queue('外购订单采购单提醒')
except Exception as e:
logging.info('add_queue error:%s' % e)
logging.info('action_confirm res:%s' % res)
return res
def confirm_to_supply_method(self):
super(SFMessageSale, self).confirm_to_supply_method()
try:
self.add_queue('待确认供货方式')
except Exception as e:
logging.info('add_queue待确认供货方式 error:%s' % e)
# 继承并重写jikimo.message.dispatch的_get_message()
def _get_message(self, message_queue_ids):
contents = []
@@ -53,14 +57,16 @@ class SFMessageSale(models.Model):
time_range = timedelta(minutes=2)
i = 0
for item in message_queue_ids:
if item.message_template_id.bussiness_node_id.name == '待接单':
content = super(SFMessageSale, self)._get_message(item)
action_id = self.env.ref('sale.action_quotations_with_onboarding').id
if item.message_template_id.bussiness_node_id.name in ('待接单', '待确认供货方式'):
content, _ = super(SFMessageSale, self)._get_message(item)
action_id = self.env.ref('sale.action_quotations_with_onboarding').id \
if item.message_template_id.bussiness_node_id.name == '待接单' \
else self.env.ref('sale.action_orders').id
url_with_id = f"{url}/web#id={item.res_id}&view_type=form&action={action_id}"
content = content[0].replace('{{url}}', url_with_id)
contents.append(content)
elif item.message_template_id.bussiness_node_id.name == '确认接单':
content = super(SFMessageSale, self)._get_message(item)
content, _ = super(SFMessageSale, self)._get_message(item)
sale_order_line = self.env['sale.order.line'].sudo().search([('order_id', '=', int(item.res_id))])
product = sale_order_line[0].product_id.name if len(sale_order_line) == 1 else '%s...' % \
sale_order_line[
@@ -97,7 +103,7 @@ class SFMessageSale(models.Model):
elif bussiness_node == '销售订单已逾期':
content = content_template.replace('{{overdue_num}}', str(i))
contents.append(content)
return contents
return contents, message_queue_ids
# # 销售订单逾期预警和已逾期
def _overdue_or_warning_func(self):

View File

@@ -12,26 +12,44 @@ class SFMessageStockPicking(models.Model):
@api.model_create_multi
def create(self, vals):
result = super(SFMessageStockPicking, self).create(vals)
for obj in result:
if obj.location_id.name == '进货' and obj.location_dest_id.name == '刀具房':
obj.add_queue('调拨入库')
try:
for obj in result:
if obj.location_id.name == '进货' and obj.location_dest_id.name == '刀具房':
obj.add_queue('调拨入库')
except Exception as e:
logging.info('add_queue调拨入库 error:%s' % e)
return result
@api.depends('move_type', 'immediate_transfer', 'move_ids.state', 'move_ids.picking_id')
def _compute_state(self):
super(SFMessageStockPicking, self)._compute_state()
for record in self:
if record.state == 'assigned' and record.check_in == 'PC':
record.add_queue('坯料发料提醒')
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('坯料发料提醒')
if record.picking_type_id.sequence_code == 'SFP' and record.state == 'done':
stock_picking_sfp = record.env['stock.picking'].search(
[('origin', '=', record.origin), ('state', '!=', 'done'),
('picking_type_id.sequence_code', '=', 'SFP')])
if not stock_picking_sfp:
stock_picking_send = self.env["jikimo.message.queue"].sudo().search([('res_id', '=', record.id)])
if not stock_picking_send:
record.add_queue('订单发货提醒')
if record.picking_type_id.sequence_code == 'SFP' and record.state == 'done':
stock_picking_sfp = record.env['stock.picking'].search(
[('origin', '=', record.origin), ('state', '!=', 'done'),
('picking_type_id.sequence_code', '=', 'SFP')])
if not stock_picking_sfp:
stock_picking_send = self.env["jikimo.message.queue"].sudo().search(
[('res_id', '=', record.id)])
if not stock_picking_send:
record.add_queue('订单发货提醒')
if record.picking_type_id.sequence_code == 'OCOUT' and record.state == 'assigned':
mrp_production = self.env['mrp.production'].sudo().search([('name', '=', record.origin)])
production_list = self.env['mrp.production'].sudo().search(
[('product_id', '=', mrp_production.product_id.id)])
manufacturing_order_names = production_list.mapped('name')
stock_picking_list = self.env['stock.picking'].sudo().search(
[('origin', 'in', manufacturing_order_names), ('picking_type_id.sequence_code', '=', 'OCOUT')])
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('工序外协发料通知')
except Exception as e:
logging.info('add_queue_compute_state error:%s' % e)
def deal_stock_picking_sfp(self, message_queue_id): # 处理订单发货提醒
content = None
@@ -49,32 +67,23 @@ class SFMessageStockPicking(models.Model):
def _get_message(self, message_queue_ids):
contents = []
product_id = []
for message_queue_id in message_queue_ids:
i = 0
if message_queue_id.message_template_id.name == '坯料发料提醒':
content = message_queue_id.message_template_id.content
stock_picking_line = self.env['stock.picking'].sudo().search([('id', '=', int(message_queue_id.res_id))])
mrp_production_info = self.env['mrp.production'].sudo().search(
[('name', '=', stock_picking_line.origin)])
mrp_production_list = self.env['mrp.production'].sudo().search(
[('product_id', '=', mrp_production_info.product_id.id)])
for mrp_production_line in mrp_production_list:
picking_ids = mrp_production_line.picking_ids
for picking_id in picking_ids:
if picking_id.state == 'assigned' and picking_id.check_in == 'PC':
i += 1
if i > 0 and mrp_production_info.product_id.id not in product_id:
url = self.request_url()
content = content.replace('{{product_id}}', mrp_production_info.product_id.name).replace(
'{{number}}', str(i)).replace('{{request_url}}', url)
product_id.append(mrp_production_info.product_id.id)
contents.append(content)
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('{{name}}', stock_picking_line.name).replace(
'{{request_url}}', url_with_id)
contents.append(content)
elif message_queue_id.message_template_id.name == '订单发货提醒':
content = self.deal_stock_picking_sfp(message_queue_id)
if content:
contents.append(content)
return contents
return contents, message_queue_ids
def get_special_url(self, id, tmplate_name, special_name, model_id):
menu_id = 0
@@ -86,19 +95,6 @@ class SFMessageStockPicking(models.Model):
else:
return super(SFMessageStockPicking, self).get_special_url(id, tmplate_name, special_name, model_id)
def request_url(self):
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
action_id = self.env.ref('stock.stock_picking_type_action').id
menu_id = self.env['ir.model.data'].sudo().search([('name', '=', 'module_theme_treehouse')]).id
# 查询参数
params = {'menu_id': menu_id, 'action': action_id, 'model': 'stock.picking',
'view_type': 'kanban'}
# 拼接查询参数
query_string = urlencode(params)
# 拼接URL
full_url = url + "/web#" + query_string
return full_url
def request_url1(self, id):
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
action_id = self.env.ref('stock.action_picking_tree_all').id

View File

@@ -16,4 +16,6 @@ class SfMessageTemplate(models.Model):
res.append('mrp.workorder')
res.append('sf.maintenance.logs')
res.append('quality.cnc.test')
res.append('mrp.production')
res.append('product.product')
return res

View File

@@ -12,19 +12,30 @@ class SFMessageWork(models.Model):
_name = 'mrp.workorder'
_inherit = ['mrp.workorder', 'jikimo.message.dispatch']
@api.depends('production_availability', 'blocked_by_workorder_ids.state', 'production_id.tool_state')
@api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state',
'production_id.tool_state', 'production_id.schedule_state', 'sequence',
'production_id.programming_state')
def _compute_state(self):
super(SFMessageWork, self)._compute_state()
for workorder in self:
if workorder.state == 'ready' and workorder.routing_type == '装夹预调':
min_sequence_wk = None
work_ids = workorder.production_id.workorder_ids.filtered(lambda w: w.routing_type == '装夹预调')
if work_ids:
min_sequence_wk = work_ids.sorted(key=lambda w: w.sequence)[0]
if (
workorder.state == 'ready' and workorder.routing_type == '装夹预调' and workorder.id == min_sequence_wk.id) or (
workorder.state == 'ready' and workorder.routing_type == '人工线下加工'):
message_template = self.env["jikimo.message.template"].sudo().search(
[("name", "=", '工单已下发通知')], limit=1)
jikimo_message_queue = self.env['jikimo.message.queue'].sudo().search(
[('res_id', '=', workorder.id), ("message_status", "=", "pending")])
[('res_id', '=', workorder.production_id.product_id.id),
("message_status", "in", ("pending", "sent")),
('message_template_id', '=', message_template.id)])
if not jikimo_message_queue:
workorder.add_queue('工单已下发通知')
workorder.production_id.product_id.add_queue('工单已下发通知')
def _get_message(self, message_queue_ids):
contents = []
product_id = []
bussiness_node = None
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
current_time_strf = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@@ -37,20 +48,7 @@ class SFMessageWork(models.Model):
}
i = 0
for message_queue_id in message_queue_ids:
if message_queue_id.message_template_id.name == '工单已下发通知':
content = message_queue_id.message_template_id.content
mrp_workorder_line = self.env['mrp.workorder'].sudo().search([('id', '=', int(message_queue_id.res_id))])
mrp_workorder_list = self.env['mrp.workorder'].sudo().search(
[('product_id', '=', mrp_workorder_line.product_id.id), ('state', '=', 'ready'),
('routing_type', '=', '装夹预调')])
if len(mrp_workorder_list) > 0 and mrp_workorder_line.product_id.id not in product_id:
url = self.request_url()
content = content.replace('{{product_id}}', mrp_workorder_line.product_id.name).replace(
'{{number}}', str(len(mrp_workorder_list))).replace(
'{{request_url}}', url)
product_id.append(mrp_workorder_line.product_id.id)
contents.append(content)
elif message_queue_id.message_template_id.name in template_names['预警'] + template_names['已逾期']:
if message_queue_id.message_template_id.name in template_names['预警'] + template_names['已逾期']:
item = message_queue_id.message_template_id
bussiness_node = item.bussiness_node_id.name
for reminder_time in item.reminder_time_ids:
@@ -93,21 +91,7 @@ class SFMessageWork(models.Model):
elif bussiness_node in template_names['已逾期']:
content = content_template.replace('{{overdue_num}}', str(i))
contents.append(content)
return contents
def request_url(self):
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['ir.model.data'].sudo().search([('name', '=', 'module_stock_dropshipping')]).id
active_id = self.env['mrp.workcenter'].sudo().search([('name', '=', '工件装夹中心')]).id
# 查询参数
params = {'menu_id': menu_id, 'action': action_id, 'model': 'mrp.workorder',
'view_type': 'list', 'active_id': active_id}
# 拼接查询参数
query_string = urlencode(params)
# 拼接URL
full_url = url + "/web#" + query_string
return full_url
return contents, message_queue_ids
def _overdue_or_warning_func(self):
workorders = self.env['mrp.workorder'].search(

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="purchase_form_warning_action" model="ir.actions.act_window">
<field name="name">采购订单</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">purchase.order</field>
<field name="view_mode">tree,kanban,form,pivot,graph,calendar,activity</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('purchase.purchase_order_view_tree')}),
(0, 0, {'view_mode': 'kanban', 'view_id': ref('purchase.purchase_order_view_kanban_without_dashboard')}),
]"/>
<field name="domain">[('state','in',('purchase', 'done'))]</field>
<field name="search_view_id" ref="purchase.purchase_order_view_search"/>
<field name="context">{'search_default_filter_order_warning':1}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
还没有采购订单, 我们先创建一个!
</p>
<p>
当您下单到您的供应商,确定您的询价它会变成采购订单.
</p>
</field>
</record>
<record id="purchase_form_overdue_action" model="ir.actions.act_window">
<field name="name">采购订单</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">purchase.order</field>
<field name="view_mode">tree,kanban,form,pivot,graph,calendar,activity</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('purchase.purchase_order_view_tree')}),
(0, 0, {'view_mode': 'kanban', 'view_id': ref('purchase.purchase_order_view_kanban_without_dashboard')}),
]"/>
<field name="domain">[('state','in',('purchase', 'done'))]</field>
<field name="search_view_id" ref="purchase.purchase_order_view_search"/>
<field name="context">{'search_default_filter_order_overdue':1}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
还没有采购订单, 我们先创建一个!
</p>
<p>
当您下单到您的供应商,确定您的询价它会变成采购订单.
</p>
</field>
</record>
</odoo>

View File

@@ -26,7 +26,7 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
ret = json.loads(ret['result'])
logging.info('下发编程单:%s' % ret)
domain = [('programming_no', '=', ret['programming_no'])]
if ret['manufacturing_type'] in ('scrap', 'invalid_tool_rework'):
if ret['manufacturing_type'] in ('scrap', 'invalid_tool_rework', 'rework'):
domain += [('state', 'not in', ['done', 'scrap', 'cancel'])]
else:
domain += [('state', 'in', ['confirmed', 'pending_cam'])]
@@ -56,7 +56,7 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
res['message'] = '编程单号为%s的CNC程序文件从FTP拉取失败' % (ret['programming_no'])
return json.JSONEncoder().encode(res)
for production in productions:
production.write({'programming_state': '已编程', 'work_state': '已编程'})
production.write({'programming_state': '已编程', 'work_state': '已编程', 'is_rework': False})
for panel in ret['processing_panel'].split(','):
# 查询状态为进行中且工序类型为CNC加工的工单
cnc_workorder_has = production.workorder_ids.filtered(

View File

@@ -13,7 +13,8 @@ class sf_production_plan(models.Model):
_description = 'sf_production_plan'
_inherit = ['mail.thread']
# _order = 'state desc, write_date desc'
part_name = fields.Char('零件名称', related='product_id.part_name', readonly=True)
part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True)
state = fields.Selection([
('draft', '待排程'),
('done', '已排程'),

View File

@@ -17,6 +17,8 @@
decoration-danger="state == 'finished'"/>
<field name="name"/>
<field name="origin"/>
<field name="part_number" optional="show"/>
<field name="part_name" optional="hide"/>
<field name="order_deadline" widget="date"/>
<field name="product_qty"/>
<field name="production_line_id"/>

View File

@@ -6678,7 +6678,7 @@ msgstr "账单状态"
#: model_terms:ir.ui.view,arch_db:account.account_journal_dashboard_kanban_view
#, python-format
msgid "Bills"
msgstr "账单"
msgstr "发票账单"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.account_journal_dashboard_kanban_view
@@ -23023,7 +23023,7 @@ msgstr "支付:支付收据"
#: model_terms:ir.ui.view,arch_db:account.view_account_payment_search
#, python-format
msgid "Payments"
msgstr "付"
msgstr "付款单"
#. module: account
#: model_terms:ir.actions.act_window,help:account.action_account_payments

View File

@@ -16,7 +16,7 @@ class SfQualityCncTest(models.Model):
equipment_id = fields.Many2one(related='workorder_id.equipment_id', string='加工设备')
production_line_id = fields.Many2one(related='workorder_id.production_line_id',
string='生产线')
part_number = fields.Char(related='workorder_id.part_number', string='成品零件图号')
part_number = fields.Char(related='workorder_id.part_number', string='零件图号')
detection_report = fields.Binary(related='workorder_id.detection_report', readonly=True, string='检测报告')
state = fields.Selection([
('waiting', '待判定'),
@@ -25,7 +25,7 @@ class SfQualityCncTest(models.Model):
('pass', '合格'),
('fail', '不合格')], string='判定结果')
number = fields.Integer('数量', default=1)
test_results = fields.Selection([("合格", "合格")], string="检测结果")
test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")], string="检测结果")
reason = fields.Selection(
[("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"), ("operate computer", "操机"),
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因")

View File

@@ -18,7 +18,8 @@
<field name="production_id"/>
<field name="processing_panel"/>
<field name="product_id"/>
<field name="part_number"/>
<field name="part_number" optional="show"/>
<field name="part_name" optional="hide"/>
<field name="number"/>
<field name="state" widget="badge"
decoration-success="state == 'done'"

View File

@@ -16,6 +16,7 @@
'security/ir.model.access.csv',
'wizard/sale_order_wizard_views.xml',
'wizard/purchase_order_wizard_views.xml',
'data/cron_data.xml',
'views/sale_team.xml',
'views/sale_order_view.xml',
'views/res_partner_view.xml',
@@ -26,6 +27,7 @@
'assets': {
'web.assets_backend': [
'sf_sale/static/js/setTableWidth.js',
'sf_sale/static/src/css/purchase_list.css',
]
},
'demo': [

View File

@@ -0,0 +1,16 @@
<odoo>
<data noupdate="1">
<record model="ir.cron" id="ir_cron_purchase_order_overdue_warning">
<field name="name">检查采购单是否已逾期预警和逾期</field>
<field name="model_id" ref="model_purchase_order"/>
<field name="state">code</field>
<field name="code">model._overdue_or_warning_func()</field>
<field name="interval_number">10</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False"/>
<field name="user_id" ref="base.user_root"/>
<field name="active" eval="True"/>
</record>
</data>
</odoo>

View File

@@ -55,12 +55,15 @@ class ReSaleOrder(models.Model):
store=True, readonly=False, copy=False, precompute=True,
states=READONLY_FIELD_STATES, default=fields.Datetime.now)
delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效',
tracking=True)
delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')],
default='normal',
string='时效', tracking=True)
order_code = fields.Char('平台订单号', readonly=True)
# 业务平台分配工厂后在智能工厂先创建销售订单
def sale_order_create(self, company_id, delivery_name, delivery_telephone, delivery_address,
deadline_of_delivery, payments_way, pay_way, state='sale'):
deadline_of_delivery, payments_way, pay_way, order_number, state='sale'):
now_time = datetime.datetime.now()
partner = self.get_customer()
data = {
@@ -76,6 +79,7 @@ class ReSaleOrder(models.Model):
'address_of_delivery': delivery_address,
'payments_way': payments_way,
'pay_way': pay_way,
'order_code': order_number,
}
if deadline_of_delivery:
# deadline_of_delivery字段存在为false字符串情况
@@ -158,10 +162,71 @@ class ReSaleOrder(models.Model):
if not line.tax_id:
raise UserError('请对【订单行】中的【税】进行选择')
consignment_purchase_order_count = fields.Integer(
"Number of consignment Purchase Order Generated",
compute='_compute_purchase_order_count')
@api.depends('order_line.purchase_line_ids.order_id')
def _compute_purchase_order_count(self):
for order in self:
order.purchase_order_count = len(order._get_purchase_orders().filtered(
lambda po: po.purchase_type not in ['outsourcing']))
order.consignment_purchase_order_count = len(order._get_purchase_orders().filtered(
lambda po: po.purchase_type in ['outsourcing']))
def action_view_purchase_orders(self):
"""
采购
"""
self.ensure_one()
purchase_order_ids = self._get_purchase_orders().filtered(
lambda po: po.purchase_type not in ['outsourcing']).ids
action = {
'res_model': 'purchase.order',
'type': 'ir.actions.act_window',
}
if len(purchase_order_ids) == 1:
action.update({
'view_mode': 'form',
'res_id': purchase_order_ids[0],
})
else:
action.update({
'name': _("%s生成采购订单", self.name),
'domain': [('id', 'in', purchase_order_ids)],
'view_mode': 'tree,form',
})
return action
def action_view_consignment_purchase_orders(self):
"""
委外加工
"""
self.ensure_one()
outsourcing_purchase_order_ids = self._get_purchase_orders().filtered(
lambda po: po.purchase_type in ['outsourcing']).ids
action = {
'res_model': 'purchase.order',
'type': 'ir.actions.act_window',
}
if len(outsourcing_purchase_order_ids) == 1:
action.update({
'view_mode': 'form',
'res_id': outsourcing_purchase_order_ids[0],
})
else:
action.update({
'name': _("%s生成委外加工订单", self.name),
'domain': [('id', 'in', outsourcing_purchase_order_ids)],
'view_mode': 'tree,form',
})
return action
class ResaleOrderLine(models.Model):
_inherit = 'sale.order.line'
# part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True)
part_name = fields.Char('零件名称', related='product_id.part_name', readonly=True)
model_glb_file = fields.Binary('模型的glb文件', compute='_compute_model_glb_file', store=True)
# product_template_id = fields.Many2one(
# string="产品",
@@ -199,6 +264,7 @@ class ProductTemplate(models.Model):
_inherit = 'product.template'
manual_quotation = fields.Boolean('人工编程', default=False)
part_name = fields.Char(string='零件名称', readonly=True)
class RePurchaseOrder(models.Model):
@@ -215,8 +281,51 @@ class RePurchaseOrder(models.Model):
compute='_compute_user_id',
store=True)
purchase_type = fields.Selection([('standard', '标准采购'), ('consignment', '委外加工')], string='采购类型',
default='standard')
purchase_type = fields.Selection(
[('standard', '标准采购'), ('consignment', '工序外协'), ('outsourcing', '委外加工'), ('outside', '外购订单')],
string='采购类型', default='standard', store=True, compute='_compute_purchase_type')
origin_sale_id = fields.Many2one('sale.order', string='销售订单号', compute='_compute_origin_sale_id')
# 合同编号
contract_number = fields.Char(string='合同编号', size=20)
# 合同概况
contract_summary = fields.Text(string='合同概况')
@api.depends('origin')
def _compute_purchase_type(self):
for purchase in self:
order_id = self.env['sale.order'].sudo().search([('name', '=', purchase.origin)])
if order_id:
product_list = [line.product_id.id for line in purchase.order_line]
for order_line in order_id.order_line:
if order_line.supply_method == 'purchase':
if order_line.product_id.id in product_list:
purchase.purchase_type = 'outside'
break
elif order_line.supply_method == 'outsourcing':
if order_line.product_id.id in product_list:
purchase.purchase_type = 'outsourcing'
break
@api.depends('order_line.move_dest_ids.group_id.mrp_production_ids',
'order_line.move_ids.move_dest_ids.group_id.mrp_production_ids', 'origin')
def _compute_origin_sale_id(self):
for purchase in self:
productions_ids = purchase._get_mrp_productions()
if productions_ids:
if productions_ids[0].sale_order_id:
purchase.origin_sale_id = productions_ids[0].sale_order_id.id
continue
order_id = self.env['sale.order'].sudo().search([('name', '=', purchase.origin)])
if order_id:
purchase.origin_sale_id = order_id.id
continue
purchase.origin_sale_id = False
delivery_warning = fields.Selection([('normal', '正常'), ('warning', '预警'), ('overdue', '已逾期')],
string='交期状态', default='normal',
tracking=True)
@api.depends('partner_id')
def _compute_user_id(self):
@@ -255,7 +364,7 @@ class RePurchaseOrder(models.Model):
else:
server_template = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', pp.surface_technics_parameters_id.id),
('detailed_type', '=', 'service')])
('detailed_type', '=', 'service')])
server_product_process.append((0, 0, {
'product_id': server_template.product_variant_id.id,
'product_qty': 1,
@@ -291,14 +400,42 @@ class RePurchaseOrder(models.Model):
move_id.put_move_line()
for line in item.order_line:
if line.product_id.categ_type == '表面工艺':
for production_name in item.origin.split(','):
production = self.env['mrp.production'].search([('name', '=', production_name)])
for workorder in production.workorder_ids.filtered(
lambda wd: wd.routing_type == '表面工艺' and wd.state == 'waiting' and line.product_id.server_product_process_parameters_id == wd.surface_technics_parameters_id):
workorder.state = 'ready'
if item.origin:
for production_name in item.origin.split(','):
production = self.env['mrp.production'].search([('name', '=', production_name)])
for workorder in production.workorder_ids.filtered(
lambda wd: wd.routing_type == '表面工艺' and wd.state == 'waiting' and line.product_id.server_product_process_parameters_id == wd.surface_technics_parameters_id):
workorder.state = 'ready'
return result
# # 采购订单逾期预警和已逾期
def _overdue_or_warning_func(self):
purchase_order = self.sudo().search(
[('state', 'in', ['purchase']), ('date_planned', '!=', False),
('receipt_status', 'in', ('partial', 'pending'))])
last_overdue_order = None
last_warning_order = None
for item in purchase_order:
current_time = datetime.datetime.now()
if item.date_planned <= current_time: # 已逾期
item.delivery_warning = 'overdue'
last_overdue_order = item
elif (item.date_planned - current_time).total_seconds() < 48 * 3600: # 预警
item.delivery_warning = 'warning'
last_warning_order = item
purchase_order_done = self.sudo().search([('state', 'in', ['purchase']), ('receipt_status', '=', 'full')])
purchase_order_overdue = purchase_order_done.filtered(lambda x: x.delivery_warning in ['overdue', 'warning'])
if purchase_order_overdue:
purchase_order_overdue.write({'delivery_warning': 'normal'})
return last_overdue_order, last_warning_order
class PurchaseOrderLine(models.Model):
_inherit = 'purchase.order.line'
part_name = fields.Char('零件名称', related='product_id.part_name', readonly=True)
# part_number = fields.Char('零件图号',related='product_id.part_number', readonly=True)
class ResPartnerToSale(models.Model):
_inherit = 'res.partner'

View File

@@ -0,0 +1,3 @@
.purchase_order_list_name {
min-width: 62px !important;
}

View File

@@ -16,10 +16,11 @@
<button name="button_confirm" type="object" states="draft" context="{'validate_analytic': True}"
string="Confirm Order" id="draft_confirm"/>
<button name="action_view_picking"
string="接收产品" class="oe_highlight" type="object"
attrs="{'invisible': ['|', '|' , ('is_shipped', '=', True), ('state','not in', ('purchase','done')), ('incoming_picking_count', '=', 0)]}"
data-hotkey="y" groups="stock.group_stock_user"/>
<button name="button_cancel" states="draft,to approve,sent,purchase" string="取消" type="object" data-hotkey="x" />
string="接收产品" class="oe_highlight" type="object"
attrs="{'invisible': ['|', '|' , ('is_shipped', '=', True), ('state','not in', ('purchase','done')), ('incoming_picking_count', '=', 0)]}"
data-hotkey="y" groups="stock.group_stock_user"/>
<button name="button_cancel" states="draft,to approve,sent,purchase" string="取消" type="object"
data-hotkey="x"/>
</xpath>
<xpath expr="//header/button[@name='button_cancel'][2]" position="attributes">
<attribute name="invisible">1</attribute>
@@ -35,13 +36,13 @@
<field name="partner_id" widget="res_partner_many2one" context="{'is_supplier': True }"/>
</field>
<field name="currency_id" position="after">
<field name="remark" attrs="{'readonly': [('state', 'in', ['purchase'])]}"/>
<field name="remark" attrs="{'readonly': [('state', 'in', ['purchase'])]}" string="订单备注"/>
</field>
<xpath expr="//form/header/button[@name='button_confirm'][2]" position="replace">
<button name="button_confirm" type="object" context="{'validate_analytic': True}"
string="确认订单" id="draft_confirm"
groups="sf_base.group_purchase,sf_base.group_purchase_director"
attrs="{'invisible': [('state', 'in', ['purchase'])]}"
attrs="{'invisible': [('state', 'in', ['purchase', 'cancel'])]}"
/>
</xpath>
<xpath expr="//form/header/button[@name='action_rfq_send'][1]" position="replace">
@@ -130,6 +131,23 @@
<xpath expr="//field[@name='order_line']/tree/field[@name='product_id']" position="attributes">
<attribute name="options">{'no_create': True}</attribute>
</xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='name']" position="attributes">
<attribute name="optional">hide</attribute>
</xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='name']" position="after">
<field name="part_name" optional="show"/>
<!-- <field name="part_number" optional="show"/>-->
</xpath>
<xpath expr="//field[@name='date_order']" position="attributes">
<attribute name="string">签约日期</attribute>
</xpath>
<field name="payment_term_id" position="attributes">
<attribute name="invisible">1</attribute>
</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"/>
</xpath>
<field name="partner_ref" position="attributes">
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}
</attribute>
@@ -159,10 +177,50 @@
</attribute>
</field>
<!-- 添加采购类型字段 -->
<field name="partner_ref" position="after">
<!-- 添加采购类型字段 -->
<field name="partner_id" position="after">
<field name="purchase_type" string="采购类型" readonly="1"/>
<field name="picking_type_id" string="作业类型" domain="[('code','=','incoming'), '|', ('warehouse_id', '=', False), ('warehouse_id.company_id', '=', company_id)]" options="{'no_create': True}" groups="stock.group_stock_multi_locations"/>
<label for="date_planned" string="最近交货日期"/>
<div name="date_planned_div" class="o_row">
<field name="date_planned" attrs="{'readonly': [('state', 'not in', ('draft', 'sent', 'to approve', 'purchase'))]}"/>
<field name="mail_reminder_confirmed" invisible="1"/>
<span class="text-muted" attrs="{'invisible': [('mail_reminder_confirmed', '=', False)]}">(confirmed by vendor)</span>
</div>
<field name="effective_date" attrs="{'invisible': [('effective_date', '=', False)]}" string="到货时间"/>
</field>
<field name="partner_ref" position="attributes">
<attribute name="invisible">1</attribute>
</field>
<xpath expr="//sheet/group/group[2]/field[1]" position="before">
<field name="partner_ref" string="合同名称"/>
<field name="contract_number"/>
</xpath>
<!-- 添加销售订单号字段-->
<xpath expr="//sheet/group/group[2]/div[@name='date_approve']" position="after">
<!-- <field name="origin_sale_id" readonly="1" attrs="{'invisible': [('origin_sale_id', '=', False)]}" string="参考销售订单"/> -->
<field name="origin_sale_id" readonly="1" string="参考销售订单"/>
</xpath>
<xpath expr="//sheet/group/group[2]/field[@name='effective_date']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<!-- 隐藏 label -->
<xpath expr="//label[@for='receipt_reminder_email']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<!-- 隐藏整个 reminder div -->
<xpath expr="//div[@name='reminder']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<!-- 使用更精确的路径定位原始元素 -->
<xpath expr="//sheet/group/group[2]/label[@for='date_planned']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<xpath expr="//sheet/group/group[2]/div[@name='date_planned_div']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
</field>
</record>
@@ -174,6 +232,27 @@
<xpath expr="//tree//header//button[@name='action_create_invoice']" position="attributes">
<attribute name="groups">sf_base.group_purchase,sf_base.group_purchase_director</attribute>
</xpath>
<xpath expr="//field[@name='user_id']" position="attributes">
<attribute name="string">采购员</attribute>
</xpath>
<xpath expr="//field[@name='activity_ids']" position="attributes">
<attribute name="optional">hide</attribute>
</xpath>
<xpath expr="//field[@name='date_order']" position="attributes">
<attribute name="string">签约日期</attribute>
<attribute name="widget">''</attribute>
</xpath>
<xpath expr="//field[@name='date_planned']" position="replace">
</xpath>
<xpath expr="//field[@name='date_order']" position="after">
<field name="date_planned" string="最近交货日期"/>
</xpath>
<xpath expr="//field[@name='name']" position="after">
<field name="purchase_type"/>
</xpath>
<xpath expr="//field[@name='name']" position="attributes">
<attribute name="class">purchase_order_list_name</attribute>
</xpath>
</field>
</record>
@@ -190,6 +269,8 @@
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}
</attribute>
</field>
<xpath expr="//field[@name='picking_type_id'][1]" position="replace">
</xpath>
</field>
</record>
@@ -200,7 +281,43 @@
<field name="arch" type="xml">
<tree position="attributes">
<attribute name="default_order">date_approve asc</attribute>
<attribute name="decoration-warning">delivery_warning == 'warning'</attribute>
<attribute name="decoration-danger">delivery_warning == 'overdue'</attribute>
</tree>
<xpath expr="//field[@name='activity_ids']" position="attributes">
<attribute name="optional">hide</attribute>
</xpath>
<xpath expr="//field[@name='date_planned']" position="replace">
</xpath>
<xpath expr="//field[@name='user_id']" position="attributes">
<attribute name="string">采购员</attribute>
</xpath>
<xpath expr="//field[@name='user_id']" position="after">
<field name="delivery_warning" optional="show"/>
<field name="date_planned" string="最近交货日期" optional="show" widget="date"/>
</xpath>
<xpath expr="//field[@name='name']" position="after">
<field name="purchase_type"/>
<field name="delivery_warning" invisible="1"/>
</xpath>
<xpath expr="//field[@name='name']" position="attributes">
<attribute name="class">purchase_order_list_name</attribute>
</xpath>
</field>
</record>
<record id="view_purchase_order_view_search_inherit_sf" model="ir.ui.view">
<field name="name">purchase.order.search.inherit.sf</field>
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.purchase_order_view_search"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='order_date']" position="after">
<separator/>
<filter string="正常" name="filter_order_normal"
domain="['|', ('delivery_warning', '=', 'normal'), ('delivery_warning', '=', False)]"/>
<filter string="预警" name="filter_order_warning" domain="[('delivery_warning', '=', 'warning')]"/>
<filter string="逾期" name="filter_order_overdue" domain="[('delivery_warning', '=', 'overdue')]"/>
</xpath>
</field>
</record>
@@ -212,8 +329,29 @@
<xpath expr="//field[@name='name']" position="replace">
<field name="name" string="单据编码" filter_domain="[('name', 'ilike', self)]"/>
</xpath>
<!-- <xpath expr="//search" position="inside">-->
<!-- <searchpanel>-->
<!-- <field name="purchase_type" icon="fa-filter"/>-->
<!-- <field name="state" icon="fa-filter"/>-->
<!-- </searchpanel>-->
<!-- </xpath>-->
</field>
</record>
<record id="purchase_order_view_search_sf" model="ir.ui.view">
<field name="name">purchase.order.list.select.sf</field>
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.purchase_order_view_search"/>
<field name="arch" type="xml">
<xpath expr="//search" position="inside">
<searchpanel>
<field name="purchase_type" icon="fa-filter" enable_counters="1"/>
<field name="delivery_warning" icon="fa-filter" enable_counters="1"/>
</searchpanel>
</xpath>
</field>
</record>
<record id="purchase.product_normal_action_puchased" model="ir.actions.act_window">
<field name="context">
{"search_default_categ_id":1,"search_default_filter_to_purchase":1, "purchase_product_template": 1}

View File

@@ -103,6 +103,7 @@
<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'])]}"/>
<field name="part_name" optional="hide"/>
</xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='price_subtotal']" position="after">
<field name="remark"/>
@@ -169,17 +170,42 @@
<field name="manual_quotation" />
<field name="is_incoming_material"/>
</xpath>
<xpath expr="//div[@class='o_td_label'][2]" position="replace"></xpath>
<xpath expr="//field[@name='date_order'][1]" position="replace"></xpath>
<xpath expr="//field[@name='date_order']" position="replace"></xpath>
<xpath expr="//div[@class='o_td_label'][1]" position="replace">
<div class="o_td_label" attrs="{'invisible': [('state', 'in', ['done', 'cancel'])]}">
<label for="date_order" string="下单日期"/>
</div>
<field name="date_order" attrs="{'invisible': [('state', 'in', ['done', 'cancel'])], 'required': True}" nolabel="1"/>
</xpath>
</field>
</record>
<record id="sale_order_inherited_form_purchase_sf" model="ir.ui.view">
<field name="name">sale.order.inherited.form.purchase</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale_purchase.sale_order_inherited_form_purchase"/>
<field name="arch" type="xml">
<xpath expr="//button[@name='action_view_purchase_orders']" position="before">
<button class="oe_stat_button" name="action_view_consignment_purchase_orders" type="object" icon="fa-credit-card"
groups='purchase.group_purchase_user'
attrs="{'invisible': [('consignment_purchase_order_count', '=', 0)]}">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value"><field name="consignment_purchase_order_count"/></span>
<span class="o_stat_text">委外加工</span>
</div>
</button>
</xpath>
</field>
</record>
<record model="ir.ui.view" id="view_sale_management_order_form_quote_inherit_sf">
<field name="name">sale.management.order.form.quote.inherit.sf</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale_management.sale_order_form_quote"/>
<field name="arch" type="xml">
<field name="date_order" position="attributes">
<attribute name="string">下单日期</attribute>
</field>
<field name="sale_order_template_id" position="after">
<field name="remark" attrs="{'readonly': [('state', 'in', ('sale','cancel'))]}"/>
</field>
@@ -215,8 +241,15 @@
<attribute name="string">订单号</attribute>
</field>
<field name="create_date" position="attributes">
<attribute name="string">下单时间</attribute>
<attribute name="string">下单日期</attribute>
</field>
<xpath expr="//field[@name='activity_ids']" position="attributes">
<attribute name="optional">hide</attribute>
</xpath>
<xpath expr="//field[@name='create_date']" position="after">
<field name="order_code"/>
<field name="deadline_of_delivery"/>
</xpath>
<!-- <field name="state" position="after">
<field name="check_status" widget="badge"
decoration-success="check_status == 'approved'"
@@ -236,6 +269,10 @@
<separator/>
<filter string="预警" name="filter_order_warning" domain="[('delivery_warning', '=', 'warning')]"/>
<filter string="逾期" name="filter_order_overdue" domain="[('delivery_warning', '=', 'overdue')]"/>
<searchpanel>
<field name="delivery_warning" string="交期状态" icon="fa-filter" enable_counters="1"/>
<field name="state" icon="fa-filter" enable_counters="1"/>
</searchpanel>
</xpath>
</field>
</record>
@@ -258,6 +295,22 @@
<field name="amount_total" position="after">
<field name="delivery_warning" invisible="1"/>
</field>
<field name="date_order" position="attributes">
<attribute name="string">下单日期</attribute>
</field>
<xpath expr="//field[@name='date_order']" position="after">
<field name="order_code"/>
<field name="deadline_of_delivery"/>
</xpath>
<xpath expr="//field[@name='activity_ids']" position="attributes">
<attribute name="optional">hide</attribute>
</xpath>
<xpath expr="//field[@name='invoice_status']" position="after">
<field name="state"/>
</xpath>
<xpath expr="//field[@name='user_id']" position="after">
<field name="delivery_warning" string="交期状态"/>
</xpath>
</field>
</record>
@@ -293,7 +346,7 @@
<record id="sale.action_orders" model="ir.actions.act_window">
<field name="search_view_id" ref="sale_order_view_search_inherit_sale_message"/>
<field name="context">{ 'search_default_filter_order_warning':1,'search_default_filter_order_overdue':1}
<field name="context">{}
</field>
</record>
</data>

View File

@@ -45,7 +45,8 @@ class FunctionalCuttingToolEntity(models.Model):
string='状态', store=True, default='正常')
current_location_id = fields.Many2one('stock.location', string='当前位置', compute='_compute_current_location_id',
store=True)
current_shelf_location_id = fields.Many2one('sf.shelf.location', string='当前货位', readonly=True)
current_shelf_location_id = fields.Many2one('sf.shelf.location', string='当前货位',
compute='_compute_current_location_id', store=True)
current_location = fields.Selection(
[('组装后', '组装后'), ('刀具房', '刀具房'), ('线边刀库', '线边刀库'), ('机内刀库', '机内刀库')],
string='位置', compute='_compute_current_location_id', store=True)
@@ -85,6 +86,10 @@ class FunctionalCuttingToolEntity(models.Model):
if quant_id.inventory_quantity_auto_apply > 0:
record.current_location_id = quant_id.location_id
if quant_id.location_id.name == '制造前':
shelf_location_id = self.env['sf.shelf.location'].sudo().search([
('product_sn_id', '=', record.barcode_id.id)])
if shelf_location_id:
record.current_shelf_location_id = shelf_location_id.id
if not record.current_shelf_location_id:
record.current_location = '机内刀库'
else:

View File

@@ -126,7 +126,7 @@ class StockLot(models.Model):
tool_material_search_id = fields.Many2one('sf.tool.material.search', string='刀具物料搜索')
fixture_material_search_id = fields.Many2one('sf.fixture.material.search', string='夹具物料搜索')
tool_material_status = fields.Selection(
[('未入库', '未入库'), ('可用', '可用'), ('在用', '在用'), ('报废', '报废')], string='状态',
[('未入库', '未入库'), ('可用', '可用'), ('在用', '空闲'), ('报废', '报废')], string='状态',
compute='_compute_tool_material_status', store=True)
@api.depends('quant_ids')

View File

@@ -198,8 +198,8 @@ class MrpProduction(models.Model):
logging.info('cnc用刀校验到无效刀自动调用重新编程方法update_programming_state()')
self[0].update_programming_state()
self[0].write({'is_rework': False})
# 修改制造订单 编程状态变为“编程中” 制造订单状态为‘返工’
self.write({'programming_state': '编程中', 'work_state': '编程中', 'state': 'rework'})
# 修改制造订单 编程状态变为“编程中”
self.write({'programming_state': '编程中', 'work_state': '编程中'})
self[0].workorder_ids.filtered(
lambda a: a.name == '装夹预调' and a.state not in ['rework', 'done', 'cancel'])._compute_state()
if missing_tool_1:

View File

@@ -23,7 +23,7 @@
<t t-if="record.workorder_count.raw_value &gt; 0">
<button class="btn btn-primary" name="action_work_order" type="object"
attrs="{'invisible': [('name', '=', '功能刀具组装中心')]}"
context="{'search_default_ready': 1, 'search_default_progress': 1}">
context="{'search_default_ready': 1, 'search_default_progress': 1, 'workorder_type': name}">
<span>工单</span>
</button>
</t>

View File

@@ -888,12 +888,11 @@ class SfStockMoveLine(models.Model):
def _check_destination_location_id(self):
for item in self:
if item:
i = 0
barcode = item.destination_location_id.barcode
for line in item.picking_id.move_line_ids_without_package:
if barcode and barcode == line.destination_location_id.barcode:
i += 1
if i > 1:
if line.destination_location_id:
if (barcode and barcode == line.destination_location_id.barcode
and item.product_id != line.product_id):
raise ValidationError(
'%s】货位已经被占用,请重新选择!!!' % item.destination_location_id.barcode)