Compare commits

...

134 Commits

Author SHA1 Message Date
刘磊
62f0578ce8 Accept Merge Request #2293: (feature/7254_new -> 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/2293
2025-07-18 15:12:05 +08:00
刘磊
b2a0899553 Accept Merge Request #2294: (feature/7230 -> 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/2294
2025-07-18 15:11:41 +08:00
陈赓
e0c1e27344 夹具型号相关数据表的优化 2025-07-18 14:24:33 +08:00
陈赓
8434172fec 夹具型号相关数据表的优化 2025-07-18 11:10:50 +08:00
陈赓
a920d4b4b8 夹具型号相关数据表的优化 2025-07-18 09:58:44 +08:00
陈赓
4609ddfa7a 夹具型号相关数据表的优化 2025-07-18 09:55:21 +08:00
陈赓
e73c0b15ea 夹具型号相关数据表的优化 2025-07-18 09:26:30 +08:00
陈赓
2347ed9cb3 材料型号增加是否磁吸 2025-07-17 14:12:35 +08:00
陈赓
f4829f57a1 还原 2025-07-17 11:08:42 +08:00
胡尧
e4c3435840 Accept Merge Request #2292: (feature/增加模块完整性验证 -> develop)
Merge Request: 去掉多余的js

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2292?initial=true
2025-07-16 15:04:01 +08:00
胡尧
2a7048dafd 去掉多余的js 2025-07-16 15:03:23 +08:00
胡尧
2bb215f9df Accept Merge Request #2291: (feature/增加模块完整性验证 -> develop)
Merge Request: 去掉多余的js文件

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2291?initial=true
2025-07-16 14:33:35 +08:00
胡尧
a2be3a0dbf 去掉多余的js文件 2025-07-16 14:32:37 +08:00
李晓斌
1f4ceab377 Accept Merge Request #2290: (feature/7253 -> develop)
Merge Request: Bug_7262_lxb_commit

Created By: @李晓斌
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @李晓斌
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2290?initial=true
2025-07-16 14:10:37 +08:00
lixiaobin@jikimo.com
94d727e8e0 Bug_7262_lxb_commit 2025-07-16 14:06:58 +08:00
陈赓
cdf6a36c30 Redis同步 2025-07-16 11:49:41 +08:00
李晓斌
42d9f894dd Accept Merge Request #2289: (feature/7253 -> develop)
Merge Request: BUG_7276_lxb_commit

Created By: @李晓斌
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @李晓斌
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2289
2025-07-16 11:23:08 +08:00
lixiaobin@jikimo.com
9416d1c1a0 BUG_7276_lxb_commit 2025-07-16 11:20:26 +08:00
管欢
7dd44ca12c Accept Merge Request #2288: (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/2288
2025-07-16 09:41:06 +08:00
guanhuan
5a98b20988 下达计划按钮重复点击生成重复数据 2025-07-16 09:32:28 +08:00
李晓斌
2738085a1e Accept Merge Request #2287: (feature/7253 -> develop)
Merge Request: Bug_7276_lxb_commit

Created By: @李晓斌
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @李晓斌
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2287?initial=true
2025-07-15 16:56:26 +08:00
lixiaobin@jikimo.com
61c1fdbd05 Bug_7276_lxb_commit 2025-07-15 16:53:52 +08:00
hyyy
7eea5a0ff2 重复点击 2025-07-15 16:25:28 +08:00
李晓斌
714c68c0c1 Accept Merge Request #2286: (feature/7253 -> develop)
Merge Request: BUG_7276_lxb_commit

Created By: @李晓斌
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @李晓斌
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2286
2025-07-15 14:35:11 +08:00
lixiaobin@jikimo.com
f3e7ba7f68 BUG_7276_lxb_commit 2025-07-15 14:17:38 +08:00
lixiaobin@jikimo.com
df589b43e7 BUG_7276_lxb_commit 2025-07-15 14:13:01 +08:00
lixiaobin@jikimo.com
8bdc65c626 BUG_7276_lxb_commit 2025-07-15 14:09:14 +08:00
guanhuan
0c3407572f 需求计划详情 2025-07-15 11:51:14 +08:00
hyyy
e5404efb60 批量下达计划禁用 2025-07-15 11:39:20 +08:00
陈赓
8e8f5eb8be feat: 新增 Redis 缓存同步相关模块与控制器 2025-07-15 11:29:18 +08:00
陈赓
cdbc277a94 feat: 新增 Redis 工具与控制器,更新同步逻辑相关文件 2025-07-15 11:28:12 +08:00
guanhuan
46f60028aa 需求位置修改 2025-07-15 10:40:19 +08:00
guanhuan
bfc071debd 需求位置修改 2025-07-15 10:34:28 +08:00
李晓斌
7fca59322e Accept Merge Request #2285: (feature/7253 -> develop)
Merge Request: BUG_7276_lxb_commit

Created By: @李晓斌
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @李晓斌
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2285
2025-07-15 09:42:48 +08:00
lixiaobin@jikimo.com
42694c1ac6 BUG_7276_lxb_commit 2025-07-15 09:39:50 +08:00
管欢
e88fc012ec Accept Merge Request #2284: (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/2284
2025-07-14 16:16:20 +08:00
guanhuan
ff7cd9c927 校验修改 2025-07-14 16:13:49 +08:00
guanhuan
588b7d340f Merge branch 'refs/heads/develop' into feature/物料需求计划管理 2025-07-14 15:52:10 +08:00
guanhuan
5902d61f13 新增单件用量显示 2025-07-14 15:39:37 +08:00
陈烨
8cfad007b9 Accept Merge Request #2283: (feature/7249 -> develop)
Merge Request: 合并 develop 分支到 feature/7249

Created By: @陈烨
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @陈烨
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2283
2025-07-14 14:29:24 +08:00
chenye
5008210176 合并 develop 分支到 feature/7249 2025-07-14 14:21:44 +08:00
chenye
f487ab4cce 提交7268,7271等bug修改 2025-07-14 14:15:28 +08:00
guanhuan
0441f345ef 新增单件用量显示 2025-07-14 14:11:28 +08:00
李晓斌
3527105e83 Accept Merge Request #2282: (feature/7253 -> develop)
Merge Request: Debug-7269_lxb_commit

Created By: @李晓斌
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @李晓斌
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2282?initial=true
2025-07-14 11:41:47 +08:00
lixiaobin@jikimo.com
88e4cfb541 Debug-7269_lxb_commit 2025-07-14 11:32:29 +08:00
管欢
5a175c078f Accept Merge Request #2281: (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/2281
2025-07-14 10:55:52 +08:00
guanhuan
20980bed9d 修复客供料产品下达计划,第二个计划下达报错 2025-07-14 10:54:17 +08:00
陈烨
9b94357439 Accept Merge Request #2280: (feature/7249 -> develop)
Merge Request: Merge branch 'develop' into feature/7249

Created By: @陈烨
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @陈烨
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2280
2025-07-14 08:50:26 +08:00
chenye
5ae3c5dd47 Merge branch 'develop' into feature/7249 2025-07-11 17:57:31 +08:00
chenye
60be14dda2 修改bug7264和7265 2025-07-11 17:49:05 +08:00
陈烨
f0ff7c4a74 Accept Merge Request #2279: (feature/7249 -> develop)
Merge Request: Merge branch 'develop' into feature/7249

Created By: @陈烨
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @陈烨
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2279
2025-07-11 17:01:05 +08:00
chenye
2b6e2fe31b Merge branch 'develop' into feature/7249 2025-07-11 16:48:24 +08:00
chenye
f1390e47c9 提交7252bug修改内容 2025-07-11 16:36:18 +08:00
李晓斌
c7cd0a6a69 Accept Merge Request #2278: (feature/7253 -> develop)
Merge Request: 7253_BUG修复

Created By: @李晓斌
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @李晓斌
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2278
2025-07-11 11:40:18 +08:00
lixiaobin@jikimo.com
fa5307a2ea 7253_BUG修复 2025-07-11 11:13:08 +08:00
陈烨
07e4cdcaa0 Accept Merge Request #2276: (feature/7249 -> develop)
Merge Request: 提交7249和7246需求

Created By: @陈烨
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @陈烨
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2276
2025-07-11 10:19:58 +08:00
陈烨
ff5925eb06 Merge branch refs/heads/develop into refs/heads/feature/7249 2025-07-11 10:10:56 +08:00
chenye
2a7e07b4c0 提交7249和7246需求 2025-07-10 17:44:10 +08:00
guanhuan
4706bfe85e 计划量字段被修改成了别的字段名 2025-07-10 17:36:37 +08:00
李晓斌
2f21c510bd Accept Merge Request #2274: (feature/7231 -> develop)
Merge Request: 7231_lxb_commit--控制物料需求计划在客户位置的计划量不允许超过需求量

Created By: @李晓斌
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @李晓斌
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2274
2025-07-10 17:01:05 +08:00
lixiaobin@jikimo.com
7adaa7e79b 7231_lxb_commit 2025-07-10 16:33:43 +08:00
禹翔辉
166d10e7d9 Accept Merge Request #2273: (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/2273
2025-07-10 15:24:52 +08:00
管欢
36d6a3ed4c Accept Merge Request #2275: (feature/物料需求计划管理 -> 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/2275
2025-07-10 15:18:35 +08:00
guanhuan
650ff9b3e2 采购申请状态修复 2025-07-10 15:11:32 +08:00
lixiaobin@jikimo.com
559c6bfb1d 7231_lxb_commit 2025-07-10 14:29:06 +08:00
guanhuan
e83d3f8a1c 修复齐套检查产品制造完成,坯料的状态又变成了未齐套 2025-07-10 14:21:10 +08:00
guanhuan
ac0966f3bf 修复齐套检查产品制造完成,坯料的状态又变成了未齐套 2025-07-10 14:20:15 +08:00
guanhuan
1f93ba3b42 修复未保存需求计划删除明细行问题,打印列表显示 2025-07-10 14:11:31 +08:00
guanhuan
4d2ab82645 修复采购类型根据供货方式来赋值 2025-07-10 08:57:38 +08:00
yuxianghui
7b4499184d 添加禁止负库存功能 2025-07-09 16:58:20 +08:00
guanhuan
d02babaf0a 修复需求计划生成的bom编码时间少8小时 2025-07-09 16:57:20 +08:00
guanhuan
acb6fd42ca 采购明细加需求计划明细字段 2025-07-09 16:46:46 +08:00
guanhuan
c2cb24c60b 需求计划优化 2025-07-09 11:38:40 +08:00
管欢
3be1c4c529 Accept Merge Request #2272: (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/2272
2025-07-09 10:26:45 +08:00
guanhuan
60e7415a27 更新报错 2025-07-09 10:25:19 +08:00
管欢
22763903a4 Accept Merge Request #2271: (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/2271
2025-07-09 10:19:50 +08:00
guanhuan
4c6cf0ba5e 更新报错 2025-07-09 09:54:58 +08:00
管欢
02012b2c52 Accept Merge Request #2270: (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/2270
2025-07-09 09:43:44 +08:00
guanhuan
a2b2efed7d 交货数量修改 2025-07-08 15:46:32 +08:00
guanhuan
925c59e1fe 采购申请添加需求明细字段 2025-07-08 11:25:00 +08:00
胡尧
f86e27b45e Accept Merge Request #2269: (feature/新增plm模块 -> develop)
Merge Request: 删除plm模块代码

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2269?initial=true
2025-07-07 15:00:13 +08:00
胡尧
b704f99a29 删除plm模块代码 2025-07-07 14:59:48 +08:00
胡尧
1ac6314f5f Accept Merge Request #2268: (feature/新增plm模块 -> develop)
Merge Request: 新增

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2268?initial=true
2025-07-07 12:16:56 +08:00
胡尧
8b3eef1256 新增 2025-07-07 11:59:14 +08:00
guanhuan
22ccb095ee 需求计划下达计划 2025-07-07 10:46:27 +08:00
guanhuan
9db4ea745b Merge branch 'refs/heads/develop' into feature/物料需求计划管理 2025-07-04 17:03:23 +08:00
guanhuan
60fd4d4a73 需求计划下达计划 2025-07-04 17:01:56 +08:00
禹翔辉
c6c4331c0b Accept Merge Request #2267: (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/2267
2025-07-04 13:49:35 +08:00
yuxianghui
10c6f59f52 处理 成品出库单创建欠单后生成的调拨单详情页中质量检查智能按钮默认展示为通过状态 问题 2025-07-04 13:48:16 +08:00
guanhuan
df4995fa24 需求计划下达计划 2025-07-04 10:18:28 +08:00
guanhuan
6e877d0449 需求计划下达计划 2025-07-03 17:32:11 +08:00
guanhuan
3822d78f35 需求计划供货方式修改 2025-07-03 14:33:19 +08:00
胡尧
b23e50daa6 Accept Merge Request #2266: (feature/6711 -> develop)
Merge Request: 解决采购申请创建的采购单取消后导致后续单据无法就绪的问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2266?initial=true
2025-07-02 17:32:14 +08:00
胡尧
4615f1576f 解决采购申请创建的采购单取消后导致后续单据无法就绪的问题 2025-07-02 17:31:43 +08:00
guanhuan
0350f78710 Merge branch 'refs/heads/develop' into feature/物料需求计划管理
# Conflicts:
#	sf_demand_plan/models/sf_production_demand_plan.py
#	sf_demand_plan/views/demand_plan.xml
2025-07-02 08:57:20 +08:00
胡尧
20df1c0365 Accept Merge Request #2265: (release/release_2.15 -> develop)
Merge Request: 需求计划列表字段位置变动

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2265
2025-07-01 17:57:16 +08:00
胡尧
20069d5336 Merge branch refs/heads/develop into refs/heads/release/release_2.15 2025-07-01 17:57:06 +08:00
胡尧
c8f1676de9 解决采购申请创建的采购单取消后导致调拨单不能就绪的问题 2025-07-01 17:55:07 +08:00
guanhuan
b267f2351d 需求计划开发 2025-07-01 14:34:27 +08:00
guanhuan
fe8df494f9 需求计划列表字段位置变动 2025-06-30 17:13:46 +08:00
guanhuan
c31cc868a9 物料需求计划管理 2025-06-30 17:10:25 +08:00
禹翔辉
0e7d6fac4e Accept Merge Request #2264: (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/2264
2025-06-30 15:20:56 +08:00
guanhuan
32cd68e15f 调拨动作中屏蔽验证 2025-06-30 13:36:04 +08:00
guanhuan
334d406c35 需求计划 2025-06-30 11:29:40 +08:00
yuxianghui
70113cb3c5 采购申请批量转采购按钮显示优化 2025-06-30 09:20:09 +08:00
guanhuan
048f07aba4 新增坯料类型 2025-06-27 17:44:32 +08:00
guanhuan
f0e47371ed 新增坯料类型 2025-06-27 15:29:02 +08:00
guanhuan
c03cda8250 物料需求计划管理 2025-06-27 15:23:05 +08:00
yuxianghui
6dde814acc 产品-form页面加工参数 添加坯料类型字段 2025-06-27 15:17:15 +08:00
yuxianghui
71ab241e94 产品添加坯料类型字段,值来着bfm下单 2025-06-27 15:10:15 +08:00
禹翔辉
272e424975 Accept Merge Request #2263: (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/2263
2025-06-27 10:17:42 +08:00
yuxianghui
1fd9900e41 处理 由采购订单-委外加工类型生成的同一个补货组的坯料采购申请单据没有合并 问题 2025-06-27 09:39:11 +08:00
guanhuan
50a066c23a 物料需求计划管理 2025-06-26 17:52:40 +08:00
guanhuan
fe4659987f 物料需求计划管理 2025-06-26 17:18:03 +08:00
yuxianghui
0a13acbb68 修改 销售订单确认时,生成产品BOM时校验单位用量值的提示信息 2025-06-26 17:04:50 +08:00
yuxianghui
a68d6d4d06 处理 采购订单确认时,一补货组的补货单没有合并问题 2025-06-26 16:55:38 +08:00
guanhuan
c14e2c19bf 物料需求计划管理 2025-06-26 11:13:56 +08:00
胡尧
c630074f4a Accept Merge Request #2262: (feature/根据项目组区分sf代码 -> develop)
Merge Request: 撤回_run_manufacturing方法

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2262?initial=true
2025-06-26 10:27:04 +08:00
禹翔辉
e2ddac4fb0 Accept Merge Request #2261: (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/2261
2025-06-26 10:24:21 +08:00
yuxianghui
e52ec3982f 调拨单验证时,对没有完成通过的质检单不进行出厂检验报告检测 2025-06-26 10:03:19 +08:00
胡尧
160bd117b7 Accept Merge Request #2260: (feature/根据项目组区分sf代码 -> develop)
Merge Request: 没有model_id的制造订单不请求编程

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2260
2025-06-26 09:35:42 +08:00
guanhuan
413cf43ea0 物料需求计划管理 2025-06-25 15:33:24 +08:00
guanhuan
deda36a0f9 物料需求计划管理 2025-06-25 15:01:02 +08:00
guanhuan
8a953a1417 Merge branch 'refs/heads/feature/库存路线' into feature/物料需求计划管理
# Conflicts:
#	sf_demand_plan/__manifest__.py
#	sf_demand_plan/models/sf_production_demand_plan.py
#	sf_demand_plan/security/ir.model.access.csv
2025-06-25 14:32:18 +08:00
guanhuan
d046fd5298 移除销售订单供货方式确认 2025-06-25 14:22:27 +08:00
hyyy
4b026535f8 修改固定列tree的传参方式 2025-06-25 14:01:34 +08:00
guanhuan
9f00fddc09 物料需求计划管理 2025-06-25 09:57:19 +08:00
hyyy
99ac89f995 需求-字段命名调整 2025-06-25 09:54:37 +08:00
guanhuan
f41d3558d2 物料需求计划管理 2025-06-24 17:46:55 +08:00
yuxianghui
35b1d648c3 产品新增单件用量 unit_number 字段,值由bfm下单同步获得;产品生成的BOM中组件数量的值由unit_number 字段提供。 2025-06-24 15:36:23 +08:00
guanhuan
fe3492ceb5 坯料尺寸显示修改 2025-06-24 11:32:41 +08:00
guanhuan
02b4f76326 物料需求计划 2025-06-24 11:17:41 +08:00
guanhuan
2f26aee90a 物料需求计划 2025-06-23 17:21:50 +08:00
yuxianghui
4274b9fe99 出厂检验报告-页脚修改回退 2025-06-20 17:53:04 +08:00
57 changed files with 2271 additions and 746 deletions

