Compare commits

...

175 Commits

Author SHA1 Message Date
yuxianghui
60560fe195 1、优化工单解绑Rfid 2024-06-19 17:40:49 +08:00
yuxianghui
21f2a704db 1、对刀仪接口获取的功能刀具半径值乘二后再赋值 2024-06-19 17:03:44 +08:00
yuxianghui
c0b673bfd0 添加注释 2024-06-19 11:23:25 +08:00
yuxianghui
6dae144d76 1、功能刀具拆解单优化;2、完成 需求坯料序列号生成规则优化2; 2024-06-19 11:19:47 +08:00
yuxianghui
4497fb04c1 1、优化 托盘绑定工单逻辑优化 ;2 完成 整体式刀具批次/序列号生成的优化需求; 3、完成 坯料序列号生成规则优化需求1。 2024-06-18 17:19:41 +08:00
马广威
903fdee420 Accept Merge Request #1081: (release/release1.6 -> master)
Merge Request: 功能升级

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1081?initial=true
2024-06-17 21:34:41 +08:00
禹翔辉
ee17889fe8 Accept Merge Request #1080: (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/1080?initial=true
2024-06-17 17:27:27 +08:00
yuxianghui
5bec3d6061 1 2024-06-17 17:24:23 +08:00
杨金灵
0fc5b12894 Accept Merge Request #1079: (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/1079?initial=true
2024-06-17 16:39:55 +08:00
jinling.yang
3258b6cc75 修复快速订单 2024-06-17 16:38:14 +08:00
jinling.yang
017404fe45 Merge branch 'feature/优化工单结束接口' into develop 2024-06-17 16:17:54 +08:00
杨金灵
cde6c76f83 Accept Merge Request #1078: (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/1078?initial=true
2024-06-17 16:16:39 +08:00
jinling.yang
7dea8c7c1e 去掉返工和报废的代码 2024-06-17 16:16:22 +08:00
jinling.yang
8ade3b43c4 中控新增是否完成字段,该接口根据该字段对cnc加工工单进行完成操作 2024-06-17 16:06:49 +08:00
禹翔辉
c2b7abaae1 Accept Merge Request #1077: (hotfix/功能刀具出入库处理 -> 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/1077
2024-06-17 14:40:14 +08:00
禹翔辉
c1f33c5e14 Merge branch refs/heads/develop into refs/heads/hotfix/功能刀具出入库处理 2024-06-17 14:36:00 +08:00
杨金灵
01f55350e2 Accept Merge Request #1076: (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/1076?initial=true
2024-06-17 11:56:45 +08:00
jinling.yang
9202409a89 去掉返工报废的代码 2024-06-17 11:53:38 +08:00
jinling.yang
2e779c4a68 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-17 11:50:25 +08:00
禹翔辉
5751837ddd Accept Merge Request #1075: (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/1075?initial=true
2024-06-17 11:46:22 +08:00
yuxianghui
e2f86097a0 Merge branch 'feature/模型优化' into feature/刀具同步接口优化 2024-06-17 11:42:25 +08:00
yuxianghui
77ad0dd635 优化动态刀具按批次管理的数据同步接口 2024-06-17 11:40:12 +08:00
jinling.yang
73e03bd815 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-17 11:00:33 +08:00
jinling.yang
51cd156e75 Merge branch 'feature/去掉客户部分字段必填校验和返工报废代码' into develop 2024-06-17 11:00:02 +08:00
杨金灵
b447db1b13 Accept Merge Request #1074: (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/1074?initial=true
2024-06-17 10:50:27 +08:00
jinling.yang
e3911c8f5a Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-17 09:52:43 +08:00
jinling.yang
2892cdd99f Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/去掉客户部分字段必填校验和返工报废代码 2024-06-17 09:52:30 +08:00
jinling.yang
2862c5a888 去掉客户部分字段必填校验和返工报废代码 2024-06-17 09:52:18 +08:00
禹翔辉
ad8927fb83 Accept Merge Request #1073: (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/1073?initial=true
2024-06-17 09:44:52 +08:00
yuxianghui
e1f4e1e701 Merge branch 'feature/刀具接口调整' into feature/模型优化 2024-06-17 09:43:00 +08:00
yuxianghui
1e90b9a18a 1、功能刀具位置计算方法添加触发条件 2024-06-14 17:01:02 +08:00
jinling.yang
24edef023f Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-14 16:53:26 +08:00
马广威
4894576d25 Accept Merge Request #1072: (release/release_1.5 -> 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/1072
2024-06-14 16:21:38 +08:00
yuxianghui
904c63bfcb 1、优化功能刀具安全库存数量计算方法;2、货位看板模型添加功能刀具Rfid、名称字段 2024-06-14 15:38:52 +08:00
yuxianghui
a86ceb951b 1、优化功能刀具安全库存数量计算方法;2、货位看板模型添加功能刀具Rfid、名称字段 2024-06-14 15:00:30 +08:00
jinling.yang
dd9015dff9 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-14 09:31:18 +08:00
yuxianghui
642877ff45 去除整体式刀产品的【粗/中/精】字段的必填 2024-06-13 17:28:44 +08:00
yuxianghui
aa0b6a9b21 1、新增功能刀具移动到线边刀库的出入库记录 2024-06-13 16:01:27 +08:00
yuxianghui
9c6ca3758b 1 2024-06-13 14:59:49 +08:00
yuxianghui
42644d449f 1、新增功能刀具移动历史,新增功能刀具入库到线边刀库 2024-06-13 14:45:29 +08:00
马广威
519c534a89 Accept Merge Request #1071: (release/release_1.5 -> master)
Merge Request: 添加刀具物料整个生命周期按批次管理

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1071?initial=true
2024-06-13 10:49:36 +08:00
马广威
741c5cf3d6 Accept Merge Request #1070: (feature/增加批量调拨功能 -> develop)
Merge Request: 修改线边库位名称

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1070?initial=true
2024-06-12 16:54:00 +08:00
yuxianghui
0ad7e72b15 1、解决 刀具组装选择的批次号队友物料只剩下1个时-组装完该单据详情不显示批次信息 的bug;2、调整拆解单界面布局 2024-06-12 16:28:42 +08:00
mgw
fa7e023dd1 修改线边库位名称 2024-06-12 16:19:33 +08:00
yuxianghui
07c7ed6dba 1、优化组装时重复选取货位时,批次号消失的问题 2024-06-12 14:20:15 +08:00
yuxianghui
0f32b0acb1 1、新增功能刀具出入库 2024-06-12 14:08:21 +08:00
yuxianghui
42cded8c64 1、优化功能刀具序列号 2024-06-12 10:24:16 +08:00
yuxianghui
469839f38e 1、处理二维码问题 2024-06-12 10:02:15 +08:00
jinling.yang
6a7fa89b67 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-06-11 17:31:59 +08:00
jinling.yang
c13078291f Merge branch 'feature/工件配送添加sns' into develop 2024-06-11 17:25:23 +08:00
杨金灵
a3580411d6 Accept Merge Request #1069: (feature/工件配送添加sns -> develop)
Merge Request: 工件配送及工单添加sns

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1069
2024-06-11 17:23:45 +08:00
yuxianghui
7d7f0348d2 1、优化产品按批量采购入库时,没有生成二维码问题; 2024-06-11 17:00:25 +08:00
jinling.yang
d7779cdb58 修改生成制造订单时未生成编程单 2024-06-11 16:57:56 +08:00
jinling.yang
adafce85bd 1.工单,工件配送新增sns及给部分字段添加tracking(跟踪)及优化页面
2.优化agv:部分字段不可编辑
2024-06-11 16:56:20 +08:00
yuxianghui
bae947bdca 1、添加采购入库时对需要录入的Rfid进行重复校验; 2024-06-11 15:18:13 +08:00
yuxianghui
14d823ab6e 1、增加是否使用刀具预调仪的配置; 2024-06-11 11:58:58 +08:00
yuxianghui
ae953c7ad6 1、更新功能刀具编码生成规则; 2024-06-11 10:15:54 +08:00
jinling.yang
7be5b53767 工件配送添加sns 2024-06-07 17:29:49 +08:00
yuxianghui
2cc91eea75 1、对模型的添加筛选和分组,优化界面 2024-06-07 17:27:25 +08:00
yuxianghui
2065625cd6 1、组装单添加校验 2024-06-07 09:42:09 +08:00
马广威
8c285c0eb5 Accept Merge Request #1068: (feature/增加批量调拨功能 -> develop)
Merge Request: 库存处增加批量调拨功能

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1068
2024-06-06 15:45:51 +08:00
mgw
22e24c5ed3 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/增加批量调拨功能 2024-06-06 15:44:38 +08:00
mgw
4459ed2e81 库存处增加批量调拨功能 2024-06-06 15:44:20 +08:00
禹翔辉
52bf899a1f Accept Merge Request #1067: (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/1067?initial=true
2024-06-06 14:41:30 +08:00
yuxianghui
002727070d 1、产品的成品、制造订单、工单添加零件图号字段; 2024-06-06 14:38:10 +08:00
杨金灵
81327f04de Accept Merge Request #1065: (hotfix/优化多次编程单下发 -> 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/1065
2024-06-06 09:40:06 +08:00
杨金灵
e8c86c6306 Accept Merge Request #1066: (hotfix/刀具预调仪 -> 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/1066
2024-06-06 09:37:10 +08:00
杨金灵
fd6b579e2a Merge branch refs/heads/develop into refs/heads/hotfix/刀具预调仪 2024-06-06 09:30:50 +08:00
杨金灵
0b2b162d85 Merge branch refs/heads/develop into refs/heads/hotfix/优化多次编程单下发 2024-06-06 09:29:39 +08:00
yuxianghui
206fc7bb21 Merge branch 'hotfix/新增刀具按批次管理' into hotfix/新增刀具按批次管理_1.1
# Conflicts:
#	sf_tool_management/controllers/controllers.py
2024-06-06 09:17:32 +08:00
马广威
07b1c3cceb Accept Merge Request #1063: (release/release_1.4 -> master)
Merge Request: 流程优化,功能增加

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1063?initial=true
2024-06-05 21:43:21 +08:00
jinling.yang
f9c697d2a7 Merge branch 'hotfix/优化多次编程单下发' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into release/release_1.4 2024-06-05 18:04:10 +08:00
jinling.yang
2eaec4fa58 修复编程单下发时,删除容器内tmp目录下模型编码已有的文件 2024-06-05 18:03:08 +08:00
黄焱
e306d02ff2 Accept Merge Request #1062: (feature/前端样式修改 -> develop)
Merge Request: 设置表单页面label文本不换行

Created By: @黄焱
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @黄焱
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1062
2024-06-05 17:42:54 +08:00
黄焱
09c56b2c07 设置表单页面label文本不换行 2024-06-05 17:35:07 +08:00
jinling.yang
8a1a06c3ad Merge branch 'hotfix/刀具预调仪' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into release/release_1.4 2024-06-05 17:28:33 +08:00
yuxianghui
f4926820fe 1、清除刀具物料的自动调用同步方法; 2024-06-05 17:18:18 +08:00
jinling.yang
2055d70e27 Merge branch 'hotfix/优化多次编程单下发' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into release/release_1.4 2024-06-05 17:15:00 +08:00
jinling.yang
5361fa7678 修复create_cam_work_plan方法 2024-06-05 17:14:20 +08:00
jinling.yang
edf8e1004e 修改条件 2024-06-05 17:00:17 +08:00
jinling.yang
17e2a3ffc3 cnc程序的程序单更新 2024-06-05 16:26:34 +08:00
jinling.yang
7a539d7916 Merge branch 'hotfix/刀具预调仪' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into release/release_1.4 2024-06-05 16:11:24 +08:00
jinling.yang
e258e43b51 优化多次编程单下发 2024-06-05 16:09:56 +08:00
杨金灵
1af160542c Accept Merge Request #1061: (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/1061
2024-06-05 15:53:56 +08:00
jinling.yang
71246af16a 还原注释代码 2024-06-05 15:52:50 +08:00
yuxianghui
d2f4e5495e 1、功能刀具拆解单优化,添加对功能刀具的当前位置校验,只有在刀具房的刀具才能进行拆解;2、中控机床刀库接口优化,清除刀位已经移除的功能刀具信息; 2024-06-05 15:20:00 +08:00
yuxianghui
ba2b833aa8 1 2024-06-05 11:00:21 +08:00
yuxianghui
cf0afd6c64 1、添加刀具预调仪功能 2024-06-05 10:54:58 +08:00
yuxianghui
b6acbb8357 1、功能刀具拆解单新增拆解单号,优化拆解单 2024-06-05 10:23:13 +08:00
杨金灵
e290760895 Accept Merge Request #1060: (hotfix/修复工件上下产线 -> 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/1060
2024-06-05 10:13:02 +08:00
杨金灵
fdfd01605d Merge branch refs/heads/develop into refs/heads/hotfix/修复工件上下产线 2024-06-05 09:30:30 +08:00
马广威
19799aefe4 Accept Merge Request #1059: (release/release_1.3 -> master)
Merge Request: 优化工件上下产线及配送、优化编程单下发

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1059?initial=true
2024-06-04 22:28:57 +08:00
yuxianghui
d47dcc7611 1、取消创建产品时,自动生成内部参考的值;2、优化功能刀具拆解单,修改由拆解单生成的移动历史单据编码生成规则;3、优化货位看板以及货位看板的货位变更功能; 2024-06-04 17:25:45 +08:00
jinling.yang
f33d7b79fc 优化工件上下产线 2024-06-04 16:13:42 +08:00
jinling.yang
8bd5841d7a 去掉重写state方法 2024-06-04 15:59:35 +08:00
yuxianghui
3882d3a3cb 1、功能刀具拆解单拆解流程优化;2、优化货位看板显示; 2024-06-04 11:26:00 +08:00
jinling.yang
e9ab57854e 修改生成外协出入库单时,查询条件排序修改为根据create_date或id排序 2024-06-04 10:48:50 +08:00
jinling.yang
6fc94042ee 中控调取cnc工单时,工单不完成,加工完成时间为当前时间 2024-06-04 10:42:34 +08:00
jinling.yang
b70ec36365 修改生成外协出入库单时,查询条件排序修改为根据id排序 2024-06-04 10:33:40 +08:00
yuxianghui
67c4f64d08 1、优化刀具组装单、刀具组装单弹窗、刀具拆解单,添加除刀柄外刀具物料按批次号进行管理;优化界面布局,优化组装流程; 2024-06-03 17:34:47 +08:00
jinling.yang
3738b8d72b 中控请求接口超时改为10秒 2024-06-03 17:21:42 +08:00
jinling.yang
5fb9326d8b 中控请求接口超时改为30秒 2024-06-03 17:09:35 +08:00
jinling.yang
2ab9f37062 1.修复工件配送2.优化运送空料架.3.cloud下发编程单至sf时,将程序单pdf同步至制造订单的装夹预调工单的加工图纸上4.修复工件上下产线接口 2024-06-03 16:59:14 +08:00
jinling.yang
711e0d1437 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化制造订单报废流程 2024-06-03 16:09:08 +08:00
杨金灵
49d55822cc Accept Merge Request #1058: (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/1058
2024-06-03 15:53:19 +08:00
jinling.yang
ea6de0c248 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化制造订单报废流程
# Conflicts:
#	sf_manufacturing/models/mrp_production.py
2024-06-03 15:43:57 +08:00
jinling.yang
2db81ac2cc Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化工件上下产线及配送 2024-06-03 15:36:37 +08:00
jinling.yang
a673630fd3 1.优化工件配送超时提示2.优化工件上下线接口3.优化编程单下发至cnc工单时,将程序单pdf文件也传给装夹预调工单的加工图纸字段上 2024-06-03 15:36:19 +08:00
yuxianghui
a7102c81d4 1、新增货位批次数量模型,用来存储货位产品的批次及其数量;完成该模型根据货位产品的出入库记录自动新增或减少货位产品批次的数量。 2024-06-03 14:14:04 +08:00
yuxianghui
d213a2cf54 接口调整 2024-05-31 11:14:02 +08:00
禹翔辉
6c5b888bba Accept Merge Request #1057: (hotfix/新增中控接口-功能刀具查询 -> 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/1057
2024-05-31 09:52:45 +08:00
yuxianghui
7d6fb4f0e3 1、优化货位 2024-05-30 17:26:28 +08:00
jinling.yang
aa6b476e35 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-05-30 16:53:13 +08:00
jinling.yang
263253e79e Merge branch 'feature/修复报价选择已有产品确认问题' into develop 2024-05-30 16:52:42 +08:00
杨金灵
edda681940 Accept Merge Request #1054: (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/1054?initial=true
2024-05-30 16:52:27 +08:00
jinling.yang
7043d2b8b7 添加日志 2024-05-30 16:52:06 +08:00
马广威
6d66872dee Accept Merge Request #1055: (feature/修复会计凭证借贷无法输入的问题 -> develop)
Merge Request: 修复会计凭证借贷无法输入的问题

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1055?initial=true
2024-05-30 16:48:56 +08:00
mgw
3ab378f3bd 修复会计凭证借贷无法输入的问题 2024-05-30 16:48:02 +08:00
jinling.yang
26c979ef1d 修复报价选择已有产品确认问题 2024-05-30 16:45:32 +08:00
yuxianghui
1d4aaa40cd 1、优化刀具采购入库,入库时自动生成批次号;2、优化货位看板 2024-05-30 14:46:15 +08:00
jinling.yang
9aa4858774 Merge branch 'feature/空料架名称去重校验' into develop 2024-05-30 11:05:09 +08:00
jinling.yang
d883a7ff22 新增名称校验 2024-05-30 11:01:29 +08:00
yuxianghui
683e9631fa 刀具物料产品优化需求 2024-05-30 09:36:17 +08:00
jinling.yang
17678c53c9 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/空料架名称去重校验 2024-05-29 17:56:55 +08:00
jinling.yang
fb3cc095b2 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-05-29 17:56:36 +08:00
jinling.yang
c437c185a4 空料架名称去重校验 2024-05-29 17:56:22 +08:00
jinling.yang
b3eb3ee8fa Merge branch 'feature/优化配送空料架' into develop 2024-05-29 15:53:59 +08:00
杨金灵
b9ec821293 Accept Merge Request #1052: (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/1052
2024-05-29 15:53:30 +08:00
jinling.yang
f8a9fcc0ce 工件配送去掉重复的起点接驳站 2024-05-29 15:26:47 +08:00
jinling.yang
d12714dd65 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化配送空料架 2024-05-29 15:23:32 +08:00
jinling.yang
a1bea537bb Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-05-29 15:23:12 +08:00
jinling.yang
5350675355 释放occ注释代码 2024-05-29 15:22:57 +08:00
jinling.yang
65d2593db9 配送空料架开放新建 2024-05-29 15:03:08 +08:00
禹翔辉
a529c5513e Accept Merge Request #1051: (hotfix/制造订单的工单顺序错误bug -> develop)
Merge Request: 修复 【制造订单的工单顺序错误,需排查处理bug】 问题

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1051
2024-05-29 14:37:58 +08:00
禹翔辉
f5f881f256 Merge branch refs/heads/develop into refs/heads/hotfix/制造订单的工单顺序错误bug 2024-05-29 14:22:15 +08:00
jinling.yang
d2babec1aa Merge tag '修复编程单下发接口' into develop 2024-05-29 12:50:05 +08:00
jinling.yang
a1d8b88db2 修复工单排序 2024-05-29 10:25:23 +08:00
jinling.yang
7ab954ebbc Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化制造订单报废流程 2024-05-29 09:30:41 +08:00
jinling.yang
f7723f1b9c Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-05-29 09:30:20 +08:00
jinling.yang
fe88a416a7 制造订单合并一张编程单下发时,工件配送记录的未下发nc程序为未下发及cnc程序没有对应ftp文件路径问题 2024-05-28 17:57:27 +08:00
禹翔辉
ef80848e9b Accept Merge Request #1048: (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/1048?initial=true
2024-05-28 16:39:52 +08:00
yuxianghui
138dd40a9e Merge branch 'feature/组装单弹窗优化' into feature/优化工单工序 2024-05-28 16:37:51 +08:00
yuxianghui
dc210f7263 1 2024-05-28 16:36:49 +08:00
yuxianghui
9055e7ed8b 1、优化生成工单工序的方法 2024-05-28 16:35:06 +08:00
黄焱
6890ddf615 Accept Merge Request #1047: (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/1047
2024-05-27 17:53:23 +08:00
黄焱
6dc29fb50a Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/前端样式修改 2024-05-27 17:48:06 +08:00
jinling.yang
412bf4b9be 优化返工和报废 2024-05-24 17:00:44 +08:00
黄焱
c7df18ace6 设置表格横向滚动 2024-05-24 14:35:22 +08:00
马广威
053eba5887 Accept Merge Request #1046: (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/1046#mr-1046-review-152801
2024-05-23 16:25:16 +08:00
yuxianghui
e935b2fa11 1、优化功能刀具定时同步接口,增加刀柄长度数据同步 2024-05-23 16:18:48 +08:00
禹翔辉
d5bc610214 Accept Merge Request #1045: (feature/组装单弹窗优化 -> develop)
Merge Request: 1、将刀具名称字段获取由onchange改为depends

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1045
2024-05-23 15:46:34 +08:00
yuxianghui
2ca67eb8ad 1、将刀具名称字段获取由onchange改为depends 2024-05-23 15:43:32 +08:00
禹翔辉
b6a69debd8 Accept Merge Request #1044: (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/1044?initial=true
2024-05-23 15:24:38 +08:00
yuxianghui
147ac35239 Merge branch 'feature/组装单优化' into feature/组装单弹窗优化 2024-05-23 15:22:35 +08:00
yuxianghui
3c3f375120 1、打开组装弹窗方法添加清除旧数据功能;2、组装弹窗界面,刷新按键名称改为获取测量值,添加是否获取测量值字段等,调整字段只读和界面布局; 2024-05-23 15:19:41 +08:00
jinling.yang
c87f310bb3 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-05-23 14:34:39 +08:00
禹翔辉
f53eecbb57 Accept Merge Request #1043: (feature/组装单优化 -> develop)
Merge Request: 1、组装单弹窗界面调整,优化按刷新后最大寿命值归0的问题;

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1043?initial=true
2024-05-23 14:33:51 +08:00
jinling.yang
1743fa45b5 Merge branch 'feature/优化返工和重新制造订单' into develop 2024-05-23 14:33:09 +08:00
yuxianghui
75f30922e6 1、组装单弹窗界面调整,优化按刷新后最大寿命值归0的问题; 2024-05-23 14:32:13 +08:00
jinling.yang
3a0db05c33 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化返工和重新制造订单 2024-05-23 11:46:26 +08:00
jinling.yang
5f42b5d056 优化制造订单报废流程,返工流程暂停(需求需重新设计) 2024-05-23 11:46:09 +08:00
禹翔辉
c698b50853 Accept Merge Request #1041: (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/1041?initial=true
2024-05-23 11:18:40 +08:00
yuxianghui
251786f6be Merge branch 'feature/刀具预调仪接口优化' into feature/组装单优化 2024-05-23 11:16:13 +08:00
yuxianghui
66b4df5193 1、优化刀具预调仪接口的数据处理方式、更改搜索组装单条件;2、优化功能刀具、组装单、组装单弹窗的字段和界面展示的字段内容;3、更改组装单编码生成规则 2024-05-23 11:15:13 +08:00
jinling.yang
57b264b84d Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化返工和重新制造订单 2024-05-22 17:36:17 +08:00
jinling.yang
ff12a86406 优化cnc工单返工:在原有的cnc工单后新增返工标志的cnc工单,且工序累加,状态为就绪,解除装夹工单的状态为等待其他工单 2024-05-22 17:35:57 +08:00
禹翔辉
b0b3ce7d99 Accept Merge Request #1040: (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/1040?initial=true
2024-05-22 15:09:22 +08:00
yuxianghui
8d02a94f78 Merge branch 'feature/物料同步接口优化' into feature/刀具预调仪接口优化 2024-05-22 15:05:55 +08:00
yuxianghui
4f6e4a8ed3 1、优化刀具预调仪接口及其写入到弹窗的流程,更改刀具总长度(高度)、直径和R角字段的精确度为小数点后三位; 2024-05-22 15:04:37 +08:00
黄焱
b524e385a1 Accept Merge Request #1039: (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/1039
2024-05-22 09:39:24 +08:00
yuxianghui
d22f08d187 1、添加刀具预调仪接口;2、刀具组装单添加开始预调字段,优化组装按键;3、优化组装单组装时的必填校验,添加刷新按钮用来获取刀具预调仪的参数信息; 2024-05-21 21:41:17 +08:00
黄焱
2ba53bbb02 // 功能刀具组装单 弹窗样式 2024-05-21 17:38:54 +08:00
禹翔辉
38e0a62130 Accept Merge Request #1038: (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/1038?initial=true
2024-05-21 09:27:33 +08:00
yuxianghui
bf1b14290e Merge branch 'feature/解决刀具组装时搜不到功能刀具产品问题' into feature/物料同步接口优化 2024-05-21 09:25:13 +08:00
yuxianghui
1b6ca90bd4 1、优化刀具、夹具同步接口;关闭刀具同步接口的实时调用; 2024-05-21 09:23:47 +08:00
53 changed files with 2265 additions and 1209 deletions

View File

@@ -503,4 +503,27 @@ div:has(.o_required_modifier) > label::before {
color: #fff;
background-color: #4A4F59;
border-color: #4A4F59;
}
// 功能刀具组装单 弹窗样式
.o_horizontal_separator.mt-4.mb-3.text-uppercase.fw-bolder.small ~ div.col-lg-6 .o_inner_group.col-lg-6 {
width: 100%;
}
.o_horizontal_separator.mt-4.mb-3.text-uppercase.fw-bolder.small ~ div .o_inner_group .o_wrap_field.d-flex.d-sm-contents.flex-column{
display: flex!important;
flex-direction: row!important;
input {
border-bottom: 1px solid;
}
}
// 设置表格横向滚动
.o_list_renderer.o_renderer {
max-width: 100%;
overflow-x: auto;
}
// 设置表单页面label文本不换行
.o_form_view .o_group .o_wrap_label .o_form_label {
white-space: nowrap;
}

View File

