Compare commits

...

179 Commits

Author SHA1 Message Date
胡尧
6ed5de6400 修改出厂检验报告预览数据 2025-06-09 09:08:26 +08:00
胡尧
03ec94d223 修复点击智能按钮点击的问题 2025-06-06 09:33:23 +08:00
胡尧
2ea24f2049 修改制造订单的采购申请只能按钮判断规则 2025-06-06 09:16:41 +08:00
胡尧
0b5415dc47 在出厂检验报告验证数量时,增加产品的匹配 2025-06-06 08:37:38 +08:00
胡尧
4e1be6f4d5 修复二次弹窗不能刷新页面的问题,但是js方法还需要修改 2025-06-06 08:29:25 +08:00
胡尧
540b7bcbea 修改质检单字段显示 2025-06-05 10:19:58 +08:00
禹翔辉
001900bd65 Accept Merge Request #2164: (feature/销售合同优化 -> develop)
Merge Request: Merge branch 'feature/采购消息通知' into feature/销售合同优化

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2164
2025-06-05 09:19:28 +08:00
yuxianghui
0204e0e24f Merge branch 'feature/采购消息通知' into feature/销售合同优化 2025-06-05 09:17:26 +08:00
yuxianghui
2b3a2dd21c 1、处理返工报错,2、保存的合同文档添加对应销售订单号 2025-06-05 09:16:02 +08:00
胡尧
57acad4716 Accept Merge Request #2163: (feature/6694 -> develop)
Merge Request: 调整采购申请菜单位置

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2163?initial=true
2025-06-05 09:15:46 +08:00
胡尧
9f97c82a46 调整采购申请菜单位置 2025-06-05 09:15:25 +08:00
胡尧
7cafddd345 Accept Merge Request #2162: (feature/6694 -> develop)
Merge Request: 修改出厂检验报告重新发布按钮,增加确认提示

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2162?initial=true
2025-06-05 08:59:10 +08:00
胡尧
c796697a8e 修改出厂检验报告重新发布按钮,增加确认提示 2025-06-05 08:56:34 +08:00
胡尧
ee523e9aac Accept Merge Request #2161: (feature/6694 -> develop)
Merge Request: 出厂检验报告发布按钮增加判断

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2161?initial=true
2025-06-04 17:36:24 +08:00
胡尧
87fdc7bf74 出厂检验报告发布按钮增加判断 2025-06-04 17:35:57 +08:00
禹翔辉
e7d84e9df2 Accept Merge Request #2160: (feature/采购消息通知 -> develop)
Merge Request: Merge branch 'feature/销售订单优化' into feature/采购消息通知

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2160
2025-06-04 16:28:15 +08:00
yuxianghui
b7642d1e0f Merge branch 'feature/销售订单优化' into feature/采购消息通知 2025-06-04 16:26:03 +08:00
yuxianghui
05ffbdcc78 采购申请、待质检消息通知优化 2025-06-04 16:24:52 +08:00
胡尧
4b8d00ec1d Accept Merge Request #2159: (feature/6694 -> develop)
Merge Request: 修复质检字段显示错误问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2159?initial=true
2025-06-04 16:06:16 +08:00
胡尧
d2dbf4f986 修复质检字段显示错误问题 2025-06-04 16:05:51 +08:00
禹翔辉
edfd59468f Accept Merge Request #2158: (feature/销售订单优化 -> develop)
Merge Request: Merge branch 'feature/消息通知优化' into feature/销售订单优化

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2158
2025-06-04 11:36:18 +08:00
yuxianghui
d7f04b61b5 Merge branch 'feature/消息通知优化' into feature/销售订单优化 2025-06-04 11:33:04 +08:00
yuxianghui
573b50da68 销售订单添加合同字段,销售订单form页面新增合同page页,优化/api/bfm_process_order/list接口添加合同信息处理。 2025-06-04 11:31:28 +08:00
胡尧
2e0dfc5b02 Accept Merge Request #2157: (feature/6694 -> develop)
Merge Request: 修改没有状态码时默认为200

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2157?initial=true
2025-06-04 09:17:01 +08:00
胡尧
18cdc39719 修改没有状态码时默认为200 2025-06-04 09:16:28 +08:00
胡尧
14fa88da01 Accept Merge Request #2156: (feature/6694 -> develop)
Merge Request: 修改编程返回记录申请人不对的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2156?initial=true
2025-05-30 10:27:10 +08:00
胡尧
162814411f 修改编程返回记录申请人不对的问题 2025-05-30 10:26:34 +08:00
胡尧
49e4c88d83 Accept Merge Request #2155: (feature/6694 -> develop)
Merge Request: 解决下发编程单报错的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2155?initial=true
2025-05-29 16:00:16 +08:00
胡尧
db81114a07 解决下发编程单报错的问题 2025-05-29 15:59:48 +08:00
禹翔辉
e019383187 Accept Merge Request #2154: (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/2154
2025-05-29 14:19:17 +08:00
yuxianghui
4a09148b53 质检消息通知优化 2025-05-29 14:10:36 +08:00
胡尧
ab139daf02 Accept Merge Request #2153: (feature/6694 -> develop)
Merge Request: 出厂检验报告需求

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2153?initial=true
2025-05-29 10:18:49 +08:00
胡尧
24d83b70d2 出厂检验报告需求 2025-05-29 10:18:18 +08:00
禹翔辉
307510e1ab Accept Merge Request #2152: (feature/消息通知优化 -> develop)
Merge Request: Merge branch 'feature/采购申请消息通知' into feature/消息通知优化

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2152
2025-05-28 16:03:52 +08:00
yuxianghui
9b7222961c Merge branch 'feature/采购申请消息通知' into feature/消息通知优化 2025-05-28 15:58:06 +08:00
yuxianghui
3304398c4c 1、重写待质检提醒消息通知;2、优化采购申请已批准消息通知 2025-05-28 15:56:42 +08:00
胡尧
074e59cee4 Accept Merge Request #2151: (feature/6694 -> develop)
Merge Request: 修改编程下发,修改编程记录

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2151?initial=true
2025-05-28 14:08:43 +08:00
胡尧
7ab8ab47ac 修改编程下发,修改编程记录 2025-05-28 14:08:10 +08:00
禹翔辉
3a89ebff60 Accept Merge Request #2150: (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/2150
2025-05-28 09:58:22 +08:00
yuxianghui
d0ee2a6564 添加采购申请状态为待批准时的消息通知 2025-05-28 09:54:27 +08:00
黄焱
257d4a3b0a Accept Merge Request #2149: (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/2149?initial=true
2025-05-27 09:13:57 +08:00
胡尧
40fe1f15a2 Accept Merge Request #2148: (feature/6694 -> develop)
Merge Request: 制造订单的编程记录修改

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2148?initial=true
2025-05-26 09:32:06 +08:00
胡尧
30a6e5bf2e 制造订单的编程记录修改 2025-05-26 09:31:47 +08:00
禹翔辉
794ea0cbb0 Accept Merge Request #2147: (feature/返工解绑rfid -> develop)
Merge Request: 1、优化返工装夹预调工单时,清理rfid问题;2、优化库位信息接口数据处理方式

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2147
2025-05-23 17:17:06 +08:00
yuxianghui
619285608d 1、优化返工装夹预调工单时,清理rfid问题;2、优化库位信息接口数据处理方式 2025-05-23 17:13:12 +08:00
管欢
433d5d63b7 Accept Merge Request #2146: (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/2146
2025-05-23 15:19:30 +08:00
guanhuan
38109028d4 采购申请关闭修改 2025-05-23 15:13:01 +08:00
guanhuan
cf16a9dd59 采购申请关闭修改 2025-05-23 14:53:34 +08:00
胡尧
4fcbeb30cf Accept Merge Request #2143: (feature/6694 -> develop)
Merge Request: 调整采购单菜单顺序

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2143
2025-05-23 14:47:58 +08:00
胡尧
be8fbca9aa 解决冲突 2025-05-23 14:47:43 +08:00
胡尧
b393951968 去掉采购申请列表默认筛选条件 2025-05-23 14:45:57 +08:00
管欢
6add565a98 Accept Merge Request #2144: (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/2144
2025-05-23 14:22:18 +08:00
guanhuan
1b0dd96b40 采购申请关闭修改 2025-05-23 14:20:48 +08:00
胡尧
89f8718fe1 当询价单确认为采购单时,根据采购申请明细行变为采购单数量来完成采购申请 2025-05-23 14:20:38 +08:00
胡尧
7941c1981c 撤回采购申请视图修改 2025-05-23 11:37:21 +08:00
胡尧
f31e25b3b1 调整采购单菜单顺序 2025-05-23 11:20:17 +08:00
胡尧
ae7e49e307 Accept Merge Request #2142: (feature/6694 -> develop)
Merge Request: 增加分组

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2142?initial=true
2025-05-23 11:11:10 +08:00
胡尧
51c8287bbc 增加分组 2025-05-23 11:10:46 +08:00
胡尧
942d6661f2 修改api接口内容 2025-05-23 10:51:39 +08:00
胡尧
51ae598aac Accept Merge Request #2141: (feature/6694 -> develop)
Merge Request: 取消采购单时,如果该采购单关联的库存移动是由采购申请创建的,则不取消

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2141
2025-05-23 09:43:07 +08:00
胡尧
83229c9ab1 解决冲突 2025-05-23 09:42:52 +08:00
管欢
e62f933ca4 Accept Merge Request #2140: (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/2140
2025-05-23 09:41:03 +08:00
胡尧
1f8e118965 取消采购单时,如果该采购单关联的库存移动是由采购申请创建的,则不取消 2025-05-23 09:39:38 +08:00
guanhuan
2c52372b0a 采购申请隐藏处理中按钮 2025-05-22 11:44:14 +08:00
禹翔辉
5cf3d399f4 Accept Merge Request #2139: (feature/销售订单链接优化 -> develop)
Merge Request: 处理 sf-销售-委外的按钮关联订单数据显示不全 问题

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2139
2025-05-22 11:37:13 +08:00
yuxianghui
e9fc78186e 处理 sf-销售-委外的按钮关联订单数据显示不全 问题 2025-05-22 11:35:53 +08:00
胡尧
a2b2faaa95 Accept Merge Request #2138: (feature/6694 -> develop)
Merge Request: 修改欠单逻辑

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2138?initial=true
2025-05-22 11:10:28 +08:00
禹翔辉
4c7208784f Accept Merge Request #2137: (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/2137
2025-05-22 11:10:12 +08:00
yuxianghui
981569170c Merge branch 'feature/销售委外加工按钮优化_1' into feature/中控接口优化 2025-05-22 11:04:48 +08:00
胡尧
6bf666ac18 修改欠单逻辑 2025-05-22 11:04:12 +08:00
yuxianghui
3a9cd3f39d 1、获取日计划接口优化;2、货位信息接口优化; 2025-05-22 11:04:06 +08:00
胡尧
45b62abcbe Accept Merge Request #2136: (feature/6694 -> develop)
Merge Request: 增加采购申请审批排除字段

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2136?initial=true
2025-05-20 16:51:09 +08:00
胡尧
87740dbee3 增加采购申请审批排除字段 2025-05-20 16:49:08 +08:00
胡尧
a323acf7fc Accept Merge Request #2135: (feature/6694 -> develop)
Merge Request: 取消采购申请部分确认生成的反向单据

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2135?initial=true
2025-05-20 16:09:06 +08:00
胡尧
c23715a1ef 取消采购申请部分确认生成的反向单据 2025-05-20 16:08:03 +08:00
yuxianghui
896c1ad3a7 更新库位信息接口,优化获取到数据后的数据处理逻辑。 2025-05-20 13:46:43 +08:00
管欢
1856a1a4ef Accept Merge Request #2134: (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/2134
2025-05-19 11:04:44 +08:00
guanhuan
8bd09cddf0 采购申请按钮优化 2025-05-19 11:02:43 +08:00
guanhuan
52579673de 采购申请单完成操作优化 2025-05-19 08:54:02 +08:00
胡尧
31cd131993 Accept Merge Request #2133: (feature/6694 -> develop)
Merge Request: 在调拨单确认生成欠单时,修改对应采购申请明细行的目标移动

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2133?initial=true
2025-05-16 11:50:47 +08:00
胡尧
9f94a4e06f 在调拨单确认生成欠单时,修改对应采购申请明细行的目标移动 2025-05-16 11:44:06 +08:00
禹翔辉
e66c6b1e1b Accept Merge Request #2132: (feature/销售委外加工按钮优化_1 -> 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/2132
2025-05-16 09:21:46 +08:00
yuxianghui
e97d0af941 1 2025-05-16 09:18:25 +08:00
yuxianghui
65122d38d5 处理 销售-外购,委外的按钮关联订单数据显示不全 bug 2025-05-16 09:17:13 +08:00
胡尧
b626cbe217 Accept Merge Request #2131: (feature/6694 -> develop)
Merge Request: 解决库存单据零件图号零件名称不对的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2131?initial=true
2025-05-16 09:15:46 +08:00
胡尧
4b443e65f6 解决库存单据零件图号零件名称不对的问题 2025-05-16 09:14:37 +08:00
胡尧
14700d6bf0 Accept Merge Request #2130: (feature/6694 -> develop)
Merge Request: 解决工艺外协采购单不显示的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2130
2025-05-15 16:49:05 +08:00
胡尧
a3c0fd3ccf 解决工艺外协采购单不显示的问题 2025-05-15 16:48:34 +08:00
胡尧
a29265f17d 修改正则匹配成品名字序号 2025-05-15 16:39:57 +08:00
胡尧
0821ed021a Accept Merge Request #2129: (feature/6694 -> develop)
Merge Request: 解决采购申请中零件图号不匹配的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2129?initial=true
2025-05-15 16:30:28 +08:00
胡尧
ac4883db66 解决采购申请中零件图号不匹配的问题 2025-05-15 16:29:33 +08:00
胡尧
5706aa0052 Accept Merge Request #2127: (feature/6694 -> develop)
Merge Request: 增加零件图号

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2127?initial=true
2025-05-15 16:12:18 +08:00
胡尧
f780e4f7ce 增加零件图号 2025-05-15 16:11:31 +08:00
胡尧
e6d8ebb7b3 修改 2025-05-15 14:39:40 +08:00
胡尧
3663e04b34 Accept Merge Request #2126: (feature/6694 -> develop)
Merge Request: 解决产品模板复制的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2126?initial=true
2025-05-15 12:14:51 +08:00
胡尧
c2e4085b50 解决产品模板复制的问题 2025-05-15 12:14:33 +08:00
胡尧
33c881b12f Accept Merge Request #2125: (feature/6694 -> develop)
Merge Request: 回退工艺外协代码

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2125?initial=true
2025-05-15 11:07:51 +08:00
胡尧
c80e12d731 回退工艺外协代码 2025-05-15 11:07:22 +08:00
胡尧
5446ef18e2 回退代码 2025-05-15 10:36:38 +08:00
胡尧
9c4d545915 Merge branch 'develop' into feature/6694 2025-05-15 10:33:42 +08:00
胡尧
f6d8cb6267 编程单增加零件图号 2025-05-15 08:45:02 +08:00
胡尧
c898e02860 解决制造申请字段未复制到成品中 2025-05-14 17:02:04 +08:00
胡尧
5477582a69 修改中控日志接口授权为none 2025-05-14 16:09:06 +08:00
胡尧
9cb22d810e 增加接口日志 2025-05-12 15:14:04 +08:00
胡尧
cab6b6fa2a 解决代码问题 2025-05-12 14:46:15 +08:00
胡尧
35bf954529 增加中控接口调用日志记录 2025-05-12 11:50:35 +08:00
胡尧
ceb38aa483 Accept Merge Request #2106: (feature/tool_standard_library_process -> develop)
Merge Request: 工艺外协代码

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2106
2025-05-12 08:41:26 +08:00
胡尧
11ecad5ef2 解决冲突 2025-05-12 08:41:12 +08:00
管欢
8249d1427f Accept Merge Request #2105: (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/2105
2025-05-09 18:28:56 +08:00
guanhuan
94bcfc0543 采购申请数量修改过滤取消 2025-05-09 18:15:40 +08:00
guanhuan
b4d31c7c4b 采购申请数量修改 2025-05-09 17:50:43 +08:00
胡尧
61bcd72a41 表面工艺外协调拨单数量问题 2025-05-09 17:24:24 +08:00
胡尧
d7f7bb9a57 Accept Merge Request #2104: (release/release_2.13 -> develop)
Merge Request: 处理特殊表面工艺采购单确认报错的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2104?initial=true
2025-05-09 15:26:50 +08:00
胡尧
ee87e1dacf 处理特殊表面工艺采购单确认报错的问题 2025-05-09 15:26:12 +08:00
胡尧
2f6c41c999 Accept Merge Request #2103: (release/release_2.13 -> develop)
Merge Request: 修改制造目录文件结构

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2103?initial=true
2025-05-09 14:58:09 +08:00
胡尧
d0d4db1555 修改制造目录文件结构 2025-05-09 14:57:35 +08:00
胡尧
62cbb4b796 Merge branch 'develop' into release/release_2.13 2025-05-09 14:08:06 +08:00
胡尧
f040406002 Accept Merge Request #2102: (feature/6694 -> develop)
Merge Request: 确认接收时,将不追溯的产品的完成数量自动填上

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2102?initial=true
2025-05-09 13:43:36 +08:00
胡尧
bfff4ac440 确认接收时,将不追溯的产品的完成数量自动填上 2025-05-09 13:43:10 +08:00
胡尧
a97386c37c Accept Merge Request #2101: (feature/6694 -> develop)
Merge Request: 调整质检单字段

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2101?initial=true
2025-05-09 10:34:59 +08:00
胡尧
18ae46207a 调整质检单字段 2025-05-09 10:34:23 +08:00
胡尧
bacddd2ad8 修改字段翻译 2025-05-09 08:47:33 +08:00
胡尧
dd5794899d Accept Merge Request #2100: (feature/6694 -> develop)
Merge Request: 人工线下返工在不选择重新编程时复制加工图纸

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2100?initial=true
2025-05-08 21:22:21 +08:00
胡尧
e5b730b2ef 人工线下返工在不选择重新编程时复制加工图纸 2025-05-08 21:21:51 +08:00
胡尧
aea158de41 质检单数量约束 2025-05-08 20:32:59 +08:00
胡尧
a933a0ffea Accept Merge Request #2099: (feature/6694 -> develop)
Merge Request: 委外加工的入库单增加采购申请按钮

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2099?initial=true
2025-05-08 20:13:20 +08:00
胡尧
7575424760 委外加工的入库单增加采购申请按钮 2025-05-08 20:12:12 +08:00
胡尧
6c2eb40e6a Accept Merge Request #2098: (feature/6694 -> develop)
Merge Request: 解决制造订单不显示采购申请按钮的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2098?initial=true
2025-05-08 17:48:05 +08:00
胡尧
f10f595fa4 解决制造订单不显示采购申请按钮的问题 2025-05-08 17:47:40 +08:00
胡尧
6d1de42d76 Accept Merge Request #2097: (feature/6694 -> develop)
Merge Request: 修复收料入库单明细不对的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2097?initial=true
2025-05-08 17:30:19 +08:00
胡尧
5dc16c039c 修复收料入库单明细不对的问题 2025-05-08 17:29:41 +08:00
胡尧
c416cdbeed Accept Merge Request #2096: (feature/6694 -> develop)
Merge Request: 取消采购申请合并明细行的代码,会导致其他后续单据的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2096?initial=true
2025-05-08 16:47:22 +08:00
胡尧
18c7b22319 取消采购申请合并明细行的代码,会导致其他后续单据的问题 2025-05-08 16:47:01 +08:00
liaodanlong
b5339046b9 工艺外协代码 2025-05-08 16:42:18 +08:00
胡尧
e0ba222382 Accept Merge Request #2095: (feature/6694 -> develop)
Merge Request: 解决计算字段报错的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2095?initial=true
2025-05-08 15:57:52 +08:00
胡尧
58b00e6442 解决计算字段报错的问题 2025-05-08 15:57:16 +08:00
胡尧
9182dbfb5d Accept Merge Request #2094: (feature/6694 -> develop)
Merge Request: 修复质检的bug

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2094?initial=true
2025-05-08 15:20:48 +08:00
胡尧
27516844af 修复质检的bug 2025-05-08 15:20:08 +08:00
胡尧
99237445ac 修复质检的bug 2025-05-08 15:17:01 +08:00
管欢
9349ca91d3 Accept Merge Request #2093: (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/2093
2025-05-08 09:18:28 +08:00
liaodanlong
51c517145b 工艺外协代码 2025-05-07 17:17:41 +08:00
guanhuan
c55f3d77bf Merge branch 'refs/heads/feature/采购申请优化' into release/release_2.13 2025-05-07 16:45:18 +08:00
guanhuan
95716c2e3e 采购申请明细优化 2025-05-07 16:44:48 +08:00
胡尧
5f72519dc2 Accept Merge Request #2092: (feature/6694 -> develop)
Merge Request: 增加采购申请对于单件制造非首个制造订单的显示,修改采购申请对于同一个补货组同一个产品的合并

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2092?initial=true
2025-05-07 16:35:22 +08:00
胡尧
c24bba3137 增加采购申请对于单件制造非首个制造订单的显示,修改采购申请对于同一个补货组同一个产品的合并 2025-05-07 16:34:45 +08:00
guanhuan
01bb6fd0aa Merge branch 'refs/heads/feature/采购申请优化' into release/release_2.13 2025-05-07 16:04:47 +08:00
guanhuan
bf4add6b78 采购申请明细优化 2025-05-07 16:02:39 +08:00
hyyy
51a8964b89 修复出厂检验质检单详情手动新增明细行后右边的【删除】按钮显示不全 2025-05-07 15:48:05 +08:00
胡尧
7d986fe139 Accept Merge Request #2091: (feature/6694 -> develop)
Merge Request: 表面工艺外协工单流程数量按照制造订单的product_uom_qty设置

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2091?initial=true
2025-05-07 13:02:51 +08:00
胡尧
fffbfc21c2 表面工艺外协工单流程数量按照制造订单的product_uom_qty设置 2025-05-07 13:02:13 +08:00
胡尧
6451bfbc42 Accept Merge Request #2090: (feature/6694 -> develop)
Merge Request: 修改判断坯料序列号的逻辑

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2090?initial=true
2025-05-07 11:01:51 +08:00
胡尧
5aa848de53 修改判断坯料序列号的逻辑 2025-05-07 11:01:01 +08:00
liaodanlong
efc4ae31c9 工艺外协代码 2025-05-07 11:00:32 +08:00
liaodanlong
0863238819 Merge branch 'refs/heads/develop' into feature/tool_standard_library_process 2025-05-07 11:00:24 +08:00
胡尧
4f181e5eba Accept Merge Request #2089: (feature/6694 -> develop)
Merge Request: 将成品的追溯复制到product上,将坯料的追溯同成品的追组

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2089
2025-05-07 08:52:19 +08:00
胡尧
2bf43ae9a1 将成品的追溯复制到product上,将坯料的追溯同成品的追组 2025-05-07 08:47:33 +08:00
胡尧
d98d04d4ed Accept Merge Request #2088: (feature/6694 -> develop)
Merge Request: Merge branch 'develop' into feature/6694

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2088
2025-05-06 15:23:05 +08:00
胡尧
602d6678bc Merge branch 'develop' into feature/6694 2025-05-06 15:22:08 +08:00
胡尧
8fd0c4e1f1 修复计算当前货位的逻辑,减少循环次数 2025-05-06 15:21:45 +08:00
管欢
514fd79c3e Accept Merge Request #2087: (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/2087
2025-04-29 13:58:19 +08:00
liaodanlong
95c25ac7b8 工艺外协代码 2025-04-29 13:39:36 +08:00
guanhuan
21d052e222 采购申请明细优化 2025-04-29 10:40:19 +08:00
胡尧
95e2c2db0d Accept Merge Request #2086: (feature/6694 -> develop)
Merge Request: 修改打印逻辑,先找默认打印机,未找到则直接返回

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2086?initial=true
2025-04-29 10:11:16 +08:00
胡尧
17a29b7b29 修改打印逻辑,先找默认打印机,未找到则直接返回 2025-04-29 10:09:41 +08:00
胡尧
dd745423a1 修改打印逻辑,先找默认打印机,未找到则直接返回 2025-04-29 10:03:09 +08:00
胡尧
a534e5f400 Accept Merge Request #2085: (feature/6694 -> develop)
Merge Request: 删除多余的打印代码

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2085?initial=true
2025-04-28 19:29:01 +08:00
胡尧
4dc7b5857e 删除多余的打印代码 2025-04-28 19:28:31 +08:00
胡尧
dc679c46cc Accept Merge Request #2084: (feature/6694 -> develop)
Merge Request: 简化打印代码

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2084?initial=true
2025-04-28 19:18:26 +08:00
胡尧
8ccf6cc365 简化打印代码 2025-04-28 19:18:02 +08:00
胡尧
f8457ae66b Accept Merge Request #2083: (feature/6694 -> develop)
Merge Request: 简化打印代码

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2083?initial=true
2025-04-28 19:12:31 +08:00
胡尧
12c8641f2e 简化打印代码 2025-04-28 19:12:08 +08:00
胡尧
f42938f668 Accept Merge Request #2082: (feature/6694 -> develop)
Merge Request: 增加打印日志

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2082?initial=true
2025-04-28 18:28:49 +08:00
胡尧
a856c5cbf7 增加打印日志 2025-04-28 18:28:21 +08:00
胡尧
6411e79904 Accept Merge Request #2081: (feature/6694 -> develop)
Merge Request: 增加日志

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2081?initial=true
2025-04-28 16:42:39 +08:00
胡尧
946f08c479 增加日志 2025-04-28 16:42:20 +08:00
胡尧
4a198639ec Accept Merge Request #2080: (feature/6694 -> develop)
Merge Request: Merge branch 'develop' into feature/6694

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2080?initial=true
2025-04-28 15:47:42 +08:00
胡尧
234812bb40 Merge branch 'develop' into feature/6694 2025-04-28 15:47:15 +08:00
胡尧
dd43e31c3c 修改打印pdf数据 2025-04-28 15:46:35 +08:00
廖丹龙
2f5b0281c3 Accept Merge Request #2079: (feature/process_outsourcing_code_stripping -> develop)
Merge Request: Merge remote-tracking branch 'origin/release/release_2.12' into release/release_2.12