View File

@@ -94,12 +94,20 @@ class PurchaseOrder(models.Model):
def button_cancel(self): def button_cancel(self):
""" """
将取消的采购订单关联的库存移动撤销 1. 先将采购订单行与目标库存移动断开链接避免采购单取消后调拨单被调整为mts的问题
2. 取消采购订单
3. 将采购订单行与目标库存移动重新建立链接
""" """
move_ids = self.order_line.move_dest_ids.filtered(lambda move: move.state != 'done' and not move.scrapped) created_purchase_request_line_ids = {}
if self.order_line.move_dest_ids.created_purchase_request_line_id:
move_ids = self.order_line.move_dest_ids.filtered(lambda move: move.state != 'done' and not move.scrapped)
created_purchase_request_line_ids = {move.id: move.created_purchase_request_line_id for move in move_ids}
self.order_line.write({'move_dest_ids': [(5, 0, 0)]})
res =super(PurchaseOrder, self).button_cancel() res =super(PurchaseOrder, self).button_cancel()
if move_ids.mapped('created_purchase_request_line_id'): for move_id, created_purchase_request_line_id in created_purchase_request_line_ids.items():
move_ids.write({'state': 'waiting', 'is_done': False}) self.env['stock.move'].browse(move_id).created_purchase_request_line_id = created_purchase_request_line_id
# if move_ids.mapped('created_purchase_request_line_id'):
# move_ids.write({'state': 'waiting', 'is_done': False})
return res return res
def write(self, vals): def write(self, vals):

View File

@@ -16,6 +16,7 @@ class PurchaseRequest(models.Model):
) )
rule_new_add = fields.Boolean('采购请求为规则创建', default=False, compute='_compute_state', store=True) rule_new_add = fields.Boolean('采购请求为规则创建', default=False, compute='_compute_state', store=True)
rule_purchase_to_request = fields.Boolean('采购单根据规则创建坯料采购申请', default=False)
@api.depends('state') @api.depends('state')
def _compute_state(self): def _compute_state(self):

View File

@@ -44,7 +44,7 @@ class StatusChange(models.Model):
else: else:
action.update({ action.update({
'name': _("%s生成采购请求单", self.name), 'name': _("%s生成采购请求单", self.name),
'domain': [('id', 'in', pr_ids)], 'domain': [('id', 'in', pr_ids.ids)],
'view_mode': 'tree,form', 'view_mode': 'tree,form',
}) })
return action return action

View File

@@ -41,7 +41,20 @@ class StockPicking(models.Model):
if backorder_ids: if backorder_ids:
purchase_request_lines = self.move_ids.move_orig_ids.purchase_line_id.purchase_request_lines purchase_request_lines = self.move_ids.move_orig_ids.purchase_line_id.purchase_request_lines
if purchase_request_lines: if purchase_request_lines:
purchase_request_lines.move_dest_ids = [ purchase_request_lines.move_dest_ids = [
(4, x.id) for x in backorder_ids.move_ids if x.product_id.id in purchase_request_lines.mapped('product_id.id') (4, x.id) for x in backorder_ids.move_ids if
x.product_id.id in purchase_request_lines.mapped('product_id.id') and \
not x.created_purchase_request_line_id
] ]
return res return res
def _subcontracted_produce(self, subcontract_details):
super()._subcontracted_produce(subcontract_details)
# 判断是否根据规则生成新的采购申请单据,如果生成则修改状态为 approved
if self:
pr_ids = self.env["purchase.request"].sudo().search(
[('origin', 'like', self.name), ('rule_purchase_to_request', '=', True), ('state', '=', 'draft')])
if pr_ids:
pr_ids.write({'need_validation': False})
pr_ids.write({"state": "approved", 'need_validation': True, 'rule_new_add': False})

View File

@@ -26,7 +26,7 @@ class StockRule(models.Model):
request_data = rule._prepare_purchase_request( request_data = rule._prepare_purchase_request(
procurement.origin, procurement.values procurement.origin, procurement.values
) )
request_data.update({'rule_new_add': True}) request_data = self._update_request_data(procurement, request_data)
pr = purchase_request_model.create(request_data) pr = purchase_request_model.create(request_data)
cache[domain] = pr cache[domain] = pr
elif ( elif (
@@ -44,6 +44,18 @@ class StockRule(models.Model):
request_line_data.update({'origin': procurement.origin}) request_line_data.update({'origin': procurement.origin})
purchase_request_line_model.create(request_line_data) purchase_request_line_model.create(request_line_data)
def _update_request_data(self, procurement, request_data):
sp = self.env['stock.picking'].sudo().search([('name', '=', procurement.origin)])
if len(sp) == 1:
po = self.env['purchase.order'].sudo().search(
[('name', '=', sp.origin), ('purchase_type', '=', 'outsourcing')])
if po:
request_data.update({'rule_purchase_to_request': True})
else:
request_data.update({'rule_new_add': True})
return request_data
def _run_buy(self, procurements): def _run_buy(self, procurements):
# 如果补货组相同,并且产品相同,则合并 # 如果补货组相同,并且产品相同,则合并
procurements_dict = defaultdict() procurements_dict = defaultdict()

View File

@@ -67,6 +67,16 @@
<field name="part_number"/> <field name="part_number"/>
<field name="part_name" invisible="1"/> <field name="part_name" invisible="1"/>
</xpath> </xpath>
<xpath expr="//tree" position="inside">
<header>
<button
name="%(purchase_request.action_purchase_request_line_make_purchase_order)d"
string="创建询价单"
type="action"
class="btn-primary"
/>
</header>
</xpath>
</field> </field>
</record> </record>

View File

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

View File

@@ -4,4 +4,4 @@ wechatpy==1.8.18
pycryptodome==3.22.0 pycryptodome==3.22.0
openupgradelib==3.10.0 openupgradelib==3.10.0
opcua==0.98.13 opcua==0.98.13
openpyxl openpyxl

View File

@@ -35,7 +35,6 @@
], ],
'web.assets_backend': [ 'web.assets_backend': [
'sf_base/static/src/scss/*.scss', 'sf_base/static/src/scss/*.scss',
'sf_base/static/src/js/*.js',
], ],
}, },

View File

@@ -67,6 +67,52 @@ class BasicParametersFixture(models.Model):
mounting_hole_depth = fields.Float('安装孔深度(mm)', digits=(16, 2)) mounting_hole_depth = fields.Float('安装孔深度(mm)', digits=(16, 2))
centering_diameter = fields.Float('定心直径(mm)', digits=(16, 2)) centering_diameter = fields.Float('定心直径(mm)', digits=(16, 2))
# ‘磁吸托盘’ 字段
magnet_tray_length = fields.Float('磁吸托盘长度(mm)', digits=(16, 2))
magnet_tray_width = fields.Float('磁吸托盘宽度(mm)', digits=(16, 2))
magnet_tray_height = fields.Float('磁吸托盘厚度(mm)', digits=(16, 2))
magnet_tray_diameter = fields.Float('磁吸托盘直径(mm)', digits=(16, 2))
magnet_tray_weight = fields.Float('磁吸托盘重量(kg)', digits=(16, 2))
magnet_max_adsorp_length = fields.Float('磁吸托盘最大吸附长度(mm)', digits=(16, 2))
magnet_max_adsorp_width = fields.Float('磁吸托盘最大吸附宽度(mm)', digits=(16, 2))
magnet_max_adsorp_height = fields.Float('磁吸托盘最大吸附厚度(mm)', digits=(16, 2))
magnet_max_adsorp_diameter = fields.Float('磁吸托盘最大吸附直径(mm)', digits=(16, 2))
magnet_max_adsorp_force = fields.Float('磁吸托盘最大吸附力(N)', digits=(16, 2))
magnet_unlocking_method = fields.Selection(
[('手动', '手动'), ('气动', '气动'), ('液压', '液压'), ('电动', '电动'), ('其他', '其他')],
string='磁吸托盘锁紧方式'
)
magnet_flatness = fields.Char('磁吸托盘平面精度(mm)', size=20)
magnet_max_load = fields.Float('磁吸托盘最大负载(kg)', digits=(16, 2))
# ‘气吸托盘’ 字段
air_tray_length = fields.Float('气吸托盘长度(mm)', digits=(16, 2))
air_tray_width = fields.Float('气吸托盘宽度(mm)', digits=(16, 2))
air_tray_height = fields.Float('气吸托盘高度(mm)', digits=(16, 2))
air_tray_diameter = fields.Float('气吸托盘直径(mm)', digits=(16, 2))
air_tray_weight = fields.Float('气吸托盘重量(kg)', digits=(16, 2))
air_max_adsorp_length = fields.Float('气吸托盘最大吸附长度(mm)', digits=(16, 2))
air_max_adsorp_width = fields.Float('气吸托盘最大吸附宽度(mm)', digits=(16, 2))
air_max_adsorp_height = fields.Float('气吸托盘最大吸附厚度(mm)', digits=(16, 2))
air_max_adsorp_diameter = fields.Float('气吸托盘最大吸附直径(mm)', digits=(16, 2))
air_max_adsorp_force = fields.Float('气吸托盘最大吸附力(N)', digits=(16, 2))
air_unlocking_method = fields.Selection(
[('手动', '手动'), ('气动', '气动'), ('液压', '液压'), ('电动', '电动'), ('其他', '其他')],
string='气吸托盘锁紧方式'
)
air_flatness = fields.Char('气吸托盘平面精度(mm)', size=20)
air_max_load = fields.Float('气吸托盘最大负载(kg)', digits=(16, 2))
air_boolean_chip_blowing_function = fields.Boolean('气吸托盘是否有吹屑功能')
air_way_to_install = fields.Selection(
[('接口式', '接口式'), ('螺栓固定', '螺栓固定'), ('磁吸式', '磁吸式'), ('其他', '其他')],
string='气吸托盘安装方式'
)
code = fields.Char('编码') code = fields.Char('编码')
active = fields.Boolean('有效', default=True) active = fields.Boolean('有效', default=True)
@@ -85,6 +131,10 @@ class BasicParametersFixture(models.Model):
return self._json_adapter_board_fixture_param(fixture_materials_data) return self._json_adapter_board_fixture_param(fixture_materials_data)
elif fixture_materials_name == '三爪卡盘': elif fixture_materials_name == '三爪卡盘':
return self._json_scroll_chuck_param(fixture_materials_data) return self._json_scroll_chuck_param(fixture_materials_data)
elif fixture_materials_name == '磁吸托盘':
return self._json_magnet_tray_param(fixture_materials_data)
elif fixture_materials_name == '气吸托盘':
return self._json_air_tray_param(fixture_materials_data)
return {} return {}
def _json_zero_chuck_param(self, obj): def _json_zero_chuck_param(self, obj):
@@ -284,3 +334,57 @@ class BasicParametersFixture(models.Model):
'centering_diameter': obj['centering_diameter'], 'centering_diameter': obj['centering_diameter'],
'type_of_drive': obj['type_of_drive'], 'type_of_drive': obj['type_of_drive'],
'active': obj['active']} 'active': obj['active']}
def _json_magnet_tray_param(self, obj):
"""磁吸托盘将data数据转换成list数据"""
return {
'code': obj['code'],
'fixture_model_id': self.env['sf.fixture.model'].sudo().search(
[('code', '=', obj.get('fixture_model_code'))]).id,
'name': obj['name'],
'length': obj['length'],
'width': obj['width'],
'height': obj['height'],
'diameter': obj['diameter'],
'weight': obj['weight'],
'max_adsorp_length': obj['max_adsorp_length'],
'max_adsorp_width': obj['max_adsorp_width'],
'max_adsorp_height': obj['max_adsorp_height'],
'max_adsorp_diameter': obj.get('max_adsorp_diameter'),
'max_adsorp_force': obj['max_adsorp_force'],
'flatness': obj.get('flatness'),
'max_load': obj.get('max_load'),
'unlocking_method': obj.get('unlocking_method'),
'materials_model_id': self.env['sf.materials.model'].sudo().search(
[('materials_no', '=', obj['materials_model_id']), ('active', '=', True)]
).id if obj.get('materials_model_id') else False,
'active': obj.get('active', True),
}
def _json_air_tray_param(self, obj):
"""气吸托盘将data数据转换成list数据"""
return {
'code': obj['code'],
'fixture_model_id': self.env['sf.fixture.model'].sudo().search(
[('code', '=', obj.get('fixture_model_code'))]).id,
'name': obj['name'],
'length': obj['length'],
'width': obj['width'],
'height': obj['height'],
'diameter': obj['diameter'],
'weight': obj['weight'],
'max_adsorp_length': obj['max_adsorp_length'],
'max_adsorp_width': obj['max_adsorp_width'],
'max_adsorp_height': obj['max_adsorp_height'],
'max_adsorp_diameter': obj.get('max_adsorp_diameter'),
'max_adsorp_force': obj['max_adsorp_force'],
'flatness': obj.get('flatness'),
'max_load': obj.get('max_load'),
'unlocking_method': obj.get('unlocking_method'),
'boolean_chip_blowing_function': obj.get('blowing_function', False),
'way_to_install': obj.get('way_to_install'),
'materials_model_id': self.env['sf.materials.model'].sudo().search(
[('materials_no', '=', obj['materials_model_id']), ('active', '=', True)]
).id if obj.get('materials_model_id') else False,
'active': obj.get('active', True),
}

View File

@@ -42,6 +42,7 @@ class MrsMaterialModel(models.Model):
materials_num = fields.Char("编码号") materials_num = fields.Char("编码号")
name = fields.Char('型号名') name = fields.Char('型号名')
need_h = fields.Boolean("热处理", default="false") need_h = fields.Boolean("热处理", default="false")
need_m = fields.Boolean("是否磁吸", default="false")
mf_materia_post = fields.Char("热处理后密度") mf_materia_post = fields.Char("热处理后密度")
density = fields.Float("密度(kg/m³)") density = fields.Float("密度(kg/m³)")
materials_id = fields.Many2one('sf.production.materials', "材料名") materials_id = fields.Many2one('sf.production.materials', "材料名")

View File

@@ -35,6 +35,7 @@ class FixtureModel(models.Model):
glb_url = fields.Char(string="图片") glb_url = fields.Char(string="图片")
status = fields.Boolean('状态') status = fields.Boolean('状态')
active = fields.Boolean('有效', default=False) active = fields.Boolean('有效', default=False)
code = fields.Char(string='编码', readonly=True)
zero_chuck_ids = fields.One2many('sf.fixture.materials.basic.parameters', 'fixture_model_id', zero_chuck_ids = fields.One2many('sf.fixture.materials.basic.parameters', 'fixture_model_id',
string='零点卡盘基本参数') string='零点卡盘基本参数')
@@ -46,11 +47,14 @@ class FixtureModel(models.Model):
string='虎钳夹具基本参数') string='虎钳夹具基本参数')
magnet_fixture_ids = fields.One2many('sf.fixture.materials.basic.parameters', 'fixture_model_id', magnet_fixture_ids = fields.One2many('sf.fixture.materials.basic.parameters', 'fixture_model_id',
string='磁吸夹具基本参数') string='磁吸夹具基本参数')
magnet_tray_ids = fields.One2many('sf.fixture.materials.basic.parameters', 'fixture_model_id',
string='磁吸托盘基本参数')
adapter_board_fixture_ids = fields.One2many('sf.fixture.materials.basic.parameters', 'fixture_model_id', adapter_board_fixture_ids = fields.One2many('sf.fixture.materials.basic.parameters', 'fixture_model_id',
string='转接板(锁板)夹具基本参数') string='转接板(锁板)夹具基本参数')
scroll_chuck_ids = fields.One2many('sf.fixture.materials.basic.parameters', 'fixture_model_id', scroll_chuck_ids = fields.One2many('sf.fixture.materials.basic.parameters', 'fixture_model_id',
string='三爪卡盘基本参数') string='三爪卡盘基本参数')
code = fields.Char(string='编码', readonly=True) air_tray_ids = fields.One2many('sf.fixture.materials.basic.parameters', 'fixture_model_id',
string='气吸托盘基本参数')
# def _get_code(self, fixture_model_type_code): # def _get_code(self, fixture_model_type_code):
# fixture_model = self.env['sf.fixture.model'].sudo().search( # fixture_model = self.env['sf.fixture.model'].sudo().search(

View File