@@ -27,7 +27,7 @@ class MrpWorkcenter(models.Model):
class MrpProductionWorkcenterLine(models.Model):
_name = 'mrp.workorder'
_inherit = ['mrp.workorder', 'barcodes.barcode_events_mixin']
_inherit = ['mrp.workorder', 'barcodes.barcode_events_mixin', 'mail.thread', 'mail.activity.mixin']
quality_point_ids = fields.Many2many('quality.point', compute='_compute_quality_point_ids', store=True)
quality_point_count = fields.Integer('Steps', compute='_compute_quality_point_count')
@@ -47,14 +47,17 @@ class MrpProductionWorkcenterLine(models.Model):
is_last_lot = fields.Boolean('Is Last lot', compute='_compute_is_last_lot')
is_first_started_wo = fields.Boolean('Is The first Work Order', compute='_compute_is_last_unfinished_wo')
is_last_unfinished_wo = fields.Boolean('Is Last Work Order To Process', compute='_compute_is_last_unfinished_wo', store=False)
is_last_unfinished_wo = fields.Boolean('Is Last Work Order To Process', compute='_compute_is_last_unfinished_wo',
store=False)
lot_id = fields.Many2one(related='current_quality_check_id.lot_id', readonly=False)
move_id = fields.Many2one(related='current_quality_check_id.move_id', readonly=False)
move_line_id = fields.Many2one(related='current_quality_check_id.move_line_id', readonly=False)
move_line_ids = fields.One2many(related='move_id.move_line_ids')
quality_state = fields.Selection(related='current_quality_check_id.quality_state', string="Quality State", readonly=False)
quality_state = fields.Selection(related='current_quality_check_id.quality_state', string="Quality State",
readonly=False)
qty_done = fields.Float(related='current_quality_check_id.qty_done', readonly=False)
test_type_id = fields.Many2one('quality.point.test_type', 'Test Type', related='current_quality_check_id.test_type_id')
test_type_id = fields.Many2one('quality.point.test_type', 'Test Type',
related='current_quality_check_id.test_type_id')
test_type = fields.Char(related='test_type_id.technical_name')
user_id = fields.Many2one(related='current_quality_check_id.user_id', readonly=False)
worksheet_page = fields.Integer('Worksheet page')
@@ -65,7 +68,8 @@ class MrpProductionWorkcenterLine(models.Model):
def _compute_quality_point_ids(self):
for workorder in self:
quality_points = workorder.operation_id.quality_point_ids
quality_points = quality_points.filtered(lambda qp: not qp.product_ids or workorder.production_id.product_id in qp.product_ids)
quality_points = quality_points.filtered(
lambda qp: not qp.product_ids or workorder.production_id.product_id in qp.product_ids)
workorder.quality_point_ids = quality_points
@api.depends('operation_id')
@@ -91,7 +95,8 @@ class MrpProductionWorkcenterLine(models.Model):
@api.depends('check_ids')
def _compute_finished_product_check_ids(self):
for wo in self:
wo.finished_product_check_ids = wo.check_ids.filtered(lambda c: c.finished_product_sequence == wo.qty_produced)
wo.finished_product_check_ids = wo.check_ids.filtered(
lambda c: c.finished_product_sequence == wo.qty_produced)
def write(self, values):
res = super().write(values)
@@ -138,7 +143,8 @@ class MrpProductionWorkcenterLine(models.Model):
self.finished_lot_id = self.env['stock.lot'].create({
'product_id': self.product_id.id,
'company_id': self.company_id.id,
'name': self.env['stock.lot']._get_next_serial(self.company_id, self.product_id) or self.env['ir.sequence'].next_by_code('stock.lot.serial'),
'name': self.env['stock.lot']._get_next_serial(self.company_id, self.product_id) or self.env[
'ir.sequence'].next_by_code('stock.lot.serial'),
})
def _create_subsequent_checks(self):
@@ -152,7 +158,7 @@ class MrpProductionWorkcenterLine(models.Model):
"""
# Create another quality check if necessary
next_check = self.current_quality_check_id.next_check_id
if next_check.component_id != self.current_quality_check_id.product_id or\
if next_check.component_id != self.current_quality_check_id.product_id or \
next_check.point_id != self.current_quality_check_id.point_id:
# TODO: manage reservation here
@@ -279,7 +285,8 @@ class MrpProductionWorkcenterLine(models.Model):
if self.current_quality_check_id:
team = self.current_quality_check_id.team_id
else:
team = self.env['quality.alert.team'].search(['|', ('company_id', '=', self.company_id.id), ('company_id', '=', False)], limit=1)
team = self.env['quality.alert.team'].search(
['|', ('company_id', '=', self.company_id.id), ('company_id', '=', False)], limit=1)
return {
'type': 'ir.actions.act_window',
'res_model': 'quality.check',
@@ -320,7 +327,8 @@ class MrpProductionWorkcenterLine(models.Model):
production = wo.production_id
move_raw_ids = wo.move_raw_ids.filtered(lambda m: m.state not in ('done', 'cancel'))
move_finished_ids = wo.move_finished_ids.filtered(lambda m: m.state not in ('done', 'cancel') and m.product_id != wo.production_id.product_id)
move_finished_ids = wo.move_finished_ids.filtered(
lambda m: m.state not in ('done', 'cancel') and m.product_id != wo.production_id.product_id)
previous_check = self.env['quality.check']
for point in wo.quality_point_ids:
# Check if we need a quality control for this point
@@ -342,11 +350,13 @@ class MrpProductionWorkcenterLine(models.Model):
if point.test_type == 'register_byproducts':
moves = move_finished_ids.filtered(lambda m: m.product_id == point.component_id)
if not moves:
moves = production.move_finished_ids.filtered(lambda m: not m.operation_id and m.product_id == point.component_id)
moves = production.move_finished_ids.filtered(
lambda m: not m.operation_id and m.product_id == point.component_id)
elif point.test_type == 'register_consumed_materials':
moves = move_raw_ids.filtered(lambda m: m.product_id == point.component_id)
if not moves:
moves = production.move_raw_ids.filtered(lambda m: not m.operation_id and m.product_id == point.component_id)
moves = production.move_raw_ids.filtered(
lambda m: not m.operation_id and m.product_id == point.component_id)
else:
check = self.env['quality.check'].create(values)
previous_check.next_check_id = check
@@ -363,8 +373,10 @@ class MrpProductionWorkcenterLine(models.Model):
processed_move |= moves
# Generate quality checks associated with unreferenced components
moves_without_check = ((move_raw_ids | move_finished_ids) - processed_move).filtered(lambda move: (move.has_tracking != 'none' and not move.raw_material_production_id.use_auto_consume_components_lots) or move.operation_id)
quality_team_id = self.env['quality.alert.team'].search(['|', ('company_id', '=', wo.company_id.id), ('company_id', '=', False)], limit=1).id
moves_without_check = ((move_raw_ids | move_finished_ids) - processed_move).filtered(lambda move: (
move.has_tracking != 'none' and not move.raw_material_production_id.use_auto_consume_components_lots) or move.operation_id)
quality_team_id = self.env['quality.alert.team'].search(
['|', ('company_id', '=', wo.company_id.id), ('company_id', '=', False)], limit=1).id
for move in moves_without_check:
values = {
'production_id': production.id,
@@ -412,7 +424,8 @@ class MrpProductionWorkcenterLine(models.Model):
backorder = False
# Trigger the backorder process if we produce less than expected
if float_compare(self.qty_producing, self.qty_remaining, precision_rounding=self.product_uom_id.rounding) == -1 and self.is_first_started_wo:
if float_compare(self.qty_producing, self.qty_remaining,
precision_rounding=self.product_uom_id.rounding) == -1 and self.is_first_started_wo:
backorder = self.production_id._split_productions()[1:]
for workorder in backorder.workorder_ids:
if workorder.product_tracking == 'serial':
@@ -423,7 +436,8 @@ class MrpProductionWorkcenterLine(models.Model):
else:
if self.operation_id:
backorder = (self.production_id.procurement_group_id.mrp_production_ids - self.production_id).filtered(
lambda p: p.workorder_ids.filtered(lambda wo: wo.operation_id == self.operation_id).state not in ('cancel', 'done')
lambda p: p.workorder_ids.filtered(lambda wo: wo.operation_id == self.operation_id).state not in (
'cancel', 'done')
)[:1]
else:
index = list(self.production_id.workorder_ids).index(self)
@@ -442,7 +456,8 @@ class MrpProductionWorkcenterLine(models.Model):
wo.current_quality_check_id._update_component_quantity()
if not self.env.context.get('no_start_next'):
if self.operation_id:
return backorder.workorder_ids.filtered(lambda wo: wo.operation_id == self.operation_id).open_tablet_view()
return backorder.workorder_ids.filtered(
lambda wo: wo.operation_id == self.operation_id).open_tablet_view()
else:
index = list(self.production_id.workorder_ids).index(self)
return backorder.workorder_ids[index].open_tablet_view()
@@ -466,7 +481,8 @@ class MrpProductionWorkcenterLine(models.Model):
def open_tablet_view(self):
self.ensure_one()
if not self.is_user_working and self.working_state != 'blocked' and self.state in ('ready', 'waiting', 'progress', 'pending'):
if not self.is_user_working and self.working_state != 'blocked' and self.state in (
'ready', 'waiting', 'progress', 'pending'):
self.button_start()
action = self.env["ir.actions.actions"]._for_xml_id("mrp_workorder.tablet_client_action")
action['target'] = 'fullscreen'
@@ -521,7 +537,8 @@ class MrpProductionWorkcenterLine(models.Model):
data = {
'mrp.workorder': self.read(self._get_fields_for_tablet(), load=False)[0],
'quality.check': self.check_ids._get_fields_for_tablet(sorted_check_list),
'operation': self.operation_id.read(self.operation_id._get_fields_for_tablet())[0] if self.operation_id else {},
'operation': self.operation_id.read(self.operation_id._get_fields_for_tablet())[
0] if self.operation_id else {},
'working_state': self.workcenter_id.working_state,
'views': {
'workorder': self.env.ref('mrp_workorder.mrp_workorder_view_form_tablet').id,
@@ -553,7 +570,8 @@ class MrpProductionWorkcenterLine(models.Model):
return {
'duration': self.duration,
'position': bisect_left(last30op, self.duration), # which position regarded other workorders ranked by duration
'position': bisect_left(last30op, self.duration),
# which position regarded other workorders ranked by duration
'quality_score': score,
'show_rainbow': show_rainbow,
}

View File

@@ -27,6 +27,13 @@
</field>
</page>
</page>
<xpath expr="//sheet" position="after">
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="activity_ids"/>
<field name="message_ids" options="{'post_refresh': 'recipients'}"/>
</div>
</xpath>
</field>
</record>

View File

@@ -1,7 +1,7 @@
.o_data_row .w-100 {
width: 40px !important;
height: 40px !important;
display: block !important;
//display: block !important;
}
.o_list_renderer .o_list_table tbody > tr > td:not(.o_list_record_selector):not(.o_handle_cell):not(.o_list_button):not(.o_list_record_remove) {

View File

@@ -67,9 +67,10 @@ class JdEclp(models.Model):
"""
判断是否为出库单
"""
if self.name:
is_check_out = self.name.split('/')
self.check_out = is_check_out[1]
for record in self:
if record.name:
is_check_out = record.name.split('/')
record.check_out = is_check_out[1]
@api.depends('carrier_tracking_ref')
def query_bill_pdf(self):

View File

@@ -10,6 +10,7 @@
<field name='categ_id' class="custom_required" attrs="{'readonly': [('id', '!=', False)]}"/>
<field name='is_bfm' invisible="1"/>
<field name='categ_type' invisible="1"/>
<field name='part_number' attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
<field name='manual_quotation' attrs="{'invisible':[('upload_model_file', '=', [])]}"/>
<field name="upload_model_file"
widget="many2many_binary"
@@ -31,9 +32,11 @@
options="{'no_create': True}"
attrs="{'invisible': [('categ_type', '!=', '刀具')],'required': [('categ_type', '=', '刀具')],'readonly': [('id', '!=', False)]}"
placeholder="请选择"/>
<field name="brand_id" options="{'no_create': True}" placeholder="请选择"
attrs="{'invisible': [('categ_type', '!=', '刀具')],'required': [('categ_type', '=', '刀具')],'readonly': [('id', '!=', False)]}"/>
<field name="cutting_tool_model_id" placeholder="请选择" class="custom_required"
options="{'no_create': True}"
domain="[('cutting_tool_material_id','=',cutting_tool_material_id)]"
domain="[('cutting_tool_material_id','=',cutting_tool_material_id),('brand_id', '=', brand_id)]"
context="{'default_cutting_tool_material_id': cutting_tool_material_id}"
attrs="{'invisible': [('categ_type', '!=', '刀具')],'required': [('categ_type', '=', '刀具')],'readonly': [('id', '!=', False)]}">
</field>
@@ -47,10 +50,10 @@
<field name="fixture_material_id"
attrs="{'invisible': [('categ_type', '!=', '夹具')],'required': [('categ_type', '=', '夹具')]}"
placeholder="请选择" options="{'no_create': True}"/>
<field name="fixture_model_id" string="型号" placeholder="请选择" options="{'no_create': True}"
<field name="fixture_model_id" string="型号名称" placeholder="请选择" options="{'no_create': True}"
attrs="{'invisible': [('categ_type', '!=', '夹具')],'required': [('categ_type', '=', '夹具')]}"
domain="[('fixture_material_id','=',fixture_material_id)]"/>
<field name="specification_fixture_id" string="规格" placeholder="请选择"
<field name="specification_fixture_id" string="物料号" placeholder="请选择"
options="{'no_create': True}"
attrs="{'invisible': [('categ_type', '!=', '夹具')],'required': [('categ_type', '=', '夹具')]}"
domain="[('fixture_model_id','=',fixture_model_id)]"/>
@@ -95,6 +98,16 @@
</group>
</page>
</xpath>
<xpath expr="//field[@name='name']" position="attributes">
<attribute name="attrs">{'readonly': ['|',('id','!=',False),('categ_type', '=',
'刀具')], 'required': True}
</attribute>
</xpath>
<!-- <xpath expr="//field[@name='default_code']" position="attributes">-->
<!-- <attribute name="attrs">{'readonly': [('categ_type', '=', '刀具')], 'invisible':-->
<!-- [('product_variant_count', '>' , 1)]}-->
<!-- </attribute>-->
<!-- </xpath>-->
</field>
</record>
@@ -292,7 +305,7 @@
<field name="cutting_tool_blade_type"
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')],'readonly': [('id', '!=', False)]}"/>
<field name="cutting_tool_coarse_medium_fine" string="粗/中/精" placeholder="请选择"
attrs="{'required': [('cutting_tool_type','=','整体式刀具')],'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀片'))],'readonly': [('id', '!=', False)]}"/>
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀片'))],'readonly': [('id', '!=', False)]}"/>
<!--整体式刀具-->
<field name="cutting_tool_shank_diameter" string="柄部直径(mm)" class="diameter"
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')],'readonly': [('id', '!=', False)]}"/>
@@ -455,7 +468,8 @@
<tree editable="bottom">
<!-- <field name="cutting_speed"-->
<!-- attrs="{'readonly': [('materials_type_id','!=',False)]}"/>-->
<field name="materials_type_id" options="{'no_create': True}" placeholder="请选择"/>
<field name="materials_type_id" options="{'no_create': True}"
placeholder="请选择"/>
<field name="blade_diameter"/>
<field name="feed_per_tooth"/>
</tree>

View File

@@ -25,6 +25,9 @@
<filter string="人工编程" name="manual_quotation" domain="[('manual_quotation', '=', True)]"/>
<filter string="自动编程" name="no_manual_quotation" domain="[('manual_quotation', '=', False)]"/>
</xpath>
<xpath expr="//field[@name='production_id']" position="before">
<field name="product_tmpl_name"/>
</xpath>
</field>
</record>
</odoo>

View File

@@ -8,7 +8,7 @@ from odoo.http import request
class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/GetWoInfo', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/GetWoInfo', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
def get_Work_Info(self, **kw):
"""
@@ -166,11 +166,13 @@ class Manufacturing_Connect(http.Controller):
if not workorder:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '该工单不存在'}
return json.JSONEncoder().encode(res)
logging.info('workorder_state:%s' % workorder.state)
if workorder.state != 'ready':
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '工单未就绪'}
return json.JSONEncoder().encode(res)
work_equipment_id = request.env['maintenance.equipment'].sudo().search([('name', '=', equipment_id)],
limit=1)
logging.info('work_equipment_id:%s' % work_equipment_id.name)
if not work_equipment_id:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '没有找到该加工设备'}
return json.JSONEncoder().encode(res)
@@ -213,7 +215,9 @@ class Manufacturing_Connect(http.Controller):
if workorder.state != 'progress':
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '该工单未开始'}
return json.JSONEncoder().encode(res)
workorder.button_finish()
# workorder.write({'date_finished': datetime.now()})
if ret['IsComplete'] is True:
workorder.button_finish()
# workorder.process_state = '待解除装夹'
# workorder.sudo().production_id.process_state = '待解除装夹'
@@ -457,20 +461,21 @@ class Manufacturing_Connect(http.Controller):
rfid_code = ret[f'RfidCode{i}']
logging.info('RfidCode:%s' % rfid_code)
domain = [
('feeder_station_destination_id.name', '=', ret['DeviceId']),
('workorder_id.rfid_code', '=', rfid_code),
('status', '=', '已配送'),
('type', '=', '上产线')
('rfid_code', '=', rfid_code),
('routing_type', '=', 'CNC加工')
]
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(domain,
order='id asc')
if workpiece_delivery:
for wd in workpiece_delivery:
logging.info('wd.production_id:%s' % wd.production_id.name)
if wd.workorder_id.state == 'done' and wd.production_id.production_line_state == '待上产线':
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
if workorder:
for order in workorder:
if order.production_id.production_line_state == '待上产线':
logging.info(
'wd.production_line_state:%s' % wd.production_id.production_line_state)
wd.production_id.write({'production_line_state': '已上产线'})
'制造订单产线状态:%s' % order.production_id.production_line_state)
order.production_id.write({'production_line_state': '已上产线'})
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search([
('rfid_code', '=', rfid_code), ('type', '=', '上产线'),
('production_id', '=', order.production_id.id)])
if workpiece_delivery.status == '待下发':
workpiece_delivery.write({'is_manual_work': True})
else:
res = {'Succeed': False, 'ErrorCode': 204,
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}
@@ -511,20 +516,20 @@ class Manufacturing_Connect(http.Controller):
rfid_code = ret[f'RfidCode{i}']
logging.info('RfidCode:%s' % rfid_code)
domain = [
('workorder_id.rfid_code', '=', rfid_code),
('status', '=', '待下发'),
('type', '=', '下产线')
('rfid_code', '=', rfid_code),
('routing_type', '=', 'CNC加工')
]
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(domain,
order='id asc')
if workpiece_delivery:
for wd in workpiece_delivery:
logging.info('wd.production_id:%s' % wd.production_id.name)
if wd.workorder_id.state == 'done' and wd.production_id.production_line_state == '已上产线':
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
if workorder:
for order in workorder:
if order.production_id.production_line_state == '已上产线':
logging.info(
'wd.production_line_state:%s' % wd.production_id.production_line_state)
wd.production_id.write({'production_line_state': '已下产线'})
delivery_Arr.append(wd.id)
'制造订单产线状态:%s' % order.production_id.production_line_state)
order.production_id.write({'production_line_state': '已下产线'})
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search([
('rfid_code', '=', rfid_code), ('type', '=', '下产线'),
('production_id', '=', order.production_id.id)])
delivery_Arr.append(workpiece_delivery.id)
else:
res = {'Succeed': False, 'ErrorCode': 204,
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}
@@ -534,10 +539,14 @@ class Manufacturing_Connect(http.Controller):
[('id', 'in', delivery_Arr)])
if delivery_workpiece:
logging.info('开始向agv下发下产线任务')
is_free = delivery_workpiece._check_avgsite_state()
if is_free is True:
delivery_workpiece._delivery_avg()
logging.info('agv下发下产线任务下发完成')
agv_site = request.env['sf.agv.site'].sudo().search([])
if agv_site:
has_site = agv_site.update_site_state()
if has_site is True:
is_free = delivery_workpiece._check_avgsite_state()
if is_free is True:
delivery_workpiece._delivery_avg()
logging.info('agv下发下产线任务下发完成')
else:
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '未传IsComplete字段'}
except Exception as e:

View File

@@ -2,7 +2,8 @@
import requests
import logging
import time
from odoo import fields, models
from odoo import fields, models, api
from odoo.exceptions import UserError
class AgvSetting(models.Model):
@@ -26,20 +27,28 @@ class AgvSetting(models.Model):
timestamp = int(time.time())
center_control_url += str(timestamp)
logging.info('工件配送-请求中控地址:%s' % center_control_url)
center_control_r = requests.get(center_control_url, params={}, headers=headers)
ret = center_control_r.json()
logging.info('工件配送-请求中控站点信息:%s' % ret)
self.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/GetAgvStationState?date=%s' % str(timestamp)})
if ret['Succeed'] is True:
datas = ret['Datas']
for item in self:
for da in datas:
if da['DeviceId'] == item.name:
if da['AtHome'] is True:
item.state = '占用'
else:
item.state = '空闲'
try:
center_control_r = requests.get(center_control_url, headers=headers, timeout=10) # 设置超时为60秒
ret = center_control_r.json()
logging.info('工件配送-请求中控站点信息:%s' % ret)
self.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/GetAgvStationState?date=%s' % str(timestamp)})
if ret['Succeed'] is True:
datas = ret['Datas']
for item in self:
for da in datas:
if da['DeviceId'] == item.name:
if da['AtHome'] is True:
item.state = '占用'
else:
item.state = '空闲'
return True
except requests.exceptions.Timeout:
logging.error('工件配送-请求中控接口超时')
return False
except requests.exceptions.RequestException as e:
logging.error('工件配送-请求中控接口错误: %s', e)
return False
class AgvTaskRoute(models.Model):
@@ -51,11 +60,17 @@ class AgvTaskRoute(models.Model):
('F01', '搬运'), ], string='任务类型', default="F01")
route_type = fields.Selection([
('上产线', '上产线'), ('下产线', '下产线'), ('运送空料架', '运送空料架')], string='类型')
start_site_id = fields.Many2one('sf.agv.site', '起点接驳站位置编号')
end_site_id = fields.Many2one('sf.agv.site', '终点接驳站位置编号')
start_site_id = fields.Many2one('sf.agv.site', '起点接驳站')
end_site_id = fields.Many2one('sf.agv.site', '终点接驳站')
destination_production_line_id = fields.Many2one('sf.production.line', '目的生产线')
active = fields.Boolean('有效', default=True)
@api.constrains('end_site_id')
def _check_end_site_id(self):
if self.end_site_id:
if self.end_site_id == self.start_site_id:
raise UserError("您选择的终点接驳站与起点接驳站重复,请重新选择")
class Center_controlInterfaceLog(models.Model):
_name = 'center_control.interface.log'

View File

