Compare commits

...

479 Commits

Author SHA1 Message Date
guyaodong
8b66fda899 增加kanban悬停tip 2025-06-18 14:26:53 +08:00
huziyang@jikimo.com
6321e7ef23 货位看板详情回退修复,删除幽灵卡片。 2025-06-12 15:38:56 +08:00
huziyang@jikimo.com
23dd88b7ba 解决冲突 2025-06-09 10:01:57 +08:00
huziyang@jikimo.com
f164488e48 屏蔽OCC导入 2025-06-09 09:59:47 +08:00
huziyang@jikimo.com
25b53794bb 处理#6973任务:
1货位编码规则简化
2货位看板直观化展示
2025-06-09 09:49:21 +08:00
胡尧
484fab85be Accept Merge Request #2176: (feature/6694 -> develop)
Merge Request: 修改出厂检验报告预览数据

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2176
2025-06-09 09:09:06 +08:00
胡尧
6ed5de6400 修改出厂检验报告预览数据 2025-06-09 09:08:26 +08:00
胡尧
2449b92bc8 Accept Merge Request #2175: (feature/6694 -> develop)
Merge Request: 修复点击智能按钮点击的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2175?initial=true
2025-06-06 09:34:15 +08:00
胡尧
03ec94d223 修复点击智能按钮点击的问题 2025-06-06 09:33:23 +08:00
胡尧
d26e6edd31 Accept Merge Request #2174: (feature/6694 -> develop)
Merge Request: 修改制造订单的采购申请只能按钮判断规则

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2174?initial=true
2025-06-06 09:17:06 +08:00
胡尧
2ea24f2049 修改制造订单的采购申请只能按钮判断规则 2025-06-06 09:16:41 +08:00
胡尧
b1a04f8f44 Accept Merge Request #2173: (feature/6694 -> develop)
Merge Request: 在出厂检验报告验证数量时,增加产品的匹配

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2173?initial=true
2025-06-06 08:37:58 +08:00
胡尧
0b5415dc47 在出厂检验报告验证数量时,增加产品的匹配 2025-06-06 08:37:38 +08:00
胡尧
59569806e6 Accept Merge Request #2172: (feature/6694 -> develop)
Merge Request: 修复二次弹窗不能刷新页面的问题,但是js方法还需要修改

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2172
2025-06-06 08:29:51 +08:00
胡尧
4e1be6f4d5 修复二次弹窗不能刷新页面的问题,但是js方法还需要修改 2025-06-06 08:29:25 +08:00
禹翔辉
cdf8fbb12a Accept Merge Request #2171: (feature/销售合同优化 -> develop)
Merge Request: 1、销售订单添加合同编号字段,bfm下单接口添加合同编号同步;2、优化解除装夹工单完工时解绑rfid逻辑,优化工单返工时新工单绑定rfid逻辑

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2171
2025-06-05 17:03:44 +08:00
yuxianghui
5d0f094da7 1 2025-06-05 17:03:06 +08:00
yuxianghui
95cb5251dc 1、销售订单添加合同编号字段,bfm下单接口添加合同编号同步;2、优化解除装夹工单完工时解绑rfid逻辑,优化工单返工时新工单绑定rfid逻辑 2025-06-05 16:59:33 +08:00
禹翔辉
c8fe7504c7 Accept Merge Request #2170: (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/2170
2025-06-05 15:35:03 +08:00
yuxianghui
222efc57c2 返工优化 2025-06-05 15:29:14 +08:00
禹翔辉
735d5c659d Accept Merge Request #2169: (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/2169
2025-06-05 14:53:53 +08:00
yuxianghui
50d188b737 处理自动化产线的确认工艺路线报错 2025-06-05 14:49:09 +08:00
禹翔辉
af3ea0f702 Accept Merge Request #2168: (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/2168
2025-06-05 13:59:35 +08:00
yuxianghui
8bf3b68cee 处理批量修改采购申请报错 2025-06-05 13:52:25 +08:00
禹翔辉
2766bc7d34 Accept Merge Request #2167: (feature/销售合同优化 -> develop)
Merge Request: 返工装夹预调工单时,清除同一个面工单的rfid,并且保留返工工单的rfid记录

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2167
2025-06-05 11:20:09 +08:00
yuxianghui
1082384d00 清除返工工单的rfid,并保留记录 2025-06-05 11:18:47 +08:00
yuxianghui
25aab1576d 返工装夹预调工单时,清除同一个面工单的rfid,并且保留返工工单的rfid记录 2025-06-05 11:11:54 +08:00
禹翔辉
87891b45ef Accept Merge Request #2165: (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/2165
2025-06-05 10:23:23 +08:00
胡尧
b2cfdd8d78 Accept Merge Request #2166: (feature/6694 -> develop)
Merge Request: 修改质检单字段显示

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2166?initial=true
2025-06-05 10:21:11 +08:00
胡尧
540b7bcbea 修改质检单字段显示 2025-06-05 10:19:58 +08:00
yuxianghui
c6cb1d367d 处理返工报错 2025-06-05 10:17:39 +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
liaodanlong
d4cf2a9d17 Merge remote-tracking branch 'origin/release/release_2.12' into release/release_2.12 2025-04-28 09:43:49 +08:00
liaodanlong
ecf5dcf2f2 sf .r-采购-采购订单-坯料委外加工生成的采购申请创建采购订单的类型不正确 2025-04-28 09:43:25 +08:00
胡尧
848e8a5fa8 merge branch 'develop' into release/release_2.12 2025-04-28 09:12:50 +08:00
胡尧
cc38383e32 Accept Merge Request #2078: (feature/6694 -> develop)
Merge Request: 修复bug

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2078?initial=true
2025-04-28 09:12:31 +08:00
胡尧
39de4e5ea1 修复bug 2025-04-28 09:12:09 +08:00
胡尧
8b6c904dae Merge branch 'develop' into release/release_2.12 2025-04-28 08:57:10 +08:00
胡尧
a63f2d28f6 Accept Merge Request #2077: (feature/6694 -> develop)
Merge Request: 修改字体获取目录

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2077?initial=true
2025-04-28 08:56:49 +08:00
liaodanlong
08812f169e Merge remote-tracking branch 'origin/release/release_2.12' into release/release_2.12 2025-04-27 17:20:40 +08:00
liaodanlong
ce79016bef 返工未申请重新编程,新工单复制程序文件问题处理 2025-04-27 17:20:15 +08:00
胡尧
fef960f7e8 修改字体获取目录 2025-04-27 16:29:39 +08:00
胡尧
425c9fb64b Merge branch 'develop' into release/release_2.12 2025-04-27 15:32:52 +08:00
胡尧
fc9a58c0c3 Accept Merge Request #2076: (feature/6694 -> develop)
Merge Request: 退回字体处理

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2076?initial=true
2025-04-27 15:32:31 +08:00
胡尧
ed90ad34e6 退回字体处理 2025-04-27 15:32:08 +08:00
胡尧
5662094ec4 Accept Merge Request #2075: (feature/6694 -> develop)
Merge Request: 屏蔽删除文件代码

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2075?initial=true
2025-04-27 15:25:42 +08:00
胡尧
404c56e134 屏蔽删除文件代码 2025-04-27 15:24:28 +08:00
胡尧
9ee614aa10 Accept Merge Request #2074: (feature/6694 -> develop)
Merge Request: 修改上传ftp代码

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2074?initial=true
2025-04-27 15:11:46 +08:00
胡尧
57789dc5a5 修改上传ftp代码 2025-04-27 15:10:22 +08:00
胡尧
52d436909b Merge branch 'develop' into release/release_2.12 2025-04-27 14:37:18 +08:00
胡尧
3a760a66e1 Accept Merge Request #2073: (feature/6694 -> develop)
Merge Request: 退回字体修改

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2073
2025-04-27 14:36:57 +08:00
胡尧
72415d633c 退回字体修改 2025-04-27 14:36:13 +08:00
胡尧
5c67a8c190 Accept Merge Request #2072: (feature/6694 -> develop)
Merge Request: 修改验证规则

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2072?initial=true
2025-04-27 14:27:08 +08:00
胡尧
46ba682848 修改验证规则 2025-04-27 14:26:31 +08:00
胡尧
6b38062e87 解决冲突 2025-04-27 14:15:26 +08:00
胡尧
0945754736 Accept Merge Request #2071: (feature/6694 -> develop)
Merge Request: 解决pdf上数字乱码的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2071?initial=true
2025-04-27 14:13:45 +08:00
胡尧
644ff967e5 解决pdf上数字乱码的问题 2025-04-27 14:10:29 +08:00
liaodanlong
5f79d2038c 工艺外协采购单展示问题 2025-04-27 11:49:56 +08:00
liaodanlong
defd779279 返工 不重新编程 cnc加工工单没有数据问题 2025-04-27 11:35:29 +08:00
liaodanlong
e2e820267e 字符串拼接问题处理 2025-04-27 11:04:09 +08:00
liaodanlong
94f179a6d6 工艺外协代码回退 2025-04-27 10:46:48 +08:00
liaodanlong
bf9f4c1276 工艺外协代码回退 2025-04-27 10:20:45 +08:00
liaodanlong
51a633594f 工艺外协代码回退 2025-04-27 09:24:39 +08:00
liaodanlong
7d7c7b0fcf 工艺外协代码回退 2025-04-27 09:09:48 +08:00
liaodanlong
d88ac22b7c Merge branch 'refs/heads/develop' into release/release_2.12 2025-04-27 09:02:27 +08:00
廖丹龙
1f4e1c11c8 Accept Merge Request #2070: (feature/process_outsourcing_code_stripping -> develop)
Merge Request: Merge remote-tracking branch 'origin/develop' into develop

Created By: @廖丹龙
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2070
2025-04-27 09:00:49 +08:00
liaodanlong
9f1beb4013 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	sf_manufacturing/models/mrp_production.py
#	sf_manufacturing/models/purchase_order.py
#	sf_sale/models/sale_order.py
2025-04-27 08:50:45 +08:00
liaodanlong
f864466987 Merge branch 'refs/heads/feature/process_outsourcing_code_stripping' into develop 2025-04-27 08:49:13 +08:00
liaodanlong
9cf70cc54c 工艺外协代码回退 2025-04-26 14:42:38 +08:00
胡尧
82bd50cb97 修改文件传输规则 2025-04-25 17:30:51 +08:00
胡尧
4bce26721d Merge branch 'develop' into release/release_2.12 2025-04-25 16:41:08 +08:00
胡尧
3fb4e7c413 Accept Merge Request #2069: (feature/6694 -> develop)
Merge Request: 调整字体大小

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2069?initial=true
2025-04-25 16:40:24 +08:00
胡尧
a7ab8679f4 调整字体大小 2025-04-25 16:39:51 +08:00
胡尧
ca9a91e30a 修改程序单二维码下方文字 2025-04-25 16:07:34 +08:00
胡尧
314d738412 接口授权 2025-04-25 15:59:11 +08:00
胡尧
699e03ccda Accept Merge Request #2068: (release/release_2.12 -> develop)
Merge Request: 解决冲突

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2068?initial=true
2025-04-25 15:58:27 +08:00
管欢
8f0ade7b43 Accept Merge Request #2067: (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/2067
2025-04-25 15:55:38 +08:00
胡尧
50bc8786e8 解决冲突 2025-04-25 15:50:40 +08:00
胡尧
0777e63bc7 修复字符集出错的问题 2025-04-25 15:49:41 +08:00
guanhuan
128bebf338 紧急采购默认为是 2025-04-25 14:57:56 +08:00
廖丹龙
7a71077aa7 Accept Merge Request #2066: (feature/tool_standard_library_process -> develop)
Merge Request: 排程

Created By: @廖丹龙
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2066
2025-04-25 14:41:01 +08:00
liaodanlong
10a1d43a17 排程 2025-04-25 14:39:58 +08:00
胡尧
87d351e9e9 Accept Merge Request #2065: (feature/6694 -> develop)
Merge Request: 增加接口授权

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2065?initial=true
2025-04-25 14:09:36 +08:00
胡尧
d2daae1a8f 增加接口授权 2025-04-25 14:09:13 +08:00
胡尧
5997c24895 Accept Merge Request #2064: (feature/6694 -> develop)
Merge Request: 增加传输文件列表返回

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2064?initial=true
2025-04-25 14:05:10 +08:00
胡尧
df53989f22 增加传输文件列表返回 2025-04-25 14:04:48 +08:00
胡尧
9bab687080 Accept Merge Request #2063: (feature/6609 -> develop)
Merge Request: 修改机台二维码为机台编码

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2063?initial=true
2025-04-25 13:45:29 +08:00
胡尧
a5ac8b8b84 修改机台二维码为机台编码 2025-04-25 13:40:18 +08:00
胡尧
2cde398e11 Accept Merge Request #2062: (feature/6609 -> develop)
Merge Request: 修改机台二维码为机台编码

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2062?initial=true
2025-04-25 13:38:29 +08:00
胡尧
88026fea5d 修改机台二维码为机台编码 2025-04-25 13:37:12 +08:00
liaodanlong
443a21a0cc 工序外协需求 2025-04-25 11:46:05 +08:00
马广威
e14646a6fc Accept Merge Request #2061: (feature/制造功能优化 -> develop)
Merge Request: 调整dashboard取值

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2061?initial=true
2025-04-25 11:35:31 +08:00
mgw
6a920be6d1 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-04-25 11:34:51 +08:00
mgw
3811079a7f 调整alarm_start_time为时间戳 2025-04-25 11:34:34 +08:00
mgw
ad8e0b6af0 调整当日故障时长取值 2025-04-25 11:25:38 +08:00
廖丹龙
04cb910803 Accept Merge Request #2060: (feature/tool_standard_library_process -> develop)
Merge Request: 【sf.t-计划】在排程单详情页选择时间段进行排程时未校验日历有效工作时间

Created By: @廖丹龙
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2060
2025-04-25 11:18:43 +08:00
liaodanlong
42292818af Merge branch 'refs/heads/develop' into feature/tool_standard_library_process
# Conflicts:
#	sf_plan/models/custom_plan.py
2025-04-25 11:17:48 +08:00
liaodanlong
bcafd9cf38 【sf.t-计划】在排程单详情页选择时间段进行排程时未校验日历有效工作时间 2025-04-25 11:16:23 +08:00
mgw
12ebd87f1d Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-04-25 10:14:00 +08:00
mgw
bdef852b98 调整当日在线时长取值为今天之前最后一次开机时长 2025-04-25 10:13:42 +08:00
胡尧
1d5fb747d4 Accept Merge Request #2059: (feature/6609 -> develop)
Merge Request: 解决更新出现字段未找到的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2059?initial=true
2025-04-25 10:09:21 +08:00
胡尧
8116e4f97d 解决更新出现字段未找到的问题 2025-04-25 10:08:03 +08:00
mgw
e3e5fcc378 调整当日故障时长 2025-04-25 09:42:45 +08:00
胡尧
879b5492db Accept Merge Request #2058: (feature/6609 -> develop)
Merge Request: 产品详情增加只有成品类型才显示模型ID

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2058
2025-04-25 09:19:17 +08:00
胡尧
27b9a4f982 解决冲突 2025-04-25 09:19:00 +08:00
胡尧
94007bae2b 产品详情增加只有成品类型才显示模型ID 2025-04-25 09:16:40 +08:00
mgw
bf92028027 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-04-24 17:27:40 +08:00
mgw
2b47e566d3 调整周期字段为当日0时间 2025-04-24 17:27:20 +08:00
胡尧
5aa2f1aa18 Accept Merge Request #2057: (feature/6694 -> develop)
Merge Request: 解决排程报错的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2057?initial=true
2025-04-24 17:08:10 +08:00
胡尧
b7128ba81a 解决排程报错的问题 2025-04-24 17:07:30 +08:00
胡尧
49546f9d08 Accept Merge Request #2056: (feature/6694 -> develop)
Merge Request: 修改参数类型判断

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2056?initial=true
2025-04-24 15:13:40 +08:00
胡尧
6959bd9a09 修改参数类型判断 2025-04-24 15:13:21 +08:00
禹翔辉
3a2babf2d5 Accept Merge Request #2055: (feature/工单排序_1 -> develop)
Merge Request: Merge branch 'feature/工单排序' into feature/工单排序_1

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2055
2025-04-24 15:11:23 +08:00
yuxianghui
d7d094c84d Merge branch 'feature/工单排序' into feature/工单排序_1 2025-04-24 15:05:57 +08:00
yuxianghui
a06e24583d 添加工单先按工序排序,再按创建时间排序 2025-04-24 15:04:04 +08:00
胡尧
0cbd830901 Accept Merge Request #2053: (feature/6694 -> develop)
Merge Request: 修改模型id为模型ID,增加机台判断

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2053?initial=true
2025-04-24 14:59:12 +08:00
胡尧
4b29def105 修改模型id为模型ID,增加机台判断 2025-04-24 14:58:50 +08:00
胡尧
582abb3f2e Accept Merge Request #2052: (feature/6694 -> develop)
Merge Request: 去掉产品form重复的字段

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2052?initial=true
2025-04-24 14:38:07 +08:00
胡尧
40137ba69c 去掉产品form重复的字段 2025-04-24 14:31:22 +08:00
胡尧
804f6a82b4 Accept Merge Request #2051: (feature/6694 -> develop)
Merge Request: 销售订单,制造订单视图中加入模型id

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2051?initial=true
2025-04-24 14:15:58 +08:00
胡尧
d16d47dfbe 销售订单,制造订单视图中加入模型id 2025-04-24 14:13:10 +08:00
胡尧
41cf9d5474 Accept Merge Request #2050: (feature/6694 -> develop)
Merge Request: 修改扫码报错信息

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2050?initial=true
2025-04-24 11:42:52 +08:00
胡尧
59aa6b4f10 修改扫码报错信息 2025-04-24 11:42:16 +08:00
胡尧
a759106fdc Accept Merge Request #2049: (feature/6694 -> develop)
Merge Request: 调整打印代码结构

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2049?initial=true
2025-04-24 11:08:45 +08:00
胡尧
8bb101c6b2 调整打印代码结构 2025-04-24 11:08:02 +08:00
胡尧
f02044b513 Accept Merge Request #2048: (feature/6694 -> develop)
Merge Request: 调整机台二维码使用A4打印机

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2048?initial=true
2025-04-24 09:59:29 +08:00
胡尧
3d937b85c9 调整机台二维码使用A4打印机 2025-04-24 09:59:05 +08:00
胡尧
5a61b3b459 调整机台二维码使用A4打印机 2025-04-24 09:58:27 +08:00
胡尧
afccb5ee6a Accept Merge Request #2047: (feature/6694 -> develop)
Merge Request: 修改机台二维码为ID

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2047?initial=true
2025-04-24 09:19:13 +08:00
胡尧
2b0648d9bc 修改机台二维码为ID 2025-04-24 09:18:49 +08:00
胡尧
8ea3487044 Accept Merge Request #2046: (feature/6694 -> develop)
Merge Request: 增加import

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2046?initial=true
2025-04-24 08:59:38 +08:00
胡尧
b24ed5fe4c 增加import 2025-04-24 08:59:07 +08:00
胡尧
b801b265c3 Accept Merge Request #2045: (feature/6694 -> develop)
Merge Request: 增加日志界面

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2045?initial=true
2025-04-23 17:51:41 +08:00
胡尧
27a67167fe 增加日志界面 2025-04-23 17:51:09 +08:00
胡尧
8fa9534b4e 增加接口请求日志,修改报工接口获取机台id 2025-04-23 17:39:42 +08:00
胡尧
db745e46b6 Accept Merge Request #2044: (feature/6609 -> develop)
Merge Request: 解决从制造订单跳转到产品无法显示模型id的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2044?initial=true
2025-04-23 16:59:51 +08:00
胡尧
f598b6c71c 解决从制造订单跳转到产品无法显示模型id的问题 2025-04-23 16:58:59 +08:00
胡尧
dccb0b3fb0 Accept Merge Request #2043: (feature/6763 -> develop)
Merge Request: 修改机台二维码为机台ID

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2043?initial=true
2025-04-23 16:16:50 +08:00
胡尧
83feb78f43 修改机台二维码为机台ID 2025-04-23 16:16:25 +08:00
胡尧
ac09794b10 Accept Merge Request #2042: (feature/6763 -> develop)
Merge Request: 解决模块更新报错问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2042?initial=true
2025-04-23 16:00:13 +08:00
胡尧
3f88b11a18 解决模块更新报错问题 2025-04-23 15:59:37 +08:00
胡尧
b97acfb181 Accept Merge Request #2041: (feature/6684 -> develop)
Merge Request: 取消线上解决bug的改动

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2041?initial=true
2025-04-23 14:24:00 +08:00
胡尧
6767b693f5 取消线上解决bug的改动 2025-04-23 14:22:50 +08:00
胡尧
7b77d846c3 Accept Merge Request #2040: (hotfix/解决采购申请bug -> develop)
Merge Request: 解决创建采购申请报错

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2040?initial=true
2025-04-23 14:21:27 +08:00
廖丹龙
5e3bce1931 Accept Merge Request #2038: (feature/tool_standard_library_process -> develop)
Merge Request: 排程产能限制问题

Created By: @廖丹龙
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2038
2025-04-23 11:25:04 +08:00
liaodanlong
ef5c2649c9 【sf.t-计划】在排程单详情页选择时间段进行排程时未校验日历有效工作时间 2025-04-23 11:24:38 +08:00
禹翔辉
5400c9ec69 Accept Merge Request #2039: (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/2039
2025-04-23 11:22:47 +08:00
yuxianghui
7c7876e96f 工单添加默认按工序排序 2025-04-23 11:19:08 +08:00
liaodanlong
5438ff6df1 排程产能限制问题 2025-04-23 11:06:03 +08:00
胡尧
05b5e9cfd9 解决创建采购申请报错 2025-04-23 10:32:39 +08:00
胡尧
86dfd437cc 解决创建采购申请报错 2025-04-23 10:31:30 +08:00
胡尧
3a92e4cfd8 解决创建采购申请报错 2025-04-23 10:22:20 +08:00
胡尧
bff0ff9401 增加日志 2025-04-23 10:18:49 +08:00
管欢
9b3fe5c070 Accept Merge Request #2036: (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/2036
2025-04-23 09:48:26 +08:00
廖丹龙
92d593bf24 Accept Merge Request #2037: (feature/tool_standard_library_process -> develop)
Merge Request: 采购申请单查询条件同一

Created By: @廖丹龙
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2037
2025-04-23 09:38:27 +08:00
liaodanlong
dc6fb4ddda 采购申请单查询条件同一 2025-04-23 09:34:02 +08:00
廖丹龙
a84f8636a4 Accept Merge Request #2035: (feature/tool_standard_library_process -> develop)
Merge Request: 工艺确认报错问题处理

Created By: @廖丹龙
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2035
2025-04-23 09:12:59 +08:00
liaodanlong
7c26045377 工艺确认报错问题处理 2025-04-23 09:12:20 +08:00
胡尧
da579a15b3 Accept Merge Request #2034: (feature/6694 -> develop)
Merge Request: 修复bug

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2034?initial=true
2025-04-23 08:53:23 +08:00
胡尧
c9ee0af25a 修复bug 2025-04-23 08:52:49 +08:00
guanhuan
151bc5da4f 报价菜单修改 2025-04-22 15:43:14 +08:00
胡尧
1015af483c Accept Merge Request #2033: (feature/6694 -> develop)
Merge Request: 修改ftp传输代码

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2033?initial=true
2025-04-22 15:23:29 +08:00
胡尧
bb3971c93e 修改ftp传输代码 2025-04-22 15:23:02 +08:00
马广威
aa9efe932e Accept Merge Request #2032: (feature/制造功能优化 -> develop)
Merge Request: 处理浮点型增0问题

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2032
2025-04-22 15:13:28 +08:00
廖丹龙
8e09e9715c Accept Merge Request #2031: (feature/tool_standard_library_process -> develop)
Merge Request: 空值处理