@@ -1,62 +0,0 @@
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { barcodeGenericHandlers } from '@barcodes/barcode_handlers';
import { patch } from "@web/core/utils/patch";
// 定义新的 clickOnButton 函数
function customClickOnButton(selector) {
console.log("This is the custom clickOnButton function!");
const buttons = document.body.querySelectorAll(selector);
let length = buttons.length;
if (length > 0) {
buttons[length - 1].click();
} else {
console.warn(`Button with selector ${selector} not found`);
}
}
patch(barcodeGenericHandlers, "start", {
start(env, { ui, barcode, notification }) {
// 使用新定义的 clickOnButton 函数
const COMMANDS = {
"O-CMD.EDIT": () => customClickOnButton(".o_form_button_edit"),
"O-CMD.DISCARD": () => customClickOnButton(".o_form_button_cancel"),
"O-CMD.SAVE": () => customClickOnButton(".o_form_button_save"),
"O-CMD.PREV": () => customClickOnButton(".o_pager_previous"),
"O-CMD.NEXT": () => customClickOnButton(".o_pager_next"),
"O-CMD.PAGER-FIRST": () => updatePager("first"),
"O-CMD.PAGER-LAST": () => updatePager("last"),
"O-CMD.CONFIRM": () => customClickOnButton(".jikimo_button_confirm"),
"O-CMD.FLUSHED": () => customClickOnButton(".jikimo_button_flushed"),
};
barcode.bus.addEventListener("barcode_scanned", (ev) => {
const barcode = ev.detail.barcode;
if (barcode.startsWith("O-BTN.")) {
let targets = [];
try {
targets = getVisibleElements(ui.activeElement, `[barcode_trigger=${barcode.slice(6)}]`);
} catch (_e) {
console.warn(`Barcode '${barcode}' is not valid`);
}
for (let elem of targets) {
elem.click();
}
}
if (barcode.startsWith("O-CMD.")) {
const fn = COMMANDS[barcode];
if (fn) {
fn();
} else {
notification.add(env._t("Barcode: ") + `'${barcode}'`, {
title: env._t("Unknown barcode command"),
type: "danger"
});
}
}
});
}
})

View File

@@ -1,42 +0,0 @@
/** @odoo-module **/
import { registry } from '@web/core/registry';
import { formView } from '@web/views/form/form_view';
import { FormController } from '@web/views/form/form_controller';
import { listView } from '@web/views/list/list_view';
import { ListController } from '@web/views/list/list_controller'
import { onRendered, onMounted } from "@odoo/owl";
export class RemoveFocusFormController extends FormController {
setup() {
super.setup();
onMounted(() => {
this.__owl__.bdom.el.querySelectorAll(':focus').forEach(element => element.blur());
})
}
}
registry.category('views').add('remove_focus_form_view', {
...formView,
Controller: RemoveFocusFormController,
});
export class RemoveFocusListController extends ListController {
setup() {
super.setup();
onMounted(() => {
this.__owl__.bdom.el.querySelectorAll(':focus').forEach(element => element.blur());
})
}
}
registry.category('views').add('remove_focus_list_view', {
...listView,
Controller: RemoveFocusListController,
});

View File

@@ -263,6 +263,7 @@
<field name="materials_no" readonly="1" force_save="1"/> <field name="materials_no" readonly="1" force_save="1"/>
<field name="gain_way" required="0"/> <field name="gain_way" required="0"/>
<field name="density" readonly="1" required="1" class="custom_required"/> <field name="density" readonly="1" required="1" class="custom_required"/>
<field name="need_m" default="false" readonly="1"/>
</group> </group>
<group> <group>
<field name="rough_machining" required="1"/> <field name="rough_machining" required="1"/>
@@ -306,6 +307,7 @@
<field name="tensile_strength"/> <field name="tensile_strength"/>
<field name="hardness" optional="show"/> <field name="hardness" optional="show"/>
<field name="need_h"/> <field name="need_h"/>
<field name="need_m"/>
<field name="apply" widget="many2many_tags" optional="show"/> <field name="apply" widget="many2many_tags" optional="show"/>
<field name="density" optional="show"/> <field name="density" optional="show"/>
<field name="rough_machining" optional="hide"/> <field name="rough_machining" optional="hide"/>
@@ -352,6 +354,7 @@
<field name="materials_no"/> <field name="materials_no"/>
<field name="name"/> <field name="name"/>
<field name="need_h"/> <field name="need_h"/>
<field name="need_m"/>
<field name="mf_materia_post"/> <field name="mf_materia_post"/>
<field name="density"/> <field name="density"/>
<field name='materials_id' default="default" invisible="1"/> <field name='materials_id' default="default" invisible="1"/>

View File

@@ -328,6 +328,52 @@
<field name="type_of_drive"/> <field name="type_of_drive"/>
</tree> </tree>
</field> </field>
<field name="air_tray_ids"
attrs="{'invisible': [('fixture_material_type', '!=', '气吸托盘')]}">
<tree editable="bottom" class="center" delete="0">
<field name="code" invisible="1"/>
<field name="name"/>
<field name="length"/>
<field name="width"/>
<field name="height"/>
<field name="diameter"/>
<field name="weight" string="重量(kg)"/>
<field name="max_adsorp_length"/>
<field name="max_adsorp_width"/>
<field name="max_adsorp_height"/>
<field name="max_adsorp_diameter"/>
<field name="max_adsorp_force"/>
<field name="flatness"/>
<field name="max_load"/>
<field name="unlocking_method"/>
<field name="boolean_chip_blowing_function"/>
<field name="way_to_install"/>
<field name="materials_model_id" options="{'no_create': True}" placeholder="请选择"/>
<field name="active" invisible="1"/>
</tree>
</field>
<field name="magnet_tray_ids"
attrs="{'invisible': [('fixture_material_type', '!=', '磁吸托盘')]}">
<tree editable="bottom" class="center" delete="0">
<field name="code" invisible="1"/>
<field name="name"/>
<field name="length"/>
<field name="width"/>
<field name="height"/>
<field name="diameter"/>
<field name="weight" string="重量(kg)"/>
<field name="max_adsorp_length"/>
<field name="max_adsorp_width"/>
<field name="max_adsorp_height"/>
<field name="max_adsorp_diameter"/>
<field name="max_adsorp_force"/>
<field name="flatness"/>
<field name="max_load"/>
<field name="unlocking_method"/>
<field name="materials_model_id" options="{'no_create': True}" placeholder="请选择"/>
<field name="active" invisible="1"/>
</tree>
</field>
<field name="scroll_chuck_ids" <field name="scroll_chuck_ids"
attrs="{'invisible': [('fixture_material_type', '!=', '三爪卡盘')]}"> attrs="{'invisible': [('fixture_material_type', '!=', '三爪卡盘')]}">
<tree editable="bottom" class="center" delete="0"> <tree editable="bottom" class="center" delete="0">

View File

@@ -2,7 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details. # Part of Odoo. See LICENSE file for full copyright and licensing details.
{ {
'name': '机企猫智能工厂 需求计划', 'name': '机企猫智能工厂 需求计划',
'version': '1.0', 'version': '1.1',
'summary': '智能工厂计划管理', 'summary': '智能工厂计划管理',
'sequence': 1, 'sequence': 1,
'description': """ 'description': """
@@ -14,9 +14,13 @@
'data': [ 'data': [
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'data/stock_route_group.xml', 'data/stock_route_group.xml',
'views/demand_plan_info.xml',
'views/demand_plan.xml', 'views/demand_plan.xml',
'views/stock_route.xml', 'views/stock_route.xml',
'views/sale_order_views.xml',
'wizard/sf_demand_plan_print_wizard_view.xml', 'wizard/sf_demand_plan_print_wizard_view.xml',
'wizard/sf_release_plan_wizard_views.xml',
'views/menu_view.xml',
], ],
'demo': [ 'demo': [
], ],
@@ -26,6 +30,7 @@
'web.assets_backend': [ 'web.assets_backend': [
'sf_demand_plan/static/src/scss/style.css', 'sf_demand_plan/static/src/scss/style.css',
'sf_demand_plan/static/src/js/print_demand.js', 'sf_demand_plan/static/src/js/print_demand.js',
'sf_demand_plan/static/src/js/custom_button.js',
] ]
}, },
'license': 'LGPL-3', 'license': 'LGPL-3',

View File

@@ -0,0 +1,25 @@
# migrations/1.1.0/post-migrate.py
import os
import csv
import logging
from odoo import api, SUPERUSER_ID
_logger = logging.getLogger(__name__)
def migrate(cr, version):
# 获取环境
env = api.Environment(cr, SUPERUSER_ID, {})
ProductionLine = env['sf.production.demand.plan']
DemandPlan = env['sf.demand.plan']
lines = ProductionLine.search([('demand_plan_id', '=', False)])
for line in lines:
vals = {
'sale_order_id': line.sale_order_id.id,
'sale_order_line_id': line.sale_order_line_id.id,
'line_ids': line.ids
}
new_plan = DemandPlan.create(vals)
line.write({'demand_plan_id': new_plan.id})

View File

@@ -1,5 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from . import sf_demand_plan
from . import sf_production_demand_plan from . import sf_production_demand_plan
from . import sale_order from . import sale_order
from . import stock_route from . import stock_route
from . import mrp_bom
from . import mrp_production
from . import stock_rule
from . import purchase_request
from . import purchase_order

View File

@@ -0,0 +1,29 @@
from odoo import models, fields
class MrpBom(models.Model):
_inherit = 'mrp.bom'
# 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品后再次进行创建bom
def bom_create(self, product, bom_type, product_type, code=None):
bom_id = self.env['mrp.bom'].create({
'product_tmpl_id': product.product_tmpl_id.id,
'type': bom_type,
# 'subcontractor_id': '' or subcontract.partner_id.id,
'product_qty': 1,
'product_uom_id': 1,
'code': code
})
if bom_type == 'subcontract' and product_type is not False:
subcontract = self.get_supplier(product.materials_type_id)
bom_id.subcontractor_id = subcontract.partner_id.id
return bom_id
def name_get(self):
"""重写name_get方法只显示BOM编码"""
result = []
for record in self:
# 只显示BOM编码如果编码为空则显示产品名称
display_name = record.code or record.product_tmpl_id.name or f'BOM-{record.id}'
result.append((record.id, display_name))
return result

View File

@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models, api
class MrpProduction(models.Model):
_inherit = 'mrp.production'
demand_plan_line_id = fields.Many2one(comodel_name="sf.production.demand.plan",
string="需求计划明细", readonly=True)
@api.depends('demand_plan_line_id')
def _compute_production_type(self):
for production in self:
if production.demand_plan_line_id.supply_method == 'automation':
production.production_type = '自动化产线加工'
elif production.demand_plan_line_id.supply_method == 'manual':
production.production_type = '人工线下加工'
else:
production.production_type = None
def _get_purchase_request(self):
"""获取跟制造订单相关的采购申请单(根据采购申请单行项目的产品匹配)"""
pr_ids = self.env['purchase.request'].sudo().search(
[('line_ids.demand_plan_line_id', 'in', self.demand_plan_line_id.ids)])
return pr_ids
@api.depends('procurement_group_id', 'procurement_group_id.stock_move_ids.group_id')
def _compute_picking_ids(self):
for order in self:
if order.product_id.product_tmpl_id.single_manufacturing == True and not order.is_remanufacture:
first_order = self.env['mrp.production'].search(
[('demand_plan_line_id', '=', order.demand_plan_line_id.id), ('product_id', '=', order.product_id.id)], limit=1, order='id asc')
order.picking_ids = self.env['stock.picking'].search([
('group_id', '=', first_order.procurement_group_id.id), ('group_id', '!=', False),
])
order.delivery_count = len(first_order.picking_ids)
else:
order.picking_ids = self.env['stock.picking'].search([
('group_id', '=', order.procurement_group_id.id), ('group_id', '!=', False),
])
order.delivery_count = len(order.picking_ids)

View File

@@ -0,0 +1,48 @@
from odoo import api, fields, models, _
from odoo.tools import float_compare
class PurchaseOrder(models.Model):
_inherit = 'purchase.order'
def button_confirm(self):
if self.order_line[0].demand_plan_line_id:
self = self.with_context(
demand_plan_line_id=self.order_line[0].demand_plan_line_id.id
)
res = super(PurchaseOrder, self).button_confirm()
return res
@api.depends('origin')
def _compute_purchase_type(self):
for purchase in self:
if purchase.order_line[0].product_id.categ_id.name == '坯料':
if purchase.order_line[0].product_id.materials_type_id.gain_way == '外协':
purchase.purchase_type = 'outsourcing'
else:
if purchase.order_line[0].demand_plan_line_id.supply_method == 'outsourcing':
purchase.purchase_type = 'outsourcing'
elif purchase.order_line[0].demand_plan_line_id.supply_method == 'purchase':
purchase.purchase_type = 'outside'
class PurchaseOrderLine(models.Model):
_inherit = 'purchase.order.line'
demand_plan_line_id = fields.Many2one(comodel_name="sf.production.demand.plan",
string="需求计划明细", readonly=True)
@api.model
def create(self, vals):
res = super(PurchaseOrderLine, self).create(vals)
if not res.demand_plan_line_id and res.order_id.origin:
origin = [origin.replace(' ', '') for origin in res.order_id.origin.split(',')]
if self.env.context.get('demand_plan_line_id'):
res.demand_plan_line_id = self.env.context.get('demand_plan_line_id')
elif 'MO' in res.order_id.origin:
# 原单据是制造订单
mp_ids = self.env['mrp.production'].sudo().search([('name', 'in', origin)])
if mp_ids:
res.demand_plan_line_id = mp_ids[0].demand_plan_line_id.id
return res

View File

@@ -0,0 +1,35 @@
from odoo import models, fields, api, _
from odoo.exceptions import UserError, ValidationError
class PurchaseRequestLine(models.Model):
_inherit = 'purchase.request.line'
_description = '采购申请明细'
supply_method = fields.Selection([
('automation', "自动化产线加工"),
('manual', "人工线下加工"),
('purchase', "外购"),
('outsourcing', "委外加工"),
], string='供货方式', readonly=True)
demand_plan_line_id = fields.Many2one(comodel_name="sf.production.demand.plan",
string="需求计划明细", readonly=True)
@api.depends('demand_plan_line_id')
def _compute_supply_method(self):
for prl in self:
if prl.demand_plan_line_id:
prl.supply_method = prl.demand_plan_line_id.supply_method
else:
prl.supply_method = None
class PurchaseRequestLineMakePurchaseOrder(models.TransientModel):
_inherit = "purchase.request.line.make.purchase.order"
@api.model
def _prepare_purchase_order_line(self, po, item):
ret = super(PurchaseRequestLineMakePurchaseOrder, self)._prepare_purchase_order_line(po, item)
ret['demand_plan_line_id'] = item.line_id.demand_plan_line_id.id
return ret

View File

@@ -10,20 +10,65 @@ class ReSaleOrder(models.Model):
string='与此销售订单相关联的制造订单', string='与此销售订单相关联的制造订单',
groups='mrp.group_mrp_user', store=True) groups='mrp.group_mrp_user', store=True)
demand_plan_ids = fields.Many2many(comodel_name="sf.demand.plan",
string="需求计划", readonly=True)
demand_plan_count = fields.Integer(
string="需求计划生成计数",
compute='_compute_demand_plan_count'
)
@api.depends('demand_plan_ids.line_ids.status')
def _compute_purchase_request_count(self):
for so in self:
pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', so.name)])
if pr_ids:
so.purchase_request_purchase_order_count = len(pr_ids)
else:
so.purchase_request_purchase_order_count = 0
@api.depends('demand_plan_ids.line_ids')
def _compute_demand_plan_count(self):
for line in self:
demand_plan = self.env['sf.production.demand.plan'].sudo().search([('sale_order_id', '=', line.id)])
line.demand_plan_count = len(demand_plan)
def sale_order_create_line(self, product, item): def sale_order_create_line(self, product, item):
ret = super(ReSaleOrder, self).sale_order_create_line(product, item) ret = super(ReSaleOrder, self).sale_order_create_line(product, item)
vals = { vals = {
'sale_order_id': ret.order_id.id, 'sale_order_id': ret.order_id.id,
'sale_order_line_id': ret.id, 'sale_order_line_id': ret.id,
} }
demand_plan_info = self.env['sf.demand.plan'].sudo().create(vals)
vals.update({'demand_plan_id': demand_plan_info.id, 'plan_uom_qty': ret.product_uom_qty,
'new_supply_method': 'custom_made', 'custom_made_type': 'manual'})
demand_plan = self.env['sf.production.demand.plan'].sudo().create(vals) demand_plan = self.env['sf.production.demand.plan'].sudo().create(vals)
demand_plan_info.write({'line_ids': demand_plan.ids})
if demand_plan.product_id.machining_drawings_name: if demand_plan.product_id.machining_drawings_name:
filename_url = demand_plan.product_id.machining_drawings_name.rsplit('.', 1)[0] filename_url = demand_plan.product_id.machining_drawings_name.rsplit('.', 1)[0]
wizard_vals = { wizard_vals = {
'demand_plan_id': demand_plan.id,
'model_id': demand_plan.model_id, 'model_id': demand_plan.model_id,
'filename_url': filename_url, 'filename_url': filename_url,
'machining_drawings': product.machining_drawings,
'type': '1', 'type': '1',
} }
self.env['sf.demand.plan.print.wizard'].sudo().create(wizard_vals) self.env['sf.demand.plan.print.wizard'].sudo().create(wizard_vals)
ret.order_id.demand_plan_ids = [(4, demand_plan_info.id)]
return ret return ret
def confirm_to_supply_method(self):
self.state = 'sale'
for line in self.order_line:
if line.product_id.auto_machining:
line.supply_method = 'automation'
def action_view_demand_plan(self):
self.ensure_one()
demand_plan_ids = self.env['sf.production.demand.plan'].sudo().search([('sale_order_id', '=', self.id)]).ids
return {
'res_model': 'sf.production.demand.plan',
'type': 'ir.actions.act_window',
'name': _("需求计划"),
'domain': [('id', 'in', demand_plan_ids)],
'view_mode': 'tree',
}