@@ -5,7 +5,7 @@ import re
import requests
from itertools import groupby
from odoo import api, fields, models, _
from odoo.exceptions import UserError,ValidationError
from odoo.exceptions import UserError, ValidationError
from odoo.addons.sf_base.commons.common import Common
from odoo.tools import float_compare, float_round, float_is_zero, format_datetime
@@ -53,13 +53,13 @@ class MrpProduction(models.Model):
active = fields.Boolean(string='已归档', default=True)
programming_no = fields.Char('编程单号')
work_state = fields.Char('业务状态')
programming_state = fields.Char('编程状态')
programming_state = fields.Char('编程状态', tracking=True)
glb_file = fields.Binary("glb模型文件")
production_line_id = fields.Many2one('sf.production.line', string='生产线')
production_line_id = fields.Many2one('sf.production.line', string='生产线', tracking=True)
plan_start_processing_time = fields.Datetime('计划开始加工时间')
production_line_state = fields.Selection(
[('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')],
string='上/下产线', default='待上产线')
string='上/下产线', default='待上产线', tracking=True)
# 工序状态
# Todo 研究下用法
process_state = fields.Selection([
@@ -77,6 +77,7 @@ class MrpProduction(models.Model):
part_drawing = fields.Binary('零件图纸')
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
rework_production = fields.Many2one('mrp.production', string='返工的制造订单')
@api.depends(
'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state',
@@ -154,6 +155,28 @@ class MrpProduction(models.Model):
for production in self:
production.maintenance_count = len(production.request_ids)
# 制造订单报废:编程单更新
def updateCNC(self):
try:
res = {'production_no': self.name, 'programming_no': self.programming_no,
'order_no': self.origin}
logging.info('res=%s:' % res)
configsettings = self.env['res.config.settings'].get_values()
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
url = '/api/intelligent_programming/update_intelligent_programmings'
config_url = configsettings['sf_url'] + url
res['token'] = configsettings['token']
ret = requests.post(config_url, json={}, data=res, headers=config_header)
ret = ret.json()
logging.info('updateCNC-ret:%s' % ret)
if ret['status'] == 1:
self.write({'work_state': '已编程'})
else:
raise UserError(ret['message'])
except Exception as e:
logging.info('updateCNC error:%s' % e)
raise UserError("更新程单失败,请联系管理员")
# cnc程序获取
def fetchCNC(self, production_names):
cnc = self.env['mrp.production'].search([('id', '=', self.id)])
@@ -171,6 +194,7 @@ class MrpProduction(models.Model):
'production_no': production_names,
'machine_tool_code': '',
'product_name': cnc.product_id.name,
'remanufacture_type': '',
'model_code': cnc.product_id.model_code,
'material_code': self.env['sf.production.materials'].search(
[('id', '=', cnc.product_id.materials_id.id)]).materials_no,
@@ -316,6 +340,7 @@ class MrpProduction(models.Model):
else:
production.write({'programming_no': production_programming.programming_no,
'programming_state': '编程中'})
# # 根据加工面板的面数及对应的工序模板生成工单
i = 0
processing_panel_len = len(production.product_id.model_processing_panel.split(','))
@@ -406,20 +431,15 @@ class MrpProduction(models.Model):
# 工单排序
def _reset_work_order_sequence1(self, k):
sequen = 0
for rec in self:
current_sequence = 10
cnc_workorder = rec.workorder_ids.filtered(lambda wo: wo.name == "CNC加工")
cnc_back_workorder = rec.workorder_ids.filtered(lambda wo: wo.name == "CNC加工(返工)")
for work in rec.workorder_ids:
work.sequence = current_sequence
current_sequence += 10
if work.name == '后置三元质量检测' and work.processing_panel == k:
sequen = work.sequence
for work in rec.workorder_ids:
if work.name == '后置三元质量检测(返工)' and work.processing_panel == k:
work.sequence = sequen + 2
if work.name == 'CNC加工(返工)' and work.processing_panel == k:
work.sequence = sequen + 1
if work.name == cnc_workorder.name and work.processing_panel == k:
cnc_back_workorder.write({'sequence': work.sequence + 1})
print(cnc_back_workorder.sequence)
elif work.routing_type not in ['装夹预调'] and work != cnc_back_workorder:
work.sequence += 1
# 在制造订单上新增工单
def _create_workorder1(self, k):
@@ -459,13 +479,7 @@ class MrpProduction(models.Model):
order='sequence asc'
)
i += 1
for route in routingworkcenter:
# if route.routing_type == '后置三元质量检测':
# workorders_values.append(
# self.env['mrp.workorder'].json_workorder_str1(k, production, route)
# )
if route.routing_type == 'CNC加工':
workorders_values.append(
self.env['mrp.workorder'].json_workorder_str1(k, production, route))

View File

@@ -21,6 +21,8 @@ class ResMrpWorkOrder(models.Model):
_inherit = 'mrp.workorder'
_order = 'sequence asc,create_date desc'
product_tmpl_name = fields.Char('坯料产品名称', related='production_bom_id.bom_line_ids.product_id.name')
product_tmpl_id_length = fields.Float(related='production_id.product_tmpl_id.length', readonly=True, store=True,
string="坯料长度(mm)")
product_tmpl_id_width = fields.Float(related='production_id.product_tmpl_id.width', readonly=True, store=True,
@@ -102,7 +104,7 @@ class ResMrpWorkOrder(models.Model):
Z10_axis = fields.Float(default=0)
X_deviation_angle = fields.Integer(string="X轴偏差度", default=0)
test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")], default='合格',
string="检测结果")
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程序")
tray_code = fields.Char(string="托盘编码")
@@ -133,7 +135,7 @@ class ResMrpWorkOrder(models.Model):
return action
supplier_id = fields.Many2one('res.partner', string='外协供应商')
equipment_id = fields.Many2one('maintenance.equipment', string='加工设备')
equipment_id = fields.Many2one('maintenance.equipment', string='加工设备', tracking=True)
is_ok = fields.Boolean(string='是否合格')
# 加工人
processing_user_id = fields.Many2one('res.users', string='加工人')
@@ -159,7 +161,7 @@ class ResMrpWorkOrder(models.Model):
('已完工', '已完工'),
], string='工序状态', default='待装夹', readonly='True')
# 加工图纸
processing_drawing = fields.Binary(string='加工图纸', related='production_id.part_drawing')
processing_drawing = fields.Binary(string='加工图纸')
@api.depends('production_id')
def _compute_save_name(self):
@@ -195,11 +197,16 @@ class ResMrpWorkOrder(models.Model):
rfid_code_old = fields.Char('RFID码(已解除)')
production_line_id = fields.Many2one('sf.production.line', related='production_id.production_line_id',
string='生产线', store=True)
string='生产线', store=True, tracking=True)
production_line_state = fields.Selection(related='production_id.production_line_state',
string='上/下产线', store=True)
string='上/下产线', store=True, tracking=True)
detection_report = fields.Binary('检测报告', readonly=True)
is_remanufacture = fields.Boolean(string='是否重新生成制造订单', default=True)
is_remanufacture = fields.Boolean(string='重新生成制造订单', default=False)
is_fetchcnc = fields.Boolean(string='重新获取NC程序', default=False)
reason = fields.Selection(
[("programming", "编程"), ("clamping", "返工"), ("cutter", "刀具"), ("operate computer", "操机"),
("technology", "工艺"), ("customer redrawing", "客户改图"), ("other", "其他"), ], string="原因", tracking=True)
detailed_reason = fields.Text('详细原因')
@api.onchange('rfid_code')
def _onchange(self):
@@ -672,18 +679,64 @@ class ResMrpWorkOrder(models.Model):
"""
重新生成制造订单或者重新生成工单
"""
if self.test_results == '报废':
if self.test_results in ['返工', '报废']:
values = self.env['mrp.production'].create_production1_values(self.production_id)
productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(
self.production_id.company_id).create(
values)
self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
# self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
productions._create_workorder()
productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \
(
p.move_dest_ids.procure_method != 'make_to_order' and
not p.move_raw_ids and not p.workorder_ids)).action_confirm()
for production_item in productions:
process_parameter_workorder = self.env['mrp.workorder'].search(
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', production_item.id),
('is_subcontract', '=', True)])
if process_parameter_workorder:
is_pick = False
consecutive_workorders = []
m = 0
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.id)
for i in range(len(sorted_workorders) - 1):
if m == 0:
is_pick = False
if sorted_workorders[i].supplier_id.id == sorted_workorders[i + 1].supplier_id.id and \
sorted_workorders[i].is_subcontract == sorted_workorders[i + 1].is_subcontract and \
sorted_workorders[i].id == sorted_workorders[i + 1].id - 1:
if sorted_workorders[i] not in consecutive_workorders:
consecutive_workorders.append(sorted_workorders[i])
consecutive_workorders.append(sorted_workorders[i + 1])
m += 1
continue
else:
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
production_item)
if sorted_workorders[i] in consecutive_workorders:
is_pick = True
consecutive_workorders = []
m = 0
# 当前面的连续工序生成对应的外协出入库单再生成当前工序的外协出入库单
if is_pick is False:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i],
production_item)
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
production_item)
if sorted_workorders[i] in consecutive_workorders:
is_pick = True
consecutive_workorders = []
m = 0
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production_item)
if is_pick is False and m == 0:
if len(sorted_workorders) == 1:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production_item)
else:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production_item)
for production in productions:
origin_production = production.move_dest_ids and production.move_dest_ids[
@@ -704,13 +757,36 @@ class ResMrpWorkOrder(models.Model):
'mail.message_origin_link',
values={'self': production, 'origin': origin_production},
subtype_id=self.env.ref('mail.mt_note').id)
if self.test_results == '返工':
productions = self.production_id
# self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
# self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
productions._create_workorder2(self.processing_panel)
else:
self.results = '合格'
'''
创建生产计划
'''
# 工单耗时
workorder_duration = 0
for workorder in productions.workorder_ids:
workorder_duration += workorder.duration_expected
sale_order = self.env['sale.order'].sudo().search([('name', '=', productions.origin)])
if sale_order:
sale_order.mrp_production_ids |= productions
# sale_order.write({'schedule_status': 'to schedule'})
self.env['sf.production.plan'].sudo().with_company(self.production_id.company_id).create({
'name': productions.name,
'order_deadline': sale_order.deadline_of_delivery,
'production_id': productions.id,
'date_planned_start': productions.date_planned_start,
'origin': productions.origin,
'product_qty': productions.product_qty,
'product_id': productions.product_id.id,
'state': 'draft',
})
# if self.test_results == '返工':
# productions = self.production_id
# # self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
# # self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
# productions._create_workorder2(self.processing_panel)
# else:
# self.results = '合格'
def json_workorder_str1(self, k, production, route):
workorders_values_str = [0, '', {
@@ -720,7 +796,6 @@ class ResMrpWorkOrder(models.Model):
'name': '%s(返工)' % route.route_workcenter_id.name,
'processing_panel': k,
'routing_type': route.routing_type,
'work_state': '' if not route.routing_type == '获取CNC加工程序' else '待发起',
'workcenter_id': self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids,
route.routing_type,
production.product_id),
@@ -728,10 +803,37 @@ class ResMrpWorkOrder(models.Model):
'date_planned_finished': datetime.now() + timedelta(days=1),
'duration_expected': 60,
'duration': 0,
'manual_quotation': production.workorder_ids.filtered(
lambda t: t.routing_type == 'CNC加工').manual_quotation,
'rfid_code': production.workorder_ids.filtered(lambda t: t.routing_type == 'CNC加工').rfid_code,
'cnc_ids': production.workorder_ids.filtered(lambda t: t.routing_type == 'CNC加工').cnc_ids,
'cmm_ids': production.workorder_ids.filtered(lambda t: t.routing_type == 'CNC加工').cmm_ids,
}]
return workorders_values_str
# @api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state')
# def _compute_state(self):
# super(ResMrpWorkOrder, self)._compute_state()
# for item in self:
# print(item.name)
# print(item.state)
# print(item.is_remanufacture)
# scrap_workorder = self.env['mrp.workorder'].search(
# [('production_id', '=', item.production_id.id), ('routing_type', '=', 'CNC加工'),
# ('state', '=', 'done'), ('test_results', 'in', ['返工', '报废'])])
# print(scrap_workorder)
# # if item.routing_type == 'CNC加工' and item.state in ['done'] and item.test_results in ['返工', '报废']:
# if item.routing_type == '解除装夹':
# if scrap_workorder and item.state not in ['cancel']:
# item.state = 'cancel'
# elif item.routing_type == '表面工艺':
# if scrap_workorder:
# stock_move = self.env['stock.move'].search(
# [('origin', '=', item.production_id.name)])
# stock_move.write({'state': 'cancel'})
# item.picking_ids.write({'state': 'cancel'})
# item.state = 'cancel'
# 重写工单开始按钮方法
def button_start(self):
if self.routing_type == '装夹预调':
@@ -749,12 +851,12 @@ class ResMrpWorkOrder(models.Model):
limit=1, order='id asc')
if not cnc_workorder.cnc_ids:
raise UserError(_('该制造订单还未下发CNC程序请稍后再试'))
else:
for item in cnc_workorder.cnc_ids:
functional_cutting_tool = self.env['sf.functional.cutting.tool.entity'].search(
[('tool_name_id.name', '=', item.cutting_tool_name)])
if not functional_cutting_tool:
raise UserError(_('该制造订单的CNC程序为%s没有对应的功能刀具' % item.cutting_tool_name))
# else:
# for item in cnc_workorder.cnc_ids:
# functional_cutting_tool = self.env['sf.functional.cutting.tool.entity'].search(
# [('tool_name_id.name', '=', item.cutting_tool_name)])
# if not functional_cutting_tool:
# raise UserError(_('该制造订单的CNC程序为%s没有对应的功能刀具' % item.cutting_tool_name))
if self.routing_type == '解除装夹':
'''
记录开始时间
@@ -887,6 +989,7 @@ class ResMrpWorkOrder(models.Model):
raise UserError(
'请先在产品中配置表面工艺为%s相关的外协服务产品' % item.surface_technics_parameters_id.name)
tem_date_planned_finished = record.date_planned_finished
tem_date_finished = record.date_finished
logging.info('routing_type:%s' % record.routing_type)
super().button_finish()
logging.info('date_planned_finished:%s' % record.date_planned_finished)
@@ -895,14 +998,28 @@ class ResMrpWorkOrder(models.Model):
record.write({
'date_planned_finished': tem_date_planned_finished # 保持原值
})
# if record.routing_type == 'CNC加工':
# record.write({
# 'date_finished': tem_date_finished # 保持原值
# })
# if record.routing_type == 'CNC加工' and record.test_results in ['返工', '报废']:
# record.production_id.action_cancel()
# record.production_id.workorder_ids.write({'rfid_code': False, 'rfid_code_old': record.rfid_code})
# if record.is_remanufacture is True:
# record.recreateManufacturingOrWorkerOrder()
is_production_id = True
for workorder in record.production_id.workorder_ids:
if workorder.state != 'done':
is_production_id = False
if is_production_id is True and record.routing_type in ['解除装夹', '表面工艺']:
if record.routing_type == '解除装夹':
for workorder in record.production_id.workorder_ids:
workorder.rfid_code_old = workorder.rfid_code
workorder.rfid_code = None
if workorder.processing_panel == record.processing_panel:
rfid_code = workorder.rfid_code
workorder.write({'rfid_code_old': rfid_code,
'rfid_code': ''})
workorder.rfid_code_old = rfid_code
workorder.rfid_code = ''
if is_production_id is True and record.routing_type in ['解除装夹', '表面工艺']:
for move_raw_id in record.production_id.move_raw_ids:
move_raw_id.quantity_done = move_raw_id.product_uom_qty
record.process_state = '已完工'
@@ -1035,14 +1152,14 @@ class CNCprocessing(models.Model):
# 根据程序名和加工面匹配到ftp里对应的Nc程序名,可优化为根据cnc_processing.program_path进行匹配
def get_cnc_processing_file(self, serverdir, cnc_processing, program_path):
logging.info('serverdir:%s' % serverdir)
logging.info('cnc_processing:%s' % cnc_processing)
for root, dirs, files in os.walk(serverdir):
for f in files:
logging.info('splitext(f):%s' % os.path.splitext(f)[1])
if os.path.splitext(f)[1] == ".pdf":
full_path = os.path.join(serverdir, root, f)
if full_path is not False:
if not cnc_processing.workorder_id.cnc_worksheet:
cnc_processing.workorder_id.cnc_worksheet = base64.b64encode(
open(full_path, 'rb').read())
cnc_processing.workorder_id.cnc_worksheet = base64.b64encode(
open(full_path, 'rb').read())
else:
if f in program_path:
# if cnc_processing.program_name == f.split('.')[0]:
@@ -1144,11 +1261,12 @@ class SfWorkOrderBarcodes(models.Model):
workorder.write(val)
self.write(val)
workorder_rfid = self.env['mrp.workorder'].search(
[('production_id', '=', workorder.production_id.id)])
[('production_id', '=', workorder.production_id.id),
('processing_panel', '=', workorder.processing_panel)])
if workorder_rfid:
for item in workorder_rfid:
item.write({'rfid_code': barcode})
logging.info("Rfid绑定成功")
logging.info("Rfid[%s]绑定成功!!!" % barcode)
else:
raise UserError('该Rfid【%s】绑定的是【%s】, 不是托盘!!!' % (barcode, lot.product_id.name))
self.process_state = '待检测'
@@ -1208,17 +1326,18 @@ class SfWorkOrderBarcodes(models.Model):
class WorkPieceDelivery(models.Model):
_name = "sf.workpiece.delivery"
_inherit = ['mail.thread', 'mail.activity.mixin']
_description = '工件配送'
name = fields.Char('单据编')
name = fields.Char('单据编')
workorder_id = fields.Many2one('mrp.workorder', string='工单', readonly=True)
workorder_state = fields.Selection(related='workorder_id.state', string='工单状态')
rfid_code = fields.Char(related='workorder_id.rfid_code', string='rfid码', store=True)
production_id = fields.Many2one('mrp.production', string='制造订单号', readonly=True)
production_line_id = fields.Many2one('sf.production.line', string='目的生产线')
production_line_id = fields.Many2one('sf.production.line', string='目的生产线', tracking=True)
plan_start_processing_time = fields.Datetime('计划开始加工时间', readonly=True)
route_id = fields.Many2one('sf.agv.task.route', '任务路线')
route_id = fields.Many2one('sf.agv.task.route', '任务路线', tracking=True)
feeder_station_start_id = fields.Many2one('sf.agv.site', '起点接驳站')
feeder_station_destination_id = fields.Many2one('sf.agv.site', '目的接驳站')
task_delivery_time = fields.Datetime('任务下发时间')
@@ -1227,17 +1346,42 @@ class WorkPieceDelivery(models.Model):
[('上产线', '上产线'), ('下产线', '下产线'), ('运送空料架', '运送空料架')], string='类型')
delivery_duration = fields.Float('配送时长', compute='_compute_delivery_duration')
status = fields.Selection(
[('待下发', '待下发'), ('待配送', '待配送'), ('已配送', '已配送')], string='状态', default='待下发')
is_cnc_program_down = fields.Boolean('程序是否下发', default=False)
[('待下发', '待下发'), ('待配送', '待配送'), ('已配送', '已配送')], string='状态', default='待下发',
tracking=True)
is_cnc_program_down = fields.Boolean('程序是否下发', default=False, tracking=True)
is_manual_work = fields.Boolean('人工操作', default=False)
active = fields.Boolean(string="有效", default=True)
@api.model
def create(self, vals):
if vals.get('name', '/') == '/' or vals.get('name', '/') is False:
vals['name'] = self.env['ir.sequence'].next_by_code('sf.workpiece.delivery') or '/'
if vals['route_id'] and vals.get('type') is None:
vals['type'] = '运送空料架'
else:
if vals.get('name', '/') == '/' or vals.get('name', '/') is False:
vals['name'] = self.env['ir.sequence'].next_by_code('sf.workpiece.delivery') or '/'
obj = super(WorkPieceDelivery, self).create(vals)
if obj.type == '运送空料架':
if obj.name is False:
obj.name = "运送空料架路线:%s-%s" % (
obj.feeder_station_start_id.name, obj.feeder_station_destination_id.name)
return obj
@api.constrains('route_id')
def _check_route_id(self):
if self.type == '运送空料架':
if self.route_id and self.name is False:
route = self.sudo().search(
[('route_id', '=', self.route_id.id), ('id', '!=', self.id), ('name', 'ilike', '运送空料架路线')])
if route:
raise UserError("该任务路线已存在,请重新选择")
# @api.constrains('name')
# def _check_name(self):
# if self.type == '运送空料架':
# wd = self.sudo().search([('name', '=', self.name), ('id', '!=', self.id)])
# if wd:
# raise UserError("该名称已存在")
def action_delivery_history(self):
return {
'name': _('配送历史'),
@@ -1287,6 +1431,8 @@ class WorkPieceDelivery(models.Model):
is_not_route += 1
# else:
# raise UserError('请选择【任务路线】再进行配送')
# if item.production_id.production_line_state == '已下产线' and item.state == '待下发' and item.type == '下产线':
# raise UserError('该制造订单已下产线,无需配送')
if production_type != item.type:
raise UserError('请选择类型为%s的制造订单进行配送' % production_type)
if down_status != item.status:
@@ -1340,22 +1486,25 @@ class WorkPieceDelivery(models.Model):
is_free = False
agv_site = self.env['sf.agv.site'].search([])
if agv_site:
agv_site.update_site_state()
for item in self:
logging.info('工件配送-起点状态:%s-%s' % (
item.feeder_station_start_id.name, item.feeder_station_start_id.state))
logging.info('工件配送-点状态:%s-%s' % (
item.feeder_station_destination_id.name, item.feeder_station_destination_id.state))
if item.type in ['上产线', '下产线']:
if (
item.feeder_station_start_id.state == '占用' and item.feeder_station_destination_id.state == '空闲') or (
item.feeder_station_start_id.state == '空闲' and item.feeder_station_destination_id.state == '空闲'):
is_free = True
has_site = agv_site.update_site_state()
if has_site is True:
for item in self:
if item.type in ['上产线', '下产线']:
logging.info('工件配送-点状态:%s-%s' % (
item.feeder_station_start_id.name, item.feeder_station_start_id.state))
logging.info('工件配送-终点状态:%s-%s' % (
item.feeder_station_destination_id.name, item.feeder_station_destination_id.state))
if (
item.feeder_station_start_id.state == '占用' and item.feeder_station_destination_id.state == '空闲') or (
item.feeder_station_start_id.state == '空闲' and item.feeder_station_destination_id.state == '空闲'):
is_free = True
else:
if item.feeder_station_destination_id.state == '空闲':
is_free = True
logging.info('is_free:%s' % is_free)
return is_free
else:
if item.feeder_station_destination_id.state == '空闲':
is_free = True
logging.info('is_free:%s' % is_free)
return is_free
raise UserError("接驳站暂未反馈站点实时状态,请稍后再试")
# 配送至avg小车
def _delivery_avg(self):

View File

@@ -50,8 +50,8 @@ class ResProductMo(models.Model):
cutting_tool_material_id = fields.Many2one('sf.cutting.tool.material', string='刀具物料')
cutting_tool_type = fields.Char(string="刀具物料类型", related='cutting_tool_material_id.name', store=True)
cutting_tool_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='型号')
specification_id = fields.Many2one('sf.tool.materials.basic.parameters', string='规格')
cutting_tool_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='型号名称')
specification_id = fields.Many2one('sf.tool.materials.basic.parameters', string='物料号')
cutting_tool_type_id = fields.Many2one('sf.cutting.tool.type', string='类型',
domain="[('cutting_tool_material_id.name', '=', cutting_tool_type)]")
@@ -104,6 +104,15 @@ class ResProductMo(models.Model):
compaction_way_id = fields.Many2one('maintenance.equipment.image',
'压紧方式', domain=[('type', '=', '压紧方式')])
name = fields.Char('产品名称', compute='_compute_tool_name', store=True, required=False)
@api.depends('cutting_tool_model_id', 'specification_id')
def _compute_tool_name(self):
for item in self:
if item.cutting_tool_model_id and item.specification_id:
name = '%s%s' % (item.cutting_tool_model_id.name, item.specification_id.name)
item.name = name
@api.onchange('cutting_tool_model_id')
def _onchange_cutting_tool_model_id(self):
for item in self:
@@ -530,6 +539,7 @@ class ResProductMo(models.Model):
# bfm下单
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
part_number = fields.Char(string='零件图号', readonly=True)
@api.constrains('tool_length')
def _check_tool_length_size(self):
@@ -626,6 +636,7 @@ class ResProductMo(models.Model):
'model_remark': item['remark'],
'default_code': '%s-%s' % (order_number, i),
'manual_quotation': item['manual_quotation'] or False,
'part_number': item.get('part_number') or '',
'active': True,
}
copy_product_id.sudo().write(vals)
@@ -721,6 +732,10 @@ class ResProductMo(models.Model):
logging.info('create-model_file:%s' % len(vals['model_file']))
self._sanitize_vals(vals)
templates = super(ResProductMo, self).create(vals_list)
# 产品名称唯一性校验
for item in templates:
if len(self.search([('name', '=', item.name)])) > 1:
raise ValidationError('产品名称【%s】已存在' % item.name)
if "create_product_product" not in self._context:
templates._create_variant_ids()

View File

@@ -3,6 +3,7 @@ import base64
import qrcode
from collections import defaultdict, namedtuple
import logging
import io
import json
from re import split as regex_split
from re import findall as regex_findall
@@ -492,7 +493,7 @@ class StockPicking(models.Model):
# 设置外协出入单的名称
def _get_name_Res(self, rescode):
last_picking = self.sudo().search([('name', 'like', rescode)], order='create_date DESC', limit=1)
last_picking = self.sudo().search([('name', 'like', rescode)], order='create_date desc,id desc', limit=1)
if not last_picking:
num = "%04d" % 1
else:
@@ -631,8 +632,8 @@ class ReStockMove(models.Model):
'reserved_uom_qty': 1.0,
'lot_id': purchase.picking_ids.move_line_ids.lot_id.id,
'company_id': self.company_id.id,
'workorder_id': '' if not sorted_workorders else sorted_workorders.id,
'production_id': '' if not sorted_workorders else sorted_workorders.production_id.id,
# 'workorder_id': '' if not sorted_workorders else sorted_workorders.id,
# 'production_id': '' if not sorted_workorders else sorted_workorders.production_id.id,
'state': 'assigned',
}
@@ -668,12 +669,14 @@ class ReStockMove(models.Model):
else:
view = self.env.ref('stock.view_stock_move_nosuggest_operations')
if self.product_id.tracking == "serial" and self.state == "assigned":
print(self.origin)
if self.product_id.categ_id.name == '刀具':
self.next_serial = self._get_tool_next_serial(self.company_id, self.product_id, self.origin)
else:
self.next_serial = self.env['stock.lot']._get_next_serial(self.company_id, self.product_id)
if self.state == "assigned":
if self.product_id.tracking == "serial":
if self.product_id.categ_id.name == '刀具':
self.next_serial = self._get_tool_next_serial(self.company_id, self.product_id, self.origin)
else:
self.next_serial = self.env['stock.lot']._get_next_serial(self.company_id, self.product_id)
elif self.product_id.tracking == "lot":
self._put_tool_lot(self.company_id, self.product_id, self.origin)
return {
'name': _('Detailed Operations'),
@@ -698,6 +701,58 @@ class ReStockMove(models.Model):
),
}
def put_move_line(self):
"""
确认订单时,自动分配序列号
"""
if self.product_id.tracking == "serial":
if self.product_id.categ_id.name == '刀具':
self.next_serial = self._get_tool_next_serial(self.company_id, self.product_id, self.origin)
else:
self.next_serial = self.env['stock.lot']._get_next_serial(self.company_id, self.product_id)
self._generate_serial_numbers()
for item in self.move_line_nosuggest_ids:
if item.lot_name:
lot_name = item.lot_name
if item.product_id.categ_id.name == '坯料':
lot_name = lot_name.split('[', 1)[0]
item.lot_qr_code = self.compute_lot_qr_code(lot_name)
def _put_tool_lot(self, company, product, origin):
if product.tracking == "lot" and self.product_id.categ_id.name == '刀具':
if not self.move_line_nosuggest_ids:
lot_code = '%s-%s-%s' % ('%s-T-DJWL-%s' % (
product.cutting_tool_model_id.code.split('-')[0], product.cutting_tool_material_id.code),
datetime.now().strftime("%Y%m%d"), origin)
move_line_ids = self.env['stock.move.line'].sudo().search([('lot_name', 'like', lot_code)], limit=1,
order='id desc')
if not move_line_ids:
lot_code = '%s-001' % lot_code
else:
lot_code = '%s-%03d' % (lot_code, int(move_line_ids.lot_name[-3:]) + 1)
lot_names = self.env['stock.lot'].generate_lot_names(lot_code, 1)
move_lines_commands = self._generate_serial_move_line_commands_tool_lot(lot_names)
self.write({'move_line_nosuggest_ids': move_lines_commands})
for item in self.move_line_nosuggest_ids:
if item.lot_name:
item.lot_qr_code = self.compute_lot_qr_code(item.lot_name)
def compute_lot_qr_code(self, lot_name):
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10,
border=4,
)
qr.add_data(lot_name)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
buffer = io.BytesIO()
img.save(buffer, format="PNG")
binary_data = buffer.getvalue()
data = base64.b64encode(binary_data).decode() # 确保返回的是字符串形式的数据
return data
def _get_tool_next_serial(self, company, product, origin):
"""Return the next serial number to be attributed to the product."""
if product.tracking == "serial":
@@ -711,6 +766,67 @@ class ReStockMove(models.Model):
else:
return "%s-T-%s-%s-%03d" % (split_codes[0], origin, product.specification_id.name, 1)
def _generate_serial_move_line_commands_tool_lot(self, lot_names, origin_move_line=None):
"""Return a list of commands to update the move lines (write on
existing ones or create new ones).
Called when user want to create and assign multiple serial numbers in
one time (using the button/wizard or copy-paste a list in the field).
:param lot_names: A list containing all serial number to assign.
:type lot_names: list
:param origin_move_line: A move line to duplicate the value from, default to None
:type origin_move_line: record of :class:`stock.move.line`
:return: A list of commands to create/update :class:`stock.move.line`
:rtype: list
"""
self.ensure_one()
# Select the right move lines depending of the picking type configuration.
move_lines = self.env['stock.move.line']
if self.picking_type_id.show_reserved:
move_lines = self.move_line_ids.filtered(lambda ml: not ml.lot_id and not ml.lot_name)
else:
move_lines = self.move_line_nosuggest_ids.filtered(lambda ml: not ml.lot_id and not ml.lot_name)
loc_dest = origin_move_line and origin_move_line.location_dest_id
move_line_vals = {
'picking_id': self.picking_id.id,
'location_id': self.location_id.id,
'product_id': self.product_id.id,
'product_uom_id': self.product_id.uom_id.id,
'qty_done': self.product_uom_qty,
}
if origin_move_line:
# `owner_id` and `package_id` are taken only in the case we create
# new move lines from an existing move line. Also, updates the
# `qty_done` because it could be usefull for products tracked by lot.
move_line_vals.update({
'owner_id': origin_move_line.owner_id.id,
'package_id': origin_move_line.package_id.id,
'qty_done': origin_move_line.qty_done or 1,
})
move_lines_commands = []
qty_by_location = defaultdict(float)
for lot_name in lot_names:
# We write the lot name on an existing move line (if we have still one)...
if move_lines:
move_lines_commands.append((1, move_lines[0].id, {
'lot_name': lot_name,
'qty_done': 1,
}))
qty_by_location[move_lines[0].location_dest_id.id] += 1
move_lines = move_lines[1:]
# ... or create a new move line with the serial name.
else:
loc = loc_dest or self.location_dest_id._get_putaway_strategy(self.product_id, quantity=1,
packaging=self.product_packaging_id,
additional_qty=qty_by_location)
move_line_cmd = dict(move_line_vals, lot_name=lot_name, location_dest_id=loc.id)
move_lines_commands.append((0, 0, move_line_cmd))
qty_by_location[loc.id] += 1
return move_lines_commands
class ReStockQuant(models.Model):
_inherit = 'stock.quant'

View File

@@ -7,9 +7,9 @@
<field name="model">sf.agv.site</field>
<field name="arch" type="xml">
<tree editable="bottom">
<field name="name" required="1"/>
<field name="owning_region" required="1"/>
<field name="state" required="1"/>
<field name="name" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
<field name="owning_region" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
<field name="state" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
<field name="divide_the_work" required="1"/>
</tree>
</field>
@@ -34,12 +34,14 @@
<field name="model">sf.agv.task.route</field>
<field name="arch" type="xml">
<tree editable="bottom">
<field name="name" required="1"/>
<field name="name" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
<field name="type" readonly="1" string="任务类型"/>
<field name="route_type" string="类型"/>
<field name="start_site_id" required="1" options="{'no_create': True}" string="起点接驳站"/>
<field name="route_type" string="类型" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
<field name="start_site_id" required="1" options="{'no_create': True}" string="起点接驳站"
attrs="{'readonly': [('id', '!=', False)]}"/>
<field name="end_site_id" required="1" options="{'no_create': True}" string="终点接驳站"/>
<field name="destination_production_line_id" required="1" options="{'no_create': True}"/>
<field name="destination_production_line_id" required="1" options="{'no_create': True}"
attrs="{'readonly': [('id', '!=', False)]}"/>
</tree>
</field>
</record>
@@ -73,7 +75,8 @@
<field name="model">center_control.interface.log</field>
<field name="arch" type="xml">
<search string="Logs">
<field name="name"/>
<field name="name" filter_domain="[('name','ilike', self)]"/>
<field name="content" filter_domain="[('content','ilike', self)]"/>
<group expand="0" string="分组">
<field name="interface_call_date"/>
</group>

View File

@@ -87,7 +87,7 @@
<xpath expr="//field[@name='user_id']" position="after">
<field name="production_line_id" readonly="1"/>
<field name="production_line_state" readonly="1"/>
<field name="part_number"/>
<field name="part_number" string="成品的零件图号"/>
<field name="part_drawing"/>
</xpath>
<xpath expr="//header//button[@name='action_cancel']" position="replace">

View File

@@ -19,6 +19,7 @@
</field>
<field name="state" position="after">
<field name="work_state" optional="hide"/>
<field name="product_tmpl_name" invisible="1"/>
</field>
<field name="product_id" position="after">
<field name="equipment_id" optional="hide"/>
@@ -80,7 +81,7 @@
<field name="view_mode">tree,form</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('mrp.mrp_production_workorder_tree_editable_view')}) ]"/>
<!-- (0, 0, {'view_mode': 'kanban', 'view_id': ref('mrp.workcenter_line_kanban')})-->
<!-- (0, 0, {'view_mode': 'kanban', 'view_id': ref('mrp.workcenter_line_kanban')})-->
<!-- <field name="target">fullscreen</field>-->
<field name="target">current</field>
<field name="domain">[('state', '!=', 'cancel'),('schedule_state', '=', '已排')]</field>
@@ -477,8 +478,13 @@
<page string="后置三元检测" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
<group>
<field name="test_results" attrs='{"invisible":[("results","!=",False)]}'/>
<field name="is_remanufacture" attrs='{"invisible":[("test_results","=","合格")]}'/>
<field name="results" readonly="1" attrs='{"invisible":[("results","!=","合格")]}'/>
<!-- <field name="is_remanufacture" attrs='{"invisible":[("test_results","!=","报废")]}'/>-->
<!-- <field name="is_fetchcnc"-->
<!-- attrs='{"invisible":["|",("test_results","=","合格"),("is_remanufacture","=",False)]}'/>-->
<!-- <field name="reason"-->
<!-- attrs='{"required":[("test_results","!=","合格")],"invisible":[("test_results","=","合格")]}'/>-->
<!-- <field name="detailed_reason" attrs='{"invisible":[("test_results","=","合格")]}'/>-->
<!-- <field name="results" readonly="1" attrs='{"invisible":[("results","!=","合格")]}'/>-->
<field name="detection_report" attrs='{"invisible":[("results","!=",False)]}'
widget="pdf_viewer"/>
</group>
@@ -573,7 +579,7 @@
<label for="material_height" string="高"/>
<field name="material_height" class="o_address_zip"/>
</div>
<field name="part_number"/>
<field name="part_number" string="成品的零件图号"/>
</xpath>
</field>
</record>
@@ -604,7 +610,7 @@
<field name="name">工件配送</field>
<field name="model">sf.workpiece.delivery</field>
<field name="arch" type="xml">
<tree string="工件配送" editable="bottom" class="center" create="0" delete="0">
<tree string="工件配送" class="center" create="0" delete="0">
<header>
<button name="button_delivery" type="object" string="配送" class="oe_highlight"/>
</header>
@@ -612,13 +618,13 @@
decoration-success="status == '已配送'"
decoration-warning="status == '待下发'"
decoration-danger="status == '待配送'"/>
<field name="name"/>
<field name="production_id"/>
<field name="type" readonly="1"/>
<field name="production_line_id" options="{'no_create': True}" readonly="1"/>
<field name="route_id" options="{'no_create': True}"
domain="[('route_type','in',['上产线','下产线'])]"/>
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
<field name="feeder_station_destination_id" readonly="1" force_save="1"/>
<field name="is_cnc_program_down" readonly="1"/>
<!-- <field name="rfid_code"/>-->
@@ -629,13 +635,58 @@
</field>
</record>
<record id="sf_workpiece_delivery_form" model="ir.ui.view">
<field name="name">工件配送</field>
<field name="model">sf.workpiece.delivery</field>
<field name="arch" type="xml">
<form string="工件配送" create="0" delete="0">
<header>
<field name="status" widget="statusbar"/>
<button name="button_delivery" type="object" string="配送" class="oe_highlight"
attrs="{'invisible': [('status', '!=', '待下发')]}"/>
</header>
<sheet>
<div class="oe_title">
<h1>
<field name="name" readonly="1"/>
</h1>
</div>
<group>
<group string="基本信息">
<field name="production_id"/>
<field name="type" readonly="1"/>
<field name="production_line_id" options="{'no_create': True}" readonly="1"/>
<field name="is_cnc_program_down" readonly="1"/>
<!-- <field name="rfid_code"/>-->
</group>
<group string="配送信息">
<field name="route_id" options="{'no_create': True}"
domain="[('route_type','in',['上产线','下产线'])]"/>
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
<field name="feeder_station_destination_id" readonly="1" force_save="1"/>
<field name="task_delivery_time" readonly="1"/>
<field name="task_completion_time" readonly="1"/>
<field name="delivery_duration" widget="float_time"/>
</group>
</group>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="activity_ids"/>
<field name="message_ids" options="{'post_refresh': 'recipients'}"/>
</div>
</form>
</field>
</record>
<record id="sf_workpiece_delivery_search" model="ir.ui.view">
<field name="name">工件配送</field>
<field name="model">sf.workpiece.delivery</field>
<field name="arch" type="xml">
<search string="工件配送">
<filter string="上产线" name="on_up" domain="[('type', '=', '上产线')]"/>
<filter string="下产线" name="on_down" domain="[('type', '=', '下产线' )]"/>
<filter name="filter_to_be_issued" string="待下发" domain="[('status', 'in', ['待下发'])]"/>
<filter name="filter_waiting_delivery" string="待配送" domain="[('status', 'in', ['待配送'])]"/>
<filter name="filter_delivered" string="已配送" domain="[('status', 'in', ['已配送'])]"/>
<field name="rfid_code"/>
<field name="production_id"/>
<field name="feeder_station_start_id"/>
@@ -657,9 +708,13 @@
<field name="name">工件配送</field>
<field name="res_model">sf.workpiece.delivery</field>
<field name="search_view_id" ref="sf_workpiece_delivery_search"/>
<field name="context">{'search_default_on_up':1}</field>
<field name="view_mode">tree,search</field>
<field name="domain">[('type','in',['上产线','下产线']),('workorder_state','=','done')]</field>
<field name="context">{'search_default_filter_to_be_issued': 1,
'search_default_filter_waiting_delivery': 1}
</field>
<field name="view_mode">tree,form</field>
<field name="domain">
[('type','in',['上产线','下产线']),('workorder_state','=','done'),('is_manual_work','=',false)]
</field>
</record>
@@ -668,20 +723,40 @@
<field name="name">空料架配送</field>
<field name="model">sf.workpiece.delivery</field>
<field name="arch" type="xml">
<tree string="工件配送" class="center" create="0" edit="0" delete="0">
<header>
<button name="button_delivery" type="object" string="配送" class="oe_highlight"/>
</header>
<field name="name" string="路线名称" readonly="1"/>
<field name="route_id" options="{'no_create': True}"/>
<field name="feeder_station_start_id" readonly="1"/>
<field name="feeder_station_destination_id" readonly="1"/>
<tree string="工件配送" editable="bottom" class="center" delete="0" create="1">
<!-- <header>-->
<!-- <button name="button_delivery" type="object" string="配送" class="oe_highlight"/>-->
<!-- </header>-->
<!-- <field name="name" string="路线名称" attrs="{'readonly': [('id', '!=', False)]}"-->
<!-- placeholder="例如:运送空料架路线:G01-A01" required="1" force_save="1"/>-->
<field name="route_id" options="{'no_create': True}" required="1"
attrs="{'readonly': [('id', '!=', False)]}" domain="[('route_type', '=', '运送空料架')]"
force_save="1"/>
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
<!-- <field name="type" readonly="1"/>-->
<field name="feeder_station_destination_id" readonly="1" force_save="1"/>
<button name="button_delivery" type="object" string="配送" class="oe_highlight"/>
<button name="action_delivery_history" type="object" class="btn btn-link text-info" icon="fa-history"
string="历史"/>
</tree>
</field>
</record>
<!-- <record id="sf_workpiece_delivery_empty_racks_kanban" model="ir.ui.view">-->
<!-- <field name="name">sf.workpiece.delivery.view.kanban</field>-->
<!-- <field name="model">sf.workpiece.delivery</field>-->
<!-- <field name="arch" type="xml">-->
<!-- <kanban>-->
<!-- <field name="route_id"/>-->
<!-- <field name="feeder_station_start_id"/>-->
<!-- <field name="feeder_station_destination_id"/>-->
<!--&lt;!&ndash; <button name="button_delivery" type="object" string="配送" class="oe_highlight"/>&ndash;&gt;-->
<!--&lt;!&ndash; <button name="action_delivery_history" type="object" class="btn btn-link text-info" icon="fa-history"&ndash;&gt;-->
<!--&lt;!&ndash; string="历史"/>&ndash;&gt;-->
<!-- </kanban>-->
<!-- </field>-->
<!-- </record>-->
<record id="sf_workpiece_delivery_empty_racks_tree" model="ir.ui.view">
<field name="name">空料架配送</field>
<field name="model">sf.workpiece.delivery</field>
@@ -704,6 +779,7 @@
<field name="model">sf.workpiece.delivery</field>
<field name="arch" type="xml">
<search string="运送空料架">
<filter name="filter_active" string="已归档" domain="[('active', '=', False)]"/>
<field name="route_id"/>
<field name="feeder_station_start_id"/>
<field name="feeder_station_destination_id"/>

View File

@@ -41,8 +41,8 @@
<field name="model">stock.lot</field>
<field name="inherit_id" ref="stock.view_production_lot_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='create_date']" position="after">
<field name="rfid" invisible="1"/>
<xpath expr="//field[@name='name']" position="after">
<field name="rfid"/>
</xpath>
</field>
</record>

View File

@@ -24,12 +24,22 @@ class Sf_Mrs_Connect(http.Controller):
ret = json.loads(datas)
ret = json.loads(ret['result'])
logging.info('下发编程单:%s' % ret)
is_delete_file = False
# 查询状态为进行中且类型为获取CNC加工程序的工单
cnc_production = request.env['mrp.production'].with_user(
request.env.ref("base.user_admin")).search([('name', '=', ret['production_order_no'].split(',')[0])])
cnc_program = request.env['mrp.production'].with_user(
request.env.ref("base.user_admin")).search(
[('programming_no', '=', cnc_production.programming_no), ('id', '!=', cnc_production.id)])
if cnc_production.workorder_ids.filtered(lambda a: a.routing_type == 'CNC加工').cnc_ids:
is_delete_file = True
cnc_production.workorder_ids.filtered(
lambda a1: a1.routing_type == 'CNC加工').cnc_ids.sudo().unlink()
request.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan(cnc_production)
if cnc_program.workorder_ids.filtered(lambda c: c.routing_type == 'CNC加工').cnc_ids:
cnc_program.workorder_ids.filtered(
lambda c1: c1.routing_type == 'CNC加工').cnc_ids.sudo().unlink()
request.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan(cnc_program)
# cnc_program = request.env['mrp.production'].with_user(
# request.env.ref("base.user_admin")).search([('programming_no', '=', cnc_production.programming_no)])
logging.info('制造订单号:%s' % cnc_production.name)
@@ -37,6 +47,13 @@ class Sf_Mrs_Connect(http.Controller):
# if ret['glb_file']:
# cnc_production.glb_file = base64.b64encode(ret['glb_file'])
# 拉取所有加工面的程序文件
if is_delete_file is True:
program_path_tmp_r = os.path.join('/tmp', ret['folder_name'], 'return', 'R')
files_r = os.listdir(program_path_tmp_r)
if files_r:
for file_name in files_r:
file_path = os.path.join(program_path_tmp_r, file_name)
os.remove(file_path)
for r in ret['processing_panel']:
download_state = request.env['sf.cnc.processing'].with_user(
request.env.ref("base.user_admin")).download_file_tmp(
@@ -64,12 +81,17 @@ class Sf_Mrs_Connect(http.Controller):
if cnc_processing:
cnc_processing_arr.append(cnc_processing._json_cnc_processing(cnc_processing))
if (cnc_program and cnc_processing_arr) or (not cnc_program and cnc_processing_arr):
logging.info('cnc_processing_arr:%s' % cnc_processing_arr)
cnc_production.workorder_ids.filtered(lambda g: g.routing_type == '装夹预调').write(
{'processing_drawing': cnc_production.workorder_ids.filtered(
lambda g1: g1.routing_type == 'CNC加工').cnc_worksheet})
if cnc_program and cnc_processing_arr:
cnc_program.write({'programming_state': '已编程', 'work_state': '已编程'})
cnc_program.workorder_ids.filtered(lambda d: d.routing_type == '装夹预调').write(
{'processing_drawing': cnc_production.workorder_ids.filtered(
lambda d1: d1.routing_type == 'CNC加工').cnc_worksheet})
cnc_program.workorder_ids.filtered(lambda b: b.routing_type == 'CNC加工').write(
{'cnc_ids': cnc_processing_arr, 'cnc_worksheet': cnc_production.workorder_ids.filtered(
lambda b: b.routing_type == 'CNC加工').cnc_worksheet})
lambda b1: b1.routing_type == 'CNC加工').cnc_worksheet})
cnc_program |= cnc_production
if not cnc_program and cnc_processing_arr:
cnc_program = cnc_production

View File

@@ -32,6 +32,9 @@ class FtpController():
logging.error(f"Error checking file: {e}")
return False
# # 检测字符串的编码
# def detect_encoding(self, s):
# result = chardet.detect(s)
@@ -43,6 +46,8 @@ class FtpController():
os.makedirs(serverdir)
try:
logging.info("进入FTP目录 ")
self.ftp.pwd()
logging.info('当前目录:%s' % self.ftp.pwd())
logging.info('目录:%s' % target_dir)
target_dir1 = target_dir.split('/')
logging.info('目录1:%s' % target_dir1[1])

View File

@@ -26,6 +26,7 @@ class ResConfigSettings(models.TransientModel):
ftp_port = fields.Char(string='FTP端口')
ftp_user = fields.Char(string='FTP用户')
ftp_password = fields.Char(string='FTP密码')
enable_tool_presetter = fields.Boolean('是否启用刀具预调仪', default=True)
def sf_all_sync(self):
try:
@@ -108,6 +109,7 @@ class ResConfigSettings(models.TransientModel):
ftp_port = config.get_param('ftp_port', default='')
ftp_user = config.get_param('ftp_user', default='')
ftp_password = config.get_param('ftp_password', default='')
enable_tool_presetter = config.get_param('enable_tool_presetter', default='')
values.update(
token=token,
@@ -121,7 +123,8 @@ class ResConfigSettings(models.TransientModel):
ftp_host=ftp_host,
ftp_port=ftp_port,
ftp_user=ftp_user,
ftp_password=ftp_password
ftp_password=ftp_password,
enable_tool_presetter=enable_tool_presetter
)
return values
@@ -140,3 +143,4 @@ class ResConfigSettings(models.TransientModel):
ir_config.set_param("ftp_port", self.ftp_port or "")
ir_config.set_param("ftp_user", self.ftp_user or "")
ir_config.set_param("ftp_password", self.ftp_password or "")
ir_config.set_param("enable_tool_presetter", self.enable_tool_presetter or False)

View File

@@ -114,6 +114,21 @@
</div>
</div>
</div>
<div>
<h2>刀具预调仪配置</h2>
<div class="row mt16 o_settings_container">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="enable_tool_presetter"/>
</div>
<div class="o_setting_right_pane">
<div class="text-muted">
<label for="enable_tool_presetter" string="是否启用刀具预调仪"/>
</div>
</div>
</div>
</div>
</div>
</xpath>
</field>
</record>

View File

@@ -135,12 +135,13 @@
<!-- <field name="delivery_quantity"/> -->
<!-- <field name="delivery_date"/> -->
<!-- </group> -->
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
</div>
</group>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>

View File

@@ -19,6 +19,7 @@ from . import parser_and_calculate_work_time as pc
class QuickEasyOrder(models.Model):
_name = 'quick.easy.order'
_inherit = ['mail.thread', 'mail.activity.mixin']
_description = '简易下单'
_order = 'id desc'
@@ -35,13 +36,14 @@ class QuickEasyOrder(models.Model):
('0.03', '±0.03mm'),
('0.02', '±0.02mm'),
('0.01', '±0.01mm')], string='加工精度', default='0.10')
material_id = fields.Many2one('sf.production.materials', '材料')
material_model_id = fields.Many2one('sf.materials.model', '型号')
material_id = fields.Many2one('sf.production.materials', '材料', tracking=True)
material_model_id = fields.Many2one('sf.materials.model', '型号', tracking=True)
# process_id = fields.Many2one('sf.production.process', string='表面工艺')
parameter_ids = fields.Many2many('sf.production.process.parameter', 'process_item_order_rel', string='可选参数')
quantity = fields.Integer('数量', default=1)
unit_price = fields.Float('单价')
price = fields.Float('')
parameter_ids = fields.Many2many('sf.production.process.parameter', 'process_item_order_rel', string='可选参数',
tracking=True)
quantity = fields.Integer('数量', default=1, tracking=True)
unit_price = fields.Float('', tracking=True)
price = fields.Float('总价', tracking=True)
model_file = fields.Binary('glb模型文件')
upload_model_file = fields.Many2many('ir.attachment', 'upload_qf_model_file_attachment_ref', string='上传模型文件')
delivery_time = fields.Date('交货日期')
@@ -49,10 +51,10 @@ class QuickEasyOrder(models.Model):
state = fields.Selection([('草稿', '草稿'), ('待派单', '待派单'),
('待接单', '待接单'), ('加工中', '加工中'),
('物流中', '物流中'), ('已交付', '已交付')], string='订单状态', default='草稿',
readonly=True)
readonly=True, tracking=True)
model_color_state = fields.Selection([
('success', '成功'),
('fail', '失败')], string='模型上色状态')
('fail', '失败')], string='模型上色状态', tracking=True)
processing_time = fields.Integer('加工时长(min)')
sale_order_id = fields.Many2one('sale.order', '销售订单号')
@@ -94,8 +96,7 @@ class QuickEasyOrder(models.Model):
obj.state = '待接单'
return obj
def model_analyze(self,model_attachment):
def model_analyze(self, model_attachment):
"""
step模型解析上传模型时转为web可显示的格式
:return:

View File

@@ -16,6 +16,7 @@ from odoo.exceptions import ValidationError, UserError
class QuickEasyOrder(models.Model):
_name = 'quick.easy.order'
_inherit = ['mail.thread', 'mail.activity.mixin']
_description = '简易下单'
_order = 'id desc'
@@ -32,13 +33,15 @@ class QuickEasyOrder(models.Model):
('0.03', '±0.03mm'),
('0.02', '±0.02mm'),
('0.01', '±0.01mm')], string='加工精度', default='0.10')
material_id = fields.Many2one('sf.production.materials', '材料')
material_model_id = fields.Many2one('sf.materials.model', '型号', domain="[('materials_id', '=', material_id)]")
material_id = fields.Many2one('sf.production.materials', '材料', tracking=True)
material_model_id = fields.Many2one('sf.materials.model', '型号', domain="[('materials_id', '=', material_id)]",
tracking=True)
# process_id = fields.Many2one('sf.production.process', string='表面工艺')
parameter_ids = fields.Many2many('sf.production.process.parameter', 'process_item_order_rel', string='可选参数')
quantity = fields.Integer('数量', default=1)
unit_price = fields.Float('单价')
price = fields.Float('')
parameter_ids = fields.Many2many('sf.production.process.parameter', 'process_item_order_rel', string='可选参数',
tracking=True)
quantity = fields.Integer('数量', default=1, tracking=True)
unit_price = fields.Float('', tracking=True)
price = fields.Float('总价', tracking=True)
model_file = fields.Binary('glb模型文件')
upload_model_file = fields.Many2many('ir.attachment', 'upload_qf_model_file_attachment_ref', string='上传模型文件')
delivery_time = fields.Date('交货日期')
@@ -46,10 +49,10 @@ class QuickEasyOrder(models.Model):
state = fields.Selection([('草稿', '草稿'), ('待派单', '待派单'),
('待接单', '待接单'), ('加工中', '加工中'),
('物流中', '物流中'), ('已交付', '已交付')], string='订单状态', default='草稿',
readonly=True)
readonly=True, tracking=True)
model_color_state = fields.Selection([
('success', '成功'),
('fail', '失败')], string='模型上色状态')
('fail', '失败')], string='模型上色状态', tracking=True)
processing_time = fields.Integer('加工时长(min)')
sale_order_id = fields.Many2one('sale.order', '销售订单号')

View File

@@ -215,6 +215,17 @@ class RePurchaseOrder(models.Model):
if len(product_id) != len(line):
raise ValidationError('%s】已存在,请勿重复添加' % product[-1].name)
def button_confirm(self):
result = super(RePurchaseOrder, self).button_confirm()
for item in self:
# 确认订单时,自动分配序列号
if item.picking_ids:
for picking_id in item.picking_ids:
if picking_id.move_ids:
for move_id in picking_id.move_ids:
move_id.put_move_line()
return result
class ResPartnerToSale(models.Model):
_inherit = 'res.partner'
@@ -233,12 +244,12 @@ class ResPartnerToSale(models.Model):
if obj:
raise UserError('该税ID已存在,请重新输入')
@api.constrains('email')
def _check_email(self):
if self.customer_rank > 0:
obj = self.sudo().search([('email', '=', self.email), ('id', '!=', self.id), ('active', '=', True)])
if obj:
raise UserError('该邮箱已存在,请重新输入')
# @api.constrains('email')
# def _check_email(self):
# if self.customer_rank > 0:
# obj = self.sudo().search([('email', '=', self.email), ('id', '!=', self.id), ('active', '=', True)])
# if obj:
# raise UserError('该邮箱已存在,请重新输入')
@api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):

View File

@@ -84,6 +84,11 @@
</group>
</group>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="activity_ids"/>
<field name="message_ids" options="{'post_refresh': 'recipients'}"/>
</div>
</form>
</field>
</record>

View File

@@ -24,18 +24,18 @@
<attribute name="required">1</attribute>
<attribute name="attrs">{'readonly': [('id','!=', False)]}</attribute>
</field>
<field name="email" position="replace">
<field name="email"
attrs="{'readonly': [('id','!=', False)]}"/>
</field>
<field name="mobile" position="attributes">
<attribute name="attrs">{'required': [('phone', '=', False)],'readonly': [('id','!=', False)]}
</attribute>
</field>
<field name="phone" position="attributes">
<attribute name="attrs">{'required': [('mobile', '=', False)],'readonly': [('id','!=', False)]}
</attribute>
</field>
<!-- <field name="email" position="replace">-->
<!-- <field name="email"-->
<!-- attrs="{'readonly': [('id','!=', False)]}"/>-->
<!-- </field>-->
<!-- <field name="mobile" position="attributes">-->
<!-- <attribute name="attrs">{'required': [('phone', '=', False)],'readonly': [('id','!=', False)]}-->
<!-- </attribute>-->
<!-- </field>-->
<!-- <field name="phone" position="attributes">-->
<!-- <attribute name="attrs">{'required': [('mobile', '=', False)],'readonly': [('id','!=', False)]}-->
<!-- </attribute>-->
<!-- </field>-->
<field name="street" position="attributes">
<attribute name="attrs">{'readonly': [('id','!=', False)]}
</attribute>
@@ -56,10 +56,10 @@
<field name="user_id" widget="many2one_avatar_user" context="{'is_sale': True }"
attrs="{'required' : [('customer_rank','>', 0)]}"/>
</xpath>
<field name="category_id" position="attributes">
<attribute name="required">1</attribute>
<attribute name="attrs">{'readonly': [('id','!=', False)]}</attribute>
</field>
<!-- <field name="category_id" position="attributes">-->
<!-- <attribute name="required">1</attribute>-->
<!-- <attribute name="attrs">{'readonly': [('id','!=', False)]}</attribute>-->
<!-- </field>-->
<field name="company_registry" position="attributes">
<attribute name="attrs">{'readonly': [('id','!=', False)]}</attribute>
</field>
@@ -101,7 +101,7 @@
<field name="property_supplier_payment_term_id" position="before">
<field name="purchase_user_id" context="{'supplier_rank': supplier_rank }"
widget="many2one_avatar_user"
attrs="{'required' : [('supplier_rank','>', 0)],'readonly': [('customer_rank','>', 0)]}"/>
attrs="{'required' : [('supplier_rank','>', 0)]}"/>
</field>
<xpath expr="//field[@name='property_account_position_id']" position="attributes">
<attribute name="attrs">{'readonly': [('id','!=', False)]}</attribute>
@@ -195,10 +195,10 @@
<attribute name="attrs">{'invisible': [('customer_rank','=', 0)]}
</attribute>
</field>
<field name="user_id" position="before">
<field name="purchase_user_id" widget="many2one_avatar_user"
attrs="{'invisible' : [('supplier_rank','=', 0)]}"/>
</field>
<!-- <field name="user_id" position="before">-->
<!-- <field name="purchase_user_id" widget="many2one_avatar_user"-->
<!-- attrs="{'invisible' : [('supplier_rank','=', 0)]}"/>-->
<!-- </field>-->
</field>
</record>
</data>

View File

@@ -1,54 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<record id="sale_order_view_search_inherit_sf" model="ir.ui.view">
<field name="name">sale.order.search.inherit.sf</field>
<field name="model">sale.order</field>
<field name="mode">primary</field>
<field name="inherit_id" ref="sale.view_sales_order_filter"/>
<field name="arch" type="xml">
<filter name="my_sale_orders_filter" position="replace">
<field name="campaign_id"/>
<separator/>
<filter string="报价" name="draft" domain="[('state','in',('draft', 'sent'))]"/>
<filter string="销售订单" name="sales" domain="[('state','in',('sale','done'))]"/>
<separator/>
<filter string="创建日期" name="filter_create_date" date="create_date"/>
</filter>
</field>
</record>
<record id="action_quotations_with_onboarding_inherit_sf" model="ir.actions.act_window">
<field name="name">报价</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sale.order</field>
<field name="view_id" ref="sale.view_quotation_tree_with_onboarding"/>
<field name="view_mode">tree,kanban,form,calendar,pivot,graph,activity</field>
<field name="search_view_id" ref="sale_order_view_search_inherit_sf"/>
<field name="context">{'search_default_my_quotation': 1}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new quotation, the first step of a new sale!
</p>
<p>
Once the quotation is confirmed by the customer, it becomes a sales order.
<br/>
You will be able to create an invoice and collect the payment.
</p>
</field>
</record>
<!-- <menuitem id="menu_sale_quotations">-->
<!-- <field name="active" eval="False"/>-->
<!-- </menuitem>-->
<menuitem id="menu_sale_quotations_inherit_sf"
action="action_quotations_with_onboarding_inherit_sf"
groups="sales_team.group_sale_salesman,sf_base.group_sale_salemanager,sf_base.group_sale_director"
parent="sale.sale_order_menu"
sequence="11"/>
<record model="ir.ui.view" id="view_sale_order_form_inherit_sf">
<field name="name">sale.order.form.inherit.sf</field>
<field name="model">sale.order</field>

View File

@@ -22,6 +22,7 @@
'views/tool_material_search.xml',
'views/fixture_material_search_views.xml',
'views/menu_view.xml',
'views/stock.xml',
'data/tool_data.xml',
],
'demo': [

View File

@@ -8,6 +8,35 @@ from odoo.http import request
class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/ToolGroup', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
def get_functional_tool_groups_Info(self, **kw):
"""
刀具组接口
:param kw:
:return:
"""
logging.info('get_functional_tool_groups_Info:%s' % kw)
try:
datas = request.httprequest.data
ret = json.loads(datas)
# ret = json.loads(ret['result'])
logging.info('DeviceId:%s' % ret)
functional_tools = request.env['sf.tool.inventory'].sudo().search([])
res = {'Succeed': True, 'Datas': []}
if functional_tools:
for item in functional_tools:
res['Datas'].append({
'GroupName': item.tool_groups_id.name,
'ToolId': item.functional_cutting_tool_model_id.name,
'ToolName': item.name
})
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
logging.info('get_functional_tool_groups_Info error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/ToolInventory', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def get_functional_tool_inventory_Info(self, **kw):
@@ -67,3 +96,35 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
logging.info('get_functional_tool_entity_Info error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/PutToolParameter', type='http', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def put_tool_preset_parameter_port(self, **kw):
"""
刀具预调仪接口
:param kw:
:return:
"""
logging.info('put_tool_preset_parameter_port:%s' % kw)
res = {'Succeed': True, 'Datas': []}
try:
datas = request.httprequest.data
logging.info('datas: %s' % datas)
ret = str(datas, 'utf-8')
data_lists = ret.split(",")
data_list = [data.replace('+', '') for data in data_lists]
logging.info(data_list)
tool_assembly = request.env['sf.functional.tool.assembly'].sudo().search(
[('assembly_order_code', '=', data_list[0]), ('start_preset_bool', '=', True)])
if not tool_assembly:
return json.JSONEncoder().encode(
{'Succeed': False, 'ErrorCode': 201, 'code': data_list[0], 'Error': '没有找到正在组装的组装单!'})
tool_assembly.write({
'after_assembly_tool_loading_length': float(data_list[1] or "0"), # 高度(总长度)
'after_assembly_functional_tool_diameter': float(data_list[2] or "0") * 2, # 直径
'after_assembly_knife_tip_r_angle': float(data_list[3] or "0") # R角
})
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
logging.info('put_tool_preset_parameter_port error:%s' % e)
return json.JSONEncoder().encode(res)

View File

@@ -5,8 +5,8 @@
<field name="model_id" ref="model_sf_tool_datasync"/>
<field name="state">code</field>
<field name="code">model._cron_tool_datasync_all()</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="interval_number">12</field>
<field name="interval_type">hours</field>
<field name="numbercall">-1</field>
</record>
</odoo>

View File

@@ -6,4 +6,6 @@ from . import mrp_workorder
from . import functional_tool_enroll
from . import fixture_material_search
from . import fixture_enroll
from . import temporary_data_processing_methods
from . import stock

View File

@@ -181,6 +181,7 @@ class MachineTableToolChangingApply(models.Model):
class CAMWorkOrderProgramKnifePlan(models.Model):
_name = 'sf.cam.work.order.program.knife.plan'
_inherit = ['mail.thread']
_description = 'CAM工单程序用刀计划'
name = fields.Char('工单任务编号')
@@ -228,7 +229,7 @@ class CAMWorkOrderProgramKnifePlan(models.Model):
estimated_processing_time = fields.Char('预计加工时间')
plan_execute_status = fields.Selection([('0', '待下发'), ('1', '执行中'), ('2', '已完成')],
string='计划执行状态', default='0', readonly=False)
string='计划执行状态', default='0', readonly=False, tracking=True)
sf_functional_tool_assembly_id = fields.Many2one('sf.functional.tool.assembly', '功能刀具组装', readonly=True)
@@ -282,7 +283,8 @@ class CAMWorkOrderProgramKnifePlan(models.Model):
'loading_task_source': '0',
'applicant': self.env.user.name,
'use_tool_time': self.need_knife_time,
'use_tool_time': fields.Datetime.now() + timedelta(
hours=4) if not self.need_knife_time else self.need_knife_time,
'reason_for_applying': '工单用刀',
'sf_cam_work_order_program_knife_plan_id': self.id
@@ -311,45 +313,58 @@ class CAMWorkOrderProgramKnifePlan(models.Model):
'applicant': None,
'sf_functional_tool_assembly_id': None})
def create_cam_work_plan(self, cnc_processing):
def create_cam_work_plan(self, cnc_processing_ids):
"""
根据传入的工单信息查询是否有需要的功能刀具如果没有则生成CAM工单程序用刀计划
"""
status = False
if cnc_processing.cutting_tool_name:
functional_tools = self.env['sf.real.time.distribution.of.functional.tools'].sudo().search(
[('name', '=', cnc_processing.cutting_tool_name)])
if functional_tools:
for functional_tool in functional_tools:
if functional_tool.on_tool_stock_num == 0:
if functional_tool.tool_stock_num == 0 and functional_tool.side_shelf_num == 0:
status = True
else:
status = True
if status:
knife_plan = self.env['sf.cam.work.order.program.knife.plan'].sudo().create({
'name': cnc_processing.workorder_id.production_id.name,
'cam_procedure_code': cnc_processing.program_name,
'filename': cnc_processing.cnc_id.name,
'functional_tool_name': cnc_processing.cutting_tool_name,
'cam_cutter_spacing_code': cnc_processing.cutting_tool_no,
'process_type': cnc_processing.processing_type,
'margin_x_y': float(cnc_processing.margin_x_y),
'margin_z': float(cnc_processing.margin_z),
'finish_depth': float(cnc_processing.depth_of_processing_z),
'extension_length': float(cnc_processing.cutting_tool_extension_length),
'shank_model': cnc_processing.cutting_tool_handle_type,
'estimated_processing_time': cnc_processing.estimated_processing_time,
})
logging.info('CAM工单程序用刀计划创建成功')
# 创建装刀请求
knife_plan.apply_for_tooling()
else:
logging.info('功能刀具【%s】满足CNC用刀需求' % cnc_processing.cutting_tool_name)
for cnc_processing in cnc_processing_ids:
status = False
if cnc_processing.cutting_tool_name:
functional_tools = self.env['sf.real.time.distribution.of.functional.tools'].sudo().search(
[('name', '=', cnc_processing.cutting_tool_name)])
if functional_tools:
for functional_tool in functional_tools:
if functional_tool.on_tool_stock_num == 0:
if functional_tool.tool_stock_num == 0 and functional_tool.side_shelf_num == 0:
status = True
else:
status = True
if status:
knife_plan = self.env['sf.cam.work.order.program.knife.plan'].sudo().create({
'name': cnc_processing.workorder_id.production_id.name,
'cam_procedure_code': cnc_processing.program_name,
'filename': cnc_processing.cnc_id.name,
'functional_tool_name': cnc_processing.cutting_tool_name,
'cam_cutter_spacing_code': cnc_processing.cutting_tool_no,
'process_type': cnc_processing.processing_type,
'margin_x_y': float(cnc_processing.margin_x_y),
'margin_z': float(cnc_processing.margin_z),
'finish_depth': float(cnc_processing.depth_of_processing_z),
'extension_length': float(cnc_processing.cutting_tool_extension_length),
'shank_model': cnc_processing.cutting_tool_handle_type,
'estimated_processing_time': cnc_processing.estimated_processing_time,
})
logging.info('CAM工单程序用刀计划创建成功')
# 创建装刀请求
knife_plan.apply_for_tooling()
else:
logging.info('功能刀具【%s】满足CNC用刀需求' % cnc_processing.cutting_tool_name)
def unlink_cam_plan(self, production):
for item in production:
cam_plan_ids = self.env['sf.cam.work.order.program.knife.plan'].search([('name', '=', item.name)])
for cam_plan_id in cam_plan_ids:
assembly_id = cam_plan_id.sf_functional_tool_assembly_id
if assembly_id.assemble_status in ('0', '待组装') and not assembly_id.start_preset_bool:
logging.info('%s删除成功!!!' % assembly_id)
assembly_id.sudo().unlink()
logging.info('unlink_cam_plan成功')
cam_plan_ids.sudo().unlink()
class FunctionalToolAssembly(models.Model):
_name = 'sf.functional.tool.assembly'
_inherit = ['mail.thread']
_description = '功能刀具组装'
_order = 'assemble_status, use_tool_time asc'
@@ -384,7 +399,7 @@ class FunctionalToolAssembly(models.Model):
applicant = fields.Char(string='申请人', readonly=True)
apply_time = fields.Datetime(string='申请时间', default=fields.Datetime.now(), readonly=True)
assemble_status = fields.Selection([('0', '待组装'), ('1', '已组装')], string='组装状态', default='0',
readonly=True)
tracking=True, readonly=True)
cutter_spacing_code_id = fields.Many2one('maintenance.equipment.tool', string='刀位号', readonly=True)
whether_standard_knife = fields.Boolean(string='是否标准刀', default=True, readonly=True)
reason_for_applying = fields.Char(string='申请原因', readonly=True)
@@ -401,8 +416,9 @@ class FunctionalToolAssembly(models.Model):
return categories.browse(functional_tool_type_ids)
# 刀具物料信息
# ==============整体式刀具型号============
integral_freight_barcode = fields.Char('整体式刀具货位')
# ==============整体式刀具型号=============
integral_freight_barcode_id = fields.Many2one('sf.shelf.location', string='整体式刀具货位')
integral_lot_id = fields.Many2one('stock.lot', string='整体式刀具批次')
integral_product_id = fields.Many2one('product.product', string='整体式刀具名称',
compute='_compute_integral_product_id', store=True)
cutting_tool_integral_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='整体式刀具型号',
@@ -412,19 +428,15 @@ class FunctionalToolAssembly(models.Model):
sf_tool_brand_id_1 = fields.Many2one('sf.machine.brand', string='整体式刀具品牌',
related='integral_product_id.brand_id')
@api.depends('integral_freight_barcode')
@api.depends('integral_lot_id')
def _compute_integral_product_id(self):
for item in self:
if item.integral_freight_barcode:
location = self.env['sf.shelf.location'].sudo().search(
[('barcode', '=', item.integral_freight_barcode)])
if location:
item.integral_product_id = location.product_id.id
else:
item.integral_product_id = False
if item.integral_lot_id:
item.integral_product_id = item.integral_lot_id.product_id.id
# =================刀片型号=============
blade_freight_barcode = fields.Char('刀片货位')
blade_freight_barcode_id = fields.Many2one('sf.shelf.location', string='刀片货位')
blade_lot_id = fields.Many2one('stock.lot', string='刀片批次')
blade_product_id = fields.Many2one('product.product', string='刀片名称', compute='_compute_blade_product_id',
store=True)
cutting_tool_blade_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀片型号',
@@ -433,18 +445,15 @@ class FunctionalToolAssembly(models.Model):
related='blade_product_id.specification_id')
sf_tool_brand_id_2 = fields.Many2one('sf.machine.brand', '刀片品牌', related='blade_product_id.brand_id')
@api.depends('blade_freight_barcode')
@api.depends('blade_lot_id')
def _compute_blade_product_id(self):
for item in self:
if item.blade_freight_barcode:
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', item.blade_freight_barcode)])
if location:
item.blade_product_id = location.product_id.id
else:
item.blade_product_id = False
if item.blade_lot_id:
item.blade_product_id = item.blade_lot_id.product_id.id
# ==============刀杆型号================
bar_freight_barcode = fields.Char('刀杆货位')
bar_freight_barcode_id = fields.Many2one('sf.shelf.location', string='刀杆货位')
bar_lot_id = fields.Many2one('stock.lot', string='刀杆批次')
bar_product_id = fields.Many2one('product.product', string='刀杆名称', compute='_compute_bar_product_id',
store=True)
cutting_tool_cutterbar_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀杆型号',
@@ -453,18 +462,15 @@ class FunctionalToolAssembly(models.Model):
related='bar_product_id.specification_id')
sf_tool_brand_id_3 = fields.Many2one('sf.machine.brand', '刀杆品牌', related='bar_product_id.brand_id')
@api.depends('bar_freight_barcode')
@api.depends('bar_lot_id')
def _compute_bar_product_id(self):
for item in self:
if item.bar_freight_barcode:
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', item.bar_freight_barcode)])
if location:
item.bar_product_id = location.product_id.id
else:
item.bar_product_id = False
if item.bar_lot_id:
item.bar_product_id = item.bar_lot_id.product_id.id
# =============刀盘型号================
pad_freight_barcode = fields.Char('刀盘货位')
pad_freight_barcode_id = fields.Many2one('sf.shelf.location', string='刀盘货位')
pad_lot_id = fields.Many2one('stock.lot', string='刀盘批次')
pad_product_id = fields.Many2one('product.product', string='刀盘名称', compute='_compute_pad_product_id',
store=True)
cutting_tool_cutterpad_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀盘型号',
@@ -473,15 +479,11 @@ class FunctionalToolAssembly(models.Model):
related='pad_product_id.specification_id')
sf_tool_brand_id_4 = fields.Many2one('sf.machine.brand', '刀盘品牌', related='pad_product_id.brand_id')
@api.depends('pad_freight_barcode')
@api.depends('pad_lot_id')
def _compute_pad_product_id(self):
for item in self:
if item.pad_freight_barcode:
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', item.pad_freight_barcode)])
if location:
item.pad_product_id = location.product_id.id
else:
item.pad_product_id = False
if item.pad_lot_id:
item.pad_product_id = item.pad_lot_id.product_id.id
# ==============刀柄型号==============
handle_freight_rfid = fields.Char('刀柄Rfid', compute='_compute_handle_product_id', store=True)
@@ -505,7 +507,8 @@ class FunctionalToolAssembly(models.Model):
item.handle_freight_rfid = False
# ==============夹头型号==============
chuck_freight_barcode = fields.Char('夹头货位')
chuck_freight_barcode_id = fields.Many2one('sf.shelf.location', string='夹头货位')
chuck_lot_id = fields.Many2one('stock.lot', string='夹头批次')
chuck_product_id = fields.Many2one('product.product', string='夹头名称', compute='_compute_chuck_product_id',
store=True)
cutting_tool_cutterhead_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='夹头型号',
@@ -514,17 +517,18 @@ class FunctionalToolAssembly(models.Model):
related='chuck_product_id.specification_id')
sf_tool_brand_id_6 = fields.Many2one('sf.machine.brand', '夹头品牌', related='chuck_product_id.brand_id')
@api.depends('chuck_freight_barcode')
@api.depends('chuck_lot_id')
def _compute_chuck_product_id(self):
for item in self:
if item.chuck_freight_barcode:
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', item.chuck_freight_barcode)])
if location:
item.chuck_product_id = location.product_id.id
else:
item.chuck_product_id = False
if item.chuck_lot_id:
item.chuck_product_id = item.chuck_lot_id.product_id.id
# ==================待删除字段==================
integral_freight_barcode = fields.Char('整体式刀具货位')
blade_freight_barcode = fields.Char('刀片货位')
bar_freight_barcode = fields.Char('刀杆货位')
pad_freight_barcode = fields.Char('刀盘货位')
chuck_freight_barcode = fields.Char('夹头货位')
blade_name = fields.Char('')
integral_name = fields.Char('')
blade_code_id = fields.Many2one('stock.lot', '刀片序列号')
@@ -538,12 +542,14 @@ class FunctionalToolAssembly(models.Model):
chuck_name = fields.Char('')
# ==============================================
# 组装功能刀具参数信息
start_preset_bool = fields.Boolean('开始预调', default=False)
barcode_id = fields.Many2one('stock.lot', string='功能刀具序列号', readonly=True)
after_assembly_functional_tool_name = fields.Char(string='组装后功能刀具名称', readonly=True)
after_assembly_functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model',
string='组装后功能刀具类型', readonly=True)
after_assembly_functional_tool_diameter = fields.Float(string='组装后功能刀具直径(mm)', readonly=True)
after_assembly_knife_tip_r_angle = fields.Float(string='组装后刀尖R角(mm)', readonly=True)
after_assembly_functional_tool_diameter = fields.Float(string='组装后功能刀具直径(mm)', readonly=True,
digits=(10, 3))
after_assembly_knife_tip_r_angle = fields.Float(string='组装后刀尖R角(mm)', readonly=True, digits=(10, 3))
after_assembly_new_former = fields.Selection([('0', ''), ('1', '')], string='组装后新/旧', readonly=True)
cut_time = fields.Integer(string='已切削时间(min)', readonly=True)
cut_length = fields.Float(string='已切削长度(mm)', readonly=True)
@@ -555,8 +561,9 @@ class FunctionalToolAssembly(models.Model):
after_assembly_max_lifetime_value = fields.Integer(string='组装后最大寿命值(min)', readonly=True)
after_assembly_alarm_value = fields.Integer(string='组装后报警值(min)', readonly=True)
after_assembly_used_value = fields.Integer(string='组装后已使用值(min)', readonly=True)
after_assembly_tool_loading_length = fields.Float(string='组装后总长度(mm)', readonly=True)
after_assembly_functional_tool_length = fields.Float(string='组装后伸出长(mm)', readonly=True)
after_assembly_tool_loading_length = fields.Float(string='组装后总长度(mm)', readonly=True, digits=(10, 3))
after_assembly_handle_length = fields.Float(string='组装后刀柄长度(mm)', readonly=True, digits=(10, 3))
after_assembly_functional_tool_length = fields.Float(string='组装后伸出长(mm)', readonly=True, digits=(10, 3))
after_assembly_effective_length = fields.Float(string='组装后有效长(mm)', readonly=True)
L_D_number = fields.Float(string='L/D值(mm)', readonly=True)
hiding_length = fields.Float(string='避空长(mm)', readonly=True)
@@ -574,29 +581,64 @@ class FunctionalToolAssembly(models.Model):
active = fields.Boolean(string='已归档', default=True)
def put_start_preset(self):
self.search([('start_preset_bool', '=', True)]).write({'start_preset_bool': False})
self.write({
'after_assembly_tool_loading_length': 0,
'after_assembly_functional_tool_diameter': 0,
'after_assembly_knife_tip_r_angle': 0,
'start_preset_bool': True
})
return {
'type': 'ir.actions.act_window',
'res_model': 'sf.functional.tool.assembly.order',
'name': '功能刀具组装单',
'view_mode': 'form',
'target': 'new',
'context': {'default_name': self.name,
'default_assembly_order_code': self.assembly_order_code,
'default_production_line_name_id': self.production_line_name_id.id,
'default_machine_tool_name_id': self.machine_tool_name_id.id,
'default_cutter_spacing_code_id': self.cutter_spacing_code_id.id,
'default_functional_tool_name': self.functional_tool_name,
'default_functional_tool_type_id': self.functional_tool_type_id.id,
'default_tool_groups_id': self.tool_groups_id.id,
'default_functional_tool_diameter': self.functional_tool_diameter,
'default_knife_tip_r_angle': self.knife_tip_r_angle,
'default_tool_loading_length': self.tool_loading_length,
'default_functional_tool_length': self.functional_tool_length,
'default_effective_length': self.effective_length,
'default_whether_standard_knife': self.whether_standard_knife,
'default_coarse_middle_thin': self.coarse_middle_thin,
'default_new_former': self.new_former,
'default_use_tool_time': self.use_tool_time,
'default_reason_for_applying': self.reason_for_applying
}
}
def _get_code(self, loading_task_source):
"""
自动生成组装单编码
"""
new_time = str(fields.Date.today())
datetime = new_time[2:4] + new_time[5:7] + new_time[-2:]
datetime = new_time[2:4] + new_time[5:7]
if loading_task_source == '0':
code = 'C' + datetime
elif loading_task_source == '1':
code = 'J' + datetime
elif loading_task_source == '2':
code = 'K' + datetime
else:
code = False
functional_tool_assembly = self.env['sf.functional.tool.assembly'].sudo().search(
[('loading_task_source', '=', loading_task_source),
('assembly_order_code', 'ilike', datetime)], limit=1, order="id desc")
('assembly_order_code', 'ilike', code)], limit=1, order="id desc")
if not functional_tool_assembly:
num = "%03d" % 1
else:
m = int(functional_tool_assembly.assembly_order_code[-3:]) + 1
num = "%03d" % m
if loading_task_source == '0':
code = 'CAMZZD' + datetime + str(num)
elif loading_task_source == '1':
code = 'JTZZD' + datetime + str(num)
elif loading_task_source == '2':
code = 'MTSZZD' + datetime + str(num)
else:
code = False
return code
return code + str(num)
def get_functional_tool(self, val):
functional_tools = self.env['sf.functional.tool.assembly'].search(
@@ -629,7 +671,7 @@ class FunctionalToolAssembly(models.Model):
class FunctionalToolDismantle(models.Model):
_name = 'sf.functional.tool.dismantle'
_inherit = ["barcodes.barcode_events_mixin"]
_inherit = ["barcodes.barcode_events_mixin", 'mail.thread']
_description = '功能刀具拆解'
def on_barcode_scanned(self, barcode):
@@ -690,6 +732,22 @@ class FunctionalToolDismantle(models.Model):
raise ValidationError('该功能刀具因为%s拆解,无需录入库位' % self.dismantle_cause)
name = fields.Char('名称', related='functional_tool_id.name')
code = fields.Char('拆解单号', default=lambda self: self._get_code(), readonly=True)
def _get_code(self):
"""
自动生成拆解单编码
"""
new_time = str(fields.Date.today())
datetime = new_time[2:4] + new_time[5:7] + new_time[-2:]
functional_tool_dismantle = self.env['sf.functional.tool.dismantle'].sudo().search(
[('code', 'ilike', datetime)], limit=1, order="id desc")
if not functional_tool_dismantle:
num = "%03d" % 1
else:
m = int(functional_tool_dismantle.code[-3:]) + 1
num = "%03d" % m
return 'GNDJ-CJD-%s-%s' % (datetime, num)
functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', required=True,
domain=[('functional_tool_status', '!=', '已拆除')])
@@ -703,7 +761,7 @@ class FunctionalToolDismantle(models.Model):
dismantle_cause = fields.Selection(
[('寿命到期报废', '寿命到期报废'), ('崩刀报废', '崩刀报废'), ('更换为其他刀具', '更换为其他刀具'),
('刀具需磨削', '刀具需磨削')], string='拆解原因', required=True)
('刀具需磨削', '刀具需磨削')], string='拆解原因', required=True, tracking=True)
dismantle_data = fields.Datetime('拆解日期', readonly=True)
dismantle_person = fields.Char('拆解人', readonly=True)
image = fields.Binary('图片', readonly=True)
@@ -711,7 +769,7 @@ class FunctionalToolDismantle(models.Model):
scrap_id = fields.Char('报废单号', readonly=True)
grinding_id = fields.Char('磨削单号', readonly=True)
state = fields.Selection([('待拆解', '待拆解'), ('已拆解', '已拆解')], default='待拆解')
state = fields.Selection([('待拆解', '待拆解'), ('已拆解', '已拆解')], default='待拆解', tracking=True)
active = fields.Boolean('有效', default=True)
# 刀柄
@@ -721,7 +779,9 @@ class FunctionalToolDismantle(models.Model):
related='handle_product_id.cutting_tool_model_id')
handle_brand_id = fields.Many2one('sf.machine.brand', string='刀柄品牌', related='handle_product_id.brand_id')
handle_rfid = fields.Char(string='刀柄Rfid', compute='_compute_functional_tool_num', store=True)
scrap_boolean = fields.Boolean(string='刀柄是否报废', default=False)
handle_lot_id = fields.Many2one('stock.lot', string='刀柄序列号', compute='_compute_functional_tool_num',
store=True)
scrap_boolean = fields.Boolean(string='刀柄是否报废', default=False, tracking=True)
# 整体式
integral_product_id = fields.Many2one('product.product', string='整体式刀具',
@@ -730,6 +790,8 @@ class FunctionalToolDismantle(models.Model):
related='integral_product_id.cutting_tool_model_id')
integral_brand_id = fields.Many2one('sf.machine.brand', string='整体式刀具品牌',
related='integral_product_id.brand_id')
integral_lot_id = fields.Many2one('stock.lot', string='整体式刀具批次', compute='_compute_functional_tool_num',
store=True)
integral_freight_id = fields.Many2one('sf.shelf.location', '整体式刀具目标货位',
domain="[('product_id', 'in', (integral_product_id, False))]")
@@ -739,6 +801,7 @@ class FunctionalToolDismantle(models.Model):
blade_type_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀片型号',
related='blade_product_id.cutting_tool_model_id')
blade_brand_id = fields.Many2one('sf.machine.brand', string='刀片品牌', related='blade_product_id.brand_id')
blade_lot_id = fields.Many2one('stock.lot', string='刀片批次', compute='_compute_functional_tool_num', store=True)
blade_freight_id = fields.Many2one('sf.shelf.location', '刀片目标货位',
domain="[('product_id', 'in', (blade_product_id, False))]")
@@ -748,6 +811,7 @@ class FunctionalToolDismantle(models.Model):
bar_type_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀杆型号',
related='bar_product_id.cutting_tool_model_id')
bar_brand_id = fields.Many2one('sf.machine.brand', string='刀杆品牌', related='bar_product_id.brand_id')
bar_lot_id = fields.Many2one('stock.lot', string='刀杆批次', compute='_compute_functional_tool_num', store=True)
bar_freight_id = fields.Many2one('sf.shelf.location', '刀杆目标货位',
domain="[('product_id', 'in', (bar_product_id, False))]")
@@ -757,6 +821,7 @@ class FunctionalToolDismantle(models.Model):
pad_type_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀盘型号',
related='pad_product_id.cutting_tool_model_id')
pad_brand_id = fields.Many2one('sf.machine.brand', string='刀盘品牌', related='pad_product_id.brand_id')
pad_lot_id = fields.Many2one('stock.lot', string='刀盘批次', compute='_compute_functional_tool_num', store=True)
pad_freight_id = fields.Many2one('sf.shelf.location', '刀盘目标货位',
domain="[('product_id', 'in', (pad_product_id, False))]")
@@ -766,6 +831,7 @@ class FunctionalToolDismantle(models.Model):
chuck_type_id = fields.Many2one('sf.cutting_tool.standard.library', string='夹头型号',
related='chuck_product_id.cutting_tool_model_id')
chuck_brand_id = fields.Many2one('sf.machine.brand', string='夹头品牌', related='chuck_product_id.brand_id')
chuck_lot_id = fields.Many2one('stock.lot', string='夹头批次', compute='_compute_functional_tool_num', store=True)
chuck_freight_id = fields.Many2one('sf.shelf.location', '夹头目标货位',
domain="[('product_id', 'in', (chuck_product_id, False))]")
@@ -789,12 +855,20 @@ class FunctionalToolDismantle(models.Model):
item.rfid = item.functional_tool_id.rfid
item.handle_rfid = item.functional_tool_id.rfid
# 产品
item.handle_product_id = item.functional_tool_id.functional_tool_name_id.handle_product_id.id
item.integral_product_id = item.functional_tool_id.functional_tool_name_id.integral_product_id.id
item.blade_product_id = item.functional_tool_id.functional_tool_name_id.blade_product_id.id
item.bar_product_id = item.functional_tool_id.functional_tool_name_id.bar_product_id.id
item.pad_product_id = item.functional_tool_id.functional_tool_name_id.pad_product_id.id
item.chuck_product_id = item.functional_tool_id.functional_tool_name_id.chuck_product_id.id
# 批次/序列号
item.handle_lot_id = item.functional_tool_id.functional_tool_name_id.handle_code_id.id
item.integral_lot_id = item.functional_tool_id.functional_tool_name_id.integral_lot_id.id
item.blade_lot_id = item.functional_tool_id.functional_tool_name_id.blade_lot_id.id
item.bar_lot_id = item.functional_tool_id.functional_tool_name_id.bar_lot_id.id
item.pad_lot_id = item.functional_tool_id.functional_tool_name_id.pad_lot_id.id
item.chuck_lot_id = item.functional_tool_id.functional_tool_name_id.chuck_lot_id.id
else:
item.tool_groups_id = False
item.tool_type_id = False
@@ -810,14 +884,49 @@ class FunctionalToolDismantle(models.Model):
item.pad_product_id = False
item.chuck_product_id = False
item.handle_lot_id = False
item.integral_lot_id = False
item.blade_lot_id = False
item.bar_lot_id = False
item.pad_lot_id = False
item.chuck_lot_id = False
def location_duplicate_check(self):
"""
目标货位去重校验
"""
if self.blade_freight_id:
if self.bar_freight_id:
if self.blade_freight_id == self.bar_freight_id:
raise ValidationError('【刀片】和【刀杆】的目标货位重复,请重新选择!')
elif self.pad_freight_id:
if self.blade_freight_id == self.pad_freight_id:
raise ValidationError('【刀片】和【刀盘】的目标货位重复,请重新选择!')
if self.chuck_freight_id:
if self.chuck_freight_id == self.integral_freight_id:
raise ValidationError('【夹头】和【整体式刀具】的目标货位重复,请重新选择!')
if self.chuck_freight_id == self.blade_freight_id:
raise ValidationError('【夹头】和【刀片】的目标货位重复,请重新选择!')
if self.chuck_freight_id == self.bar_freight_id:
raise ValidationError('【夹头】和【刀杆】的目标货位重复,请重新选择!')
if self.chuck_freight_id == self.pad_freight_id:
raise ValidationError('【夹头】和【刀盘】的目标货位重复,请重新选择!')
def confirmation_disassembly(self):
logging.info('%s刀具确认开始拆解' % self.dismantle_cause)
code = self.code
if self.functional_tool_id.functional_tool_status == '已拆除':
raise ValidationError('Rfid为【%s】的功能刀具已经拆解,请勿重复操作!' % self.functional_tool_id.rfid_dismantle)
# 对拆解的功能刀具进行校验,只有在刀具房的功能刀具才能拆解
if self.functional_tool_id.tool_room_num == 0:
raise ValidationError('Rfid为【%s】的功能刀具当前位置为【%s】,不能进行拆解!' % (
self.rfid, self.functional_tool_id.current_location))
# 目标重复校验
self.location_duplicate_check()
location = self.env['stock.location'].search([('name', '=', '刀具组装位置')])
location_dest = self.env['stock.location'].search([('name', '=', '刀具房')])
# =================刀柄是否[报废]拆解=======
location_dest_scrap = self.env['stock.location'].search([('name', '=', 'Scrap')])
location_dest_scrap_ids = self.env['stock.location'].search([('name', 'in', ('Scrap', '报废'))])
if self.handle_rfid:
lot = self.env['stock.lot'].sudo().search([('rfid', '=', self.handle_rfid)])
if not lot:
@@ -825,53 +934,65 @@ class FunctionalToolDismantle(models.Model):
functional_tool_assembly = self.functional_tool_id.functional_tool_name_id
if self.scrap_boolean:
# 刀柄报废 入库到Scrap
lot.create_stock_quant(location, location_dest_scrap, functional_tool_assembly.id, '功能刀具拆解',
lot.create_stock_quant(location, location_dest_scrap_ids[-1], functional_tool_assembly.id, code,
functional_tool_assembly, functional_tool_assembly.tool_groups_id)
lot.tool_material_status = '报废'
else:
# 刀柄不报废 入库到刀具房
lot.create_stock_quant(location, location_dest, functional_tool_assembly.id, '功能刀具拆解',
lot.create_stock_quant(location, location_dest, functional_tool_assembly.id, code,
functional_tool_assembly, functional_tool_assembly.tool_groups_id)
lot.tool_material_status = '可用'
# ==============功能刀具[报废]拆解================
if self.dismantle_cause in ['寿命到期报废', '崩刀报废']:
# 除刀柄外物料报废 入库到Scrap
if self.integral_product_id:
self.integral_product_id.dismantle_stock_moves(False, location, location_dest_scrap)
self.integral_product_id.dismantle_stock_moves(False, self.integral_lot_id, location,
location_dest_scrap_ids[-1], code)
elif self.blade_product_id:
self.blade_product_id.dismantle_stock_moves(False, location, location_dest_scrap)
self.blade_product_id.dismantle_stock_moves(False, self.blade_lot_id, location,
location_dest_scrap_ids[-1], code)
if self.bar_product_id:
self.bar_product_id.dismantle_stock_moves(False, location, location_dest_scrap)
self.bar_product_id.dismantle_stock_moves(False, self.bar_lot_id, location,
location_dest_scrap_ids[-1], code)
elif self.pad_product_id:
self.pad_product_id.dismantle_stock_moves(False, location, location_dest_scrap)
self.pad_product_id.dismantle_stock_moves(False, self.pad_lot_id, location,
location_dest_scrap_ids[-1], code)
if self.chuck_product_id:
self.chuck_product_id.dismantle_stock_moves(False, location, location_dest_scrap)
self.chuck_product_id.dismantle_stock_moves(False, self.chuck_lot_id, location,
location_dest_scrap_ids[-1], code)
# ===========功能刀具[磨削]拆解==============
elif self.dismantle_cause in ['刀具需磨削']:
location_dest = self.env['stock.location'].search([('name', '=', '磨削房')])
# 除刀柄外物料拆解 入库到具体库位
if self.integral_product_id:
self.integral_product_id.dismantle_stock_moves(False, location, location_dest)
elif self.blade_product_id:
self.blade_product_id.dismantle_stock_moves(False, location, location_dest)
if self.bar_product_id:
self.bar_product_id.dismantle_stock_moves(False, location, location_dest)
elif self.pad_product_id:
self.pad_product_id.dismantle_stock_moves(False, location, location_dest)
if self.chuck_product_id:
self.chuck_product_id.dismantle_stock_moves(False, location, location_dest)
# ==============功能刀具[更换]拆解==============
elif self.dismantle_cause in ['更换为其他刀具']:
# 除刀柄外物料拆解 入库到具体
# elif self.dismantle_cause in ['刀具需磨削']:
# location_dest = self.env['stock.location'].search([('name', '=', '磨削房')])
# # 除刀柄外物料拆解 入库到具体库位
# if self.integral_product_id:
# self.integral_product_id.dismantle_stock_moves(False, location, location_dest)
# elif self.blade_product_id:
# self.blade_product_id.dismantle_stock_moves(False, location, location_dest)
# if self.bar_product_id:
# self.bar_product_id.dismantle_stock_moves(False, location, location_dest)
# elif self.pad_product_id:
# self.pad_product_id.dismantle_stock_moves(False, location, location_dest)
# if self.chuck_product_id:
# self.chuck_product_id.dismantle_stock_moves(False, location, location_dest)
# ==============功能刀具[更换,磨削]拆解==============
elif self.dismantle_cause in ['更换为其他刀具', '刀具需磨削']:
# 除刀柄外物料拆解 入库到具体
if self.integral_freight_id:
self.integral_product_id.dismantle_stock_moves(self.integral_freight_id.barcode, location,
location_dest)
self.integral_product_id.dismantle_stock_moves(self.integral_freight_id, self.integral_lot_id, location,
location_dest, code)
elif self.blade_freight_id:
self.blade_product_id.dismantle_stock_moves(self.blade_freight_id.barcode, location, location_dest)
self.blade_product_id.dismantle_stock_moves(self.blade_freight_id, self.blade_lot_id, location,
location_dest, code)
if self.bar_freight_id:
self.bar_product_id.dismantle_stock_moves(self.bar_freight_id.barcode, location, location_dest)
self.bar_product_id.dismantle_stock_moves(self.bar_freight_id, self.bar_lot_id, location,
location_dest, code)
elif self.pad_freight_id:
self.pad_product_id.dismantle_stock_moves(self.pad_freight_id.barcode, location, location_dest)
self.pad_product_id.dismantle_stock_moves(self.pad_freight_id, self.pad_lot_id, location,
location_dest, code)
if self.chuck_freight_id:
self.chuck_product_id.dismantle_stock_moves(self.chuck_freight_id.barcode, location, location_dest)
self.chuck_product_id.dismantle_stock_moves(self.chuck_freight_id, self.chuck_lot_id, location,
location_dest, code)
# ===============删除功能刀具的Rfid字段的值 赋值给Rfid(已拆解)字段=====
self.functional_tool_id.write({
'rfid_dismantle': self.functional_tool_id.rfid,
@@ -892,26 +1013,22 @@ class FunctionalToolDismantle(models.Model):
class ProductProduct(models.Model):
_inherit = 'product.product'
def dismantle_stock_moves(self, shelf_location_barcode, location_id, location_dest_id):
def dismantle_stock_moves(self, shelf_location_id, lot_id, location_id, location_dest_id, code):
# 创建功能刀具拆解单产品库存移动记录
stock_move_id = self.env['stock.move'].sudo().create({
'name': '功能刀具拆解',
'name': code,
'product_id': self.id,
'location_id': location_id.id,
'location_dest_id': location_dest_id.id,
'product_uom_qty': 1.00,
'state': 'done'
})
if shelf_location_barcode:
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', shelf_location_barcode)])
location.product_num = location.product_num + 1
else:
location = self.env['sf.shelf.location']
# 创建移动历史记录
stock_move_line_id = self.env['stock.move.line'].sudo().create({
'product_id': self.id,
'lot_id': lot_id.id,
'move_id': stock_move_id.id,
'current_location_id': location.id,
'destination_location_id': shelf_location_id.id,
'install_tool_time': fields.Datetime.now(),
'qty_done': 1.0,
'state': 'done',

View File

@@ -1,6 +1,7 @@
import json
import base64
import requests
import logging
from odoo import models, api
from odoo.addons.sf_base.commons.common import Common
from odoo.exceptions import UserError
@@ -10,30 +11,36 @@ class StockLot(models.Model):
_inherit = 'stock.lot'
_description = '夹具物料序列号注册'
def enroll_fixture_material_stock(self):
def sync_enroll_fixture_material_stock_all(self):
logging.info("调用 sync_enroll_fixture_material_stock_all 同步接口")
sf_sync_config = self.env['res.config.settings'].get_values()
token = sf_sync_config['token']
sf_secret_key = sf_sync_config['sf_secret_key']
headers = Common.get_headers(self, token, sf_secret_key)
str_url = sf_sync_config['sf_url'] + "/api/fixture_material_stock/create"
objs_all = self.env['stock.lot'].search([('id', '=', self.id)])
product_ids = self.env['product.product'].sudo().search([('categ_type', '=', '夹具')]).ids
objs_all = self.env['stock.lot'].search([('rfid', '!=', False), ('product_id', 'in', product_ids)])
fixture_material_stock_list = []
if objs_all:
for item in objs_all:
val = {
'name': item.name,
'tool_material_status': item.tool_material_status,
'location': [] if not item.quant_ids else item.quant_ids[-1].location_id.name,
'fixture_material_search_id': item.fixture_material_search_id.id,
}
fixture_material_stock_list.append(val)
kw = json.dumps(fixture_material_stock_list, ensure_ascii=False)
r = requests.post(str_url, json={}, data={'kw': kw, 'token': token}, headers=headers)
ret = r.json()
if ret.get('code') == 200:
return '夹具物料序列号注册成功'
else:
raise UserError("没有注册夹具物料序列号信息")
try:
if objs_all:
for item in objs_all:
val = {
'name': item.name,
'tool_material_status': item.tool_material_status,
'location': [] if not item.quant_ids else item.quant_ids[-1].location_id.name,
'fixture_material_search_id': item.fixture_material_search_id.id,
}
fixture_material_stock_list.append(val)
kw = json.dumps(fixture_material_stock_list, ensure_ascii=False)
r = requests.post(str_url, json={}, data={'kw': kw, 'token': token}, headers=headers)
ret = r.json()
if ret.get('code') == 200:
logging.info("夹具物料序列号每日同步成功")
return '夹具物料序列号注册成功'
else:
logging.info("没有注册夹具物料序列号信息")
except Exception as e:
logging.info("夹具物料序列号同步失败:%s" % e)
class FixtureMaterialSearch(models.Model):
@@ -42,41 +49,46 @@ class FixtureMaterialSearch(models.Model):
crea_url = "/api/fixture_material/create"
def enroll_fixture_material(self):
def sync_enroll_fixture_material_all(self):
logging.info("调用 sync_enroll_fixture_material_all 同步接口")
sf_sync_config = self.env['res.config.settings'].get_values()
token = sf_sync_config['token']
sf_secret_key = sf_sync_config['sf_secret_key']
headers = Common.get_headers(self, token, sf_secret_key)
str_url = sf_sync_config['sf_url'] + self.crea_url
objs_all = self.search([('id', '=', self.id)])
objs_all = self.search([])
fixture_material_list = []
if objs_all:
for obj in objs_all:
val = {
'name': obj.name,
'id': obj.id,
'fixture_material_code': obj.fixture_material_id.code,
'fixture_model_code': obj.fixture_model_id.code,
'specification_name': obj.specification_fixture_id.name,
'image': '' if not obj.image else base64.b64encode(obj.image).decode('utf-8'),
'number': obj.number,
'usable_num': obj.usable_num,
'have_been_used_num': obj.have_been_used_num,
'scrap_num': obj.scrap_num
}
fixture_material_list.append(val)
kw = json.dumps(fixture_material_list, ensure_ascii=False)
r = requests.post(str_url, json={}, data={'kw': kw, 'token': token}, headers=headers)
ret = r.json()
if ret.get('code') == 200:
return '夹具物料注册成功'
else:
raise UserError("没有注册夹具物料信息")
try:
if objs_all:
for obj in objs_all:
val = {
'name': obj.name,
'id': obj.id,
'fixture_material_code': obj.fixture_material_id.code,
'fixture_model_code': obj.fixture_model_id.code,
'specification_name': obj.specification_fixture_id.name,
'image': '' if not obj.image else base64.b64encode(obj.image).decode('utf-8'),
'number': obj.number,
'usable_num': obj.usable_num,
'have_been_used_num': obj.have_been_used_num,
'scrap_num': obj.scrap_num
}
fixture_material_list.append(val)
kw = json.dumps(fixture_material_list, ensure_ascii=False)
r = requests.post(str_url, json={}, data={'kw': kw, 'token': token}, headers=headers)
ret = r.json()
if ret.get('code') == 200:
logging.info("夹具物料每日同步成功")
return '夹具物料注册成功'
else:
logging.info("没有注册夹具物料信息")
except Exception as e:
logging.info("夹具物料同步失败:%s" % e)
@api.model_create_multi
def create(self, vals_list):
records = super(FixtureMaterialSearch, self).create(vals_list)
for record in records:
if record:
record.enroll_fixture_material()
return records
# @api.model_create_multi
# def create(self, vals_list):
# records = super(FixtureMaterialSearch, self).create(vals_list)
# for record in records:
# if record:
# record.enroll_fixture_material()
# return records

View File

@@ -14,88 +14,152 @@ class FunctionalCuttingToolEntity(models.Model):
functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具组装单', readonly=True)
tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', related='functional_tool_name_id.tool_groups_id')
tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', related='functional_tool_name_id.tool_groups_id',
store=True)
code = fields.Char('编码')
rfid = fields.Char('Rfid', readonly=True)
rfid_dismantle = fields.Char('Rfid(已拆解)', readonly=True)
name = fields.Char('名称')
tool_name_id = fields.Many2one('sf.tool.inventory', '功能刀具名称')
sf_cutting_tool_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀具型号')
barcode_id = fields.Many2one('stock.lot', string='功能刀具序列号', readonly=True)
barcode_id = fields.Many2one('stock.lot', string='序列号', readonly=True)
sf_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型',
group_expand='_read_group_mrs_cutting_tool_type_id', compute_sudo=True)
functional_tool_diameter = fields.Float(string='刀具直径(mm)', readonly=True)
knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', readonly=True)
functional_tool_diameter = fields.Float(string='刀具直径(mm)', readonly=True, digits=(10, 3))
knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', readonly=True, digits=(10, 3))
coarse_middle_thin = fields.Selection([("1", ""), ('2', ''), ('3', '')], string='粗/中/精', readonly=True)
new_former = fields.Selection([('0', ''), ('1', '')], string='新/旧', readonly=True)
tool_loading_length = fields.Float(string='总长度(mm)', readonly=True)
functional_tool_length = fields.Float(string='伸出长(mm)', readonly=True)
tool_loading_length = fields.Float(string='总长度(mm)', readonly=True, digits=(10, 3))
handle_length = fields.Float(string='刀柄长度(mm)', readonly=True, digits=(10, 3))
functional_tool_length = fields.Float(string='伸出长(mm)', readonly=True, digits=(10, 3))
effective_length = fields.Float(string='有效长(mm)', readonly=True)
tool_room_num = fields.Integer(string='刀具房数量', readonly=True)
line_edge_knife_library_num = fields.Integer(string='线边刀库数量', readonly=True)
machine_knife_library_num = fields.Integer(string='机内刀库数量', readonly=True)
tool_room_num = fields.Integer(string='刀具房数量', compute='_compute_num', store=True)
line_edge_knife_library_num = fields.Integer(string='线边刀库数量', compute='_compute_num', store=True)
machine_knife_library_num = fields.Integer(string='机内刀库数量', compute='_compute_num', store=True)
max_lifetime_value = fields.Integer(string='最大寿命值(min)', readonly=True)
alarm_value = fields.Integer(string='报警值(min)', readonly=True)
used_value = fields.Integer(string='已使用值(min)', readonly=True)
functional_tool_status = fields.Selection([('正常', '正常'), ('报警', '报警'), ('已拆除', '已拆除')],
string='状态', store=True, default='正常')
current_location_id = fields.Many2one('stock.location', string='当前位置', readonly=True)
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_location = fields.Selection(
[('组装后', '组装后'), ('刀具房', '刀具房'), ('线边刀库', '线边刀库'), ('机内刀库', '机内刀库')],
string='位置', compute='_compute_current_location_id', store=True)
image = fields.Binary('图片', readonly=True)
active = fields.Boolean(string='已归档', default=True)
safe_inventory_id = fields.Many2one('sf.real.time.distribution.of.functional.tools',
string='功能刀具安全库存', readonly=True)
def button_safe_inventory_id(self):
"""更新功能刀具关联的安全库存记录"""
tool_is = self.search([])
for item in tool_is:
item.safe_inventory_id = self.env['sf.real.time.distribution.of.functional.tools'].search(
[('functional_name_id', '=', item.tool_name_id.id)])[0]
@api.depends('barcode_id.quant_ids', 'functional_tool_status')
@api.depends('barcode_id.quant_ids', 'functional_tool_status', 'current_shelf_location_id')
def _compute_current_location_id(self):
for record in self:
if record.barcode_id.quant_ids:
for quant_id in record.barcode_id.quant_ids:
if quant_id.inventory_quantity_auto_apply > 0:
record.current_location_id = quant_id.location_id
if quant_id.location_id.name == '制造前':
record.current_location = '机内刀库'
else:
record.current_location = quant_id.location_id.name
if record.current_location_id:
record.sudo().get_location_num()
else:
record.current_location_id = False
record.current_location = False
if record.functional_tool_status == '已拆除':
record.current_location_id = False
record.current_location = False
record.tool_room_num = 0
record.line_edge_knife_library_num = 0
record.machine_knife_library_num = 0
else:
if record.barcode_id.quant_ids:
for quant_id in record.barcode_id.quant_ids:
if quant_id.inventory_quantity_auto_apply > 0:
record.current_location_id = quant_id.location_id
if quant_id.location_id.name == '制造前':
if not record.current_shelf_location_id:
record.current_location = '机内刀库'
else:
record.current_location = '线边刀库'
else:
record.current_location = '刀具房'
else:
record.current_location_id = False
record.current_location = False
def get_location_num(self):
@api.depends('current_location', 'functional_tool_status')
def _compute_num(self):
"""
计算库存位置数量
"""
for obj in self:
if obj.current_location_id:
if obj.functional_tool_status == '已拆除':
obj.tool_room_num = 0
obj.line_edge_knife_library_num = 0
obj.machine_knife_library_num = 0
if obj.current_location in ['刀具房']:
obj.tool_room_num = 1
elif "线边刀库" in obj.current_location:
obj.line_edge_knife_library_num = 1
elif "机内刀库" in obj.current_location:
obj.machine_knife_library_num = 1
else:
if obj.current_location_id:
obj.tool_room_num = 0
obj.line_edge_knife_library_num = 0
obj.machine_knife_library_num = 0
if obj.current_location in ['刀具房']:
obj.tool_room_num = 1
elif "线边刀库" in obj.current_location:
obj.line_edge_knife_library_num = 1
elif "机内刀库" in obj.current_location:
obj.machine_knife_library_num = 1
def tool_in_out_stock_location(self, location_id):
tool_room_id = self.env['stock.location'].search([('name', '=', '刀具房')])
pre_manufacturing_id = self.env['stock.location'].search([('name', '=', '制造前')])
for item in self:
# 中控反馈该位置有刀
if item:
# 系统该位置有刀
if location_id.product_sn_id:
# 中控反馈和系统中,该位置是同一把刀
if item.barcode_id == location_id.product_sn_id:
return True
# 中控反馈和系统中,该位置不是同一把刀
else:
# 原刀从线边出库
item.tool_in_out_stock_location_1(location_id, tool_room_id)
# 新刀入库到线边
item.create_stock_move(pre_manufacturing_id, location_id)
item.current_shelf_location_id = location_id.id
# 中控反馈该位置没有刀
else:
# 系统该位置有刀
if location_id.product_sn_id:
item.tool_in_out_stock_location_1(location_id, tool_room_id)
def tool_in_out_stock_location_1(self, location_id, tool_room_id):
tool = self.env['sf.functional.cutting.tool.entity'].search(
[('barcode_id', '=', location_id.product_sn_id.id)])
if tool.current_location == '线边刀库':
tool.create_stock_move(tool_room_id, False)
# 修改功能刀具的当前位置
tool.current_shelf_location_id = False
def create_stock_move(self, location_dest_id, destination_location_id):
# 创建库存移动记录
stock_move_id = self.env['stock.move'].sudo().create({
'name': '/',
'product_id': self.barcode_id.product_id.id,
'location_id': self.current_location_id.id,
'location_dest_id': location_dest_id.id,
'product_uom_qty': 1.00,
'state': 'done'
})
# 创建移动历史记录
stock_move_line_id = self.env['stock.move.line'].sudo().create({
'product_id': self.barcode_id.product_id.id,
'lot_id': self.barcode_id.id,
'move_id': stock_move_id.id,
'current_location_id': False if not self.current_shelf_location_id else self.current_shelf_location_id.id,
'destination_location_id': False if not destination_location_id else destination_location_id.id,
'qty_done': 1.0,
'state': 'done',
'functional_tool_type_id': self.sf_cutting_tool_type_id.id,
'diameter': self.functional_tool_diameter,
'knife_tip_r_angle': self.knife_tip_r_angle,
'code': self.code,
'rfid': self.rfid,
'functional_tool_name': self.name,
'tool_groups_id': self.tool_groups_id.id
})
return stock_move_id, stock_move_line_id
@api.model
def _read_group_mrs_cutting_tool_type_id(self, categories, domain, order):
@@ -198,12 +262,14 @@ class FunctionalCuttingToolEntity(models.Model):
机床当前刀库实时信息接口,功能刀具出库
"""
# 获取位置对象
location_inventory_id = self.current_location_id
stock_location_id = self.env['stock.location'].search([('name', '=', '制造前')])
# 创建功能刀具该批次/序列号 库存移动和移动历史
self.barcode_id.create_stock_quant(location_inventory_id, stock_location_id,
self.functional_tool_name_id.id, '机床装刀', self.functional_tool_name_id,
self.functional_tool_name_id.tool_groups_id)
self.create_stock_move(stock_location_id, False)
self.current_location_id = stock_location_id.id
self.current_shelf_location_id = False
# self.barcode_id.create_stock_quant(location_inventory_id, stock_location_id,
# self.functional_tool_name_id.id, '机床装刀', self.functional_tool_name_id,
# self.functional_tool_name_id.tool_groups_id)
# ==========刀具组接口==========
# def _register_functional_tool_groups(self, obj):
@@ -311,7 +377,7 @@ class StockMoveLine(models.Model):
functional_tool_name = fields.Char('刀具名称')
diameter = fields.Float(string='刀具直径(mm)')
knife_tip_r_angle = fields.Float(string='刀尖R角(mm)')
install_tool_time = fields.Datetime("刀具组装时间", default=fields.Datetime.now())
install_tool_time = fields.Datetime("刀具组装时间")
code = fields.Char('编码')
rfid = fields.Char('Rfid')
tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组')
@@ -324,6 +390,7 @@ class StockMoveLine(models.Model):
class RealTimeDistributionOfFunctionalTools(models.Model):
_name = 'sf.real.time.distribution.of.functional.tools'
_inherit = ['mail.thread']
_description = '功能刀具安全库存'
name = fields.Char('名称', readonly=True, compute='_compute_name', store=True)
@@ -333,15 +400,15 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
group_expand='_read_mrs_cutting_tool_type_ids', store=True)
diameter = fields.Float(string='刀具直径(mm)', readonly=False)
knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', readonly=False)
tool_stock_num = fields.Integer(string='刀具房数量')
side_shelf_num = fields.Integer(string='线边刀库数量')
on_tool_stock_num = fields.Integer(string='机内刀库数量')
tool_stock_total = fields.Integer(string='当前库存量', readonly=True)
tool_stock_num = fields.Integer(string='刀具房数量', compute='_compute_stock_num', store=True)
side_shelf_num = fields.Integer(string='线边刀库数量', compute='_compute_stock_num', store=True)
on_tool_stock_num = fields.Integer(string='机内刀库数量', compute='_compute_stock_num', store=True)
tool_stock_total = fields.Integer(string='当前库存量', compute='_compute_tool_stock_total', store=True)
min_stock_num = fields.Integer('最低库存量')
max_stock_num = fields.Integer('最高库存量')
batch_replenishment_num = fields.Integer('批次补货量', readonly=True, compute='_compute_batch_replenishment_num',
store=True)
unit = fields.Char('单位')
unit = fields.Char('单位', default="")
image = fields.Binary('图片', readonly=False)
coarse_middle_thin = fields.Selection([("1", ""), ('2', ''), ('3', '')], string='粗/中/精', readonly=False)
@@ -409,10 +476,6 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
def _compute_batch_replenishment_num(self):
for tool in self:
if tool:
# 计算刀具房数量、线边刀库数量、机内刀库数量
tool.sudo().get_stock_num(tool)
# 计算当前库存量
tool.sudo().tool_stock_total = tool.tool_stock_num + tool.side_shelf_num + tool.on_tool_stock_num
# 如果当前库存量小于最低库存量,计算批次补货量
tool.sudo().open_batch_replenishment_num(tool)
@@ -431,6 +494,38 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
else:
tool.sudo().batch_replenishment_num = 0
@api.depends('sf_functional_tool_entity_ids', 'sf_functional_tool_entity_ids.tool_room_num',
'sf_functional_tool_entity_ids.line_edge_knife_library_num',
'sf_functional_tool_entity_ids.machine_knife_library_num')
def _compute_stock_num(self):
"""
计算刀具房数量、线边刀库数量、机内刀库数量
"""
for tool in self:
if tool:
tool.tool_stock_num = 0
tool.side_shelf_num = 0
tool.on_tool_stock_num = 0
if tool.sf_functional_tool_entity_ids:
for cutting_tool in tool.sf_functional_tool_entity_ids:
if cutting_tool.tool_room_num > 0:
tool.tool_stock_num += 1
elif cutting_tool.line_edge_knife_library_num > 0:
tool.side_shelf_num += 1
elif cutting_tool.machine_knife_library_num > 0:
tool.on_tool_stock_num += 1
else:
tool.tool_stock_num = 0
tool.side_shelf_num = 0
tool.on_tool_stock_num = 0
@api.depends('tool_stock_num', 'side_shelf_num', 'on_tool_stock_num')
def _compute_tool_stock_total(self):
for tool in self:
if tool:
# 计算当前库存量
tool.tool_stock_total = tool.tool_stock_num + tool.side_shelf_num + tool.on_tool_stock_num
def create_functional_tool_assembly(self, tool):
"""
创建功能刀具组装单
@@ -451,27 +546,6 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
})
tool.sudo().sf_functional_tool_assembly_ids = [(4, functional_tool_assembly.id)]
def get_stock_num(self, tool):
"""
计算刀具房数量、线边刀库数量、机内刀库数量
"""
if tool:
tool.tool_stock_num = 0
tool.side_shelf_num = 0
tool.on_tool_stock_num = 0
if tool.sf_functional_tool_entity_ids:
for cutting_tool in tool.sf_functional_tool_entity_ids:
if cutting_tool.tool_room_num > 0:
tool.tool_stock_num += 1
elif cutting_tool.line_edge_knife_library_num > 0:
tool.side_shelf_num += 1
elif cutting_tool.machine_knife_library_num > 0:
tool.on_tool_stock_num += 1
else:
tool.tool_stock_num = 0
tool.side_shelf_num = 0
tool.on_tool_stock_num = 0
def create_or_edit_safety_stock(self, vals, sf_functional_tool_entity_ids):
"""
根据传入的信息新增或者更新功能刀具安全库存的信息

View File

@@ -33,23 +33,26 @@ def get_suitable_coolant_names(item):
class ToolDatasync(models.Model):
_name = 'sf.tool.datasync'
_description = '定时同步所有刀具'
_description = '定时同步所有刀具、夹具'
def _cron_tool_datasync_all(self):
try:
self.env['stock.lot'].sudo().sync_enroll_tool_material_stock_all()
logging.info("刀具物料序列号每日同步成功")
self.env['stock.lot'].sudo().sync_enroll_fixture_material_stock_all()
self.env['sf.tool.material.search'].sudo().sync_enroll_tool_material_all()
logging.info("刀具物料每日同步成功")
self.env['sf.fixture.material.search'].sudo().sync_enroll_fixture_material_all()
self.env['sf.functional.cutting.tool.entity'].sudo().esync_enroll_functional_tool_entity_all()
logging.info("功能刀具列表每日同步成功")
self.env['sf.functional.tool.warning'].sudo().sync_enroll_functional_tool_warning_all()
logging.info("功能刀具预警每日同步成功")
self.env['stock.move.line'].sudo().sync_enroll_functional_tool_move_all()
logging.info("功能刀具出入库记录每日同步成功")
self.env[
'sf.real.time.distribution.of.functional.tools'].sudo().sync_enroll_functional_tool_real_time_distribution_all()
logging.info("功能刀具安全库存每日同步成功")
logging.info("已全部同步完成!!!")
# self.env['sf.functional.tool.warning'].sudo().sync_enroll_functional_tool_warning_all()
# logging.info("功能刀具预警每日同步成功")
# self.env['stock.move.line'].sudo().sync_enroll_functional_tool_move_all()
# logging.info("功能刀具出入库记录每日同步成功")
# self.env['sf.real.time.distribution.of.functional.tools'].sudo().sync_enroll_functional_tool_real_time_distribution_all()
# logging.info("功能刀具安全库存每日同步成功")
except Exception as e:
logging.info("捕获错误信息:%s" % e)
raise ValidationError("数据错误导致同步失败,请联系管理员")
@@ -59,24 +62,25 @@ class StockLot(models.Model):
_inherit = 'stock.lot'
_description = '刀具物料序列号注册'
def enroll_tool_material_stock(self):
logging.info('调用刀具物料序列号注册接口: enroll_tool_material_stock()')
sf_sync_config = self.env['res.config.settings'].get_values()
token = sf_sync_config['token']
sf_secret_key = sf_sync_config['sf_secret_key']
headers = Common.get_headers(self, token, sf_secret_key)
str_url = sf_sync_config['sf_url'] + "/api/tool_material_stock/create"
objs_all = self.env['stock.lot'].search([('id', '=', self.id), ('active', 'in', [True, False])])
self._get_sync_stock_lot(objs_all, str_url, token, headers)
# def enroll_tool_material_stock(self):
# logging.info('调用刀具物料序列号注册接口: enroll_tool_material_stock()')
# sf_sync_config = self.env['res.config.settings'].get_values()
# token = sf_sync_config['token']
# sf_secret_key = sf_sync_config['sf_secret_key']
# headers = Common.get_headers(self, token, sf_secret_key)
# str_url = sf_sync_config['sf_url'] + "/api/tool_material_stock/create"
# objs_all = self.env['stock.lot'].search([('id', '=', self.id), ('active', 'in', [True, False])])
# self._get_sync_stock_lot(objs_all, str_url, token, headers)
def sync_enroll_tool_material_stock_all(self):
logging.info('调用刀具物料序列号注册接口: sync_enroll_tool_material_stock_all()')
logging.info('调用 sync_enroll_tool_material_stock_all 同步接口')
sf_sync_config = self.env['res.config.settings'].get_values()
token = sf_sync_config['token']
sf_secret_key = sf_sync_config['sf_secret_key']
headers = Common.get_headers(self, token, sf_secret_key)
str_url = sf_sync_config['sf_url'] + "/api/tool_material_stock/create"
objs_all = self.env['stock.lot'].search([('rfid', '!=', False)])
product_ids = self.env['product.product'].sudo().search([('categ_type', '=', '刀具')]).ids
objs_all = self.env['stock.lot'].search([('product_id', 'in', product_ids)])
self._get_sync_stock_lot(objs_all, str_url, token, headers)
def _get_sync_stock_lot(self, objs_all, str_url, token, headers):
@@ -86,6 +90,7 @@ class StockLot(models.Model):
for item in objs_all:
val = {
'name': item.name,
'qty': item.product_qty,
'tool_material_status': item.tool_material_status,
'location': [] if not item.quant_ids else item.quant_ids[-1].location_id.name,
'tool_material_search_id': item.tool_material_search_id.id,
@@ -95,12 +100,13 @@ class StockLot(models.Model):
r = requests.post(str_url, json={}, data={'kw': kw, 'token': token}, headers=headers)
ret = r.json()
if ret.get('code') == 200:
return '刀具物料序列号注册成功'
logging.info("刀具物料序列号每日同步成功")
return '刀具(夹具)物料序列号注册成功'
else:
logging.info("没有注册刀具物料序列号信息")
logging.info("没有刀具物料序列号信息")
except Exception as e:
logging.info("捕获错误信息:%s" % e)
logging.info("刀具物料序列号同步失败:%s" % e)
class ToolMaterial(models.Model):
_inherit = 'sf.tool.material.search'
@@ -108,18 +114,18 @@ class ToolMaterial(models.Model):
crea_url = '/api/tool_material/create'
def enroll_tool_material(self):
logging.info('调用刀具物料注册接口: enroll_tool_material()')
sf_sync_config = self.env['res.config.settings'].get_values()
token = sf_sync_config['token']
sf_secret_key = sf_sync_config['sf_secret_key']
headers = Common.get_headers(self, token, sf_secret_key)
str_url = sf_sync_config['sf_url'] + self.crea_url
objs_all = self.search([('id', '=', self.id)])
self._get_sync_tool_material_search(objs_all, str_url, token, headers)
# def enroll_tool_material(self):
# logging.info('调用刀具物料注册接口: enroll_tool_material()')
# sf_sync_config = self.env['res.config.settings'].get_values()
# token = sf_sync_config['token']
# sf_secret_key = sf_sync_config['sf_secret_key']
# headers = Common.get_headers(self, token, sf_secret_key)
# str_url = sf_sync_config['sf_url'] + self.crea_url
# objs_all = self.search([('id', '=', self.id)])
# self._get_sync_tool_material_search(objs_all, str_url, token, headers)
def sync_enroll_tool_material_all(self):
logging.info('调用刀具物料注册接口: sync_enroll_tool_material_all()')
logging.info('调用 sync_enroll_tool_material_all 同步接口')
sf_sync_config = self.env['res.config.settings'].get_values()
token = sf_sync_config['token']
sf_secret_key = sf_sync_config['sf_secret_key']
@@ -150,19 +156,12 @@ class ToolMaterial(models.Model):
r = requests.post(str_url, json={}, data={'kw': kw, 'token': token}, headers=headers)
ret = r.json()
if ret.get('code') == 200:
logging.info("刀具物料每日同步成功")
return '刀具物料注册成功'
else:
logging.info('没有注册刀具物料信息')
except Exception as e:
logging.info("捕获错误信息:%s" % e)
@api.model_create_multi
def create(self, vals_list):
records = super(ToolMaterial, self).create(vals_list)
for record in records:
if record:
record.enroll_tool_material()
return records
logging.info("刀具物料同步失败:%s" % e)
class FunctionalCuttingToolEntity(models.Model):
@@ -172,18 +171,18 @@ class FunctionalCuttingToolEntity(models.Model):
crea_url = "/api/functional_tool_entity/create"
# 注册同步功能刀具列表
def enroll_functional_tool_entity(self):
logging.info('调用功能刀具列表注册接口: enroll_functional_tool_entity()')
sf_sync_config = self.env['res.config.settings'].get_values()
token = sf_sync_config['token']
sf_secret_key = sf_sync_config['sf_secret_key']
headers = Common.get_headers(self, token, sf_secret_key)
str_url = sf_sync_config['sf_url'] + self.crea_url
objs_all = self.env['sf.functional.cutting.tool.entity'].search([('id', '=', self.id)])
self._get_sync_functional_cutting_tool_entity(objs_all, str_url, token, headers)
# def enroll_functional_tool_entity(self):
# logging.info('调用功能刀具列表注册接口: enroll_functional_tool_entity()')
# sf_sync_config = self.env['res.config.settings'].get_values()
# token = sf_sync_config['token']
# sf_secret_key = sf_sync_config['sf_secret_key']
# headers = Common.get_headers(self, token, sf_secret_key)
# str_url = sf_sync_config['sf_url'] + self.crea_url
# objs_all = self.env['sf.functional.cutting.tool.entity'].search([('id', '=', self.id)])
# self._get_sync_functional_cutting_tool_entity(objs_all, str_url, token, headers)
def esync_enroll_functional_tool_entity_all(self):
logging.info('调用功能刀具列表注册接口: esync_enroll_functional_tool_entity_all()')
logging.info('调用 esync_enroll_functional_tool_entity_all 同步接口')
sf_sync_config = self.env['res.config.settings'].get_values()
token = sf_sync_config['token']
sf_secret_key = sf_sync_config['sf_secret_key']
@@ -210,6 +209,7 @@ class FunctionalCuttingToolEntity(models.Model):
'coarse_middle_thin': item.coarse_middle_thin,
'new_former': item.new_former,
'tool_loading_length': item.tool_loading_length,
'handle_length': item.handle_length,
'functional_tool_length': item.functional_tool_length,
'effective_length': item.effective_length,
'max_lifetime_value': item.max_lifetime_value,
@@ -234,33 +234,19 @@ class FunctionalCuttingToolEntity(models.Model):
'blade_tip_characteristics_name': item.blade_tip_characteristics_id.name,
'handle_type_name': item.handle_type_id.name,
'cutting_direction_names': get_cutting_direction_names(item),
'suitable_coolant_names': get_suitable_coolant_names(item),
'active': item.active,
'suitable_coolant_names': get_suitable_coolant_names(item)
}
functional_tool_list.append(val)
kw = json.dumps(functional_tool_list, ensure_ascii=False)
r = requests.post(str_url, json={}, data={'kw': kw, 'token': token}, headers=headers)
ret = r.json()
if ret.get('code') == 200:
logging.info("功能刀具列表每日同步成功")
return "功能刀具注册成功"
else:
logging.info('没有注册功能刀具信息')
except Exception as e:
logging.info("捕获错误信息:%s" % e)
@api.model_create_multi
def create(self, vals_list):
records = super(FunctionalCuttingToolEntity, self).create(vals_list)
for record in records:
if record:
record.enroll_functional_tool_entity()
return records
def write(self, vals):
res = super().write(vals)
if vals.get('current_location'):
self.enroll_functional_tool_entity()
return res
logging.info("功能刀具同步失败:%s" % e)
class FunctionalToolWarning(models.Model):
@@ -332,14 +318,6 @@ class FunctionalToolWarning(models.Model):
except Exception as e:
logging.info("捕获错误信息:%s" % e)
@api.model_create_multi
def create(self, vals_list):
records = super(FunctionalToolWarning, self).create(vals_list)
for record in records:
if record:
record.enroll_functional_tool_warning()
return records
class StockMoveLine(models.Model):
_inherit = 'stock.move.line'
@@ -401,14 +379,6 @@ class StockMoveLine(models.Model):
except Exception as e:
logging.info("捕获错误信息:%s" % e)
@api.model_create_multi
def create(self, vals_list):
records = super(StockMoveLine, self).create(vals_list)
for record in records:
if record.functional_tool_name_id:
record.enroll_functional_tool_move()
return records
class RealTimeDistributionFunctionalTools(models.Model):
_inherit = 'sf.real.time.distribution.of.functional.tools'
@@ -481,17 +451,3 @@ class RealTimeDistributionFunctionalTools(models.Model):
logging.info('没有注册功能刀具出入库记录信息')
except Exception as e:
logging.info("捕获错误信息:%s" % e)
@api.model_create_multi
def create(self, vals_list):
records = super(RealTimeDistributionFunctionalTools, self).create(vals_list)
for record in records:
if record:
record.enroll_functional_tool_real_time_distribution()
return records
def write(self, vals):
res = super().write(vals)
if vals.get('sf_functional_tool_entity_ids') or vals.get('min_stock_num') or vals.get('max_stock_num'):
self.enroll_functional_tool_real_time_distribution()
return res

View File

@@ -44,65 +44,79 @@ class SfMaintenanceEquipment(models.Model):
# ==========机床当前刀库实时信息接口==========
def register_equipment_tool(self):
config = self.env['res.config.settings'].get_values()
# token = sf_sync_config['token'Ba F2CF5DCC-1A00-4234-9E95-65603F70CC8A]
headers = {'Authorization': config['center_control_Authorization']}
crea_url = config['center_control_url'] + "/AutoDeviceApi/GetToolInfos"
params = {"DeviceId": self.name}
r = requests.get(crea_url, params=params, headers=headers)
ret = r.json()
logging.info('register_equipment_tool:%s' % ret)
datas = ret['Datas']
self.write_maintenance_equipment_tool(datas)
if ret['Succeed']:
return "机床当前刀库实时信息指令发送成功"
else:
raise ValidationError("机床当前刀库实时信息指令发送失败")
try:
config = self.env['res.config.settings'].get_values()
# token = sf_sync_config['token'Ba F2CF5DCC-1A00-4234-9E95-65603F70CC8A]
headers = {'Authorization': config['center_control_Authorization']}
crea_url = config['center_control_url'] + "/AutoDeviceApi/GetToolInfos"
params = {"DeviceId": self.name}
r = requests.get(crea_url, params=params, headers=headers)
ret = r.json()
logging.info('register_equipment_tool:%s' % ret)
datas = ret['Datas']
self.write_maintenance_equipment_tool(datas)
if ret['Succeed']:
return "机床当前刀库实时信息指令发送成功"
else:
raise ValidationError("机床当前刀库实时信息指令发送失败")
except Exception as e:
logging.info("register_equipment_tool()捕获错误信息:%s" % e)
def write_maintenance_equipment_tool(self, datas):
if datas:
for data in datas:
maintenance_equipment_id = self.search([('name', '=', data['DeviceId'])])
if maintenance_equipment_id:
tool_id = '%s%s' % (data['ToolId'][0:1], data['ToolId'][1:].zfill(2))
equipment_tool_id = self.env['maintenance.equipment.tool'].sudo().search(
[('equipment_id', '=', maintenance_equipment_id.id), ('code', '=', tool_id)])
functional_tool_id = self.env['sf.functional.cutting.tool.entity'].sudo().search(
[('rfid', '=', data['RfidCode'])])
if functional_tool_id:
# 查询该功能刀具是否已经装在机床内其他位置,如果是就删除
equipment_tools = self.env['maintenance.equipment.tool'].sudo().search(
[('functional_tool_name_id', '=', functional_tool_id.id), ('code', '!=', tool_id)])
if equipment_tools:
for item in equipment_tools:
item.write({
'functional_tool_name_id': False,
'tool_install_time': None
})
try:
if datas:
# 清除设备机床刀位的刀具信息
for obj in self.product_template_ids:
obj.write({
'functional_tool_name_id': False,
'tool_install_time': None
})
for data in datas:
maintenance_equipment_id = self.search([('name', '=', data['DeviceId'])])
if maintenance_equipment_id:
tool_id = '%s%s' % (data['ToolId'][0:1], data['ToolId'][1:].zfill(2))
equipment_tool_id = self.env['maintenance.equipment.tool'].sudo().search(
[('equipment_id', '=', maintenance_equipment_id.id), ('code', '=', tool_id)])
functional_tool_id = self.env['sf.functional.cutting.tool.entity'].sudo().search(
[('rfid', '=', data['RfidCode'])])
if functional_tool_id:
if len(functional_tool_id) > 1:
functional_tool_id = functional_tool_id[-1]
# 查询该功能刀具是否已经装在机床内其他位置,如果是就删除
equipment_tools = self.env['maintenance.equipment.tool'].sudo().search(
[('functional_tool_name_id', '=', functional_tool_id.id), ('code', '!=', tool_id)])
if equipment_tools:
for item in equipment_tools:
item.write({
'functional_tool_name_id': False,
'tool_install_time': None
})
else:
logging.info('Rfid为【%s】的功能刀具不存在!' % data['RfidCode'])
time = None
if data['AddDatetime']:
datatime = str(data['AddDatetime'])
time = fields.Datetime.from_string(datatime[0:10] + ' ' + datatime[11:19])
if equipment_tool_id and functional_tool_id:
tool_install_time = {'Nomal': '正常', 'Warning': '报警'}
equipment_tool_id.write({
'functional_tool_name_id': functional_tool_id.id,
'tool_install_time': time
})
if functional_tool_id.current_location != '机内刀库':
# 对功能刀具进行移动到生产线
functional_tool_id.tool_inventory_displacement_out()
functional_tool_id.write({
'max_lifetime_value': data['MaxLife'],
'used_value': data['UseLife'],
'functional_tool_status': tool_install_time.get(data['State'])
})
else:
logging.info('Rfid为%s的功能刀具不存在!' % data['RfidCode'])
time = None
if data['AddDatetime']:
datatime = str(data['AddDatetime'])
time = fields.Datetime.from_string(datatime[0:10] + ' ' + datatime[11:19])
if equipment_tool_id and functional_tool_id:
tool_install_time = {'Nomal': '正常', 'Warning': '报警'}
equipment_tool_id.write({
'functional_tool_name_id': functional_tool_id.id,
'tool_install_time': time
})
if functional_tool_id.current_location_id.name != '制造前':
# 对功能刀具进行出库到生产线
functional_tool_id.tool_inventory_displacement_out()
functional_tool_id.write({
'max_lifetime_value': data['MaxLife'],
'used_value': data['UseLife'],
'functional_tool_status': tool_install_time.get(data['State'])
})
else:
raise ValidationError('获取的【%s】设备不存在!!!' % data['DeviceId'])
else:
raise ValidationError('没有获取到刀具库信息!!!')
logging.info('获取的%s设备不存在!' % data['DeviceId'])
else:
logging.info('没有获取到【%s】设备的刀具库信息!!!' % self.name)
except Exception as e:
logging.info("write_maintenance_equipment_tool()捕获错误信息:%s" % e)
class StockLot(models.Model):
@@ -128,12 +142,6 @@ class StockLot(models.Model):
record.tool_material_status = '报废'
else:
record.tool_material_status = '未入库'
if record.tool_material_search_id:
# 注册刀具物料状态到cloud平台
record.enroll_tool_material_stock()
elif record.fixture_material_search_id:
# 注册夹具物料状态到cloud平台
record.enroll_fixture_material_stock()
@api.model
def name_search(self, name='', args=None, operator='ilike', limit=100):

View File

@@ -0,0 +1,23 @@
from odoo import api, fields, models, _
class ShelfLocation(models.Model):
_inherit = 'sf.shelf.location'
tool_rfid = fields.Char('Rfid', compute='_compute_tool', store=True)
tool_name_id = fields.Many2one('sf.functional.cutting.tool.entity', string='功能刀具名称', compute='_compute_tool',
store=True)
@api.depends('product_id')
def _compute_tool(self):
for item in self:
if item.product_id:
if item.product_id.categ_id.name == '功能刀具':
tool_id = self.env['sf.functional.cutting.tool.entity'].sudo().search(
[('barcode_id', '=', item.product_sn_id.id)])
if tool_id:
item.tool_rfid = tool_id.rfid
item.tool_name_id = tool_id.id
continue
item.tool_rfid = ''
item.tool_name_id = False

View File

@@ -0,0 +1,38 @@
from odoo import fields, models, api
class FunctionalCuttingToolEntity(models.Model):
_inherit = 'sf.functional.cutting.tool.entity'
def button_safe_inventory_id(self):
"""更新功能刀具关联的安全库存记录"""
tool_is = self.search([])
for item in tool_is:
item.safe_inventory_id = self.env['sf.real.time.distribution.of.functional.tools'].search(
[('functional_name_id', '=', item.tool_name_id.id)])[0]
class FunctionalToolAssembly(models.Model):
_inherit = 'sf.functional.tool.assembly'
def put_assembly_order_code(self):
assembly_list = self.search([('assemble_status', '=', '0')], order="id asc")
for item in assembly_list:
code = item.assembly_order_code[-9:-5]
if item.loading_task_source == '0':
code = 'C' + code
elif item.loading_task_source == '1':
code = 'J' + code
elif item.loading_task_source == '2':
code = 'K' + code
functional_tool_assembly = self.env['sf.functional.tool.assembly'].sudo().search(
[('loading_task_source', '=', item.loading_task_source),
('assembly_order_code', 'ilike', code)], limit=1, order="id desc")
if not functional_tool_assembly:
num = "%04d" % 1
else:
m = int(functional_tool_assembly.assembly_order_code[-3:]) + 1
num = "%04d" % m
item.assembly_order_code = "%s%s" % (code, num)

View File

@@ -45,8 +45,8 @@ class ToolMaterial(models.Model):
record.have_been_used_num = have_been_used_num
record.scrap_num = scrap_num
record.number = usable_num + have_been_used_num + scrap_num
# 更新数据到cloud的动态数据
record.enroll_tool_material()
@api.model
def _read_group_cutting_tool_material_id(self, categories, domain, order):

View File

@@ -6,7 +6,7 @@
<field name="name">sf.functional.cutting.tool.entity.list.tree</field>
<field name="model">sf.functional.cutting.tool.entity</field>
<field name="arch" type="xml">
<tree string="功能刀具列表" create="0" edit="0" delete="0">
<tree string="功能刀具" create="0" edit="0" delete="0">
<field name="barcode_id" invisible="1"/>
<field name="rfid"/>
<field name="tool_name_id"/>
@@ -19,15 +19,16 @@
<field name="tool_loading_length" optional="hide"/>
<field name="functional_tool_length" optional="hide"/>
<field name="effective_length" optional="hide"/>
<field name="tool_room_num"/>
<field name="line_edge_knife_library_num"/>
<field name="machine_knife_library_num"/>
<field name="tool_room_num" optional="hide"/>
<field name="line_edge_knife_library_num" optional="hide"/>
<field name="machine_knife_library_num" optional="hide"/>
<field name="max_lifetime_value"/>
<field name="alarm_value"/>
<field name="used_value"/>
<field name="functional_tool_status"/>
<field name="current_location" string="当前位置"/>
<field name="current_location_id" optional="hide"/>
<field name="current_location_id" invisible="1"/>
<field name="current_location" optional="hide"/>
<field name="sf_cutting_tool_type_id" invisible="True"/>
</tree>
@@ -46,8 +47,8 @@
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="button_safe_inventory_id" string="更新功能刀具关联的安全库存记录"
type="object" class="btn-primary"/>
<!-- <button name="button_safe_inventory_id" string="更新功能刀具关联的安全库存记录"-->
<!-- type="object" class="btn-primary"/>-->
<button class="oe_stat_button" groups="sf_base.group_sf_mrp_user"
name="open_functional_tool_warning"
icon="fa-list-ul"
@@ -86,7 +87,7 @@
</div>
<group>
<group>
<field name="barcode_id" invisible="1"/>
<field name="barcode_id" invisible="0"/>
<field name="rfid" readonly="1"
attrs="{'invisible': [('functional_tool_status', '=', '已拆除')]}"/>
<field name="rfid_dismantle" readonly="1"
@@ -115,7 +116,7 @@
options="{'no_create': True, 'no_quick_create': True}"/>
<field name="cutting_tool_cutterhead_model_id"
options="{'no_create': True, 'no_quick_create': True}"/>
<field name="safe_inventory_id" readonly="0"/>
<field name="safe_inventory_id" invisible="1"/>
</group>
<group>
<field name="image" nolabel="1" widget="image"/>
@@ -155,24 +156,28 @@
<group>
<field name="functional_tool_diameter"/>
<field name="knife_tip_r_angle"/>
<field name="whether_standard_knife"/>
<field name="coarse_middle_thin"/>
<field name="tool_loading_length"/>
<field name="handle_length"/>
<field name="functional_tool_length"/>
<field name="max_lifetime_value"/>
<field name="alarm_value"/>
<field name="used_value"/>
<field name="used_value" invisible="1"/>
<field name="current_location_id" invisible="1"/>
<field name="current_location"/>
</group>
<group>
<field name="tool_loading_length"/>
<field name="functional_tool_length"/>
<field name="effective_length"/>
<field name="L_D_number"/>
<field name="hiding_length"/>
<field name="whether_standard_knife"/>
<field name="coarse_middle_thin"/>
<field name="effective_length" invisible="1"/>
<field name="L_D_number" invisible="1"/>
<field name="hiding_length" invisible="1"/>
<field name="new_former"/>
<field name="cut_time" attrs="{'invisible': [('new_former','=','0')]}"/>
<field name="cut_length" attrs="{'invisible': [('new_former','=','0')]}"/>
<field name="cut_number" attrs="{'invisible': [('new_former','=','0')]}"/>
<field name="current_location_id" string="当前位置"/>
<field name="current_location" string="当前位置"/>
<field name="current_shelf_location_id" string="当前货位"
attrs="{'invisible': [('current_shelf_location_id', '=', False)]}"/>
</group>
</group>
</page>
@@ -188,19 +193,34 @@
<field name="arch" type="xml">
<search>
<field name="rfid"/>
<field name="barcode_id"/>
<field name="tool_name_id"/>
<field name="functional_tool_diameter"/>
<field name="knife_tip_r_angle"/>
<filter string="刀具房" name="tool_room" domain="[('current_location', '=', '刀具房')]"/>
<filter string="线边刀库" name="storage_area" domain="[('current_location', '=', '线边刀库')]"/>
<filter string="机内刀库" name="machine_knife_library"
domain="[('current_location', '=', '机内刀库')]"/>
<separator/>
<filter string="正常" name="normal" domain="[('functional_tool_status', '=', '正常')]"/>
<filter string="报警" name="alarm" domain="[('functional_tool_status', '=', '报警')]"/>
<separator/>
<filter string="未拆除" name="no_state_removed"
domain="[('functional_tool_status', '!=', '已拆除')]"/>
<filter string="已拆除" name="state_removed" domain="[('functional_tool_status', '=', '已拆除')]"/>
<separator/>
<filter string="已归档" name="inactive" domain="[('active', '=', False)]"/>
<searchpanel>
<field name="sf_cutting_tool_type_id" icon="fa-building" enable_counters="1"/>
<field name="current_location" icon="fa-building" enable_counters="1"/>
<field name="functional_tool_status" icon="fa-building" enable_counters="1"/>
<field name="sf_cutting_tool_type_id" icon="fa-building" enable_counters="1"/>
</searchpanel>
<group expand="0">
<filter string="功能刀具名称" name="tool_name" domain="[]"
context="{'group_by': 'tool_name_id'}"/>
<filter string="刀具组" name="tool_groups" domain="[]"
context="{'group_by': 'tool_groups_id'}"/>
<filter string="当前位置" name="current_location" domain="[]"
context="{'group_by': 'current_location'}"/>
</group>
</search>
</field>
</record>
@@ -296,11 +316,10 @@
<tree>
<field name="name" invisible="1"/>
<field name="functional_name_id"/>
<field name="sf_cutting_tool_type_id" invisible="True"/>
<field name="tool_groups_id"/>
<field name="diameter"/>
<field name="knife_tip_r_angle"/>
<field name="coarse_middle_thin"/>
<field name="coarse_middle_thin" optional="hide"/>
<field name="tool_stock_num"/>
<field name="side_shelf_num"/>
<field name="on_tool_stock_num"/>
@@ -309,6 +328,8 @@
<field name="max_stock_num"/>
<field name="batch_replenishment_num"/>
<field name="unit"/>
<field name="sf_cutting_tool_type_id" invisible="True"/>
</tree>
</field>
</record>
@@ -410,6 +431,10 @@
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
@@ -420,21 +445,20 @@
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="sf_cutting_tool_type_id" invisible="True"/>
<field name="tool_groups_id"/>
<field name="sf_cutting_tool_type_id"/>
<field name="diameter"/>
<field name="knife_tip_r_angle"/>
<field name="tool_stock_num"/>
<field name="side_shelf_num"/>
<field name="on_tool_stock_num"/>
<field name="tool_stock_total"/>
<field name="min_stock_num"/>
<field name="max_stock_num"/>
<field name="batch_replenishment_num"/>
<field name="unit"/>
<filter string="需补货" name="batch_replenishment" domain="[('batch_replenishment_num', '>', 0)]"/>
<separator/>
<filter string="已归档" name="inactive" domain="[('active', '=', False)]"/>
<searchpanel>
<field name="sf_cutting_tool_type_id" enable_counters="1" icon="fa-building"/>
</searchpanel>
<group expand="0">
<filter string="刀具组" name="tool_groups" domain="[]"
context="{'group_by': 'tool_groups_id'}"/>
</group>
</search>
</field>
</record>
@@ -463,7 +487,9 @@
<field name="knife_tip_r_angle"/>
<field name="install_tool_time"/>
<field name="location_id"/>
<field name="current_location_id"/>
<field name="location_dest_id"/>
<field name="destination_location_id"/>
<field name="date"/>
<field name="qty_done" string="数量"/>
<field name="functional_tool_type_id" invisible="True"/>
@@ -478,21 +504,20 @@
<field name="model">stock.move.line</field>
<field name="arch" type="xml">
<search>
<field name="reference"/>
<field name="lot_id"/>
<field name="rfid"/>
<field name="functional_tool_name"/>
<field name="diameter"/>
<field name="knife_tip_r_angle"/>
<field name="install_tool_time"/>
<field name="location_id"/>
<field name="location_dest_id"/>
<field name="date"/>
<field name="qty_done"/>
<field name="functional_tool_type_id" invisible="True"/>
<field name="reference"/>
<field name="lot_id"/>
<searchpanel>
<field name="functional_tool_type_id" enable_counters="1" icon="fa-building"/>
</searchpanel>
<group expand="0">
<filter string="功能刀具名称" name="functional_tool_name" domain="[]"
context="{'group_by': 'functional_tool_name'}"/>
<filter string="日期" name="date" domain="[]" context="{'group_by': 'date'}"/>
</group>
</search>
</field>
</record>
@@ -506,7 +531,7 @@
ref="sf_tool_management.sf_inbound_and_outbound_records_of_functional_tools_view_tree"/>
<field name="search_view_id"
ref="sf_tool_management.sf_inbound_and_outbound_records_of_functional_tools_view_search"/>
<field name="domain">[('functional_tool_name_id', '!=', False)]</field>
<field name="domain">[('functional_tool_name', '!=', False)]</field>
</record>
</data>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_shelf_location_tool" model="ir.ui.view">
<field name="name">sf.shelf.location.form.tool</field>
<field name="model">sf.shelf.location</field>
<field name="inherit_id" ref="sf_warehouse.view_shelf_location_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='product_id']" position="after">
<field name="tool_rfid" attrs="{'invisible': [('tool_name_id', '=', False)]}"/>
<field name="tool_name_id" attrs="{'invisible': [('tool_name_id', '=', False)]}"/>
</xpath>
</field>
</record>
</odoo>

View File

@@ -376,6 +376,10 @@
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
@@ -430,17 +434,17 @@
<field name="code" optional="hide"/>
<field name="functional_tool_name"/>
<field name="tool_groups_id"/>
<field name="functional_tool_diameter" string="刀具直径"/>
<field name="functional_tool_diameter" string="刀具直径(mm)"/>
<field name="knife_tip_r_angle"/>
<field name="coarse_middle_thin" optional="hide"/>
<field name="new_former" optional="hide"/>
<field name="tool_loading_length" optional="hide"/>
<field name="functional_tool_length" optional="hide"/>
<field name="effective_length" optional="hide"/>
<field name="effective_length" invisible="1"/>
<field name="loading_task_source" string="任务来源"/>
<field name="use_tool_time"/>
<field name="production_line_name_id"/>
<field name="machine_tool_name_id"/>
<field name="production_line_name_id" optional="hide"/>
<field name="machine_tool_name_id" optional="hide"/>
<field name="applicant"/>
<field name="apply_time"/>
<field name="assemble_status" optional="hide"/>
@@ -451,36 +455,9 @@
<field name="whether_standard_knife" invisible="True"/>
<field name="reason_for_applying" invisible="True"/>
<field name="functional_tool_type_id" invisible="True"/>
<!-- <field name="functional_tool_cutting_type" invisible="True"/>-->
<button string="组装"
name="%(sf_tool_management.sf_functional_tool_assembly_order_act)d"
type="action"
context="{'default_name':name,
'default_assembly_order_code':assembly_order_code,
'default_production_line_name_id':production_line_name_id,
'default_machine_tool_name_id':machine_tool_name_id,
'default_cutter_spacing_code_id':cutter_spacing_code_id,
'default_functional_tool_name':functional_tool_name,
'default_functional_tool_type_id':functional_tool_type_id,
'default_tool_groups_id': tool_groups_id,
'default_functional_tool_diameter':functional_tool_diameter,
'default_knife_tip_r_angle':knife_tip_r_angle,
'default_tool_loading_length':tool_loading_length,
'default_functional_tool_length':functional_tool_length,
'default_effective_length':effective_length,
'default_whether_standard_knife':whether_standard_knife,
'default_coarse_middle_thin':coarse_middle_thin,
'default_new_former':new_former,
'default_use_tool_time':use_tool_time,
'default_reason_for_applying':reason_for_applying,
}"
attrs="{'invisible': [('assemble_status', '!=', '0')]}" groups="sf_base.group_sf_mrp_user"
<button string="组装" name="put_start_preset" type="object"
attrs="{'invisible': [('assemble_status', '!=', '0')]}"
class="btn-primary"/>
<!-- <button string="组装单打印" name="assemble_single_print" type="object"-->
<!-- groups="sf_base.group_sf_mrp_user"-->
<!-- attrs="{'invisible': [('assemble_status', '=', '0')]}" class="btn-primary"-->
<!-- confirm="是否确认打印组装单"/>-->
</tree>
</field>
</record>
@@ -491,35 +468,11 @@
<field name="arch" type="xml">
<form create="0" delete="0" edit="0">
<header>
<button string="组装"
name="%(sf_tool_management.sf_functional_tool_assembly_order_act)d"
type="action" groups="sf_base.group_sf_mrp_user"
context="{'default_name':name,
'default_assembly_order_code':assembly_order_code,
'default_production_line_name_id':production_line_name_id,
'default_machine_tool_name_id':machine_tool_name_id,
'default_cutter_spacing_code_id':cutter_spacing_code_id,
'default_functional_tool_name':functional_tool_name,
'default_functional_tool_type_id':functional_tool_type_id,
'default_tool_groups_id': tool_groups_id,
'default_functional_tool_diameter':functional_tool_diameter,
'default_knife_tip_r_angle':knife_tip_r_angle,
'default_tool_loading_length':tool_loading_length,
'default_functional_tool_length':functional_tool_length,
'default_effective_length':effective_length,
'default_whether_standard_knife':whether_standard_knife,
'default_coarse_middle_thin':coarse_middle_thin,
'default_new_former':new_former,
'default_use_tool_time':use_tool_time,
'default_reason_for_applying':reason_for_applying,
}"
<!-- <button string="修改编码" name="put_assembly_order_code" type="object"-->
<!-- class="btn-primary" confirm="是否确认修改编码"/>-->
<button string="组装" name="put_start_preset" type="object"
attrs="{'invisible': [('assemble_status', '!=', '0')]}"
class="btn-primary"/>
<!-- <button string="组装单打印" name="assemble_single_print" type="object"-->
<!-- groups="sf_base.group_sf_mrp_user"-->
<!-- attrs="{'invisible': [('assemble_status', '=', '0')]}" class="btn-primary"-->
<!-- confirm="是否确认打印组装单"/>-->
<field name="assemble_status" widget="statusbar" statusbar_visible="0,1"/>
</header>
<sheet>
@@ -544,7 +497,7 @@
</group>
</group>
<notebook>
<page string="组装信息" attrs="{'invisible': [('assemble_status', '=' ,'0')]}">
<page string="组装信息" attrs="{'invisible': [('assemble_status', '=', '0')]}">
<group col="1">
<group col="1">
<group>
@@ -557,9 +510,9 @@
<field name="after_assembly_functional_tool_type_id"
string="功能刀具类型"/>
<field name="tool_groups_id"/>
<field name="after_assembly_functional_tool_diameter"
string="刀具直径(mm)"/>
<field name="after_assembly_knife_tip_r_angle" string="刀尖R角(mm)"/>
<field name="after_assembly_whether_standard_knife"
string="是否标准刀"/>
<field name="after_assembly_coarse_middle_thin" string="粗/中/精"/>
<field name="after_assembly_new_former" string="新/旧"/>
<field name="cut_time"
attrs="{'invisible': [('after_assembly_new_former', '=', '0')]}"/>
@@ -569,19 +522,22 @@
attrs="{'invisible': [('after_assembly_new_former', '=', '0')]}"/>
</group>
<group>
<field name="after_assembly_whether_standard_knife"
string="是否标准刀"/>
<field name="after_assembly_coarse_middle_thin" string="粗/中/精"/>
<field name="after_assembly_functional_tool_diameter"
string="刀具直径(mm)"/>
<field name="after_assembly_knife_tip_r_angle" string="刀尖R角(mm)"/>
<field name="after_assembly_tool_loading_length" string="总长度(mm)"/>
<field name="after_assembly_handle_length" string="刀柄长度(mm)"/>
<field name="after_assembly_functional_tool_length"
string="伸出长(mm)"/>
<field name="after_assembly_max_lifetime_value"
string="最大寿命值(min)"/>
<field name="after_assembly_alarm_value" string="报警值(min)"/>
<field name="after_assembly_used_value" string="已使用值(min)"/>
<field name="after_assembly_tool_loading_length" string="总长度(mm)"/>
<field name="after_assembly_functional_tool_length"
string="伸出长(mm)"/>
<field name="after_assembly_effective_length" string="有效长(mm)"/>
<field name="L_D_number"/>
<field name="hiding_length"/>
<field name="after_assembly_used_value" string="已使用值(min)"
invisible="1"/>
<field name="after_assembly_effective_length" string="有效长(mm)"
invisible="1"/>
<field name="L_D_number" invisible="1"/>
<field name="hiding_length" invisible="1"/>
</group>
</group>
</group>
@@ -601,13 +557,14 @@
<field name="sf_tool_brand_id_5" string="品牌"/>
</group>
</group>
<group col="1" attrs="{'invisible': [('chuck_freight_barcode', '=', False)]}">
<group col="1" attrs="{'invisible': [('chuck_freight_barcode_id', '=', False)]}">
<div>
<separator string="夹头:" style="font-size: 13px;"/>
</div>
<group>
<group>
<field name="chuck_freight_barcode" string="货位"/>
<field name="chuck_freight_barcode_id" string="货位"/>
<field name="chuck_lot_id" string="批次"/>
<field name="chuck_product_id" string="名称"/>
<field name="cutting_tool_cutterhead_model_id" string="型号"/>
<field name="chuck_specification_id" string="规格"/>
@@ -619,24 +576,27 @@
<group>
<group col="1">
<group col="1"
attrs="{'invisible': [('integral_freight_barcode', '=', False)]}">
attrs="{'invisible': [('integral_freight_barcode_id', '=', False)]}">
<div>
<separator string="整体式刀具:" style="font-size: 13px;"/>
</div>
<group>
<field name="integral_freight_barcode" string="货位"/>
<field name="integral_freight_barcode_id" string="货位"/>
<field name="integral_lot_id" string="批次"/>
<field name="integral_product_id" string="名称"/>
<field name="cutting_tool_integral_model_id" string="型号"/>
<field name="integral_specification_id" string="规格"/>
<field name="sf_tool_brand_id_1" string="品牌"/>
</group>
</group>
<group col="1" attrs="{'invisible': [('blade_freight_barcode', '=', False)]}">
<group col="1"
attrs="{'invisible': [('blade_freight_barcode_id', '=', False)]}">
<div>
<separator string="刀片:" style="font-size: 13px;"/>
</div>
<group>
<field name="blade_freight_barcode" string="货位"/>
<field name="blade_freight_barcode_id" string="货位"/>
<field name="blade_lot_id" string="批次"/>
<field name="blade_product_id" string="名称"/>
<field name="cutting_tool_blade_model_id" string="型号"/>
<field name="blade_specification_id" string="规格"/>
@@ -645,13 +605,14 @@
</group>
</group>
<group col="1">
<group col="1" attrs="{'invisible': [('bar_freight_barcode', '=', False)]}">
<group col="1" attrs="{'invisible': [('bar_freight_barcode_id', '=', False)]}">
<div>
<separator string="刀杆:" style="font-size: 13px;"/>
</div>
<group>
<group>
<field name="bar_freight_barcode" string="货位"/>
<field name="bar_freight_barcode_id" string="货位"/>
<field name="bar_lot_id" string="批次"/>
<field name="bar_product_id" string="名称"/>
<field name="cutting_tool_cutterbar_model_id" string="型号"/>
<field name="bar_specification_id" string="规格"/>
@@ -659,13 +620,14 @@
</group>
</group>
</group>
<group col="1" attrs="{'invisible': [('pad_freight_barcode', '=', False)]}">
<group col="1" attrs="{'invisible': [('pad_freight_barcode_id', '=', False)]}">
<div>
<separator string="刀盘:" style="font-size: 13px;"/>
</div>
<group>
<group>
<field name="pad_freight_barcode" string="货位"/>
<field name="pad_freight_barcode_id" string="货位"/>
<field name="pad_lot_id" string="批次"/>
<field name="pad_product_id" string="名称"/>
<field name="cutting_tool_cutterpad_model_id" string="型号"/>
<field name="pad_specification_id" string="规格"/>
@@ -718,6 +680,10 @@
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
@@ -727,30 +693,29 @@
<field name="arch" type="xml">
<search>
<field name="assembly_order_code"/>
<field name="barcode_id" optional="hide"/>
<field name="code" string="功能刀具编码"/>
<field name="barcode_id"/>
<field name="functional_tool_name"/>
<field name="functional_tool_diameter"/>
<field name="knife_tip_r_angle"/>
<field name="coarse_middle_thin"/>
<field name="new_former"/>
<field name="tool_loading_length"/>
<field name="functional_tool_length"/>
<field name="effective_length"/>
<field name="functional_tool_type_id"/>
<field name="tool_groups_id"/>
<field name="loading_task_source" string="任务来源"/>
<field name="use_tool_time"/>
<field name="production_line_name_id"/>
<field name="machine_tool_name_id"/>
<field name="applicant"/>
<field name="apply_time"/>
<field name="functional_tool_type_id"/>
<filter name="no_assemble_status" string="未组装" domain="[('assemble_status', '=', '0')]"/>
<filter name="yes_assemble_status" string="已组装" domain="[('assemble_status', '=', '1')]"/>
<separator/>
<filter string="已归档" name="inactive" domain="[('active', '=', False)]"/>
<searchpanel>
<field name="functional_tool_type_id" enable_counters="1" icon="fa-filter"/>
<!-- <field name="assemble_status" enable_counters="1" icon="fa-filter"/>-->
</searchpanel>
<group expand="0" string="Group By...">
<filter string="功能刀具名称" name="name" domain="[]" context="{'group_by': 'functional_tool_name'}"/>
<filter string="刀具组" name="tool_groups" domain="[]" context="{'group_by': 'tool_groups_id'}"/>
<filter string="任务来源" name="loading_task_source" domain="[]" context="{'group_by': 'loading_task_source'}"/>
<filter string="用刀时间" name="use_tool_time" domain="[]" context="{'group_by': 'use_tool_time'}"/>
</group>
</search>
</field>
</record>
@@ -772,6 +737,7 @@
<field name="model">sf.functional.tool.dismantle</field>
<field name="arch" type="xml">
<tree create="1">
<field name="code"/>
<field name="rfid"/>
<field name="functional_tool_id"/>
<field name="tool_type_id" invisible="1"/>
@@ -799,13 +765,14 @@
<sheet>
<div class="oe_title">
<h1>
<field name="functional_tool_id" placeholder="请选择将要拆解的功能刀具"
options="{'no_create': True}" attrs="{'readonly': [('state', '=', '已拆解')]}"/>
<field name="code"/>
</h1>
</div>
<field name="_barcode_scanned" widget="barcode_handler"/>
<group>
<group>
<field name="functional_tool_id" placeholder="请选择将要拆解的功能刀具"
options="{'no_create': True}" attrs="{'readonly': [('state', '=', '已拆解')]}"/>
<field name="rfid" attrs="{'invisible': [('state', '=', '已拆解')]}"/>
<field name="rfid_dismantle" attrs="{'invisible': [('state', '!=', '已拆解')]}"/>
<field name="tool_type_id"/>
@@ -836,91 +803,98 @@
<group>
<group string="刀柄" attrs="{'invisible': [('handle_product_id', '=', False)]}">
<group>
<field name="scrap_boolean" string="是否报废"
attrs="{'invisible': [('dismantle_cause', 'not in', ['寿命到期报废','崩刀报废'])], 'readonly': [('state', '=', '已拆解')]}"/>
<field name="handle_rfid" string="Rfid"/>
<field name="handle_lot_id" string="序列号"/>
<field name="handle_product_id" string="名称"/>
<field name="handle_type_id" string="型号"/>
<field name="handle_brand_id" string="品牌"/>
</group>
<group>
<field name="handle_rfid" string="Rfid"/>
<field name="scrap_boolean" string="是否报废"
attrs="{'invisible': [('dismantle_cause', 'not in', ['寿命到期报废','崩刀报废'])], 'readonly': [('state', '=', '已拆解')]}"/>
</group>
</group>
<group string="夹头"
attrs="{'invisible': [('chuck_product_id', '=', False)]}">
<group>
<field name="chuck_freight_id" string="目标货位" placeholder="请选择"
options="{'no_create': True,'no_create_edit':True}"
attrs="{'invisible': [('dismantle_cause', 'not in', ['更换为其他刀具', '刀具需磨削'])], 'readonly': [('state', '=', '已拆解')],
'required': [('chuck_lot_id', '!=', False),('dismantle_cause', 'in', ['更换为其他刀具', '刀具需磨削'])]}"/>
<field name="chuck_lot_id" string="批次"/>
<field name="chuck_product_id" string="名称"/>
<field name="chuck_type_id" string="型号"/>
<field name="chuck_brand_id" string="品牌"/>
</group>
<group>
<field name="chuck_freight_id" string="目标货位"
options="{'no_create': True,'no_create_edit':True}"
attrs="{'invisible': [('dismantle_cause', 'not in', ['更换为其他刀具'])], 'readonly': [('state', '=', '已拆解')],
'required': [('chuck_product_id', '!=', False),('dismantle_cause', 'in', ['更换为其他刀具'])]}"/>
</group>
</group>
</group>
<group attrs="{'invisible': [('integral_product_id', '=', False)]}">
<group string="整体式刀具">
<group>
<field name="integral_freight_id" string="目标货位" placeholder="请选择"
options="{'no_create': True,'no_create_edit':True}"
attrs="{'invisible': [('dismantle_cause', 'not in', ['更换为其他刀具', '刀具需磨削'])], 'readonly': [('state', '=', '已拆解')],
'required': [('integral_lot_id', '!=', False),('dismantle_cause', 'in', ['更换为其他刀具', '刀具需磨削'])]}"/>
<field name="integral_lot_id" string="批次"/>
<field name="integral_product_id" string="名称"/>
<field name="integral_type_id" string="型号"/>
<field name="integral_brand_id" string="品牌"/>
</group>
<group>
<field name="integral_freight_id" string="目标货位"
options="{'no_create': True,'no_create_edit':True}"
attrs="{'invisible': [('dismantle_cause', 'not in', ['更换为其他刀具'])], 'readonly': [('state', '=', '已拆解')],
'required': [('integral_product_id', '!=', False),('dismantle_cause', 'in', ['更换为其他刀具'])]}"/>
</group>
</group>
</group>
<group>
<group string="刀片" attrs="{'invisible': [('blade_product_id', '=', False)]}">
<group>
<field name="blade_freight_id" string="目标货位" placeholder="请选择"
options="{'no_create': True,'no_create_edit':True}"
attrs="{'invisible': [('dismantle_cause', 'not in', ['更换为其他刀具', '刀具需磨削'])], 'readonly': [('state', '=', '已拆解')],
'required': [('blade_lot_id', '!=', False),('dismantle_cause', 'in', ['更换为其他刀具', '刀具需磨削'])]}"/>
<field name="blade_lot_id" string="批次"/>
<field name="blade_product_id" string="名称"/>
<field name="blade_type_id" string="型号"/>
<field name="blade_brand_id" string="品牌"/>
</group>
<group>
<field name="blade_freight_id" string="目标货位"
options="{'no_create': True,'no_create_edit':True}"
attrs="{'invisible': [('dismantle_cause', 'not in', ['更换为其他刀具'])], 'readonly': [('state', '=', '已拆解')],
'required': [('blade_product_id', '!=', False),('dismantle_cause', 'in', ['更换为其他刀具'])]}"/>
</group>
</group>
<group string="刀杆" attrs="{'invisible': [('bar_product_id', '=', False)]}">
<group>
<field name="bar_freight_id" string="目标货位" placeholder="请选择"
options="{'no_create': True,'no_create_edit':True}"
attrs="{'invisible': [('dismantle_cause', 'not in', ['更换为其他刀具', '刀具需磨削'])], 'readonly': [('state', '=', '已拆解')],
'required': [('bar_lot_id', '!=', False),('dismantle_cause', 'in', ['更换为其他刀具', '刀具需磨削'])]}"/>
<field name="bar_lot_id" string="批次"/>
<field name="bar_product_id" string="名称"/>
<field name="bar_type_id" string="型号"/>
<field name="bar_brand_id" string="品牌"/>
</group>
<group>
<field name="bar_freight_id" string="目标货位"
options="{'no_create': True,'no_create_edit':True}"
attrs="{'invisible': [('dismantle_cause', 'not in', ['更换为其他刀具'])], 'readonly': [('state', '=', '已拆解')],
'required': [('bar_product_id', '!=', False),('dismantle_cause', 'in', ['更换为其他刀具'])]}"/>
</group>
</group>
<group string="刀盘" attrs="{'invisible': [('pad_product_id', '=', False)]}">
<group>
<field name="pad_freight_id" string="目标货位" placeholder="请选择"
options="{'no_create': True,'no_create_edit':True}"
attrs="{'invisible': [('dismantle_cause', 'not in', ['更换为其他刀具', '刀具需磨削'])], 'readonly': [('state', '=', '已拆解')],
'required': [('pad_lot_id', '!=', False), ('dismantle_cause', 'in', ['更换为其他刀具', '刀具需磨削'])]}"/>
<field name="pad_lot_id" string="批次"/>
<field name="pad_product_id" string="名称"/>
<field name="pad_type_id" string="型号"/>
<field name="pad_brand_id" string="品牌"/>
</group>
<group>
<field name="pad_freight_id" string="目标货位"
options="{'no_create': True,'no_create_edit':True}"
attrs="{'invisible': [('dismantle_cause', 'not in', ['更换为其他刀具'])], 'readonly': [('state', '=', '已拆解')],
'required': [('pad_product_id', '!=', False), ('dismantle_cause', 'in', ['更换为其他刀具'])]}"/>
</group>
</group>
</group>
</page>
<page string="其他">
<group>
<group>
<field name="dismantle_person"/>
</group>
<group>
<field name="dismantle_data"/>
</group>
</group>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
@@ -930,6 +904,7 @@
<field name="arch" type="xml">
<search>
<field name="functional_tool_id"/>
<field name="code" string="拆解单编码"/>
<filter name="no_dismantle_state" string="未拆解" domain="[('state','!=','已拆解')]"/>
<filter name="dismantle_state" string="已拆解" domain="[('state','=','已拆解')]"/>
<separator/>

View File

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

View File

@@ -223,8 +223,8 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
image = fields.Binary('图片')
@api.onchange('functional_tool_name')
def _onchange_functional_tool_name(self):
@api.depends('functional_tool_name')
def _compute_functional_tool_name(self):
for item in self:
if item.functional_tool_name:
inventory = self.env['sf.tool.inventory'].sudo().search([('name', '=', item.functional_tool_name)])
@@ -233,7 +233,10 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
# 功能刀具组装信息
# ===============整体式刀具型号=================
integral_freight_barcode = fields.Char('整体式刀具货位')
integral_freight_barcode_id = fields.Many2one('sf.shelf.location', string='整体式刀具货位',
domain="[('product_id.cutting_tool_material_id.name', '=', '整体式刀具'),('product_num', '>', 0)]")
integral_freight_lot_id = fields.Many2one('sf.shelf.location.lot', string='整体式刀具批次',
domain="[('shelf_location_id', '=', integral_freight_barcode_id)]")
integral_product_id = fields.Many2one('product.product', string='整体式刀具名称',
compute='_compute_integral_product_id', store=True)
cutting_tool_integral_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='整体式刀具型号',
@@ -243,28 +246,23 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
sf_tool_brand_id_1 = fields.Many2one('sf.machine.brand', string='整体式刀具品牌',
related='integral_product_id.brand_id')
@api.depends('integral_freight_barcode')
@api.onchange('integral_freight_barcode_id')
def _onchange_integral_freight_barcode_id(self):
for item in self:
item.integral_freight_lot_id = False
@api.depends('integral_freight_lot_id')
def _compute_integral_product_id(self):
if self.integral_freight_barcode:
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', self.integral_freight_barcode)])
if location:
if not location.product_id:
raise ValidationError('编码为【%s】的货位为空货位!' % location.barcode)
else:
material_name_id = location.product_id.cutting_tool_material_id
if material_name_id and material_name_id.name == '整体式刀具':
if location.product_num == 0:
raise ValidationError('编码为【%s】的货位的产品库存数量为0,请重新选择!' % location.barcode)
self.integral_product_id = location.product_id.id
else:
raise ValidationError(
'编码为【%s】的货位存放的产品为【%s】,不是整体式刀具,请重新选择!' % (
location.barcode, location.product_id.name))
else:
raise ValidationError('编码为【%s】的货位不存在!' % self.integral_freight_barcode)
if self.integral_freight_lot_id:
self.integral_product_id = self.integral_freight_lot_id.lot_id.product_id.id
else:
self.integral_product_id = False
# ===============刀片型号====================
blade_freight_barcode = fields.Char('刀片货位')
blade_freight_barcode_id = fields.Many2one('sf.shelf.location', string='刀片货位',
domain="[('product_id.cutting_tool_material_id.name', '=', '刀片'),('product_num', '>', 0)]")
blade_freight_lot_id = fields.Many2one('sf.shelf.location.lot', string='刀片批次',
domain="[('shelf_location_id', '=', blade_freight_barcode_id)]")
blade_product_id = fields.Many2one('product.product', string='刀片名称', compute='_compute_blade_product_id',
store=True)
cutting_tool_blade_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀片型号',
@@ -273,28 +271,23 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
related='blade_product_id.specification_id')
sf_tool_brand_id_2 = fields.Many2one('sf.machine.brand', '刀片品牌', related='blade_product_id.brand_id')
@api.depends('blade_freight_barcode')
@api.onchange('blade_freight_barcode_id')
def _onchange_blade_freight_barcode_id(self):
for item in self:
item.blade_freight_lot_id = False
@api.depends('blade_freight_lot_id')
def _compute_blade_product_id(self):
if self.blade_freight_barcode:
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', self.blade_freight_barcode)])
if location:
if not location.product_id:
raise ValidationError('编码为【%s】的货位为空货位!' % location.barcode)
else:
material_name_id = location.product_id.cutting_tool_material_id
if material_name_id and material_name_id.name == '刀片':
if location.product_num == 0:
raise ValidationError('编码为【%s】的货位的产品库存数量为0,请重新选择!' % location.barcode)
self.blade_product_id = location.product_id.id
else:
raise ValidationError(
'编码为【%s】的货位存放的产品为【%s】,不是刀片,请重新选择!' % (
location.barcode, location.product_id.name))
else:
raise ValidationError('编码为【%s】的货位不存在!' % self.blade_freight_barcode)
if self.blade_freight_lot_id:
self.blade_product_id = self.blade_freight_lot_id.lot_id.product_id.id
else:
self.blade_product_id = False
# ====================刀杆型号==================
bar_freight_barcode = fields.Char('刀杆货位')
bar_freight_barcode_id = fields.Many2one('sf.shelf.location', string='刀杆货位',
domain="[('product_id.cutting_tool_material_id.name', '=', '刀杆'),('product_num', '>', 0)]")
bar_freight_lot_id = fields.Many2one('sf.shelf.location.lot', string='刀杆批次',
domain="[('shelf_location_id', '=', bar_freight_barcode_id)]")
bar_product_id = fields.Many2one('product.product', string='刀杆名称', compute='_compute_bar_product_id',
store=True)
cutting_tool_cutterbar_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀杆型号',
@@ -303,28 +296,23 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
related='bar_product_id.specification_id')
sf_tool_brand_id_3 = fields.Many2one('sf.machine.brand', '刀杆品牌', related='bar_product_id.brand_id')
@api.depends('bar_freight_barcode')
@api.onchange('bar_freight_barcode_id')
def _onchange_bar_freight_barcode_id(self):
for item in self:
item.bar_freight_lot_id = False
@api.depends('bar_freight_lot_id')
def _compute_bar_product_id(self):
if self.bar_freight_barcode:
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', self.bar_freight_barcode)])
if location:
if not location.product_id:
raise ValidationError('编码为【%s】的货位为空货位!' % location.barcode)
else:
material_name_id = location.product_id.cutting_tool_material_id
if material_name_id and material_name_id.name == '刀杆':
if location.product_num == 0:
raise ValidationError('编码为【%s】的货位的产品库存数量为0,请重新选择!' % location.barcode)
self.bar_product_id = location.product_id.id
else:
raise ValidationError(
'编码为【%s】的货位存放的产品为【%s】,不是刀杆,请重新选择!' % (
location.barcode, location.product_id.name))
else:
raise ValidationError('编码为【%s】的货位不存在!' % self.bar_freight_barcode)
if self.bar_freight_lot_id:
self.bar_product_id = self.bar_freight_lot_id.lot_id.product_id.id
else:
self.bar_product_id = False
# ===============刀盘型号===================
pad_freight_barcode = fields.Char('刀盘货位')
pad_freight_barcode_id = fields.Many2one('sf.shelf.location', string='刀盘货位',
domain="[('product_id.cutting_tool_material_id.name', '=', '刀盘'),('product_num', '>', 0)]")
pad_freight_lot_id = fields.Many2one('sf.shelf.location.lot', string='刀盘批次',
domain="[('shelf_location_id', '=', pad_freight_barcode_id)]")
pad_product_id = fields.Many2one('product.product', string='刀盘名称', compute='_compute_pad_product_id',
store=True)
cutting_tool_cutterpad_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀盘型号',
@@ -333,29 +321,21 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
related='pad_product_id.specification_id')
sf_tool_brand_id_4 = fields.Many2one('sf.machine.brand', '刀盘品牌', related='pad_product_id.brand_id')
@api.depends('pad_freight_barcode')
@api.onchange('pad_freight_barcode_id')
def _onchange_pad_freight_barcode_id(self):
for item in self:
item.pad_freight_lot_id = False
@api.depends('pad_freight_lot_id')
def _compute_pad_product_id(self):
if self.pad_freight_barcode:
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', self.pad_freight_barcode)])
if location:
if not location.product_id:
raise ValidationError('编码为【%s】的货位为空货位!' % location.barcode)
else:
material_name_id = location.product_id.cutting_tool_material_id
if material_name_id and material_name_id.name == '刀盘':
if location.product_num == 0:
raise ValidationError('编码为【%s】的货位的产品库存数量为0,请重新选择!' % location.barcode)
self.pad_product_id = location.product_id.id
else:
raise ValidationError(
'编码为【%s】的货位存放的产品为【%s】,不是刀盘,请重新选择!' % (
location.barcode, location.product_id.name))
else:
raise ValidationError('编码为【%s】的货位不存在!' % self.pad_freight_barcode)
if self.pad_freight_lot_id:
self.pad_product_id = self.pad_freight_lot_id.lot_id.product_id.id
else:
self.pad_product_id = False
# ================刀柄型号===============
handle_freight_rfid = fields.Char('刀柄Rfid', compute='_compute_rfid')
handle_code_id = fields.Many2one('stock.lot', '刀柄序列号', required=True,
handle_code_id = fields.Many2one('stock.lot', '刀柄序列号',
domain=[('product_id.cutting_tool_material_id.name', '=', '刀柄'),
('tool_material_status', '=', '可用')])
handle_product_id = fields.Many2one('product.product', string='刀柄名称', compute='_compute_handle_product_id',
@@ -374,7 +354,10 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
self.pad_product_id = False
# =================夹头型号==============
chuck_freight_barcode = fields.Char('夹头货位')
chuck_freight_barcode_id = fields.Many2one('sf.shelf.location', string='夹头货位',
domain="[('product_id.cutting_tool_material_id.name', '=', '夹头'),('product_num', '>', 0)]")
chuck_freight_lot_id = fields.Many2one('sf.shelf.location.lot', string='夹头批次',
domain="[('shelf_location_id', '=', chuck_freight_barcode_id)]")
chuck_product_id = fields.Many2one('product.product', string='夹头名称', compute='_compute_chuck_product_id',
store=True)
cutting_tool_cutterhead_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='夹头型号',
@@ -383,25 +366,17 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
related='chuck_product_id.specification_id')
sf_tool_brand_id_6 = fields.Many2one('sf.machine.brand', '夹头品牌', related='chuck_product_id.brand_id')
@api.depends('chuck_freight_barcode')
@api.onchange('chuck_freight_barcode_id')
def _onchange_chuck_freight_barcode_id(self):
for item in self:
item.chuck_freight_lot_id = False
@api.depends('chuck_freight_lot_id')
def _compute_chuck_product_id(self):
if self.chuck_freight_barcode:
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', self.chuck_freight_barcode)])
if location:
if not location.product_id:
raise ValidationError('编码为【%s】的货位为空货位!' % location.barcode)
else:
material_name_id = location.product_id.cutting_tool_material_id
if material_name_id and material_name_id.name == '夹头':
if location.product_num == 0:
raise ValidationError('编码为【%s】的货位的产品库存数量为0,请重新选择!' % location.barcode)
self.chuck_product_id = location.product_id.id
else:
raise ValidationError(
'编码为【%s】的货位存放的产品为【%s】,不是夹头,请重新选择!' % (
location.barcode, location.product_id.name))
else:
raise ValidationError('编码为【%s】的货位不存在!' % self.chuck_freight_barcode)
if self.chuck_freight_lot_id:
self.chuck_product_id = self.chuck_freight_lot_id.lot_id.product_id.id
else:
self.chuck_product_id = False
# ========================================
def on_barcode_scanned(self, barcode):
@@ -423,23 +398,23 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
if location:
material_name = location.product_id.cutting_tool_material_id.name
if material_name == '夹头':
record.chuck_freight_barcode = barcode
record.chuck_freight_barcode_id = location.id
elif material_name == '整体式刀具':
record.integral_freight_barcode = barcode
record.blade_freight_barcode = ''
record.bar_freight_barcode = ''
record.pad_freight_barcode = ''
record.integral_freight_barcode_id = location.id
record.blade_freight_barcode_id = False
record.bar_freight_barcode_id = False
record.pad_freight_barcode_id = False
elif material_name == '刀片':
record.blade_freight_barcode = barcode
record.integral_freight_barcode = ''
record.blade_freight_barcode_id = location.id
record.integral_freight_barcode_id = False
elif material_name == '刀杆':
record.bar_freight_barcode = barcode
record.integral_freight_barcode = ''
record.pad_freight_barcode = ''
record.bar_freight_barcode_id = location.id
record.integral_freight_barcode_id = False
record.pad_freight_barcode_id = False
elif material_name == '刀盘':
record.pad_freight_barcode = barcode
record.integral_freight_barcode = ''
record.bar_freight_barcode = ''
record.pad_freight_barcode_id = location.id
record.integral_freight_barcode_id = False
record.bar_freight_barcode_id = False
else:
raise ValidationError('扫描的刀具物料不存在,请重新扫描!')
else:
@@ -456,15 +431,17 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
item.handle_freight_rfid = None
# 组装功能刀具参数信息
after_name_id = fields.Many2one('sf.tool.inventory', string='功能刀具名称', required=True)
after_name_id = fields.Many2one('sf.tool.inventory', string='功能刀具名称', compute='_compute_functional_tool_name',
store=True)
barcode_id = fields.Many2one('stock.lot', string='功能刀具序列号')
rfid = fields.Char('Rfid', compute='_compute_rfid')
code = fields.Char(string='功能刀具编码', compute='_compute_code')
after_assembly_functional_tool_name = fields.Char(string='组装后功能刀具名称', compute='_compute_name', store=True)
after_assembly_functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model',
string='组装后功能刀具类型')
after_assembly_functional_tool_diameter = fields.Float(string='组装后功能刀具直径(mm)')
after_assembly_knife_tip_r_angle = fields.Float(string='组装后刀尖R角(mm)')
string='组装后功能刀具类型', store=True,
compute='_compute_after_assembly_max_lifetime_value')
after_assembly_functional_tool_diameter = fields.Float(string='组装后功能刀具直径(mm)', digits=(10, 3))
after_assembly_knife_tip_r_angle = fields.Float(string='组装后刀尖R角(mm)', digits=(10, 3))
after_assembly_new_former = fields.Selection([('0', ''), ('1', '')], string='组装后新/旧', default='0')
cut_time = fields.Integer(string='已切削时间(min)')
cut_length = fields.Float(string='已切削长度(mm)')
@@ -473,15 +450,75 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
after_assembly_whether_standard_knife = fields.Boolean(string='组装后是否标准刀', default=True)
after_assembly_coarse_middle_thin = fields.Selection([("1", ""), ('2', ''), ('3', '')],
string='组装后粗/中/精', default='3')
after_assembly_max_lifetime_value = fields.Integer(string='组装后最大寿命值(min)')
after_assembly_max_lifetime_value = fields.Integer(string='组装后最大寿命值(min)', store=True,
compute='_compute_after_assembly_max_lifetime_value')
after_assembly_alarm_value = fields.Integer(string='组装后报警值(min)')
after_assembly_used_value = fields.Integer(string='组装后已使用值(min)')
after_assembly_tool_loading_length = fields.Float(string='组装后总长度(mm)')
after_assembly_functional_tool_length = fields.Float(string='组装后伸出长(mm)', required=True)
after_assembly_tool_loading_length = fields.Float(string='组装后总长度(mm)', digits=(10, 3))
after_assembly_handle_length = fields.Float(string='组装后刀柄长度(mm)', digits=(10, 3))
after_assembly_functional_tool_length = fields.Float(string='组装后伸出长(mm)', digits=(10, 3),
compute='_compute_after_assembly_functional_tool_length')
after_assembly_effective_length = fields.Float(string='组装后有效长(mm)')
L_D_number = fields.Float(string='L/D值(mm)', compute='_compute_l_d_number')
hiding_length = fields.Float(string='避空长(mm)')
after_tool_groups_id = fields.Many2one('sf.tool.groups', string='组装后刀具组')
after_tool_groups_id = fields.Many2one('sf.tool.groups', string='组装后刀具组', store=True,
compute='_compute_after_assembly_max_lifetime_value')
obtain_measurement_status = fields.Boolean('是否获取测量值', default=False)
enable_tool_presetter = fields.Boolean('是否启用刀具预调仪', default=lambda self: self.get_enable_tool_presetter())
def get_enable_tool_presetter(self):
"""
获取是否启用刀具预调仪数据
"""
sf_sync_config = self.env['res.config.settings'].get_values()
enable_tool_presetter = sf_sync_config['enable_tool_presetter']
return enable_tool_presetter
@api.depends('after_assembly_tool_loading_length', 'after_assembly_handle_length')
def _compute_after_assembly_functional_tool_length(self):
for item in self:
item.after_assembly_functional_tool_length = item.after_assembly_tool_loading_length - item.after_assembly_handle_length
@api.onchange('after_assembly_max_lifetime_value')
def _onchange_after_assembly_alarm_value(self):
for item in self:
if item.after_assembly_max_lifetime_value:
item.after_assembly_alarm_value = item.after_assembly_max_lifetime_value * 0.95
def get_tool_preset_parameter(self):
"""
获取刀具预调仪数据
"""
tool = self.env['sf.functional.tool.assembly'].search([('assembly_order_code', '=', self.assembly_order_code)])
tool_loading_length = tool.after_assembly_tool_loading_length
diameter = tool.after_assembly_functional_tool_diameter
r_angle = tool.after_assembly_knife_tip_r_angle
if tool_loading_length == 0 and diameter == 0 and r_angle == 0:
raise ValidationError('没有获取到测量数据!')
self.write({
'after_assembly_tool_loading_length': tool_loading_length, # 总长度
'after_assembly_functional_tool_diameter': diameter, # 直径
'after_assembly_knife_tip_r_angle': r_angle, # R角
'obtain_measurement_status': True # 是否获取测量值
})
return {
'type': 'ir.actions.act_window',
'res_model': 'sf.functional.tool.assembly.order',
'view_mode': 'form',
'res_id': self.id,
'target': 'new',
'flags': {'form': {'action_buttons': True, 'options': {'mode': 'modal'}}}
}
@api.depends('after_name_id')
def _compute_after_assembly_max_lifetime_value(self):
for item in self:
if item.after_name_id:
item.after_assembly_max_lifetime_value = item.after_name_id.life_span
item.after_assembly_functional_tool_type_id = item.after_name_id.functional_cutting_tool_model_id.id
item.after_tool_groups_id = item.after_name_id.tool_groups_id.id
@api.onchange('after_name_id')
def _onchange_number(self):
@@ -489,45 +526,32 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
if item.after_name_id:
item.after_assembly_functional_tool_diameter = item.after_name_id.diameter
item.after_assembly_knife_tip_r_angle = item.after_name_id.angle
item.after_assembly_max_lifetime_value = item.after_name_id.life_span
item.after_assembly_tool_loading_length = item.after_name_id.tool_length
item.after_assembly_functional_tool_length = item.after_name_id.extension
item.hiding_length = item.after_name_id.blade_length
item.after_assembly_functional_tool_type_id = item.after_name_id.functional_cutting_tool_model_id.id
item.after_tool_groups_id = item.after_name_id.tool_groups_id.id
else:
item.after_assembly_functional_tool_type_id = item.functional_tool_type_id
item.after_assembly_functional_tool_type_id = item.functional_tool_type_id.id
item.after_tool_groups_id = item.tool_groups_id.id
# functional_tool_cutting_type = fields.Char(string='功能刀具切削类型', readonly=False)
# res_partner_id = fields.Many2one('res.partner', '智能工厂', domain="[('is_factory', '=', True)]")
@api.depends('after_assembly_functional_tool_type_id', 'integral_specification_id', 'bar_specification_id',
'pad_specification_id', 'handle_specification_id', 'after_assembly_tool_loading_length')
@api.depends('after_assembly_functional_tool_type_id', 'cutting_tool_cutterhandle_model_id',
'after_assembly_functional_tool_diameter', 'after_assembly_tool_loading_length',
'after_assembly_knife_tip_r_angle', 'after_assembly_functional_tool_length',
'after_assembly_handle_length')
def _compute_code(self):
for obj in self:
str_1 = 'GNDJ-%s' % obj.after_assembly_functional_tool_type_id.code
str_2 = ''
if obj.handle_specification_id:
if obj.integral_specification_id:
str_2 = '%s-D%sL%sB%sH%s-' % (
str_1, obj.integral_specification_id.blade_diameter, obj.after_assembly_tool_loading_length,
obj.integral_specification_id.blade_length, obj.handle_specification_id.total_length)
elif obj.bar_specification_id:
str_2 = '%s-D%sL%sB%sH%s-' % (
str_1, obj.bar_specification_id.cutter_arbor_diameter, obj.after_assembly_tool_loading_length,
obj.bar_specification_id.blade_length, obj.handle_specification_id.total_length)
elif obj.pad_specification_id:
str_2 = '%s-D%sL%sB%sH%s-' % (
str_1, obj.pad_specification_id.cutter_head_diameter, obj.after_assembly_tool_loading_length,
obj.pad_specification_id.cut_depth_max, obj.handle_specification_id.total_length,
)
else:
obj.code = str_2
return True
obj.code = str_2 + str(self._get_code(str_2))
if obj.cutting_tool_cutterhandle_model_id:
code = obj.cutting_tool_cutterhandle_model_id.code.split('-', 1)[0]
str_1 = '%s-GNDJ-%s-D%sL%sR%sB%sH%s' % (
code, obj.after_assembly_functional_tool_type_id.code, obj.after_assembly_functional_tool_diameter,
obj.after_assembly_tool_loading_length, obj.after_assembly_knife_tip_r_angle,
round(obj.after_assembly_functional_tool_length, 3), obj.after_assembly_handle_length)
obj.code = '%s-%s' % (str_1, self._get_code(str_1))
else:
obj.code = str_2
obj.code = ''
def _get_code(self, str_2):
functional_tool_assembly = self.env['sf.functional.cutting.tool.entity'].sudo().search(
@@ -550,7 +574,7 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
else:
obj.after_assembly_functional_tool_name = ''
@api.onchange('integral_freight_barcode')
@api.onchange('integral_freight_barcode_id')
def _onchange_after_assembly_functional_tool_diameter(self):
for obj in self:
if obj.integral_product_id:
@@ -558,7 +582,7 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
else:
obj.after_assembly_functional_tool_diameter = 0
@api.onchange('blade_freight_barcode')
@api.onchange('blade_freight_barcode_id')
def _onchange_after_assembly_knife_tip_r_angle(self):
for obj in self:
if obj.blade_product_id:
@@ -574,24 +598,6 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
else:
record.L_D_number = 0
@api.constrains('after_assembly_tool_loading_length', 'after_assembly_functional_tool_length',
'after_assembly_max_lifetime_value', 'after_assembly_alarm_value',
'after_assembly_effective_length', 'hiding_length')
def _check_length_control(self):
for obj in self:
if obj.after_assembly_tool_loading_length == 0:
raise ValidationError('组装参数信息【总长度】不能为0')
if obj.after_assembly_functional_tool_length == 0:
raise ValidationError('组装参数信息【伸出长】不能为0')
if obj.after_assembly_max_lifetime_value == 0:
raise ValidationError('组装参数信息【最大寿命值】不能为0')
if obj.after_assembly_alarm_value == 0:
raise ValidationError('组装参数信息【报警值】不能为0')
if obj.after_assembly_effective_length == 0:
raise ValidationError('组装参数信息【有效长】不能为0')
if obj.hiding_length == 0:
raise ValidationError('组装参数信息【避空长】不能为0')
def functional_tool_assembly(self):
"""
功能刀具组装
@@ -611,25 +617,32 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
# 创建组装入库单
# 创建功能刀具批次/序列号记录
stock_lot = product_id.create_assemble_warehouse_receipt(self.id, functional_tool_assembly, self)
# 封装功能刀具数据,用于更新组装单信息
desc_1 = self.get_desc_1(stock_lot)
# 封装功能刀具数据,用于创建功能刀具记录
desc_2 = self.get_desc_2(stock_lot, functional_tool_assembly)
# 创建刀具组装入库单
self.env['stock.picking'].create_stocking_picking(stock_lot, functional_tool_assembly, self)
# 刀具物料出库
if self.handle_code_id:
product_id.tool_material_stock_moves(self.handle_code_id, self.assembly_order_code)
if self.integral_product_id:
self.integral_product_id.material_stock_moves(self.integral_freight_barcode, self.assembly_order_code)
self.integral_product_id.material_stock_moves(self.integral_freight_barcode_id,
self.integral_freight_lot_id, self.assembly_order_code)
if self.blade_product_id:
self.blade_product_id.material_stock_moves(self.blade_freight_barcode, self.assembly_order_code)
self.blade_product_id.material_stock_moves(self.blade_freight_barcode_id,
self.blade_freight_lot_id, self.assembly_order_code)
if self.bar_product_id:
self.bar_product_id.material_stock_moves(self.bar_freight_barcode, self.assembly_order_code)
self.bar_product_id.material_stock_moves(self.bar_freight_barcode_id,
self.bar_freight_lot_id, self.assembly_order_code)
if self.pad_product_id:
self.pad_product_id.material_stock_moves(self.pad_freight_barcode, self.assembly_order_code)
self.pad_product_id.material_stock_moves(self.pad_freight_barcode_id,
self.pad_freight_lot_id, self.assembly_order_code)
if self.chuck_product_id:
self.chuck_product_id.material_stock_moves(self.chuck_freight_barcode, self.assembly_order_code)
self.chuck_product_id.material_stock_moves(self.chuck_freight_barcode_id,
self.chuck_freight_lot_id, self.assembly_order_code)
# ============================创建功能刀具列表、安全库存记录===============================
# 封装功能刀具数据
desc_2 = self.get_desc_2(stock_lot, functional_tool_assembly)
# 创建功能刀具列表记录
record_1 = self.env['sf.functional.cutting.tool.entity'].create(desc_2)
# 创建安全库存信息
@@ -638,8 +651,6 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
}, record_1)
# =====================修改功能刀具组装单、机床换刀申请、CAM工单程序用刀计划的状态==============
# 封装功能刀具数据
desc_1 = self.get_desc_1(stock_lot)
# 修改功能刀具组装单信息
functional_tool_assembly.write(desc_1)
if functional_tool_assembly.sf_machine_table_tool_changing_apply_id:
@@ -659,26 +670,54 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
def materials_must_be_judged(self):
"""
功能刀具组装物料必填判断
功能刀具组装必填判断
"""
# 物料必填校验
if not self.handle_code_id:
raise ValidationError('缺少【刀柄】物料信息!')
if not self.integral_product_id and not self.blade_product_id:
raise ValidationError('【整体式刀具】和【刀片】必须填写一个!')
if self.blade_product_id:
if not self.bar_product_id and not self.pad_product_id:
raise ValidationError('【刀盘】和【刀杆】必须填写一个!')
# 组装参数必填校验
if self.after_assembly_functional_tool_length == 0:
raise ValidationError('组装参数信息【伸出长】不能为0')
if self.after_assembly_max_lifetime_value == 0:
raise ValidationError('组装参数信息【最大寿命值】不能为0')
if self.after_assembly_alarm_value == 0:
raise ValidationError('组装参数信息【报警值】不能为0')
# if self.after_assembly_effective_length == 0:
# raise ValidationError('组装参数信息【有效长】不能为0')
# if self.hiding_length == 0:
# raise ValidationError('组装参数信息【避空长】不能为0')
if self.after_assembly_functional_tool_diameter == 0:
raise ValidationError('组装参数信息【刀具直径】不能为0')
if self.after_assembly_tool_loading_length == 0:
raise ValidationError('组装参数信息【总长度】不能为0')
if self.after_assembly_handle_length == 0:
raise ValidationError('组装参数信息【刀柄长度】不能为0')
if self.after_assembly_tool_loading_length < self.after_assembly_handle_length:
raise ValidationError('组装参数信息【刀柄长度】不能大于【总长度】!')
def get_desc_1(self, stock_lot):
return {
'start_preset_bool': False,
'barcode_id': stock_lot.id,
'code': self.code,
'rfid': self.rfid,
'tool_groups_id': self.after_tool_groups_id.id,
'integral_freight_barcode': self.integral_freight_barcode,
'blade_freight_barcode': self.blade_freight_barcode,
'bar_freight_barcode': self.bar_freight_barcode,
'pad_freight_barcode': self.pad_freight_barcode,
'handle_code_id': self.handle_code_id.id,
'chuck_freight_barcode': self.chuck_freight_barcode,
'integral_freight_barcode_id': self.integral_freight_barcode_id.id,
'integral_lot_id': self.integral_freight_lot_id.lot_id.id,
'blade_freight_barcode_id': self.blade_freight_barcode_id.id,
'blade_lot_id': self.blade_freight_lot_id.lot_id.id,
'bar_freight_barcode_id': self.bar_freight_barcode_id.id,
'bar_lot_id': self.bar_freight_lot_id.lot_id.id,
'pad_freight_barcode_id': self.pad_freight_barcode_id.id,
'pad_lot_id': self.pad_freight_lot_id.lot_id.id,
'chuck_freight_barcode_id': self.chuck_freight_barcode_id.id,
'chuck_lot_id': self.chuck_freight_lot_id.lot_id.id,
'after_assembly_functional_tool_name': self.after_assembly_functional_tool_name,
'after_assembly_functional_tool_type_id': self.after_assembly_functional_tool_type_id.id,
@@ -694,6 +733,7 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
'after_assembly_alarm_value': self.after_assembly_alarm_value,
'after_assembly_used_value': self.after_assembly_used_value,
'after_assembly_tool_loading_length': self.after_assembly_tool_loading_length,
'after_assembly_handle_length': self.after_assembly_handle_length,
'after_assembly_functional_tool_length': self.after_assembly_functional_tool_length,
'after_assembly_effective_length': self.after_assembly_effective_length,
'L_D_number': self.L_D_number,
@@ -726,6 +766,7 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
'coarse_middle_thin': self.after_assembly_coarse_middle_thin,
'new_former': self.after_assembly_new_former,
'tool_loading_length': self.after_assembly_tool_loading_length,
'handle_length': self.after_assembly_handle_length,
'functional_tool_length': self.after_assembly_functional_tool_length,
'effective_length': self.after_assembly_effective_length,
@@ -757,6 +798,7 @@ class StockPicking(models.Model):
'picking_type_id': picking_type_id.id,
'location_id': picking_type_id.default_location_src_id.id,
'location_dest_id': picking_type_id.default_location_dest_id.id,
'origin': obj.assembly_order_code
})
# 创建作业详情对象记录,并绑定到刀具组装入库单
self.env['stock.move.line'].create({
@@ -807,38 +849,34 @@ class ProductProduct(models.Model):
logging.info('没有搜索到功能刀具产品:%s' % product_id)
raise ValidationError('没有找到按唯一序列号追溯的功能刀具产品信息!')
stock_lot = self.env['stock.lot'].create({
'name': self.get_stock_lot_name(tool_assembly_order_id),
'name': self.get_stock_lot_name(obj),
'product_id': product_id[0].id,
'company_id': self.env.company.id
})
# 获取位置对象
location_inventory_id = self.env['stock.location'].search([('name', '=', 'Production')])
location_inventory_ids = self.env['stock.location'].search([('name', 'in', ('Production', '生产'))])
stock_location_id = self.env['stock.location'].search([('name', '=', '组装后')])
# 创建功能刀具该批次/序列号 库存移动和移动历史
stock_lot.create_stock_quant(location_inventory_id, stock_location_id, functional_tool_assembly.id,
stock_lot.create_stock_quant(location_inventory_ids[-1], stock_location_id, functional_tool_assembly.id,
obj.assembly_order_code, obj, obj.after_tool_groups_id)
return stock_lot
def get_stock_lot_name(self, tool_assembly_order_id):
def get_stock_lot_name(self, obj):
"""
生成功能刀具序列号
"""
tool_assembly_order = self.env['sf.functional.tool.assembly.order'].search(
[('id', '=', tool_assembly_order_id)])
code = 'JKM-T-' + str(tool_assembly_order.after_assembly_functional_tool_type_id.code) + '-' + str(
tool_assembly_order.after_assembly_functional_tool_diameter) + '-'
company = obj.cutting_tool_cutterhandle_model_id.code.split('-', 1)[0]
new_time = datetime.strptime(str(fields.Date.today()), "%Y-%m-%d").strftime("%Y%m%d")
code += str(new_time) + '-'
code = '%s-GNDJ-%s-%s' % (company, obj.after_assembly_functional_tool_type_id.code, new_time)
stock_lot_id = self.env['stock.lot'].sudo().search(
[('name', 'like', new_time), ('product_id.categ_type', '=', '功能刀具'),
('product_id.tracking', '=', 'serial')], limit=1, order="id desc")
[('name', 'like', code)], limit=1, order="id desc")
if not stock_lot_id:
num = "%03d" % 1
else:
m = int(stock_lot_id.name[-3:]) + 1
num = "%03d" % m
return code + str(num)
return '%s-%s' % (code, num)
def tool_material_stock_moves(self, tool_material, assembly_order_code):
"""
@@ -851,7 +889,7 @@ class ProductProduct(models.Model):
tool_material.create_stock_quant(location_inventory_id, stock_location_id, None, assembly_order_code, False,
False)
def material_stock_moves(self, shelf_location_barcode, assembly_order_code):
def material_stock_moves(self, shelf_location_barcode_id, lot_id, assembly_order_code):
# 创建库存移动记录
stock_move_id = self.env['stock.move'].sudo().create({
'name': assembly_order_code,
@@ -862,21 +900,16 @@ class ProductProduct(models.Model):
'state': 'done'
})
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', shelf_location_barcode)])
# 创建移动历史记录
stock_move_line_id = self.env['stock.move.line'].sudo().create({
'product_id': self.id,
'move_id': stock_move_id.id,
'current_location_id': location.id,
'lot_id': lot_id.lot_id.id,
'current_location_id': shelf_location_barcode_id.id,
'install_tool_time': fields.Datetime.now(),
'qty_done': 1.0,
'state': 'done',
})
if location.product_num > 0:
location.product_num = location.product_num - 1
else:
raise ValidationError(
'%s】货位的【%s】产品库存数量为零,请采购入库后再重新组装!' % (location.barcode, location.product_id.name))
return stock_move_id, stock_move_line_id
@@ -918,14 +951,13 @@ class StockLot(models.Model):
})
return stock_move_id, stock_move_line_id
class StockQuant(models.Model):
_inherit = 'stock.quant'
@api.model_create_multi
def create(self, vals_list):
records = super(StockQuant, self).create(vals_list)
for record in records:
if record.lot_id.product_id.categ_id.name == '刀具':
record.lot_id.enroll_tool_material_stock()
return records
# class StockQuant(models.Model):
# _inherit = 'stock.quant'
#
# @api.model_create_multi
# def create(self, vals_list):
# records = super(StockQuant, self).create(vals_list)
# for record in records:
# if record.lot_id.product_id.categ_id.name == '刀具':
# record.lot_id.enroll_tool_material_stock()
# return records

View File

@@ -166,11 +166,12 @@
<sheet>
<div class="oe_title">
<h1>
<field name="production_line_name_id"/>
<field name="assembly_order_code"/>
</h1>
</div>
<group>
<group>
<field name="production_line_name_id"/>
<field name="machine_tool_name_id"/>
<field name="cutter_spacing_code_id"/>
</group>
@@ -178,24 +179,26 @@
<field name="image" nolabel="1" widget="image"/>
</group>
</group>
<group string="功能刀具申请信息">
<group string="功能刀具申请信息" col="1">
<group>
<field name="functional_tool_name"/>
<field name="functional_tool_type_id"/>
<field name="tool_groups_id"/>
<field name="functional_tool_diameter" string="刀具直径(mm)"/>
<field name="knife_tip_r_angle"/>
<field name="tool_loading_length"/>
<field name="functional_tool_length"/>
<field name="effective_length"/>
</group>
<group>
<field name="whether_standard_knife"/>
<field name="coarse_middle_thin"/>
<field name="new_former"/>
<field name="use_tool_time"/>
<field name="reason_for_applying"/>
<!-- <field name="functional_tool_cutting_type"/>-->
<group>
<field name="functional_tool_name"/>
<field name="functional_tool_type_id"/>
<field name="tool_groups_id"/>
<field name="functional_tool_diameter" string="刀具直径(mm)"/>
<field name="knife_tip_r_angle"/>
<field name="tool_loading_length"/>
<field name="functional_tool_length"/>
<field name="effective_length"/>
</group>
<group>
<field name="whether_standard_knife"/>
<field name="coarse_middle_thin"/>
<field name="new_former"/>
<field name="use_tool_time"/>
<field name="reason_for_applying"/>
<!-- <field name="functional_tool_cutting_type"/>-->
</group>
</group>
</group>
<group string="组装物料信息" col="1">
@@ -226,13 +229,18 @@
</group>
</group>
<group col="1"
attrs="{'invisible': ['|','|',('blade_freight_barcode', '!=', False),('bar_freight_barcode', '!=', False),('pad_freight_barcode', '!=', False)]}">
attrs="{'invisible': ['|','|',('blade_freight_barcode_id', '!=', False),('bar_freight_barcode_id', '!=', False),('pad_freight_barcode_id', '!=', False)]}">
<div>
<separator string="整体式刀具:" style="font-size: 13px;"/>
</div>
<group>
<group>
<field name="integral_freight_barcode" string="货位"/>
<field name="integral_freight_barcode_id" options="{'no_create': True}"
placeholder="请选择" string="货位"/>
</group>
<group>
<field name="integral_freight_lot_id" options="{'no_create': True}"
placeholder="请选择" string="批次"/>
</group>
</group>
<group col="2">
@@ -246,13 +254,18 @@
</group>
</group>
</group>
<group col="1" attrs="{'invisible': [('integral_freight_barcode', '!=', False)]}">
<group col="1" attrs="{'invisible': [('integral_freight_barcode_id', '!=', False)]}">
<div>
<separator string="刀片:" style="font-size: 13px;"/>
</div>
<group>
<group>
<field name="blade_freight_barcode" string="货位"/>
<field name="blade_freight_barcode_id" options="{'no_create': True}"
placeholder="请选择" string="货位"/>
</group>
<group>
<field name="blade_freight_lot_id" options="{'no_create': True}"
placeholder="请选择" string="批次"/>
</group>
</group>
<group col="2">
@@ -267,13 +280,18 @@
</group>
</group>
<group col="1"
attrs="{'invisible': ['|',('integral_freight_barcode', '!=', False),('pad_freight_barcode', '!=', False)]}">
attrs="{'invisible': ['|',('integral_freight_barcode_id', '!=', False),('pad_freight_barcode_id', '!=', False)]}">
<div>
<separator string="刀杆:" style="font-size: 13px;"/>
</div>
<group>
<group>
<field name="bar_freight_barcode" string="货位"/>
<field name="bar_freight_barcode_id" options="{'no_create': True}"
placeholder="请选择" string="货位"/>
</group>
<group>
<field name="bar_freight_lot_id" options="{'no_create': True}" placeholder="请选择"
string="批次"/>
</group>
</group>
<group col="2">
@@ -288,13 +306,18 @@
</group>
</group>
<group col="1"
attrs="{'invisible': ['|',('integral_freight_barcode', '!=', False),('bar_freight_barcode', '!=', False)]}">
attrs="{'invisible': ['|',('integral_freight_barcode_id', '!=', False),('bar_freight_barcode_id', '!=', False)]}">
<div>
<separator string="刀盘:" style="font-size: 13px;"/>
</div>
<group>
<group>
<field name="pad_freight_barcode" string="货位"/>
<field name="pad_freight_barcode_id" options="{'no_create': True}"
placeholder="请选择" string="货位"/>
</group>
<group>
<field name="pad_freight_lot_id" options="{'no_create': True}" placeholder="请选择"
string="批次"/>
</group>
</group>
<group col="2">
@@ -314,7 +337,12 @@
</div>
<group>
<group>
<field name="chuck_freight_barcode" string="货位"/>
<field name="chuck_freight_barcode_id" options="{'no_create': True}"
placeholder="请选择" string="货位"/>
</group>
<group>
<field name="chuck_freight_lot_id" options="{'no_create': True}"
placeholder="请选择" string="批次"/>
</group>
</group>
<group col="2">
@@ -330,53 +358,72 @@
</group>
</group>
<group string="组装参数信息">
<group string="组装参数信息" col="1">
<group>
<group>
<field name="barcode_id" invisible="True"/>
<field name="code" readonly="True"/>
<field name="rfid" class="custom_required"/>
</group>
<group>
<field name="obtain_measurement_status" invisible="1"/>
<button name="get_tool_preset_parameter" string="获取测量值" type="object"
attrs="{'invisible': [('enable_tool_presetter', '=', False)]}"
class="btn-primary"/>
</group>
</group>
<group>
<group>
<field name="after_name_id" string="功能刀具名称" placeholder="请选择功能刀具名称"
readonly="1"
options="{'no_create': True, 'no_quick_create': True}"/>
<field name="after_assembly_functional_tool_name" string="功能刀具名称" invisible="1"/>
<field name="after_assembly_functional_tool_type_id" string="功能刀具类型"
<field name="after_assembly_functional_tool_name" string="功能刀具名称"
invisible="1"/>
<field name="after_assembly_functional_tool_type_id" string="功能刀具类型" readonly="1"
options="{'no_create': True, 'no_quick_create': True}"/>
<field name="after_tool_groups_id"
<field name="after_tool_groups_id" readonly="1"
options="{'no_create': True, 'no_quick_create': True}"/>
<field name="after_assembly_functional_tool_diameter" string="刀具直径(mm)"
class="custom_required"/>
<field name="after_assembly_knife_tip_r_angle" string="刀尖R角(mm)"
class="custom_required"/>
<field name="after_assembly_whether_standard_knife" string="是否标准刀"/>
<field name="after_assembly_coarse_middle_thin" string="粗/中/精"/>
<field name="after_assembly_new_former" string="新/旧"/>
<field name="cut_time" attrs="{'invisible': [('after_assembly_new_former','=','0')]}"/>
<field name="cut_time"
attrs="{'invisible': [('after_assembly_new_former','=','0')]}"/>
<field name="cut_length"
attrs="{'invisible': [('after_assembly_new_former','=','0')]}"/>
<field name="cut_number"
attrs="{'invisible': [('after_assembly_new_former','=','0')]}"/>
</group>
</group>
<group>
<group>
<field name="after_assembly_whether_standard_knife" string="是否标准刀"/>
<field name="after_assembly_coarse_middle_thin" string="粗/中/精"/>
<field name="after_assembly_max_lifetime_value" string="最大寿命值(min)"
<field name="enable_tool_presetter" invisible="1"/>
<field name="after_assembly_functional_tool_diameter" string="刀具直径(mm)"
attrs="{'readonly': [('enable_tool_presetter', '=', True)]}"
class="custom_required"/>
<field name="after_assembly_knife_tip_r_angle" string="刀尖R角(mm)"
attrs="{'readonly': [('enable_tool_presetter', '=', True)]}"
class="custom_required"/>
<field name="after_assembly_alarm_value" string="报警值(min)" class="custom_required"/>
<field name="after_assembly_used_value" string="已使用值(min)"/>
<field name="after_assembly_tool_loading_length" string="总长度(mm)"
attrs="{'readonly': [('enable_tool_presetter', '=', True)]}"
class="custom_required"/>
<field name="after_assembly_handle_length" string="刀柄长度(mm)"
class="custom_required"/>
<field name="after_assembly_functional_tool_length" string="伸出长(mm)"
class="custom_required"/>
<field name="after_assembly_effective_length" string="有效长(mm)"
<field name="after_assembly_max_lifetime_value" string="最大寿命值(min)"
class="custom_required"/>
<field name="hiding_length" class="custom_required"/>
<field name="L_D_number"/>
<field name="after_assembly_alarm_value" string="报警值(min)"
class="custom_required"/>
<field name="after_assembly_used_value" string="已使用值(min)" invisible="1"/>
<field name="after_assembly_effective_length" string="有效长(mm)" invisible="1"
class="custom_required"/>
<field name="hiding_length" class="custom_required" invisible="1"/>
<field name="L_D_number" invisible="1"/>
</group>
</group>
</group>
</sheet>
<footer>
<button string="确定" name="functional_tool_assembly" type="object" class="btn-primary"
attrs="{'invisible': [('obtain_measurement_status', '=', False),('enable_tool_presetter', '=', True)]}"
confirm="是否确认申请组装"/>
<button string="取消" class="btn-secondary" special="cancel"/>
</footer>

View File

@@ -452,8 +452,9 @@ class ShelfLocation(models.Model):
# product_id = fields.Many2one('product.template', string='产品')
product_id = fields.Many2one('product.product', string='产品', compute='_compute_product_id', store=True)
product_sn_id = fields.Many2one('stock.lot', string='产品序列号')
product_sn_ids = fields.One2many('sf.shelf.location.lot', 'shelf_location_id', string='产品批次号')
# 产品数量
product_num = fields.Integer('数量')
product_num = fields.Integer('数量', compute='_compute_number', store=True)
@api.depends('product_num')
def _compute_product_num(self):
@@ -463,6 +464,15 @@ class ShelfLocation(models.Model):
elif record.product_num == 0:
record.location_status = '空闲'
@api.depends('product_sn_ids.qty')
def _compute_number(self):
for item in self:
if item.product_sn_ids:
qty = 0
for product_sn_id in item.product_sn_ids:
qty += product_sn_id.qty
item.product_num = qty
# 修改货位状态为禁用
def action_location_status_disable(self):
self.location_status = '禁用'
@@ -471,7 +481,7 @@ class ShelfLocation(models.Model):
def action_location_status_enable(self):
self.location_status = '空闲'
@api.depends('product_sn_id')
@api.depends('product_sn_id', 'product_sn_ids')
def _compute_product_id(self):
"""
根据产品序列号,获取产品
@@ -484,7 +494,8 @@ class ShelfLocation(models.Model):
record.sudo().product_num = 1
except Exception as e:
print('eeeeeee占用', e)
elif record.product_sn_ids:
return True
else:
try:
record.sudo().product_id = False
@@ -525,7 +536,24 @@ class ShelfLocation(models.Model):
return records
class Sf_stock_move_line(models.Model):
class SfShelfLocationLot(models.Model):
_name = 'sf.shelf.location.lot'
_description = '批次数量'
name = fields.Char('名称', related='lot_id.name')
shelf_location_id = fields.Many2one('sf.shelf.location', string="货位")
lot_id = fields.Many2one('stock.lot', string='批次号')
qty = fields.Integer('数量')
qty_num = fields.Integer('变更数量')
@api.onchange('qty_num')
def _onchange_qty_num(self):
for item in self:
if item.qty_num > item.qty:
raise ValidationError('变更数量不能比库存数量大!!!')
class SfStockMoveLine(models.Model):
_name = 'stock.move.line'
_inherit = ['stock.move.line', 'printing.utils']
@@ -825,15 +853,20 @@ class Sf_stock_move_line(models.Model):
obj = self.env['sf.shelf.location'].search([('name', '=',
self.destination_location_id.name)])
if record.lot_id:
shelf_location_obj = self.env['sf.shelf.location'].search(
[('product_sn_id', '=', record.lot_id.id)])
if shelf_location_obj:
shelf_location_obj.product_sn_id = False
if obj:
obj.product_sn_id = record.lot_id.id
else:
if obj:
obj.product_sn_id = record.lot_id.id
if record.product_id.tracking == 'serial':
shelf_location_obj = self.env['sf.shelf.location'].search(
[('product_sn_id', '=', record.lot_id.id)])
if shelf_location_obj:
shelf_location_obj.product_sn_id = False
if obj:
obj.product_sn_id = record.lot_id.id
else:
if obj:
obj.product_sn_id = record.lot_id.id
elif record.product_id.tracking == 'lot':
self.put_shelf_location(record)
if not obj.product_id:
obj.product_id = record.product_id.id
else:
if obj:
obj.product_id = record.product_id.id
@@ -853,20 +886,83 @@ class Sf_stock_move_line(models.Model):
raise ValidationError(
'%s】货位已经被占用,请重新选择!!!' % item.destination_location_id.barcode)
def put_shelf_location(self, vals):
"""
对货位的批量数据进行数量计算
"""
for record in vals:
if record.lot_id and record.product_id.tracking == 'lot':
if record.current_location_id:
location_lot = self.env['sf.shelf.location.lot'].sudo().search(
[('shelf_location_id', '=', record.current_location_id.id), ('lot_id', '=', record.lot_id.id)])
if location_lot:
location_lot.qty -= record.qty_done
if location_lot.qty == 0:
location_lot.unlink()
elif location_lot.qty < 0:
raise ValidationError('%s】货位【%s】批次的【%s】产品数量不足!' % (
record.current_location_id.barcode, record.lot_id.name, record.product_id.name))
else:
raise ValidationError('%s】货位不存在【%s】批次的【%s】产品' % (
record.current_location_id.barcode, record.lot_id.name, record.product_id.name))
if record.destination_location_id:
location_lot = self.env['sf.shelf.location.lot'].sudo().search(
[('shelf_location_id', '=', record.destination_location_id.id),
('lot_id', '=', record.lot_id.id)])
if location_lot:
location_lot.qty += record.qty_done
else:
self.env['sf.shelf.location.lot'].sudo().create({
'shelf_location_id': record.destination_location_id.id,
'lot_id': record.lot_id.id,
'qty': record.qty_done
})
if not record.destination_location_id.product_id:
record.destination_location_id.product_id = record.product_id.id
@api.model_create_multi
def create(self, vals_list):
records = super(SfStockMoveLine, self).create(vals_list)
self.put_shelf_location(records)
return records
class SfStockPicking(models.Model):
_inherit = 'stock.picking'
check_in = fields.Char(string='查询是否为入库单', compute='_check_is_in')
def batch_stock_move(self):
"""
批量调拨,非就绪状态的会被忽略,完成后有通知提示
"""
for record in self:
if record.state != 'assigned':
continue
record.action_set_quantities_to_reservation()
record.button_validate()
notification_message = '批量调拨完成!请注意,状态非就绪的单据会被忽略'
return {
'effect': {
'fadeout': 'fast',
'message': notification_message,
'img_url': '/web/image/%s/%s/image_1024' % (
self.create_uid._name, self.create_uid.id) if 0 else '/web/static/img/smile.svg',
'type': 'rainbow_man',
}
}
@api.depends('name')
def _check_is_in(self):
"""
判断是否为出库单
"""
if self.name:
is_check_in = self.name.split('/')
self.check_in = is_check_in[1]
for record in self:
if record.name:
is_check_in = record.name.split('/')
record.check_in = is_check_in[1]
def button_validate(self):
"""
@@ -894,6 +990,9 @@ class SfStockPicking(models.Model):
if move and move.product_id.cutting_tool_material_id.name == '刀柄' or '托盘' in (
move.product_id.fixture_material_id.name or ''):
for item in move.move_line_nosuggest_ids:
if item.rfid:
if self.env['stock.lot'].search([('rfid', '=', item.rfid)]):
raise ValidationError('该Rfid【%s】在系统中已经存在,请重新录入!' % item.rfid)
if item.location_dest_id.name == '进货':
if not item.rfid:
raise ValidationError('你需要提供%s的Rfid' % move.product_id.name)
@@ -1044,7 +1143,10 @@ class CustomStockMove(models.Model):
move_lines = self.move_line_ids # 获取当前 stock.move 对应的所有 stock.move.line 记录
for line in move_lines:
if line.lot_name: # 确保 lot_name 存在
qr_data = self.compute_lot_qr_code(line.lot_name)
lot_name = line.lot_name
if line.product_id.categ_id.name == '坯料':
lot_name = lot_name.split('[', 1)[0]
qr_data = self.compute_lot_qr_code(lot_name)
# 假设 stock.move.line 模型中有一个字段叫做 lot_qr_code 用于存储二维码数据
line.lot_qr_code = qr_data
return result

View File

@@ -20,7 +20,7 @@ class MrsShelfLocationDataSync(models.Model):
paired_data = list(zip(my_data, their_data))
return paired_data
shelf_1_obj = self.env['sf.shelf'].search([('name', '=', '一号线边刀架')], limit=1)
shelf_1_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-一号线边刀架')], limit=1)
tool_location_objs_1 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_1_obj.id)], order='id')
location_codes_1 = [location.barcode for location in tool_location_objs_1]
@@ -32,7 +32,7 @@ class MrsShelfLocationDataSync(models.Model):
aligned_data_1 = align_data(location_codes_1, their_data_1)
# 2
shelf_2_obj = self.env['sf.shelf'].search([('name', '=', '二号线边刀架')], limit=1)
shelf_2_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-二号线边刀架')], limit=1)
tool_location_objs_2 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_2_obj.id)], order='id')
location_codes_2 = [location.barcode for location in tool_location_objs_2]
@@ -44,7 +44,7 @@ class MrsShelfLocationDataSync(models.Model):
aligned_data_2 = align_data(location_codes_2, their_data_2)
# 4
shelf_4_obj = self.env['sf.shelf'].search([('name', '=', '一号线边料架')], limit=1)
shelf_4_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-一号线边料架')], limit=1)
tool_location_objs_4 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_4_obj.id)], order='id')
location_codes_4 = [location.barcode for location in tool_location_objs_4]
@@ -56,7 +56,7 @@ class MrsShelfLocationDataSync(models.Model):
aligned_data_4 = align_data(location_codes_4, their_data_4)
# 3
shelf_3_obj = self.env['sf.shelf'].search([('name', '=', '一号线边料架')], limit=1)
shelf_3_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-二号线边料架')], limit=1)
tool_location_objs_3 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_3_obj.id)], order='id')
location_codes_3 = [location.barcode for location in tool_location_objs_3]
@@ -68,7 +68,7 @@ class MrsShelfLocationDataSync(models.Model):
aligned_data_3 = align_data(location_codes_3, their_data_3)
# 5
shelf_5_obj = self.env['sf.shelf'].search([('name', '=', '一号线边料架')], limit=1)
shelf_5_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-三号线边料架')], limit=1)
tool_location_objs_5 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_5_obj.id)], order='id')
location_codes_5 = [location.barcode for location in tool_location_objs_5]
@@ -95,17 +95,34 @@ class MrsShelfLocationDataSync(models.Model):
return code_pair[0]
return None # 如果没有找到对应的值返回None或适当的默认值
# 定时更新所有设备机床刀库信息
equipment_ids = self.env['maintenance.equipment'].search(
[('equipment_type', '=', '机床'), ('function_type', '!=', False)])
for equipment_id in equipment_ids:
if equipment_id:
equipment_id.register_equipment_tool()
shelfinfo = self.env['sf.shelf.location'].get_sf_shelf_location_info()
print('shelfinfo:', shelfinfo)
for item in shelfinfo:
shelf_barcode = find_our_code(item['Postion'], total_data)
location_id = self.env['sf.shelf.location'].search([('barcode', '=', shelf_barcode)], limit=1)
if location_id:
stock_lot_obj = self.env['stock.lot'].search([('rfid', '=', item['RfidCode'])], limit=1)
if stock_lot_obj:
location_id.product_sn_id = stock_lot_obj.id
# 如果是线边刀库信息,则对功能刀具移动生成记录
if 'Tool' in item['Postion']:
tool = self.env['sf.functional.cutting.tool.entity'].sudo().search(
[('rfid', '=', item['RfidCode']), ('functional_tool_status', '!=', '已拆除')])
tool.tool_in_out_stock_location(location_id)
if tool:
location_id.product_sn_id = tool.barcode_id.id
else:
location_id.product_sn_id = False
else:
location_id.product_sn_id = False
stock_lot_obj = self.env['stock.lot'].search([('rfid', '=', item['RfidCode'])], limit=1)
if stock_lot_obj:
location_id.product_sn_id = stock_lot_obj.id
else:
location_id.product_sn_id = False
logging.info('货架已获取信息:%s' % item)
except Exception as e:

View File

@@ -1,7 +1,9 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sf_shelf_location_group_sf_stock_user_group_sf_stock_user,sf.shelf.location,model_sf_shelf_location,sf_base.group_sf_stock_user,1,0,0,0
access_sf_shelf_location_lot_group_sf_stock_user_group_sf_stock_user,sf.shelf.location.lot,model_sf_shelf_location_lot,sf_base.group_sf_stock_user,1,0,0,0
access_sf_shelf_location_group_sf_stock_manager,sf.shelf.location,model_sf_shelf_location,sf_base.group_sf_stock_manager,1,1,1,0
access_sf_shelf_location_lot_group_sf_stock_manager,sf.shelf.location.lot,model_sf_shelf_location_lot,sf_base.group_sf_stock_manager,1,1,1,0
access_sf_shelf_group_sf_stock_user_group_sf_stock_user,sf.shelf.group.sf.stock.user,model_sf_shelf,sf_base.group_sf_stock_user,1,0,0,0
access_sf_shelf_group_sf_stock_manager,sf.shelf.group.sf.stock.manager,model_sf_shelf,sf_base.group_sf_stock_manager,1,1,1,0
@@ -101,6 +103,7 @@ access_stock_replenish_option_group_sf_stock_user,stock.replenishment.option,sto
access_mrp_production_group_sf_stock_user,mrp.production,mrp.model_mrp_production,sf_base.group_sf_stock_user,1,1,1,0
access_sf_shelf_location_group_plan_dispatch,sf.shelf.location,model_sf_shelf_location,sf_base.group_plan_dispatch,1,0,0,0
access_sf_shelf_location_lot_group_plan_dispatch,sf.shelf.location.lot,model_sf_shelf_location_lot,sf_base.group_plan_dispatch,1,0,0,0
access_stock_move,stock.move,stock.model_stock_move,sf_base.group_plan_dispatch,1,1,1,0
access_stock_picking_group_plan_dispatch,stock.picking,stock.model_stock_picking,sf_base.group_plan_dispatch,1,0,0,0
access_stock_lot_group_plan_dispatch,stock.lot,stock.model_stock_lot,sf_base.group_plan_dispatch,1,0,0,0
@@ -142,6 +145,9 @@ access_sf_shelf_location_wizard_group_sf_stock_manager,sf_shelf_location_wizard_
access_sf_shelf_location_group_sf_tool_user,sf.shelf.location.group_sf_tool_user,model_sf_shelf_location,sf_base.group_sf_tool_user,1,1,0,0
access_sf_shelf_group_user,sf.shelf.location.group_user,model_sf_shelf_location,base.group_user,1,1,0,0
access_sf_shelf_location_lot_group_sf_tool_user,sf.shelf.location.lot.group_sf_tool_user,model_sf_shelf_location_lot,sf_base.group_sf_tool_user,1,1,0,0
access_sf_shelf_lot_group_user,sf.shelf.location.lot.group_user,model_sf_shelf_location_lot,base.group_user,1,1,0,0
access_ir_model_group_sf_stock_user,ir_model_group_sf_stock_user,base.model_ir_model,sf_base.group_sf_stock_user,1,1,0,0
access_mrp_workorder_group_sf_stock_user,mrp_workorder_group_sf_stock_user,mrp.model_mrp_workorder,sf_base.group_sf_stock_user,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_sf_shelf_location_group_sf_stock_user_group_sf_stock_user sf.shelf.location model_sf_shelf_location sf_base.group_sf_stock_user 1 0 0 0
3 access_sf_shelf_location_group_sf_stock_manager access_sf_shelf_location_lot_group_sf_stock_user_group_sf_stock_user sf.shelf.location sf.shelf.location.lot model_sf_shelf_location model_sf_shelf_location_lot sf_base.group_sf_stock_manager sf_base.group_sf_stock_user 1 1 0 1 0 0
4 access_sf_shelf_location_group_sf_stock_manager sf.shelf.location model_sf_shelf_location sf_base.group_sf_stock_manager 1 1 1 0
5 access_sf_shelf_group_sf_stock_user_group_sf_stock_user access_sf_shelf_location_lot_group_sf_stock_manager sf.shelf.group.sf.stock.user sf.shelf.location.lot model_sf_shelf model_sf_shelf_location_lot sf_base.group_sf_stock_user sf_base.group_sf_stock_manager 1 0 1 0 1 0
6 access_sf_shelf_group_sf_stock_user_group_sf_stock_user sf.shelf.group.sf.stock.user model_sf_shelf sf_base.group_sf_stock_user 1 0 0 0
7 access_sf_shelf_group_sf_stock_manager sf.shelf.group.sf.stock.manager model_sf_shelf sf_base.group_sf_stock_manager 1 1 1 0
8 access_procurement_group procurement.group stock.model_procurement_group base.group_user 1 1 1 0
9 access_stock_warehouse_manager_group_sf_stock_user stock.warehouse.manager stock.model_stock_warehouse sf_base.group_sf_stock_user 1 1 1 0
103 access_stock_picking_group_plan_dispatch access_stock_move stock.picking stock.move stock.model_stock_picking stock.model_stock_move sf_base.group_plan_dispatch 1 0 1 0 1 0
104 access_stock_lot_group_plan_dispatch access_stock_picking_group_plan_dispatch stock.lot stock.picking stock.model_stock_lot stock.model_stock_picking sf_base.group_plan_dispatch 1 0 0 0
105 access_stock_lot_group_plan_director access_stock_lot_group_plan_dispatch stock.lot stock.model_stock_lot sf_base.group_plan_director sf_base.group_plan_dispatch 1 1 0 1 0 0
106 access_stock_lot_group_plan_director stock.lot stock.model_stock_lot sf_base.group_plan_director 1 1 1 0
107 access_stock_warehouse_orderpoint stock.warehouse.orderpoint stock.model_stock_warehouse_orderpoint sf_base.group_plan_dispatch 1 1 0 0
108 access_stock_inventory_conflict stock.inventory.conflict stock.model_stock_inventory_conflict sf_base.group_plan_dispatch 1 0 0 0
109 access_stock_inventory_warning stock.inventory.warning stock.model_stock_inventory_warning sf_base.group_plan_dispatch 1 0 0 0
145
146
147
148
149
150
151
152
153

View File

@@ -149,6 +149,9 @@
<button name="action_assign" type="object" string="检查可用量"
groups="sf_base.group_sf_stock_user"/>
</xpath>
<xpath expr="//header" position="inside">
<button name="batch_stock_move" type='object' string="批量调拨"/>
</xpath>
</field>
</record>

View File

@@ -133,9 +133,11 @@
type="action"
context="{'default_name':name,
'default_current_name':name,
'default_current_product_sn_ids':product_sn_ids,
'default_lot_id':product_sn_id,
'default_current_shelf_id':shelf_id,
'default_current_location_id':location_id,
'default_current_barcode':barcode,
'default_current_barcode_id':id,
'default_current_product_id':product_id,
}"
class="btn-primary" attrs="{'invisible':[('location_status','!=','占用')]}"/>
@@ -169,8 +171,16 @@
<field name="name" readonly="1"/>
<field name="shelf_id" readonly="1"/>
<field name="location_id" readonly="1"/>
<field name="product_sn_id" options="{'no_create': True}"/>
<field name="product_id"/>
<field name="product_sn_id" options="{'no_create': True}"
attrs="{'invisible': [('product_sn_ids', '!=', [])]}"/>
<field name="product_sn_ids"
attrs="{'invisible': [('product_sn_ids', '=', [])]}">
<tree edit="1" create="0" delete="0" editable="bottom">
<field name="lot_id" readonly="1"/>
<field name="qty" readonly="1"/>
</tree>
</field>
<field name="product_num" readonly="1"/>
<field name="location_status"/>
<field name="storage_time" widget="datetime"/>

View File

@@ -8,17 +8,23 @@ class ShelfLocationWizard(models.TransientModel):
name = fields.Char('')
current_location_id = fields.Many2one('stock.location', string='所属库区', readonly=True)
lot_id = fields.Many2one('stock.lot', string="序列号", readonly=True)
current_location_id = fields.Many2one('stock.location', string='所属库区', readonly=True)
current_shelf_id = fields.Many2one('sf.shelf', string='当前货架', readonly=True)
current_barcode = fields.Char('当前货位编码', readonly=True)
current_barcode_id = fields.Many2one('sf.shelf.location', string='当前货位编码', readonly=True)
current_name = fields.Char('当前货位名称', readonly=True)
current_product_id = fields.Many2one('product.product', string='产品', readonly=True)
current_product_sn_ids = fields.Many2many('sf.shelf.location.lot', 'shelf_location_wizard', string='产品批次号',
readonly=True)
destination_location_id = fields.Many2one('stock.location', string='目标库区', compute='_compute_destination_name')
destination_shelf_id = fields.Many2one('sf.shelf', string='目标货架', compute='_compute_destination_name')
destination_barcode_id = fields.Many2one('sf.shelf.location', string='目标货位编码', required=True,
domain="")
domain="[('product_id', 'in', (False, current_product_id))]")
destination_name = fields.Char('目标货位名称', compute='_compute_destination_name')
destination_product_sn_ids = fields.Many2many('sf.shelf.location.lot', 'shelf_location_wizard', string='批次号',
domain="[('shelf_location_id', '=', current_barcode_id)]")
def return_domain(self):
val = [('location_status', '=', '空闲')]
@@ -32,9 +38,11 @@ class ShelfLocationWizard(models.TransientModel):
def _compute_destination_name(self):
if self.destination_barcode_id:
self.destination_name = self.destination_barcode_id.name
self.destination_location_id = self.destination_barcode_id.location_id.id
self.destination_shelf_id = self.destination_barcode_id.shelf_id.id
else:
self.destination_name = ''
self.destination_location_id = False
self.destination_shelf_id = False
#
@@ -43,22 +51,46 @@ class ShelfLocationWizard(models.TransientModel):
# if self.destination_barcode_id:
# self.destination_shelf_id = self.destination_barcode_id.shelf_id.id
def confirm_the_change(self):
shelf_location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', self.current_barcode)])
# 变更货位
if self.destination_barcode_id and shelf_location:
if self.destination_barcode_id.product_id and self.destination_barcode_id.product_id == shelf_location.product_id and not self.destination_barcode_id.product_sn_id:
self.destination_barcode_id.product_num += shelf_location.product_num
else:
self.destination_barcode_id.product_sn_id = shelf_location.product_sn_id.id
self.destination_barcode_id.product_id = shelf_location.product_id.id
self.destination_barcode_id.product_num = shelf_location.product_num
def create_stock_moves(self, lot_id, num):
# 创建产品货位变更的库存移动记录
stock_move_id = self.env['stock.move'].sudo().create({
'name': 'HWBG/%s' % self.id,
'product_id': self.current_product_id.id,
'location_id': self.current_location_id.id,
'location_dest_id': self.destination_location_id.id,
'product_uom_qty': num,
'state': 'done'
})
# 创建移动历史记录
stock_move_line_id = self.env['stock.move.line'].sudo().create({
'product_id': self.current_product_id.id,
'lot_id': lot_id.id,
'move_id': stock_move_id.id,
'current_location_id': self.current_barcode_id.id,
'destination_location_id': self.destination_barcode_id.id,
'install_tool_time': fields.Datetime.now(),
'qty_done': num,
'state': 'done'
})
shelf_location.product_sn_id = False
shelf_location.product_id = False
shelf_location.product_num = 0
return stock_move_id, stock_move_line_id
def confirm_the_change(self):
if self.destination_barcode_id:
if self.lot_id:
self.current_barcode_id.product_sn_id = False
self.destination_barcode_id.product_sn_id = self.lot_id.id
self.create_stock_moves(self.lot_id, 1)
elif self.current_product_sn_ids:
for current_product_sn_id in self.current_product_sn_ids:
self.create_stock_moves(current_product_sn_id.lot_id, current_product_sn_id.qty_num)
current_product_sn_id.write({
'qty_num': 0
})
else:
raise ValidationError('没有需要变更的批次/序列号!')
else:
raise ValidationError('目标货位出错,请联系管理员!')
raise ValidationError('请选择目标货位编码!')
# 关闭弹出窗口
return {'type': 'ir.actions.act_window_close'}

View File

@@ -6,27 +6,45 @@
<field name="arch" type="xml">
<form string="货位变更">
<sheet>
<group>
<group col="1">
<group string="初始货位">
<group>
<field name="current_location_id"/>
<field name="current_shelf_id" string="货架"/>
<field name="current_barcode" string="编码"/>
<field name="current_name" string="名称"/>
</group>
<group>
<field name="current_barcode_id" string="编码"/>
<field name="lot_id" attrs="{'invisible': [('lot_id', '=', False)]}"/>
</group>
<field name="current_product_sn_ids"
attrs="{'invisible': [('current_product_sn_ids', '=', [])]}">
<tree edit="1" create="0" delete="0" editable="bottom">
<field name="lot_id" readonly="1"/>
<field name="qty" readonly="1"/>
</tree>
</field>
</group>
<group string="目标货位">
<group>
<field name="current_location_id"/>
<field name="destination_shelf_id" string="货架" options="{'no_create': True}"
placeholder="请选择目标货架"/>
<field name="destination_barcode_id" string="编码" options="{'no_create': True}"
placeholder="请选择目标货位"
domain="['|', ('location_status', '=', '空闲'), ('product_id', '=', current_product_id)]"/>
<field name="destination_location_id"/>
<field name="destination_shelf_id" string="货架" options="{'no_create': True}"/>
<field name="destination_name" string="名称"/>
<field name="current_product_id" invisible="1"/>
</group>
<group>
<field name="destination_barcode_id" string="编码" options="{'no_create': True}"
placeholder="请选择目标货位"/>
<field name="lot_id" attrs="{'invisible': [('lot_id', '=', False)]}"/>
</group>
</group>
<field name="destination_product_sn_ids"
attrs="{'invisible': [('current_product_sn_ids', '=', [])]}">
<tree edit="1" create="0" delete="1" editable="bottom">
<field name="lot_id" readonly="1"/>
<field name="qty_num"/>
</tree>
</field>
</group>
</sheet>
<footer>