Created By: @廖丹龙
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2031
2025-04-22 15:05:42 +08:00
mgw
30a55658de 处理浮点型增0问题 2025-04-22 15:02:38 +08:00
liaodanlong
7ecdc31db4 空值处理 2025-04-22 15:00:54 +08:00
廖丹龙
4234a6bbd9 Accept Merge Request #2030: (feature/tool_standard_library_process -> develop)
Merge Request: 设置字段必填

Created By: @廖丹龙
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2030
2025-04-22 14:28:29 +08:00
liaodanlong
8fcc436ce5 设置字段必填 2025-04-22 14:24:10 +08:00
liaodanlong
53a676bc93 查询条件修改 2025-04-22 14:22:31 +08:00
廖丹龙
c6f625fe44 Accept Merge Request #2027: (feature/tool_standard_library_process -> develop)
Merge Request: 工艺相关展示调整

Created By: @廖丹龙
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2027
2025-04-22 13:49:59 +08:00
liaodanlong
397c35867b 产品设置默认供应商数据 2025-04-22 13:41:22 +08:00
liaodanlong
f843610872 采购申请处理 2025-04-22 13:40:15 +08:00
liaodanlong
41e4d2656a 采购申请单 2025-04-22 13:39:57 +08:00
liaodanlong
4272d2855f 坯料自动勾选工艺路线 2025-04-22 13:38:50 +08:00
马广威
e63a12cbc1 Accept Merge Request #2029: (feature/制造功能优化 -> develop)
Merge Request: 调整上传文件名

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2029?initial=true
2025-04-22 13:17:03 +08:00
mgw
2867264e88 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-04-22 13:16:34 +08:00
mgw
7c2ddd8a0c 调整上传文件名 2025-04-22 13:16:15 +08:00
马广威
4daddcff61 Accept Merge Request #2028: (feature/制造功能优化 -> develop)
Merge Request: 调整适配零件图号为数字

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2028?initial=true
2025-04-22 11:51:29 +08:00
mgw
666c0167d7 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-04-22 11:50:33 +08:00
mgw
6b7f1aedbe 调整适配零件图号为数字 2025-04-22 11:50:15 +08:00
mgw
4234494f08 调整可用率 2025-04-22 10:55:21 +08:00
liaodanlong
cbc73ee8e3 工艺相关展示调整 2025-04-21 16:04:36 +08:00
黄焱
ec3d2a9239 Accept Merge Request #2026: (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/2026?initial=true
2025-04-21 16:04:32 +08:00
hyyy
a55da93dc4 修复样式问题 2025-04-21 16:01:04 +08:00
廖丹龙
73dce5e75e Accept Merge Request #2025: (feature/tool_standard_library_process -> develop)
Merge Request: 外协工单采购申请查询问题

Created By: @廖丹龙
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2025
2025-04-21 15:00:30 +08:00
liaodanlong
9b009ec5a3 外协工单采购申请查询问题 2025-04-21 14:55:52 +08:00
liaodanlong
7d70184a5d 外协工单采购申请查询问题 2025-04-21 14:55:20 +08:00
liaodanlong
9f9c08b1f0 制造订单采购申请查询问题 2025-04-21 14:35:57 +08:00
廖丹龙
5886b4e132 Accept Merge Request #2024: (feature/tool_standard_library_process -> develop)
Merge Request: 制造订单报错问题

Created By: @廖丹龙
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2024
2025-04-21 13:18:49 +08:00
liaodanlong
77478fd173 制造订单报错问题 2025-04-21 13:16:15 +08:00
胡尧
2154533ad5 Accept Merge Request #2023: (feature/6694 -> develop)
Merge Request: 去掉打印模块的type字段

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2023?initial=true
2025-04-21 13:15:22 +08:00
胡尧
eab7bf04c3 去掉打印模块的type字段 2025-04-21 13:15:02 +08:00
马广威
ba910f011e Accept Merge Request #2022: (feature/制造功能优化 -> develop)
Merge Request: sf-质量检查-上传报告校验必填,及上传时检查图号跟产品的零件图号是否一致

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2022?initial=true
2025-04-21 12:22:20 +08:00
mgw
c43d8ee280 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-04-21 12:20:56 +08:00
mgw
acc04f396d sf-质量检查-上传报告校验必填,及上传时检查图号跟产品的零件图号是否一致 2025-04-21 12:20:37 +08:00
胡尧
3425376705 Accept Merge Request #2021: (feature/6763 -> develop)
Merge Request: 工单列表增加模型ID搜索

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2021?initial=true
2025-04-21 11:26:29 +08:00
胡尧
7849ce6342 工单列表增加模型ID搜索 2025-04-21 11:25:37 +08:00
liaodanlong
69e8bf67d1 sf-制造-表面工艺可选参数-从cloud同步过来的数据,获取方式方式=外协 2025-04-21 10:04:59 +08:00
廖丹龙
722bdc251d Accept Merge Request #2020: (feature/tool_standard_library_process -> develop)
Merge Request: 开放分组类型限制工艺参数字段显示

Created By: @廖丹龙
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2020
2025-04-21 09:53:27 +08:00
liaodanlong
0403a2d223 开放分组类型限制工艺参数字段显示 2025-04-21 09:52:52 +08:00
liaodanlong
7a4c3f155c 开放分组类型限制工艺参数字段显示 2025-04-21 09:52:24 +08:00
胡尧
acd8d9c758 Accept Merge Request #2019: (feature/6694 -> develop)
Merge Request: 去掉多余的代码

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2019?initial=true
2025-04-21 09:23:48 +08:00
廖丹龙
d33a801f89 Accept Merge Request #2018: (feature/tool_standard_library_process -> develop)
Merge Request: 字段名写错

Created By: @廖丹龙
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2018
2025-04-21 09:20:29 +08:00
liaodanlong
a74c07f0bb 字段名写错 2025-04-21 09:19:36 +08:00
胡尧
9f1cac0789 去掉多余的代码 2025-04-21 09:10:20 +08:00
廖丹龙
fcd0319a9c Accept Merge Request #2017: (feature/tool_standard_library_process -> develop)
Merge Request: 工厂日历独立菜单到计划模块

Created By: @廖丹龙
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2017
2025-04-18 17:29:28 +08:00
liaodanlong
ed2c76adf1 工厂日历独立菜单到计划模块 2025-04-18 17:24:41 +08:00
廖丹龙
1d45486e20 Accept Merge Request #2016: (feature/tool_standard_library_process -> develop)
Merge Request: sf-制造-表面工艺-菜单名称修改

Created By: @廖丹龙
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2016
2025-04-18 16:46:38 +08:00
liaodanlong
d209d40356 sf-制造-表面工艺-菜单名称修改 2025-04-18 16:39:09 +08:00
胡尧
3355731632 Accept Merge Request #2015: (feature/6694 -> develop)
Merge Request: 增加打印模块,增加工单下发后打印程序单

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2015?initial=true
2025-04-18 16:07:01 +08:00
liaodanlong
ddd902cad8 修改创建表面工艺产品时表面工艺参数不能为空的问题 2025-04-18 16:06:08 +08:00
liaodanlong
e5566edd7c sf_同步cloud表面工艺可选参数创建产品,修改产品名命名规则 2025-04-18 15:45:32 +08:00
liaodanlong
ea0f9d6ee2 sf-制造-作业-表单详情内,可选工艺参数手动新增编码规则跟外协服务产品名称不正确 2025-04-18 15:45:00 +08:00
liaodanlong
79006e062a 1.限制工序-作业配置 产品只能选中一个
2.添加产品下拉框查询过滤
3.产品选择框可以跳转到产品页面
2025-04-18 15:44:13 +08:00
胡尧
ee9783d4a6 增加打印模块,增加工单下发后打印程序单 2025-04-18 14:50:03 +08:00
马广威
ff7c4a9ce7 Accept Merge Request #2014: (feature/制造功能优化 -> develop)
Merge Request: 调整设备可用率接口

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2014?initial=true
2025-04-18 14:40:31 +08:00
mgw
48b94e8d84 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-04-18 14:39:11 +08:00
mgw
a395994a1c 调整设备可用率接口 2025-04-18 14:38:39 +08:00
mgw
43c29e30b0 暂时注释稼动率接口 2025-04-18 14:18:41 +08:00
管欢
be639d7631 Accept Merge Request #2013: (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/2013
2025-04-18 14:08:29 +08:00
guanhuan
66c745af3c 排序优化 2025-04-18 13:22:25 +08:00
mgw
29ee275840 增加上传支持格式 2025-04-18 13:00:45 +08:00
管欢
f92b844c16 Accept Merge Request #2012: (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/2012
2025-04-18 09:50:58 +08:00
廖丹龙
63870ed7a9 Accept Merge Request #2011: (feature/tool_standard_library_process -> develop)
Merge Request: 产品表面工艺参数更名为工艺参数

Created By: @廖丹龙
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2011
2025-04-18 09:07:24 +08:00
liaodanlong
558c99f48b 产品表面工艺参数更名为工艺参数 2025-04-18 09:00:06 +08:00
廖丹龙
1264a4305e Accept Merge Request #2010: (feature/tool_standard_library_process -> develop)
Merge Request: 更新工艺外协流程的采购单查询逻辑

Created By: @廖丹龙
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2010
2025-04-17 17:13:54 +08:00
liaodanlong
a4f8183654 更新工艺外协流程的采购单查询逻辑 2025-04-17 17:02:43 +08:00
廖丹龙
3ea8e9d35f Accept Merge Request #2009: (feature/tool_standard_library_process -> develop)
Merge Request: 更新工艺外协流程的报错问题

Created By: @廖丹龙
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2009
2025-04-17 16:34:27 +08:00
liaodanlong
3fb2890bf1 更新工艺外协流程的报错问题 2025-04-17 16:32:18 +08:00
廖丹龙
0c3ba5c172 Accept Merge Request #2008: (feature/tool_standard_library_process -> develop)
Merge Request: 外协流程更改

Created By: @廖丹龙
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2008
2025-04-17 14:39:18 +08:00
liaodanlong
1b22d02700 Merge branch 'refs/heads/develop' into feature/tool_standard_library_process
# Conflicts:
#	sf_manufacturing/models/__init__.py
2025-04-17 14:35:24 +08:00
liaodanlong
962931bc9d 外协流程更改 2025-04-17 14:29:06 +08:00
guanhuan
9cfc8418ec 优化库存排序 2025-04-17 13:44:25 +08:00
胡尧
e4af2aacb8 Accept Merge Request #2007: (feature/6694 -> develop)
Merge Request: 增加工单下发时打印编程文件pdf的功能

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2007?initial=true
2025-04-17 13:18:03 +08:00
胡尧
c67944f689 增加工单下发时打印编程文件pdf的功能 2025-04-17 13:15:48 +08:00
胡尧
9c713c2a37 设备增加网络配置,增加编程文件传输接口 2025-04-16 17:21:36 +08:00
mgw
53d4be596f 调整日志 2025-04-16 11:26:21 +08:00
mgw
195302b67f 调整月合格率 2025-04-16 11:20:35 +08:00
mgw
6cb5e909d4 调整稼动率 2025-04-16 11:12:04 +08:00
胡尧
cb98f5cf25 Accept Merge Request #2006: (feature/6609 -> develop)
Merge Request: 产品主数据【条码】存储模型ID

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2006?initial=true
2025-04-16 09:30:28 +08:00
胡尧
f87a6690fe Accept Merge Request #2005: (feature/6684 -> develop)
Merge Request: 修改sf定时任务不在部署时更新配置

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2005?initial=true
2025-04-16 09:29:59 +08:00
胡尧
b041feb297 Merge branch 'release/release_2.11' 2025-04-15 20:54:53 +08:00
禹翔辉
06e9d5a538 Accept Merge Request #2004: (feature/功能刀具拆解单优化_1 -> develop)
Merge Request: Merge branch 'feature/功能刀具拆解单优化' into feature/功能刀具拆解单优化_1

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2004
2025-04-15 17:22:50 +08:00
yuxianghui
b9aac6c558 Merge branch 'feature/功能刀具拆解单优化' into feature/功能刀具拆解单优化_1 2025-04-15 17:21:31 +08:00
yuxianghui
2de0e9f02f 处理功能刀具预警没有自动创建预警记录和拆解单问题 2025-04-15 17:20:16 +08:00
胡尧
722b601890 Accept Merge Request #2003: (release/release_2.11 -> develop)
Merge Request: 修改质检单的质检员字段

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2003?initial=true
2025-04-15 17:14:51 +08:00
胡尧
20722c12b8 修改质检单的质检员字段 2025-04-15 17:08:43 +08:00
胡尧
2100ee9590 增加备注 2025-04-15 17:08:17 +08:00
禹翔辉
56f2ea0356 Accept Merge Request #2002: (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/2002
2025-04-15 16:58:28 +08:00
胡尧
6ff8395916 增加向pdf中添加二维码的功能 2025-04-15 16:57:32 +08:00
yuxianghui
bc85c457ad 功能刀具添加状态变更跟踪 2025-04-15 16:46:49 +08:00
胡尧
085e6359ce 编程单增加二维码 2025-04-15 15:47:35 +08:00
禹翔辉
7eeea92a3e Accept Merge Request #2001: (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/2001
2025-04-15 15:09:36 +08:00
yuxianghui
d672f3f4d7 功能刀具拆解单添加可以选择线边刀库的功能刀具进行拆解 2025-04-15 15:05:23 +08:00
黄焱
c79cf2e5ad Accept Merge Request #2000: (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/2000?initial=true
2025-04-15 11:26:42 +08:00
hyyy
33a5fc0ff4 删除多余字段 2025-04-15 11:24:45 +08:00
黄焱
2dcaa25952 Accept Merge Request #1999: (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/1999
2025-04-15 09:43:20 +08:00
hyyy
ba88070fad 修改刀片刀杆样式 2025-04-15 09:31:42 +08:00
马广威
3f940992be Accept Merge Request #1998: (feature/制造功能优化 -> develop)
Merge Request: 调整报告,增加稼动率接口(暂未调用)

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1998?initial=true
2025-04-14 17:24:04 +08:00
mgw
197ae6bc01 去掉基础数据 2025-04-14 17:22:27 +08:00
mgw
07336326ce 优化稼动率接口 2025-04-14 17:16:38 +08:00
mgw
c93553e78e 增加稼动率接口 2025-04-14 16:42:33 +08:00
liaodanlong
0db31f7936 Merge branch 'refs/heads/develop' into feature/tool_standard_library_process 2025-04-14 15:24:22 +08:00
liaodanlong
61a3cf606e 表面工艺数据内置与数据同步调整 2025-04-14 15:23:47 +08:00
mgw
91d79008e1 故障时长提取 2025-04-14 11:21:24 +08:00
禹翔辉
8cdf77f609 Accept Merge Request #1997: (feature/功能刀具组装初始化优化 -> develop)
Merge Request: 功能刀具组装自动根据BOM配置初始物料信息优化

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1997
2025-04-14 10:34:47 +08:00
yuxianghui
37edc858c2 1 2025-04-14 10:32:26 +08:00
yuxianghui
17fdf20e03 功能刀具组装自动根据BOM配置初始物料信息优化 2025-04-14 10:30:51 +08:00
胡尧
a896108638 产品主数据【条码】存储模型ID 2025-04-11 16:49:18 +08:00
胡尧
026697f363 修改sf定时任务不在部署时更新配置 2025-04-11 16:12:58 +08:00
胡尧
ec934abc42 Accept Merge Request #1996: (feature/6679 -> develop)
Merge Request: 给仓储岗增加修改工单的权限

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1996
2025-04-11 16:04:02 +08:00
胡尧
cc030957fb 给仓储岗增加修改工单的权限 2025-04-11 16:02:24 +08:00
mgw
87786dbd80 调整故障时长 2025-04-11 14:16:18 +08:00
禹翔辉
9d0ffd23b2 Accept Merge Request #1995: (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/1995
2025-04-10 14:27:04 +08:00
yuxianghui
3fb56f15c8 处理功能刀具组装时,有时扫描货位编码验证刀具信息扫不到货位的问题 2025-04-10 14:24:25 +08:00
mgw
bdf4696c08 调整待完成工单明细 2025-04-09 16:56:53 +08:00
mgw
6c926bf081 调整质检取值 2025-04-09 16:39:21 +08:00
mgw
ddb0c304b9 修改字段路径 2025-04-09 15:57:13 +08:00
mgw
cf8c14e738 调整查询范围 2025-04-09 15:22:41 +08:00
黄焱
6bd6816495 Accept Merge Request #1994: (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/1994?initial=true
2025-04-09 14:03:06 +08:00
hyyy
2bae98950e 功能刀具组装优化需求 2025-04-09 13:57:48 +08:00
胡尧
ec379a7541 Accept Merge Request #1993: (feature/6679 -> develop)
Merge Request: 增加不同工作中心配置相同接驳站的功能

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1993?initial=true
2025-04-08 17:08:20 +08:00
胡尧
119acf1543 Accept Merge Request #1992: (feature/6686 -> develop)
Merge Request: agv配置不可修改

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1992
2025-04-08 17:07:57 +08:00
禹翔辉
0f6f1aae24 Accept Merge Request #1991: (feature/功能刀具拆解优化_1 -> develop)
Merge Request: 将寿命到期拆解创建的组装单添加到对应安全库存组装单列表

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1991
2025-04-08 17:01:56 +08:00
yuxianghui
c40ecfb6ce 将寿命到期拆解创建的组装单添加到对应安全库存组装单列表 2025-04-08 17:00:00 +08:00
禹翔辉
a51a4c2fbb Accept Merge Request #1990: (feature/功能刀具拆解优化_1 -> develop)
Merge Request: 功能刀具拆解寿命到期刀具创建新的组装单时,添加几个参数的值。

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1990?initial=true
2025-04-08 15:59:04 +08:00
yuxianghui
315e2aa03d 功能刀具拆解寿命到期刀具创建新的组装单时,添加几个参数的值。 2025-04-08 15:56:08 +08:00
胡尧
10bea40159 增加不同工作中心配置相同接驳站的功能 2025-04-08 10:29:29 +08:00
马广威
78ba8d0ead Accept Merge Request #1989: (feature/制造功能优化 -> develop)
Merge Request: 调整报告页码

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1989
2025-04-08 10:11:41 +08:00
mgw
e61742cc5b Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化 2025-04-08 10:11:05 +08:00
mgw
d0d1a640d9 调整报告页码 2025-04-08 10:10:36 +08:00
禹翔辉
e613a2f283 Accept Merge Request #1987: (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/1987
2025-04-08 09:59:09 +08:00
马广威
88e83c0e14 Accept Merge Request #1988: (feature/制造功能优化 -> develop)
Merge Request: 调整报告产品取值

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1988
2025-04-08 09:48:48 +08:00
mgw
f912a81e7b 调整报告产品取值 2025-04-08 09:48:05 +08:00
yuxianghui
e09226e966 新增对寿命未到期且位置在线边刀库的功能刀具进行拆解时,不直接报错,而是进行二次确认是否进行拆除。 2025-04-08 09:44:46 +08:00
胡尧
8a7a90ff0d agv配置不可修改 2025-04-07 17:04:34 +08:00
胡尧
d05a3606c3 Merge branch 'release/release_2.10' 2025-04-02 11:50:07 +08:00
胡尧
1c34f2f15c Merge branch 'release/release_2.10' 2025-04-02 09:08:47 +08:00
胡尧
fd5ff0904e Merge branch 'release/release_2.10' 2025-04-01 20:43:29 +08:00
胡尧
3d355ec303 Merge branch 'release/release_2.9' 2025-03-04 09:16:48 +08:00
胡尧
d3a1e9b341 Merge branch 'release/release_2.8' 2025-01-21 10:43:09 +08:00
胡尧
8db80e1ed6 Merge branch 'release/release_2.7' 2025-01-07 20:16:29 +08:00
胡尧
fc378df597 Merge branch 'release/release_2.6' 2024-12-08 19:56:05 +08:00
胡尧
0575d89227 Merge branch 'release/release_2.5' 2024-11-04 19:49:05 +08:00
胡尧
39afc99b8f Merge branch 'release/release_2.4' 2024-10-11 09:29:34 +08:00
杨金灵
f230ad55fb Accept Merge Request #1284: (hotfix/修复报废-外协入库验证 -> master)
Merge Request: 修复报废-外协入库验证

Created By: @杨金灵
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1284
2024-09-06 13:47:55 +08:00
159 changed files with 6534 additions and 2387 deletions

View File

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

View File