Created By: @廖丹龙
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2079
2025-04-28 15:26:22 +08:00
82 changed files with 1682 additions and 391 deletions

View File

@@ -119,13 +119,24 @@ patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
this.listherHeaderBodyNum() this.listherHeaderBodyNum()
}) })
const treeModifiers = this.getFieldModifiers(this.props.archInfo.__rawArch); const treeModifiers = this.getFieldModifiers(this.props.archInfo.__rawArch);
if(treeModifiers) { if(treeModifiers) {
this.props.merge_key = treeModifiers.merge_key; if(treeModifiers.merge_fields) {
this.props.merge_fields = treeModifiers.merge_fields.split(','); this.props.merge_key = treeModifiers.merge_key;
const data = this.setColumns(this.props.merge_key); this.props.merge_fields = treeModifiers.merge_fields.split(',');
owl.onMounted(() => { const data = this.setColumns(this.props.merge_key);
this.mergeColumns(this.props.merge_fields, data) owl.onMounted(() => {
}) this.mergeColumns(this.props.merge_fields, data)
})
}
if(treeModifiers.pacthResize) {
owl.onPatched(() => {
this.columnWidths = null;
this.freezeColumnWidths();
})
}
} }
return this._super(...arguments); return this._super(...arguments);
}, },

View File

@@ -3,8 +3,12 @@ import qrcode
from reportlab.pdfgen import canvas from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4 from reportlab.lib.pagesizes import A4
from PIL import Image from PIL import Image
import logging
from reportlab.lib.utils import ImageReader from reportlab.lib.utils import ImageReader
from odoo import models, fields, api from odoo import models, fields, api
import base64
_logger = logging.getLogger(__name__)
class JikimoPrinting(models.AbstractModel): class JikimoPrinting(models.AbstractModel):
_name = 'jikimo.printing' _name = 'jikimo.printing'
@@ -13,6 +17,11 @@ class JikimoPrinting(models.AbstractModel):
""" """
打印二维码 打印二维码
""" """
printer = self.env['printing.printer'].get_default()
if not printer:
_logger.error("未找到默认打印机")
return False
# 生成二维码 # 生成二维码
qr = qrcode.QRCode(version=1, box_size=10, border=5) qr = qrcode.QRCode(version=1, box_size=10, border=5)
qr.add_data(data) qr.add_data(data)
@@ -41,16 +50,38 @@ class JikimoPrinting(models.AbstractModel):
# 获取PDF内容并打印 # 获取PDF内容并打印
pdf_content = pdf_buffer.getvalue() pdf_content = pdf_buffer.getvalue()
printer = self.env['printing.printer'].get_default() # _logger.info(f"打印内容: {pdf_content}")
printer.print_document(report=None, content=pdf_content, doc_format='pdf') printer.print_document(report=None, content=pdf_content, doc_format='pdf')
# 清理资源 # 清理资源
pdf_buffer.close() pdf_buffer.close()
temp_image.close() temp_image.close()
return True
def print_pdf(self, pdf_data): def print_pdf(self, pdf_data):
""" """
打印PDF 打印PDF
""" """
printer = self.env['printing.printer'].get_default() printer = self.env['printing.printer'].get_default()
printer.print_document(report=None, content = pdf_data, doc_format='pdf') if not printer:
_logger.error("未找到默认打印机")
return False
pdf_data_str = pdf_data.decode('ascii', errors='ignore')
decoded_data = base64.b64decode(pdf_data_str)
# 处理二进制数据
pdf_buffer = BytesIO()
pdf_buffer.write(decoded_data)
pdf_buffer.seek(0)
# 获取PDF内容
pdf_content = pdf_buffer.getvalue()
printer.print_document(report=None, content=pdf_content, doc_format='pdf')
# 清理资源
pdf_buffer.close()
_logger.info("成功打印PDF")
return True

View File

@@ -21,8 +21,6 @@ class MrpWorkorder(models.Model):
# 执行打印 # 执行打印
self.env['jikimo.printing'].print_pdf(pdf_data) self.env['jikimo.printing'].print_pdf(pdf_data)
wo.production_id.product_id.is_print_program = True wo.production_id.product_id.is_print_program = True
_logger.info(f"工单 {wo.name} 的PDF已成功打印")
except Exception as e: except Exception as e:
_logger.error(f"工单 {wo.name} 的PDF打印失败: {str(e)}") _logger.error(f"工单 {wo.name} 的PDF打印失败: {str(e)}")

View File

@@ -8,16 +8,21 @@
'category': 'purchase', 'category': 'purchase',
'depends': ['sf_manufacturing', 'purchase_request'], 'depends': ['sf_manufacturing', 'purchase_request'],
'data': [ 'data': [
'security/ir.model.access.csv',
'views/sale_order_view.xml', 'views/sale_order_view.xml',
'views/mrp_production.xml', 'views/mrp_production.xml',
'views/purchase_request_view.xml', 'views/purchase_request_view.xml',
'wizard/purchase_request_line_make_purchase_order_view.xml', 'wizard/purchase_request_line_make_purchase_order_view.xml',
'views/purchase_request_line_view.xml',
'views/stock_picking_views.xml',
'wizard/purchase_request_wizard_views.xml',
'views/purchase_request_menu_views.xml',
], ],
# 'assets': { 'assets': {
# 'web.assets_backend': [ 'web.assets_backend': [
# 'jikimo_purchase_request/static/src/**/*' 'jikimo_purchase_request/static/src/**/*'
# ], ],
# }, },
'application': True, 'application': True,
'installable': True, 'installable': True,
'auto_install': False, 'auto_install': False,

View File

@@ -410,7 +410,7 @@ msgstr "显示名称"
#: model_terms:ir.ui.view,arch_db:purchase_request.view_purchase_request_form #: model_terms:ir.ui.view,arch_db:purchase_request.view_purchase_request_form
#: model_terms:ir.ui.view,arch_db:purchase_request.view_purchase_request_search #: model_terms:ir.ui.view,arch_db:purchase_request.view_purchase_request_search
msgid "Done" msgid "Done"
msgstr "完成" msgstr "关闭"
#. module: purchase_request #. module: purchase_request
#: model:ir.model.fields,field_description:purchase_request.field_purchase_request_line__move_dest_ids #: model:ir.model.fields,field_description:purchase_request.field_purchase_request_line__move_dest_ids
@@ -1043,7 +1043,7 @@ msgstr "询价单"
#. module: purchase_request #. module: purchase_request
#: model:ir.model.fields,field_description:purchase_request.field_purchase_request_line__purchased_qty #: model:ir.model.fields,field_description:purchase_request.field_purchase_request_line__purchased_qty
msgid "RFQ/PO Qty" msgid "RFQ/PO Qty"
msgstr "" msgstr "已订购数"
#. module: purchase_request #. module: purchase_request
#. odoo-python #. odoo-python

View File

@@ -5,3 +5,4 @@ from . import sale_order
from . import mrp_production from . import mrp_production
from . import purchase_order from . import purchase_order
from . import stock_rule from . import stock_rule
from . import stock_picking

View File

@@ -9,12 +9,19 @@ class MrpProduction(models.Model):
@api.depends('state') @api.depends('state')
def _compute_pr_mp_count(self): def _compute_pr_mp_count(self):
for item in self: for item in self:
pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name)]) # if item.product_id.product_tmpl_id.single_manufacturing == True and not item.is_remanufacture:
# first_order = self.env['mrp.production'].search(
# [('origin', '=', item.origin), ('product_id', '=', item.product_id.id)], limit=1, order='id asc')
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', first_order.name)])
# item.pr_mp_count = len(pr_ids)
# else:
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name)])
# item.pr_mp_count = len(pr_ids)
# 由于采购申请合并了所有销售订单行的采购,所以不区分产品
mrp_names = self.env['mrp.production'].search([('origin', '=', item.origin)]).mapped('name')
pr_ids = self.env['purchase.request'].sudo().search([('origin', 'in', mrp_names)])
item.pr_mp_count = len(pr_ids)
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name), ('is_subcontract', '!=', 'True')]) # pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name), ('is_subcontract', '!=', 'True')])
if pr_ids:
item.pr_mp_count = len(pr_ids)
else:
item.pr_mp_count = 0
def action_view_pr_mp(self): def action_view_pr_mp(self):
""" """
@@ -22,7 +29,17 @@ class MrpProduction(models.Model):
""" """
self.ensure_one() self.ensure_one()
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name),('is_subcontract', '!=', True)]) # pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name),('is_subcontract', '!=', True)])
pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name)]) # if self.product_id.product_tmpl_id.single_manufacturing == True and not self.is_remanufacture:
# first_order = self.env['mrp.production'].search(
# [('origin', '=', self.origin), ('product_id', '=', self.product_id.id)], limit=1, order='id asc')
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', first_order.name)])
# else:
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name)])
# 由于采购申请合并了所有销售订单行的采购,所以不区分产品
mrp_names = self.env['mrp.production'].search([('origin', '=', self.origin)]).mapped('name')
pr_ids = self.env['purchase.request'].sudo().search([('origin', 'in', mrp_names)])
action = { action = {
'res_model': 'purchase.request', 'res_model': 'purchase.request',
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',

View File

@@ -1,4 +1,5 @@
from odoo import api, fields, models, _ from odoo import api, fields, models, _
from odoo.tools import float_compare
class PurchaseOrder(models.Model): class PurchaseOrder(models.Model):
@@ -14,3 +15,42 @@ class PurchaseOrder(models.Model):
('cancel', '取消'), ('cancel', '取消'),
('rejected', '已驳回') ('rejected', '已驳回')
], string='Status', readonly=True, index=True, copy=False, default='draft', tracking=True) ], string='Status', readonly=True, index=True, copy=False, default='draft', tracking=True)
def button_confirm(self):
res = super(PurchaseOrder, self).button_confirm()
# 取消反向调拨单
reverse_move_ids = self.env['stock.move'].search([
('origin', '=', self.name),
('purchase_line_id', '=', False),
('state', '!=', 'done')
])
if reverse_move_ids:
reverse_move_ids.picking_id.action_cancel()
return res
def button_cancel(self):
"""
将取消的采购订单关联的库存移动撤销
"""
move_ids = self.order_line.move_dest_ids.filtered(lambda move: move.state != 'done' and not move.scrapped)
res =super(PurchaseOrder, self).button_cancel()
if move_ids.mapped('created_purchase_request_line_id'):
move_ids.write({'state': 'waiting', 'is_done': False})
return res
def write(self, vals):
res = super(PurchaseOrder, self).write(vals)
if 'state' in vals and vals['state'] == 'purchase':
purchase_request = self.order_line.purchase_request_lines.request_id
if purchase_request:
finished = True
# 判断该采购申请所有明细行是否都完成
for purchase_request_line in purchase_request.line_ids:
finished_qty = sum(purchase_request_line.purchase_lines.filtered(lambda line: line.state == 'purchase').mapped('product_qty'))
if float_compare(finished_qty ,purchase_request_line.product_qty, precision_rounding=purchase_request_line.product_id.uom_id.rounding) < 0:
finished = False
break
if finished:
purchase_request.button_done()
return res

View File

@@ -1,13 +1,15 @@
import re import re
import ast import ast
from odoo import models, fields, api from odoo import models, fields, api, _
from itertools import groupby
from odoo.tools import float_compare
class PurchaseRequest(models.Model): class PurchaseRequest(models.Model):
_inherit = 'purchase.request' _inherit = 'purchase.request'
_description = '采购申请' _description = '采购申请'
# 为state添加取消状态 # 为state添加取消状态
state = fields.Selection( state = fields.Selection(
selection_add=[('cancel', '已取消')], selection_add=[('cancel', '已取消')],
ondelete={'cancel': 'set default'} # 添加 ondelete 策略 ondelete={'cancel': 'set default'} # 添加 ondelete 策略
@@ -29,6 +31,56 @@ class PurchaseRequest(models.Model):
action['context'] = origin_context action['context'] = origin_context
return action return action
def button_done(self):
product_qty_map = {key: sum(line.product_qty for line in group) for key, group in
groupby(self.line_ids, key=lambda x: x.product_id.id)}
lines = self.mapped("line_ids.purchase_lines.order_id")
# 采购单产品和数量
product_summary = {}
product_rounding = {}
if lines:
for line in lines:
for line_item in line.order_line:
product_id = line_item.product_id.id
qty = line_item.product_qty
product_rounding[product_id] = line_item.product_id.uom_id.rounding
if product_id in product_summary:
product_summary[product_id] += qty
else:
product_summary[product_id] = qty
# 校验产品数量
discrepancies = []
for product_id, qty in product_qty_map.items():
if product_id in product_summary:
if float_compare(product_summary[product_id], qty, precision_rounding=product_rounding[product_id]) < 0:
discrepancies.append((product_id, qty, product_summary[product_id]))
else:
discrepancies.append((product_id, qty, 0))
if discrepancies:
# 弹出提示框
message = "产品数量不一致:\n"
for product_id, required_qty, order_qty in discrepancies:
product_name = self.env['product.product'].browse(product_id).display_name # 获取产品名称
message += f"产品 {product_name},需求数量 {required_qty},关联采购订单数量 {order_qty}(含询价状态)\n"
# 添加确认框
message += "确认关闭?"
return {
'name': _('采购申请'),
'type': 'ir.actions.act_window',
'views': [(self.env.ref(
'jikimo_purchase_request.purchase_request_wizard_wizard_form_view').id,
'form')],
'res_model': 'purchase.request.wizard',
'target': 'new',
'context': {
'default_purchase_request_id': self.id,
'default_message': message,
}}
return super(PurchaseRequest, self).button_done()
class PurchaseRequestLine(models.Model): class PurchaseRequestLine(models.Model):
_inherit = 'purchase.request.line' _inherit = 'purchase.request.line'
_description = '采购申请明细' _description = '采购申请明细'
@@ -47,6 +99,20 @@ class PurchaseRequestLine(models.Model):
('outsourcing', "委外加工"), ('outsourcing', "委外加工"),
], string='供货方式', compute='_compute_supply_method', store=True) ], string='供货方式', compute='_compute_supply_method', store=True)
purchase_request_count = fields.Integer(string='采购申请数量', compute='_compute_purchase_request_count',
readonly=True)
purchase_count = fields.Integer(string="采购订单数量", compute="_compute_purchase_count", readonly=True)
@api.depends("purchase_lines")
def _compute_purchase_count(self):
for rec in self:
rec.purchase_count = len(rec.mapped("purchase_lines.order_id"))
@api.depends('request_id')
def _compute_purchase_request_count(self):
for order in self:
order.purchase_request_count = len(order.request_id)
@api.depends('origin') @api.depends('origin')
def _compute_supply_method(self): def _compute_supply_method(self):
for prl in self: for prl in self:
@@ -79,7 +145,7 @@ class PurchaseRequestLine(models.Model):
continue continue
if record.product_id.categ_id.name == '坯料': if record.product_id.categ_id.name == '坯料':
product_name = '' product_name = ''
match = re.search(r'(S\d{5}-\d)', record.product_id.name) match = re.search(r'(S\d{5}-\d+)', record.product_id.name)
# 如果匹配成功,提取结果 # 如果匹配成功,提取结果
if match: if match:
product_name = match.group(0) product_name = match.group(0)
@@ -98,3 +164,36 @@ class PurchaseRequestLine(models.Model):
else: else:
record.part_number = record.product_id.part_number record.part_number = record.product_id.part_number
record.part_name = record.product_id.part_name record.part_name = record.product_id.part_name
def _compute_qty_to_buy(self):
for pr in self:
qty_to_buy = sum(pr.mapped("product_qty"))
if pr.purchase_count > 0:
qty_to_buy -= sum(pr.mapped("purchase_lines").filtered(lambda po: po.state != 'cancel').mapped(
"product_qty"))
pr.qty_to_buy = qty_to_buy > 0.0
pr.pending_qty_to_receive = qty_to_buy
def action_view_purchase_request(self):
action = self.env["ir.actions.actions"]._for_xml_id("purchase_request.purchase_request_form_action")
action.update({
'res_id': self.request_id.id,
'views': [[False, 'form']],
})
return action
def action_view_purchase_order(self):
action = self.env["ir.actions.actions"]._for_xml_id("purchase.purchase_rfq")
lines = self.mapped("purchase_lines.order_id")
if len(lines) > 1:
action["domain"] = [("id", "in", lines.ids)]
elif lines:
action["views"] = [
(self.env.ref("purchase.purchase_order_form").id, "form")
]
action["res_id"] = lines.id
origin_context = ast.literal_eval(action['context'])
if 'search_default_draft' in origin_context:
origin_context.pop('search_default_draft')
action['context'] = origin_context
return action

View File

@@ -0,0 +1,47 @@
from odoo import fields, api, models, _
class StockPicking(models.Model):
_inherit = "stock.picking"
purchase_request_count = fields.Integer('采购订单数量', compute='_compute_purchase_request')
@api.depends('name')
def _compute_purchase_request(self):
for record in self:
purchase_request_ids = self.env['purchase.request'].search([('origin', '=', record.name)])
record.purchase_request_count = len(purchase_request_ids)
def action_view_purchase_request(self):
self.ensure_one()
purchase_request_ids = self.env['purchase.request'].search([('origin', '=', self.name)])
action = {
'res_model': 'purchase.request',
'type': 'ir.actions.act_window',
}
if len(purchase_request_ids) == 1:
action.update({
'view_mode': 'form',
'res_id': purchase_request_ids[0].id,
})
else:
action.update({
'name': _("%s生成采购请求单", self.name),
'domain': [('id', 'in', purchase_request_ids.ids)],
'view_mode': 'tree,form',
})
return action
def _action_done(self):
res = super(StockPicking, self)._action_done()
# 将新产生的backorder对应上原来的采购申请明细行
backorder_ids = self.backorder_ids
if backorder_ids:
purchase_request_lines = self.move_ids.move_orig_ids.purchase_line_id.purchase_request_lines
if purchase_request_lines:
purchase_request_lines.move_dest_ids = [
(4, x.id) for x in backorder_ids.move_ids if x.product_id.id == purchase_request_lines.product_id.id
]
return res

View File

@@ -1,4 +1,5 @@
from odoo import api, fields, models from odoo import api, fields, models
from collections import defaultdict
class StockRule(models.Model): class StockRule(models.Model):
@@ -44,13 +45,38 @@ class StockRule(models.Model):
purchase_request_line_model.create(request_line_data) purchase_request_line_model.create(request_line_data)
def _run_buy(self, procurements): def _run_buy(self, procurements):
res = super(StockRule, self)._run_buy(procurements) # 如果补货组相同,并且产品相同,则合并
# 判断是否根据规则生成新的采购申请单据,如果生成则修改状态为 approved procurements_dict = defaultdict()
origins = list(set([procurement[0].origin for procurement in procurements])) for procurement, rule in procurements:
for origin in origins: if (procurement.product_id.id, procurement.values['group_id'], rule.id) not in procurements_dict:
pr_ids = self.env["purchase.request"].sudo().search( procurements_dict[(procurement.product_id.id, procurement.values['group_id'], rule.id)] = {
[('origin', 'like', origin), ('rule_new_add', '=', True), ('state', '=', 'draft')]) 'product_id': procurement.product_id,
if pr_ids: 'product_qty': procurement.product_qty,
pr_ids.write({'need_validation': False}) 'product_uom': procurement.product_uom,
pr_ids.write({"state": "approved", 'need_validation': True, 'rule_new_add': False}) 'location_id': procurement.location_id,
'name': procurement.name,
'origin': procurement.origin,
'company_id': procurement.company_id,
'values': procurement.values,
'rule': rule
}
else:
procurements_dict[(procurement.product_id.id, procurement.values['group_id'], rule.id)]['product_qty'] += procurement.product_qty
procurements_dict[(procurement.product_id.id, procurement.values['group_id'], rule.id)]['values']['move_dest_ids'] |= procurement.values['move_dest_ids']
new_procurements = []
for k, p in procurements_dict.items():
new_procurements.append((
self.env['procurement.group'].Procurement(
product_id=p['product_id'],
product_qty=p['product_qty'],
product_uom=p['product_uom'],
location_id=p['location_id'],
name=p['name'],
origin=p['origin'],
company_id=p['company_id'],
values=p['values']
), p['rule'])
)
res = super(StockRule, self)._run_buy(new_procurements)
return res return res

View File