View File

@@ -0,0 +1,265 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
from odoo.tools import float_compare
from odoo.exceptions import ValidationError
import re
class SfDemandPlan(models.Model):
_name = 'sf.demand.plan'
_description = 'sf_demand_plan'
state = fields.Selection([
('10', '待工艺设计'),
('30', '部分下达'),
('40', '已下达'),
('50', '取消'),
], string='状态', default='10', compute='_compute_state', store=True)
line_ids = fields.One2many(comodel_name='sf.production.demand.plan',
inverse_name='demand_plan_id', string="需求计划", copy=True)
sale_order_id = fields.Many2one(comodel_name="sale.order",
string="销售订单", readonly=True)
sale_order_line_id = fields.Many2one(comodel_name="sale.order.line",
string="销售订单明细", readonly=True)
product_id = fields.Many2one(
comodel_name='product.product',
related='sale_order_line_id.product_id',
string='产品', store=True, index=True)
part_name = fields.Char('零件名称', related='product_id.part_name')
part_number = fields.Char('零件图号', compute='_compute_part_number', store=True)
materials_id = fields.Char('材料', compute='_compute_materials_id', store=True)
blank_type = fields.Selection([('圆料', '圆料'), ('方料', '方料')], string='坯料分类',
related='product_id.blank_type')
blank_precision = fields.Selection([('精坯', '精坯'), ('粗坯', '粗坯')], string='坯料类型',
related='product_id.blank_precision')
manual_quotation = fields.Boolean('人工编程', related='product_id.manual_quotation', default=False)
embryo_long = fields.Char('坯料尺寸(mm)', compute='_compute_embryo_long', store=True)
is_incoming_material = fields.Boolean('客供料', related='sale_order_line_id.is_incoming_material', store=True)
pending_qty = fields.Float(
string="待计划",
compute='_compute_pending_qty', store=True)
planned_qty = fields.Float(
string="已计划",
compute='_compute_planned_qty', store=True)
model_id = fields.Char('模型ID', related='product_id.model_id')
customer_name = fields.Char('客户', related='sale_order_id.customer_name')
product_uom_qty = fields.Float(
string="需求数量",
related='sale_order_line_id.product_uom_qty', store=True)
deadline_of_delivery = fields.Date('客户交期', related='sale_order_line_id.delivery_end_date', store=True)
contract_date = fields.Date('合同日期', related='sale_order_id.contract_date')
contract_code = fields.Char('合同号', related='sale_order_id.contract_code', store=True)
model_process_parameters_ids = fields.Many2many('sf.production.process.parameter',
'demand_plan_process_parameter_rel',
string='表面工艺',
compute='_compute_model_process_parameters_ids'
, store=True
)
model_machining_precision = fields.Selection(related='product_id.model_machining_precision', string='精度')
inventory_quantity_auto_apply = fields.Float(
string="成品库存",
compute='_compute_inventory_quantity_auto_apply'
)
priority = fields.Selection([
('1', '紧急'),
('2', ''),
('3', ''),
('4', ''),
], string='优先级', default='3')
overdelivery_allowed = fields.Boolean('可超量发货', default=False)
hide_button_release_plan = fields.Boolean(
string='显示下达计划按钮',
compute='_compute_hide_button_release_plan',
default=False
)
readonly_custom_made_type = fields.Boolean(
string='字段自制类型只读',
compute='_compute_readonly_custom_made_type',
default=False
)
demand_plan_number = fields.Char('需求计划号', compute='_compute_demand_plan_number', readonly=True, store=True)
origin = fields.Char('来源', related='sale_order_id.name', readonly=True, store=True)
@api.depends('product_id.part_number', 'product_id.model_name')
def _compute_part_number(self):
for line in self:
if line.product_id:
if line.product_id.part_number:
line.part_number = line.product_id.part_number
else:
if line.product_id.model_name:
line.part_number = line.product_id.model_name.rsplit('.', 1)[0]
else:
line.part_number = None
@api.depends('product_id.materials_id')
def _compute_materials_id(self):
for line in self:
if line.product_id:
line.materials_id = f"{line.product_id.materials_id.name}/{line.product_id.materials_type_id.name}"
else:
line.materials_id = None
@api.depends('product_id.model_long', 'product_id.model_width', 'product_id.model_height')
def _compute_embryo_long(self):
for line in self:
if line.product_id:
if line.product_id.blank_type == '圆料':
line.embryo_long = f"Ø{round(line.product_id.model_width, 3)}*{round(line.product_id.model_long, 3)}"
else:
line.embryo_long = f"{round(line.product_id.model_long, 3)}*{round(line.product_id.model_width, 3)}*{round(line.product_id.model_height, 3)}"
else:
line.embryo_long = None
@api.depends('product_id.model_process_parameters_ids')
def _compute_model_process_parameters_ids(self):
for line in self:
if line.product_id and line.product_id.model_process_parameters_ids:
line.model_process_parameters_ids = [(6, 0, line.product_id.model_process_parameters_ids.ids)]
else:
line.model_process_parameters_ids = [(5, 0, 0)]
def _compute_inventory_quantity_auto_apply(self):
location_id = self.env['stock.location'].search([('name', '=', '成品存货区')], limit=1).id
product_ids = self.mapped('product_id').ids
if product_ids:
quant_data = self.env['stock.quant'].read_group(
domain=[
('product_id', 'in', product_ids),
('location_id', '=', location_id)
],
fields=['product_id', 'inventory_quantity_auto_apply'],
groupby=['product_id']
)
quantity_map = {item['product_id'][0]: item['inventory_quantity_auto_apply'] for item in quant_data}
else:
quantity_map = {}
for line in self:
if line.product_id:
line.inventory_quantity_auto_apply = quantity_map.get(line.product_id.id, 0.0)
else:
line.inventory_quantity_auto_apply = 0.0
@api.depends('product_uom_qty', 'line_ids.plan_uom_qty')
def _compute_pending_qty(self):
for line in self:
sum_plan_uom_qty = sum(line.line_ids.mapped('plan_uom_qty'))
pending_qty = line.product_uom_qty - sum_plan_uom_qty
if float_compare(pending_qty, 0,
precision_rounding=line.product_id.uom_id.rounding) == -1:
line.pending_qty = 0
else:
line.pending_qty = pending_qty
@api.depends('line_ids.plan_uom_qty')
def _compute_planned_qty(self):
for line in self:
line.planned_qty = sum(line.line_ids.mapped('plan_uom_qty'))
@api.depends('line_ids.status')
def _compute_hide_button_release_plan(self):
for line in self:
line.hide_button_release_plan = bool(line.line_ids.filtered(
lambda p: p.status == '30'))
@api.depends('line_ids.status', 'sale_order_id.state')
def _compute_state(self):
for line in self:
status_line = line.line_ids.filtered(lambda p: p.status == '60')
if not line.line_ids:
line.state = '10'
elif line.sale_order_id.state == 'cancel':
line.state = '50'
line.line_ids.status = '100'
elif len(line.line_ids) == len(status_line):
line.state = '40'
elif bool(status_line):
line.state = '30'
else:
line.state = '10'
@api.depends('line_ids.status')
def _compute_readonly_custom_made_type(self):
for line in self:
production_demand_plan = line.line_ids.filtered(
lambda p: p.status in ('50', '60') and p.new_supply_method == 'custom_made')
line.readonly_custom_made_type = bool(production_demand_plan)
@api.constrains('line_ids')
def check_line_ids(self):
for item in self:
if not item.line_ids:
raise ValidationError('计划不能为空!')
def write(self, vals):
res = super(SfDemandPlan, self).write(vals)
if 'line_ids' in vals:
for line in self.line_ids:
if not line.sale_order_id:
line.sale_order_id = self.sale_order_id
if not line.sale_order_line_id:
line.sale_order_line_id = self.sale_order_line_id
return res
def name_get(self):
result = []
for plan in self:
result.append((plan.id, plan.demand_plan_number))
return result
def button_production_release_plan(self):
line_ids = self.line_ids.filtered(lambda p: p.status == '30')
sum_product_uom_qty = sum(line_ids.mapped('plan_uom_qty'))
customer_location_id = self.env['ir.model.data']._xmlid_to_res_id('stock.stock_location_customers')
check_overdelivery_allowed = False
for line in line_ids:
if line.location_id.id == customer_location_id:
if not self.overdelivery_allowed:
if float_compare(sum_product_uom_qty, self.product_uom_qty,
precision_rounding=line.product_id.uom_id.rounding) == 1:
check_overdelivery_allowed = True
if check_overdelivery_allowed:
raise ValidationError(f"已禁止向合作伙伴/客户超量发货,请更换“补货原因”或将“可超量发货”设置为“是”。")
elif float_compare(sum_product_uom_qty, self.product_uom_qty,
precision_rounding=self.product_id.uom_id.rounding) == 1:
return {
'name': _('需求计划'),
'type': 'ir.actions.act_window',
'views': [(self.env.ref(
'sf_demand_plan.sf_release_plan_wizard_form').id,
'form')],
'res_model': 'sf.release.plan.wizard',
'target': 'new',
'context': {
'default_demand_plan_line_id': line_ids.ids,
'default_release_message': f"您正在下达计划量 {sum_product_uom_qty},需求数量为 {self.product_uom_qty},已超过需求数量,是否继续?",
}}
else:
for demand_plan_line_id in line_ids:
demand_plan_line_id.action_confirm()
# 需求要求取值格式是来源+来源明细行ID,但是来源明细行ID取得就是product_id.name得最后一位所以这里也直接截取product_id.name
@api.depends('product_id.name')
def _compute_demand_plan_number(self):
for line in self:
product_name = line.product_id.name or ''
plan_no = None
if line.product_id:
# 使用正则表达式匹配P-后面的所有字符
match = re.search(r'P-(.*)', product_name)
if match:
plan_no = match.group(1)
line.demand_plan_number = plan_no
else:
line.demand_plan_number = None

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,7 @@ class SfStockRoute(models.Model):
[('supply_method', 'in', stock_route_group)]) [('supply_method', 'in', stock_route_group)])
if demand_plan_ids: if demand_plan_ids:
sr.demand_plan_ids = demand_plan_ids.ids sr.demand_plan_ids = demand_plan_ids.ids
break continue
sr.demand_plan_ids = None sr.demand_plan_ids = None
# def name_get(self): # def name_get(self):

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
class StockRule(models.Model):
_inherit = 'stock.rule'
def _prepare_mo_vals(self, product_id, product_qty, product_uom, location_id, name, origin, company_id, values,
bom):
res = super()._prepare_mo_vals(product_id, product_qty, product_uom, location_id, name, origin, company_id,
values, bom)
if self.env.context.get('demand_plan_line_id'):
res['demand_plan_line_id'] = self.env.context.get('demand_plan_line_id')
return res
@api.model
def _prepare_purchase_request_line(self, request_id, procurement):
res = super()._prepare_purchase_request_line(request_id, procurement)
if self.env.context.get('demand_plan_line_id'):
res['demand_plan_line_id'] = self.env.context.get('demand_plan_line_id')
return res

View File

@@ -1,9 +1,16 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sf_production_demand_plan,sf.production.demand.plan,model_sf_production_demand_plan,base.group_user,1,0,0,0 access_sf_production_demand_plan,sf.production.demand.plan,model_sf_production_demand_plan,base.group_user,1,0,0,0
access_sf_production_demand_plan_for_dispatch,sf.production.demand.plan for dispatch,model_sf_production_demand_plan,sf_base.group_plan_dispatch,1,1,0,0 access_sf_production_demand_plan_for_dispatch,sf.production.demand.plan for dispatch,model_sf_production_demand_plan,sf_base.group_plan_dispatch,1,1,1,1
access_sf_demand_plan_print_wizard,sf.demand.plan.print.wizard,model_sf_demand_plan_print_wizard,base.group_user,1,0,0,0 access_sf_demand_plan_print_wizard,sf.demand.plan.print.wizard,model_sf_demand_plan_print_wizard,base.group_user,1,0,0,0
access_sf_demand_plan_print_wizard_for_dispatch,sf.demand.plan.print.wizard for dispatch,model_sf_demand_plan_print_wizard,sf_base.group_plan_dispatch,1,1,0,0 access_sf_demand_plan_print_wizard_for_dispatch,sf.demand.plan.print.wizard for dispatch,model_sf_demand_plan_print_wizard,sf_base.group_plan_dispatch,1,1,0,0
access_sf_demand_plan,sf.demand.plan,model_sf_demand_plan,base.group_user,1,0,0,0
access_sf_demand_plan_for_dispatch,sf.demand.plan for dispatch,model_sf_demand_plan,sf_base.group_plan_dispatch,1,1,0,0
access_stock_route_group,stock.route.group,model_stock_route_group,base.group_user,1,0,0,0 access_stock_route_group,stock.route.group,model_stock_route_group,base.group_user,1,0,0,0
access_stock_route_group_dispatch,stock.route.group.dispatch,model_stock_route_group,sf_base.group_plan_dispatch,1,1,0,0 access_stock_route_group_dispatch,stock.route.group.dispatch,model_stock_route_group,sf_base.group_plan_dispatch,1,1,0,0
access_sf_release_plan_wizard,sf.release.plan.wizard,model_sf_release_plan_wizard,base.group_user,1,0,0,0
access_sf_release_plan_wizard_for_dispatch,sf.release.plan.wizard for dispatch,model_sf_release_plan_wizard,sf_base.group_plan_dispatch,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_sf_production_demand_plan sf.production.demand.plan model_sf_production_demand_plan base.group_user 1 0 0 0
3 access_sf_production_demand_plan_for_dispatch sf.production.demand.plan for dispatch model_sf_production_demand_plan sf_base.group_plan_dispatch 1 1 0 1 0 1
4 access_sf_demand_plan_print_wizard sf.demand.plan.print.wizard model_sf_demand_plan_print_wizard base.group_user 1 0 0 0
5 access_sf_demand_plan_print_wizard_for_dispatch sf.demand.plan.print.wizard for dispatch model_sf_demand_plan_print_wizard sf_base.group_plan_dispatch 1 1 0 0
6 access_stock_route_group access_sf_demand_plan stock.route.group sf.demand.plan model_stock_route_group model_sf_demand_plan base.group_user 1 0 0 0
7 access_stock_route_group_dispatch access_sf_demand_plan_for_dispatch stock.route.group.dispatch sf.demand.plan for dispatch model_stock_route_group model_sf_demand_plan sf_base.group_plan_dispatch 1 1 0 0
8 access_stock_route_group stock.route.group model_stock_route_group base.group_user 1 0 0 0
9 access_stock_route_group_dispatch stock.route.group.dispatch model_stock_route_group sf_base.group_plan_dispatch 1 1 0 0
10 access_sf_release_plan_wizard sf.release.plan.wizard model_sf_release_plan_wizard base.group_user 1 0 0 0
11 access_sf_release_plan_wizard_for_dispatch sf.release.plan.wizard for dispatch model_sf_release_plan_wizard sf_base.group_plan_dispatch 1 1 1 1
12
13
14
15
16

View File

@@ -0,0 +1,59 @@
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { ListRenderer } from "@web/views/list/list_renderer";
import { useService } from "@web/core/utils/hooks";
import { useEffect } from "@odoo/owl";
export class CustomDemandPlanListRenderer extends ListRenderer {
setup() {
super.setup();
this.orm = useService("orm");
this.notification = useService("notification");
console.log('setup', this.props);
// 监听selection属性的变化
useEffect(() => {
this.updateButtonState();
}, () => [this.props.list.selection]);
}
/**
* 更新按钮状态
*/
async updateButtonState() {
const selectedRecords = this.props.list.selection;
const isStatus30 = selectedRecords.some(record => record.data.status != "30");
const button = $(this.__owl__.parent.bdom.parentEl).find('button[name="button_batch_release_plan"]');
console.log('isStatus30', isStatus30, button);
if (isStatus30) {
// 禁用按钮
button.attr('disabled', true);
} else {
button.attr('disabled', false);
}
}
}
// 使用setTimeout延迟注册避免在模块加载时立即执行
setTimeout(() => {
const registerCustomRenderer = () => {
try {
const listView = registry.category("views").get("list");
if (listView) {
registry.category("views").add("custom_demand_plan_list", {
...listView,
Renderer: CustomDemandPlanListRenderer,
});
console.log("Custom demand plan list renderer registered successfully");
} else {
console.warn("List view not found, retrying...");
// 如果还没找到,再等一段时间
setTimeout(registerCustomRenderer, 1000);
}
} catch (error) {
console.error("Error registering custom renderer:", error);
}
};
registerCustomRenderer();
}, 1000);

View File

@@ -81,4 +81,9 @@
input,label { input,label {
cursor: pointer; cursor: pointer;
} }
} }
/*.demand_plan_tree th[data-name=planned_start_date] + th::before{*/
/* content: '待执行单据';*/
/* line-height: 38px;*/
/*}*/

View File