@@ -157,11 +157,11 @@ td.o_required_modifier {
color: #aaa;
}
.o_kanban_primary_left {
display: flex;
flex-direction: row-reverse;
justify-content: flex-start;
}
// .o_kanban_primary_left {
// display: flex;
// flex-direction: row-reverse;
// justify-content: flex-start;
// }
.o_list_view .o_list_table thead {
position: sticky;

View File

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

View File

@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
{
'name': '机企猫 打印模块',
'version': '1.0',
'summary': """ 包含机台二维码,程序单打印等 """,
'author': '机企猫',
'website': 'https://www.jikimo.com',
'category': '机企猫',
'depends': ['sf_manufacturing', 'sf_maintenance', 'base_report_to_printer'],
'data': [
'views/maintenance_views.xml',
],
'application': True,
'installable': True,
'auto_install': False,
'license': 'LGPL-3',
}

View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
from . import jikimo_printing
from . import maintenance_printing
from . import workorder_printing

View File

@@ -0,0 +1,87 @@
from io import BytesIO
import qrcode
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from PIL import Image
import logging
from reportlab.lib.utils import ImageReader
from odoo import models, fields, api
import base64
_logger = logging.getLogger(__name__)
class JikimoPrinting(models.AbstractModel):
_name = 'jikimo.printing'
def print_qr_code(self, data):
"""
打印二维码
"""
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.add_data(data)
qr.make(fit=True)
qr_image = qr.make_image(fill_color="black", back_color="white")
# 将PIL Image转换为reportlab可用的格式
temp_image = BytesIO()
qr_image.save(temp_image, format="PNG")
temp_image.seek(0)
# 创建PDF
pdf_buffer = BytesIO()
c = canvas.Canvas(pdf_buffer, pagesize=A4)
# 计算位置
a4_width, a4_height = A4
qr_width = 200
qr_height = 200
x = (a4_width - qr_width) / 2
y = (a4_height - qr_height) / 2
# 直接从BytesIO绘制图片
c.drawImage(ImageReader(Image.open(temp_image)), x, y, width=qr_width, height=qr_height)
c.save()
# 获取PDF内容并打印
pdf_content = pdf_buffer.getvalue()
# _logger.info(f"打印内容: {pdf_content}")
printer.print_document(report=None, content=pdf_content, doc_format='pdf')
# 清理资源
pdf_buffer.close()
temp_image.close()
return True
def print_pdf(self, pdf_data):
"""
打印PDF
"""
printer = self.env['printing.printer'].get_default()
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

@@ -0,0 +1,69 @@
from odoo import models, fields, api
class MaintenancePrinting(models.Model):
_inherit = 'maintenance.equipment'
def print_single_method(self):
print('self.name========== %s' % self.name)
self.ensure_one()
# maintenance_equipment_id = self.id
# # host = "192.168.50.110" # 可以根据实际情况修改
# # port = 9100 # 可以根据实际情况修改
# # 获取默认打印机配置
# printer_config = self.env['printer.configuration'].sudo().search([('model', '=', self._name)], limit=1)
# if not printer_config:
# raise UserError('请先配置打印机')
# host = printer_config.printer_id.ip_address
# port = printer_config.printer_id.port
# self.print_qr_code(maintenance_equipment_id, host, port)
# 切换成A4打印机
try:
self.env['jikimo.printing'].print_qr_code(self.MTcode)
except Exception as e:
raise UserError(f"打印失败: {str(e)}")
# def generate_zpl_code(self, code):
# """生成ZPL代码用于打印二维码标签
# Args:
# code: 需要编码的内容
# Returns:
# str: ZPL指令字符串
# """
# zpl_code = "^XA\n" # 开始ZPL格式
# # 设置打印参数
# zpl_code += "^LH0,0\n" # 设置标签起始位置
# zpl_code += "^CI28\n" # 设置中文编码
# zpl_code += "^PW400\n" # 设置打印宽度为400点
# zpl_code += "^LL300\n" # 设置标签长度为300点
# # 打印标题
# zpl_code += "^FO10,20\n" # 设置标题位置
# zpl_code += "^A0N,30,30\n" # 设置字体大小
# zpl_code += "^FD机床二维码^FS\n" # 打印标题文本
# # 打印二维码
# zpl_code += "^FO50,60\n" # 设置二维码位置
# zpl_code += f"^BQN,2,8\n" # 设置二维码参数:模式2,放大倍数8
# zpl_code += f"^FDLA,{code}^FS\n" # 二维码内容
# # 打印编码文本
# zpl_code += "^FO50,220\n" # 设置编码文本位置
# zpl_code += "^A0N,25,25\n" # 设置字体大小
# zpl_code += f"^FD编码: {code}^FS\n" # 打印编码文本
# # 打印日期
# zpl_code += "^FO50,260\n"
# zpl_code += "^A0N,20,20\n"
# zpl_code += f"^FD打印日期: {fields.Date.today()}^FS\n"
# zpl_code += "^PQ1\n" # 打印1份
# zpl_code += "^XZ\n" # 结束ZPL格式
# return zpl_code

View File

@@ -0,0 +1,31 @@
import logging
from odoo import models, fields, api
_logger = logging.getLogger(__name__)
class MrpWorkorder(models.Model):
_name = 'mrp.workorder'
_inherit = ['mrp.workorder']
def _compute_state(self):
super(MrpWorkorder, self)._compute_state()
for workorder in self:
work_ids = workorder.production_id.workorder_ids.filtered(lambda w: w.routing_type == '装夹预调' or w.routing_type == '人工线下加工')
for wo in work_ids:
if wo.state == 'ready' and not wo.production_id.product_id.is_print_program:
# 触发打印程序
pdf_data = workorder.processing_drawing
if pdf_data:
try:
# 执行打印
self.env['jikimo.printing'].print_pdf(pdf_data)
wo.production_id.product_id.is_print_program = True
except Exception as e:
_logger.error(f"工单 {wo.name} 的PDF打印失败: {str(e)}")
class ProductTemplate(models.Model):
_inherit = 'product.template'
is_print_program = fields.Boolean(string='是否打印程序', default=False)

View File

@@ -0,0 +1,19 @@
<?xml version="1.0"?>
<odoo>
<record id="sf_maintenance_equipment_view_form_qrcode_print" model="ir.ui.view">
<field name="name">sf_equipment.form</field>
<field name="model">maintenance.equipment</field>
<field name="inherit_id" ref="sf_maintenance.sf_hr_equipment_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='qr_code_image']" position="after">
<label for="print_single_method"/>
<div class="col-12 col-lg-6 o_setting_box" style="white-space: nowrap">
<button type="object" class="oe_highlight" name='print_single_method' string="打印机床二维码"
attrs="{'invisible': [('equipment_type', '!=', '机床')]}"/>
</div>
</xpath>
</field>
</record>
</odoo>

View File

@@ -8,16 +8,21 @@
'category': 'purchase',
'depends': ['sf_manufacturing', 'purchase_request'],
'data': [
'security/ir.model.access.csv',
'views/sale_order_view.xml',
'views/mrp_production.xml',
'views/purchase_request_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': {
# 'web.assets_backend': [
# 'jikimo_purchase_request/static/src/**/*'
# ],
# },
'assets': {
'web.assets_backend': [
'jikimo_purchase_request/static/src/**/*'
],
},
'application': True,
'installable': True,
'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_search
msgid "Done"
msgstr "完成"
msgstr "关闭"
#. module: purchase_request
#: model:ir.model.fields,field_description:purchase_request.field_purchase_request_line__move_dest_ids
@@ -1043,7 +1043,7 @@ msgstr "询价单"
#. module: purchase_request
#: model:ir.model.fields,field_description:purchase_request.field_purchase_request_line__purchased_qty
msgid "RFQ/PO Qty"
msgstr ""
msgstr "已订购数"
#. module: purchase_request
#. odoo-python

View File

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

View File

@@ -9,18 +9,37 @@ class MrpProduction(models.Model):
@api.depends('state')
def _compute_pr_mp_count(self):
for item in self:
pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name)])
if pr_ids:
item.pr_mp_count = len(pr_ids)
else:
item.pr_mp_count = 0
# 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')])
def action_view_pr_mp(self):
"""
采购请求
"""
self.ensure_one()
pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name)])
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name),('is_subcontract', '!=', True)])
# 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 = {
'res_model': 'purchase.request',
'type': 'ir.actions.act_window',

View File

@@ -1,4 +1,5 @@
from odoo import api, fields, models, _
from odoo.tools import float_compare
class PurchaseOrder(models.Model):
@@ -13,4 +14,43 @@ class PurchaseOrder(models.Model):
('done', '完成'),
('cancel', '取消'),
('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 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):
_inherit = 'purchase.request'
_description = '采购申请'
# 为state添加取消状态
# 为state添加取消状态
state = fields.Selection(
selection_add=[('cancel', '已取消')],
ondelete={'cancel': 'set default'} # 添加 ondelete 策略
@@ -29,6 +31,56 @@ class PurchaseRequest(models.Model):
action['context'] = origin_context
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):
_inherit = 'purchase.request.line'
_description = '采购申请明细'
@@ -47,6 +99,20 @@ class PurchaseRequestLine(models.Model):
('outsourcing', "委外加工"),
], 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')
def _compute_supply_method(self):
for prl in self:
@@ -79,7 +145,7 @@ class PurchaseRequestLine(models.Model):
continue
if record.product_id.categ_id.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:
product_name = match.group(0)
@@ -98,3 +164,36 @@ class PurchaseRequestLine(models.Model):
else:
record.part_number = record.product_id.part_number
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 collections import defaultdict
class StockRule(models.Model):
@@ -44,13 +45,38 @@ class StockRule(models.Model):
purchase_request_line_model.create(request_line_data)
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})
# 如果补货组相同,并且产品相同,则合并
procurements_dict = defaultdict()
for procurement, rule in procurements:
if (procurement.product_id.id, procurement.values['group_id'], rule.id) not in procurements_dict:
procurements_dict[(procurement.product_id.id, procurement.values['group_id'], rule.id)] = {
'product_id': procurement.product_id,
'product_qty': procurement.product_qty,
'product_uom': procurement.product_uom,
'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

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_name"/>
</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>
</record>
@@ -63,4 +83,9 @@
</xpath>
</field>
</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>

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)
from . import purchase_request_line_make_purchase_order
from . import purchase_request_wizard

View File

@@ -32,6 +32,7 @@ class PurchaseRequestLineMakePurchaseOrder(models.TransientModel):
line.company_id,
line.request_id.origin,
)
# po_data.update({'related_product':line.related_product.id})
purchase = purchase_obj.create(po_data)
# Look for any other PO line in the selected PO with same
@@ -63,6 +64,8 @@ class PurchaseRequestLineMakePurchaseOrder(models.TransientModel):
po_line_data = self._prepare_purchase_order_line(purchase, item)
if item.keep_description:
po_line_data["name"] = item.name
if line.related_product:
po_line_data.update({'related_product': line.related_product.id})
po_line = po_line_obj.create(po_line_data)
po_line_product_uom_qty = po_line.product_uom._compute_quantity(
po_line.product_uom_qty, alloc_uom
@@ -100,9 +103,27 @@ class PurchaseRequestLineMakePurchaseOrder(models.TransientModel):
def check_group(self, request_lines):
# 去掉合并必须同一采购组的限制
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):
_inherit = "purchase.request.line.make.purchase.order.item"
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 -*-
{
'name': "机企猫 采购审批流程",
'name': "机企猫 采购申请审批流程",
'summary': """
Short (1 phrase/line) summary of the module's purpose, used as
subtitle on modules listing or apps.openerp.com""",
采购申请审批流程""",
'description': """
Long description of module's purpose

View File

@@ -1,3 +1,4 @@
# -*- 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'
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 -*-
{
'name': "机企猫 采购申请审批流程",
'name': "机企猫 采购审批流程",
'summary': """
采购申请审批流程""",
采购审批流程""",
'description': """
采购申请审批流程""",
采购审批流程""",
'author': "My Company",
'website': "https://www.yourcompany.com",

View File

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

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
{
'name': '机企猫 报工系统API',
'version': '1.0.0',
'summary': """ 机企猫 报工系统API """,
'author': '机企猫',
'website': 'https://xt.sf.jikimo.com',
'category': 'sf',
'depends': ['base', 'sf_maintenance', 'jikimo_mini_program'],
'data': [
],
'application': True,
'installable': True,
'auto_install': False,
'license': 'LGPL-3',
}

View File

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

View File

@@ -0,0 +1,69 @@
import json
from odoo import http
from odoo.http import request
from odoo.addons.sf_machine_connect.models.ftp_operate import transfer_files
from odoo.addons.sf_base.decorators.api_log import api_log
class MainController(http.Controller):
@http.route('/api/manual_download_program', type='json', methods=['POST'], auth='wechat_token', cors='*')
@api_log('人工线下加工编程文件传输', requester='报工系统')
def manual_download_program(self):
"""
人工线下加工传输编程文件
"""
data = json.loads(request.httprequest.data)
maintenance_equipment_id = data.get('maintenance_equipment_id')
model_id = data.get('model_id')
if not maintenance_equipment_id or not model_id:
return {'code': 400, 'message': '参数错误'}
try:
model_id = int(model_id)
except Exception as e:
return {'code': 400, 'message': '参数类型错误'}
maintenance_equipment = request.env['maintenance.equipment'].sudo().search(
[('MTcode', '=', maintenance_equipment_id), ('category_id.equipment_type', '=', '机床')],
limit=1
)
if not maintenance_equipment:
return {'code': 400, 'message': '机台不存在,请扫描正确的机台二维码'}
product = request.env['product.template'].sudo().search([('model_id', '=', model_id)], limit=1)
if not product:
return {'code': 400, 'message': '请扫描正确的图纸'}
# 获取刀具组
tool_groups_id = request.env['sf.tool.groups'].sudo().search([('equipment_ids', 'in', maintenance_equipment.id)], limit=1)
if not tool_groups_id:
return {'code': 400, 'message': '刀具组不存在'}
ftp_resconfig = request.env['res.config.settings'].sudo().get_values()
if not ftp_resconfig['ftp_host'] or not ftp_resconfig['ftp_port'] or not ftp_resconfig['ftp_user'] or not ftp_resconfig['ftp_password']:
return {'code': 400, 'message': '编程文件FTP配置错误'}
source_ftp_info = {
'host': ftp_resconfig['ftp_host'],
'port': int(ftp_resconfig['ftp_port']),
'username': ftp_resconfig['ftp_user'],
'password': ftp_resconfig['ftp_password']
}
if not maintenance_equipment.ftp_host or not maintenance_equipment.ftp_port or not maintenance_equipment.ftp_username or not maintenance_equipment.ftp_password:
return {'code': 400, 'message': '机台FTP配置错误'}
target_ftp_info = {
'host': maintenance_equipment.ftp_host,
'port': int(maintenance_equipment.ftp_port),
'username': maintenance_equipment.ftp_username,
'password': maintenance_equipment.ftp_password
}
# 传输nc文件
try:
result = transfer_files(
source_ftp_info,
target_ftp_info,
'/' + str(model_id),
'/',
match_str=r'^\d*-' + tool_groups_id.name + r'-\w{2}-all\.nc$'
)
if len(result) > 0:
return {'code': 200, 'message': '传输成功', 'file_list': result}
else:
return {'code': 404, 'message': '未找到编程文件'}
except Exception as e:
return {'code': 500, 'message': str(e)}

View File

@@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View File

@@ -4,6 +4,7 @@ import json
import logging
from odoo.addons.sf_mrs_connect.controllers.controllers import Sf_Mrs_Connect
from odoo.addons.sf_manufacturing.controllers.controllers import Manufacturing_Connect
from odoo.addons.sf_base.decorators.api_log import api_log
from datetime import datetime
_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,
cors="*")
@api_log('工单对接错误', requester='中控系统')
def workder_exception(self, **kw):
"""
记录工单异常

View File

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

View File

@@ -133,6 +133,7 @@ class QualityCheck(models.Model):
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)
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)
@@ -141,7 +142,7 @@ class QualityCheck(models.Model):
# # 出厂检验报告编号
# 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)
@@ -153,9 +154,9 @@ class QualityCheck(models.Model):
for move in record.picking_id.move_ids_without_package:
if move.product_id == record.product_id:
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:
record.total_qty = ''
record.total_qty = 0
# 检验数
check_qty = fields.Integer('检验数', default=lambda self: self._get_default_check_qty())
@@ -206,7 +207,7 @@ class QualityCheck(models.Model):
('NG', 'NG')
], string='出厂检验报告结果', default='OK')
measure_operator = fields.Many2one('res.users', string='操机员')
quality_manager = fields.Many2one('res.users', string='质检员', compute='_compute_quality_manager', store=True)
quality_manager = fields.Many2one('res.users', string='质检员', compute='_compute_quality_manager')
@api.depends('measure_line_ids')
def _compute_quality_manager(self):
@@ -258,6 +259,23 @@ class QualityCheck(models.Model):
line[field_name] = False
self.column_nums = self.column_nums - 1
def upload_measure_line(self):
"""
上传测量值
"""
for record in self:
if not record.part_name or not record.part_number:
raise UserError(_('零件名称和零件图号均不能为空'))
# 如果验证通过,返回原动作
action = self.env.ref('quality_control.import_complex_model_wizard').read()[0]
action['context'] = {
'default_model_name': 'quality.check.measure.line',
'default_check_id': self.id,
}
return action
def do_preview(self):
"""
预览出厂检验报告
@@ -321,7 +339,7 @@ class QualityCheck(models.Model):
# 4. 获取报告动作并生成PDF此时二维码将包含正确的文档ID
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,
res_ids=self.ids
)
@@ -718,8 +736,9 @@ class QualityCheck(models.Model):
def _compute_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.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:
qc.qty_to_test = qc.qty_line

View File

@@ -267,7 +267,7 @@
<field name="company_id" invisible="1"/>
<field name="categ_type" invisible="1"/>
<field name="product_id" attrs="{'invisible' : [('measure_on', '=', 'operation')]}"/>
<field name="part_name" attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
<field name="part_name" attrs="{'invisible': [('categ_type', '!=', '成品')], 'readonly': [('publish_status', '=', 'published')]}"/>
<field name="part_number" attrs="{'invisible': [('categ_type', '!=', '成品')], 'readonly': [('publish_status', '=', 'published')]}"/>
<field name="material_name" attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
<field name="total_qty" attrs="{'invisible': ['|', ('measure_on', '!=', 'product'), ('is_out_check', '=', False)]}"/>
@@ -334,11 +334,15 @@
<div class="o_row">
<button name="add_measure_line" type="object" class="btn-primary" string="添加测量值" attrs="{'invisible': [('publish_status', '=', 'published')]}"/>
<button name="remove_measure_line" type="object" class="btn-primary" string="删除测量值" attrs="{'invisible': [('publish_status', '=', 'published')]}"/>
<button name="%(quality_control.import_complex_model_wizard)d" string="上传"
type="action"
<!-- <button name="%(quality_control.import_complex_model_wizard)d" string="上传111" -->
<!-- type="action" -->
<!-- class="btn-primary" -->
<!-- attrs="{'force_show':1, 'invisible': [('publish_status', '=', 'published')]}" -->
<!-- context="{'default_model_name': 'quality.check.measure.line', 'default_check_id': id}"/> -->
<button name="upload_measure_line" string="上传"
type="object"
class="btn-primary"
attrs="{'force_show':1, 'invisible': [('publish_status', '=', 'published')]}"
context="{'default_model_name': 'quality.check.measure.line', 'default_check_id': id}"/>
attrs="{'force_show':1, 'invisible': [('publish_status', '=', 'published')]}"/>
</div>
<br/>
<div class="o_row">
@@ -489,6 +493,9 @@
<field name="picking_id"/>
<field name="lot_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="Passed" name="passed" domain="[('quality_state', '=', 'pass')]"/>
<filter string="Failed" name="failed" domain="[('quality_state', '=', 'fail')]"/>

View File

@@ -22,6 +22,7 @@ _logger = logging.getLogger(__name__)
class ImportComplexModelWizard(models.TransientModel):
_name = 'quality.check.import.complex.model.wizard'
file_data = fields.Binary("数据文件")
filename = fields.Char(string='文件名')
model_name = fields.Char(string='Model Name')
field_basis = fields.Char(string='Field Basis')
check_id = fields.Many2one(string='质检单', comodel_name='quality.check')
@@ -93,11 +94,16 @@ class ImportComplexModelWizard(models.TransientModel):
return repeats
def convert_float(self, value):
if isinstance(value, float) and value.is_integer():
return int(value)
return value
def import_data(self):
"""导入Excel数据"""
if not self.file_data:
raise UserError(_('请先上传Excel文件'))
if self.check_id.measure_line_ids:
self.sudo().check_id.measure_line_ids.unlink()
@@ -161,6 +167,20 @@ class ImportComplexModelWizard(models.TransientModel):
# 从第二行开始读取数据(跳过表头)
max_columns = 1
for row_index in range(1, sheet.nrows):
row = sheet.row_values(row_index)
# 检查行是否有数据
if not any(row):
continue
if row[2] == '':
continue
logging.info('================%s, %s==' % (row[1], type(row[1])))
compare_value = self.convert_float(row[1])
if str(compare_value) != quality_check.part_number:
print(sheet.row_values(row_index))
raise UserError(_('上传内容图号错误,请修改'))
for row_index in range(1, sheet.nrows):
row = sheet.row_values(row_index)
@@ -177,14 +197,14 @@ class ImportComplexModelWizard(models.TransientModel):
'sequence': len(quality_check.measure_line_ids) + 1,
'product_name': str(row[0]) if row[0] else '', # 产品名称列
'drawing_no': str(row[1]) if row[1] else '', # 图号列
'measure_item': row[2] or '', # 检测项目列
'measure_value1': str(row[4]) if row[4] else '', # 测量值1
'measure_value2': str(row[5]) if row[5] else '', # 测量值2
'measure_value3': str(row[6]) if len(row) > 6 and row[6] else '', # 测量值3
'measure_value4': str(row[7]) if len(row) > 7 and row[7] else '', # 测量值4
'measure_value5': str(row[8]) if len(row) > 8 and row[8] else '', # 测量值5
'measure_item': str(self.convert_float(row[2])) or '', # 检测项目列
'measure_value1': str(self.convert_float(row[4])) if row[4] else '', # 测量值1
'measure_value2': str(self.convert_float(row[5])) if row[5] else '', # 测量值2
'measure_value3': str(self.convert_float(row[6])) if len(row) > 6 and row[6] else '', # 测量值3
'measure_value4': str(self.convert_float(row[7])) if len(row) > 7 and row[7] else '', # 测量值4
'measure_value5': str(self.convert_float(row[8])) if len(row) > 8 and row[8] else '', # 测量值5
'measure_result': 'NG' if row[9] == 'NG' else 'OK', # 判定列
'remark': row[10] if len(row) > 10 and row[10] else '', # 备注列
'remark': self.convert_float(row[10]) if len(row) > 10 and row[10] else '', # 备注列
}
for i in range(1, 6):
@@ -194,7 +214,7 @@ class ImportComplexModelWizard(models.TransientModel):
self.env['quality.check.measure.line'].create(measure_line_vals)
valid_data_imported = True
quality_check.column_nums = max_columns
# 检查是否有有效数据被导入
@@ -424,7 +444,8 @@ class ImportComplexModelWizard(models.TransientModel):
# )
def download_excel_template(self):
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') + '/quality_control/static/src/binary/出厂检验报告上传模版.xlsx'
base_url = self.env['ir.config_parameter'].sudo().get_param(
'web.base.url') + '/quality_control/static/src/binary/出厂检验报告上传模版.xlsx'
# 只有当原始 URL 使用 http 时才替换为 https
if base_url.startswith("http://"):

View File

@@ -6,7 +6,8 @@
<field name="arch" type="xml">
<form>
<group>
<field name="file_data" widget="binary" options="{'accepted_file_extensions': '.xlsx'}"/>
<field name="file_data" widget="binary" filename="filename" options="{'accepted_file_extensions': '.xls,.xlsx'}"/>
<field name="filename" invisible="1"/>
</group>
<footer>
<button string="确认导入" name="import_data" type="object" class="btn-primary"/>

View File

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

View File

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

View File

@@ -1,3 +1,4 @@
from . import models
from . import commons
from . import controllers
from . import decorators

View File

@@ -25,7 +25,7 @@
'views/menu_fixture_view.xml',
'views/change_base_view.xml',
'views/Printer.xml',
'views/api_log_views.xml',
],
'demo': [
],

View File

@@ -9,7 +9,6 @@ class Printer(models.Model):
ip_address = fields.Char(string='IP 地址', required=True)
port = fields.Integer(string='端口', default=9100)
class TableStyle(models.Model):
_name = 'table.style'
_description = '标签样式'

View File