@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_purchase_request_wizard_group_user,purchase.request.wizard,model_purchase_request_wizard,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_purchase_request_wizard_group_user purchase.request.wizard model_purchase_request_wizard base.group_user 1 1 1 1

View File

@@ -0,0 +1,3 @@
th[data-name=keep_description] {
min-width: 220px;
}

View File

@@ -0,0 +1,22 @@
<odoo>
<record id="purchase_request_line_form_sf" model="ir.ui.view">
<field name="name">purchase.request.line.sf.form</field>
<field name="model">purchase.request.line</field>
<field name="inherit_id" ref="purchase_request.purchase_request_line_form"/>
<field name="arch" type="xml">
<xpath expr="//h1" position="before">
<div class="oe_button_box" name="button_box">
<button type="object" name="action_view_purchase_request" class="oe_stat_button"
icon="fa-file">
<field name="purchase_request_count" widget="statinfo" string="采购申请"/>
</button>
<button type="object" name="action_view_purchase_order" class="oe_stat_button"
attrs="{'invisible': [('purchase_count', '=', 0)]}" icon="fa-shopping-cart">
<field name="purchase_count" widget="statinfo" string="采购订单"/>
</button>
</div>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="menu_purhcase_request" model="ir.ui.menu">
<field name="name">采购申请</field>
<field name="parent_id" ref="purchase.menu_purchase_root" />
<field name="sequence">2</field>
</record>
<record id="purchase_request.menu_purchase_request_pro_mgt" model="ir.ui.menu">
<field name="sequence">1</field>
<field name="parent_id" ref="jikimo_purchase_request.menu_purhcase_request"/>
</record>
<record id="purchase_request.menu_purchase_request_line" model="ir.ui.menu">
<field name="sequence">10</field>
<field name="parent_id" ref="jikimo_purchase_request.menu_purhcase_request"/>
</record>
</data>
</odoo>

View File

@@ -15,6 +15,26 @@
<field name="part_number"/> <field name="part_number"/>
<field name="part_name"/> <field name="part_name"/>
</xpath> </xpath>
<xpath expr="//button[@name='button_done']" position="attributes">
<attribute name="class"/>
</xpath>
<xpath expr="//button[@name='button_in_progress']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<xpath expr="//button[@name='%(purchase_request.action_purchase_request_line_make_purchase_order)d']" position="attributes">
<attribute name="class">oe_highlight</attribute>
</xpath>
</field>
</record>
<record id="view_purchase_request_tree_sf" model="ir.ui.view">
<field name="name">purchase.request.sf.tree</field>
<field name="model">purchase.request</field>
<field name="inherit_id" ref="purchase_request.view_purchase_request_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='activity_ids']" position="attributes">
<attribute name="optional">hide</attribute>
</xpath>
</field> </field>
</record> </record>
@@ -63,4 +83,9 @@
</xpath> </xpath>
</field> </field>
</record> </record>
<record model="ir.actions.act_window" id="purchase_request.purchase_request_form_action">
<field name="name">Purchase Requests</field>
<field name="context"></field>
</record>
</odoo> </odoo>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="stock_pikcing_inherited_form_jikimo_purchase_request" model="ir.ui.view">
<field name="name">stock.pikcing.inherited.form.jikimo.purchase.request</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']/button" position="before">
<button class="oe_stat_button" name="action_view_purchase_request" type="object" icon="fa-credit-card"
attrs="{'invisible': [('purchase_request_count', '=', 0)]}">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
<field name="purchase_request_count"/>
</span>
<span class="o_stat_text">采购申请</span>
</div>
</button>
</xpath>
</field>
</record>
</odoo>

View File

@@ -1,3 +1,4 @@
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0) # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0)
from . import purchase_request_line_make_purchase_order from . import purchase_request_line_make_purchase_order
from . import purchase_request_wizard

View File

@@ -104,9 +104,26 @@ class PurchaseRequestLineMakePurchaseOrder(models.TransientModel):
# 去掉合并必须同一采购组的限制 # 去掉合并必须同一采购组的限制
pass pass
def get_items(self, request_line_ids):
request_line_obj = self.env["purchase.request.line"]
items = []
request_lines = request_line_obj.browse(request_line_ids).filtered(lambda line: line.pending_qty_to_receive > 0)
self._check_valid_request_line(request_line_ids)
self.check_group(request_lines)
for line in request_lines:
items.append([0, 0, self._prepare_item(line)])
return items
class PurchaseRequestLineMakePurchaseOrderItem(models.TransientModel): class PurchaseRequestLineMakePurchaseOrderItem(models.TransientModel):
_inherit = "purchase.request.line.make.purchase.order.item" _inherit = "purchase.request.line.make.purchase.order.item"
supply_method = fields.Selection(related='line_id.supply_method', string='供货方式') supply_method = fields.Selection(related='line_id.supply_method', string='供货方式')
wiz_id = fields.Many2one(
comodel_name="purchase.request.line.make.purchase.order",
string="Wizard",
required=False,
ondelete="cascade",
readonly=True,
)

View File

@@ -0,0 +1,12 @@
from odoo import models, fields, api
class PurchaseRequestWizard(models.TransientModel):
_name = 'purchase.request.wizard'
_description = '采购申请向导'
purchase_request_id = fields.Many2one('purchase.request', string='采购申请')
message = fields.Char(string='提示', readonly=True)
def confirm(self):
return self.purchase_request_id.write({"state": "done"})

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.ui.view" id="purchase_request_wizard_wizard_form_view">
<field name="name">purchase.request.wizard.form.view</field>
<field name="model">purchase.request.wizard</field>
<field name="arch" type="xml">
<form>
<sheet>
<div>
<div style="white-space: pre-wrap;">
<field name="message"/>
</div>
</div>
<footer>
<button string="确认" name="confirm" type="object" class="oe_highlight"/>
<button string="取消" class="btn btn-secondary" special="cancel"/>
</footer>
</sheet>
</form>
</field>
</record>
</odoo>

View File