@@ -4,14 +4,17 @@
<field name="model">sf.production.demand.plan</field> <field name="model">sf.production.demand.plan</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="需求计划" default_order="sequence desc,id desc" editable="bottom" <tree string="需求计划" default_order="sequence desc,id desc" editable="bottom"
class="demand_plan_tree"> class="demand_plan_tree freeze-columns-before-part_number" create="false" delete="false"
js_class="custom_demand_plan_list">
<header> <header>
<button string="打印" name="button_action_print" type="object" <button string="打印" name="button_action_print" type="object"
class="btn-primary"/> class="btn-primary"/>
<button string="下达计划" name="button_batch_release_plan" type="object"
class="btn-primary"
/>
</header> </header>
<field name="sequence" widget="handle"/> <field name="sequence" widget="handle"/>
<field name="id" optional="hide"/> <field name="id" optional="hide"/>
<field name="priority"/>
<field name="status"/> <field name="status"/>
<field name="customer_name"/> <field name="customer_name"/>
<field name="order_remark"/> <field name="order_remark"/>
@@ -20,16 +23,24 @@
<field name="model_id" optional="hide"/> <field name="model_id" optional="hide"/>
<field name="part_name"/> <field name="part_name"/>
<field name="part_number"/> <field name="part_number"/>
<field name="manual_quotation" optional="hide"/>
<field name="is_incoming_material"/> <field name="is_incoming_material"/>
<field name="supply_method"/> <field name="new_supply_method" attrs="{'readonly': [('status', '!=', '30')]}"/>
<field name="readonly_custom_made_type" invisible="1"/>
<field name="custom_made_type"
attrs="{'readonly': ['|',('status', '!=', '30'),('readonly_custom_made_type', '=', True)],
'required': [('new_supply_method', '=', 'custom_made')]}"/>
<field name="product_uom_qty"/> <field name="product_uom_qty"/>
<field name="plan_uom_qty" attrs="{'readonly': [('status', '!=', '30')]}"/>
<field name="deadline_of_delivery"/> <field name="deadline_of_delivery"/>
<field name="inventory_quantity_auto_apply"/> <field name="inventory_quantity_auto_apply" optional="hide"/>
<field name="qty_delivered"/> <field name="qty_delivered" optional="hide"/>
<field name="qty_to_deliver"/> <field name="qty_to_deliver" optional="hide"/>
<field name="model_long"/> <field name="model_long"/>
<field name="blank_type" optional="hide"/> <field name="blank_type" optional="hide"/>
<field name="blank_precision"/>
<field name="embryo_long"/> <field name="embryo_long"/>
<field name="unit_number" optional="hide"/>
<field name="materials_id"/> <field name="materials_id"/>
<field name="model_machining_precision"/> <field name="model_machining_precision"/>
<field name="model_process_parameters_ids" widget="many2many_tags"/> <field name="model_process_parameters_ids" widget="many2many_tags"/>
@@ -38,38 +49,49 @@
<field name="sale_order_id" optional="hide"/> <field name="sale_order_id" optional="hide"/>
<field name="sale_order_line_number" optional="hide"/> <field name="sale_order_line_number" optional="hide"/>
<field name="order_state"/> <field name="order_state"/>
<field name="route_ids" widget="many2many_tags" optional="hide" readonly="0" <field name="route_ids" widget="many2many_tags" optional="hide"
context="{'demand_plan_search_stock_route_id': id}"/> context="{'demand_plan_search_stock_route_id': id}"/>
<field name="contract_date"/> <field name="contract_date"/>
<field name="date_order"/> <field name="date_order"/>
<field name="contract_code"/> <field name="contract_code"/>
<field name="plan_remark"/> <field name="plan_remark" attrs="{'readonly': [('status', 'in', ('60','100'))]}"/>
<field name="processing_time"/> <field name="priority" decoration-danger="priority == '1'"
decoration-warning="priority == '2'"
decoration-info="priority == '3'"
decoration-success="priority == '4'"/>
<field name="material_check" optional="hide"/> <field name="material_check" optional="hide"/>
<field name="hide_action_open_mrp_production" invisible="1"/> <!-- <field name="hide_action_open_mrp_production" invisible="1"/>-->
<field name="hide_action_purchase_orders" invisible="1"/> <!-- <field name="hide_action_purchase_orders" invisible="1"/>-->
<field name="hide_action_stock_picking" invisible="1"/> <!-- <field name="hide_action_stock_picking" invisible="1"/>-->
<field name="hide_action_view_programming" invisible="1"/> <!-- <field name="hide_action_view_programming" invisible="1"/>-->
<button name="action_open_sale_order" type="object" string="供货方式待确认" class="btn-secondary" <!-- <button name="action_open_sale_order" type="object" string="供货方式待确认" class="btn-secondary"-->
attrs="{'invisible': [('supply_method', '!=', False)]}"/> <!-- attrs="{'invisible': [('supply_method', '!=', False)]}"/>-->
<button name="action_open_mrp_production" type="object" string="待工艺确认" class="btn-secondary" <!-- <button name="action_open_mrp_production" type="object" string="待工艺确认" class="btn-secondary"-->
attrs="{'invisible': [('hide_action_open_mrp_production', '=', False)]}"/> <!-- attrs="{'invisible': [('hide_action_open_mrp_production', '=', False)]}"/>-->
<button name="action_view_purchase_request" type="object" string="采购申请" class="btn-secondary" <!-- <button name="action_view_purchase_request" type="object" string="采购申请" class="btn-secondary"-->
attrs="{'invisible': [('hide_action_purchase_orders', '=', False)]}"/> <!-- attrs="{'invisible': [('hide_action_purchase_orders', '=', False)]}"/>-->
<button name="action_view_stock_picking" type="object" string="调拨单" class="btn-secondary" <!-- <button name="action_view_stock_picking" type="object" string="调拨单" class="btn-secondary"-->
attrs="{'invisible': [('hide_action_stock_picking', '=', False)]}"/> <!-- attrs="{'invisible': [('hide_action_stock_picking', '=', False)]}"/>-->
<button name="action_view_programming" type="object" string="编程单" class="btn-secondary" <!-- <button name="action_view_programming" type="object" string="编程单" class="btn-secondary"-->
attrs="{'invisible': [('hide_action_view_programming', '=', False)]}"/> <!-- attrs="{'invisible': [('hide_action_view_programming', '=', False)]}"/>-->
<field name="planned_start_date"/> <field name="planned_start_date" attrs="{'readonly': [('status', 'in', ('60','100'))]}"/>
<field name="actual_start_date"/> <field name="actual_start_date"/>
<field name="actual_end_date"/> <field name="actual_end_date"/>
<field name="processing_time"/>
<field name="create_date" optional="hide" string="创建时间"/> <field name="create_date" optional="hide" string="创建时间"/>
<field name="create_uid" optional="hide" string="创建人"/> <field name="create_uid" optional="hide" string="创建人"/>
<field name="write_date" string="更新时间"/> <field name="write_date" string="更新时间"/>
<field name="write_uid" optional="hide" string="更新人"/> <field name="write_uid" optional="hide" string="更新人"/>
<field name="print_count"/> <field name="print_count"/>
<button name="release_production_order" type="object" string="下达生产" class="btn-primary" <field name="hide_release_production_order" invisible="1"/>
attrs="{'invisible': ['|',('status', '!=', '50'), ('supply_method', 'not in', ['automation', 'manual'])]}"/> <button string="下达计划" name="button_release_plan" type="object"
class="btn-primary"
attrs="{'invisible': [('status', 'in', ('50','60','100'))]}"
/>
<button name="button_release_production" type="object" string="下发生产" class="btn-primary"
attrs="{'invisible': [('hide_release_production_order', '=', False)]}"
/>
<button name="edit_button" type="object" string="拆分" class="btn-primary"/>
</tree> </tree>
</field> </field>
</record> </record>
@@ -92,7 +114,8 @@
<group expand="0" string="Group By"> <group expand="0" string="Group By">
<filter name="group_by_priority" string="优先级" domain="[]" context="{'group_by': 'priority'}"/> <filter name="group_by_priority" string="优先级" domain="[]" context="{'group_by': 'priority'}"/>
<filter name="group_by_status" string="状态" domain="[]" context="{'group_by': 'status'}"/> <filter name="group_by_status" string="状态" domain="[]" context="{'group_by': 'status'}"/>
<filter name="group_by_customer_name" string="客户" domain="[]" context="{'group_by': 'customer_name'}"/> <filter name="group_by_customer_name" string="客户" domain="[]"
context="{'group_by': 'customer_name'}"/>
<filter name="group_by_is_incoming_material" string="客供料" domain="[]" <filter name="group_by_is_incoming_material" string="客供料" domain="[]"
context="{'group_by': 'is_incoming_material'}"/> context="{'group_by': 'is_incoming_material'}"/>
<filter name="group_by_supply_method" string="供货方式" domain="[]" <filter name="group_by_supply_method" string="供货方式" domain="[]"
@@ -115,12 +138,4 @@
<field name="view_mode">tree</field> <field name="view_mode">tree</field>
</record> </record>
<menuitem
id="demand_plan_menu"
name="需求计划"
sequence="140"
action="sf_production_demand_plan_action"
parent="sf_plan.sf_production_plan_menu"
/>
</odoo> </odoo>

View File

@@ -0,0 +1,123 @@
<odoo>
<record id="view_sf_demand_plan_form" model="ir.ui.view">
<field name="name">sf.demand.plan.form</field>
<field name="model">sf.demand.plan</field>
<field name="arch" type="xml">
<form>
<header>
<field name="state" widget="statusbar"/>
<field name="hide_button_release_plan" invisible="1"/>
<button string="下达计划" name="button_production_release_plan" type="object"
class="btn-primary"
attrs="{'invisible': [('hide_button_release_plan', '=', False)]}"/>
</header>
<sheet>
<group>
<group>
<field name="demand_plan_number"/>
<field name="product_id"/>
<field name="part_name"/>
<field name="part_number"/>
<field name="materials_id"/>
<field name="blank_type"/>
<field name="blank_precision"/>
<field name="embryo_long"/>
<field name="manual_quotation"/>
<field name="is_incoming_material"/>
<field name="pending_qty"/>
<field name="planned_qty"/>
<field name="model_id"/>
</group>
<group>
<field name="customer_name"/>
<field name="product_uom_qty"/>
<field name="deadline_of_delivery"/>
<field name="contract_date"/>
<field name="contract_code"/>
<field name="model_process_parameters_ids" widget="many2many_tags"/>
<field name="model_machining_precision"/>
<field name="inventory_quantity_auto_apply"/>
<field name="priority" attrs="{'readonly': [('state', 'in', ('40','50'))]}"/>
<field name="overdelivery_allowed"/>
<field name="origin"/>
</group>
</group>
<notebook>
<page string="计划">
<field name="line_ids" attrs="{'invisible': [('state', 'in', ('40','50'))]}">
<tree editable="bottom" create="false" delete="false">
<field name="status"/>
<field name="readonly_custom_made_type" invisible="1"/>
<field name="new_supply_method" attrs="{'readonly': [('status', '!=', '30')]}"/>
<field name="custom_made_type"
attrs="{
'readonly': ['|', '|', ('new_supply_method', '!=', 'custom_made'), ('status', '!=', '30'), ('readonly_custom_made_type', '=', True)],
'required': [('new_supply_method', '=', 'custom_made')]}"/>
<field name="route_ids" widget="many2many_tags" optional="hide"/>
<field name="location_id" optional="hide"/>
<field name="bom_id" optional="hide"/>
<field name="processing_time" optional="hide"/>
<field name="plan_uom_qty" attrs="{'readonly': [('status', '!=', '30')]}"/>
<field name="blank_arrival_date"/>
<field name="finished_product_arrival_date"/>
<field name="planned_start_date"/>
<field name="actual_start_date"/>
<field name="actual_end_date"/>
<field name="plan_remark"/>
<field name="procurement_reason"/>
<field name="write_date" string="更新时间"/>
<field name="hide_release_production_order" invisible="1"/>
<button string="下达计划" name="button_release_plan" type="object"
class="btn-primary"
attrs="{'invisible': [('status', 'in', ('50','60','100'))]}"
/>
<button name="button_release_production" type="object" string="下发生产"
class="btn-primary"
attrs="{'invisible': [('hide_release_production_order', '=', False)]}"
/>
</tree>
</field>
<field name="line_ids" attrs="{'invisible': [('state', 'not in', ('40','50'))]}">
<tree editable="bottom">
<field name="status"/>
<field name="readonly_custom_made_type" invisible="1"/>
<field name="new_supply_method" attrs="{'readonly': [('status', '!=', '30')]}"/>
<field name="custom_made_type"
attrs="{
'readonly': ['|', '|', ('new_supply_method', '!=', 'custom_made'), ('status', '!=', '30'), ('readonly_custom_made_type', '=', True)],
'required': [('new_supply_method', '=', 'custom_made')]}"/>
<field name="route_ids" widget="many2many_tags" optional="hide"/>
<field name="location_id" optional="hide"/>
<field name="bom_id" optional="hide" readonly="1" options="{'no_create': True}"/>
<field name="processing_time" optional="hide"/>
<field name="plan_uom_qty" attrs="{'readonly': [('status', '!=', '30')]}"/>
<field name="blank_arrival_date"/>
<field name="finished_product_arrival_date"/>
<field name="planned_start_date"/>
<field name="actual_start_date"/>
<field name="actual_end_date"/>
<field name="plan_remark"/>
<field name="procurement_reason"/>
<field name="write_date" string="更新时间"/>
<field name="hide_release_production_order" invisible="1"/>
<button string="下达计划" name="button_release_plan" type="object"
class="btn-primary"
attrs="{'invisible': [('status', 'in', ('50','60','100'))]}"
/>
<button name="button_release_production" type="object" string="下发生产"
class="btn-primary"
attrs="{'invisible': [('hide_release_production_order', '=', False)]}"
/>
<button string="详情" name="button_plan_detail" type="object"
class="btn-primary"
/>
</tree>
</field>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<menuitem
id="demand_plan_menu"
name="需求计划"
sequence="140"
action="sf_demand_plan.sf_production_demand_plan_action"
parent="sf_plan.sf_production_plan_menu"
/>
<!-- 调拨动作中屏蔽验证-->
<record id="stock.action_validate_picking" model="ir.actions.server">
<field name="binding_model_id" eval="False"/>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_order_form_inherit_plan" model="ir.ui.view">
<field name="name">view.sale.order.form.inherit.plan</field>
<field name="inherit_id" ref="sf_manufacturing.view_order_form_inherit_supply_method"/>
<field name="model">sale.order</field>
<field name="arch" type="xml">
<xpath expr="//header/button[@name='action_confirm'][last()]" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//page/field[@name='order_line']/tree/field[@name='supply_method']" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//div[@name='button_box']" position="inside">
<button class="oe_stat_button" name="action_view_demand_plan" type="object" icon="fa-pencil-square-o"
attrs="{'invisible': [('demand_plan_count', '=', 0)]}">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
<field name="demand_plan_count"/>
</span>
<span class="o_stat_text">需求计划</span>
</div>
</button>
</xpath>
</field>
</record>
</odoo>

View File

@@ -1 +1,2 @@
from . import sf_demand_plan_print_wizard from . import sf_demand_plan_print_wizard
from . import sf_release_plan_wizard

View File

@@ -9,11 +9,6 @@ class SfDemandPlanPrintWizard(models.TransientModel):
_name = 'sf.demand.plan.print.wizard' _name = 'sf.demand.plan.print.wizard'
_description = u'打印向导' _description = u'打印向导'
demand_plan_id = fields.Many2one('sf.production.demand.plan', string='需求计划ID')
product_id = fields.Many2one(
comodel_name='product.product',
related='demand_plan_id.product_id',
string='产品', store=True, index=True)
model_id = fields.Char('模型ID') model_id = fields.Char('模型ID')
filename_url = fields.Char('文件名/URL') filename_url = fields.Char('文件名/URL')
type = fields.Selection([ type = fields.Selection([
@@ -25,7 +20,7 @@ class SfDemandPlanPrintWizard(models.TransientModel):
('success', '成功'), ('success', '成功'),
('fail', '失败'), ('fail', '失败'),
], string='状态', default='not_start') ], string='状态', default='not_start')
machining_drawings = fields.Binary('2D加工图纸', related='product_id.machining_drawings', store=True) machining_drawings = fields.Binary('2D加工图纸')
cnc_worksheet = fields.Binary('程序单') cnc_worksheet = fields.Binary('程序单')
@@ -42,16 +37,19 @@ class SfDemandPlanPrintWizard(models.TransientModel):
if pdf_data: if pdf_data:
try: try:
# 执行打印 # 执行打印
self.env['jikimo.printing'].sudo().print_pdf(pdf_data) # self.env['jikimo.printing'].sudo().print_pdf(pdf_data)
record.status = 'success' record.status = 'success'
t_part, c_part = record.demand_plan_id.print_count.split('C') production_demand_plan_id = self.env['sf.production.demand.plan'].sudo().search(
t_num = int(t_part[1:]) [('model_id', '=', record.model_id)])
c_num = int(c_part) for production_demand_plan in production_demand_plan_id:
if record.type == '1': t_part, c_part = production_demand_plan.print_count.split('C')
t_num += 1 t_num = int(t_part[1:])
elif record.type == '2': c_num = int(c_part)
c_num += 1 if record.type == '1':
record.demand_plan_id.print_count = f"T{t_num}C{c_num}" t_num += 1
elif record.type == '2':
c_num += 1
production_demand_plan.print_count = f"T{t_num}C{c_num}"
success_records.append({ success_records.append({
'filename_url': record.filename_url, 'filename_url': record.filename_url,
}) })
@@ -78,18 +76,14 @@ class MrpWorkorder(models.Model):
demand_plan_print = self.env['sf.demand.plan.print.wizard'].sudo().search( demand_plan_print = self.env['sf.demand.plan.print.wizard'].sudo().search(
[('model_id', '=', record.model_id), ('type', '=', '2')]) [('model_id', '=', record.model_id), ('type', '=', '2')])
if demand_plan_print: if demand_plan_print:
self.env['sf.demand.plan.print.wizard'].sudo().write( demand_plan_print.write(
{'cnc_worksheet': record.cnc_worksheet, 'filename_url': record.cnc_worksheet_name}) {'cnc_worksheet': record.cnc_worksheet, 'filename_url': record.cnc_worksheet_name})
else: else:
demand_plan = self.env['sf.production.demand.plan'].sudo().search( wizard_vals = {
[('product_id', '=', record.product_id.id)]) 'model_id': record.model_id,
if demand_plan: 'type': '2',
wizard_vals = { 'cnc_worksheet': record.cnc_worksheet,
'demand_plan_id': demand_plan.id, 'filename_url': record.cnc_worksheet_name
'model_id': demand_plan.model_id, }
'type': '2', self.env['sf.demand.plan.print.wizard'].sudo().create(wizard_vals)
'cnc_worksheet': record.cnc_worksheet,
'filename_url': record.cnc_worksheet_name
}
self.env['sf.demand.plan.print.wizard'].sudo().create(wizard_vals)
return res return res