@@ -2,7 +2,16 @@
import time, datetime
import hashlib
from odoo import models
from typing import Optional
import socket
import os
import logging
import qrcode
from reportlab.pdfgen import canvas
from reportlab.lib.units import inch
from PyPDF2 import PdfFileReader, PdfFileWriter
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
class Common(models.Model):
_name = 'sf.sync.common'
@@ -92,3 +101,145 @@ class PrintingUtils(models.AbstractModel):
# host = "192.168.50.110" # 可以作为参数传入,或者在此配置
# port = 9100 # 可以作为参数传入,或者在此配置
self.send_to_printer(host, port, zpl_code)
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文件中添加二维码
:param pdf_path: PDF文件路径
:param content: 二维码内容
:param qr_code_buttom_text: 二维码下方文字
:param buttom_text: 正文下方文字
:return: 是否成功
"""
if not os.path.exists(pdf_path):
logging.warning(f'PDF文件不存在: {pdf_path}')
return False
# 生成二维码
qr = qrcode.QRCode(version=1, box_size=10, border=5)
qr.add_data(str(content))
qr.make(fit=True)
qr_img = qr.make_image(fill_color="black", back_color="white")
# 保存二维码为临时文件
qr_temp_path = '/tmp/qr_temp.png'
qr_img.save(qr_temp_path)
# 创建一个临时PDF文件路径
output_temp_path = '/tmp/output_temp.pdf'
try:
# 使用reportlab创建一个新的PDF
# 注册中文字体
font_paths = [
"/usr/share/fonts/chinese/simsun.ttc", # Windows系统宋体
"c:/windows/fonts/simsun.ttc", # Windows系统宋体另一个位置
"/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf", # Linux Droid字体
"/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", # 文泉驿正黑
"/usr/share/fonts/chinese/TrueType/simsun.ttc", # 某些Linux发行版位置
]
font_found = False
for font_path in font_paths:
if os.path.exists(font_path):
try:
pdfmetrics.registerFont(TTFont('SimSun', font_path))
font_found = True
break
except:
continue
# 读取原始PDF
with open(pdf_path, "rb") as original_file:
existing_pdf = PdfFileReader(original_file)
output = PdfFileWriter()
# 处理最后一页
last_page = existing_pdf.getNumPages() - 1
page = existing_pdf.getPage(last_page)
# 获取页面尺寸
page_width = float(page.mediaBox.getWidth())
page_height = float(page.mediaBox.getHeight())
# 创建一个新的PDF页面用于放置二维码
c = canvas.Canvas(output_temp_path, pagesize=(page_width, page_height))
# 设置字体
if font_found:
c.setFont('SimSun', 10) # 增大字体大小到14pt
else:
# 如果没有找到中文字体,使用默认字体
c.setFont('Helvetica', 10)
logging.warning("未找到中文字体,将使用默认字体")
# 在右下角绘制二维码,预留边距
qr_size = 1.5 * inch # 二维码大小为2英寸
margin = 0.1 * inch # 边距为0.4英寸
qr_y = margin + 20 # 将二维码向上移动一点,为文字留出空间
c.drawImage(qr_temp_path, page_width - qr_size - margin, qr_y, width=qr_size, height=qr_size)
if qr_code_buttom_text:
# 在二维码下方绘制文字
text = qr_code_buttom_text
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_y = margin + 20 # 文字位置靠近底部
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()
# 读取带有二维码的临时PDF
with open(output_temp_path, "rb") as qr_file:
qr_pdf = PdfFileReader(qr_file)
qr_page = qr_pdf.getPage(0)
# 合并原始页面和二维码页面
page.mergePage(qr_page)
# 添加剩余的页面
for i in range(0, last_page):
output.addPage(existing_pdf.getPage(i))
output.addPage(page)
# 保存最终的PDF到一个临时文件
final_temp_path = pdf_path + '.tmp'
with open(final_temp_path, "wb") as output_file:
output.write(output_file)
# 替换原始文件
os.replace(final_temp_path, pdf_path)
return True
finally:
# 清理临时文件
if os.path.exists(qr_temp_path):
os.remove(qr_temp_path)
if os.path.exists(output_temp_path):
os.remove(output_temp_path)

View File

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

View File

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

View File

@@ -0,0 +1,65 @@
import functools
import json
import logging
from datetime import datetime
from odoo.http import request
_logger = logging.getLogger(__name__)
def api_log(name=None, requester=None):
"""记录API请求日志的装饰器"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = datetime.now()
# 获取请求信息
try:
# 获取请求数据
request_data = json.loads(request.httprequest.data) if request.httprequest.data else {}
# 获取请求路径
path = request.httprequest.path
# 获取请求方法
method = request.httprequest.method
# 获取客户端IP
remote_addr = request.httprequest.remote_addr
# 执行原始函数
result = func(*args, **kwargs)
origin_result = result
if isinstance(result, str):
result = json.loads(result)
# 计算响应时间
end_time = datetime.now()
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 = {
'name': name or func.__name__,
'path': path,
'method': method.upper(),
'request_data': json.dumps(request_data, ensure_ascii=False),
'response_data': json.dumps(result, ensure_ascii=False),
'remote_addr': remote_addr,
'response_time': response_time,
'status': 200 if status == 0 else status,
'requester': requester,
'responser': '智能工厂'
}
# 异步创建日志记录
request.env['api.request.log'].sudo().with_context(tracking_disable=True).create(log_vals)
return origin_result
except Exception as e:
_logger.error(f"API日志记录失败: {str(e)}")
# 即使日志记录失败,也要返回原始结果
return func(*args, **kwargs)
return wrapper
return decorator

View File

@@ -6,3 +6,4 @@ from . import functional_fixture
from . import tool_other_features
from . import basic_parameters_fixture
from . import ir_sequence
from . import api_log

72
sf_base/models/api_log.py Normal file
View File

@@ -0,0 +1,72 @@
from odoo import models, fields, api
import json, ast
import logging
import requests
_logger = logging.getLogger(__name__)
class ApiRequestLog(models.Model):
_name = 'api.request.log'
_description = '接口请求日志'
_order = 'id desc'
name = fields.Char('接口名称')
path = fields.Char('请求路径')
method = fields.Char('请求方法')
request_data = fields.Text('请求数据')
response_data = fields.Text('响应数据')
remote_addr = fields.Char('客户端IP')
response_time = fields.Float('响应时间(秒)', digits=(16, 6))
status = fields.Integer('状态码')
requester = 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

@@ -254,3 +254,6 @@ access_sf_machining_accuracy_admin,sf_machining_accuracy_admin,model_sf_machinin
access_sf_embryo_redundancy,sf_embryo_redundancy,model_sf_embryo_redundancy,base.group_user,1,0,0,0
access_sf_embryo_redundancy_admin,sf_embryo_redundancy_admin,model_sf_embryo_redundancy,base.group_system,1,0,0,0
access_api_request_log_user,api.request.log.user,model_api_request_log,base.group_user,1,0,0,0
access_api_request_log_admin,api.request.log.admin,model_api_request_log,base.group_system,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
254
255
256
257
258
259

View File