@@ -1,10 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
{ {
'name': "机企猫 采购审批流程", 'name': "机企猫 采购申请审批流程",
'summary': """ 'summary': """
Short (1 phrase/line) summary of the module's purpose, used as 采购申请审批流程""",
subtitle on modules listing or apps.openerp.com""",
'description': """ 'description': """
Long description of module's purpose Long description of module's purpose

View File

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

View File

@@ -22,3 +22,9 @@ class PurchaseRequest(models.Model):
self.state = 'approved' self.state = 'approved'
return res return res
@api.model
def _get_under_validation_exceptions(self):
res = super(PurchaseRequest, self)._get_under_validation_exceptions()
res.append("state")
return res

View File

@@ -0,0 +1,16 @@
from odoo import models, api
class StockRule(models.Model):
_inherit = 'stock.rule'
def _run_buy(self, procurements):
res = super(StockRule, self)._run_buy(procurements)
# 判断是否根据规则生成新的采购申请单据,如果生成则修改状态为 approved
origins = list(set([procurement[0].origin for procurement in procurements]))
for origin in origins:
pr_ids = self.env["purchase.request"].sudo().search(
[('origin', 'like', origin), ('rule_new_add', '=', True), ('state', '=', 'draft')])
if pr_ids:
pr_ids.write({'need_validation': False})
pr_ids.write({"state": "approved", 'need_validation': True, 'rule_new_add': False})
return res

View File

@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
{ {
'name': "机企猫 采购申请审批流程", 'name': "机企猫 采购审批流程",
'summary': """ 'summary': """
采购申请审批流程""", 采购审批流程""",
'description': """ 'description': """
采购申请审批流程""", 采购审批流程""",
'author': "My Company", 'author': "My Company",
'website': "https://www.yourcompany.com", 'website': "https://www.yourcompany.com",

View File

@@ -4,6 +4,7 @@ import json
import logging import logging
from odoo.addons.sf_mrs_connect.controllers.controllers import Sf_Mrs_Connect from odoo.addons.sf_mrs_connect.controllers.controllers import Sf_Mrs_Connect
from odoo.addons.sf_manufacturing.controllers.controllers import Manufacturing_Connect from odoo.addons.sf_manufacturing.controllers.controllers import Manufacturing_Connect
from odoo.addons.sf_base.decorators.api_log import api_log
from datetime import datetime from datetime import datetime
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@@ -12,6 +13,7 @@ class WorkorderExceptionConroller(http.Controller):
@http.route('/AutoDeviceApi/BillError', type='json', auth='public', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/BillError', type='json', auth='public', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('工单对接错误', requester='中控系统')
def workder_exception(self, **kw): def workder_exception(self, **kw):
""" """
记录工单异常 记录工单异常

View File

@@ -9,7 +9,7 @@ class MrpWorkorder(models.Model):
exception_ids = fields.One2many('jikimo.workorder.exception', 'workorder_id', string='工单异常记录') exception_ids = fields.One2many('jikimo.workorder.exception', 'workorder_id', string='工单异常记录')
def write(self, values): def write(self, values):
if values.get('test_results') and self.exception_ids: if 'test_results' in values and self.exception_ids:
pending_exception = self.exception_ids.filtered( pending_exception = self.exception_ids.filtered(
lambda exc: exc.state == 'pending' and exc.exception_code == 'YC0005' lambda exc: exc.state == 'pending' and exc.exception_code == 'YC0005'
) )

View File

@@ -133,6 +133,7 @@ class QualityCheck(models.Model):
part_name = fields.Char('零件名称', related='product_id.part_name', readonly=False, store=True) part_name = fields.Char('零件名称', related='product_id.part_name', readonly=False, store=True)
part_number = fields.Char('零件图号', related='product_id.part_number', readonly=False, store=True) part_number = fields.Char('零件图号', related='product_id.part_number', readonly=False, store=True)
material_name = fields.Char('材料名称', compute='_compute_material_name') material_name = fields.Char('材料名称', compute='_compute_material_name')
model_id = fields.Char('模型ID', related='product_id.model_id')
# # 总数量值为调拨单_产品明细_数量 # # 总数量值为调拨单_产品明细_数量
# total_qty = fields.Float('总数量', compute='_compute_total_qty', readonly=True) # total_qty = fields.Float('总数量', compute='_compute_total_qty', readonly=True)
@@ -141,7 +142,7 @@ class QualityCheck(models.Model):
# # 出厂检验报告编号 # # 出厂检验报告编号
# report_number = fields.Char('出厂检验报告编号', compute='_compute_report_number', readonly=True) # report_number = fields.Char('出厂检验报告编号', compute='_compute_report_number', readonly=True)
# 总数量值为调拨单_产品明细_数量 # 总数量值为调拨单_产品明细_数量
total_qty = fields.Char('总数量', compute='_compute_total_qty') total_qty = fields.Char('总数量', compute='_compute_total_qty', store=True)
column_nums = fields.Integer('测量值列数', default=1) column_nums = fields.Integer('测量值列数', default=1)
@@ -153,9 +154,9 @@ class QualityCheck(models.Model):
for move in record.picking_id.move_ids_without_package: for move in record.picking_id.move_ids_without_package:
if move.product_id == record.product_id: if move.product_id == record.product_id:
total_qty = int(move.product_uom_qty) total_qty = int(move.product_uom_qty)
record.total_qty = total_qty if total_qty > 0 else '' record.total_qty = total_qty if total_qty > 0 else 0
else: else:
record.total_qty = '' record.total_qty = 0
# 检验数 # 检验数
check_qty = fields.Integer('检验数', default=lambda self: self._get_default_check_qty()) check_qty = fields.Integer('检验数', default=lambda self: self._get_default_check_qty())
@@ -338,7 +339,7 @@ class QualityCheck(models.Model):
# 4. 获取报告动作并生成PDF此时二维码将包含正确的文档ID # 4. 获取报告动作并生成PDF此时二维码将包含正确的文档ID
report_action = self.env.ref('sf_quality.action_report_quality_inspection') report_action = self.env.ref('sf_quality.action_report_quality_inspection')
pdf_content, _ = report_action._render_qweb_pdf( pdf_content, v = report_action._render_qweb_pdf(
report_ref=report_action.report_name, report_ref=report_action.report_name,
res_ids=self.ids res_ids=self.ids
) )
@@ -735,8 +736,9 @@ class QualityCheck(models.Model):
def _compute_qty_to_test(self): def _compute_qty_to_test(self):
for qc in self: for qc in self:
if qc.is_lot_tested_fractionally: if qc.is_lot_tested_fractionally:
rounding = qc.product_id.uom_id.rounding if qc.product_id.uom_id else 0.01
qc.qty_to_test = float_round(qc.qty_line * qc.testing_percentage_within_lot / 100, qc.qty_to_test = float_round(qc.qty_line * qc.testing_percentage_within_lot / 100,
precision_rounding=self.product_id.uom_id.rounding, rounding_method="UP") precision_rounding=rounding, rounding_method="UP")
else: else:
qc.qty_to_test = qc.qty_line qc.qty_to_test = qc.qty_line

View File

@@ -493,6 +493,9 @@
<field name="picking_id"/> <field name="picking_id"/>
<field name="lot_id"/> <field name="lot_id"/>
<field name="team_id"/> <field name="team_id"/>
<field name="part_number"/>
<field name="part_name"/>
<field name="model_id"/>
<filter string="In Progress" name="progress" domain="[('quality_state', '=', 'none')]"/> <filter string="In Progress" name="progress" domain="[('quality_state', '=', 'none')]"/>
<filter string="Passed" name="passed" domain="[('quality_state', '=', 'pass')]"/> <filter string="Passed" name="passed" domain="[('quality_state', '=', 'pass')]"/>
<filter string="Failed" name="failed" domain="[('quality_state', '=', 'fail')]"/> <filter string="Failed" name="failed" domain="[('quality_state', '=', 'fail')]"/>

View File

@@ -23,8 +23,8 @@ class QualityCheckWizard(models.TransientModel):
lot_name = fields.Char(related='current_check_id.lot_name') lot_name = fields.Char(related='current_check_id.lot_name')
lot_line_id = fields.Many2one(related='current_check_id.lot_line_id') lot_line_id = fields.Many2one(related='current_check_id.lot_line_id')
qty_line = fields.Float(related='current_check_id.qty_line') qty_line = fields.Float(related='current_check_id.qty_line')
qty_to_test = fields.Float(related='current_check_id.qty_to_test') qty_to_test = fields.Float(related='current_check_id.qty_to_test', string='待检')
qty_tested = fields.Float(related='current_check_id.qty_tested', readonly=False) qty_tested = fields.Float(related='current_check_id.qty_tested', string='已检', readonly=False)
measure = fields.Float(related='current_check_id.measure', readonly=False) measure = fields.Float(related='current_check_id.measure', readonly=False)
measure_on = fields.Selection(related='current_check_id.measure_on') measure_on = fields.Selection(related='current_check_id.measure_on')
quality_state = fields.Selection(related='current_check_id.quality_state') quality_state = fields.Selection(related='current_check_id.quality_state')

View File

@@ -1,3 +1,7 @@
pystrich pystrich
cpca cpca==0.5.5
pycryptodome==3.20 wechatpy==1.8.18
pycryptodome==3.22.0
openupgradelib==3.10.0
opcua==0.98.13
openpyxl

View File

@@ -103,12 +103,19 @@ class PrintingUtils(models.AbstractModel):
self.send_to_printer(host, port, zpl_code) self.send_to_printer(host, port, zpl_code)
def add_qr_code_to_pdf(self, pdf_path:str, content:str, buttom_text:Optional[str]=False): def add_qr_code_to_pdf(
self,
pdf_path:str,
content:str,
qr_code_buttom_text:Optional[str]=False,
buttom_text:Optional[str]=False,
):
""" """
在PDF文件中添加二维码 在PDF文件中添加二维码
:param pdf_path: PDF文件路径 :param pdf_path: PDF文件路径
:param content: 二维码内容 :param content: 二维码内容
:param buttom_text: 二维码下方文字 :param qr_code_buttom_text: 二维码下方文字
:param buttom_text: 正文下方文字
:return: 是否成功 :return: 是否成功
""" """
if not os.path.exists(pdf_path): if not os.path.exists(pdf_path):
@@ -156,8 +163,9 @@ class PrintingUtils(models.AbstractModel):
existing_pdf = PdfFileReader(original_file) existing_pdf = PdfFileReader(original_file)
output = PdfFileWriter() output = PdfFileWriter()
# 处理一页 # 处理最后一页
page = existing_pdf.getPage(0) last_page = existing_pdf.getNumPages() - 1
page = existing_pdf.getPage(last_page)
# 获取页面尺寸 # 获取页面尺寸
page_width = float(page.mediaBox.getWidth()) page_width = float(page.mediaBox.getWidth())
page_height = float(page.mediaBox.getHeight()) page_height = float(page.mediaBox.getHeight())
@@ -179,14 +187,30 @@ class PrintingUtils(models.AbstractModel):
qr_y = margin + 20 # 将二维码向上移动一点,为文字留出空间 qr_y = margin + 20 # 将二维码向上移动一点,为文字留出空间
c.drawImage(qr_temp_path, page_width - qr_size - margin, qr_y, width=qr_size, height=qr_size) c.drawImage(qr_temp_path, page_width - qr_size - margin, qr_y, width=qr_size, height=qr_size)
if buttom_text: if qr_code_buttom_text:
# 在二维码下方绘制文字 # 在二维码下方绘制文字
text = buttom_text text = qr_code_buttom_text
text_width = c.stringWidth(text, "SimSun" if font_found else "Helvetica", 10) # 准确计算文字宽度 text_width = c.stringWidth(text, "SimSun" if font_found else "Helvetica", 10) # 准确计算文字宽度
text_x = page_width - qr_size - margin + (qr_size - text_width) / 2 # 文字居中对齐 text_x = page_width - qr_size - margin + (qr_size - text_width) / 2 # 文字居中对齐
text_y = margin + 20 # 文字位置靠近底部 text_y = margin + 20 # 文字位置靠近底部
c.drawString(text_x, text_y, text) c.drawString(text_x, text_y, text)
# 设置字体
if font_found:
c.setFont('SimSun', 12) # 增大字体大小到14pt
else:
# 如果没有找到中文字体,使用默认字体
c.setFont('Helvetica', 120)
logging.warning("未找到中文字体,将使用默认字体")
if buttom_text:
# 在下方中间添加文字
text = buttom_text
text_width = c.stringWidth(text, "SimSun" if font_found else "Helvetica", 12) # 准确计算文字宽度
text_x = (page_width - text_width) / 2 # 文字居中对齐
text_y = margin + 20 # 文字位置靠近底部
c.drawString(text_x, text_y, text)
c.save() c.save()
# 读取带有二维码的临时PDF # 读取带有二维码的临时PDF
@@ -196,12 +220,13 @@ class PrintingUtils(models.AbstractModel):
# 合并原始页面和二维码页面 # 合并原始页面和二维码页面
page.mergePage(qr_page) page.mergePage(qr_page)
output.addPage(page)
# 添加剩余的页面 # 添加剩余的页面
for i in range(1, existing_pdf.getNumPages()): for i in range(0, last_page):
output.addPage(existing_pdf.getPage(i)) output.addPage(existing_pdf.getPage(i))
output.addPage(page)
# 保存最终的PDF到一个临时文件 # 保存最终的PDF到一个临时文件
final_temp_path = pdf_path + '.tmp' final_temp_path = pdf_path + '.tmp'
with open(final_temp_path, "wb") as output_file: with open(final_temp_path, "wb") as output_file:

View File

@@ -4,6 +4,7 @@ import json
import logging import logging
from odoo import http from odoo import http
from odoo.http import request from odoo.http import request
from odoo.addons.sf_base.decorators.api_log import api_log
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@@ -11,6 +12,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/MachineToolGroup', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/MachineToolGroup', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('机床刀具组', requester='中控系统')
def get_maintenance_tool_groups_Info(self, **kw): def get_maintenance_tool_groups_Info(self, **kw):
""" """
机床刀具组接口 机床刀具组接口

View File

@@ -27,21 +27,27 @@ def api_log(name=None, requester=None):
# 执行原始函数 # 执行原始函数
result = func(*args, **kwargs) result = func(*args, **kwargs)
origin_result = result
if isinstance(result, str):
result = json.loads(result)
# 计算响应时间 # 计算响应时间
end_time = datetime.now() end_time = datetime.now()
response_time = (end_time - start_time).total_seconds() response_time = (end_time - start_time).total_seconds()
# 获取响应状态
status = result.get('code') if 'code' in result else result.get('ErrorCode') if 'ErrorCode' in result else 200
# 创建日志记录 # 创建日志记录
log_vals = { log_vals = {
'name': name or func.__name__, 'name': name or func.__name__,
'path': path, 'path': path,
'method': method, 'method': method.upper(),
'request_data': json.dumps(request_data, ensure_ascii=False), 'request_data': json.dumps(request_data, ensure_ascii=False),
'response_data': json.dumps(result, ensure_ascii=False), 'response_data': json.dumps(result, ensure_ascii=False),
'remote_addr': remote_addr, 'remote_addr': remote_addr,
'response_time': response_time, 'response_time': response_time,
'status': result.get('code', 500), 'status': 200 if status == 0 else status,
'requester': requester, 'requester': requester,
'responser': '智能工厂' 'responser': '智能工厂'
} }
@@ -49,7 +55,7 @@ def api_log(name=None, requester=None):
# 异步创建日志记录 # 异步创建日志记录
request.env['api.request.log'].sudo().with_context(tracking_disable=True).create(log_vals) request.env['api.request.log'].sudo().with_context(tracking_disable=True).create(log_vals)
return result return origin_result
except Exception as e: except Exception as e:
_logger.error(f"API日志记录失败: {str(e)}") _logger.error(f"API日志记录失败: {str(e)}")

View File

@@ -1,4 +1,9 @@
from odoo import models, fields, api from odoo import models, fields, api
import json, ast
import logging
import requests
_logger = logging.getLogger(__name__)
class ApiRequestLog(models.Model): class ApiRequestLog(models.Model):
@@ -16,3 +21,52 @@ class ApiRequestLog(models.Model):
status = fields.Integer('状态码') status = fields.Integer('状态码')
requester = fields.Char('请求方') requester = fields.Char('请求方')
responser = fields.Char('响应方') responser = fields.Char('响应方')
@api.model
def log_request(self, method, url, name=None, responser=None, **kwargs):
# Log the request
request_headers = kwargs.get('headers', {})
request_body = kwargs.get('json') or kwargs.get('params') or {}
_logger.info(f"Request: {method} {url} Headers: {request_headers} Body: {request_body}")
# Make the actual request
response = requests.request(method, url, **kwargs)
# Log the response
response_status = response.status_code
response_headers = response.headers
response_body = response.text
response_time = response.elapsed.total_seconds()
_logger.info(f"Response: Status: {response_status} Headers: {response_headers} Body: {response_body}")
try:
# 如果是字符串,先尝试用 ast.literal_eval 安全地转换成 Python 对象
if isinstance(response_body, str):
response_body_obj = json.loads(response_body)
else:
response_body_obj = response_body
# 再使用 json.dumps 转换成标准的 JSON 字符串
response_body = json.dumps(response_body_obj, ensure_ascii=False)
except Exception as e:
_logger.warning(f"转换 response_body 到标准 JSON 失败: {str(e)}")
# 如果转换失败,保持原样
# Save to database
self.sudo().create({
'name': name,
'path': url,
'method': method.upper(),
'request_data': request_body,
'response_data': response_body,
'remote_addr': None,
'response_time': response_time,
'status': response_status,
'requester': '智能工厂',
'responser': responser
})
return response

View File

@@ -5,13 +5,15 @@
<field name="model">api.request.log</field> <field name="model">api.request.log</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree> <tree>
<field name="create_date"/>
<field name="name"/> <field name="name"/>
<field name="path"/> <field name="path"/>
<field name="method"/> <field name="method"/>
<field name="remote_addr"/> <field name="remote_addr"/>
<field name="response_time"/> <field name="response_time" sum="0"/>
<field name="status"/> <field name="requester"/>
<field name="responser"/>
<field name="create_date" string="请求时间"/>
<field name="status" sum="0"/>
</tree> </tree>
</field> </field>
</record> </record>
@@ -32,6 +34,8 @@
<group> <group>
<field name="response_time"/> <field name="response_time"/>
<field name="status"/> <field name="status"/>
<field name="requester"/>
<field name="responser"/>
<field name="create_date" string="请求时间"/> <field name="create_date" string="请求时间"/>
</group> </group>
</group> </group>
@@ -48,6 +52,23 @@
</field> </field>
</record> </record>
<record model="ir.ui.view" id="view_api_request_log_search">
<field name="name">api.request.log.search</field>
<field name="model">api.request.log</field>
<field name="arch" type="xml">
<search string="API请求日志">
<field name="name"/>
<field name="requester"/>
<field name="responser"/>
<group>
<filter name="name" context="{'group_by':'name'}"/>
<filter name="requester" context="{'group_by':'requester'}"/>
<filter name="responser" context="{'group_by':'responser'}"/>
</group>
</search>
</field>
</record>
<record id="action_api_request_log" model="ir.actions.act_window"> <record id="action_api_request_log" model="ir.actions.act_window">
<field name="name">API请求日志</field> <field name="name">API请求日志</field>
<field name="res_model">api.request.log</field> <field name="res_model">api.request.log</field>

View File

@@ -17,7 +17,7 @@ class ResProductCategory(models.Model):
class ResProductProduct(models.Model): class ResProductProduct(models.Model):
_inherit = 'product.product' _inherit = 'product.product'
single_manufacturing = fields.Boolean(string="单个制造") # single_manufacturing = fields.Boolean(string="单个制造")
is_bfm = fields.Boolean('业务平台是否自动创建', default=False) is_bfm = fields.Boolean('业务平台是否自动创建', default=False)

View File

@@ -2,8 +2,8 @@
# from odoo import fields, models, api # from odoo import fields, models, api
# from odoo.exceptions import UserError # from odoo.exceptions import UserError
# from odoo.tools import str2bool # from odoo.tools import str2bool
#
#
# class ResMrpRoutingWorkcenter(models.Model): # class ResMrpRoutingWorkcenter(models.Model):
# _inherit = 'mrp.routing.workcenter' # _inherit = 'mrp.routing.workcenter'
# def init(self): # def init(self):

View File

@@ -3,12 +3,12 @@
# from odoo import fields, models, api # from odoo import fields, models, api
# from odoo.exceptions import UserError # from odoo.exceptions import UserError
# from odoo.tools import str2bool # from odoo.tools import str2bool
#
#
# class SfProductionProcessParameter(models.Model): # class SfProductionProcessParameter(models.Model):
# _inherit = 'sf.production.process.parameter' # _inherit = 'sf.production.process.parameter'
#
#
# @api.model # @api.model
# def create(self, vals): # def create(self, vals):
# # if vals.get('code', '/') == '/' or vals.get('code', '/') is False: # # if vals.get('code', '/') == '/' or vals.get('code', '/') is False:
@@ -26,7 +26,7 @@
# def create_service_product(self): # def create_service_product(self):
# service_categ = self.env.ref( # service_categ = self.env.ref(
# 'sf_dlm.product_category_surface_technics_sf').sudo() # 'sf_dlm.product_category_surface_technics_sf').sudo()
#
# product_name = f"{self.process_id.name}_{self.name}" # product_name = f"{self.process_id.name}_{self.name}"
# product_id = self.env['product.template'].search( # product_id = self.env['product.template'].search(
# [("name", '=', product_name)]) # [("name", '=', product_name)])
@@ -48,7 +48,7 @@
# 'partner_id': res_partner.id, # 'partner_id': res_partner.id,
# 'price': 1, })], # 'price': 1, })],
# }) # })
#
# def create_work_center(self): # def create_work_center(self):
# production_process_parameter = self # production_process_parameter = self
# if not production_process_parameter.process_id: # if not production_process_parameter.process_id:
@@ -70,7 +70,7 @@
# production_process_parameter.routing_id = routing_id.id # production_process_parameter.routing_id = routing_id.id
# else: # else:
# production_process_parameter.routing_id = workcenter_id.id # production_process_parameter.routing_id = workcenter_id.id
#
# def init(self): # def init(self):
# super(SfProductionProcessParameter, self).init() # super(SfProductionProcessParameter, self).init()
# # 在模块初始化时触发计算字段的更新 # # 在模块初始化时触发计算字段的更新

View File

@@ -48,6 +48,7 @@
'views/mrp_workorder_batch_replan.xml', 'views/mrp_workorder_batch_replan.xml',
'views/purchase_order_view.xml', 'views/purchase_order_view.xml',
'views/product_template_views.xml', 'views/product_template_views.xml',
# 'views/stock_warehouse_orderpoint.xml',
], ],
'assets': { 'assets': {

View File

@@ -6,12 +6,14 @@ from datetime import datetime
from odoo.addons.sf_manufacturing.models.agv_scheduling import RepeatTaskException from odoo.addons.sf_manufacturing.models.agv_scheduling import RepeatTaskException
from odoo import http from odoo import http
from odoo.http import request from odoo.http import request
from odoo.addons.sf_base.decorators.api_log import api_log
class Manufacturing_Connect(http.Controller): class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/GetWoInfo', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/GetWoInfo', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('获取工单', requester='中控系统')
def get_Work_Info(self, **kw): def get_Work_Info(self, **kw):
""" """
自动化传递工单号获取工单信息 自动化传递工单号获取工单信息
@@ -54,6 +56,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/GetShiftPlan', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/GetShiftPlan', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('获取日计划', requester='中控系统')
def get_ShiftPlan(self, **kw): def get_ShiftPlan(self, **kw):
""" """
自动化每天获取机台日计划 自动化每天获取机台日计划
@@ -107,6 +110,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/QcCheck', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/QcCheck', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('工件预调(前置三元检测)', requester='中控系统')
def get_qcCheck(self, **kw): def get_qcCheck(self, **kw):
""" """
工件预调(前置三元检测) 工件预调(前置三元检测)
@@ -149,6 +153,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/FeedBackStart', type='json', auth='none', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/FeedBackStart', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('工单开始', requester='中控系统')
def button_Work_START(self, **kw): def button_Work_START(self, **kw):
""" """
工单任务开始 工单任务开始
@@ -198,6 +203,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/FeedBackEnd', type='json', auth='none', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/FeedBackEnd', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('工单结束', requester='中控系统')
def button_Work_End(self, **kw): def button_Work_End(self, **kw):
""" """
工单任务结束 工单任务结束
@@ -249,6 +255,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/PartQualityInspect', type='json', auth='none', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/PartQualityInspect', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('零件检测(后置三元检测)', requester='中控系统')
def PartQualityInspect(self, **kw): def PartQualityInspect(self, **kw):
""" """
零件质检 零件质检
@@ -295,6 +302,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/CMMProgDolod', type='json', auth='none', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/CMMProgDolod', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('CMM测量程序下载', requester='中控系统')
def CMMProgDolod(self, **kw): def CMMProgDolod(self, **kw):
""" """
中控系统传递RFID编号给MES获取测量程序文件。Ftp下载文件 中控系统传递RFID编号给MES获取测量程序文件。Ftp下载文件
@@ -335,6 +343,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/NCProgDolod', type='json', auth='none', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/NCProgDolod', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('CAM加工程序下载', requester='中控系统')
def NCProgDolod(self, **kw): def NCProgDolod(self, **kw):
""" """
中控系统传递RFID编号给MES获取程序单及程序文件。Ftp下载文件 中控系统传递RFID编号给MES获取程序单及程序文件。Ftp下载文件
@@ -376,6 +385,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/LocationChange', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/LocationChange', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('库位变更', requester='中控系统')
def LocationChange(self, **kw): def LocationChange(self, **kw):
""" """
库位变更 库位变更
@@ -480,6 +490,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/AGVToProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/AGVToProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('AGV运送上产线', requester='中控系统')
def AGVToProduct(self, **kw): def AGVToProduct(self, **kw):
""" """
AGV运送上产线完成 AGV运送上产线完成
@@ -552,6 +563,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/AGVDownProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/AGVDownProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('AGV运送下产线', requester='中控系统')
def AGVDownProduct(self, **kw): def AGVDownProduct(self, **kw):
""" """
MES调度AGV搬运零件AGV托盘到产线接驳站。 MES调度AGV搬运零件AGV托盘到产线接驳站。

View File

@@ -45,6 +45,8 @@ class JikimoSaleRoutePicking(Sf_Bf_Connect):
product.product_tmpl_id.is_customer_provided = True if item['embryo_redundancy_id'] else False product.product_tmpl_id.is_customer_provided = True if item['embryo_redundancy_id'] else False
order_id.with_user(request.env.ref("base.user_admin")).sale_order_create_line(product, item) order_id.with_user(request.env.ref("base.user_admin")).sale_order_create_line(product, item)
i += 1 i += 1
if kw.get('contract_file_name') and kw.get('contract_file'):
order_id.create_sale_documents(kw.get('contract_file_name'), kw.get('contract_file'))
res['factory_order_no'] = order_id.name res['factory_order_no'] = order_id.name
order_id.confirm_to_supply_method() order_id.confirm_to_supply_method()
except Exception as e: except Exception as e:

View File

@@ -18,3 +18,4 @@ from . import quick_easy_order
from . import purchase_order from . import purchase_order
from . import quality_check from . import quality_check
from . import purchase_request_line from . import purchase_request_line
# from . import stock_warehouse_orderpoint

View File

@@ -279,7 +279,7 @@ class MrpProduction(models.Model):
production_id.part_name = production_id.product_id.part_name production_id.part_name = production_id.product_id.part_name
elif production_id.product_id.categ_id.type == '坯料': elif production_id.product_id.categ_id.type == '坯料':
product_name = '' product_name = ''
match = re.search(r'(S\d{5}-\d)', production_id.product_id.name) match = re.search(r'(S\d{5}-\d+)', production_id.product_id.name)
# 如果匹配成功,提取结果 # 如果匹配成功,提取结果
if match: if match:
product_name = match.group(0) product_name = match.group(0)
@@ -636,13 +636,17 @@ class MrpProduction(models.Model):
# 增加触发时间参数 # 增加触发时间参数
def update_programming_state(self, trigger_time=None, reprogramming_reason=None): def update_programming_state(self, trigger_time=None, reprogramming_reason=None):
try: try:
reason = ""
manufacturing_type = None manufacturing_type = None
if self.is_scrap: if self.is_scrap:
manufacturing_type = 'scrap' manufacturing_type = 'scrap'
reason = "报废"
elif self.tool_state == '2': elif self.tool_state == '2':
manufacturing_type = 'invalid_tool_rework' manufacturing_type = 'invalid_tool_rework'
reason = "无效功能刀具"
elif self.is_rework: elif self.is_rework:
manufacturing_type = 'rework' manufacturing_type = 'rework'
reason = "返工"
res = {'programming_no': self.programming_no, res = {'programming_no': self.programming_no,
'manufacturing_type': manufacturing_type, 'manufacturing_type': manufacturing_type,
'trigger_time': trigger_time, 'trigger_time': trigger_time,
@@ -657,6 +661,16 @@ class MrpProduction(models.Model):
result = json.loads(ret['result']) result = json.loads(ret['result'])
logging.info('update_programming_state-ret:%s' % result) logging.info('update_programming_state-ret:%s' % result)
if result['status'] == 1: if result['status'] == 1:
self.programming_record_ids.create({
'number': len(self.programming_record_ids) + 1,
'production_id': self.id,
'reason': reason,
'programming_method': False,
'current_programming_count': False,
'target_production_id': False,
'apply_time': fields.Datetime.now(),
'send_time': False,
})
self.write({'is_rework': True}) self.write({'is_rework': True})
else: else:
raise UserError(ret['message']) raise UserError(ret['message'])
@@ -787,6 +801,17 @@ class MrpProduction(models.Model):
if ret['status'] == 1: if ret['status'] == 1:
self.write( self.write(
{'programming_no': ret['programming_no'], 'programming_state': '编程中', 'work_state': '编程中'}) {'programming_no': ret['programming_no'], 'programming_state': '编程中', 'work_state': '编程中'})
# 生成编程记录
self.programming_record_ids.create({
'number': len(self.programming_record_ids) + 1,
'production_id': self.id,
'reason': '首次下发',
'programming_method': False,
'current_programming_count': False,
'target_production_id': False,
'apply_time': fields.Datetime.now(),
'send_time': False,
})
else: else:
raise UserError(ret['message']) raise UserError(ret['message'])
except Exception as e: except Exception as e:
@@ -928,12 +953,13 @@ class MrpProduction(models.Model):
# 'sf_stock.stock_route_process_outsourcing').id)] # 'sf_stock.stock_route_process_outsourcing').id)]
# for product_id, request_line_list in grouped_purchase_request_line_sorted_list.items(): # for product_id, request_line_list in grouped_purchase_request_line_sorted_list.items():
# cur_request_line = request_line_list[0] # cur_request_line = request_line_list[0]
# cur_request_line['product_qty'] = len(request_line_list) # # cur_request_line['product_qty'] = cur_request_line['product_qty']
# cur_request_line['request_id'] = pr.id # cur_request_line['request_id'] = pr.id
# cur_request_line['origin'] = ", ".join({item['production_name'] for item in request_line_list if item.get('production_name')}) # cur_request_line['origin'] = ", ".join({item['production_name'] for item in request_line_list if item.get('production_name')})
# cur_request_line.pop('group_id', None) # cur_request_line.pop('group_id', None)
# cur_request_line.pop('production_name', None) # cur_request_line.pop('production_name', None)
# self.env["purchase.request.line"].create(cur_request_line) # self.env["purchase.request.line"].create(cur_request_line)
# pr.button_approved()
# 外协出入库单处理 # 外协出入库单处理
def get_subcontract_pick_purchase(self): def get_subcontract_pick_purchase(self):
@@ -1771,7 +1797,6 @@ class MrpProduction(models.Model):
""" """
检查前置条件:制造订单【状态】=“待排程、待加工”,制造订单的【编程状态】=“已编程”。 检查前置条件:制造订单【状态】=“待排程、待加工”,制造订单的【编程状态】=“已编程”。
""" """
print('申请编程')
if len(self) > 1: if len(self) > 1:
raise UserError('仅支持选择单个制造订单进行编程申请,请重新选择') raise UserError('仅支持选择单个制造订单进行编程申请,请重新选择')
for production in self: for production in self:
@@ -1840,6 +1865,7 @@ class sf_programming_record(models.Model):
target_production_id = fields.Char('目标制造单号') target_production_id = fields.Char('目标制造单号')
apply_time = fields.Datetime('申请时间') apply_time = fields.Datetime('申请时间')
send_time = fields.Datetime('下发时间') send_time = fields.Datetime('下发时间')
apply_uid = fields.Many2one('res.users', '申请人', default=lambda self: self.env.user)
class sf_detection_result(models.Model): class sf_detection_result(models.Model):

View File

@@ -71,7 +71,7 @@ class ResMrpWorkOrder(models.Model):
tracking=True) tracking=True)
back_button_display = fields.Boolean(default=False, compute='_compute_back_button_display', store=True) back_button_display = fields.Boolean(default=False, compute='_compute_back_button_display', store=True)
# pr_mp_count = fields.Integer('采购申请单数量', compute='_compute_pr_mp_count', store=True) # pr_mp_count = fields.Integer('采购申请单数量', compute='_compute_pr_mp_count', store=True)
#
# @api.depends('state') # @api.depends('state')
# def _compute_pr_mp_count(self): # def _compute_pr_mp_count(self):
# for item in self: # for item in self:
@@ -85,6 +85,7 @@ class ResMrpWorkOrder(models.Model):
# item.pr_mp_count = len(pr_ids) # item.pr_mp_count = len(pr_ids)
# else: # else:
# item.pr_mp_count = 0 # item.pr_mp_count = 0
@api.depends('state') @api.depends('state')
def _compute_back_button_display(self): def _compute_back_button_display(self):
for record in self: for record in self:
@@ -129,8 +130,14 @@ class ResMrpWorkOrder(models.Model):
record.back_button_display = False record.back_button_display = False
else: else:
next_workorder = sorted_workorders[position + 1] next_workorder = sorted_workorders[position + 1]
next_state = next_workorder.state # 持续获取下一个工单,直到找到一个不是返工的工单
if (next_state == 'ready' or ( while next_workorder and next_workorder.state == 'rework':
position += 1
if position + 1 < len(sorted_workorders):
next_workorder = sorted_workorders[position + 1]
else:
next_workorder = None
if next_workorder and (next_workorder.state == 'ready' or (
next_workorder.state == 'waiting' and next_workorder.is_subcontract)) and cur_workorder.state == 'done': next_workorder.state == 'waiting' and next_workorder.is_subcontract)) and cur_workorder.state == 'done':
record.back_button_display = True record.back_button_display = True
else: else:
@@ -439,7 +446,6 @@ class ResMrpWorkOrder(models.Model):
action['context'] = dict(self._context, default_origin=self.name) action['context'] = dict(self._context, default_origin=self.name)
return action return action
@api.depends('state', 'production_id.name')
def _compute_surface_technics_purchase_ids(self): def _compute_surface_technics_purchase_ids(self):
for order in self: for order in self:
if order.routing_type == '表面工艺' and order.state not in ['cancel']: if order.routing_type == '表面工艺' and order.state not in ['cancel']:
@@ -485,6 +491,7 @@ class ResMrpWorkOrder(models.Model):
# 'view_mode': 'tree,form', # 'view_mode': 'tree,form',
# }) # })
# return action # return action
def action_view_surface_technics_purchase(self): def action_view_surface_technics_purchase(self):
self.ensure_one() self.ensure_one()
# if self.routing_type == '表面工艺': # if self.routing_type == '表面工艺':
@@ -513,7 +520,8 @@ class ResMrpWorkOrder(models.Model):
return result return result
def _get_surface_technics_purchase_ids(self): def _get_surface_technics_purchase_ids(self):
domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment'), ('state', '!=', 'cancel')] domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment'),
('state', '!=', 'cancel')]
# domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment')] # domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment')]
# domain = [('group_id', '=', self.production_id.procurement_group_id.id), ('purchase_type', '=', 'consignment')] # domain = [('group_id', '=', self.production_id.procurement_group_id.id), ('purchase_type', '=', 'consignment')]
purchase_orders = self.env['purchase.order'].search(domain, order='id desc') purchase_orders = self.env['purchase.order'].search(domain, order='id desc')
@@ -736,21 +744,25 @@ class ResMrpWorkOrder(models.Model):
# self.workpiece_delivery_ids[0].write({'rfid_code': self.rfid_code}) # self.workpiece_delivery_ids[0].write({'rfid_code': self.rfid_code})
def get_plan_workorder(self, production_line): def get_plan_workorder(self, production_line):
tomorrow = (date.today() + timedelta(days=+1)).strftime("%Y-%m-%d") tomorrow = (date.today() + timedelta(days=1)).strftime("%Y-%m-%d")
tomorrow_start = tomorrow + ' 00:00:00' tomorrow_start = f"{tomorrow} 00:00:00"
tomorrow_end = tomorrow + ' 23:59:59' tomorrow_end = f"{tomorrow} 23:59:59"
logging.info('tomorrow:%s' % tomorrow) logging.info('tomorrow:%s' % tomorrow)
sql = """ sql = """
SELECT * SELECT *
FROM mrp_workorder FROM mrp_workorder
WHERE state!='rework' WHERE state!='rework'
to_char(date_planned_start::timestamp + '8 hour','YYYY-MM-DD HH:mm:SS')>= %s AND (date_planned_start + interval '8 hours') >= %s::timestamp
AND to_char(date_planned_finished::timestamp + '8 hour','YYYY-MM-DD HH:mm:SS')<= %s AND (date_planned_finished + interval '8 hours') <= %s::timestamp
""" """
params = [tomorrow_start, tomorrow_end] params = [tomorrow_start, tomorrow_end]
if production_line: if production_line:
line = self.env['sf.production.line'].search(
[('name', '=', production_line)], limit=1)
if not line:
raise ValueError(f"生产线'{production_line}'不存在")
sql += "AND production_line_id = %s" sql += "AND production_line_id = %s"
params.append(production_line) params.append(line.id)
self.env.cr.execute(sql, params) self.env.cr.execute(sql, params)
ids = [t[0] for t in self.env.cr.fetchall()] ids = [t[0] for t in self.env.cr.fetchall()]
return [('id', 'in', ids)] return [('id', 'in', ids)]
@@ -1243,6 +1255,13 @@ class ResMrpWorkOrder(models.Model):
}] }]
return workorders_values_str return workorders_values_str
# def check_lot_exists(self, picking_id, lot_id):
# return bool(
# picking_id.move_ids.move_line_ids.filtered(
# lambda line: line.lot_id.id == lot_id
# )
# )
def _process_compute_state(self): def _process_compute_state(self):
sorted_workorders = sorted(self, key=lambda x: x.sequence) sorted_workorders = sorted(self, key=lambda x: x.sequence)
for workorder in sorted_workorders: for workorder in sorted_workorders:
@@ -1264,10 +1283,17 @@ class ResMrpWorkOrder(models.Model):
workorder.state = 'pending' workorder.state = 'pending'
continue continue
# ================= 如果制造订单制造类型为【人工线下加工】========================== # ================= 如果制造订单制造类型为【人工线下加工】==========================
# lot_id = workorder.production_id.move_raw_ids.move_line_ids.lot_id
# picking_ids = workorder.production_id.picking_ids.filtered(
# lambda wk: wk.location_id.name == '外协收料区' and wk.location_dest_id.name == '制造前')
# exists = any(
# move_line.lot_id == lot_id
# for picking in picking_ids
# for move in picking.move_ids
# for move_line in move.move_line_ids
# )
if (workorder.production_id.production_type == '人工线下加工' if (workorder.production_id.production_type == '人工线下加工'
and workorder.production_id.schedule_state == '已排' and workorder.production_id.schedule_state == '已排'):
and len(workorder.production_id.picking_ids.filtered(
lambda w: w.state not in ['done', 'cancel'])) == 0):
# and workorder.production_id.programming_state == '已编程' # and workorder.production_id.programming_state == '已编程'
if workorder.is_subcontract is True: if workorder.is_subcontract is True:
if workorder.production_id.state == 'rework': if workorder.production_id.state == 'rework':
@@ -1276,6 +1302,9 @@ class ResMrpWorkOrder(models.Model):
purchase_orders_id = self._get_surface_technics_purchase_ids() purchase_orders_id = self._get_surface_technics_purchase_ids()
if purchase_orders_id.state == 'purchase': if purchase_orders_id.state == 'purchase':
workorder.state = 'ready' workorder.state = 'ready'
# picking_id = workorder.production_id.picking_ids.filtered(
# lambda wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区')
# move_out = picking_id.move_ids
move_out = workorder.move_subcontract_workorder_ids[1] move_out = workorder.move_subcontract_workorder_ids[1]
for mo in move_out: for mo in move_out:
if mo.state != 'done': if mo.state != 'done':
@@ -1316,6 +1345,10 @@ class ResMrpWorkOrder(models.Model):
if purchase_orders_id.state == 'purchase': if purchase_orders_id.state == 'purchase':
workorder.state = 'ready' workorder.state = 'ready'
move_out = workorder.move_subcontract_workorder_ids[1] move_out = workorder.move_subcontract_workorder_ids[1]
# picking_id = workorder.production_id.picking_ids.filtered(
# lambda
# wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区')
# move_out = picking_id.move_ids
for mo in move_out: for mo in move_out:
if mo.state != 'done': if mo.state != 'done':
mo.write({'state': 'assigned', 'production_id': False}) mo.write({'state': 'assigned', 'production_id': False})
@@ -1325,7 +1358,6 @@ class ResMrpWorkOrder(models.Model):
else: else:
workorder.state = 'waiting' workorder.state = 'waiting'
@api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state', @api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state',
'production_id.tool_state', 'production_id.schedule_state', 'sequence', 'production_id.tool_state', 'production_id.schedule_state', 'sequence',
'production_id.programming_state') 'production_id.programming_state')
@@ -1357,11 +1389,11 @@ class ResMrpWorkOrder(models.Model):
# 判断是否有坯料的序列号信息 # 判断是否有坯料的序列号信息
boolean = False boolean = False
if self.production_id.move_raw_ids: if self.production_id.move_raw_ids:
if self.production_id.move_raw_ids[0].product_id.categ_type == '坯料': if self.production_id.move_raw_ids[0].product_id.categ_type == '坯料' and \
self.production_id.move_raw_ids[0].product_id.tracking == 'serial':
if self.production_id.move_raw_ids[0].move_line_ids: if self.production_id.move_raw_ids[0].move_line_ids:
if self.production_id.move_raw_ids[0].move_line_ids: if self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name:
if self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name: boolean = True
boolean = True
else: else:
boolean = True boolean = True
if not boolean: if not boolean:
@@ -1391,6 +1423,10 @@ class ResMrpWorkOrder(models.Model):
if self.routing_type == '表面工艺': if self.routing_type == '表面工艺':
if self.is_subcontract is True: if self.is_subcontract is True:
move_out = self.move_subcontract_workorder_ids[1] move_out = self.move_subcontract_workorder_ids[1]
# picking_id = self.production_id.picking_ids.filtered(
# lambda wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区')
# move_out = picking_id.move_ids
# move_out = self.move_subcontract_workorder_ids[1]
# move_out = self.env['stock.move'].search( # move_out = self.env['stock.move'].search(
# [('location_id', '=', self.env['stock.location'].search( # [('location_id', '=', self.env['stock.location'].search(
# [('barcode', 'ilike', 'WH-PREPRODUCTION')]).id), # [('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),

View File

@@ -795,7 +795,7 @@ class ResProductMo(models.Model):
for record in self: for record in self:
if record.categ_id.name == '坯料': if record.categ_id.name == '坯料':
product_name = '' product_name = ''
match = re.search(r'(S\d{5}-\d)', record.name) match = re.search(r'(S\d{5}-\d+)', record.name)
# 如果匹配成功,提取结果 # 如果匹配成功,提取结果
if match: if match:
product_name = match.group(0) product_name = match.group(0)
@@ -1030,6 +1030,7 @@ class ResProductMo(models.Model):
'single_manufacturing': product_id.single_manufacturing, 'single_manufacturing': product_id.single_manufacturing,
'is_bfm': True, 'is_bfm': True,
'active': True, 'active': True,
'tracking': finish_product.tracking, # 坯料的跟踪方式跟随成品
} }
# 外协和采购生成的坯料需要根据材料型号绑定供应商 # 外协和采购生成的坯料需要根据材料型号绑定供应商
if route_type == 'subcontract' or route_type == 'purchase': if route_type == 'subcontract' or route_type == 'purchase':

View File

@@ -59,6 +59,86 @@ class PurchaseOrder(models.Model):
production_id = self.env['mrp.production'].search([('origin', 'in', origins)]) production_id = self.env['mrp.production'].search([('origin', 'in', origins)])
purchase.production_count = len(production_id) purchase.production_count = len(production_id)
# def process_replenish(self,production,total_qty):
# record = self
# bom_line_id = production.bom_id.bom_line_ids
# replenish = self.env['stock.warehouse.orderpoint'].search([
# ('product_id', '=', bom_line_id.product_id.id),
# (
# 'location_id', '=', self.env.ref('sf_stock.stock_location_outsourcing_material_receiving_area').id),
# # ('state', 'in', ['draft', 'confirmed'])
# ], limit=1)
# if not replenish:
# replenish_model = self.env['stock.warehouse.orderpoint']
# replenish = replenish_model.create({
# 'product_id': bom_line_id.product_id.id,
# 'location_id': self.env.ref(
# 'sf_stock.stock_location_outsourcing_material_receiving_area').id,
# 'route_id': self.env.ref('sf_stock.stock_route_process_outsourcing').id,
# 'group_id': record.group_id.id,
# 'qty_to_order': total_qty,
# 'origin': record.name,
# })
# else:
# replenish.write({
# 'product_id': bom_line_id.product_id.id,
# 'location_id': self.env.ref(
# 'sf_stock.stock_location_outsourcing_material_receiving_area').id,
# 'route_id': self.env.ref('sf_stock.stock_route_process_outsourcing').id,
# 'group_id': record.group_id.id,
# 'qty_to_order': total_qty + replenish.qty_to_order,
# 'origin': record.name + ',' + replenish.origin,
# })
# replenish.action_replenish()
# def outsourcing_service_replenishment(self):
# record = self
# if record.purchase_type != 'consignment':
# return
# grouped_lines = {}
# for line in record.order_line:
# if line.related_product.id not in grouped_lines:
# grouped_lines[line.related_product.id] = []
# grouped_lines[line.related_product.id].append(line)
# for product_id,lines in grouped_lines.items():
# production = self.env['mrp.production'].search([('product_id', '=', product_id)], limit=1)
# if not production:
# continue
# total_qty = sum(line.product_qty for line in lines)
# record.process_replenish(production,total_qty)
# for product_id,lines in grouped_lines.items():
# productions = self.env['mrp.production'].search([('product_id', '=', product_id)], limit=1)
# if not productions:
# continue
# # production.bom_id.bom_line_ids.product_id
# location_id = self.env['stock.location'].search([('name', '=', '制造前')])
# quants = self.env['stock.quant'].search([
# ('product_id', '=', productions.bom_id.bom_line_ids.product_id.id),
# ('location_id', '=', location_id.id)
# ])
# total_qty = sum(quants.mapped('quantity')) # 计算该位置的总库存量
# is_available = total_qty > 0
# if not is_available:
# raise UserError('请先完成坯料入库')
# for production_id in productions:
# work_ids = production_id.workorder_ids.filtered(
# lambda wk: wk.state not in ['done', 'rework', 'cancel'])
# if not work_ids:
# continue
# min_sequence_wk = min(work_ids, key=lambda wk: wk.sequence)
# if min_sequence_wk.is_subcontract:
# picking_id = production_id.picking_ids.filtered(
# lambda wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区')
# move_out = picking_id.move_ids
# for mo in move_out:
# if mo.state != 'done':
# mo.write({'state': 'assigned', 'production_id': False})
# if not mo.move_line_ids:
# self.env['stock.move.line'].create(
# mo.get_move_line(production_id, min_sequence_wk))
# product = self.env['mrp.production'].search([('product_id', '=', product_id)], limit=1)
# match = re.search(r'(S\d{5}-\d)',product.name)
# pass
def button_confirm(self): def button_confirm(self):
for record in self: for record in self:
for line in record.order_line: for line in record.order_line:
@@ -66,38 +146,15 @@ class PurchaseOrder(models.Model):
raise UserError('请对【产品】中的【数量】进行输入') raise UserError('请对【产品】中的【数量】进行输入')
if line.price_unit <= 0: if line.price_unit <= 0:
raise UserError('请对【产品】中的【单价】进行输入') raise UserError('请对【产品】中的【单价】进行输入')
# if record.purchase_type == 'consignment': # record.outsourcing_service_replenishment()
# bom_line_id = record.order_line[0].purchase_request_lines.request_id.bom_id.bom_line_ids
# replenish = self.env['stock.warehouse.orderpoint'].search([
# ('product_id', '=', bom_line_id.product_id.id),
# (
# 'location_id', '=', self.env.ref('sf_stock.stock_location_outsourcing_material_receiving_area').id),
# # ('state', 'in', ['draft', 'confirmed'])
# ], limit=1)
# if not replenish:
# replenish_model = self.env['stock.warehouse.orderpoint']
# replenish = replenish_model.create({
# 'product_id': bom_line_id.product_id.id,
# 'location_id': self.env.ref(
# 'sf_stock.stock_location_outsourcing_material_receiving_area').id,
# 'route_id': self.env.ref('sf_stock.stock_route_process_outsourcing').id,
# 'group_id': record.group_id.id,
# 'qty_to_order': 1,
# 'origin': record.name,
# })
# else:
# replenish.write({
# 'product_id': bom_line_id.product_id.id,
# 'location_id': self.env.ref(
# 'sf_stock.stock_location_outsourcing_material_receiving_area').id,
# 'route_id': self.env.ref('sf_stock.stock_route_process_outsourcing').id,
# 'group_id': record.group_id.id,
# 'qty_to_order': 1 + replenish.qty_to_order,
# 'origin': record.name + ',' + replenish.origin,
# })
# replenish.action_replenish()
return super(PurchaseOrder, self).button_confirm() res = super(PurchaseOrder, self).button_confirm()
for line in self.order_line:
# 将产品不追踪序列号的行项目设置qty_done
if line.move_ids and line.move_ids[0].product_id.tracking == 'none':
line.move_ids[0].quantity_done = line.move_ids[0].product_qty
return res
origin_sale_id = fields.Many2one('sale.order', string='销售订单号', store=True, compute='_compute_origin_sale_id') origin_sale_id = fields.Many2one('sale.order', string='销售订单号', store=True, compute='_compute_origin_sale_id')
@@ -151,7 +208,7 @@ class PurchaseOrderLine(models.Model):
continue continue
if record.product_id.categ_id.name == '坯料': if record.product_id.categ_id.name == '坯料':
product_name = '' product_name = ''
match = re.search(r'(S\d{5}-\d)', record.product_id.name) match = re.search(r'(S\d{5}-\d+)', record.product_id.name)
# 如果匹配成功,提取结果 # 如果匹配成功,提取结果
if match: if match:
product_name = match.group(0) product_name = match.group(0)

View File

@@ -1,4 +1,4 @@
# # -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# import base64 # import base64
# import datetime # import datetime
# import logging # import logging
@@ -7,24 +7,24 @@
# import re # import re
# import traceback # import traceback
# from operator import itemgetter # from operator import itemgetter
#
# import requests # import requests
# from itertools import groupby # from itertools import groupby
# from collections import defaultdict, namedtuple # from collections import defaultdict, namedtuple
#
# from odoo import api, fields, models, SUPERUSER_ID, _ # from odoo import api, fields, models, SUPERUSER_ID, _
# from odoo.exceptions import UserError, ValidationError # from odoo.exceptions import UserError, ValidationError
# from odoo.tools import float_compare, float_round, float_is_zero, format_datetime # from odoo.tools import float_compare, float_round, float_is_zero, format_datetime
#
#
# class PurchaseRequestLine(models.Model): # class PurchaseRequestLine(models.Model):
# _inherit = 'purchase.request' # _inherit = 'purchase.request'
# is_subcontract = fields.Boolean(string='是否外协',default=False) # is_subcontract = fields.Boolean(string='是否外协',default=False)
# class PurchaseRequestLine(models.Model): # class PurchaseRequestLine(models.Model):
# _inherit = 'purchase.request.line' # _inherit = 'purchase.request.line'
# is_subcontract = fields.Boolean(string='是否外协') # is_subcontract = fields.Boolean(string='是否外协')
#
#
# class PurchaseRequest(models.Model): # class PurchaseRequest(models.Model):
# _inherit = 'purchase.request' # _inherit = 'purchase.request'
# bom_id = fields.Many2one('mrp.bom') # bom_id = fields.Many2one('mrp.bom')

View File

@@ -58,7 +58,8 @@ class SaleOrder(models.Model):
# 复制成品模板上的属性 # 复制成品模板上的属性
line.product_id.product_tmpl_id.copy_template(product_template_id) line.product_id.product_tmpl_id.copy_template(product_template_id)
# 将模板上的single_manufacturing属性复制到成品上 # 将模板上的single_manufacturing属性复制到成品上
line.product_id.single_manufacturing = product_template_id.single_manufacturing # line.product_id.single_manufacturing = product_template_id.single_manufacturing
# line.product_id.tracking = product_template_id.tracking
order_id = self order_id = self
product = line.product_id product = line.product_id
@@ -75,7 +76,7 @@ class SaleOrder(models.Model):
'embryo_redundancy_id': line.embryo_redundancy_id, 'embryo_redundancy_id': line.embryo_redundancy_id,
} }
product_name = '' product_name = ''
match = re.search(r'(S\d{5}-\d)', product.name) match = re.search(r'(S\d{5}-\d+)', product.name)
# 如果匹配成功,提取结果 # 如果匹配成功,提取结果
if match: if match:
product_name = match.group(0) product_name = match.group(0)
@@ -189,7 +190,30 @@ class SaleOrder(models.Model):
'target': 'new', 'target': 'new',
'res_id': wizard.id, 'res_id': wizard.id,
} }
def create_sale_documents(self, contract_file_name, contract_file):
# 创建ir.attachment记录
attachment = self.env['ir.attachment'].sudo().create({
'name': contract_file_name,
'type': 'binary',
'datas': contract_file,
'res_model': 'sale.order',
})
# 获取默认的文档文件夹
workspace = self.env.ref('sf_sale.documents_sales_contracts_folder_1').id
# 创建 documents.document 记录
document = self.env['documents.document'].sudo().create({
'name': contract_file_name,
'attachment_id': attachment.id,
'folder_id': workspace,
'res_model': 'sale.order',
'res_id': self.id,
})
self.write({
'contract_document_id': document.id
})
class SaleOrderLine(models.Model): class SaleOrderLine(models.Model):
_inherit = 'sale.order.line' _inherit = 'sale.order.line'

View File

@@ -20,13 +20,13 @@ class SfProductionProcessParameter(models.Model):
# is_product_button = fields.Boolean(compute='_compute_is_product_button',default=False) # is_product_button = fields.Boolean(compute='_compute_is_product_button',default=False)
# is_delete_button = fields.Boolean(compute='_compute_is_delete_button', default=False) # is_delete_button = fields.Boolean(compute='_compute_is_delete_button', default=False)
# routing_id = fields.Many2one('mrp.routing.workcenter', string="工序") # routing_id = fields.Many2one('mrp.routing.workcenter', string="工序")
#
# @api.depends('outsourced_service_products') # @api.depends('outsourced_service_products')
# def _compute_service_products(self): # def _compute_service_products(self):
# for record in self: # for record in self:
# # 假设取第一条作为主明细 # # 假设取第一条作为主明细
# record.service_products = record.outsourced_service_products.id if record.outsourced_service_products else False # record.service_products = record.outsourced_service_products.ids if record.outsourced_service_products else False
#
# def _inverse_service_products(self): # def _inverse_service_products(self):
# for record in self: # for record in self:
# if record.service_products: # if record.service_products:
@@ -45,7 +45,7 @@ class SfProductionProcessParameter(models.Model):
# for record in self: # for record in self:
# if len(record.outsourced_service_products) > 1: # if len(record.outsourced_service_products) > 1:
# raise ValidationError("工艺参数不能与多个产品关联") # raise ValidationError("工艺参数不能与多个产品关联")
#
# @api.onchange('outsourced_service_products') # @api.onchange('outsourced_service_products')
# def _onchange_validate_partner_limit(self): # def _onchange_validate_partner_limit(self):
# for record in self: # for record in self:
@@ -58,7 +58,7 @@ class SfProductionProcessParameter(models.Model):
# record.is_product_button = True # record.is_product_button = True
# else: # else:
# record.is_product_button = False # record.is_product_button = False
#
# def has_wksp_prefix(self): # def has_wksp_prefix(self):
# """ # """
# 判断字符串是否以WKSP开头不区分大小写 # 判断字符串是否以WKSP开头不区分大小写

View File

@@ -564,6 +564,13 @@ class StockPicking(models.Model):
part_numbers = fields.Char(string="零件图号", compute='_compute_part_info', store=True, index=True) part_numbers = fields.Char(string="零件图号", compute='_compute_part_info', store=True, index=True)
part_names = fields.Char(string="零件名称", compute='_compute_part_info', store=True, index=True) part_names = fields.Char(string="零件名称", compute='_compute_part_info', store=True, index=True)
model_id = fields.Char('模型ID', compute='_compute_model_id', store=True, index=True)
@api.depends('move_ids_without_package.model_id')
def _compute_model_id(self):
for picking in self:
model_id = picking.move_ids_without_package.mapped('model_id')
picking.model_id = ','.join(filter(None, model_id))
@api.depends('move_ids_without_package.part_number', 'move_ids_without_package.part_name') @api.depends('move_ids_without_package.part_number', 'move_ids_without_package.part_name')
def _compute_part_info(self): def _compute_part_info(self):
@@ -631,6 +638,62 @@ class StockPicking(models.Model):
move.action_clear_lines_show_details() move.action_clear_lines_show_details()
move.action_show_details() move.action_show_details()
res = super().button_validate() res = super().button_validate()
# lot_ids = None
# product_ids = self.move_ids.mapped('product_id')
# if not self.move_ids[0].product_id.single_manufacturing and self.move_ids[0].product_id.tracking == 'none':
# lot_ids = self.move_ids.move_line_ids.mapped('lot_id')
# production_ids = self.sale_order_id.mrp_production_ids if self.sale_order_id else self.env['mrp.production']
# if res and self.location_id.name == '外协收料区' and self.location_dest_id.name == '制造前':
# # 如果是最后一张外协入库单,则设置库存位置的预留数量
# for production_id in production_ids:
# if lot_ids:
# lot_id = production_id.move_raw_ids.move_line_ids.lot_id
# # picking_ids = production_id.picking_ids.filtered(
# # lambda wk: wk.location_id.name == '外协收料区' and wk.location_dest_id.name == '制造前')
# if lot_id in lot_ids:
# workorder_id = production_id.workorder_ids.filtered(
# lambda a: a.state == 'progress' and a.is_subcontract)
# if not workorder_id:
# continue
# workorder_id.button_finish()
# else:
# workorder_id = production_id.workorder_ids.filtered(lambda a: a.state == 'progress' and a.is_subcontract)
# if not workorder_id:
# continue
# workorder_id.button_finish()
# # lot_id = workorder.production_id.move_raw_ids.move_line_ids.lot_id
# # picking_ids = workorder.production_id.picking_ids.filtered(
# # lambda wk: wk.location_id.name == '外协收料区' and wk.location_dest_id.name == '制造前')
# # if move_in:
# # workorder = move_in.subcontract_workorder_id
# # workorders = workorder.production_id.workorder_ids
# # subcontract_workorders = workorders.filtered(
# # lambda wo: wo.is_subcontract == True and wo.state != 'cancel').sorted('sequence')
# # # if workorder == subcontract_workorders[-1]:
# # # self.env['stock.quant']._update_reserved_quantity(
# # # move_in.product_id, move_in.location_dest_id, move_in.product_uom_qty,
# # # lot_id=move_in.move_line_ids.lot_id,
# # # package_id=False, owner_id=False, strict=False
# # # )
# # workorder.button_finish()
# if res and self.location_id.name == '制造前' and self.location_dest_id.name == '外协加工区':
# for production_id in production_ids:
# if lot_ids:
# lot_id = production_id.move_raw_ids.move_line_ids.lot_id
# # picking_ids = production_id.picking_ids.filtered(
# # lambda wk: wk.location_id.name == '外协收料区' and wk.location_dest_id.name == '制造前')
# if lot_id in lot_ids:
# workorder_id = production_id.workorder_ids.filtered(
# lambda a: a.state == 'progress' and a.is_subcontract)
# if not workorder_id:
# continue
# workorder_id.button_finish()
# else:
# workorder_id = production_id.workorder_ids.filtered(lambda a: a.state == 'ready' and a.is_subcontract)
# if not workorder_id:
# continue
# workorder_id.button_start()
picking_type_in = self.env.ref('sf_manufacturing.outcontract_picking_in').id picking_type_in = self.env.ref('sf_manufacturing.outcontract_picking_in').id
if res is True and self.picking_type_id.id == picking_type_in: if res is True and self.picking_type_id.id == picking_type_in:
# 如果是最后一张外协入库单,则设置库存位置的预留数量 # 如果是最后一张外协入库单,则设置库存位置的预留数量
@@ -675,6 +738,7 @@ class StockPicking(models.Model):
# 创建 外协出库入单 # 创建 外协出库入单
def create_outcontract_picking(self, workorders, item, sorted_workorders): def create_outcontract_picking(self, workorders, item, sorted_workorders):
production = workorders[0].production_id
for workorder in workorders: for workorder in workorders:
if workorder.move_subcontract_workorder_ids: if workorder.move_subcontract_workorder_ids:
workorder.move_subcontract_workorder_ids.write({'state': 'cancel'}) workorder.move_subcontract_workorder_ids.write({'state': 'cancel'})
@@ -706,7 +770,7 @@ class StockPicking(models.Model):
}) })
moves_in = self.env['stock.move'].sudo().with_context(context).create( moves_in = self.env['stock.move'].sudo().with_context(context).create(
self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_in, self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_in,
procurement_group_id.id, move_dest_id)) procurement_group_id.id, move_dest_id, production.product_uom_qty))
picking_in = self.create( picking_in = self.create(
moves_in._get_new_picking_values_Res(item, workorder, 'WH/OCIN/')) moves_in._get_new_picking_values_Res(item, workorder, 'WH/OCIN/'))
# pick_ids.append(picking_in.id) # pick_ids.append(picking_in.id)
@@ -716,7 +780,7 @@ class StockPicking(models.Model):
# self.env.context.get('default_production_id') # self.env.context.get('default_production_id')
moves_out = self.env['stock.move'].sudo().with_context(context).create( moves_out = self.env['stock.move'].sudo().with_context(context).create(
self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_out, self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_out,
procurement_group_id.id, moves_in.id)) procurement_group_id.id, moves_in.id, production.product_uom_qty))
workorder.write({'move_subcontract_workorder_ids': [(6, 0, [moves_in.id, moves_out.id])]}) workorder.write({'move_subcontract_workorder_ids': [(6, 0, [moves_in.id, moves_out.id])]})
picking_out = self.create( picking_out = self.create(
moves_out._get_new_picking_values_Res(item, workorder, 'WH/OCOUT/')) moves_out._get_new_picking_values_Res(item, workorder, 'WH/OCOUT/'))
@@ -782,6 +846,7 @@ class ReStockMove(models.Model):
materiel_height = fields.Float(string='物料高度', digits=(16, 4)) materiel_height = fields.Float(string='物料高度', digits=(16, 4))
part_number = fields.Char(string='零件图号', compute='_compute_part_info', store=True) part_number = fields.Char(string='零件图号', compute='_compute_part_info', store=True)
part_name = fields.Char(string='零件名称', compute='_compute_part_info', store=True) part_name = fields.Char(string='零件名称', compute='_compute_part_info', store=True)
model_id = fields.Char('模型ID', related='product_id.model_id')
@api.depends('product_id') @api.depends('product_id')
def _compute_part_info(self): def _compute_part_info(self):
@@ -792,7 +857,7 @@ class ReStockMove(models.Model):
move.part_name = move.product_id.part_name move.part_name = move.product_id.part_name
elif move.product_id.categ_id.type == '坯料': elif move.product_id.categ_id.type == '坯料':
product_name = '' product_name = ''
match = re.search(r'(S\d{5}-\d)', move.product_id.name) match = re.search(r'(S\d{5}-\d+)', move.product_id.name)
# 如果匹配成功,提取结果 # 如果匹配成功,提取结果
if match: if match:
product_name = match.group(0) product_name = match.group(0)
@@ -824,7 +889,7 @@ class ReStockMove(models.Model):
continue continue
product_name = '' product_name = ''
logging.info('制造订单的产品 %s', production_id.product_id.name) logging.info('制造订单的产品 %s', production_id.product_id.name)
match = re.search(r'(S\d{5}-\d)', production_id.product_id.name) match = re.search(r'(S\d{5}-\d+)', production_id.product_id.name)
# 如果匹配成功,提取结果 # 如果匹配成功,提取结果
if match: if match:
product_name = match.group(0) product_name = match.group(0)
@@ -848,7 +913,7 @@ class ReStockMove(models.Model):
traceback_error = traceback.format_exc() traceback_error = traceback.format_exc()
logging.error("零件图号 零件名称获取失败:%s" % traceback_error) logging.error("零件图号 零件名称获取失败:%s" % traceback_error)
def _get_stock_move_values_Res(self, item, picking_type_id, group_id, move_dest_ids=False): def _get_stock_move_values_Res(self, item, picking_type_id, group_id, move_dest_ids=False, product_uom_qty=1.0):
route_id = self.env.ref('sf_manufacturing.route_surface_technology_outsourcing').id route_id = self.env.ref('sf_manufacturing.route_surface_technology_outsourcing').id
stock_rule = self.env['stock.rule'].sudo().search( stock_rule = self.env['stock.rule'].sudo().search(
[('route_id', '=', route_id), ('picking_type_id', '=', picking_type_id)]) [('route_id', '=', route_id), ('picking_type_id', '=', picking_type_id)])
@@ -857,7 +922,7 @@ class ReStockMove(models.Model):
'company_id': item.company_id.id, 'company_id': item.company_id.id,
'product_id': item.bom_id.bom_line_ids.product_id.id, 'product_id': item.bom_id.bom_line_ids.product_id.id,
'product_uom': item.bom_id.bom_line_ids.product_uom_id.id, 'product_uom': item.bom_id.bom_line_ids.product_uom_id.id,
'product_uom_qty': 1.0, 'product_uom_qty': product_uom_qty,
'location_id': stock_rule.location_src_id.id, 'location_id': stock_rule.location_src_id.id,
'location_dest_id': stock_rule.location_dest_id.id, 'location_dest_id': stock_rule.location_dest_id.id,
'origin': item.name, 'origin': item.name,
@@ -902,7 +967,7 @@ class ReStockMove(models.Model):
'location_id': self.picking_id.location_id.id, 'location_id': self.picking_id.location_id.id,
'location_dest_id': self.picking_id.location_dest_id.id, 'location_dest_id': self.picking_id.location_dest_id.id,
'picking_id': self.picking_id.id, 'picking_id': self.picking_id.id,
'reserved_uom_qty': 1.0, 'reserved_uom_qty': self.product_uom_qty,
'lot_id': production_id.move_line_raw_ids.lot_id.id, 'lot_id': production_id.move_line_raw_ids.lot_id.id,
'company_id': self.env.company.id, 'company_id': self.env.company.id,
# 'workorder_id': '' if not sorted_workorders else sorted_workorders.id, # 'workorder_id': '' if not sorted_workorders else sorted_workorders.id,