View File

@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
import logging
from odoo import models, fields, api, _
from werkzeug.exceptions import InternalServerError
_logger = logging.getLogger(__name__)
class SfReleasePlanWizard(models.TransientModel):
_name = 'sf.release.plan.wizard'
_description = u'下达计划向导'
demand_plan_line_id = fields.Many2many(comodel_name="sf.production.demand.plan",
string="需求计划明细", readonly=True)
release_message = fields.Char(string='提示', readonly=True)
def confirm(self):
if self.demand_plan_line_id:
for demand_plan_line_id in self.demand_plan_line_id:
try:
demand_plan_line_id.action_confirm()
except Exception as e:
self.env.cr.rollback()
demand_plan_line_id.write({'is_processing': False})
self.env.cr.commit()
raise InternalServerError('操作失败', e)

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.ui.view" id="sf_release_plan_wizard_form">
<field name="name">sf.release.plan.wizard.form</field>
<field name="model">sf.release.plan.wizard</field>
<field name="arch" type="xml">
<form>
<sheet>
<div>
<div style="white-space: pre-wrap;">
<field name="release_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

@@ -3,6 +3,7 @@ import logging
import re import re
from odoo import models, fields, api from odoo import models, fields, api
from odoo.exceptions import ValidationError
class ResProductCategory(models.Model): class ResProductCategory(models.Model):
@@ -47,11 +48,14 @@ class ResMrpBomMo(models.Model):
item.subcontractor_name = '' item.subcontractor_name = ''
def bom_create_line_has(self, embryo): def bom_create_line_has(self, embryo):
product = self.product_tmpl_id
if product.unit_number in (0, None, False):
raise ValidationError(f'产品{product.name}单件用量的值不能为{product.unit_number}')
vals = { vals = {
'bom_id': self.id, 'bom_id': self.id,
'product_id': embryo.id, 'product_id': embryo.id,
'product_tmpl_id': embryo.product_tmpl_id.id, 'product_tmpl_id': embryo.product_tmpl_id.id,
'product_qty': 1, 'product_qty': product.unit_number,
'product_uom_id': 1 'product_uom_id': 1
} }
return self.env['mrp.bom.line'].sudo().create(vals) return self.env['mrp.bom.line'].sudo().create(vals)
@@ -122,7 +126,7 @@ class ResMrpBomMo(models.Model):
# 查bom的原材料 # 查bom的原材料
def get_raw_bom(self, product): def get_raw_bom(self, product):
raw_bom = self.env['product.product'].search( raw_bom = self.env['product.product'].search(
[('categ_id.type', '=', '原材料'), ('materials_type_id', '=', product.materials_type_id.id)],limit=1) [('categ_id.type', '=', '原材料'), ('materials_type_id', '=', product.materials_type_id.id)], limit=1)
return raw_bom return raw_bom

View File

@@ -6,50 +6,50 @@ from odoo import models, fields, api, _
class StockRuleInherit(models.Model): class StockRuleInherit(models.Model):
_inherit = 'stock.rule' _inherit = 'stock.rule'
@api.model # @api.model
def _run_buy(self, procurements): # def _run_buy(self, procurements):
# 判断补货组的采购类型 # # 判断补货组的采购类型
procurements_group = {'standard': [], 'outsourcing': []} # procurements_group = {'standard': [], 'outsourcing': []}
for procurement, rule in procurements: # for procurement, rule in procurements:
is_outsourcing = False # is_outsourcing = False
product = procurement.product_id # product = procurement.product_id
# 获取主 BOM # # 获取主 BOM
bom = self.env['mrp.bom'].search([('product_tmpl_id', '=', product.product_tmpl_id.id)], limit=1) # bom = self.env['mrp.bom'].search([('product_tmpl_id', '=', product.product_tmpl_id.id)], limit=1)
#
if bom: # if bom:
# 遍历 BOM 中的组件(即坯料等) # # 遍历 BOM 中的组件(即坯料等)
for line in bom.bom_line_ids: # for line in bom.bom_line_ids:
raw_material = line.product_id # raw_material = line.product_id
# 检查路线 # # 检查路线
for route in raw_material.route_ids: # for route in raw_material.route_ids:
# print('route.name:', route.name) # # print('route.name:', route.name)
if route.name == '按订单补给外包商': # if route.name == '按订单补给外包商':
is_outsourcing = True # is_outsourcing = True
#
if is_outsourcing: # if is_outsourcing:
procurements_group['outsourcing'].append((procurement, rule)) # procurements_group['outsourcing'].append((procurement, rule))
else: # else:
procurements_group['standard'].append((procurement, rule)) # procurements_group['standard'].append((procurement, rule))
#
for key, value in procurements_group.items(): # for key, value in procurements_group.items():
super(StockRuleInherit, self)._run_buy(value) # super(StockRuleInherit, self)._run_buy(value)
#
if key == 'outsourcing': # if key == 'outsourcing':
for procurement, rule in value: # for procurement, rule in value:
supplier = procurement.values.get('supplier') # supplier = procurement.values.get('supplier')
if supplier: # if supplier:
domain = rule._make_po_get_domain(procurement.company_id, procurement.values, # domain = rule._make_po_get_domain(procurement.company_id, procurement.values,
supplier.partner_id) # supplier.partner_id)
logging.info("domain=============: %s", domain) # logging.info("domain=============: %s", domain)
po = self.env['purchase.order'].sudo().search([ # po = self.env['purchase.order'].sudo().search([
('partner_id', '=', supplier.partner_id.id), # ('partner_id', '=', supplier.partner_id.id),
('company_id', '=', procurement.company_id.id), # 保证公司一致 # ('company_id', '=', procurement.company_id.id), # 保证公司一致
('origin', 'like', procurement.origin), # 根据来源匹配 # ('origin', 'like', procurement.origin), # 根据来源匹配
('state', '=', 'draft') # 状态为草稿 # ('state', '=', 'draft') # 状态为草稿
], limit=1) # ], limit=1)
logging.info("po=: %s", po) # logging.info("po=: %s", po)
if po: # if po:
po.write({'purchase_type': 'outsourcing'}) # po.write({'purchase_type': 'outsourcing'})
# # 首先调用父类的 _run_buy 方法,以保留原有逻辑 # # 首先调用父类的 _run_buy 方法,以保留原有逻辑
# super(StockRuleInherit, self)._run_buy(procurements) # super(StockRuleInherit, self)._run_buy(procurements)

View File

@@ -95,6 +95,8 @@
<page string="加工参数"> <page string="加工参数">
<group> <group>
<group string="模型"> <group string="模型">
<field name="blank_type" readonly="1"/>
<field name="blank_precision" readonly="1"/>
<label for="model_long" string="坯料尺寸[mm]"/> <label for="model_long" string="坯料尺寸[mm]"/>
<div class="o_address_format"> <div class="o_address_format">
<label for="model_long" string="长"/> <label for="model_long" string="长"/>
@@ -104,7 +106,7 @@
<label for="model_height" string="高"/> <label for="model_height" string="高"/>
<field name="model_height" class="o_address_zip"/> <field name="model_height" class="o_address_zip"/>
</div> </div>
<field name="blank_type" readonly="1"/> <field name="unit_number" readonly="1"/>
<field name="model_volume" string="体积[mm³]"/> <field name="model_volume" string="体积[mm³]"/>
<field name="product_model_type_id" string="模型类型"/> <field name="product_model_type_id" string="模型类型"/>
<field name="model_processing_panel" placeholder="例如R,U" string="加工面板" <field name="model_processing_panel" placeholder="例如R,U" string="加工面板"
@@ -511,112 +513,168 @@
</notebook> </notebook>
</page> </page>
<page string="夹具物料参数" attrs="{'invisible': [('categ_type', '!=', '夹具')]}"> <page string="夹具物料参数" attrs="{'invisible': [('categ_type', '!=', '夹具')]}">
<group>
<group> <group>
<field name="brand_id" placeholder="请选择" options="{'no_create': True}"/> <group>
<field name="multi_mounting_type_id" placeholder="请选择" options="{'no_create': True}" <field name="brand_id" placeholder="请选择" options="{'no_create': True}"/>
attrs="{'required': [('categ_type', '=', '夹具')]}"/> <field name="multi_mounting_type_id" placeholder="请选择" options="{'no_create': True}"
<field name="length" string="长度(mm)"/> attrs="{'required': [('categ_type', '=', '夹具')]}"/>
<field name="width" string="度(mm)"/> <field name="length" string="度(mm)"/>
<field name="height" string="度(mm)"/> <field name="width" string="度(mm)"/>
<field name="height_tolerance_value" <field name="height" string="高度(mm)"/>
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸夹具'])]}"/> <field name="height_tolerance_value"
<field name="diameter" attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸夹具'])]}"/>
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点卡盘', '零点托盘', '三爪卡盘'])]}"/> <field name="diameter"
<field name="weight"/> attrs="{'invisible': [('fixture_material_type', 'not in', ['零点卡盘', '零点托盘', '三爪卡盘', '磁吸托盘', '气吸托盘'])]}"/>
<field name="chucking_power_max" <field name="weight"/>
attrs="{'invisible': [('fixture_material_type', '=','磁吸夹具')]}"/> <field name="chucking_power_max"
<field name="carrying_capacity_max"/> attrs="{'invisible': [('fixture_material_type', '=','磁吸夹具')]}"/>
<field name="rigidity"/> <field name="carrying_capacity_max"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸夹具'])]}"/>
<field name="rigidity"/>
</group>
<group>
<!-- 夹持工件尺寸 -->
<label for="gripper_length_min" string="夹持工件最小尺寸"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气动夹具','虎钳夹具','磁吸夹具','转接板(锁板)夹具','三爪卡盘'])]}"/>
<div class="o_address_format"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气动夹具','虎钳夹具','磁吸夹具','转接板(锁板)夹具','三爪卡盘'])]}">
<label for="gripper_length_min" string="长"/>
<field name="gripper_length_min" class="o_address_zip"/>
<span>&amp;nbsp;</span>
<label for="gripper_width_min" string="宽"/>
<field name="gripper_width_min" class="o_address_zip"/>
<span>&amp;nbsp;</span>
<label for="gripper_height_min" string="高"/>
<field name="gripper_height_min" class="o_address_zip"/>
</div>
<label for="gripper_length_max" string="夹持工件最大尺寸"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气动夹具','虎钳夹具','磁吸夹具','转接板(锁板)夹具','三爪卡盘'])]}"/>
<div class="o_address_format"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气动夹具','虎钳夹具','磁吸夹具','转接板(锁板)夹具','三爪卡盘'])]}">
<label for="gripper_length_max" string="长"/>
<field name="gripper_length_max" class="o_address_zip"/>
<span>&amp;nbsp;</span>
<label for="gripper_width_max" string="宽"/>
<field name="gripper_width_max" class="o_address_zip"/>
<span>&amp;nbsp;</span>
<label for="gripper_height_max" string="高"/>
<field name="gripper_height_max" class="o_address_zip"/>
</div>
<field name="gripper_diameter_min" string="夹持工件最小直径(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气动夹具','虎钳夹具','磁吸夹具','转接板(锁板)夹具','三爪卡盘'])]}"/>
<field name="gripper_diameter_max" string="夹持工件最大直径(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气动夹具','虎钳夹具','磁吸夹具','转接板(锁板)夹具','三爪卡盘'])]}"/>
<field name="clamping_diameter" string="装夹直径(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点卡盘','零点托盘'])]}"/>
<field name="clamping_num" placeholder="请选择" string="装夹单元数"
attrs="{'invisible': [('fixture_material_type', '!=', '零点卡盘')]}"/>
<field name="repeated_positioning_accuracy" placeholder="请输入重复定位孔精度"
string="重复定位精度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点卡盘','零点托盘'])]}"/>
<field name="orientation_dish_diameter" string="定位盘直径(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点卡盘'])]}"/>
<field name="boolean_transposing_hole" string="是否有转位孔"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点卡盘'])]}"/>
<field name="connector_diameter" placeholder="请选择" string="连接头直径(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点托盘'])]}"/>
<field name="way_to_install" placeholder="请选择" string="安装方式"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点托盘','气吸托盘'])]}"/>
<field name="rated_air_pressure" string="额定气压(Mpa)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气动夹具'])]}"/>
<field name="transverse_groove" string="横向配合槽n(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['虎钳夹具'])]}"/>
<field name="longitudinal_fitting_groove" string="纵向配合槽l(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['虎钳夹具'])]}"/>
<field name="rated_adsorption_force" string="额定吸附力(N/cm²)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸夹具'])]}"/>
<field name="magnetic_field_height" string="磁场高度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸夹具'])]}"/>
<field name="magnetic_pole_plate_grinding_allowance" string="磁极板磨削余量(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸夹具'])]}"/>
<!-- 磁吸托盘字段 -->
<field name="magnet_tray_length" string="磁吸托盘长度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸托盘'])]}"/>
<field name="magnet_tray_width" string="磁吸托盘宽度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸托盘'])]}"/>
<field name="magnet_tray_height" string="磁吸托盘厚度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸托盘'])]}"/>
<field name="magnet_tray_diameter" string="磁吸托盘直径(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸托盘'])]}"/>
<field name="magnet_tray_weight" string="磁吸托盘重量(kg)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸托盘'])]}"/>
<field name="magnet_max_adsorp_length" string="磁吸托盘最大吸附长度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸托盘'])]}"/>
<field name="magnet_max_adsorp_width" string="磁吸托盘最大吸附宽度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸托盘'])]}"/>
<field name="magnet_max_adsorp_height" string="磁吸托盘最大吸附厚度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸托盘'])]}"/>
<field name="magnet_max_adsorp_diameter" string="磁吸托盘最大吸附直径(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸托盘'])]}"/>
<field name="magnet_max_adsorp_force" string="磁吸托盘最大吸附力(N)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸托盘'])]}"/>
<field name="magnet_unlocking_method" string="磁吸托盘锁紧方式"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸托盘'])]}"/>
<field name="magnet_flatness" string="磁吸托盘平面精度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸托盘'])]}"/>
<field name="magnet_max_load" string="磁吸托盘最大负载(kg)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸托盘'])]}"/>
<!-- 气吸托盘字段 -->
<field name="air_tray_length" string="气吸托盘长度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气吸托盘'])]}"/>
<field name="air_tray_width" string="气吸托盘宽度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气吸托盘'])]}"/>
<field name="air_tray_height" string="气吸托盘高度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气吸托盘'])]}"/>
<field name="air_tray_diameter" string="气吸托盘直径(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气吸托盘'])]}"/>
<field name="air_tray_weight" string="气吸托盘重量(kg)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气吸托盘'])]}"/>
<field name="air_max_adsorp_length" string="气吸托盘最大吸附长度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气吸托盘'])]}"/>
<field name="air_max_adsorp_width" string="气吸托盘最大吸附宽度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气吸托盘'])]}"/>
<field name="air_max_adsorp_height" string="气吸托盘最大吸附高度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气吸托盘'])]}"/>
<field name="air_max_adsorp_diameter" string="气吸托盘最大吸附直径(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气吸托盘'])]}"/>
<field name="air_max_adsorp_force" string="气吸托盘最大吸附力(N)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气吸托盘'])]}"/>
<field name="air_unlocking_method" string="气吸托盘锁紧方式"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气吸托盘'])]}"/>
<field name="air_flatness" string="气吸托盘平面精度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气吸托盘'])]}"/>
<field name="air_max_load" string="气吸托盘最大负载(kg)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气吸托盘'])]}"/>
<field name="air_boolean_chip_blowing_function" string="气吸托盘是否有吹屑功能"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气吸托盘'])]}"/>
<field name="air_way_to_install" string="气吸托盘安装方式"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气吸托盘'])]}"/>
<field name="boolean_chip_blowing_function" string="是否有吹屑功能"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点卡盘','零点托盘','气吸托盘'])]}"/>
<field name="materials_model_id" placeholder="请选择" options="{'no_create': True}"/>
<field name="interface_materials_model_id" placeholder="请选择" string="接口类型"
options="{'no_create': True}"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气动夹具','虎钳夹具','磁吸夹具'])]}"/>
<field name="type_of_drive" placeholder="请选择" string="驱动方式"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点托盘','气动夹具','虎钳夹具','磁吸夹具','转接板(锁板)夹具','三爪卡盘'])]}"/>
<field name="unlocking_method" string="解锁方式" placeholder="请选择"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点卡盘','气吸托盘', '磁吸托盘'])]}"/>
<field name="machine_tool_type_id" string="适用机床型号" placeholder="请选择"
options="{'no_create': True}"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点卡盘'])]}"/>
</group>
</group> </group>
<group> </page>
<label for="gripper_length_min" string="夹持工件最小尺寸"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气动夹具','虎钳夹具','磁吸夹具','转接板(锁板)夹具','三爪卡盘'])]}"/>
<div class="o_address_format"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气动夹具','虎钳夹具','磁吸夹具','转接板(锁板)夹具','三爪卡盘'])]}">
<label for="gripper_length_min" string="长"/>
<field name="gripper_length_min" class="o_address_zip"/>
<span>&amp;nbsp;</span>
<label for="gripper_width_min" string="宽"/>
<field name="gripper_width_min" class="o_address_zip"/>
<span>&amp;nbsp;</span>
<label for="gripper_height_min" string="高"/>
<field name="gripper_height_min" class="o_address_zip"/>
</div>
<label for="gripper_length_max" string="夹持工件最大尺寸"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气动夹具','虎钳夹具','磁吸夹具','转接板(锁板)夹具','三爪卡盘'])]}"/>
<div class="o_address_format"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气动夹具','虎钳夹具','磁吸夹具','转接板(锁板)夹具','三爪卡盘'])]}">
<label for="gripper_length_max" string="长"/>
<field name="gripper_length_max" class="o_address_zip"/>
<span>&amp;nbsp;</span>
<label for="gripper_width_max" string="宽"/>
<field name="gripper_width_max" class="o_address_zip"/>
<span>&amp;nbsp;</span>
<label for="gripper_width_max" string="高"/>
<field name="gripper_width_max" class="o_address_zip"/>
</div>
<field name="gripper_diameter_min" string="夹持工件最小直径(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气动夹具','虎钳夹具','磁吸夹具','转接板(锁板)夹具','三爪卡盘'])]}"/>
<field name="gripper_diameter_max" string="夹持工件最大直径(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气动夹具','虎钳夹具','磁吸夹具','转接板(锁板)夹具','三爪卡盘'])]}"/>
<field name="clamping_diameter" string="装夹直径(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点卡盘','零点托盘'])]}"/>
<field name="clamping_num" placeholder="请选择" string="装夹单元数"
attrs="{'invisible': [('fixture_material_type', '!=', '零点卡盘')]}"/>
<field name="repeated_positioning_accuracy" placeholder="请输入重复定位孔精度"
string="重复定位精度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点卡盘','零点托盘'])]}"/>
<field name="orientation_dish_diameter" string="定位盘直径(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点卡盘'])]}"/>
<field name="boolean_transposing_hole" string="是否有转位孔"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点卡盘'])]}"/>
<field name="connector_diameter" placeholder="请选择" string="连接头直径(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点托盘'])]}"/>
<field name="way_to_install" placeholder="请选择" string="安装方式"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点托盘'])]}"/>
<field name="rated_air_pressure" string="额定气压(Mpa)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气动夹具'])]}"/>
<field name="transverse_groove" string="横向配合槽n(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['虎钳夹具'])]}"/>
<field name="longitudinal_fitting_groove" string="纵向配合槽l(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['虎钳夹具'])]}"/>
<field name="rated_adsorption_force" string="额定吸附力(N/cm²)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸夹具'])]}"/>
<field name="magnetic_field_height" string="磁场高度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸夹具'])]}"/>
<field name="magnetic_pole_plate_grinding_allowance" string="磁极板磨削余量(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['磁吸夹具'])]}"/>
<field name="screw_size" string="螺牙大小(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['转接板(锁板)夹具'])]}"/>
<field name="via_hole_diameter" string="过孔直径(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['转接板(锁板)夹具'])]}"/>
<field name="mounting_hole_depth" string="安装孔深度(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['三爪卡盘'])]}"/>
<field name="centering_diameter" string="定心直径(mm)"
attrs="{'invisible': [('fixture_material_type', 'not in', ['三爪卡盘'])]}"/>
<field name="boolean_chip_blowing_function" string="是否有吹屑功能"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点卡盘','零点托盘'])]}"/>
<field name="materials_model_id" placeholder="请选择" options="{'no_create': True}"/>
<field name="interface_materials_model_id" placeholder="请选择" string="接口类型"
options="{'no_create': True}"
attrs="{'invisible': [('fixture_material_type', 'not in', ['气动夹具','虎钳夹具','磁吸夹具'])]}"/>
<field name="type_of_drive" placeholder="请选择" string="驱动方式"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点托盘','气动夹具','虎钳夹具','磁吸夹具','转接板(锁板)夹具','三爪卡盘'])]}"/>
<field name="unlocking_method" string="解锁方式" placeholder="请选择"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点卡盘'])]}"/>
<field name="machine_tool_type_id" string="适用机床型号" placeholder="请选择"
options="{'no_create': True}"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点卡盘'])]}"/>
</group>
</group>
</page>
</xpath> </xpath>
</field> </field>
</record> </record>