@@ -148,12 +148,17 @@ td.o_required_modifier {
color: #aaa;
}
.o_kanban_primary_left {
display: flex;
flex-direction: row-reverse;
justify-content: flex-start;
// .o_kanban_primary_left {
// display: flex;
// flex-direction: row-reverse;
// justify-content: flex-start;
// }
.o_list_button {
min-width: 32px;
}
.o_list_record_remove {
padding-left: 0px !important;
}
.diameter:before {
content:"Ф";
display:inline;

View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_api_request_log_tree" model="ir.ui.view">
<field name="name">api.request.log.tree</field>
<field name="model">api.request.log</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="path"/>
<field name="method"/>
<field name="remote_addr"/>
<field name="response_time" sum="0"/>
<field name="requester"/>
<field name="responser"/>
<field name="create_date" string="请求时间"/>
<field name="status" sum="0"/>
</tree>
</field>
</record>
<record id="view_api_request_log_form" model="ir.ui.view">
<field name="name">api.request.log.form</field>
<field name="model">api.request.log</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<group>
<field name="name"/>
<field name="path"/>
<field name="method"/>
<field name="remote_addr"/>
</group>
<group>
<field name="response_time"/>
<field name="status"/>
<field name="requester"/>
<field name="responser"/>
<field name="create_date" string="请求时间"/>
</group>
</group>
<notebook>
<page string="请求数据">
<field name="request_data"/>
</page>
<page string="响应数据">
<field name="response_data"/>
</page>
</notebook>
</sheet>
</form>
</field>
</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">
<field name="name">API请求日志</field>
<field name="res_model">api.request.log</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="menu_api_request_log"
name="API请求日志"
parent="base.next_id_9"
action="action_api_request_log"
sequence="100"/>
</odoo>

View File

@@ -5,7 +5,7 @@
<record model="ir.ui.view" id="mrs_production_process_parameter_tree">
<field name="model">sf.production.process.parameter</field>
<field name="arch" type="xml">
<tree string="表面工艺可选参数" create="0" delete="0">
<tree string="工艺可选参数" create="0" delete="0">
<field name="code"/>
<field name="name"/>
<field name="gain_way"/>
@@ -15,7 +15,7 @@
<record model="ir.ui.view" id="mrs_production_process_parameter_form">
<field name="model">sf.production.process.parameter</field>
<field name="arch" type="xml">
<form string="表面工艺可选参数" create="0" delete="0" >
<form string="工艺可选参数" create="0" delete="0" >
<sheet>
<div class="oe_title">
<h1>
@@ -104,7 +104,7 @@
<record model="ir.ui.view" id="sf_production_process_category_form">
<field name="model">sf.production.process.category</field>
<field name="arch" type="xml">
<form string="表面工艺类别" create="0" edit="0" delete="1">
<form string="工艺类别" create="0" edit="0" delete="1">
<sheet>
<div class="oe_title">
<h1>
@@ -120,7 +120,7 @@
</group>
</group>
<notebook>
<page string="表面工艺">
<page string="工艺">
<field name='production_process_ids' widget="ony2many">
<tree editable="bottom">
<field name="code" string="编码号" readonly="1" force_save="1"/>
@@ -139,7 +139,7 @@
<record model="ir.ui.view" id="sf_production_process_category_tree">
<field name="model">sf.production.process.category</field>
<field name="arch" type="xml">
<tree string="表面工艺类别" default_order="sequence, id" create="0" edit="0" delete="1">
<tree string="工艺类别" default_order="sequence, id" create="0" edit="0" delete="1">
<field name="sequence" widget="handle" string="序号" readonly="1"/>
<field name="code"/>
<field name="name" string="名称"/>
@@ -163,7 +163,7 @@
<record model="ir.ui.view" id="sf_production_process_tree">
<field name="model">sf.production.process</field>
<field name="arch" type="xml">
<tree string="表面工艺" create="0" edit="0" delete="0">
<tree string="工艺" create="0" edit="0" delete="0">
<field name="sequence" string="加工顺序" readonly="1"/>
<field name="code"/>
<field name="name" string="名称"/>
@@ -175,7 +175,7 @@
<record model="ir.ui.view" id="sf_production_process_form">
<field name="model">sf.production.process</field>
<field name="arch" type="xml">
<form string="表面工艺" create="0" delete="0">
<form string="工艺" create="0" delete="0">
<sheet>
<div class="oe_title">
<h1>
@@ -395,7 +395,7 @@
<field name="view_mode">tree,form</field>
</record>
<record id="sf_production_process" model="ir.actions.act_window">
<field name="name">表面工艺</field>
<field name="name">工艺</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sf.production.process</field>
<field name="view_mode">tree,form</field>
@@ -414,13 +414,13 @@
<!-- </record>-->
<record id="sf_production_process_category" model="ir.actions.act_window">
<field name="name">表面工艺类别</field>
<field name="name">工艺类别</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sf.production.process.category</field>
<field name="view_mode">tree,form</field>
</record>
<record id="mrs_production_process_parameter_action" model="ir.actions.act_window">
<field name="name">表面工艺可选参数</field>
<field name="name">工艺可选参数</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sf.production.process.parameter</field>
<field name="view_mode">tree,form</field>

View File

@@ -78,7 +78,7 @@
<menuitem
id="menu_sf_production_process"
name="表面工艺"
name="工艺"
parent="menu_sf_production_process_1"
sequence="2"
action="sf_production_process"
@@ -86,7 +86,7 @@
<menuitem
id="menu_sf_production_process_category"
name="表面工艺类别"
name="工艺类别"
parent="menu_sf_production_process_1"
sequence="1"
action="sf_production_process_category"
@@ -113,7 +113,7 @@
<menuitem
id="mrs_production_process_parameter_view"
name="表面工艺可选参数"
name="工艺可选参数"
parent="menu_sf_production_process_1"
sequence="2"
action="mrs_production_process_parameter_action"

View File

@@ -14,10 +14,12 @@
<field name="name">原材料</field>
<field name="type">原材料</field>
</record>
<record id="product_category_surface_technics_sf" model="product.category">
<field name="name">表面工艺</field>
<field name="type">表面工艺</field>
<field name="parent_id" ref="sf_manufacturing.product_category_outsource_process"/>
<field name="property_cost_method">fifo</field>
<field name="property_valuation">manual_periodic</field>
</record>
<record id="product_category_cutting_tool_sf" model="product.category">
@@ -40,10 +42,10 @@
<!-- <field name="company_id" ref="base.main_company"/>-->
</record>
<!-- <record id="res_users_bfm" model="res.users">-->
<!-- <field name="name">业务平台</field>-->
<!--&lt;!&ndash; <field name="partner_id" ref="res_partner_bfm"/>&ndash;&gt;-->
<!-- </record>-->
<!-- <record id="res_users_bfm" model="res.users">-->
<!-- <field name="name">业务平台</field>-->
<!--&lt;!&ndash; <field name="partner_id" ref="res_partner_bfm"/>&ndash;&gt;-->
<!-- </record>-->
<record id="product_functional_tool_sf" model="product.product">
<field name="name">功能刀具</field>

View File

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

View File

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

View File

@@ -9,8 +9,9 @@
""",
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
'depends': ['sf_sale', 'sf_dlm', 'sf_manufacturing','jikimo_attachment_viewer'],
'depends': ['sf_sale', 'sf_dlm', 'sf_manufacturing', 'jikimo_attachment_viewer'],
'data': [
'data/sequence.xml',
'data/stock_data.xml',
'views/product_template_management_view.xml',
],

View File

@@ -0,0 +1,10 @@
<odoo>
<data>
<record id="sequence_production_process_parameter" model="ir.sequence">
<field name="name">工艺可选参数编码序列</field>
<field name="code">sf.production.process.parameter</field>
<field name="padding">3</field>
<field name="number_increment">1</field>
</record>
</data>
</odoo>

View File

@@ -1,2 +1,4 @@
# from . import product_template
# from . import product_supplierinfo
from . import sf_production_common
from . import mrp_routing_workcenter

View File

@@ -0,0 +1,16 @@
# import logging
# from odoo import fields, models, api
# from odoo.exceptions import UserError
# from odoo.tools import str2bool
# class ResMrpRoutingWorkcenter(models.Model):
# _inherit = 'mrp.routing.workcenter'
# def init(self):
# super(ResMrpRoutingWorkcenter, self).init()
# # 在模块初始化时触发计算字段的更新
# records = self.search([])
# if str2bool(self.env['ir.config_parameter'].get_param('sf.production.process.parameter.is_init_workcenter',default='False')):
# return
# records.optional_process_parameters_date()
# self.env['ir.config_parameter'].set_param('sf.production.process.parameter.is_init_workcenter', True)

View File

@@ -0,0 +1,85 @@
# # -*- coding: utf-8 -*-
# import logging
# from odoo import fields, models, api
# from odoo.exceptions import UserError
# from odoo.tools import str2bool
# class SfProductionProcessParameter(models.Model):
# _inherit = 'sf.production.process.parameter'
# @api.model
# def create(self, vals):
# # if vals.get('code', '/') == '/' or vals.get('code', '/') is False:
# # vals['code'] = '101'+self.routing_id.code +self.env['ir.sequence'].next_by_code('sf.production.process.parameter')
# if vals.get('routing_id'):
# # vals['gain_way'] = '外协'
# routing_id = self.env['mrp.routing.workcenter'].browse(vals.get('routing_id'))
# if routing_id.surface_technics_id and not vals.get('process_id'):
# vals['process_id'] = routing_id.surface_technics_id.id
# if vals.get('code', '/') == '/' or vals.get('code', '/') is False:
# vals['code'] = '101' + routing_id.code + self.env['ir.sequence'].next_by_code(
# 'sf.production.process.parameter')
# obj = super(SfProductionProcessParameter, self).create(vals)
# return obj
# def create_service_product(self):
# service_categ = self.env.ref(
# 'sf_dlm.product_category_surface_technics_sf').sudo()
# product_name = f"{self.process_id.name}_{self.name}"
# product_id = self.env['product.template'].search(
# [("name", '=', product_name)])
# if product_id:
# product_id.server_product_process_parameters_id = self.id
# else:
# res_partner = self.env['res.partner'].search([('name','=','湖南傲派自动化设备有限公司')])
# self.env['product.template'].create({
# 'detailed_type': 'service',
# 'name': product_name,
# 'invoice_policy': 'delivery',
# 'categ_id': service_categ.id,
# 'description': f"基于{self.name}创建的服务产品",
# 'sale_ok': True, # 可销售
# 'purchase_ok': True, # 可采购
# 'server_product_process_parameters_id': self.id,
# 'seller_ids': [(0, 0, {
# # 'delay': 1,
# 'partner_id': res_partner.id,
# 'price': 1, })],
# })
# def create_work_center(self):
# production_process_parameter = self
# if not production_process_parameter.process_id:
# return
# if not production_process_parameter.routing_id:
# workcenter_id = self.env['mrp.routing.workcenter'].search(
# [("surface_technics_id", '=', production_process_parameter.process_id.id)])
# if not workcenter_id:
# outsourcing_work_center = self.env['mrp.workcenter'].search(
# [("name", '=', '外协工作中心')])
# routing_id = self.env['mrp.routing.workcenter'].create({
# 'workcenter_ids': [(6, 0, outsourcing_work_center.ids)],
# 'routing_tag': 'special',
# 'routing_type': '表面工艺',
# 'is_outsource': True,
# 'surface_technics_id': production_process_parameter.process_id.id,
# 'name': production_process_parameter.process_id.name,
# })
# production_process_parameter.routing_id = routing_id.id
# else:
# production_process_parameter.routing_id = workcenter_id.id
# def init(self):
# super(SfProductionProcessParameter, self).init()
# # 在模块初始化时触发计算字段的更新
# records = self.search([])
# if str2bool(self.env['ir.config_parameter'].get_param('sf.production.process.parameter.is_init_process',
# default='False')):
# return
# for record in records:
# if not record.outsourced_service_products:
# record.create_service_product()
# record.create_work_center()
# self.env['ir.config_parameter'].set_param('sf.production.process.parameter.is_init_process', True)

View File

@@ -39,9 +39,9 @@
attrs="{'invisible': [('categ_type', 'not in', ['成品','坯料', '原材料'])],'readonly': [('id', '!=', False)]}"/>
<field name="materials_type_id" string="型号" placeholder="请选择" options="{'no_create': True}"
attrs="{'invisible': [('categ_type', 'not in', ['成品','坯料', '原材料'])],'readonly': [('id', '!=', False)]}"/>
<field name="server_product_process_parameters_id" string="表面工艺参数"
<field name="server_product_process_parameters_id" string="工艺参数"
options="{'no_create': True}"
attrs="{'invisible': ['|',('categ_type', '!=', '表面工艺'),('categ_type', '=', False)]}"/>
attrs="{'invisible': ['|',('detailed_type', '!=', 'service'),('detailed_type', '=', False)]}"/>
<field name="cutting_tool_material_id" class="custom_required"
options="{'no_create': True}"
attrs="{'invisible': [('categ_type', '!=', '刀具')],'required': [('categ_type', '=', '刀具')],'readonly': [('id', '!=', False)]}"

View File

@@ -5,7 +5,7 @@ import json
import base64
import logging
import psycopg2
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
from odoo import http, fields
from odoo.http import request
@@ -414,7 +414,7 @@ class Sf_Dashboard_Connect(http.Controller):
# 工单计划量切换为CNC工单
plan_data_total_counts = work_order_obj.search_count(
[('production_id.production_line_id.name', '=', line),
[('production_line_id.name', '=', line), ('id', '!=', 8061),
('state', 'in', ['ready', 'progress', 'done']), ('routing_type', '=', 'CNC加工')])
# # 工单完成量
@@ -423,13 +423,13 @@ class Sf_Dashboard_Connect(http.Controller):
# 工单完成量切换为CNC工单
plan_data_finish_counts = work_order_obj.search_count(
[('production_id.production_line_id.name', '=', line),
[('production_line_id.name', '=', line),
('state', 'in', ['done']), ('routing_type', '=', 'CNC加工')])
# 超期完成量
# 搜索所有已经完成的工单
plan_data_overtime = work_order_obj.search([
('production_id.production_line_id.name', '=', line),
('production_line_id.name', '=', line),
('state', 'in', ['done']),
('routing_type', '=', 'CNC加工')
])
@@ -448,9 +448,14 @@ class Sf_Dashboard_Connect(http.Controller):
])
# 过滤出那些检测结果状态为 '返工' 或 '报废' 的记录
faulty_plans = plan_data.filtered(lambda p: any(
result.test_results in ['返工', '报废'] for result in p.production_id.detection_result_ids
))
# faulty_plans = plan_data.filtered(lambda p: any(
# result.test_results in ['返工', '报废'] for result in p.production_id.detection_result_ids
# ))
faulty_plans = request.env['quality.check'].sudo().search([
('operation_id.name', '=', 'CNC加工'),
('quality_state', 'in', ['fail'])
])
# 查找制造订单取消与归档的数量
cancel_order_count = production_obj.search_count(
@@ -567,7 +572,7 @@ class Sf_Dashboard_Connect(http.Controller):
"""
res = {'status': 1, 'message': '成功', 'data': {}}
# plan_obj = request.env['sf.production.plan'].sudo()
plan_obj = request.env['mrp.workorder'].sudo().search([('routing_type', '=', 'CNC加工')])
# plan_obj = request.env['mrp.workorder'].sudo().search([('routing_type', '=', 'CNC加工')])
line_list = ast.literal_eval(kw['line_list'])
begin_time_str = kw['begin_time'].strip('"')
end_time_str = kw['end_time'].strip('"')
@@ -617,11 +622,19 @@ class Sf_Dashboard_Connect(http.Controller):
for time_interval in time_intervals:
start_time, end_time = time_interval
orders = plan_obj.search([
('production_id.production_line_id.name', '=', line),
# orders = plan_obj.search([
# ('production_line_id.name', '=', line),
# ('state', 'in', ['done']),
# (date_field_name, '>=', start_time.strftime('%Y-%m-%d %H:%M:%S')),
# (date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')) # 包括结束时间
# ])
orders = request.env['mrp.workorder'].sudo().search([
('routing_type', '=', 'CNC加工'), # 将第一个条件合并进来
('production_line_id.name', '=', line),
('state', 'in', ['done']),
(date_field_name, '>=', start_time.strftime('%Y-%m-%d %H:%M:%S')),
(date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')) # 包括结束时间
(date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S'))
])
# 使用小时和分钟作为键,确保每个小时的数据有独立的键
@@ -638,18 +651,22 @@ class Sf_Dashboard_Connect(http.Controller):
for date in date_list:
next_day = date + timedelta(days=1)
orders = plan_obj.search([('production_id.production_line_id.name', '=', line), ('state', 'in', ['done']),
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
])
rework_orders = plan_obj.search(
[('production_id.production_line_id.name', '=', line), ('state', 'in', ['rework']),
orders = request.env['mrp.workorder'].sudo().search(
[('production_id.production_line_id.name', '=', line), ('state', 'in', ['done']),
('routing_type', '=', 'CNC加工'),
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
])
not_passed_orders = plan_obj.search(
rework_orders = request.env['mrp.workorder'].sudo().search(
[('production_id.production_line_id.name', '=', line), ('state', 'in', ['rework']),
('routing_type', '=', 'CNC加工'),
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
])
not_passed_orders = request.env['mrp.workorder'].sudo().search(
[('production_id.production_line_id.name', '=', line), ('state', 'in', ['scrap', 'cancel']),
('routing_type', '=', 'CNC加工'),
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
])
@@ -751,11 +768,14 @@ class Sf_Dashboard_Connect(http.Controller):
for line in line_list:
# 未完成订单
not_done_orders = plan_obj.search(
[('production_line_id.name', '=', line), ('state', 'not in', ['finished']),
('production_id.state', 'not in', ['cancel', 'done']), ('active', '=', True)
# not_done_orders = plan_obj.search(
# [('production_line_id.name', '=', line), ('state', 'not in', ['finished']),
# ('production_id.state', 'not in', ['cancel', 'done']), ('active', '=', True)
# ])
not_done_orders = request.env['mrp.workorder'].sudo().search(
[('production_line_id.name', '=', line), ('state', 'in', ['ready', 'progress']),
('routing_type', '=', 'CNC加工')
])
# print(not_done_orders)
# 完成订单
# 获取当前时间并计算24小时前的时间
@@ -807,16 +827,18 @@ class Sf_Dashboard_Connect(http.Controller):
'draft': '待排程',
'done': '已排程',
'processing': '生产中',
'finished': '已完成'
'finished': '已完成',
'ready': '待加工',
'progress': '生产中',
}
line_dict = {
'sequence': id_to_sequence[order.id],
'workorder_name': order.name,
'workorder_name': order.production_id.name,
'blank_name': blank_name,
'material': material,
'dimensions': dimensions,
'order_qty': order.product_qty,
'order_qty': 1,
'state': state_dict[order.state],
}
@@ -897,15 +919,17 @@ class Sf_Dashboard_Connect(http.Controller):
cur.execute(sql2, (item,))
result2 = cur.fetchall()
# print('result2========', result2)
#
for row in result:
res['data'][item] = {'idle_count': row[0]}
alarm_count = []
for row in result2:
alarm_count.append(row[1])
if row[0]:
total_alarm_time += abs(float(row[0]))
if float(row[0]) >= 28800:
continue
# total_alarm_time += abs(float(row[0]))
total_alarm_time += float(row[0])
else:
total_alarm_time += 0.0
if len(list(set(alarm_count))) == 1:
@@ -915,6 +939,7 @@ class Sf_Dashboard_Connect(http.Controller):
alarm_count_num = 1
else:
alarm_count_num = len(list(set(alarm_count)))
res['data'][item]['total_alarm_time'] = total_alarm_time / 3600
res['data'][item]['alarm_count_num'] = alarm_count_num
@@ -1097,169 +1122,169 @@ class Sf_Dashboard_Connect(http.Controller):
return request.not_found()
# 设备运行率
@http.route('/api/RunningTime', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
def RunningTime(self, **kw):
"""
获取设备运行时长
"""
res = {'status': 1, 'message': '成功', 'data': {}}
# 连接数据库
conn = psycopg2.connect(**db_config)
# 获取请求的机床数据
machine_list = ast.literal_eval(kw['machine_list'])
def fetch_result_as_dict(cursor):
"""辅助函数:将查询结果转为字典"""
columns = [desc[0] for desc in cursor.description]
return dict(zip(columns, cursor.fetchone())) if cursor.rowcount != 0 else None
# 初始化当天、当月和有记录以来的总时长
day_total_running_time = 0
day_total_process_time = 0
day_work_rate = 0
month_total_running_time = 0
month_total_process_time = 0
month_work_rate = 0
all_time_total_running_time = 0
all_time_total_process_time = 0
all_time_work_rate = 0
for item in machine_list:
# 获取当天第一条记录排除device_state等于离线的记录
with conn.cursor() as cur:
cur.execute("""
SELECT * FROM device_data
WHERE device_name = %s
AND time >= CURRENT_DATE -- 今日 00:00:00
AND time < CURRENT_DATE + 1 -- 明日 00:00:00
AND device_state in ('待机', '警告', '运行中')
ORDER BY time ASC
LIMIT 1;
""", (item,))
first_today = fetch_result_as_dict(cur)
# print("当天第一条记录(非离线):", first_today)
# 获取当天最新一条记录排除device_state等于离线的记录
with conn.cursor() as cur:
cur.execute("""
SELECT * FROM device_data
WHERE device_name = %s
AND time >= CURRENT_DATE -- 今日 00:00:00
AND time < CURRENT_DATE + 1 -- 明日 00:00:00
AND device_state in ('待机', '警告', '运行中')
ORDER BY time DESC
LIMIT 1;
""", (item,))
last_today = fetch_result_as_dict(cur)
# print("当天最新一条记录(非离线):", last_today)
# 计算当天运行时长
if first_today and last_today:
running_time = convert_to_seconds(last_today['run_time']) - convert_to_seconds(first_today['run_time'])
process_time = convert_to_seconds(last_today['process_time']) - convert_to_seconds(
first_today['process_time'])
day_total_running_time += abs(running_time)
day_total_process_time += abs(process_time)
# 获取当月第一条记录排除device_state等于离线的记录
with conn.cursor() as cur:
cur.execute("""
SELECT * FROM device_data
WHERE device_name = %s
AND time >= DATE_TRUNC('MONTH', CURRENT_DATE)
AND time < DATE_TRUNC('MONTH', CURRENT_DATE) + INTERVAL '1 MONTH'
AND device_state in ('待机', '警告', '运行中')
ORDER BY time ASC
LIMIT 1;
""", (item,))
first_month = fetch_result_as_dict(cur)
# print("当月第一条记录:", first_month)
# 获取当月最新一条记录
with conn.cursor() as cur:
cur.execute("""
SELECT * FROM device_data
WHERE device_name = %s
AND time >= DATE_TRUNC('MONTH', CURRENT_DATE)
AND time < DATE_TRUNC('MONTH', CURRENT_DATE) + INTERVAL '1 MONTH'
AND device_state in ('待机', '警告', '运行中')
ORDER BY time DESC
LIMIT 1;
""", (item,))
last_month = fetch_result_as_dict(cur)
# print("当月最新一条记录(非离线):", last_month)
# 计算当月运行时长
if first_month and last_month:
month_running_time = convert_to_seconds(last_month['run_time']) - convert_to_seconds(
first_month['run_time'])
month_process_time = convert_to_seconds(last_month['process_time']) - convert_to_seconds(
first_month['process_time'])
month_total_running_time += abs(month_running_time)
month_total_process_time += abs(month_process_time)
# 获取有记录以来的第一条记录排除device_state等于离线的记录
with conn.cursor() as cur:
cur.execute("""
SELECT * FROM device_data
WHERE device_name = %s
AND device_state in ('待机', '警告', '运行中')
ORDER BY time ASC
LIMIT 1;
""", (item,))
first_all_time = fetch_result_as_dict(cur)
# print("有记录以来的第一条记录(非离线):", first_all_time)
# 获取有记录以来的最新一条记录排除device_state等于离线的记录
with conn.cursor() as cur:
cur.execute("""
SELECT * FROM device_data
WHERE device_name = %s
AND device_state in ('待机', '警告', '运行中')
ORDER BY time DESC
LIMIT 1;
""", (item,))
last_all_time = fetch_result_as_dict(cur)
# print("有记录以来的最新一条记录(非离线):", last_all_time)
# 计算有记录以来的运行时长
if first_all_time and last_all_time:
all_time_running_time = convert_to_seconds(last_all_time['run_time']) - convert_to_seconds(
first_all_time['run_time'])
all_time_process_time = convert_to_seconds(last_all_time['process_time']) - convert_to_seconds(
first_all_time['process_time'])
all_time_total_running_time += abs(all_time_running_time)
all_time_total_process_time += abs(all_time_process_time)
# 计算当天工作效率
if day_total_running_time > day_total_process_time:
day_work_rate = day_total_process_time / day_total_running_time if day_total_running_time != 0 else 0
else:
day_work_rate = day_total_running_time / day_total_process_time if day_total_process_time != 0 else 0
print("当天工作效率: %s" % day_work_rate)
# 计算当月工作效率
if month_total_running_time > month_total_process_time:
month_work_rate = month_total_process_time / month_total_running_time if month_total_running_time != 0 else 0
else:
month_work_rate = month_total_running_time / month_total_process_time if month_total_process_time != 0 else 0
print("当月工作效率: %s" % month_work_rate)
# 计算有记录以来的工作效率
if all_time_total_running_time > all_time_total_process_time:
all_time_work_rate = all_time_total_process_time / all_time_total_running_time if all_time_total_running_time != 0 else 0
else:
all_time_work_rate = all_time_total_running_time / all_time_total_process_time if all_time_total_process_time != 0 else 0
print("有记录以来的工作效率: %s" % all_time_work_rate)
conn.close()
# 返回数据
res['data']['day_work_rate'] = day_work_rate
res['data']['month_work_rate'] = month_work_rate
res['data']['all_time_work_rate'] = all_time_work_rate
return json.dumps(res)
# @http.route('/api/RunningTime', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
# def RunningTime(self, **kw):
# """
# 获取设备运行时长
# """
# res = {'status': 1, 'message': '成功', 'data': {}}
# # 连接数据库
# conn = psycopg2.connect(**db_config)
# # 获取请求的机床数据
# machine_list = ast.literal_eval(kw['machine_list'])
#
# def fetch_result_as_dict(cursor):
# """辅助函数:将查询结果转为字典"""
# columns = [desc[0] for desc in cursor.description]
# return dict(zip(columns, cursor.fetchone())) if cursor.rowcount != 0 else None
#
# # 初始化当天、当月和有记录以来的总时长
# day_total_running_time = 0
# day_total_process_time = 0
# day_work_rate = 0
# month_total_running_time = 0
# month_total_process_time = 0
# month_work_rate = 0
# all_time_total_running_time = 0
# all_time_total_process_time = 0
# all_time_work_rate = 0
#
# for item in machine_list:
# # 获取当天第一条记录排除device_state等于离线的记录
# with conn.cursor() as cur:
# cur.execute("""
# SELECT * FROM device_data
# WHERE device_name = %s
# AND time >= CURRENT_DATE -- 今日 00:00:00
# AND time < CURRENT_DATE + 1 -- 明日 00:00:00
# AND device_state in ('待机', '警告', '运行中')
# ORDER BY time ASC
# LIMIT 1;
# """, (item,))
# first_today = fetch_result_as_dict(cur)
# # print("当天第一条记录(非离线):", first_today)
#
# # 获取当天最新一条记录排除device_state等于离线的记录
# with conn.cursor() as cur:
# cur.execute("""
# SELECT * FROM device_data
# WHERE device_name = %s
# AND time >= CURRENT_DATE -- 今日 00:00:00
# AND time < CURRENT_DATE + 1 -- 明日 00:00:00
# AND device_state in ('待机', '警告', '运行中')
# ORDER BY time DESC
# LIMIT 1;
# """, (item,))
# last_today = fetch_result_as_dict(cur)
# # print("当天最新一条记录(非离线):", last_today)
#
# # 计算当天运行时长
# if first_today and last_today:
# running_time = convert_to_seconds(last_today['run_time']) - convert_to_seconds(first_today['run_time'])
# process_time = convert_to_seconds(last_today['process_time']) - convert_to_seconds(
# first_today['process_time'])
# day_total_running_time += abs(running_time)
# day_total_process_time += abs(process_time)
#
# # 获取当月第一条记录排除device_state等于离线的记录
# with conn.cursor() as cur:
# cur.execute("""
# SELECT * FROM device_data
# WHERE device_name = %s
# AND time >= DATE_TRUNC('MONTH', CURRENT_DATE)
# AND time < DATE_TRUNC('MONTH', CURRENT_DATE) + INTERVAL '1 MONTH'
# AND device_state in ('待机', '警告', '运行中')
# ORDER BY time ASC
# LIMIT 1;
# """, (item,))
# first_month = fetch_result_as_dict(cur)
# # print("当月第一条记录:", first_month)
#
# # 获取当月最新一条记录
# with conn.cursor() as cur:
# cur.execute("""
# SELECT * FROM device_data
# WHERE device_name = %s
# AND time >= DATE_TRUNC('MONTH', CURRENT_DATE)
# AND time < DATE_TRUNC('MONTH', CURRENT_DATE) + INTERVAL '1 MONTH'
# AND device_state in ('待机', '警告', '运行中')
# ORDER BY time DESC
# LIMIT 1;
# """, (item,))
# last_month = fetch_result_as_dict(cur)
# # print("当月最新一条记录(非离线):", last_month)
#
# # 计算当月运行时长
# if first_month and last_month:
# month_running_time = convert_to_seconds(last_month['run_time']) - convert_to_seconds(
# first_month['run_time'])
# month_process_time = convert_to_seconds(last_month['process_time']) - convert_to_seconds(
# first_month['process_time'])
# month_total_running_time += abs(month_running_time)
# month_total_process_time += abs(month_process_time)
#
# # 获取有记录以来的第一条记录排除device_state等于离线的记录
# with conn.cursor() as cur:
# cur.execute("""
# SELECT * FROM device_data
# WHERE device_name = %s
# AND device_state in ('待机', '警告', '运行中')
# ORDER BY time ASC
# LIMIT 1;
# """, (item,))
# first_all_time = fetch_result_as_dict(cur)
# # print("有记录以来的第一条记录(非离线):", first_all_time)
#
# # 获取有记录以来的最新一条记录排除device_state等于离线的记录
# with conn.cursor() as cur:
# cur.execute("""
# SELECT * FROM device_data
# WHERE device_name = %s
# AND device_state in ('待机', '警告', '运行中')
# ORDER BY time DESC
# LIMIT 1;
# """, (item,))
# last_all_time = fetch_result_as_dict(cur)
# # print("有记录以来的最新一条记录(非离线):", last_all_time)
#
# # 计算有记录以来的运行时长
# if first_all_time and last_all_time:
# all_time_running_time = convert_to_seconds(last_all_time['run_time']) - convert_to_seconds(
# first_all_time['run_time'])
# all_time_process_time = convert_to_seconds(last_all_time['process_time']) - convert_to_seconds(
# first_all_time['process_time'])
# all_time_total_running_time += abs(all_time_running_time)
# all_time_total_process_time += abs(all_time_process_time)
#
# # 计算当天工作效率
# if day_total_running_time > day_total_process_time:
# day_work_rate = day_total_process_time / day_total_running_time if day_total_running_time != 0 else 0
# else:
# day_work_rate = day_total_running_time / day_total_process_time if day_total_process_time != 0 else 0
# print("当天工作效率: %s" % day_work_rate)
#
# # 计算当月工作效率
# if month_total_running_time > month_total_process_time:
# month_work_rate = month_total_process_time / month_total_running_time if month_total_running_time != 0 else 0
# else:
# month_work_rate = month_total_running_time / month_total_process_time if month_total_process_time != 0 else 0
# print("当月工作效率: %s" % month_work_rate)
#
# # 计算有记录以来的工作效率
# if all_time_total_running_time > all_time_total_process_time:
# all_time_work_rate = all_time_total_process_time / all_time_total_running_time if all_time_total_running_time != 0 else 0
# else:
# all_time_work_rate = all_time_total_running_time / all_time_total_process_time if all_time_total_process_time != 0 else 0
# print("有记录以来的工作效率: %s" % all_time_work_rate)
#
# conn.close()
#
# # 返回数据
# res['data']['day_work_rate'] = day_work_rate
# res['data']['month_work_rate'] = month_work_rate
# res['data']['all_time_work_rate'] = all_time_work_rate
#
# return json.dumps(res)
# 设备运行时长
@http.route('/api/RunningTimeDetail', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
@@ -1273,10 +1298,7 @@ class Sf_Dashboard_Connect(http.Controller):
conn = psycopg2.connect(**db_config)
# 获取请求的机床数据
machine_list = ast.literal_eval(kw['machine_list'])
time_threshold = datetime.now() - timedelta(days=1)
alarm_last_24_time = 0.0
alarm_all_time = 0.0
time_threshold = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
def fetch_result_as_dict(cursor):
"""辅助函数:将查询结果转为字典"""
@@ -1286,6 +1308,9 @@ class Sf_Dashboard_Connect(http.Controller):
# 获取当前时间的时间戳
current_timestamp = datetime.now().timestamp()
for item in machine_list:
alarm_last_24_time = 0.0
alarm_all_time = 0.0
euipment_obj = request.env['maintenance.equipment'].sudo().search([('code', '=', item)])
# 机床上线时间段
first_online_duration = current_timestamp - euipment_obj.first_online_time.timestamp()
@@ -1302,8 +1327,8 @@ class Sf_Dashboard_Connect(http.Controller):
cur.execute("""
SELECT * FROM device_data
WHERE device_name = %s
AND device_state in ('待机', '警告', '运行中') AND time >= %s AND process_time IS NOT NULL
ORDER BY time ASC
AND device_state in ('待机', '警告', '运行中') AND time <= %s AND process_time IS NOT NULL
ORDER BY time DESC
LIMIT 1;
""", (item, time_threshold))
last_24_time = fetch_result_as_dict(cur)
@@ -1323,16 +1348,17 @@ class Sf_Dashboard_Connect(http.Controller):
alarm_last_24_nums = []
with conn.cursor() as cur:
cur.execute("""
SELECT DISTINCT ON (alarm_start_time) alarm_time, alarm_start_time
SELECT DISTINCT ON (alarm_start_time, alarm_time) alarm_time, alarm_start_time
FROM device_data
WHERE device_name = %s
AND alarm_start_time IS NOT NULL AND time >= %s;
AND alarm_start_time IS NOT NULL AND alarm_start_time::timestamp >= %s;
""", (item, time_threshold))
results = cur.fetchall()
logging.info("results============:%s" % results)
for result in results:
alarm_last_24_nums.append(result[1])
if result[0]:
if float(result[0]) >= 1000:
if float(result[0]) >= 28800:
continue
alarm_last_24_time += float(result[0])
else:
@@ -1350,7 +1376,7 @@ class Sf_Dashboard_Connect(http.Controller):
for result in results:
alarm_all_nums.append(result[1])
if result[0]:
if float(result[0]) >= 1000:
if float(result[0]) >= 28800:
continue
alarm_all_time += float(result[0])
else:
@@ -1385,3 +1411,384 @@ class Sf_Dashboard_Connect(http.Controller):
conn.close()
return json.dumps(res)
# @http.route('/api/utilization/rate', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
# def UtilizationRate(self, **kw):
# """
# 获取稼动率
# """
# logging.info("kw=:%s" % kw)
# res = {'status': 1, 'message': '成功', 'data': {}}
# # 获取请求的机床数据
# machine_list = ast.literal_eval(kw['machine_list'])
# line = kw['line']
# orders = request.env['mrp.workorder'].sudo().search([
# ('routing_type', '=', 'CNC加工'), # 将第一个条件合并进来
# ('production_line_id.name', '=', line),
# ('state', 'in', ['done'])
# ])
#
# faulty_plans = request.env['quality.check'].sudo().search([
# ('operation_id.name', '=', 'CNC加工'),
# ('quality_state', 'in', ['fail'])
# ])
#
# # 计算时间范围
# now = datetime.now()
# today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
# month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
#
# total_power_on_time = 0
# month_power_on_time = 0
# today_power_on_time = 0
# today_power_on_dict = {}
# today_alarm_dict = {}
# single_machine_dict = {}
#
# today_order_data = []
# month_order_data = []
# today_check_ng = []
# month_check_ng = []
#
# total_alarm_time = 0
# today_alarm_time = 0
# month_alarm_time = 0
#
# for order in orders:
# time = order.date_finished
# if time >= today_start:
# today_order_data.append(order)
# if time >= month_start:
# month_order_data.append(order)
#
# for faulty_plan in faulty_plans:
# time = faulty_plan.write_date
# if time >= today_start:
# today_check_ng.append(faulty_plan)
# if time >= month_start:
# month_check_ng.append(faulty_plan)
#
# # 连接数据库
# conn = psycopg2.connect(**db_config)
# for item in machine_list:
# with conn.cursor() as cur:
# cur.execute("""
# (
# SELECT power_on_time, 'latest' AS record_type
# FROM device_data
# WHERE device_name = %s
# AND power_on_time IS NOT NULL
# ORDER BY time DESC
# LIMIT 1
# )
# UNION ALL
# (
# SELECT power_on_time, 'month_first' AS record_type
# FROM device_data
# WHERE device_name = %s
# AND power_on_time IS NOT NULL
# AND time >= date_trunc('month', CURRENT_DATE) -- ✅ 修复日期函数
# AND time < (date_trunc('month', CURRENT_DATE) + INTERVAL '1 month')::date
# ORDER BY time ASC
# LIMIT 1
# )
# UNION ALL
# (
# SELECT power_on_time, 'day_first' AS record_type
# FROM device_data
# WHERE device_name = %s
# AND power_on_time IS NOT NULL
# AND time::date = CURRENT_DATE -- ✅ 更高效的写法
# ORDER BY time ASC
# LIMIT 1
# );
# """, (item, item, item))
# results = cur.fetchall()
# if len(results) >= 1:
# total_power_on_time += convert_to_seconds(results[0][0])
# else:
# total_power_on_time += 0
# if len(results) >= 2:
# month_power_on_time += convert_to_seconds(results[1][0])
# else:
# month_power_on_time += 0
# if len(results) >= 3:
# today_power_on_time += convert_to_seconds(results[2][0])
# today_power_on_dict[item] = today_power_on_time
# else:
# today_power_on_time += 0
# today_power_on_dict[item] = 0
#
# with conn.cursor() as cur:
# cur.execute("""
# SELECT DISTINCT ON (alarm_start_time) alarm_time, alarm_start_time
# FROM device_data
# WHERE device_name = %s AND alarm_start_time IS NOT NULL
# ORDER BY alarm_start_time, time;
# """, (item,))
# results = cur.fetchall()
# today_data = []
# month_data = []
#
# for record in results:
# if record[0]:
# if float(record[0]) >= 28800:
# continue
# total_alarm_time += float(record[0])
# else:
# total_alarm_time += 0.0
# alarm_start = datetime.strptime(record[1], "%Y-%m-%d %H:%M:%S")
# if alarm_start >= today_start:
# today_data.append(record)
# if alarm_start >= month_start:
# month_data.append(record)
# if today_data:
# for today in today_data:
# if today[0]:
# if float(today[0]) >= 28800:
# continue
# today_alarm_time += float(today[0])
# today_alarm_dict[item] = today_alarm_time
# else:
# today_alarm_time += 0.0
# today_alarm_dict[item] = 0
# else:
# today_alarm_dict[item] = 0
# for month in month_data:
# if month[0]:
# if float(month[0]) >= 28800:
# continue
# month_alarm_time += float(month[0])
# else:
# month_alarm_time += 0.0
#
# conn.close()
#
# logging.info('报警时间总月日=============%s, %s, %s' % (total_alarm_time, month_alarm_time, today_alarm_time))
# # 计算时间开动率(累计、月、日)
# if total_power_on_time:
# total_power_on_rate = (total_power_on_time - total_alarm_time) / total_power_on_time
# else:
# total_power_on_rate = 0
# if month_power_on_time:
# month_power_on_rate = (total_power_on_time - month_power_on_time - month_alarm_time) / (
# total_power_on_time - month_power_on_time)
# else:
# month_power_on_rate = 0
# if today_power_on_time:
# today_power_on_rate = (total_power_on_time - today_power_on_time - today_alarm_time) / (
# total_power_on_time - today_power_on_time)
# else:
# today_power_on_rate = 0
# logging.info("总开动率: %s" % total_power_on_rate)
# logging.info("月开动率: %s" % month_power_on_rate)
# logging.info("日开动率: %s" % today_power_on_rate)
#
# # 计算性能开动率(累计、月、日)
# logging.info('完成工单数量: %s' % len(orders))
# total_performance_rate = len(orders) * 30 * 60 / (total_power_on_time - total_alarm_time)
# month_performance_rate = len(month_data) * 30 * 60 / (
# total_power_on_time - month_power_on_time - month_alarm_time)
# today_performance_rate = len(today_data) * 30 * 60 / (
# total_power_on_time - today_power_on_time - today_alarm_time) if today_power_on_time != 0 else 0
# logging.info("总性能率: %s" % total_performance_rate)
# logging.info("月性能率: %s" % month_performance_rate)
# logging.info("日性能率: %s" % today_performance_rate)
#
# # 计算累计合格率
# total_pass_rate = (len(orders) - len(today_check_ng)) / len(orders) if len(orders) != 0 else 0
# month_pass_rate = (len(month_order_data) - len(month_check_ng)) / len(month_order_data) if len(month_order_data) != 0 else 0
# today_pass_rate = (len(today_order_data) - len(today_check_ng)) / len(today_order_data) if len(today_order_data) != 0 else 0
# logging.info("总合格率: %s" % total_pass_rate)
# logging.info("月合格率: %s" % month_pass_rate)
# logging.info("日合格率: %s" % today_pass_rate)
#
# # # 返回数据
# # res['data'][item] = {
# # 'total_utilization_rate': total_power_on_rate * total_performance_rate * total_pass_rate,
# # 'month_utilization_rate': month_power_on_rate * month_performance_rate * month_pass_rate,
# # 'today_utilization_rate': today_power_on_rate * today_performance_rate * today_pass_rate,
# # }
# for i in machine_list:
# single_machine_utilization_rate = total_power_on_time - today_power_on_dict[i] - today_alarm_dict[i] / (
# total_power_on_time - today_power_on_dict[i])
# single_machine_dict[i] = single_machine_utilization_rate * today_performance_rate * today_pass_rate
#
# res['data'] = {
# 'total_utilization_rate': total_power_on_rate * total_performance_rate * total_pass_rate,
# 'month_utilization_rate': month_power_on_rate * month_performance_rate * month_pass_rate,
# 'today_utilization_rate': today_power_on_rate * today_performance_rate * today_pass_rate,
# 'single_machine_dict': single_machine_dict
# }
#
# return json.dumps(res)
@http.route('/api/RunningTime', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
def UtilizationRate(self, **kw):
"""
获取稼动率
"""
logging.info("kw=:%s" % kw)
res = {'status': 1, 'message': '成功', 'data': {}}
# 获取请求的机床数据
machine_list = ast.literal_eval(kw['machine_list'])
faulty_plans = request.env['quality.check'].sudo().search([
('operation_id.name', '=', 'CNC加工'),
('quality_state', 'in', ['fail'])
])
# 计算时间范围
now = datetime.now()
today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
total_power_on_time = 0
month_power_on_time = 0
today_power_on_time = 0
today_power_on_dict = {}
today_alarm_dict = {}
single_machine_dict = {}
today_order_data = []
month_order_data = []
today_check_ng = []
month_check_ng = []
total_alarm_time = 0
today_alarm_time = 0
month_alarm_time = 0
# 连接数据库
conn = psycopg2.connect(**db_config)
for item in machine_list:
with conn.cursor() as cur:
cur.execute("""
(
SELECT power_on_time, 'latest' AS record_type
FROM device_data
WHERE device_name = %s
AND power_on_time IS NOT NULL
ORDER BY time DESC
LIMIT 1
)
UNION ALL
(
SELECT power_on_time, 'month_first' AS record_type
FROM device_data
WHERE device_name = %s
AND power_on_time IS NOT NULL
AND time >= date_trunc('month', CURRENT_DATE) -- ✅ 修复日期函数
AND time < (date_trunc('month', CURRENT_DATE) + INTERVAL '1 month')::date
ORDER BY time ASC
LIMIT 1
)
UNION ALL
(
SELECT power_on_time, 'day_first' AS record_type
FROM device_data
WHERE device_name = %s
AND power_on_time IS NOT NULL
AND time >= CURRENT_DATE
AND time < CURRENT_DATE + INTERVAL '1 day'
ORDER BY time ASC
LIMIT 1
);
""", (item, item, item))
results = cur.fetchall()
logging.info('====================%s' % results)
if len(results) >= 1:
total_power_on_time += convert_to_seconds(results[0][0])
else:
total_power_on_time += 0
if len(results) >= 2:
month_power_on_time += convert_to_seconds(results[1][0])
else:
month_power_on_time += 0
if len(results) >= 3:
today_power_on_time += convert_to_seconds(results[2][0])
# today_power_on_dict[item] = today_power_on_time
else:
today_power_on_time += convert_to_seconds(results[0][0])
# today_power_on_dict[item] = 0
if results[0][0] == '0H0M':
logging.info('=woshide=%s' % results[0][0])
logging.info(type(results[0][0]))
total_power_on_time += convert_to_seconds(results[1][0])
today_power_on_time += convert_to_seconds(results[1][0])
with conn.cursor() as cur:
cur.execute("""
SELECT DISTINCT ON (alarm_start_time) alarm_time, alarm_start_time
FROM device_data
WHERE device_name = %s AND alarm_start_time IS NOT NULL
ORDER BY alarm_start_time, time;
""", (item,))
results = cur.fetchall()
today_data = []
month_data = []
for record in results:
if record[0]:
if float(record[0]) >= 86400:
continue
total_alarm_time += abs(float(record[0]))
else:
total_alarm_time += 0.0
alarm_start = datetime.strptime(record[1], "%Y-%m-%d %H:%M:%S")
if alarm_start >= today_start:
today_data.append(record)
if alarm_start >= month_start:
month_data.append(record)
if today_data:
for today in today_data:
if today[0]:
if float(today[0]) >= 28800:
continue
today_alarm_time += abs(float(today[0]))
today_alarm_dict[item] = today_alarm_time
else:
today_alarm_time += 0.0
today_alarm_dict[item] = 0
else:
today_alarm_dict[item] = 0
for month in month_data:
if month[0]:
if float(month[0]) >= 28800:
continue
month_alarm_time += abs(float(month[0]))
else:
month_alarm_time += 0.0
conn.close()
logging.info('在线时间总月日=============%s, %s, %s' % (total_power_on_time, month_power_on_time, today_power_on_time))
logging.info('报警时间总月日=============%s, %s, %s' % (total_alarm_time, month_alarm_time, today_alarm_time))
# 计算时间开动率(累计、月、日)
if total_power_on_time:
total_power_on_rate = (total_power_on_time - total_alarm_time) / total_power_on_time
else:
total_power_on_rate = 0
if month_power_on_time:
month_power_on_rate = (total_power_on_time - month_power_on_time - month_alarm_time) / (
total_power_on_time - month_power_on_time)
else:
month_power_on_rate = 0
if today_power_on_time:
today_power_on_rate = (total_power_on_time - today_power_on_time - today_alarm_time) / (
total_power_on_time - today_power_on_time)
else:
today_power_on_rate = 0
logging.info("总开动率: %s" % total_power_on_rate)
logging.info("月开动率: %s" % month_power_on_rate)
logging.info("日开动率: %s" % today_power_on_rate)
res['data'] = {
'all_time_work_rate': total_power_on_rate,
'month_work_rate': month_power_on_rate,
'day_work_rate': today_power_on_rate,
}
return json.dumps(res)

View File

@@ -1,15 +1,21 @@
# -*- coding: utf-8 -*-
import logging
from ftplib import FTP
import os
import re
from ftplib import FTP, error_perm
_logger = logging.getLogger(__name__)
class FTP_P(FTP):
"""
重写FTP类重写dirs方法
重写FTP类重写dirs方法,增加编码处理
"""
def __init__(self, host='', user='', passwd='', acct='', timeout=None, encoding='gbk'):
"""初始化时指定编码方式"""
super().__init__(host, user, passwd, acct, timeout)
self.encoding = encoding
def dirs(self, *args):
"""List a directory in long form.
By default list current directory to stdout.
@@ -30,7 +36,50 @@ class FTP_P(FTP):
tempdic['name'] = [file for file in r_files if file != "." and file != ".."]
# 去除. ..
return tempdic
# return [file for file in r_files if file != "." and file != ".."]
def nlst(self, *args):
"""Get a list of files in a directory."""
files = []
def append(line):
try:
if isinstance(line, bytes):
files.append(line.decode(self.encoding))
else:
files.append(line)
except UnicodeDecodeError:
files.append(line.decode('utf-8', errors='replace'))
cmd = 'NLST'
if args:
cmd = cmd + ' ' + args[0]
self.retrlines(cmd, append)
return files
def cwd(self, dirname):
"""Change to a directory."""
try:
if isinstance(dirname, bytes):
dirname = dirname.decode(self.encoding)
return super().cwd(dirname)
except UnicodeEncodeError:
return super().cwd(dirname.encode(self.encoding).decode('utf-8'))
def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
"""Store a file in binary mode."""
try:
if isinstance(cmd, bytes):
cmd = cmd.decode(self.encoding)
return super().storbinary(cmd, fp, blocksize, callback, rest)
except UnicodeEncodeError:
return super().storbinary(cmd.encode(self.encoding).decode('utf-8'), fp, blocksize, callback, rest)
def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
"""Retrieve a file in binary mode."""
try:
if isinstance(cmd, bytes):
cmd = cmd.decode(self.encoding)
return super().retrbinary(cmd, callback, blocksize, rest)
except UnicodeEncodeError:
return super().retrbinary(cmd.encode(self.encoding).decode('utf-8'), callback, blocksize, rest)
# FTP接口类
@@ -52,8 +101,8 @@ class FtpController:
print(self.username, self.port, self.host, self.password)
ftp = FTP_P()
_logger.info("===================connect==================")
# self.ftp.set_debuglevel(2) #打开调试级别2显示详细信息
ftp.set_pasv(0) # 0主动模式 1 #被动模式
# ftp.set_debuglevel(2) #打开调试级别2显示详细信息
# ftp.set_pasv(1) # 0主动模式 1 #被动模式
try:
ftp.connect(self.host, self.port)
ftp.login(self.username, self.password)
@@ -128,3 +177,187 @@ class FtpController:
:return:
"""
self.ftp.delete(delpath)
def transfer_files(
source_ftp_info,
target_ftp_info,
source_dir,
target_dir,
end_with=None,
match_str=None,
keep_dir=False):
"""
从源FTP服务器下载所有{end_with}结尾的文件并上传到目标FTP服务器,保持目录结构
Args:
source_ftp_info: dict, 源FTP连接信息 {host, port, username, password}
target_ftp_info: dict, 目标FTP连接信息 {host, port, username, password}
source_dir: str, 源FTP上的起始目录
target_dir: str, 目标FTP上的目标目录
keep_dir: bool, 是否保持目录结构,默认False
"""
transfered_file_list = []
try:
# 连接源FTP
source_ftp = FtpController(
source_ftp_info['host'],
source_ftp_info['port'],
source_ftp_info['username'],
source_ftp_info['password']
)
if not source_ftp.ftp:
raise Exception("编程文件FTP连接失败")
source_ftp.ftp.set_pasv(1)
# 连接目标FTP
target_ftp = FtpController(
target_ftp_info['host'],
target_ftp_info['port'],
target_ftp_info['username'],
target_ftp_info['password']
)
if not source_ftp.ftp:
raise Exception("机床FTP连接失败")
source_ftp.ftp.set_pasv(1)
# 递归遍历源目录
def traverse_dir(current_dir, relative_path=''):
source_ftp.ftp.cwd(current_dir)
file_list = source_ftp.ftp.nlst()
for item in file_list:
try:
# 尝试进入目录
source_ftp.ftp.cwd(f"{current_dir}/{item}")
# 如果成功则是目录
new_relative_path = os.path.join(relative_path, item)
# 在目标FTP创建对应目录
try:
if keep_dir:
target_ftp.ftp.mkd(f"{target_dir}/{new_relative_path}")
except:
pass # 目录可能已存在
# 递归遍历子目录
traverse_dir(f"{current_dir}/{item}", new_relative_path)
source_ftp.ftp.cwd('..')
except:
matched = False
# 文件名匹配字符串BT30-(两个字符)-all.nc, 例6667_20250422-BT30-ZM-all.nc
if match_str and re.match(match_str, item):
matched = True
elif end_with and item.lower().endswith(end_with):
matched = True
if matched:
# 下载到临时文件
temp_path = f"/tmp/{item}"
with open(temp_path, 'wb') as f:
source_ftp.ftp.retrbinary(f'RETR {item}', f.write)
# 上传到目标FTP对应目录
if keep_dir:
target_path = f"{target_dir}/{relative_path}/{item}"
else:
target_path = f"{target_dir}/{item}"
# 规范化路径
target_path = target_path.replace('\\', '/').strip('/')
# 确保目标目录存在
target_dir_path = '/'.join(target_path.split('/')[:-1])
try:
target_ftp.ftp.cwd('/') # 回到根目录
for dir_part in target_dir_path.split('/'):
if dir_part:
try:
target_ftp.ftp.cwd(dir_part)
except:
try:
target_ftp.ftp.mkd(dir_part)
target_ftp.ftp.cwd(dir_part)
except Exception as e:
logging.error(f"创建目录失败 {dir_part}: {str(e)}")
raise
except Exception as e:
logging.error(f"处理目标目录失败: {str(e)}")
raise
# 检查FTP连接状态
try:
target_ftp.ftp.voidcmd('NOOP')
except:
logging.error("FTP连接已断开尝试重新连接")
target_ftp.ftp.connect(target_ftp_info['host'], target_ftp_info['port'])
target_ftp.ftp.login(target_ftp_info['username'], target_ftp_info['password'])
# 上传文件
try:
with open(temp_path, 'rb') as f:
# 检查文件是否可读
content = f.read()
if not content:
raise Exception("临时文件为空")
f.seek(0) # 重置文件指针
target_ftp.ftp.storbinary(f'STOR {target_path}', f)
except Exception as e:
logging.error(f"上传文件失败: {str(e)}")
logging.error(f"目标路径: {target_path}")
raise
transfered_file_list.append(item)
# 删除临时文件
os.remove(temp_path)
logging.info(f"已传输文件: {item}")
# 清空目标目录下的所有内容
# try:
# target_ftp.ftp.cwd(target_dir)
# files = target_ftp.ftp.nlst()
# for f in files:
# try:
# # 尝试删除文件
# target_ftp.ftp.delete(f)
# except:
# try:
# # 如果删除失败,可能是目录,递归删除目录
# def remove_dir(path):
# target_ftp.ftp.cwd(path)
# sub_files = target_ftp.ftp.nlst()
# for sf in sub_files:
# try:
# target_ftp.ftp.delete(sf)
# except:
# remove_dir(f"{path}/{sf}")
# target_ftp.ftp.cwd('..')
# target_ftp.ftp.rmd(path)
# remove_dir(f"{target_dir}/{f}")
# except:
# logging.error(f"无法删除 {f}")
# pass
# logging.info(f"已清空目标目录 {target_dir}")
# except Exception as e:
# logging.error(f"清空目标目录失败: {str(e)}")
# raise Exception(f"清空目标目录失败: {str(e)}")
# 开始遍历
traverse_dir(source_dir)
logging.info("所有文件传输完成")
return transfered_file_list
except Exception as e:
logging.error(f"传输过程出错: {str(e)}")
raise e
finally:
# 关闭FTP连接
try:
source_ftp.ftp.quit()
target_ftp.ftp.quit()
except:
pass

View File

@@ -2,6 +2,8 @@
import json
import base64
import logging
import qrcode
from io import BytesIO
from datetime import timedelta
import requests
from odoo.addons.sf_base.commons.common import Common
@@ -826,6 +828,34 @@ class SfMaintenanceEquipment(models.Model):
image_lq_id = fields.Many2many('maintenance.equipment.image', 'equipment_lq_id', string='冷却方式',
domain="[('type', '=', '冷却方式')]")
ftp_host = fields.Char('FTP 主机')
ftp_port = fields.Char('FTP 端口')
ftp_username = fields.Char('FTP 用户名')
ftp_password = fields.Char('FTP 密码')
qr_code_image = fields.Binary(string='二维码', compute='_generate_qr_code')
@api.depends('name')
def _generate_qr_code(self):
for record in self:
# Generate QR code
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10,
border=4,
)
qr.add_data(record.MTcode)
qr.make(fit=True)
qr_image = qr.make_image(fill_color="black", back_color="white")
# Encode the image data in base64
image_stream = BytesIO()
qr_image.save(image_stream, format="PNG")
encoded_image = base64.b64encode(image_stream.getvalue())
record.qr_code_image = encoded_image
class SfRobotAxisNum(models.Model):
_name = 'sf.robot.axis.num'

View File

@@ -1053,6 +1053,21 @@
</page>
</xpath>
<xpath expr="//group/field[@name='location']" position="after">
<field name="qr_code_image" widget="image" readonly="1" attrs="{'invisible': [('equipment_type', '!=', '机床')]}" />
</xpath>
<xpath expr="//page[@name='maintenance']" position="after">
<page name="network_config" string="网络配置" attrs="{'invisible': [('equipment_type', '!=', '机床')]}" >
<group>
<group string="ftp配置">
<field name="ftp_host" string="主机"/>
<field name="ftp_port" string="端口"/>
<field name="ftp_username" string="用户名"/>
<field name="ftp_password" string="密码" password="True"/>
</group>
</group>
</page>
</xpath>
</data>
</field>
</record>

View File

@@ -10,7 +10,8 @@
""",
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
'depends': ['sf_base', 'sf_maintenance', 'web_widget_model_viewer', 'sf_warehouse','jikimo_attachment_viewer', 'jikimo_sale_multiple_supply_methods'],
'depends': ['sf_base', 'sf_maintenance', 'web_widget_model_viewer', 'sf_warehouse', 'jikimo_attachment_viewer',
'jikimo_sale_multiple_supply_methods', 'product'],
'data': [
'data/cron_data.xml',
'data/stock_data.xml',
@@ -18,6 +19,7 @@
'data/panel_data.xml',
'data/sf_work_individuation_page.xml',
'data/agv_scheduling_data.xml',
'data/product_data.xml',
'security/group_security.xml',
'security/ir.model.access.csv',
'wizard/workpiece_delivery_views.xml',
@@ -28,6 +30,7 @@
'wizard/mrp_workorder_batch_replan_wizard_views.xml',
'wizard/sf_programming_reason_views.xml',
'wizard/sale_order_cancel_views.xml',
'wizard/process_outsourcing.xml',
'views/mrp_views_menus.xml',
'views/agv_scheduling_views.xml',
'views/stock_lot_views.xml',
@@ -44,6 +47,8 @@
'views/sale_order_views.xml',
'views/mrp_workorder_batch_replan.xml',
'views/purchase_order_view.xml',
'views/product_template_views.xml',
# 'views/stock_warehouse_orderpoint.xml',
],
'assets': {

View File

@@ -6,12 +6,14 @@ from datetime import datetime
from odoo.addons.sf_manufacturing.models.agv_scheduling import RepeatTaskException
from odoo import http
from odoo.http import request
from odoo.addons.sf_base.decorators.api_log import api_log
class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/GetWoInfo', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
@api_log('获取工单', requester='中控系统')
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,
cors="*")
@api_log('获取日计划', requester='中控系统')
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,
cors="*")
@api_log('工件预调(前置三元检测)', requester='中控系统')
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,
cors="*")
@api_log('工单开始', requester='中控系统')
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,
cors="*")
@api_log('工单结束', requester='中控系统')
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,
cors="*")
@api_log('零件检测(后置三元检测)', requester='中控系统')
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,
cors="*")
@api_log('CMM测量程序下载', requester='中控系统')
def CMMProgDolod(self, **kw):
"""
中控系统传递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,
cors="*")
@api_log('CAM加工程序下载', requester='中控系统')
def NCProgDolod(self, **kw):
"""
中控系统传递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,
cors="*")
@api_log('库位变更', requester='中控系统')
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,
cors="*")
@api_log('AGV运送上产线', requester='中控系统')
def AGVToProduct(self, **kw):
"""
AGV运送上产线完成
@@ -552,6 +563,7 @@ class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/AGVDownProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
@api_log('AGV运送下产线', requester='中控系统')
def AGVDownProduct(self, **kw):
"""
MES调度AGV搬运零件AGV托盘到产线接驳站。

View File

@@ -45,6 +45,9 @@ class JikimoSaleRoutePicking(Sf_Bf_Connect):
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)
i += 1
if kw.get('contract_file_name') and kw.get('contract_file') and kw.get('contract_code'):
order_id.create_sale_documents(kw.get('contract_file_name'), kw.get('contract_file'))
order_id.write({'contract_code': kw.get('contract_code')})
res['factory_order_no'] = order_id.name
order_id.confirm_to_supply_method()
except Exception as e:

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data noupdate="1">
<record id="product_category_service" model="product.category">
<field name="name">服务</field>
<field name="parent_id" ref="product.product_category_all"/>
<field name="property_cost_method">fifo</field>
<field name="property_valuation">manual_periodic</field>
</record>
<record id="product_category_outsource_process" model="product.category">
<field name="name">工序外协</field>
<field name="parent_id" ref="sf_manufacturing.product_category_service"/>
<field name="property_cost_method">fifo</field>
<field name="property_valuation">manual_periodic</field>
</record>
<record id="product_category_outsource_other_process" model="product.category">
<field name="name">其他</field>
<field name="parent_id" ref="sf_manufacturing.product_category_outsource_process"/>
<field name="property_cost_method">fifo</field>
<field name="property_valuation">manual_periodic</field>
</record>
</data>
</odoo>

View File

@@ -16,4 +16,6 @@ from . import sf_production_common
from . import sale_order
from . import quick_easy_order
from . import purchase_order
from . import quality_check
from . import quality_check
from . import purchase_request_line
# from . import stock_warehouse_orderpoint

View File

@@ -87,11 +87,12 @@ class AgvScheduling(models.Model):
agv_route_type: AGV任务类型
workorders: 工单
"""
scheduling = None
_logger.info('创建AGV调度任务\r\n起点为【%s】,任务类型为【%s】,工单为【%s' % (agv_start_site_name, agv_route_type, workorders))
if not workorders:
raise UserError(_('工单不能为空'))
agv_start_site = self.env['sf.agv.site'].sudo().search([('name', '=', agv_start_site_name)], limit=1)
if not agv_start_site:
agv_start_sites = self.env['sf.agv.site'].sudo().search([('name', '=', agv_start_site_name)])
if not agv_start_sites:
raise UserError(_('不存在名称为【%s】的接驳站,请先创建!' % agv_start_site_name))
# 如果存在相同任务类型工单的AGV调度任务则提示错误
agv_scheduling = self.sudo().search([
@@ -107,24 +108,32 @@ class AgvScheduling(models.Model):
(','.join(repetitive_workorders.mapped('production_id.name')), agv_scheduling.name)
)
# 如果只有唯一任务路线,则自动赋予终点接驳站跟任务名称
agv_routes = self.env['sf.agv.task.route'].sudo().search([
('route_type', '=', agv_route_type),
('start_site_id', 'in', agv_start_sites.ids)
])
vals = {
'start_site_id': agv_start_site.id,
'agv_route_type': agv_route_type,
'workorder_ids': workorders.ids,
# 'workpiece_delivery_ids': deliveries.mapped('id') if deliveries else [],
'task_create_time': fields.Datetime.now()
}
# 如果只有唯一任务路线,则自动赋予终点接驳站跟任务名称
agv_routes = self.env['sf.agv.task.route'].sudo().search([
('route_type', '=', agv_route_type),
('start_site_id', '=', agv_start_site.id)
])
if not agv_routes:
raise UserError(_('不存在起点为【%s】的【%s】任务路线,请先创建!' % (agv_start_site_name, agv_route_type)))
# 如果路线中包含起点与终点相同的接驳站则不创建AGV调度任务
if agv_routes.filtered(lambda r: r.start_site_id.name == r.end_site_id.name):
return True
# 配送类型相同的接驳站为同一个,取第一个即可
vals.update({
'start_site_id': agv_routes[0].start_site_id.id,
})
idle_route = None
if len(agv_routes) == 1:
idle_route = agv_routes[0]
vals.update({'end_site_id': idle_route.end_site_id.id, 'agv_route_id': idle_route.id})
vals.update({
'end_site_id': idle_route.end_site_id.id, 'agv_route_id': idle_route.id
})
else:
# 判断终点接驳站是否为空闲
idle_routes = agv_routes.filtered(lambda r: r.end_site_id.state == '空闲')
@@ -132,7 +141,10 @@ class AgvScheduling(models.Model):
# 将空闲的路线按照终点接驳站名称排序
idle_routes = sorted(idle_routes, key=lambda r: r.end_site_id.name)
idle_route = idle_routes[0]
vals.update({'end_site_id': idle_route.end_site_id.id, 'agv_route_id': idle_route.id})
vals.update({
'end_site_id': idle_route.end_site_id.id, 'agv_route_id': idle_route.id
})
try:
scheduling = self.env['sf.agv.scheduling'].sudo().create(vals)
# 触发空闲接驳站状态更新,触发新任务下发
@@ -142,7 +154,7 @@ class AgvScheduling(models.Model):
except Exception as e:
_logger.error('添加AGV调度任务失败: %s', e)
raise UserError(_('添加AGV调度任务失败: %s', e))
return scheduling
def on_site_state_change(self, agv_site_id, agv_site_state):

View File

@@ -24,7 +24,7 @@ class AgvSetting(models.Model):
# name必须唯一
_sql_constraints = [
('name_uniq', 'unique (name)', '站点编号必须唯一!'),
('name_uniq', 'unique (name, workcenter_id)', '同一工作中心的站点编号必须唯一!'),
]
# def update_site_state(self):
@@ -68,11 +68,12 @@ class AgvSetting(models.Model):
"""
if isinstance(agv_site_state_arr, dict):
for agv_site_name, is_occupy in agv_site_state_arr.items():
agv_site = self.env['sf.agv.site'].sudo().search([('name', '=', agv_site_name)])
if agv_site:
agv_site.state = is_occupy
agv_sites = self.env['sf.agv.site'].sudo().search([('name', '=', agv_site_name)])
if agv_sites:
agv_sites.state = is_occupy
if notify:
self.env['sf.agv.scheduling'].on_site_state_change(agv_site.id, agv_site.state)
for agv_site in agv_sites:
self.env['sf.agv.scheduling'].on_site_state_change(agv_site.id, agv_site.state)
else:
_logger.error("更新失败:接驳站站点错误!%s" % agv_site_name)
raise UserError("更新失败:接驳站站点错误!")

View File

@@ -6,6 +6,7 @@ import json
import os
import re
import traceback
from operator import itemgetter
import requests
from itertools import groupby
@@ -238,7 +239,8 @@ class MrpProduction(models.Model):
programming_no = fields.Char('编程单号')
work_state = fields.Char('业务状态')
programming_state = fields.Selection(
[('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'), ('已下发', '已下发'), ('已取消', '已取消')],
[('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'), ('已下发', '已下发'),
('已取消', '已取消')],
string='编程状态',
tracking=True)
glb_file = fields.Binary("glb模型文件")
@@ -267,6 +269,7 @@ class MrpProduction(models.Model):
quality_standard = fields.Binary('质检标准', related='product_id.quality_standard', readonly=True)
part_name = fields.Char(string='零件名称', compute='_compute_part_info', store=True)
@api.depends('product_id')
def _compute_part_info(self):
try:
@@ -276,7 +279,7 @@ class MrpProduction(models.Model):
production_id.part_name = production_id.product_id.part_name
elif production_id.product_id.categ_id.type == '坯料':
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:
product_name = match.group(0)
@@ -400,8 +403,10 @@ class MrpProduction(models.Model):
and production.schedule_state == '已排' and production.is_rework is False):
production.state = 'pending_cam'
if any((wo.test_results == '返工' and wo.state == 'done' and
(production.programming_state in ['已编程'] or(wo.individuation_page_list and 'PTD' in wo.individuation_page_list)))
or (wo.is_rework is True and wo.state == 'done' and production.programming_state in ['编程中', '已编程'])
(production.programming_state in ['已编程'] or (
wo.individuation_page_list and 'PTD' in wo.individuation_page_list)))
or (wo.is_rework is True and wo.state == 'done' and production.programming_state in ['编程中',
'已编程'])
for wo in production.workorder_ids) or production.is_rework is True:
production.state = 'rework'
if any(wo.test_results == '报废' and wo.state == 'done' for wo in production.workorder_ids):
@@ -617,7 +622,7 @@ class MrpProduction(models.Model):
for rp in reproduction:
if rp.programming_no == item['programming_no']:
rp.write({'programming_state': '已编程未下发' if item[
'programming_state'] == '已编程' else '编程中'})
'programming_state'] == '已编程' else '编程中'})
else:
return item
@@ -631,13 +636,17 @@ class MrpProduction(models.Model):
# 增加触发时间参数
def update_programming_state(self, trigger_time=None, reprogramming_reason=None):
try:
reason = ""
manufacturing_type = None
if self.is_scrap:
manufacturing_type = 'scrap'
reason = "报废"
elif self.tool_state == '2':
manufacturing_type = 'invalid_tool_rework'
reason = "无效功能刀具"
elif self.is_rework:
manufacturing_type = 'rework'
reason = "返工"
res = {'programming_no': self.programming_no,
'manufacturing_type': manufacturing_type,
'trigger_time': trigger_time,
@@ -652,6 +661,16 @@ class MrpProduction(models.Model):
result = json.loads(ret['result'])
logging.info('update_programming_state-ret:%s' % result)
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})
else:
raise UserError(ret['message'])
@@ -782,6 +801,17 @@ class MrpProduction(models.Model):
if ret['status'] == 1:
self.write(
{'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:
raise UserError(ret['message'])
except Exception as e:
@@ -889,11 +919,48 @@ class MrpProduction(models.Model):
workorders_values.append(
self.env[
'mrp.workorder']._json_workorder_surface_process_str(
production, route, product_production_process.seller_ids[0].partner_id.id))
production, route, product_production_process.seller_ids[
0].partner_id.id if product_production_process.seller_ids else False))
production.workorder_ids = workorders_values
for workorder in production.workorder_ids:
workorder.duration_expected = workorder._get_duration_expected()
# def _create_subcontract_purchase_request(self, purchase_request_line):
# sorted_list = sorted(purchase_request_line, key=itemgetter('name'))
# grouped_purchase_request_line = {
# k: list(g)
# for k, g in groupby(sorted_list, key=itemgetter('name'))
# }
# for name, request_line in grouped_purchase_request_line.items():
# request_line_sorted_list = sorted(request_line, key=itemgetter('product_id'))
# grouped_purchase_request_line_sorted_list = {
# k: list(g)
# for k, g in groupby(request_line_sorted_list, key=itemgetter('product_id'))
# }
# purchase_request_model = self.env["purchase.request"]
# origin = ", ".join({item['production_name'] for item in request_line_sorted_list if item.get('production_name')})
# pr = purchase_request_model.create({
# "origin": origin,
# "company_id": self.company_id.id,
# "picking_type_id": self.env.ref('stock.picking_type_in').id,
# "group_id": request_line[0].get('group_id'),
# "requested_by": self.env.context.get("uid", self.env.uid),
# "assigned_to": False,
# "bom_id": self[0].bom_id.id,
# "is_subcontract":True,
# })
# self[0].bom_id.bom_line_ids.product_id.route_ids = [(4,self.env.ref(
# 'sf_stock.stock_route_process_outsourcing').id)]
# for product_id, request_line_list in grouped_purchase_request_line_sorted_list.items():
# cur_request_line = request_line_list[0]
# # cur_request_line['product_qty'] = cur_request_line['product_qty']
# 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.pop('group_id', None)
# cur_request_line.pop('production_name', None)
# self.env["purchase.request.line"].create(cur_request_line)
# pr.button_approved()
# 外协出入库单处理
def get_subcontract_pick_purchase(self):
production_all = self.sorted(lambda x: x.id)
@@ -903,6 +970,8 @@ class MrpProduction(models.Model):
for product_id, pd in grouped_product_ids.items():
product_id_to_production_names[product_id] = [p.name for p in pd]
sorted_workorders = None
purchase_request_line = []
all_workorders = []
for production in production_all:
proc_workorders = []
process_parameter_workorder = self.env['mrp.workorder'].search(
@@ -920,7 +989,12 @@ class MrpProduction(models.Model):
for workorders in reversed(sorted_workorders):
self.env['stock.picking'].create_outcontract_picking(workorders, production, sorted_workorders)
self.env['purchase.order'].get_purchase_order(workorders, production, product_id_to_production_names)
# purchase_request_line = purchase_request_line + self.env['purchase.order'].get_purchase_request(
# workorders, production)
# all_workorders += workorders
# self._create_subcontract_purchase_request(purchase_request_line)
# for workorder in all_workorders:
# workorder._compute_pr_mp_count()
# 工单排序
def _reset_work_order_sequence1(self, k):
for rec in self:
@@ -1341,7 +1415,7 @@ class MrpProduction(models.Model):
'default_reprogramming_num': cloud_programming.get('reprogramming_num') if cloud_programming else '',
'default_programming_state': cloud_programming.get('programming_state') if cloud_programming else '',
'default_is_reprogramming': True if cloud_programming and (
cloud_programming.get('programming_state') in ['已下发']) else False
cloud_programming.get('programming_state') in ['已下发']) else False
}
}
@@ -1723,12 +1797,12 @@ class MrpProduction(models.Model):
"""
检查前置条件:制造订单【状态】=“待排程、待加工”,制造订单的【编程状态】=“已编程”。
"""
print('申请编程')
if len(self) > 1:
raise UserError('仅支持选择单个制造订单进行编程申请,请重新选择')
for production in self:
if production.state not in ['confirmed', 'pending_cam'] or production.programming_state != '已编程':
raise UserError('不可操作。所选制造订单必须同时满足如下条件:\n1、制造订单状态待排程 或 待加工;\n2、制造订单编程状态已编程。\n请检查!')
raise UserError(
'不可操作。所选制造订单必须同时满足如下条件:\n1、制造订单状态待排程 或 待加工;\n2、制造订单编程状态已编程。\n请检查!')
cloud_programming = production._cron_get_programming_state()
if cloud_programming['programming_state'] in ['待编程', '已编程', '编程中']:
raise UserError("当前编程单正在重新编程,请注意查看当前制造订单的“编程记录”确认进度!")
@@ -1771,6 +1845,8 @@ class MrpProduction(models.Model):
except Exception as e:
logging.info('update_programming_state error:%s' % e)
raise UserError("更新编程单状态失败,请联系管理员")
model_id = fields.Char('模型ID', related='product_id.model_id')
# 编程记录
@@ -1789,6 +1865,7 @@ class sf_programming_record(models.Model):
target_production_id = fields.Char('目标制造单号')
apply_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):

View File

@@ -1,6 +1,7 @@
import logging
from odoo import fields, models, api
from odoo.exceptions import UserError
# from odoo.tools import str2bool
class ResMrpRoutingWorkcenter(models.Model):
@@ -24,10 +25,41 @@ class ResMrpRoutingWorkcenter(models.Model):
workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_route', required=True)
bom_id = fields.Many2one('mrp.bom', required=False)
surface_technics_id = fields.Many2one('sf.production.process', string="表面工艺")
# optional_process_parameters = fields.One2many('sf.production.process.parameter','routing_id',string='可选工艺参数')
reserved_duration = fields.Float('预留时长', default=30, tracking=True)
is_outsource = fields.Boolean('外协', default=False)
individuation_page_ids = fields.Many2many('sf.work.individuation.page', string='个性化记录')
# @api.onchange('surface_technics_id')
# def optional_process_parameters_date(self):
# for record in self:
# if not record.surface_technics_id:
# continue
# parameter_ids = self.env['sf.production.process.parameter'].search([
# ('process_id', '=', record.surface_technics_id.id),
# ])
# record.optional_process_parameters = parameter_ids.ids
# @api.model
# def _auto_init(self):
# # 先执行标准初始化
# res = super(ResMrpRoutingWorkcenter, self)._auto_init()
# # 然后执行自定义初始化
# records = self.search([])
# if str2bool(self.env['ir.config_parameter'].get_param('sf.production.process.parameter.is_init_workcenter',
# default='False')):
# return
# records.optional_process_parameters_date()
# self.env['ir.config_parameter'].set_param('sf.production.process.parameter.is_init_workcenter', True)
# return res
# def init(self):
# super(ResMrpRoutingWorkcenter, self).init()
# # 在模块初始化时触发计算字段的更新
# records = self.search([])
# if str2bool(self.env['ir.config_parameter'].get_param('sf.production.process.parameter.is_init_workcenter',default='False')):
# return
# records.optional_process_parameters_date()
# self.env['ir.config_parameter'].set_param('sf.production.process.parameter.is_init_workcenter', True)
def get_no(self):
international_standards = self.search(
[('code', '!=', ''), ('active', 'in', [True, False])],

View File

@@ -21,7 +21,16 @@ class ResWorkcenter(models.Model):
related='equipment_id.production_line_id', store=True)
is_process_outsourcing = fields.Boolean('工艺外协')
users_ids = fields.Many2many("res.users", 'users_workcenter', tracking=True)
# @api.constrains('name')
# def _check_unique_name_code(self):
# for record in self:
# # 检查是否已经存在相同的 name 和 code 组合
# existing = self.search([
# ('name', '=', record.name),
# ('id', '!=', record.id) # 排除当前记录
# ])
# if existing:
# raise ValueError('记录已存在')
def write(self, vals):
if 'users_ids' in vals:
old_users = self.users_ids
@@ -247,14 +256,12 @@ class ResWorkcenter(models.Model):
date_planned_end),
('state', 'not in', ['draft', 'cancel'])])
if plan_ids:
sum_qty = sum([p.product_qty for p in plan_ids])
production_line_hour_capacity = self.production_line_hour_capacity
if sum_qty >= production_line_hour_capacity:
message = '当前计划开始时间不能预约排程,超过生产线小时产能(%d件)%d' % (
production_line_hour_capacity, count)
raise UserError(message)
return False
sum_qty = sum([p.product_qty for p in plan_ids]) if plan_ids else count
production_line_hour_capacity = self.production_line_hour_capacity
if sum_qty > production_line_hour_capacity:
message = '当前计划开始时间不能预约排程,超过生产线小时产能(%d件)%d' % (
production_line_hour_capacity, count)
raise UserError(message)
return True

View File

@@ -19,8 +19,8 @@ from odoo.addons.sf_mrs_connect.models.ftp_operate import FtpController
class ResMrpWorkOrder(models.Model):
_inherit = 'mrp.workorder'
_order = 'sequence asc'
_description = '工单'
_order = 'sequence'
product_tmpl_name = fields.Char('坯料产品名称', related='production_bom_id.bom_line_ids.product_id.name')
@@ -70,6 +70,21 @@ class ResMrpWorkOrder(models.Model):
delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效',
tracking=True)
back_button_display = fields.Boolean(default=False, compute='_compute_back_button_display', store=True)
# pr_mp_count = fields.Integer('采购申请单数量', compute='_compute_pr_mp_count', store=True)
# @api.depends('state')
# def _compute_pr_mp_count(self):
# for item in self:
# if not item.is_subcontract:
# item.pr_mp_count = 0
# continue
# pr_ids = self.env['purchase.request'].sudo().search(
# [('origin', 'like', item.production_id.name), ('is_subcontract', '=', 'True'),
# ('state', '!=', 'rejected')])
# if pr_ids:
# item.pr_mp_count = len(pr_ids)
# else:
# item.pr_mp_count = 0
@api.depends('state')
def _compute_back_button_display(self):
@@ -106,27 +121,33 @@ class ResMrpWorkOrder(models.Model):
record.back_button_display = False
# tag_type
if cur_workorder.is_subcontract or cur_workorder.routing_type == '解除装夹' or cur_workorder.routing_type == '切割' or any(
detection_result.processing_panel == cur_workorder.processing_panel and
detection_result.routing_type == cur_workorder.routing_type and
cur_workorder.tag_type !='重新加工' and
detection_result.test_results != '合格'
for detection_result in cur_workorder.production_id.detection_result_ids
detection_result.processing_panel == cur_workorder.processing_panel and
detection_result.routing_type == cur_workorder.routing_type and
cur_workorder.tag_type != '重新加工' and
detection_result.test_results != '合格'
for detection_result in cur_workorder.production_id.detection_result_ids
):
record.back_button_display = False
else:
next_workorder = sorted_workorders[position + 1]
next_state = next_workorder.state
if (next_state == 'ready' or (
# 持续获取下一个工单,直到找到一个不是返工的工单
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':
record.back_button_display = True
else:
record.back_button_display = False
if cur_workorder.is_subcontract or cur_workorder.routing_type == '解除装夹' or cur_workorder.routing_type == '切割' or any(
detection_result.processing_panel == cur_workorder.processing_panel and
detection_result.routing_type == cur_workorder.routing_type and
cur_workorder.tag_type !='重新加工' and
detection_result.test_results != '合格'
for detection_result in cur_workorder.production_id.detection_result_ids
detection_result.processing_panel == cur_workorder.processing_panel and
detection_result.routing_type == cur_workorder.routing_type and
cur_workorder.tag_type != '重新加工' and
detection_result.test_results != '合格'
for detection_result in cur_workorder.production_id.detection_result_ids
):
record.back_button_display = False
@@ -425,13 +446,15 @@ class ResMrpWorkOrder(models.Model):
action['context'] = dict(self._context, default_origin=self.name)
return action
@api.depends('state', 'production_id.name')
def _compute_surface_technics_purchase_ids(self):
for order in self:
if order.routing_type == '表面工艺' and order.state not in ['cancel']:
domain = [('purchase_type', '=', 'consignment'),
('origin', 'like', '%' + self.production_id.name + '%'),
('state', '!=', 'cancel')]
# domain = [('purchase_type', '=', 'consignment'),
# ('origin', 'like', '%' + self.production_id.name + '%'),
# ('state', '!=', 'cancel')]
purchase = self.env['purchase.order'].search(domain)
order.surface_technics_purchase_count = 0
if not purchase:
@@ -444,6 +467,31 @@ class ResMrpWorkOrder(models.Model):
else:
order.surface_technics_purchase_count = 0
# def action_view_pr_mrp_workorder(self):
# """
# 采购请求
# """
# self.ensure_one()
# pr_ids = self.env['purchase.request'].sudo().search(
# [('origin', 'like', self.production_id.name), ('is_subcontract', '=', 'True'),
# ('state', '!=', 'rejected')])
# action = {
# 'res_model': 'purchase.request',
# 'type': 'ir.actions.act_window',
# }
# if len(pr_ids) == 1:
# action.update({
# 'view_mode': 'form',
# 'res_id': pr_ids[0].id,
# })
# else:
# action.update({
# 'name': _("从 %s生成采购请求单", self.name),
# 'domain': [('id', 'in', pr_ids)],
# 'view_mode': 'tree,form',
# })
# return action
def action_view_surface_technics_purchase(self):
self.ensure_one()
# if self.routing_type == '表面工艺':
@@ -472,14 +520,16 @@ class ResMrpWorkOrder(models.Model):
return result
def _get_surface_technics_purchase_ids(self):
domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment')]
purchase_orders = self.env['purchase.order'].search(domain)
domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment'),
('state', '!=', 'cancel')]
# domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('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_id = self.env['purchase.order']
for po in purchase_orders:
for line in po.order_line:
if line.product_id.server_product_process_parameters_id == self.surface_technics_parameters_id:
if line.product_qty == 1:
purchase_orders_id = line.order_id
purchase_orders_id = line.order_id
return purchase_orders_id
supplier_id = fields.Many2one('res.partner', string='外协供应商')
@@ -694,21 +744,25 @@ class ResMrpWorkOrder(models.Model):
# self.workpiece_delivery_ids[0].write({'rfid_code': self.rfid_code})
def get_plan_workorder(self, production_line):
tomorrow = (date.today() + timedelta(days=+1)).strftime("%Y-%m-%d")
tomorrow_start = tomorrow + ' 00:00:00'
tomorrow_end = tomorrow + ' 23:59:59'
tomorrow = (date.today() + timedelta(days=1)).strftime("%Y-%m-%d")
tomorrow_start = f"{tomorrow} 00:00:00"
tomorrow_end = f"{tomorrow} 23:59:59"
logging.info('tomorrow:%s' % tomorrow)
sql = """
SELECT *
FROM mrp_workorder
WHERE state!='rework'
to_char(date_planned_start::timestamp + '8 hour','YYYY-MM-DD HH:mm:SS')>= %s
AND to_char(date_planned_finished::timestamp + '8 hour','YYYY-MM-DD HH:mm:SS')<= %s
AND (date_planned_start + interval '8 hours') >= %s::timestamp
AND (date_planned_finished + interval '8 hours') <= %s::timestamp
"""
params = [tomorrow_start, tomorrow_end]
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"
params.append(production_line)
params.append(line.id)
self.env.cr.execute(sql, params)
ids = [t[0] for t in self.env.cr.fetchall()]
return [('id', 'in', ids)]
@@ -1200,8 +1254,17 @@ class ResMrpWorkOrder(models.Model):
'cmm_ids': production.workorder_ids.filtered(lambda t: t.routing_type == 'CNC加工').cmm_ids,
}]
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):
for workorder in self:
sorted_workorders = sorted(self, key=lambda x: x.sequence)
for workorder in sorted_workorders:
# 如果工单的工序没有进行排序则跳出循环
if workorder.production_id.workorder_ids.filtered(lambda wk: wk.sequence == 0):
continue
@@ -1220,11 +1283,18 @@ class ResMrpWorkOrder(models.Model):
workorder.state = 'pending'
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 == '人工线下加工'
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.schedule_state == '已排'):
# and workorder.production_id.programming_state == '已编程'
if workorder.is_subcontract is True:
if workorder.production_id.state == 'rework':
workorder.state = 'waiting'
@@ -1232,6 +1302,9 @@ class ResMrpWorkOrder(models.Model):
purchase_orders_id = self._get_surface_technics_purchase_ids()
if purchase_orders_id.state == 'purchase':
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]
for mo in move_out:
if mo.state != 'done':
@@ -1260,13 +1333,6 @@ class ResMrpWorkOrder(models.Model):
workorder.state = 'ready'
elif workorder.state != 'waiting':
workorder.state = 'waiting'
# =========== 特殊工艺工单处理 ===================
# if workorder.routing_type == '表面工艺' and workorder.is_subcontrac:
# purchase_order = self.env['purchase.order'].search(
# [('origin', 'ilike', workorder.production_id.name)])
# if purchase_order.picking_ids.filtered(lambda p: p.state in ['waiting', 'confirmed', 'assigned']):
# workorder.state = 'waiting'
# continue
if workorder.technology_design_id.routing_tag == 'special':
if workorder.is_subcontract is False:
workorder.state = 'ready'
@@ -1279,6 +1345,10 @@ class ResMrpWorkOrder(models.Model):
if purchase_orders_id.state == 'purchase':
workorder.state = 'ready'
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:
if mo.state != 'done':
mo.write({'state': 'assigned', 'production_id': False})
@@ -1287,6 +1357,7 @@ class ResMrpWorkOrder(models.Model):
mo.get_move_line(workorder.production_id, workorder))
else:
workorder.state = 'waiting'
@api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state',
'production_id.tool_state', 'production_id.schedule_state', 'sequence',
'production_id.programming_state')
@@ -1301,6 +1372,7 @@ class ResMrpWorkOrder(models.Model):
for check_id in workorder.check_ids:
if not check_id.is_inspect:
check_id.quality_state = 'none'
# 重写工单开始按钮方法
def button_start(self):
# 判断工单状态是否为等待组件
@@ -1317,11 +1389,11 @@ class ResMrpWorkOrder(models.Model):
# 判断是否有坯料的序列号信息
boolean = False
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[0].lot_id.name:
boolean = True
if self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name:
boolean = True
else:
boolean = True
if not boolean:
@@ -1351,6 +1423,10 @@ class ResMrpWorkOrder(models.Model):
if self.routing_type == '表面工艺':
if self.is_subcontract is True:
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(
# [('location_id', '=', self.env['stock.location'].search(
# [('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
@@ -1516,25 +1592,17 @@ class ResMrpWorkOrder(models.Model):
len(done_workorder) == len(
record.production_id.workorder_ids.filtered(lambda wo: wo.state != 'cancel'))):
is_production_id = True
if record.routing_type in ['解除装夹'] or (
record.is_rework is True and record.routing_type in ['装夹预调']):
for workorder in record.production_id.workorder_ids:
if workorder.processing_panel == record.processing_panel:
rfid_code = workorder.rfid_code
if record.is_rework is not True:
workorder.write({'rfid_code_old': rfid_code, 'rfid_code': False})
elif workorder.routing_type != '装夹预调' and workorder.state != 'rework':
workorder.write({'rfid_code_old': False, 'rfid_code': False})
elif workorder.routing_type == '装夹预调' and workorder.state != 'rework':
workorder.write({'rfid_code_old': rfid_code, 'rfid_code': False})
self.env['stock.lot'].sudo().search([('rfid', '=', rfid_code)]).write(
{'tool_material_status': '可用'})
if workorder.rfid_code:
raise ValidationError(f'{workorder.name}】工单解绑失败,请重新点击完成按钮!!!')
# workorder.rfid_code_old = rfid_code
# workorder.rfid_code = False
logging.info('workorder.rfid_code:%s' % workorder.rfid_code)
if record.routing_type in ['解除装夹']:
rfid_code = record.rfid_code
work_ids = record.production_id.workorder_ids.filtered(
lambda wo: wo.processing_panel == record.processing_panel and wo.state != 'rework')
work_ids.write({'rfid_code_old': rfid_code, 'rfid_code': False})
self.env['stock.lot'].sudo().search([('rfid', '=', rfid_code)]).write(
{'tool_material_status': '可用'})
if any(wo.rfid_code for wo in work_ids):
raise ValidationError(f'{record.name}】工单解绑失败,请重新点击完成按钮!!!')
logging.info('work_ids.rfid_code:%s' % [wo.rfid_code for wo in work_ids])
if is_production_id is True:
logging.info('product_qty:%s' % record.production_id.product_qty)
for move_raw_id in record.production_id.move_raw_ids:
@@ -1552,7 +1620,8 @@ class ResMrpWorkOrder(models.Model):
# 如果工单包含了外协工序,需要预留数量
if self.move_raw_ids.move_orig_ids.subcontract_workorder_id:
location_id = self.move_raw_ids.location_id
quant = self.move_raw_ids.lot_ids.quant_ids.filtered(lambda q: q.location_id.id == location_id.id)
quant = self.move_raw_ids.lot_ids.quant_ids.filtered(
lambda q: q.location_id.id == location_id.id)
if quant.reserved_quantity == 0:
self.env['stock.quant']._update_reserved_quantity(
self.move_raw_ids.product_id,
@@ -1706,7 +1775,8 @@ class ResMrpWorkOrder(models.Model):
store=True, string='工序作业')
individuation_page_ids = fields.Many2many('sf.work.individuation.page', string='个性化记录',
related='routing_work_center_id.individuation_page_ids')
individuation_page_list = fields.Char('个性化记录', default='', compute='_compute_individuation_page_ids', store=True)
individuation_page_list = fields.Char('个性化记录', default='', compute='_compute_individuation_page_ids',
store=True)
@api.depends('name')
def _compute_routing_work_center_id(self):
@@ -1727,6 +1797,7 @@ class ResMrpWorkOrder(models.Model):
individuation_page_list = [item.code for item in mw.individuation_page_ids]
if individuation_page_list:
mw.individuation_page_list = list(set(individuation_page_list))
# =============================================================================================
is_inspect = fields.Boolean('需送检', compute='_compute_is_inspect', store=True, default=False)
@@ -1750,6 +1821,25 @@ class ResMrpWorkOrder(models.Model):
self.check_ids.filtered(lambda ch: ch.is_inspect is True and ch.quality_state == 'waiting').write(
{'quality_state': 'none'})
@api.model
def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
aggregate_field = 'create_date:max'
if aggregate_field not in fields:
fields.append(aggregate_field)
orderby = "create_date desc"
return super(ResMrpWorkOrder, self).read_group(
domain,
fields,
groupby,
offset=offset,
limit=limit,
orderby=orderby,
lazy=lazy
)
model_id = fields.Char('模型ID', related='production_id.model_id')
class CNCprocessing(models.Model):
_name = 'sf.cnc.processing'
@@ -2040,6 +2130,7 @@ class WorkPieceDelivery(models.Model):
def _get_agv_route_type_selection(self):
return self.env['sf.agv.task.route'].fields_get(['route_type'])['route_type']['selection']
type = fields.Selection(selection=_get_agv_route_type_selection, string='类型')
delivery_duration = fields.Float('配送时长', compute='_compute_delivery_duration')
status = fields.Selection(

View File

@@ -10,8 +10,8 @@ from odoo.exceptions import ValidationError, UserError
from odoo.modules import get_resource_path
from OCC.Extend.DataExchange import read_step_file
from OCC.Extend.DataExchange import write_stl_file
# from OCC.Extend.DataExchange import read_step_file
# from OCC.Extend.DataExchange import write_stl_file
class ResProductMo(models.Model):
@@ -51,7 +51,7 @@ class ResProductMo(models.Model):
# domain="[('materials_id', '=', materials_id)]")
# cutting_tool_model_id.material_model_id
server_product_process_parameters_id = fields.Many2one('sf.production.process.parameter',
string='表面工艺参数(服务产品)')
string='工艺参数(服务产品)')
model_process_parameters_ids = fields.Many2many('sf.production.process.parameter', 'process_parameter_rel',
string='表面工艺参数')
@@ -787,7 +787,7 @@ class ResProductMo(models.Model):
glb_url = fields.Char('glb文件地址')
area = fields.Float('表面积(m²)')
auto_machining = fields.Boolean('自动化加工(模型识别)', default=False)
model_id = fields.Char('模型id')
model_id = fields.Char('模型ID')
@api.depends('name')
@@ -795,7 +795,7 @@ class ResProductMo(models.Model):
for record in self:
if record.categ_id.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:
product_name = match.group(0)
@@ -1030,6 +1030,7 @@ class ResProductMo(models.Model):
'single_manufacturing': product_id.single_manufacturing,
'is_bfm': True,
'active': True,
'tracking': finish_product.tracking, # 坯料的跟踪方式跟随成品
}
# 外协和采购生成的坯料需要根据材料型号绑定供应商
if route_type == 'subcontract' or route_type == 'purchase':
@@ -1067,7 +1068,8 @@ class ResProductMo(models.Model):
raise UserError('产品名称【%s】已存在' % item.name)
if item.categ_type == '表面工艺':
if len(self.search([('server_product_process_parameters_id', '=',
item.server_product_process_parameters_id.id)])) > 1:
item.server_product_process_parameters_id.id),('server_product_process_parameters_id', '!=',
False)])) > 1:
raise UserError('表面工艺参数为【%s】的产品已存在' % item.server_product_process_parameters_id.name)
if "create_product_product" not in self._context:
templates._create_variant_ids()