View File

@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
# Part of SmartGo. See LICENSE file for full copyright and licensing details.
import base64
from io import BytesIO
from odoo import api, fields, models, SUPERUSER_ID, _
class StockWarehouseOrderpoint(models.Model):
_inherit = 'stock.warehouse.orderpoint'
origin = fields.Char(string='来源')
_order = 'create_date DESC'

View File

@@ -427,6 +427,7 @@
<field name="programming_method"/> <field name="programming_method"/>
<field name="current_programming_count"/> <field name="current_programming_count"/>
<field name="target_production_id"/> <field name="target_production_id"/>
<field name="apply_uid"/>
<field name="apply_time"/> <field name="apply_time"/>
<field name="send_time"/> <field name="send_time"/>
</tree> </tree>
@@ -602,6 +603,7 @@
<field name="part_number"/> <field name="part_number"/>
<field name="sale_order_id"/> <field name="sale_order_id"/>
<field name="deadline_of_delivery" icon="fa-calendar" enable_counters="1" filter_domain="[('deadline_of_delivery', 'ilike', self)]"/> <field name="deadline_of_delivery" icon="fa-calendar" enable_counters="1" filter_domain="[('deadline_of_delivery', 'ilike', self)]"/>
<field name="model_id"/>
</xpath> </xpath>
<xpath expr="//field[@name='product_variant_attributes']" position="attributes"> <xpath expr="//field[@name='product_variant_attributes']" position="attributes">
<attribute name="invisible">1</attribute> <attribute name="invisible">1</attribute>