View File

@@ -11,7 +11,7 @@
'category': 'sf', 'category': 'sf',
'website': 'https://www.sf.jikimo.com', 'website': 'https://www.sf.jikimo.com',
'depends': ['sf_base', 'sf_maintenance', 'web_widget_model_viewer', 'sf_warehouse', 'jikimo_attachment_viewer', 'depends': ['sf_base', 'sf_maintenance', 'web_widget_model_viewer', 'sf_warehouse', 'jikimo_attachment_viewer',
'jikimo_sale_multiple_supply_methods', 'product'], 'jikimo_sale_multiple_supply_methods', 'product', 'jikimo_tree_header_buttons_always_visible'],
'data': [ 'data': [
'data/cron_data.xml', 'data/cron_data.xml',
'data/stock_data.xml', 'data/stock_data.xml',

View File

@@ -27,13 +27,15 @@ class ResProductMo(models.Model):
categ_type = fields.Selection(string='产品的类别', related='categ_id.type', store=True) categ_type = fields.Selection(string='产品的类别', related='categ_id.type', store=True)
model_name = fields.Char('模型名称') model_name = fields.Char('模型名称')
blank_type = fields.Selection([('圆料', '圆料'), ('方料', '方料')], string='坯料分类') blank_type = fields.Selection([('圆料', '圆料'), ('方料', '方料')], string='坯料分类')
blank_precision = fields.Selection([('精坯', '精坯'), ('粗坯', '粗坯')], string='坯料类型')
model_long = fields.Float('模型长(mm)', digits=(16, 3)) model_long = fields.Float('模型长(mm)', digits=(16, 3))
model_width = fields.Float('模型宽(mm)', digits=(16, 3)) model_width = fields.Float('模型宽(mm)', digits=(16, 3))
model_height = fields.Float('模型高(mm)', digits=(16, 3)) model_height = fields.Float('模型高(mm)', digits=(16, 3))
unit_number = fields.Float('单件用量', digits=(16, 3), default=1)
model_volume = fields.Float('模型体积(m³)') model_volume = fields.Float('模型体积(m³)')
model_area = fields.Float('模型表面积(m²)') model_area = fields.Float('模型表面积(m²)')
model_machining_precision = fields.Selection(selection=_get_machining_precision, string='加工精度') model_machining_precision = fields.Selection(selection=_get_machining_precision, string='加工精度')
model_processing_panel = fields.Char('模型加工面板') model_processing_panel = fields.Char('模型加工面板', default='')
model_remark = fields.Char('模型备注说明') model_remark = fields.Char('模型备注说明')
length = fields.Float('长(mm)', digits=(16, 3)) length = fields.Float('长(mm)', digits=(16, 3))
width = fields.Float('宽(mm)', digits=(16, 3)) width = fields.Float('宽(mm)', digits=(16, 3))
@@ -901,15 +903,17 @@ class ResProductMo(models.Model):
vals = { vals = {
'name': product_name, 'name': product_name,
'blank_type': item.get('blank_type'), 'blank_type': item.get('blank_type'),
'blank_precision': item.get('blank_precision'),
'model_long': item.get('blank_length') if blank_bool else self.format_float(item['model_long'] + embryo_redundancy_id.long), 'model_long': item.get('blank_length') if blank_bool else self.format_float(item['model_long'] + embryo_redundancy_id.long),
'model_width': item.get('blank_width') if blank_bool else self.format_float(item['model_width'] + embryo_redundancy_id.width), 'model_width': item.get('blank_width') if blank_bool else self.format_float(item['model_width'] + embryo_redundancy_id.width),
'model_height': item.get('blank_height') if blank_bool else self.format_float(item['model_height'] + embryo_redundancy_id.height), 'model_height': item.get('blank_height') if blank_bool else self.format_float(item['model_height'] + embryo_redundancy_id.height),
'unit_number': item.get('unit_number'),
'model_volume': self.format_float(((item['model_long'] + embryo_redundancy_id.long) * 'model_volume': self.format_float(((item['model_long'] + embryo_redundancy_id.long) *
(item['model_width'] + embryo_redundancy_id.width) * (item['model_width'] + embryo_redundancy_id.width) *
(item['model_height'] + embryo_redundancy_id.height))) if not blank_bool else ( (item['model_height'] + embryo_redundancy_id.height))) if not blank_bool else (
item.get('blank_length') * item.get('blank_width') * item.get('blank_height')), item.get('blank_length') * item.get('blank_width') * item.get('blank_height')),
'product_model_type_id': model_type.id, 'product_model_type_id': model_type.id,
'model_processing_panel': item['processing_panel_detail'], 'model_processing_panel': item['processing_panel_detail'] if item['processing_panel_detail'] else '',
'model_machining_precision': item['model_machining_precision'], 'model_machining_precision': item['model_machining_precision'],
'model_code': item['barcode'], 'model_code': item['barcode'],
'length': item['model_long'], 'length': item['model_long'],
@@ -957,7 +961,7 @@ class ResProductMo(models.Model):
self.attachment_update(item['quality_standard_name'], copy_product_id.product_tmpl_id.id, self.attachment_update(item['quality_standard_name'], copy_product_id.product_tmpl_id.id,
'quality_standard', item['quality_standard_mimetype']) 'quality_standard', item['quality_standard_mimetype'])
return copy_product_id return copy_product_id
def format_float(self, value): def format_float(self, value):
# 将浮点数转换为字符串 # 将浮点数转换为字符串
value_str = str(value) value_str = str(value)
@@ -1211,6 +1215,51 @@ class ResProductFixture(models.Model):
mounting_hole_depth = fields.Float('安装孔深度(mm)', digits=(16, 2)) mounting_hole_depth = fields.Float('安装孔深度(mm)', digits=(16, 2))
centering_diameter = fields.Float('定心直径(mm)', digits=(16, 2)) centering_diameter = fields.Float('定心直径(mm)', digits=(16, 2))
# ‘磁吸托盘’ 字段
magnet_tray_length = fields.Float('磁吸托盘长度(mm)', digits=(16, 2))
magnet_tray_width = fields.Float('磁吸托盘宽度(mm)', digits=(16, 2))
magnet_tray_height = fields.Float('磁吸托盘厚度(mm)', digits=(16, 2))
magnet_tray_diameter = fields.Float('磁吸托盘直径(mm)', digits=(16, 2))
magnet_tray_weight = fields.Float('磁吸托盘重量(kg)', digits=(16, 2))
magnet_max_adsorp_length = fields.Float('磁吸托盘最大吸附长度(mm)', digits=(16, 2))
magnet_max_adsorp_width = fields.Float('磁吸托盘最大吸附宽度(mm)', digits=(16, 2))
magnet_max_adsorp_height = fields.Float('磁吸托盘最大吸附厚度(mm)', digits=(16, 2))
magnet_max_adsorp_diameter = fields.Float('磁吸托盘最大吸附直径(mm)', digits=(16, 2))
magnet_max_adsorp_force = fields.Float('磁吸托盘最大吸附力(N)', digits=(16, 2))
magnet_unlocking_method = fields.Selection(
[('手动', '手动'), ('气动', '气动'), ('液压', '液压'), ('电动', '电动'), ('其他', '其他')],
string='磁吸托盘锁紧方式'
)
magnet_flatness = fields.Char('磁吸托盘平面精度(mm)', size=20)
magnet_max_load = fields.Float('磁吸托盘最大负载(kg)', digits=(16, 2))
# ‘气吸托盘’ 字段
air_tray_length = fields.Float('气吸托盘长度(mm)', digits=(16, 2))
air_tray_width = fields.Float('气吸托盘宽度(mm)', digits=(16, 2))
air_tray_height = fields.Float('气吸托盘高度(mm)', digits=(16, 2))
air_tray_diameter = fields.Float('气吸托盘直径(mm)', digits=(16, 2))
air_tray_weight = fields.Float('气吸托盘重量(kg)', digits=(16, 2))
air_max_adsorp_length = fields.Float('气吸托盘最大吸附长度(mm)', digits=(16, 2))
air_max_adsorp_width = fields.Float('气吸托盘最大吸附宽度(mm)', digits=(16, 2))
air_max_adsorp_height = fields.Float('气吸托盘最大吸附厚度(mm)', digits=(16, 2))
air_max_adsorp_diameter = fields.Float('气吸托盘最大吸附直径(mm)', digits=(16, 2))
air_max_adsorp_force = fields.Float('气吸托盘最大吸附力(N)', digits=(16, 2))
air_unlocking_method = fields.Selection(
[('手动', '手动'), ('气动', '气动'), ('液压', '液压'), ('电动', '电动'), ('其他', '其他')],
string='气吸托盘锁紧方式'
)
air_flatness = fields.Char('气吸托盘平面精度(mm)', size=20)
air_max_load = fields.Float('气吸托盘最大负载(kg)', digits=(16, 2))
air_boolean_chip_blowing_function = fields.Boolean('气吸托盘是否有吹屑功能')
air_way_to_install = fields.Selection(
[('接口式', '接口式'), ('螺栓固定', '螺栓固定'), ('磁吸式', '磁吸式'), ('其他', '其他')],
string='气吸托盘安装方式'
)
@api.onchange('specification_fixture_id') @api.onchange('specification_fixture_id')
def _onchange_specification_fixture_id(self): def _onchange_specification_fixture_id(self):
if self.specification_fixture_id: if self.specification_fixture_id:

View File

@@ -21,6 +21,7 @@ from odoo.addons.sf_base.commons.common import Common
from odoo.exceptions import UserError from odoo.exceptions import UserError
from io import BytesIO from io import BytesIO
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
from dateutil.relativedelta import relativedelta
class stockWarehouse(models.Model): class stockWarehouse(models.Model):
@@ -216,7 +217,8 @@ class StockRule(models.Model):
''' '''
创建制造订单时生成序列号 创建制造订单时生成序列号
''' '''
production.action_generate_serial() if production.product_id.tracking != "none":
production.action_generate_serial()
origin_production = production.move_dest_ids and production.move_dest_ids[ origin_production = production.move_dest_ids and production.move_dest_ids[
0].raw_material_production_id or False 0].raw_material_production_id or False
orderpoint = production.orderpoint_id orderpoint = production.orderpoint_id
@@ -441,7 +443,7 @@ class ProductionLot(models.Model):
@api.model @api.model
def _get_next_serial(self, company, product): def _get_next_serial(self, company, product):
"""Return the next serial number to be attributed to the product.""" """Return the next serial number to be attributed to the product."""
if product.tracking == "serial": if product.tracking != "none":
last_serial = self.env['stock.lot'].search( last_serial = self.env['stock.lot'].search(
[('company_id', '=', company.id), ('product_id', '=', product.id), ('name', 'ilike', product.name)], [('company_id', '=', company.id), ('product_id', '=', product.id), ('name', 'ilike', product.name)],
limit=1, order='name desc') limit=1, order='name desc')
@@ -452,7 +454,9 @@ class ProductionLot(models.Model):
return self.env['stock.lot'].generate_lot_names1(product.name, last_serial.name if ( return self.env['stock.lot'].generate_lot_names1(product.name, last_serial.name if (
not move_line_id or not move_line_id or
(last_serial and last_serial.name > move_line_id.lot_name)) else move_line_id.lot_name, 2)[1] (last_serial and last_serial.name > move_line_id.lot_name)) else move_line_id.lot_name, 2)[1]
return "%s-%03d" % (product.name, 1) else:
return "%s-%03d" % (product.name, 1)
return False
qr_code_image = fields.Binary(string='二维码', compute='_generate_qr_code') qr_code_image = fields.Binary(string='二维码', compute='_generate_qr_code')
@@ -727,6 +731,33 @@ class StockPicking(models.Model):
production.workorder_ids.write({'back_button_display': False}) production.workorder_ids.write({'back_button_display': False})
return res return res
def _prepare_subcontract_mo_vals(self, subcontract_move, bom):
subcontract_move.ensure_one()
group = self.env['procurement.group'].sudo().search([('name', '=', self.name)])
if not group:
group = self.env['procurement.group'].create({
'name': self.name,
'partner_id': self.partner_id.id,
})
product = subcontract_move.product_id
warehouse = self._get_warehouse(subcontract_move)
vals = {
'company_id': subcontract_move.company_id.id,
'procurement_group_id': group.id,
'subcontractor_id': subcontract_move.picking_id.partner_id.commercial_partner_id.id,
'picking_ids': [subcontract_move.picking_id.id],
'product_id': product.id,
'product_uom_id': subcontract_move.product_uom.id,
'bom_id': bom.id,
'location_src_id': subcontract_move.picking_id.partner_id.with_company(subcontract_move.company_id).property_stock_subcontractor.id,
'location_dest_id': subcontract_move.picking_id.partner_id.with_company(subcontract_move.company_id).property_stock_subcontractor.id,
'product_qty': subcontract_move.product_uom_qty,
'picking_type_id': warehouse.subcontracting_type_id.id,
'date_planned_start': subcontract_move.date - relativedelta(days=product.produce_delay)
}
return vals
# 创建 外协出库入单 # 创建 外协出库入单
def create_outcontract_picking(self, workorders, item, sorted_workorders): def create_outcontract_picking(self, workorders, item, sorted_workorders):
production = workorders[0].production_id production = workorders[0].production_id

View File

@@ -44,7 +44,8 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
if productions: if productions:
# 修改需求计划中的程序工时 # 修改需求计划中的程序工时
demand_plan = request.env['sf.production.demand.plan'].with_user( demand_plan = request.env['sf.production.demand.plan'].with_user(
request.env.ref("base.user_admin")).search([('model_id', '=', ret['folder_name'])]) request.env.ref("base.user_admin")).search(
[('model_id', '=', ret['folder_name']), ('new_supply_method', '=', 'custom_made')])
if demand_plan and ret['total_estimated_time']: if demand_plan and ret['total_estimated_time']:
demand_plan.write( demand_plan.write(
{'processing_time': ret['total_estimated_time']}) {'processing_time': ret['total_estimated_time']})

View File

@@ -9,6 +9,7 @@ from odoo import models
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
from odoo.addons.sf_base.commons.common import Common from odoo.addons.sf_base.commons.common import Common
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@@ -178,6 +179,7 @@ class sfMaterialModel(models.Model):
materials_model.mf_materia_post = item['mf_materia_post'] materials_model.mf_materia_post = item['mf_materia_post']
materials_model.materials_id = materials.id materials_model.materials_id = materials.id
materials_model.need_h = item['need_h'] materials_model.need_h = item['need_h']
materials_model.need_m = item['need_m']
materials_model.density = item['density'] materials_model.density = item['density']
materials_model.active = item['active'] materials_model.active = item['active']
else: else:
@@ -192,6 +194,7 @@ class sfMaterialModel(models.Model):
"active": item['active'], "active": item['active'],
"materials_id": materials.id, "materials_id": materials.id,
"need_h": item['need_h'], "need_h": item['need_h'],
"need_m": item['need_m'],
"mf_materia_post": item['mf_materia_post'], "mf_materia_post": item['mf_materia_post'],
"density": item['density'], "density": item['density'],
}) })
@@ -228,6 +231,7 @@ class sfMaterialModel(models.Model):
"standards_id": self.env['sf.international.standards'].search( "standards_id": self.env['sf.international.standards'].search(
[("name", '=', item['standards_id'])]).id, [("name", '=', item['standards_id'])]).id,
"need_h": item['need_h'], "need_h": item['need_h'],
"need_m": item['need_m'],
"alloy_code": item['alloy_code'], "alloy_code": item['alloy_code'],
"mf_materia_post": item['mf_materia_post'], "mf_materia_post": item['mf_materia_post'],
"density": item['density'], "density": item['density'],
@@ -248,6 +252,7 @@ class sfMaterialModel(models.Model):
materials_model.mf_materia_post = item['mf_materia_post'] materials_model.mf_materia_post = item['mf_materia_post']
materials_model.materials_id = materials.id materials_model.materials_id = materials.id
materials_model.need_h = item['need_h'] materials_model.need_h = item['need_h']
materials_model.need_m = item['need_m']
materials_model.density = item['density'] materials_model.density = item['density']
materials_model.active = item['active'] materials_model.active = item['active']
materials_model.materials_code= item['materials_code'] materials_model.materials_code= item['materials_code']
@@ -1610,6 +1615,10 @@ class SyncfixtureMaterialsBasicParameters(models.Model):
self._write_or_create(all_list.get('adapter_board_yesterday_list'), '转接板(锁板)夹具') self._write_or_create(all_list.get('adapter_board_yesterday_list'), '转接板(锁板)夹具')
if all_list.get('scroll_chuck_all_list'): if all_list.get('scroll_chuck_all_list'):
self._write_or_create(all_list.get('scroll_chuck_yesterday_list'), '三爪卡盘') self._write_or_create(all_list.get('scroll_chuck_yesterday_list'), '三爪卡盘')
if all_list.get('air_blow_tray_all_list'):
self._write_or_create(all_list.get('air_tray_yesterday_list'), '气吸托盘')
if all_list.get('magnet_tray_all_list'):
self._write_or_create(all_list.get('magnet_tray_yesterday_list'), '磁吸托盘')
else: else:
raise ValidationError("夹具型号基本参数认证未通过") raise ValidationError("夹具型号基本参数认证未通过")
@@ -1638,6 +1647,10 @@ class SyncfixtureMaterialsBasicParameters(models.Model):
self._write_or_create(all_list.get('adapter_board_all_list'), '转接板(锁板)夹具') self._write_or_create(all_list.get('adapter_board_all_list'), '转接板(锁板)夹具')
if all_list.get('scroll_chuck_all_list'): if all_list.get('scroll_chuck_all_list'):
self._write_or_create(all_list.get('scroll_chuck_all_list'), '三爪卡盘') self._write_or_create(all_list.get('scroll_chuck_all_list'), '三爪卡盘')
if all_list.get('air_blow_tray_all_list'):
self._write_or_create(all_list.get('air_tray_all_list'), '气吸托盘')
if all_list.get('magnet_tray_all_list'):
self._write_or_create(all_list.get('magnet_tray_all_list'), '磁吸托盘')
else: else:
raise ValidationError("夹具型号基本参数认证未通过") raise ValidationError("夹具型号基本参数认证未通过")
@@ -3230,4 +3243,4 @@ class EmbryoRedundancySync(models.Model):
"height": item['height'], "height": item['height'],
"active": item['active'], "active": item['active'],
"remark": item['remark'], "remark": item['remark'],
}) })