View File

@@ -59,6 +59,86 @@ class PurchaseOrder(models.Model):
production_id = self.env['mrp.production'].search([('origin', 'in', origins)])
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):
for record in self:
for line in record.order_line:
@@ -66,7 +146,15 @@ class PurchaseOrder(models.Model):
raise UserError('请对【产品】中的【数量】进行输入')
if line.price_unit <= 0:
raise UserError('请对【产品】中的【单价】进行输入')
return super(PurchaseOrder, self).button_confirm()
# record.outsourcing_service_replenishment()
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')
@@ -120,7 +208,7 @@ class PurchaseOrderLine(models.Model):
continue
if record.product_id.categ_id.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:
product_name = match.group(0)

View File

@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# import base64
# import datetime
# import logging
# import json
# import os
# import re
# import traceback
# from operator import itemgetter
# import requests
# from itertools import groupby
# from collections import defaultdict, namedtuple
# from odoo import api, fields, models, SUPERUSER_ID, _
# from odoo.exceptions import UserError, ValidationError
# from odoo.tools import float_compare, float_round, float_is_zero, format_datetime
# class PurchaseRequestLine(models.Model):
# _inherit = 'purchase.request'
# is_subcontract = fields.Boolean(string='是否外协',default=False)
# class PurchaseRequestLine(models.Model):
# _inherit = 'purchase.request.line'
# is_subcontract = fields.Boolean(string='是否外协')
# class PurchaseRequest(models.Model):
# _inherit = 'purchase.request'
# 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)
# 将模板上的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
product = line.product_id
@@ -75,7 +76,7 @@ class SaleOrder(models.Model):
'embryo_redundancy_id': line.embryo_redundancy_id,
}
product_name = ''
match = re.search(r'(S\d{5}-\d)', product.name)
match = re.search(r'(S\d{5}-\d+)', product.name)
# 如果匹配成功,提取结果
if match:
product_name = match.group(0)
@@ -189,7 +190,30 @@ class SaleOrder(models.Model):
'target': 'new',
'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):
_inherit = 'sale.order.line'