View File

@@ -22,26 +22,26 @@
<field name="is_repeat"/> <field name="is_repeat"/>
<field name="reserved_duration"/> <field name="reserved_duration"/>
</field> </field>
<!-- <xpath expr="//notebook/page[1]" position="before">--> <!-- <xpath expr="//notebook/page[1]" position="before">
<!-- <page string="可选工艺参数">--> <page string="可选工艺参数">
<!-- <field name="optional_process_parameters">--> <field name="optional_process_parameters">
<!-- <tree editable="bottom">--> <tree editable="bottom">
<!-- <field name="is_product_button" invisible="1"/>--> <field name="is_product_button" invisible="1"/>
<!-- <field name="is_delete_button" invisible="1"/>--> <field name="is_delete_button" invisible="1"/>
<!-- <field name="code" attrs="{'readonly': True}"/>--> <field name="code" attrs="{'readonly': True}"/>
<!-- <field name="name" required="1"/>--> <field name="name" required="1"/>
<!-- <field name="service_products" domain="[('detailed_type', '=', 'service'),('server_product_process_parameters_id', '=', False)]"/>--> <field name="service_products" domain="[('detailed_type', '=', 'service'),('server_product_process_parameters_id', '=', False)]"/> -->
<!-- &lt;!&ndash; 按钮列 &ndash;&gt;--> <!-- 按钮列 -->
<!-- <button name="action_create_service_product" string="创建服务产品" type="object"--> <!-- <button name="action_create_service_product" string="创建服务产品" type="object"
<!-- class="btn-primary"--> class="btn-primary"
<!-- attrs="{'invisible': [('is_product_button', '=', True)]}" context="{'default_process_parameter_id':id}"/>--> attrs="{'invisible': [('is_product_button', '=', True)]}" context="{'default_process_parameter_id':id}"/>
<!-- <button name="action_hide_service_products" string="删除" type="object"--> <button name="action_hide_service_products" string="删除" type="object"
<!-- class="oe_highlight"--> class="oe_highlight"
<!-- attrs="{'invisible': [('is_delete_button', '=', True)]}"/>--> attrs="{'invisible': [('is_delete_button', '=', True)]}"/>
<!-- </tree>--> </tree>
<!-- </field>--> </field>
<!-- </page>--> </page>
<!-- </xpath>--> </xpath> -->
</field> </field>
</record> </record>
</data> </data>

View File

@@ -4,30 +4,32 @@
name="Manufacturing" name="Manufacturing"
groups="mrp.group_mrp_user,mrp.group_mrp_manager,sf_base.group_sf_mrp_user,sf_base.group_sf_mrp_manager" groups="mrp.group_mrp_user,mrp.group_mrp_manager,sf_base.group_sf_mrp_user,sf_base.group_sf_mrp_manager"
web_icon="mrp,static/description/icon.svg" web_icon="mrp,static/description/icon.svg"
sequence="145"> sequence="145"/>
<menuitem id="mrp.menu_mrp_manufacturing" <menuitem id="mrp.menu_mrp_manufacturing"
name="Operations" name="Operations"
sequence="10"/> parent="mrp.menu_mrp_root"
sequence="10"/>
<menuitem id="mrp.mrp_planning_menu_root" <menuitem id="mrp.mrp_planning_menu_root"
name="Planning" name="Planning"
sequence="15"/> parent="mrp.menu_mrp_root"
sequence="15"/>
<menuitem id="mrp.enu_mrp_bom" <menuitem id="mrp.menu_mrp_bom"
name="Products" name="Products"
sequence="20"/> parent="mrp.menu_mrp_root"
sequence="20"/>
<menuitem id="mrp.menu_mrp_reporting" <menuitem id="mrp.menu_mrp_reporting"
name="Reporting" name="Reporting"
sequence="25"/> parent="mrp.menu_mrp_root"
sequence="25"/>
<menuitem id="mrp.menu_mrp_configuration"
name="Configuration"
groups="mrp.group_mrp_manager,sf_base.group_sf_mrp_manager"
sequence="100"/>
</menuitem>
<menuitem id="mrp.menu_mrp_configuration"
name="Configuration"
parent="mrp.menu_mrp_root"
groups="mrp.group_mrp_manager,sf_base.group_sf_mrp_manager"
sequence="100"/>
</odoo> </odoo>

View File

@@ -144,17 +144,17 @@
statusbar_visible="pending,waiting,ready,progress,to be detected,done,rework"/> statusbar_visible="pending,waiting,ready,progress,to be detected,done,rework"/>
</xpath> </xpath>
<xpath expr="//div[@name='button_box']" position="inside"> <xpath expr="//div[@name='button_box']" position="inside">
<!-- <button type="object" name="action_view_pr_mrp_workorder" class="oe_stat_button"--> <!-- <button type="object" name="action_view_pr_mrp_workorder" class="oe_stat_button"
<!-- icon="fa-credit-card"--> icon="fa-credit-card"
<!-- groups="base.group_user,sf_base.group_sf_order_user"--> groups="base.group_user,sf_base.group_sf_order_user"
<!-- attrs="{'invisible': [('pr_mp_count', '=', 0)]}">--> attrs="{'invisible': [('pr_mp_count', '=', 0)]}">
<!-- <div class="o_field_widget o_stat_info">--> <div class="o_field_widget o_stat_info">
<!-- <span class="o_stat_value">--> <span class="o_stat_value">
<!-- <field name="pr_mp_count"/>--> <field name="pr_mp_count"/>
<!-- </span>--> </span>
<!-- <span class="o_stat_text">采购申请</span>--> <span class="o_stat_text">采购申请</span>
<!-- </div>--> </div>
<!-- </button>--> </button> -->
<button type="object" name="action_view_surface_technics_purchase" class="oe_stat_button" <button type="object" name="action_view_surface_technics_purchase" class="oe_stat_button"
icon="fa-credit-card" icon="fa-credit-card"
groups="base.group_user,sf_base.group_sf_order_user" groups="base.group_user,sf_base.group_sf_order_user"
@@ -677,8 +677,9 @@
<field name="inherit_id" ref="mrp.view_mrp_production_work_order_search"/> <field name="inherit_id" ref="mrp.view_mrp_production_work_order_search"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="product_id" position="after"> <field name="product_id" position="after">
<field name="part_number" string="成品零件图号"/> <field name="part_number" string="零件图号"/>
<field name="model_id" string="模型id"/> <field name="part_name" string="零件名称"/>
<field name="model_id" string="模型ID"/>
</field> </field>
<xpath expr="//filter[@name='progress']" position="after"> <xpath expr="//filter[@name='progress']" position="after">
<filter string="待检测" name="state" domain="[('state','=','to be detected')]"/> <filter string="待检测" name="state" domain="[('state','=','to be detected')]"/>

View File

@@ -12,5 +12,18 @@
</xpath> </xpath>
</field> </field>
</record> </record>
<record id="product_template_search_inherit_sf_manufacturing" model="ir.ui.view">
<field name="name">product.template.search</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_search_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='categ_id']" position="after">
<field name="part_number" string="零件图号"/>
<field name="part_name" string="零件名称"/>
<field name="model_id" string="模型ID"/>
</xpath>
</field>
</record>
</data> </data>
</odoo> </odoo>

View File

@@ -73,6 +73,7 @@
<xpath expr="//field[@name='picking_type_id']" position="after"> <xpath expr="//field[@name='picking_type_id']" position="after">
<field name="part_numbers" string="零件图号" filter_domain="[('part_numbers', 'ilike', self)]"/> <field name="part_numbers" string="零件图号" filter_domain="[('part_numbers', 'ilike', self)]"/>
<field name="part_names" string="零件名称" filter_domain="[('part_names', 'ilike', self)]"/> <field name="part_names" string="零件名称" filter_domain="[('part_names', 'ilike', self)]"/>
<field name="model_id" string="模型ID" filter_domain="[('model_id', 'ilike', self)]"/>
</xpath> </xpath>
</field> </field>
</record> </record>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="view_warehouse_orderpoint_tree_editable_inherit" model="ir.ui.view">
<field name="name">补货</field>
<field name="model">stock.warehouse.orderpoint</field>
<field name="inherit_id" ref="stock.view_warehouse_orderpoint_tree_editable"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='qty_to_order']" position="after">
<field name="origin"/>
</xpath>
</field>
</record>
<!-- 继承补货单的搜索视图 -->
</data>
</odoo>

View File