View File

@@ -45759,6 +45759,11 @@ msgstr ""
msgid "热处理" msgid "热处理"
msgstr "" msgstr ""
#. module: sf_base
#: model:ir.model.fields,field_description:sf_base.field_sf_materials_model__need_m
msgid "是否磁吸"
msgstr ""
#. module: sf_base #. module: sf_base
#: model:ir.model.fields,field_description:sf_base.field_sf_materials_model__mf_materia_post #: model:ir.model.fields,field_description:sf_base.field_sf_materials_model__mf_materia_post
msgid "热处理后密度" msgid "热处理后密度"

View File

@@ -22,6 +22,7 @@
'data/report_actions.xml', 'data/report_actions.xml',
'views/view.xml', 'views/view.xml',
'views/quality_cnc_test_view.xml', 'views/quality_cnc_test_view.xml',
'views/stock_picking.xml',
'views/mrp_workorder.xml', 'views/mrp_workorder.xml',
'views/quality_check_view.xml', 'views/quality_check_view.xml',
'views/quality_company.xml', 'views/quality_company.xml',

View File

@@ -1,24 +1,29 @@
import logging import logging
from odoo import api, models from odoo import api, models, fields
from odoo.exceptions import ValidationError, UserError from odoo.exceptions import ValidationError, UserError
class StockPicking(models.Model): class StockPicking(models.Model):
_inherit = 'stock.picking' _inherit = 'stock.picking'
whether_show_quality_check = fields.Boolean('是否显示质量检测按钮', default=True)
def _compute_check(self): def _compute_check(self):
super()._compute_check() super()._compute_check()
for picking in self: for picking in self:
picking_to_quality = picking.get_picking_to_quality() picking_to_quality = picking.get_picking_to_quality()
if not picking_to_quality: if not picking_to_quality:
picking.quality_check_todo = False picking.quality_check_todo = False
picking.whether_show_quality_check = False
break break
else: else:
picking.whether_show_quality_check = True
need_quality_line = picking.get_need_quality_line(picking_to_quality) need_quality_line = picking.get_need_quality_line(picking_to_quality)
if not need_quality_line or all(not line.get('need_done_check_ids') for line in need_quality_line): if not need_quality_line or all(not line.get('need_done_check_ids') for line in need_quality_line):
picking.quality_check_todo = False picking.quality_check_todo = False
def check_quality(self): def check_quality(self):
self.ensure_one() self.ensure_one()
# checkable_products = self.mapped('move_line_ids').mapped('product_id') # checkable_products = self.mapped('move_line_ids').mapped('product_id')
@@ -40,7 +45,8 @@ class StockPicking(models.Model):
""" """
out_quality_checks = self.env['quality.check'].search( out_quality_checks = self.env['quality.check'].search(
[('picking_id', '=', self.id), ('test_type_id.name', '=', '出厂检验报告')]) [('picking_id', '=', self.id), ('test_type_id.name', '=', '出厂检验报告'),
('quality_state', '=', 'pass')])
# out_quality_checks 可能存在多个 # out_quality_checks 可能存在多个
if out_quality_checks: if out_quality_checks:
for out_quality_check in out_quality_checks: for out_quality_check in out_quality_checks:

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="stock_picking_view_form_inherit_quality_sf_quality" model="ir.ui.view">
<field name="name">stock.picking.view.form.sf.quality</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="quality_control.stock_picking_view_form_inherit_quality"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<field name="whether_show_quality_check" invisible="1"/>
</xpath>
<xpath expr="//button[@name='action_open_quality_check_picking'][1]" position="attributes">
<attribute name="attrs">{'invisible': ['|', '|','|', ('check_ids', '=', []), ('quality_check_fail', '=',
True), ('quality_check_todo', '!=', True), ('whether_show_quality_check', '!=', True)]}
</attribute>
</xpath>
<xpath expr="//button[@name='action_open_quality_check_picking'][2]" position="attributes">
<attribute name="attrs">{'invisible': ['|', '|','|', ('check_ids', '=', []), ('quality_check_fail', '=',
True), ('quality_check_todo', '=', True), ('whether_show_quality_check', '!=', True)]}
</attribute>
</xpath>
<xpath expr="//button[@name='action_open_quality_check_picking'][3]" position="attributes">
<attribute name="attrs">{'invisible': ['|', '|',('check_ids', '=', []), ('quality_check_fail', '!=',
True), ('whether_show_quality_check', '!=', True)]}
</attribute>
</xpath>
</field>
</record>
</odoo>

View File

@@ -155,7 +155,7 @@ class ReSaleOrder(models.Model):
'glb_url': item['glb_url'], 'glb_url': item['glb_url'],
'remark': item.get('remark'), 'remark': item.get('remark'),
'embryo_redundancy_id': item.get('embryo_redundancy_id'), 'embryo_redundancy_id': item.get('embryo_redundancy_id'),
'is_incoming_material': True if item.get('embryo_redundancy_id') else False, 'is_incoming_material': True if item.get('incoming_size') else False,
'manual_quotation': item.get('manual_quotation'), 'manual_quotation': item.get('manual_quotation'),
'model_id': item['model_id'], 'model_id': item['model_id'],
'delivery_end_date': item['delivery_end_date'] 'delivery_end_date': item['delivery_end_date']
@@ -287,7 +287,7 @@ class ResaleOrderLine(models.Model):
check_status = fields.Selection(related='order_id.check_status') check_status = fields.Selection(related='order_id.check_status')
remark = fields.Char('备注') remark = fields.Char('备注')
is_incoming_material = fields.Boolean('客供料', compute='_compute_is_incoming_material', store=True) is_incoming_material = fields.Boolean('客供料', store=True)
embryo_redundancy_id = fields.Many2one('sf.embryo.redundancy', '坯料冗余') embryo_redundancy_id = fields.Many2one('sf.embryo.redundancy', '坯料冗余')
manual_quotation = fields.Boolean('人工编程', default=False) manual_quotation = fields.Boolean('人工编程', default=False)
model_url = fields.Char('模型文件地址') model_url = fields.Char('模型文件地址')

View File

@@ -16,6 +16,7 @@
# 'security/sf_stock_security.xml', # 'security/sf_stock_security.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'wizard/wizard_view.xml', 'wizard/wizard_view.xml',
'views/product.xml',
'views/view.xml', 'views/view.xml',
'views/shelf_location.xml', 'views/shelf_location.xml',
'views/change_stock_move_views.xml', 'views/change_stock_move_views.xml',

View File

@@ -1,2 +1,3 @@
from . import model from . import model
from . import sync_common from . import sync_common
from . import product

View File

@@ -471,7 +471,7 @@ class ShelfLocation(models.Model):
record.display_rfid = record.product_sn_id.rfid if record.product_sn_id else '' record.display_rfid = record.product_sn_id.rfid if record.product_sn_id else ''
except Exception as e: except Exception as e:
record.display_rfid = '' record.display_rfid = ''
@api.depends('product_id') @api.depends('product_id')
def _compute_tool(self): def _compute_tool(self):
"""计算工具 RFID""" """计算工具 RFID"""
@@ -594,7 +594,7 @@ class ShelfLocation(models.Model):
_layer_capacity = _cc_code % record.shelf_id.layer_capacity _layer_capacity = _cc_code % record.shelf_id.layer_capacity
if _layer_capacity == 0: if _layer_capacity == 0:
_layer_capacity = record.shelf_id.layer_capacity _layer_capacity = record.shelf_id.layer_capacity
else: else:
_layer_capacity = _layer_capacity _layer_capacity = _layer_capacity
_layer = _layer+1 _layer = _layer+1
_layer_capacity = f"{_layer_capacity:02d}" _layer_capacity = f"{_layer_capacity:02d}"
@@ -634,7 +634,7 @@ class SfShelfLocationLot(models.Model):
for item in self: for item in self:
if item.qty_num > item.qty: if item.qty_num > item.qty:
raise ValidationError('变更数量不能比库存数量大!!!') raise ValidationError('变更数量不能比库存数量大!!!')
class SfStockMoveLine(models.Model): class SfStockMoveLine(models.Model):
@@ -899,7 +899,7 @@ class SfStockMoveLine(models.Model):
def _compute_current_location_id(self): def _compute_current_location_id(self):
# 批量获取所有相关记录的picking # 批量获取所有相关记录的picking
pickings = self.mapped('picking_id') pickings = self.mapped('picking_id')
# 构建源picking的移库行与目标位置的映射 # 构建源picking的移库行与目标位置的映射
origin_location_map = {} origin_location_map = {}
for picking in pickings: for picking in pickings:
@@ -907,11 +907,11 @@ class SfStockMoveLine(models.Model):
origin_move = picking.move_ids[:1].move_orig_ids[:1] origin_move = picking.move_ids[:1].move_orig_ids[:1]
if not origin_move: if not origin_move:
continue continue
origin_picking = origin_move.picking_id origin_picking = origin_move.picking_id
if not origin_picking: if not origin_picking:
continue continue
# 为每个picking构建lot_id到location的映射 # 为每个picking构建lot_id到location的映射
origin_location_map[picking.id] = { origin_location_map[picking.id] = {
move_line.lot_id.id: move_line.destination_location_id move_line.lot_id.id: move_line.destination_location_id
@@ -919,17 +919,17 @@ class SfStockMoveLine(models.Model):
lambda ml: ml.destination_location_id and ml.lot_id lambda ml: ml.destination_location_id and ml.lot_id
) )
} }
# 批量更新current_location_id # 批量更新current_location_id
for record in self: for record in self:
current_picking = record.picking_id current_picking = record.picking_id
if not current_picking: if not current_picking:
record.current_location_id = False record.current_location_id = False
continue continue
# 获取当前picking对应的lot_location映射 # 获取当前picking对应的lot_location映射
lot_dest_map = origin_location_map.get(current_picking.id, {}) lot_dest_map = origin_location_map.get(current_picking.id, {})
# 查找匹配的lot_id # 查找匹配的lot_id
for move_line in current_picking.move_line_ids: for move_line in current_picking.move_line_ids:
if move_line.lot_id and move_line.lot_id.id in lot_dest_map: if move_line.lot_id and move_line.lot_id.id in lot_dest_map:
@@ -1082,6 +1082,10 @@ class SfStockPicking(models.Model):
重写验证方法,当验证时意味着调拨单已经完成,已经移动到了目标货位,所以需要将当前货位的状态改为空闲 重写验证方法,当验证时意味着调拨单已经完成,已经移动到了目标货位,所以需要将当前货位的状态改为空闲
""" """
res = super(SfStockPicking, self).button_validate() res = super(SfStockPicking, self).button_validate()
if any(ml.state == 'done' for ml in self.move_line_ids):
# 验证产品库存为负库存问题
self.move_ids.product_id.verify_product_repertory(self.location_id)
for line in self.move_line_ids: for line in self.move_line_ids:
if line: if line:
if line.destination_location_id: if line.destination_location_id:

View File

@@ -0,0 +1,29 @@
from odoo import models, fields
from odoo.exceptions import ValidationError
class SfProductCategory(models.Model):
_inherit = 'product.category'
negative_inventory_allowed = fields.Boolean('可负库存', default=True)
class SfProductTemplate(models.Model):
_inherit = 'product.product'
def verify_product_repertory(self, location_id):
"""
验证产品 负库存
"""
if not location_id:
raise ValidationError('当前位置为空!!')
elif len(location_id) != 1:
raise ValidationError(f'存在多个当前位置{[item.name for item in location_id]}')
elif location_id.usage == 'supplier':
return True
for pp in self:
if not pp.categ_id.negative_inventory_allowed:
sq = pp.stock_quant_ids.filtered(lambda sq: sq.quantity < 0 and sq.location_id == location_id)
if sq:
raise ValidationError(
f'产品{pp.name}的产品类型设置为不可负库存,当前操作会导致产品{pp.name}在库存{location_id.name}上的库存数量为负!!!')

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="product_category_form_view_sf_warehouse" model="ir.ui.view">
<field name="name">product.category.property.form.warehouse</field>
<field name="model">product.category</field>
<field name="inherit_id" ref="account.view_category_property_form"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='account_property']" position="after">
<group name="other">
<group string="其他">
<field name="negative_inventory_allowed"/>
</group>
</group>
</xpath>
</field>
</record>
</odoo>