View File

@@ -1,12 +1,80 @@
# -*- coding: utf-8 -*-
import logging
from odoo import fields, models, api
from odoo.exceptions import UserError
from odoo.exceptions import UserError, ValidationError
# from odoo.tools import str2bool
class SfProductionProcessParameter(models.Model):
_inherit = 'sf.production.process.parameter'
# service_products = fields.Many2one(
# 'product.template',
# string='外协服务产品',compute='_compute_service_products',inverse='_inverse_service_products',
# store=True
# )
# outsourced_service_products = fields.One2many(
# 'product.template', # 另一个模型的名称
# 'server_product_process_parameters_id', # 对应的 Many2one 字段名称
# string='外协服务产品'
# )
# is_product_button = fields.Boolean(compute='_compute_is_product_button',default=False)
# is_delete_button = fields.Boolean(compute='_compute_is_delete_button', default=False)
# routing_id = fields.Many2one('mrp.routing.workcenter', string="工序")
# @api.depends('outsourced_service_products')
# def _compute_service_products(self):
# for record in self:
# # 假设取第一条作为主明细
# record.service_products = record.outsourced_service_products.ids if record.outsourced_service_products else False
# def _inverse_service_products(self):
# for record in self:
# if record.service_products:
# # 确保关联关系正确
# record.outsourced_service_products = record.service_products.ids if record.service_products else False
# else:
# record.outsourced_service_products = False
# def name_get(self):
# result = []
# for record in self:
# name = f"{record.process_id.name} - {record.name}" # 自定义显示格式
# result.append((record.id, name))
# return result
# @api.constrains('outsourced_service_products')
# def _validate_partner_limit(self):
# for record in self:
# if len(record.outsourced_service_products) > 1:
# raise ValidationError("工艺参数不能与多个产品关联")
# @api.onchange('outsourced_service_products')
# def _onchange_validate_partner_limit(self):
# for record in self:
# if len(record.outsourced_service_products) > 1:
# raise ValidationError("工艺参数不能与多个产品关联")
# @api.depends('outsourced_service_products')
# def _compute_is_product_button(self):
# for record in self:
# if record.outsourced_service_products:
# record.is_product_button = True
# else:
# record.is_product_button = False
# def has_wksp_prefix(self):
# """
# 判断字符串是否以WKSP开头不区分大小写
# :param text: 要检查的字符串
# :return: True/False
# """
# return self.code.upper().startswith('101'+self.routing_id.code)
# @api.depends('outsourced_service_products','code')
# def _compute_is_delete_button(self):
# for record in self:
# if record.outsourced_service_products and record.has_wksp_prefix():
# record.is_delete_button = False
# elif record.outsourced_service_products:
# record.is_delete_button = True
# else:
# record.is_delete_button = True
@api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
if self._context.get('route_id'):
@@ -21,3 +89,33 @@ class SfProductionProcessParameter(models.Model):
domain = [('process_id', '=', routing.surface_technics_id.id), ('id', 'not in', parameter)]
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
return super()._name_search(name, args, operator, limit, name_get_uid)
# def action_create_service_product(self):
# if self.id: # 如果是已存在的记录
# self.write({}) # 空写入会触发保存
# else: # 如果是新记录
# self = self.create(self._convert_to_write(self.read()[0]))
# return {
# 'type': 'ir.actions.act_window',
# 'name': '向导名称',
# 'res_model': 'product.creation.wizard',
# 'view_mode': 'form',
# 'target': 'new',
# 'context': {'default_process_parameter_id': self.id}, # 传递当前记录ID
# }
#
# return {
# 'name': '创建服务产品',
# 'type': 'ir.actions.act_window',
# 'res_model': 'product.product',
# 'view_mode': 'form',
# 'view_id': self.env.ref('product.product_normal_form_view').id,
# 'target': 'new', # 关键参数,使窗口以弹窗形式打开
# 'context': {
# 'default_' + k: v for k, v in default_values.items()
# },
# }
# def action_hide_service_products(self):
# # self.outsourced_service_products.active = False
# self.active = False

View File

@@ -564,6 +564,13 @@ class StockPicking(models.Model):
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)
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')
def _compute_part_info(self):
@@ -631,6 +638,62 @@ class StockPicking(models.Model):
move.action_clear_lines_show_details()
move.action_show_details()
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
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):
production = workorders[0].production_id
for workorder in workorders:
if workorder.move_subcontract_workorder_ids:
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(
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(
moves_in._get_new_picking_values_Res(item, workorder, 'WH/OCIN/'))
# pick_ids.append(picking_in.id)
@@ -716,7 +780,7 @@ class StockPicking(models.Model):
# self.env.context.get('default_production_id')
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,
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])]})
picking_out = self.create(
moves_out._get_new_picking_values_Res(item, workorder, 'WH/OCOUT/'))
@@ -755,6 +819,24 @@ class StockPicking(models.Model):
if move_id.product_id.tracking in ['serial', 'lot'] and not move_id.move_line_nosuggest_ids:
move_id.action_show_details()
@api.model
def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
aggregate_field = 'create_date:max'
if aggregate_field not in fields:
fields.append(aggregate_field)
orderby = "create_date desc"
return super(StockPicking, self).read_group(
domain,
fields,
groupby,
offset=offset,
limit=limit,
orderby=orderby,
lazy=lazy
)
class ReStockMove(models.Model):
_inherit = 'stock.move'
@@ -764,6 +846,7 @@ class ReStockMove(models.Model):
materiel_height = fields.Float(string='物料高度', digits=(16, 4))
part_number = 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')
def _compute_part_info(self):
@@ -774,7 +857,7 @@ class ReStockMove(models.Model):
move.part_name = move.product_id.part_name
elif move.product_id.categ_id.type == '坯料':
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:
product_name = match.group(0)
@@ -806,7 +889,7 @@ class ReStockMove(models.Model):
continue
product_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:
product_name = match.group(0)
@@ -830,7 +913,7 @@ class ReStockMove(models.Model):
traceback_error = traceback.format_exc()
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
stock_rule = self.env['stock.rule'].sudo().search(
[('route_id', '=', route_id), ('picking_type_id', '=', picking_type_id)])
@@ -839,7 +922,7 @@ class ReStockMove(models.Model):
'company_id': item.company_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_qty': 1.0,
'product_uom_qty': product_uom_qty,
'location_id': stock_rule.location_src_id.id,
'location_dest_id': stock_rule.location_dest_id.id,
'origin': item.name,
@@ -884,7 +967,7 @@ class ReStockMove(models.Model):
'location_id': self.picking_id.location_id.id,
'location_dest_id': self.picking_id.location_dest_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,
'company_id': self.env.company.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

@@ -193,4 +193,6 @@ access_sf_programming_record,sf_programming_record,model_sf_programming_record,b
access_sf_work_individuation_page,sf_work_individuation_page,model_sf_work_individuation_page,sf_base.group_sf_mrp_user,1,1,1,0
access_sf_work_individuation_page_group_plan_dispatch,sf_work_individuation_page_group_plan_dispatch,model_sf_work_individuation_page,sf_base.group_plan_dispatch,1,1,0,0
access_sf_sale_order_cancel_wizard,sf_sale_order_cancel_wizard,model_sf_sale_order_cancel_wizard,sf_base.group_sf_order_user,1,1,1,0
access_sf_sale_order_cancel_line,sf_sale_order_cancel_line,model_sf_sale_order_cancel_line,sf_base.group_sf_order_user,1,0,1,1
access_sf_sale_order_cancel_line,sf_sale_order_cancel_line,model_sf_sale_order_cancel_line,sf_base.group_sf_order_user,1,0,1,1
access_product_creation_wizard,product_creation_wizard,model_product_creation_wizard,base.group_user,1,1,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
193
194
195
196
197
198

View File

@@ -15,6 +15,23 @@
</field>
</record>
<record id="view_agv_site_form" model="ir.ui.view">
<field name="name">agv.site.form</field>
<field name="model">sf.agv.site</field>
<field name="arch" type="xml">
<form create="false" edit="false">
<sheet>
<group>
<field name="name" readonly="1" required="1"/>
<field name="workcenter_id" readonly="1" required="1" options="{'no_create': True}"/>
<field name="state" readonly="1" required="1"/>
<field name="divide_the_work" readonly="1" required="1"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="action_agv_site_form" model="ir.actions.act_window">
<field name="name">AGV站点</field>
<field name="res_model">sf.agv.site</field>
@@ -39,7 +56,8 @@
<field name="route_type" string="类型" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
<field name="start_site_id" required="1" options="{'no_create': True}" string="起点接驳站"
attrs="{'readonly': [('id', '!=', False)]}"/>
<field name="end_site_id" required="1" options="{'no_create': True}" string="终点接驳站"/>
<field name="end_site_id" required="1" options="{'no_create': True}" string="终点接驳站"
attrs="{'readonly': [('id', '!=', False)]}"/>
<!-- <field name="destination_production_line_id" required="1" options="{'no_create': True}"-->
<!-- attrs="{'readonly': [('id', '!=', False)]}"/>-->
<field name="workcenter_id"/>

View File

@@ -110,12 +110,12 @@
</xpath>
<xpath expr="//sheet//group//group[2]//label" position="before">
<!-- <field name="process_state"/> -->
<field name="production_type" readonly="1"/>
<field name="state" readonly="1"/>
<!-- <field name="process_state"/> -->
</xpath>
<xpath expr="//sheet//group//group//div[3]" position="after">
<field name="production_type" readonly="1"/>
<field name="production_product_type" invisible="1"/>
<field name="manual_quotation" readonly="1"
attrs="{'invisible': ['|', ('production_type', 'not in', ['自动化产线加工', '人工线下加工']), ('production_product_type', '!=', '成品')]}"/>
@@ -427,6 +427,7 @@
<field name="programming_method"/>
<field name="current_programming_count"/>
<field name="target_production_id"/>
<field name="apply_uid"/>
<field name="apply_time"/>
<field name="send_time"/>
</tree>
@@ -455,11 +456,15 @@
<field name="inherit_id" ref="mrp.mrp_production_workorder_tree_editable_view"/>
<field name="arch" type="xml">
<xpath expr="//tree" position="attributes">
<attribute name="default_order">sequence</attribute>
<attribute name="default_order">sequence,create_date desc</attribute>
<attribute name="decoration-warning">delivery_warning == 'warning'</attribute>
<attribute name="decoration-danger">delivery_warning == 'overdue'</attribute>
</xpath>
<!-- <xpath expr="//field[@name='create_date']" position="before">-->
<!-- -->
<!-- </xpath>-->
<xpath expr="//field[@name='state']" position="replace">
<field name="create_date" invisible="True"/>
<field name="delivery_warning" invisible="True"/>
<field name="state" widget="badge" decoration-warning="state == 'progress'"
decoration-success="state == 'done'" decoration-danger="state in ('cancel','rework')"
@@ -598,6 +603,7 @@
<field name="part_number"/>
<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="model_id"/>
</xpath>
<xpath expr="//field[@name='product_variant_attributes']" position="attributes">
<attribute name="invisible">1</attribute>

View File

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

View File

@@ -4,30 +4,32 @@
name="Manufacturing"
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"
sequence="145">
sequence="145"/>
<menuitem id="mrp.menu_mrp_manufacturing"
name="Operations"
sequence="10"/>
<menuitem id="mrp.menu_mrp_manufacturing"
name="Operations"
parent="mrp.menu_mrp_root"
sequence="10"/>
<menuitem id="mrp.mrp_planning_menu_root"
name="Planning"
sequence="15"/>
<menuitem id="mrp.mrp_planning_menu_root"
name="Planning"
parent="mrp.menu_mrp_root"
sequence="15"/>
<menuitem id="mrp.enu_mrp_bom"
name="Products"
sequence="20"/>
<menuitem id="mrp.menu_mrp_bom"
name="Products"
parent="mrp.menu_mrp_root"
sequence="20"/>
<menuitem id="mrp.menu_mrp_reporting"
name="Reporting"
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_reporting"
name="Reporting"
parent="mrp.menu_mrp_root"
sequence="25"/>
<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>

View File

@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- 工作中心看板 -->
<record id="mrp_production_view_form_inherit_maintenance" model="ir.ui.view">
<!-- <record id="mrp_production_view_form_inherit_maintenance" model="ir.ui.view">
<field name="name">mrp.production.view.form.inherit.maintenance</field>
<field name="model">mrp.production</field>
<field name="inherit_id" ref="mrp.mrp_production_form_view"/>
<field name="arch" type="xml">
<field name="arch" type="xml"> -->
<!-- <button name="action_cancel" position="before"> -->
<!-- <button name="button_maintenance_req" type="object" string="维修请求"/> -->
<!-- </button> -->
<div name="button_box" position="inside">
<!-- <div name="button_box" position="inside">
<button name="open_maintenance_request_mo" type="object" class="oe_stat_button" icon="fa-wrench"
attrs="{'invisible': [('maintenance_count', '=', 0)]}"
context="{'search_default_production_id': active_id}">
@@ -22,7 +22,7 @@
</button>
</div>
</field>
</record>
</record> -->
<record id="custom_model_form_view_inherit" model="ir.ui.view">
<field name="name">custom.model.form.view.inherit</field>
@@ -451,6 +451,7 @@
</div>
<field name="product_id" position="after">
<field name="model_file" string="产品模型" readonly="1" widget="Viewer3D" attrs="{'invisible': [('model_file', '=', False)]}"/>
<field name="model_id" readonly="1"/>
<field name="glb_url" widget="Viewer3D" string="模型" readonly="1" force_save="1"
attrs="{'invisible': [('glb_url', '=', False)]}"/>
</field>

View File

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

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record model="ir.ui.view" id="view_product_template_form_inherit_sf_manufacturing">
<field name="name">product.template.product.form.inherit.sf_manufacture</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="sf_sale.view_product_template_form_inherit_sf"/>
<field name="arch" type="xml">
<xpath expr="//page[@name='general_information']/group/group[@name='group_standard_price']/field[@name='product_tag_ids']" position="after">
<field name="categ_type" invisible="1"/>
<field name="model_id" readonly="1" attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
</xpath>
</field>
</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>
</odoo>

View File

@@ -101,7 +101,7 @@
action="action_quotations_supply_method"
parent="sale.sale_order_menu"
groups="sf_base.group_production_engineer"
sequence="2"/>
sequence="20"/>
<record id="sale.menu_sale_order" model="ir.ui.menu">
<field name="groups_id" eval="[(4, ref('sf_base.group_production_engineer'))]"/>

View File

@@ -50,6 +50,9 @@
<xpath expr="//field[@name='origin']" position="after">
<field name="retrospect_ref"/>
</xpath>
<xpath expr="//tree" position="attributes">
<attribute name="default_order">create_date desc</attribute>
</xpath>
</field>
</record>
@@ -70,6 +73,7 @@
<xpath expr="//field[@name='picking_type_id']" position="after">
<field name="part_numbers" string="零件图号" filter_domain="[('part_numbers', '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>
</field>
</record>

Some files were not shown because too many files have changed in this diff Show More