@@ -125,11 +125,17 @@ class ReworkWizard(models.TransientModel):
# 1、单独返工CNC工单则不解绑托盘RFID如单独返工装夹预调工单则自动解绑托盘RFID # 1、单独返工CNC工单则不解绑托盘RFID如单独返工装夹预调工单则自动解绑托盘RFID
# 2、返工CNC工单和装夹预调工单则自动解绑RFID # 2、返工CNC工单和装夹预调工单则自动解绑RFID
clamp_workorder_ids = rework_workorder_ids.filtered(lambda rp: rp.routing_type == '装夹预调') clamp_workorder_ids = rework_workorder_ids.filtered(lambda rp: rp.routing_type == '装夹预调')
vals_list = [{
'id': order.id,
'rfid_code_old': order.rfid_code,
'rfid_code': False
} for order in rework_workorder_ids]
rework_workorder_ids.write(vals_list)
if clamp_workorder_ids: if clamp_workorder_ids:
for clamp_workorder_id in clamp_workorder_ids: for clamp_workorder_id in clamp_workorder_ids:
self.production_id.workorder_ids.filtered( self.production_id.workorder_ids.filtered(lambda wk: (
lambda wk: wk.processing_panel == clamp_workorder_id.processing_panel).write( wk.processing_panel == clamp_workorder_id.processing_panel)).write({'rfid_code': None})
{'rfid_code': None})
# 返工工单状态设置为【返工】 # 返工工单状态设置为【返工】
rework_workorder_ids.write({'state': 'rework'}) rework_workorder_ids.write({'state': 'rework'})
# 查询返工工单对应的工艺设计记录,并调用方法拼接数据,用于创建新的工单 # 查询返工工单对应的工艺设计记录,并调用方法拼接数据,用于创建新的工单
@@ -268,6 +274,8 @@ class ReworkWizard(models.TransientModel):
'cmm_ids': new_cnc_workorder.cmm_ids.sudo()._json_cmm_program( 'cmm_ids': new_cnc_workorder.cmm_ids.sudo()._json_cmm_program(
cnc_work.processing_panel, ret), cnc_work.processing_panel, ret),
'cnc_worksheet': old_cnc_rework.cnc_worksheet}) 'cnc_worksheet': old_cnc_rework.cnc_worksheet})
# 复制装夹图纸
new_cnc_workorder.processing_drawing = old_cnc_rework.processing_drawing
# ========== 处理装夹预调 【装夹图纸】 数据 ================ # ========== 处理装夹预调 【装夹图纸】 数据 ================
for new_pre_work in new_pre_workorder_ids: for new_pre_work in new_pre_workorder_ids:
pre_rework = max(self.production_id.workorder_ids.filtered( pre_rework = max(self.production_id.workorder_ids.filtered(

View File

@@ -126,6 +126,11 @@
<field name="model">mrp.production</field> <field name="model">mrp.production</field>
</record> </record>
<record id="bussiness_purchase_request" model="jikimo.message.bussiness.node">
<field name="name">采购申请待处理通知</field>
<field name="model">purchase.request</field>
</record>
<record id="bussiness_outsourcing" model="jikimo.message.bussiness.node"> <record id="bussiness_outsourcing" model="jikimo.message.bussiness.node">
<field name="name">委外加工采购单提醒</field> <field name="name">委外加工采购单提醒</field>
<field name="model">purchase.order</field> <field name="model">purchase.order</field>
@@ -156,9 +161,13 @@
<field name="model">purchase.order</field> <field name="model">purchase.order</field>
</record> </record>
<record id="bussiness_quality_check" model="jikimo.message.bussiness.node"> <!-- <record id="bussiness_quality_check" model="jikimo.message.bussiness.node">-->
<field name="name">待质检提醒</field> <!-- <field name="name">待质检提醒</field>-->
<field name="model">product.product</field> <!-- <field name="model">product.product</field>-->
<!-- </record>-->
<record id="bussiness_quality_check_none" model="jikimo.message.bussiness.node">
<field name="name">待质检</field>
<field name="model">quality.check</field>
</record> </record>
</data> </data>

View File

@@ -339,6 +339,18 @@
事项:请确认委外采购单并处理。</field> 事项:请确认委外采购单并处理。</field>
</record> </record>
<record id="template_purchase_request" model="jikimo.message.template">
<field name="name">采购申请待处理通知</field>
<field name="model_id" ref="purchase_request.model_purchase_request"/>
<field name="model">purchase.request</field>
<field name="bussiness_node_id" ref="bussiness_purchase_request"/>
<field name="msgtype">markdown</field>
<field name="urgency">normal</field>
<field name="content">### 采购申请待处理提醒:
单号:[{{name}}]({{request_url}})
事项:您有一张新的采购申请单待处理。</field>
</record>
<record id="template_purchase_remind" model="jikimo.message.template"> <record id="template_purchase_remind" model="jikimo.message.template">
<field name="name">外购订单采购单提醒</field> <field name="name">外购订单采购单提醒</field>
<field name="model_id" ref="purchase.model_purchase_order"/> <field name="model_id" ref="purchase.model_purchase_order"/>
@@ -402,16 +414,28 @@
</field> </field>
</record> </record>
<record id="template_quality_check" model="jikimo.message.template"> <!-- <record id="template_quality_check" model="jikimo.message.template">-->
<!-- <field name="name">待质检提醒</field>-->
<!-- <field name="model_id" ref="product.model_product_product"/>-->
<!-- <field name="model">product.product</field>-->
<!-- <field name="bussiness_node_id" ref="bussiness_quality_check"/>-->
<!-- <field name="msgtype">markdown</field>-->
<!-- <field name="urgency">normal</field>-->
<!-- <field name="content">### 待质检提醒:-->
<!--单号:产品[{{name}}]({{url}})-->
<!--事项:有{{num}}个质检单需要处理。</field>-->
<!-- </record>-->
<!-- </data>-->
<record id="template_quality_check_none" model="jikimo.message.template">
<field name="name">待质检提醒</field> <field name="name">待质检提醒</field>
<field name="model_id" ref="product.model_product_product"/> <field name="model_id" ref="quality.model_quality_check"/>
<field name="model">product.product</field> <field name="model">quality.check</field>
<field name="bussiness_node_id" ref="bussiness_quality_check"/> <field name="bussiness_node_id" ref="bussiness_quality_check_none"/>
<field name="msgtype">markdown</field> <field name="msgtype">markdown</field>
<field name="urgency">normal</field> <field name="urgency">normal</field>
<field name="content">### 待质检提醒: <field name="content">### 待质检提醒:
单号:产品[{{name}}]({{url}}) 单号:[{{name}}]({{url}})
事项:有{{num}}个质检单需要处理。</field> 事项:有个质检单需要处理({{type_name}})</field>
</record> </record>
</data> </data>

View File

@@ -14,3 +14,4 @@ from . import sf_message_mrp_production_wizard
from . import sf_message_mrp_production_adjust_wizard from . import sf_message_mrp_production_adjust_wizard
from . import sf_message_product from . import sf_message_product
from . import sf_message_quality_check from . import sf_message_quality_check
from . import sf_message_purchase_request

View File

@@ -29,18 +29,18 @@ class SFMessageProduct(models.Model):
'{{number}}', str(production_num)).replace( '{{number}}', str(production_num)).replace(
'{{request_url}}', url) '{{request_url}}', url)
contents.append(content) contents.append(content)
if message_queue_id.message_template_id.name == '待质检提醒': # if message_queue_id.message_template_id.name == '待质检提醒':
content = message_queue_id.message_template_id.content # content = message_queue_id.message_template_id.content
product_product = self.env['product.product'].sudo().search([('id', '=', int(message_queue_id.res_id))]) # product_product = self.env['product.product'].sudo().search([('id', '=', int(message_queue_id.res_id))])
quality_check_num = self.env['quality.check'].sudo().search_count( # quality_check_num = self.env['quality.check'].sudo().search_count(
[('product_id', '=', product_product.id), ('quality_state', '=', 'none')]) # [('product_id', '=', product_product.id), ('quality_state', '=', 'none')])
if quality_check_num >= 1: # if quality_check_num >= 1:
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') # url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
action_id = self.env.ref('quality_control.quality_check_action_report').id # action_id = self.env.ref('quality_control.quality_check_action_report').id
url_with_id = f"{url}/web#view_type=list&action={action_id}" # url_with_id = f"{url}/web#view_type=list&action={action_id}"
content = content.replace('{{name}}', product_product.name).replace('{{url}}', url_with_id).replace( # content = content.replace('{{name}}', product_product.name).replace('{{url}}', url_with_id).replace(
'{{num}}', str(quality_check_num)) # '{{num}}', str(quality_check_num))
contents.append(content) # contents.append(content)
return contents, message_queue_ids return contents, message_queue_ids
def get_request_url(self, routing_type): def get_request_url(self, routing_type):

View File

@@ -0,0 +1,43 @@
import logging
import re
from odoo import models, fields, api, _
from urllib.parse import urlencode
class SFMessagePurchaseRequest(models.Model):
_name = 'purchase.request'
_description = "采购申请"
_inherit = ['purchase.request', 'jikimo.message.dispatch']
def write(self, vals):
original_state = self.state
res = super(SFMessagePurchaseRequest, self).write(vals)
if vals.get('state') == 'approved' and original_state != 'approved':
self.add_queue('采购申请待处理通知')
return res
def _get_message(self, message_queue_ids):
contents = []
for message_queue_id in message_queue_ids:
if message_queue_id.message_template_id.name == '采购申请待处理通知':
content = message_queue_id.message_template_id.content
url = self.request_url(int(message_queue_id.res_id))
request_line = self.env['purchase.request'].search([('id', '=', int(message_queue_id.res_id))])
content = content.replace('{{name}}', request_line.name).replace(
'{{request_url}}', url)
contents.append(content)
return contents, message_queue_ids
def request_url(self, id):
url = self.env['ir.config_parameter'].get_param('web.base.url')
action_id = self.env.ref('purchase_request.purchase_request_form_action').id
menu_id = self.env.ref('purchase_request.menu_purchase_request_act').id
# 查询参数
params = {'id': id, 'menu_id': menu_id, 'action': action_id,
'model': 'purchase.request',
'view_type': 'form'}
# 拼接查询参数
query_string = urlencode(params)
# 拼接URL
full_url = url + "/web#" + query_string
return full_url

View File

@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import logging import logging
from odoo import models, fields, api, _ from odoo import models, fields, api, _
from urllib.parse import urlencode
class SFMessageQualityCheck(models.Model): class SFMessageQualityCheck(models.Model):
@@ -10,25 +11,70 @@ class SFMessageQualityCheck(models.Model):
@api.model_create_multi @api.model_create_multi
def create(self, vals_list): def create(self, vals_list):
result = super().create(vals_list) result = super().create(vals_list)
try: # try:
# 判断是否为web页面创建请求 # # 判断是否为web页面创建请求
is_web_request = self.env.context.get('is_web_request', False) # is_web_request = self.env.context.get('is_web_request', False)
if not is_web_request: # if not is_web_request:
for obj in result: # for obj in result:
jikimo_message_queue = self.get_message_queue(obj.product_id.id) # jikimo_message_queue = self.get_message_queue(obj.product_id.id)
if not jikimo_message_queue: # if not jikimo_message_queue:
obj.product_id.add_queue('待质检提醒') # obj.product_id.add_queue('待质检提醒')
except Exception as e: # except Exception as e:
logging.info('add_queue待质检提醒 error:%s' % e) # logging.info('add_queue待质检提醒 error:%s' % e)
qc_ids = result.filtered(lambda qc: qc.quality_state == 'none')
if qc_ids:
message_template_id = self.env['jikimo.message.template'].sudo().search([('name', '=', '待质检提醒')])
for qc in qc_ids:
queue_id = self.env['jikimo.message.queue'].sudo().search(
[('message_template_id', '=', message_template_id[-1].id), ('res_id', '=', qc.id)])
if not queue_id and '制造' not in [pt.name for pt in qc.point_id.picking_type_ids]:
qc.add_queue('待质检')
return result return result
def get_message_queue(self, res_id): #
business_node_id = self.env.ref('sf_message.bussiness_quality_check').id # def get_message_queue(self, res_id):
message_template = self.env["jikimo.message.template"].sudo().search([ # business_node_id = self.env.ref('sf_message.bussiness_quality_check').id
("name", "=", '待质检提醒'), # message_template = self.env["jikimo.message.template"].sudo().search([
("bussiness_node_id", "=", business_node_id) # ("name", "=", '待质检提醒'),
], limit=1) # ("bussiness_node_id", "=", business_node_id)
jikimo_message_queue = self.env['jikimo.message.queue'].sudo().search( # ], limit=1)
[('res_id', '=', res_id), ("message_status", "in", ("pending", "sent")), # jikimo_message_queue = self.env['jikimo.message.queue'].sudo().search(
('message_template_id', '=', message_template.id)]) # [('res_id', '=', res_id), ("message_status", "in", ("pending", "sent")),
return jikimo_message_queue # ('message_template_id', '=', message_template.id)])
# return jikimo_message_queue
def write(self, vals):
original_state = self.quality_state
res = super(SFMessageQualityCheck, self).write(vals)
if res and vals.get('quality_state') == 'none' and original_state != 'none':
message_template_id = self.env['jikimo.message.template'].sudo().search([('name', '=', '待质检提醒')])
queue_id = self.env['jikimo.message.queue'].sudo().search(
[('message_template_id', '=', message_template_id[-1].id), ('res_id', '=', self.id)])
if not queue_id and '制造' not in [pt.name for pt in self.point_id.picking_type_ids]:
self.add_queue('待质检')
return res
def _get_message(self, message_queue_ids):
contents = []
for message_queue_id in message_queue_ids:
if message_queue_id.message_template_id.name == '待质检提醒':
content = message_queue_id.message_template_id.content
qc_line = self.search([('id', '=', int(message_queue_id.res_id))])
url = self.request_url(int(message_queue_id.res_id))
content = content.replace('{{name}}', qc_line.name).replace(
'{{url}}', url).replace('{{type_name}}', qc_line.point_id.title)
contents.append(content)
return contents, message_queue_ids
def request_url(self, id):
url = self.env['ir.config_parameter'].get_param('web.base.url')
action_id = self.env.ref('quality_control.quality_check_action_main').id
menu_id = self.env.ref('quality_control.menu_quality_checks').id
# 查询参数
params = {'id': id, 'menu_id': menu_id, 'action': action_id,
'model': 'quality.check',
'view_type': 'form'}
# 拼接查询参数
query_string = urlencode(params)
# 拼接URL
full_url = url + "/web#" + query_string
return full_url

View File

@@ -24,6 +24,7 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
res = {'status': 1, 'message': '成功'} res = {'status': 1, 'message': '成功'}
datas = request.httprequest.data datas = request.httprequest.data
model_id = None model_id = None
part_number = None
ret = json.loads(datas) ret = json.loads(datas)
ret = json.loads(ret['result']) ret = json.loads(ret['result'])
logging.info('下发编程单:%s' % ret) logging.info('下发编程单:%s' % ret)
@@ -59,7 +60,6 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
res['message'] = '编程单号为%s的CNC程序文件从FTP拉取失败' % (ret['programming_no']) res['message'] = '编程单号为%s的CNC程序文件从FTP拉取失败' % (ret['programming_no'])
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
for production in productions: for production in productions:
model_id = production.product_id.model_id # 一个编程单的制造订单对应同一个模型
production.write({'programming_state': '已编程', 'work_state': '已编程', 'is_rework': False}) production.write({'programming_state': '已编程', 'work_state': '已编程', 'is_rework': False})
for panel in ret['processing_panel'].split(','): for panel in ret['processing_panel'].split(','):
# 查询状态为进行中且工序类型为CNC加工的工单 # 查询状态为进行中且工序类型为CNC加工的工单
@@ -76,6 +76,10 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
cnc_workorder_has.write( cnc_workorder_has.write(
{'cnc_ids': cnc_workorder_has.cnc_ids.sudo()._json_cnc_processing(panel, ret), {'cnc_ids': cnc_workorder_has.cnc_ids.sudo()._json_cnc_processing(panel, ret),
'cmm_ids': cnc_workorder_has.cmm_ids.sudo()._json_cmm_program(panel, ret)}) 'cmm_ids': cnc_workorder_has.cmm_ids.sudo()._json_cmm_program(panel, ret)})
# 一个编程单的制造订单对应同一个模型
model_id = productions[0].product_id.model_id
part_number = productions[0].product_id.part_number
for panel in ret['processing_panel'].split(','): for panel in ret['processing_panel'].split(','):
# 查询状态为进行中且工序类型为CNC加工的工单 # 查询状态为进行中且工序类型为CNC加工的工单
cnc_workorder = productions.workorder_ids.filtered( cnc_workorder = productions.workorder_ids.filtered(
@@ -95,7 +99,12 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
logging.info('panel_file_path:%s' % panel_file_path) logging.info('panel_file_path:%s' % panel_file_path)
# 向编程单中添加二维码 # 向编程单中添加二维码
request.env['printing.utils'].add_qr_code_to_pdf(panel_file_path, model_id, "模型ID%s" % model_id) request.env['printing.utils'].add_qr_code_to_pdf(
panel_file_path,
model_id,
"模型ID%s" % model_id,
"零件图号:%s" % part_number if part_number else None
)
cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())}) cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
pre_workorder = productions.workorder_ids.filtered( pre_workorder = productions.workorder_ids.filtered(
lambda ap: ap.routing_type in ['装夹预调', '人工线下加工'] and ap.state not in ['done', 'rework' lambda ap: ap.routing_type in ['装夹预调', '人工线下加工'] and ap.state not in ['done', 'rework'
@@ -134,94 +143,114 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
for production in productions: for production in productions:
logging.info('production====:%s' % production.name) logging.info('production====:%s' % production.name)
record_ids_obj = production.programming_record_ids.filtered( record_ids_obj = production.programming_record_ids.filtered(
lambda r: r.current_programming_count == ret['reprogramming_num']) lambda r: r.send_time is False)
logging.info('record_ids_obj====:%s' % record_ids_obj) logging.info('record_ids_obj====:%s' % record_ids_obj)
if record_ids_obj: if record_ids_obj:
record_ids_obj = record_ids_obj.sorted('id')[0]
logging.info('record_ids_obj.reason====:%s' % record_ids_obj.reason) logging.info('record_ids_obj.reason====:%s' % record_ids_obj.reason)
record_ids_obj.write( record_ids_obj.write({
{'send_time': ret['send_time'], 'target_production_id': productions_reprogram}) 'current_programming_count': ret['reprogramming_num'],
'send_time': ret['send_time'],
'target_production_id': productions_reprogram,
'programming_method': ret['programme_way']
})
logging.info('已更新编程记录:%s' % record_ids_obj) logging.info('已更新编程记录:%s' % record_ids_obj)
correct_record_ids_obj = record_ids_obj correct_record_ids_obj = record_ids_obj
correct_production_id = production.id correct_production_id = production.id
if ret['reprogramming_num'] == 0: # 更新重新编程记录
logging.info('首次下发')
production.programming_record_ids.create({
'number': 1,
'production_id': production.id,
'reason': '首次下发',
'programming_method': ret['programme_way'],
'current_programming_count': ret['reprogramming_num'],
'target_production_id': productions_reprogram,
'apply_time': False,
'send_time': ret['send_time'],
})
logging.info('已创建首次下发的编程记录:%s' % production.name)
elif ret['reset_flag']:
logging.info('重置状态')
production.programming_record_ids.create({
'number': len(production.programming_record_ids) + 1,
'production_id': production.id,
'reason': '重置状态',
'programming_method': ret['programme_way'],
'current_programming_count': ret['reprogramming_num'],
'target_production_id': productions_reprogram,
'apply_time': False,
'send_time': ret['send_time'],
})
logging.info('已创建重置状态的编程记录:%s' % production.name)
elif ret['manufacturing_type'] == 'rework':
logging.info('返工')
rework_record_ids_obj = production.programming_record_ids.create({
'number': len(production.programming_record_ids) + 1,
'production_id': production.id,
'reason': '返工',
'programming_method': ret['programme_way'],
'current_programming_count': ret['reprogramming_num'],
'target_production_id': productions_reprogram,
'apply_time': ret['trigger_time'],
'send_time': ret['send_time'],
})
logging.info('已创建返工的编程记录:%s' % production.name)
logging.info('rework_record_ids_obj====:%s' % rework_record_ids_obj)
# rework_production_id = production.id
# logging.info('rework_production_id====:%s' % rework_production_id)
elif ret['manufacturing_type'] == 'scrap':
production.programming_record_ids.create({
'number': len(production.programming_record_ids) + 1,
'production_id': production.id,
'reason': '报废',
'programming_method': ret['programme_way'],
'current_programming_count': ret['reprogramming_num'],
'target_production_id': productions_reprogram,
'apply_time': ret['trigger_time'],
'send_time': ret['send_time'],
})
elif ret['manufacturing_type'] == 'invalid_tool_rework':
logging.info('无效功能刀具')
production.programming_record_ids.create({
'number': len(production.programming_record_ids) + 1,
'production_id': production.id,
'reason': '无效功能刀具',
'programming_method': ret['programme_way'],
'current_programming_count': ret['reprogramming_num'],
'target_production_id': productions_reprogram,
'apply_time': ret['trigger_time'],
'send_time': ret['send_time'],
})
logging.info('已创建无效功能刀具的编程记录:%s' % production.name)
elif ret['reprogramming_reason']:
production.programming_record_ids.create({
'number': len(production.programming_record_ids) + 1,
'production_id': production.id,
'reason': ret['reprogramming_reason'],
'programming_method': ret['programme_way'],
'current_programming_count': ret['reprogramming_num'],
'target_production_id': productions_reprogram,
'apply_time': ret['trigger_time'],
'send_time': ret['send_time'],
})
else: else:
logging.info('无对应状态,不需更新编程记录') if ret['reset_flag']:
logging.info('重置状态')
production.programming_record_ids.create({
'number': len(production.programming_record_ids) + 1,
'production_id': production.id,
'reason': '重置状态',
'programming_method': ret['programme_way'],
'current_programming_count': ret['reprogramming_num'],
'target_production_id': productions_reprogram,
'apply_time': False,
'send_time': ret['send_time'],
})
else:
logging.info('无对应状态,不需更新编程记录')
# if ret['reprogramming_num'] == 0:
# logging.info('首次下发')
# production.programming_record_ids.create({
# 'number': 1,
# 'production_id': production.id,
# 'reason': '首次下发',
# 'programming_method': ret['programme_way'],
# 'current_programming_count': ret['reprogramming_num'],
# 'target_production_id': productions_reprogram,
# 'apply_time': False,
# 'send_time': ret['send_time'],
# })
# logging.info('已创建首次下发的编程记录:%s' % production.name)
# elif ret['reset_flag']:
# logging.info('重置状态')
# production.programming_record_ids.create({
# 'number': len(production.programming_record_ids) + 1,
# 'production_id': production.id,
# 'reason': '重置状态',
# 'programming_method': ret['programme_way'],
# 'current_programming_count': ret['reprogramming_num'],
# 'target_production_id': productions_reprogram,
# 'apply_time': False,
# 'send_time': ret['send_time'],
# })
# logging.info('已创建重置状态的编程记录:%s' % production.name)
# elif ret['manufacturing_type'] == 'rework':
# logging.info('返工')
# rework_record_ids_obj = production.programming_record_ids.create({
# 'number': len(production.programming_record_ids) + 1,
# 'production_id': production.id,
# 'reason': '返工',
# 'programming_method': ret['programme_way'],
# 'current_programming_count': ret['reprogramming_num'],
# 'target_production_id': productions_reprogram,
# 'apply_time': ret['trigger_time'],
# 'send_time': ret['send_time'],
# })
# logging.info('已创建返工的编程记录:%s' % production.name)
# logging.info('rework_record_ids_obj====:%s' % rework_record_ids_obj)
# # rework_production_id = production.id
# # logging.info('rework_production_id====:%s' % rework_production_id)
# elif ret['manufacturing_type'] == 'scrap':
# production.programming_record_ids.create({
# 'number': len(production.programming_record_ids) + 1,
# 'production_id': production.id,
# 'reason': '报废',
# 'programming_method': ret['programme_way'],
# 'current_programming_count': ret['reprogramming_num'],
# 'target_production_id': productions_reprogram,
# 'apply_time': ret['trigger_time'],
# 'send_time': ret['send_time'],
# })
# elif ret['manufacturing_type'] == 'invalid_tool_rework':
# logging.info('无效功能刀具')
# production.programming_record_ids.create({
# 'number': len(production.programming_record_ids) + 1,
# 'production_id': production.id,
# 'reason': '无效功能刀具',
# 'programming_method': ret['programme_way'],
# 'current_programming_count': ret['reprogramming_num'],
# 'target_production_id': productions_reprogram,
# 'apply_time': ret['trigger_time'],
# 'send_time': ret['send_time'],
# })
# logging.info('已创建无效功能刀具的编程记录:%s' % production.name)
# elif ret['reprogramming_reason']:
# production.programming_record_ids.create({
# 'number': len(production.programming_record_ids) + 1,
# 'production_id': production.id,
# 'reason': ret['reprogramming_reason'],
# 'programming_method': ret['programme_way'],
# 'current_programming_count': ret['reprogramming_num'],
# 'target_production_id': productions_reprogram,
# 'apply_time': ret['trigger_time'],
# 'send_time': ret['send_time'],
# })
for production in productions: for production in productions:
logging.info('production====:%s' % production.name) logging.info('production====:%s' % production.name)
@@ -240,6 +269,7 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
'current_programming_count': correct_record_ids_obj.current_programming_count, 'current_programming_count': correct_record_ids_obj.current_programming_count,
'target_production_id': correct_record_ids_obj.target_production_id, 'target_production_id': correct_record_ids_obj.target_production_id,
'apply_time': correct_record_ids_obj.apply_time, 'apply_time': correct_record_ids_obj.apply_time,
'apply_uid': correct_record_ids_obj.apply_uid.id,
'send_time': correct_record_ids_obj.send_time, 'send_time': correct_record_ids_obj.send_time,
}) })
logging.info('已创建正确的制造订单的编程记录:%s' % production.name) logging.info('已创建正确的制造订单的编程记录:%s' % production.name)

View File

@@ -15,6 +15,7 @@ class sf_production_plan(models.Model):
# _order = 'state desc, write_date desc' # _order = 'state desc, write_date desc'
part_name = fields.Char('零件名称', related='product_id.part_name', readonly=True) part_name = fields.Char('零件名称', related='product_id.part_name', readonly=True)
part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True) part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True)
model_id = fields.Char('模型ID', related='product_id.model_id')
state = fields.Selection([ state = fields.Selection([
('draft', '待排程'), ('draft', '待排程'),
('done', '已排程'), ('done', '已排程'),

View File

@@ -170,6 +170,7 @@
<field name="part_number"/> <field name="part_number"/>
<field name="order_deadline" filter_domain="[('order_deadline', 'ilike', self)]"/> <field name="order_deadline" filter_domain="[('order_deadline', 'ilike', self)]"/>
<field name="production_line_id"/> <field name="production_line_id"/>
<field name="model_id"/>
<filter string="待排程" name="draft" domain="[('state','=','draft')]"/> <filter string="待排程" name="draft" domain="[('state','=','draft')]"/>
<filter string="已排程" name="done" domain="[('state','=','done')]"/> <filter string="已排程" name="done" domain="[('state','=','done')]"/>
<filter string="加工中" name="processing" domain="[('state','=','processing')]"/> <filter string="加工中" name="processing" domain="[('state','=','processing')]"/>

View File

@@ -13,7 +13,7 @@
'author': 'jikimo', 'author': 'jikimo',
'website': 'https://sf.cs.jikimo.com', 'website': 'https://sf.cs.jikimo.com',
# 此处依赖sf_manufacturing是因为我要重写其中的一个字段operation_id的string故需要sf_manufacturing先安装 # 此处依赖sf_manufacturing是因为我要重写其中的一个字段operation_id的string故需要sf_manufacturing先安装
'depends': ['quality_control', 'web_widget_model_viewer', 'sf_manufacturing','jikimo_attachment_viewer'], 'depends': ['quality_control', 'web_widget_model_viewer', 'sf_manufacturing','jikimo_attachment_viewer', 'jikimo_confirm_dialog'],
'data': [ 'data': [
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'data/check_standards.xml', 'data/check_standards.xml',

View File

@@ -5,6 +5,7 @@ from odoo import fields, models, api
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
from datetime import datetime from datetime import datetime
from odoo.addons.sf_base.commons.common import Common from odoo.addons.sf_base.commons.common import Common
from odoo.tools import float_round, float_compare
class QualityCheck(models.Model): class QualityCheck(models.Model):
@@ -125,10 +126,178 @@ class QualityCheck(models.Model):
# todo 需修改 # todo 需修改
val = ['0037818516'] val = ['0037818516']
logging.info('获取到的工单信息%s' % val) logging.info('获取到的工单信息%s' % val)
r = requests.post(crea_url, json=val, headers=headers) # r = requests.post(crea_url, json=val, headers=headers)
r = self.env['api.request.log'].log_request(
'get',
crea_url,
name='零件特采',
responser='中控系统',
json=val,
headers=headers
)
ret = r.json() ret = r.json()
logging.info('_register_quality_check:%s' % ret) logging.info('_register_quality_check:%s' % ret)
if ret['Succeed']: if ret['Succeed']:
return "零件特采发送成功" return "零件特采发送成功"
else: else:
raise ValidationError("零件特采发送失败") raise ValidationError("零件特采发送失败")
@api.model_create_multi
def create(self, vals_list):
for val in vals_list:
if 'point_id' in val and 'measure_on' not in val:
# 如果没有控制方式字段,则从检查点读取质量方式
point_id = self.env['quality.point'].browse(val['point_id'])
val.update({'measure_on': point_id.measure_on})
return super(QualityCheck, self).create(vals_list)
@api.depends('total_qty','testing_percentage_within_lot', 'is_lot_tested_fractionally')
def _compute_workorder_qty_to_test(self):
for qc in self:
if qc.is_lot_tested_fractionally:
rounding = qc.product_id.uom_id.rounding if qc.product_id.uom_id else 0.01
qc.workorder_qty_to_test = float_round(float(qc.total_qty) * qc.testing_percentage_within_lot / 100,
precision_rounding=rounding, rounding_method="UP")
else:
qc.workorder_qty_to_test = qc.total_qty
@api.depends('picking_id', 'workorder_id')
def _compute_total_qty(self):
super(QualityCheck, self)._compute_total_qty()
for qc in self:
if not qc.picking_id and qc.workorder_id:
qc.total_qty = qc.workorder_id.production_id.product_qty
@api.depends('workorder_qty_to_test')
def _compute_workorder_qty_tested(self):
for qc in self:
qc.workorder_qty_tested = qc.workorder_qty_to_test
workorder_qty_to_test = fields.Float('应检', compute='_compute_workorder_qty_to_test', store=True)
workorder_qty_tested = fields.Float('已检', compute='_compute_workorder_qty_tested', store=True)
workorder_qty_test_failed = fields.Float('不合格数')
@api.onchange('total_qty', 'workorder_qty_test_failed', 'workorder_qty_to_test', 'workorder_qty_tested')
def _onchage_qty(self):
for record in self:
if record.total_qty and record.workorder_qty_to_test and record.workorder_qty_to_test > float(record.total_qty):
record.workorder_qty_to_test = float(record.total_qty)
return {
'warning': {
'title': '警告',
'message': '待检数量不能超过总数量'
}
}
if record.workorder_qty_to_test and record.workorder_qty_tested and record.workorder_qty_tested > record.workorder_qty_to_test:
record.workorder_qty_tested = record.workorder_qty_to_test
return {
'warning': {
'title': '警告',
'message': '已检数量不能超过待检数量'
}
}
if record.workorder_qty_tested and record.workorder_qty_test_failed and record.workorder_qty_test_failed > record.workorder_qty_tested:
record.workorder_qty_test_failed = record.workorder_qty_tested
return {
'warning': {
'title': '警告',
'message': '不合格数量不能超过已检数量'
}
}
total_qty_readonly = fields.Boolean(compute='_compute_total_qty_readonly', store=True)
def _compute_total_qty_readonly(self):
report_test_type_id = self.env.ref('sf_quality.test_type_factory_inspection').id
for record in self:
if (record.measure_on != 'move_line' or record.workorder_id is False) and record.point_id.test_type_id.id != report_test_type_id:
record.total_qty_readonly = True
else:
record.total_qty_readonly = False
def preview_doc(self):
"""预览出厂检验报告"""
self.ensure_one()
picking_qty = sum(self.picking_id.move_ids.filtered(lambda m: m.product_id == self.product_id).mapped('product_uom_qty'))
if not self._check_total_qty(picking_qty) and self.quality_state in ['waiting', 'none']:
return {
'type': 'ir.actions.client',
'tag': 'jikimo_confirm_dialog',
'params': {
'active_id': self.id,
'message': f"拣货调拨单号{self.picking_id.name}需求数量为{picking_qty},当前质量检查单产品数量为{self.total_qty},数量不一致,是否确认继续?",
'next_model': self._name,
'next_method': 'preview_doc_confirm',
'context': self.env.context
}
}
action = self.env.ref("sf_quality.action_report_quality_inspection_preview").read()[0]
return action
def preview_doc_confirm(self):
self.ensure_one()
action = self.env.ref("sf_quality.action_report_quality_inspection_preview").read()[0]
action['context'] = {
'active_id': self.id,
'active_ids': [self.id]
}
return action
def _check_total_qty(self, compare_qty):
"""
检查质量检查单的总数量是否匹配
"""
self.ensure_one()
return float_compare(float(self.total_qty), compare_qty, self.picking_id.product_id.uom_id.rounding) == 0
def preview_do_publish(self):
self.ensure_one()
self._check_part_number()
self._check_measure_line()
self._check_check_qty_and_total_qty()
picking_qty = sum(self.picking_id.move_ids.filtered(lambda m: m.product_id == self.product_id).mapped('product_uom_qty'))
if not self._check_total_qty(picking_qty):
return {
'type': 'ir.actions.client',
'tag': 'jikimo_confirm_dialog',
'params': {
'active_id': self.id,
'message': f"拣货调拨单号{self.picking_id.name}需求数量为{picking_qty},当前质量检查单产品数量为{self.total_qty},数量不一致,是否确认继续?",
'next_model': self._name,
'next_method': 'preview_do_publish_confirm',
'context': self.env.context,
}
}
else:
return self.do_publish()
def preview_do_publish_confirm(self):
self.ensure_one()
return {
'name': '发布确认',
'type': 'ir.actions.act_window',
'res_model': 'quality.check.publish.wizard',
'view_mode': 'form',
'views': [[False, 'form']],
'target': 'new',
'context': {
'default_check_id': self.id,
'default_product_name': self.product_id.name,
'default_total_qty': self.total_qty,
'default_check_qty': self.check_qty,
'default_measure_count': self.column_nums,
'default_item_count': len(self.measure_line_ids),
'default_old_report_name': self.old_report_name,
'default_publish_status': self.publish_status,
'is_web_request': True
}
}

View File

@@ -73,20 +73,59 @@
<xpath expr="//header" position="inside"> <xpath expr="//header" position="inside">
<field name="is_out_check" invisible="1"/> <field name="is_out_check" invisible="1"/>
<field name="publish_status" invisible="1"/> <field name="publish_status" invisible="1"/>
<button name="%(sf_quality.action_report_quality_inspection_preview)d" <button name="preview_doc"
string="预览" string="预览"
type="action" type="object"
class="oe_highlight" attrs="{'invisible': [('is_out_check', '=', False)]}"/> class="oe_highlight" attrs="{'invisible': [('is_out_check', '=', False)]}"/>
<!-- --><!-- 如果还需要打印按钮 --> <!-- --><!-- 如果还需要打印按钮 -->
<!-- <button name="%(sf_quality.action_report_quality_inspection)d" --> <!-- <button name="%(sf_quality.action_report_quality_inspection)d" -->
<!-- string="打印报告" --> <!-- string="打印报告" -->
<!-- type="action"/> --> <!-- type="action"/> -->
<!-- <button name="do_preview" string="预览" type="object" class="btn-primary" attrs="{'invisible': [('is_out_check', '=', False)]}"/> --> <!-- <button name="do_preview" string="预览" type="object" class="btn-primary" attrs="{'invisible': [('is_out_check', '=', False)]}"/> -->
<button name="do_publish" string="发布" type="object" class="btn-primary" attrs="{'invisible': ['|', ('is_out_check', '=', False), ('publish_status', '!=', 'draft')]}"/> <button name="preview_do_publish" string="发布" type="object" class="btn-primary" attrs="{'invisible': ['|', ('is_out_check', '=', False), ('publish_status', '!=', 'draft')]}"/>
<!-- <button name="get_report_url" string="ceshi" type="object" class="btn-primary"/> --> <!-- <button name="get_report_url" string="ceshi" type="object" class="btn-primary"/> -->
<!-- <button name="upload_factory_report" string="upload_factory_report" type="object" class="btn-primary"/> --> <!-- <button name="upload_factory_report" string="upload_factory_report" type="object" class="btn-primary"/> -->
<button name="do_cancel_publish" string="取消发布" type="object" class="btn-primary" confirm="确定取消发布吗?" attrs="{'invisible': ['|',('is_out_check', '=', False), ('publish_status', '!=', 'published')]}"/> <button name="do_cancel_publish" string="取消发布" type="object" class="btn-primary" confirm="确定取消发布吗?" attrs="{'invisible': ['|',('is_out_check', '=', False), ('publish_status', '!=', 'published')]}"/>
<button name="do_re_publish" string="重新发布" type="object" class="btn-primary" attrs="{'invisible': ['|', ('is_out_check', '=', False), ('publish_status', '!=', 'canceled')]}"/> <button name="preview_do_publish" string="重新发布" type="object" class="btn-primary" attrs="{'invisible': ['|', ('is_out_check', '=', False), ('publish_status', '!=', 'canceled')]}"/>
</xpath>
<xpath expr="//field[@name='total_qty']" position="before">
<field name="total_qty_readonly" invisible="1"/>
</xpath>
<xpath expr="//field[@name='total_qty']" position="attributes">
<attribute name="attrs">{
'invisible': ['&amp;', '|', ('measure_on', '!=', 'product'), ('is_out_check', '=', False), '|', ('measure_on', '!=', 'move_line'), ('workorder_id', '=', False)],
'readonly': [('total_qty_readonly', '=', True)],
'on_change': ['|', ('measure_on', '!=', 'move_line'), ('workorder_id', '=', False)]
}</attribute>
</xpath>
<xpath expr="//field[@name='total_qty']" position="after">
<label for="workorder_qty_to_test"
attrs="{'invisible': ['|', ('measure_on', '!=', 'move_line'), '&amp;', ('workorder_id', '=', False), ('is_lot_tested_fractionally', '=', False)]}"/>
<div class="o_row"
attrs="{'invisible': ['|', ('measure_on', '!=', 'move_line'), '&amp;', ('workorder_id', '=', False), ('is_lot_tested_fractionally', '=', False)]}">
<field name="workorder_qty_to_test" attrs="{'readonly': 0, 'on_chnage': 1}"/>
<field name="uom_id"/>
</div>
<label for="workorder_qty_tested"
attrs="{'invisible': ['|', ('measure_on', '!=', 'move_line'), '&amp;', ('workorder_id', '=', False), ('is_lot_tested_fractionally', '=', False)]}"/>
<div class="o_row"
attrs="{'invisible': ['|', ('measure_on', '!=', 'move_line'), '&amp;', ('workorder_id', '=', False), ('is_lot_tested_fractionally', '=', False)]}">
<field name="workorder_qty_tested" attrs="{'readonly': [('quality_state', '!=', 'none')], 'on_chnage': 1}"/>
<field name="uom_id"/>
</div>
<label for="workorder_qty_test_failed"
attrs="{'invisible': ['|', ('measure_on', '!=', 'move_line'), '&amp;', ('workorder_id', '=', False), ('is_lot_tested_fractionally', '=', False)]}"/>
<div class="o_row"
attrs="{'invisible': ['|', ('measure_on', '!=', 'move_line'), '&amp;', ('workorder_id', '=', False), ('is_lot_tested_fractionally', '=', False)]}">
<field name="workorder_qty_test_failed" attrs="{'readonly': [('quality_state', '!=', 'none')], 'on_chnage': 1}"/>
<field name="uom_id"/>
</div>
</xpath>
<xpath expr="//label[@for='qty_tested']" position="attributes">
<attribute name="attrs">{'invisible': ['|', '|', ('measure_on', '!=', 'move_line'), ('is_lot_tested_fractionally', '=', False), '&amp;', ('measure_on', '=', 'move_line'), ('workorder_id', '!=', False)]}</attribute>
</xpath>
<xpath expr="//div[@class='o_row'][.//field[@name='qty_tested']]" position="attributes">
<attribute name="attrs">{'invisible': ['|', '|', ('measure_on', '!=', 'move_line'), ('is_lot_tested_fractionally', '=', False), '&amp;', ('measure_on', '=', 'move_line'), ('workorder_id', '!=', False)]}</attribute>
</xpath> </xpath>
</field> </field>
</record> </record>

View File

@@ -17,6 +17,7 @@
'wizard/sale_order_wizard_views.xml', 'wizard/sale_order_wizard_views.xml',
'wizard/purchase_order_wizard_views.xml', 'wizard/purchase_order_wizard_views.xml',
'data/cron_data.xml', 'data/cron_data.xml',
'data/documents_data.xml',
'views/sale_team.xml', 'views/sale_team.xml',
'views/sale_order_view.xml', 'views/sale_order_view.xml',
'views/res_partner_view.xml', 'views/res_partner_view.xml',

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- 创建采购合同文件夹 -->
<record id="documents_sales_contracts_folder" model="documents.folder">
<field name="name">销售合同</field>
<field name="description">存放销售合同相关文件</field>
<field name="sequence">8</field>
</record>
<record id="documents_sales_contracts_folder_1" model="documents.folder">
<field name="name">下单凭证</field>
<field name="parent_folder_id" ref="documents_sales_contracts_folder"/>
</record>
</data>
</odoo>

View File

@@ -63,6 +63,10 @@ class ReSaleOrder(models.Model):
model_display_version = fields.Char('模型展示版本', default="v1") model_display_version = fields.Char('模型展示版本', default="v1")
contract_document_id = fields.Many2one('documents.document', string='合同文件')
contract_file = fields.Binary(related='contract_document_id.datas', string='合同文件内容')
contract_file_name = fields.Char(related='contract_document_id.attachment_id.name', string='文件名')
# 业务平台分配工厂后在智能工厂先创建销售订单 # 业务平台分配工厂后在智能工厂先创建销售订单
def sale_order_create(self, company_id, delivery_name, delivery_telephone, delivery_address, def sale_order_create(self, company_id, delivery_name, delivery_telephone, delivery_address,
deadline_of_delivery, payments_way, pay_way, order_number, state='sale', deadline_of_delivery, payments_way, pay_way, order_number, state='sale',
@@ -194,18 +198,15 @@ class ReSaleOrder(models.Model):
@api.depends('order_line.purchase_line_ids.order_id') @api.depends('order_line.purchase_line_ids.order_id')
def _compute_purchase_order_count(self): def _compute_purchase_order_count(self):
for order in self: for order in self:
order.purchase_order_count = len(order._get_purchase_orders().filtered( order.purchase_order_count = len(order._get_sale_to_purchase('outsourcing'))
lambda po: po.purchase_type not in ['outsourcing'])) order.consignment_purchase_order_count = len(order._get_sale_to_purchase_1('outsourcing'))
order.consignment_purchase_order_count = len(order._get_purchase_orders().filtered(
lambda po: po.purchase_type in ['outsourcing']))
def action_view_purchase_orders(self): def action_view_purchase_orders(self):
""" """
采购 采购
""" """
self.ensure_one() self.ensure_one()
purchase_order_ids = self._get_purchase_orders().filtered( purchase_order_ids = self._get_sale_to_purchase('outsourcing')
lambda po: po.purchase_type not in ['outsourcing']).ids
action = { action = {
'res_model': 'purchase.order', 'res_model': 'purchase.order',
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
@@ -223,13 +224,20 @@ class ReSaleOrder(models.Model):
}) })
return action return action
def _get_sale_to_purchase(self, purchase_type):
"""查询满足条件的采购订单"""
purchase_order_ids = self._get_purchase_orders().filtered(
lambda po: po.purchase_type not in ['outsourcing']).ids
order_ids = self.env['purchase.order'].sudo().search(
[('origin', '=', self.name), ('purchase_type', '!=', purchase_type)]).ids
return list(set(purchase_order_ids) | set(order_ids))
def action_view_consignment_purchase_orders(self): def action_view_consignment_purchase_orders(self):
""" """
委外加工 委外加工
""" """
self.ensure_one() self.ensure_one()
outsourcing_purchase_order_ids = self._get_purchase_orders().filtered( outsourcing_purchase_order_ids = self._get_sale_to_purchase_1('outsourcing')
lambda po: po.purchase_type in ['outsourcing']).ids
action = { action = {
'res_model': 'purchase.order', 'res_model': 'purchase.order',
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
@@ -247,6 +255,14 @@ class ReSaleOrder(models.Model):
}) })
return action return action
def _get_sale_to_purchase_1(self, purchase_type):
"""查询满足条件的采购订单"""
purchase_order_ids = self._get_purchase_orders().filtered(
lambda po: po.purchase_type == purchase_type).ids
order_ids = self.env['purchase.order'].sudo().search(
[('origin', '=', self.name), ('purchase_type', '=', purchase_type)]).ids
return list(set(purchase_order_ids) | set(order_ids))
class ResaleOrderLine(models.Model): class ResaleOrderLine(models.Model):
_inherit = 'sale.order.line' _inherit = 'sale.order.line'
@@ -393,10 +409,11 @@ class RePurchaseOrder(models.Model):
# # route_ids # # route_ids
# result.append({ # result.append({
# "product_id": server_template.product_variant_id.id, # "product_id": server_template.product_variant_id.id,
# 'related_product': production.product_id.id,
# "name": production.procurement_group_id.name, # "name": production.procurement_group_id.name,
# "date_required": fields.Datetime.now(), # "date_required": fields.Datetime.now(),
# "product_uom_id":server_template.uom_id.id, # "product_uom_id":server_template.uom_id.id,
# "product_qty": 1, # "product_qty": production.product_qty,
# "request_id": False, # "request_id": False,
# "move_dest_ids": False, # "move_dest_ids": False,
# "orderpoint_id": False, # "orderpoint_id": False,
@@ -419,7 +436,7 @@ class RePurchaseOrder(models.Model):
('detailed_type', '=', 'service')]) ('detailed_type', '=', 'service')])
server_product_process.append((0, 0, { server_product_process.append((0, 0, {
'product_id': server_template.product_variant_id.id, 'product_id': server_template.product_variant_id.id,
'product_qty': 1, 'product_qty': production.product_uom_qty,
'product_uom': server_template.uom_id.id, 'product_uom': server_template.uom_id.id,
'related_product': production.product_id.id, 'related_product': production.product_id.id,
'manual_part_number': pp.part_number, 'manual_part_number': pp.part_number,

View File

@@ -198,7 +198,20 @@
</div> </div>
<field name="date_order" attrs="{'invisible': [('state', 'in', ['done', 'cancel'])], 'required': True}" nolabel="1"/> <field name="date_order" attrs="{'invisible': [('state', 'in', ['done', 'cancel'])], 'required': True}" nolabel="1"/>
</xpath> </xpath>
<xpath expr="//notebook/page[@name='customer_signature']" position="after">
<page string="合同" name="contract_documents"
attrs="{'invisible': [('contract_document_id', '=', False)]}">
<group>
<group>
<field name="contract_document_id" invisible="1"/>
<field name="contract_file_name" invisible="1"/>
<field name="contract_file"
widget="adaptive_viewer"
filename="contract_file_name"/>
</group>
</group>
</page>
</xpath>
</field> </field>
</record> </record>

View File

@@ -4,12 +4,14 @@ import json
import base64 import base64
from odoo import http from odoo import http
from odoo.http import request from odoo.http import request
from odoo.addons.sf_base.decorators.api_log import api_log
class Manufacturing_Connect(http.Controller): class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/ToolGroup', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/ToolGroup', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('刀具组', requester='中控系统')
def get_functional_tool_groups_Info(self, **kw): def get_functional_tool_groups_Info(self, **kw):
""" """
刀具组接口 刀具组接口
@@ -39,6 +41,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/ToolInventory', type='json', auth='none', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/ToolInventory', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('功能刀具清单', requester='中控系统')
def get_functional_tool_inventory_Info(self, **kw): def get_functional_tool_inventory_Info(self, **kw):
""" """
功能刀具清单接口 功能刀具清单接口
@@ -68,6 +71,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/ToolEntity', type='json', auth='none', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/ToolEntity', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('功能刀具', requester='中控系统')
def get_functional_tool_entity_Info(self, **kw): def get_functional_tool_entity_Info(self, **kw):
""" """
功能刀具列表接口 功能刀具列表接口

View File

@@ -150,6 +150,17 @@ class FunctionalCuttingToolEntity(models.Model):
else: else:
# 原刀从线边出库 # 原刀从线边出库
item.tool_in_out_stock_location_1(location_id, tool_room_id) item.tool_in_out_stock_location_1(location_id, tool_room_id)
# 系统中该刀在线边刀架其他位置,需先清除这个位置的刀具信息
shelf_location_id = self.env['sf.shelf.location'].sudo().search([
('product_sn_id', '=', item.barcode_id.id)])
if shelf_location_id:
shelf_location_id.write(
{'product_id': None,
'product_sn_id': None,
'tool_rfid': None,
"tool_name_id": None,
'product_num': 0,
'location_status': '空闲'})
# 新刀入库到线边 # 新刀入库到线边
item.create_stock_move(pre_manufacturing_id, location_id) item.create_stock_move(pre_manufacturing_id, location_id)
item.current_shelf_location_id = location_id.id item.current_shelf_location_id = location_id.id

View File

@@ -51,7 +51,15 @@ class SfMaintenanceEquipment(models.Model):
headers = {'Authorization': config['center_control_Authorization']} headers = {'Authorization': config['center_control_Authorization']}
crea_url = config['center_control_url'] + "/AutoDeviceApi/GetToolInfos" crea_url = config['center_control_url'] + "/AutoDeviceApi/GetToolInfos"
params = {"DeviceId": self.name} params = {"DeviceId": self.name}
r = requests.get(crea_url, params=params, headers=headers) # r = requests.get(crea_url, params=params, headers=headers)
r = self.env['api.request.log'].log_request(
'get',
crea_url,
name='机床刀库',
responser='中控系统',
params=params,
headers=headers
)
ret = r.json() ret = r.json()
logging.info('机床刀库register_equipment_tool():%s' % ret) logging.info('机床刀库register_equipment_tool():%s' % ret)
datas = ret['Datas'] datas = ret['Datas']

View File

@@ -514,7 +514,15 @@ class ShelfLocation(models.Model):
crea_url = config['center_control_url'] + "/AutoDeviceApi/GetLocationInfos" crea_url = config['center_control_url'] + "/AutoDeviceApi/GetLocationInfos"
params = {'DeviceId': device_id} params = {'DeviceId': device_id}
r = requests.get(crea_url, params=params, headers=headers) # r = requests.get(crea_url, params=params, headers=headers)
r = self.env['api.request.log'].log_request(
'get',
crea_url,
name='库位信息',
responser='中控系统',
params=params,
headers=headers
)
ret = r.json() ret = r.json()
@@ -812,40 +820,49 @@ class SfStockMoveLine(models.Model):
# # 从目标stock.move对象获取目标stock.picking对象 # # 从目标stock.move对象获取目标stock.picking对象
# dest_picking = dest_move.picking_id if dest_move else False # dest_picking = dest_move.picking_id if dest_move else False
# # 现在dest_picking就是current_picking的下一步 # # 现在dest_picking就是current_picking的下一步
# 添加所有需要的依赖字段
@api.depends('location_id') @api.depends('location_id')
def _compute_current_location_id(self): def _compute_current_location_id(self):
# 批量获取所有相关记录的picking
pickings = self.mapped('picking_id')
# 构建源picking的移库行与目标位置的映射
origin_location_map = {}
for picking in pickings:
# 获取源picking
origin_move = picking.move_ids[:1].move_orig_ids[:1]
if not origin_move:
continue
origin_picking = origin_move.picking_id
if not origin_picking:
continue
# 为每个picking构建lot_id到location的映射
origin_location_map[picking.id] = {
move_line.lot_id.id: move_line.destination_location_id
for move_line in origin_picking.move_line_ids.filtered(
lambda ml: ml.destination_location_id and ml.lot_id
)
}
# 批量更新current_location_id
for record in self: for record in self:
# 使用record代替self来引用当前遍历到的记录 current_picking = record.picking_id
logging.info('record.picking_id.name: %s' % record.picking_id.name) if not current_picking:
logging.info('record.env: %s' % record.env['stock.picking'].search([('name', '=', record.picking_id.name)])) record.current_location_id = False
continue
# 获取当前的stock.picking对 # 获取当前picking对应的lot_location映射
current_picking = record.env['stock.picking'].search([('name', '=', record.picking_id.name)], limit=1) lot_dest_map = origin_location_map.get(current_picking.id, {})
# 获取当前picking的第一个stock.move对象 # 查找匹配的lot_id
current_move = current_picking.move_ids[0] if current_picking.move_ids else False for move_line in current_picking.move_line_ids:
if move_line.lot_id and move_line.lot_id.id in lot_dest_map:
# 如果存在相关的stock.move对象 record.current_location_id = lot_dest_map[move_line.lot_id.id]
if current_move: break
# 获取源stock.move对象 else:
origin_move = current_move.move_orig_ids[0] if current_move.move_orig_ids else False record.current_location_id = False
# 从源stock.move对象获取源stock.picking对象
origin_picking = origin_move.picking_id if origin_move else False
# 如果前一个调拨单有目标货位
if origin_picking:
for i in current_picking.move_line_ids:
for j in origin_picking.move_line_ids:
if j.destination_location_id and i.lot_id == j.lot_id:
# 更新当前记录的current_location_id字段
record.current_location_id = j.destination_location_id
# # 获取目标stock.move对象
# dest_move = current_move.move_dest_ids[0] if current_move.move_dest_ids else False
#
# # 从目标stock.move对象获取目标stock.picking对象
# dest_picking = dest_move.picking_id if dest_move else False
# # 现在dest_picking就是current_picking的下一步
# 是一张单据一张单据往下走的,所以这里的目标货位是上一张单据的当前货位,且这样去计算是可以的。 # 是一张单据一张单据往下走的,所以这里的目标货位是上一张单据的当前货位,且这样去计算是可以的。
@api.depends('location_dest_id') @api.depends('location_dest_id')

View File

@@ -121,12 +121,13 @@ class MrsShelfLocationDataSync(models.Model):
tool.tool_in_out_stock_location(location_id) tool.tool_in_out_stock_location(location_id)
if tool: if tool:
location_id.product_sn_id = tool.barcode_id.id location_id.product_sn_id = tool.barcode_id.id
# 修改功能刀具状态 # 修改功能刀具标准状态值和已使用寿命值、功能刀具状态
if item.get('State') == '报警': if 'LifeStd' in item and 'LifeUse' in item:
if tool.functional_tool_status != item.get('State'): tool.sudo().write({
tool.write({ 'max_lifetime_value': item['LifeStd'],
'functional_tool_status': item['State'] 'used_value': item['LifeUse'],
}) 'functional_tool_status': item['State'],
})
else: else:
location_id.product_sn_id = False location_id.product_sn_id = False
if item['RfidCode']: if item['RfidCode']: