Compare commits

..

272 Commits

Author SHA1 Message Date
马广威
0b9c856d56 Accept Merge Request #1050: (release/release_1_1 -> master)
Merge Request: hotfix工厂环境问题,详见描述

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1050?initial=true
2024-05-29 12:47:10 +08:00
jinling.yang
66dc7b540c Merge branch 'hotfix/修复编程单下发接口' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into release/release_1_1 2024-05-29 12:44:06 +08:00
jinling.yang
decfd27401 1.修复cnc工单没有程序文件路径
2.修复编程单已下发,在工件配送时,提示未下发nc程序
2024-05-29 12:41:55 +08:00
yuxianghui
f71d62dad1 优化工单工序 2024-05-29 11:26:17 +08:00
yuxianghui
b7a11f0abe 修复 【制造订单的工单顺序错误,需排查处理bug】 问题 2024-05-29 09:34:41 +08:00
马广威
8ced717b1c Accept Merge Request #1037: (release/release1.0 -> master)
Merge Request: master_sf_2.0

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1037?initial=true
2024-05-18 22:44:11 +08:00
mgw
187495eb67 Merge branch 'master' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into release/release1.0
# Conflicts:
#	sf_base/__init__.py
#	sf_base/__manifest__.py
#	sf_base/commons/__init__.py
#	sf_base/commons/common.py
#	sf_base/models/__init__.py
#	sf_base/models/base.py
#	sf_base/models/common.py
#	sf_base/security/group_security.xml
#	sf_base/security/ir.model.access.csv
#	sf_base/views/base_view.xml
#	sf_base/views/common_view.xml
#	sf_base/views/menu_view.xml
#	sf_bf_connect/__init__.py
#	sf_bf_connect/__manifest__.py
#	sf_bf_connect/controllers/__init__.py
#	sf_bf_connect/controllers/controllers.py
#	sf_bf_connect/models/__init__.py
#	sf_bf_connect/models/http.py
#	sf_bf_connect/models/models.py
#	sf_bf_connect/models/process_status.py
#	sf_bf_connect/views/res_partner_view.xml
#	sf_dlm/__manifest__.py
#	sf_dlm/data/product_data.xml
#	sf_dlm/models/__init__.py
#	sf_dlm/models/product_supplierinfo.py
#	sf_dlm/views/product_template_view.xml
#	sf_machine_connect/__init__.py
#	sf_machine_connect/__manifest__.py
#	sf_machine_connect/models/__init__.py
#	sf_machine_connect/models/ftp_client.py
#	sf_machine_connect/models/ftp_operate.py
#	sf_machine_connect/models/py2opcua.py
#	sf_machine_connect/security/ir.model.access.csv
#	sf_machine_connect/views/SfWorkOrderBarcodes.xml
#	sf_machine_connect/views/WorkCenterBarcodes.xml
#	sf_machine_connect/views/compensation.xml
#	sf_machine_connect/views/default_delivery.xml
#	sf_machine_connect/views/delivery_record.xml
#	sf_machine_connect/views/ftp_button.xml
#	sf_machine_connect/views/machine_info_present.xml
#	sf_machine_connect/views/machine_monitor.xml
#	sf_machine_connect/wizard/__init__.py
#	sf_machine_connect/wizard/action_up.py
#	sf_manufacturing/__init__.py
#	sf_manufacturing/__manifest__.py
#	sf_manufacturing/models/__init__.py
#	sf_manufacturing/models/model_type.py
#	sf_manufacturing/models/mrp_maintenance.py
#	sf_manufacturing/models/mrp_production.py
#	sf_manufacturing/models/mrp_routing_workcenter.py
#	sf_manufacturing/models/mrp_workcenter.py
#	sf_manufacturing/models/mrp_workorder.py
#	sf_manufacturing/models/res_user.py
#	sf_manufacturing/models/stock.py
#	sf_manufacturing/security/group_security.xml
#	sf_manufacturing/security/ir.model.access.csv
#	sf_manufacturing/views/model_type_view.xml
#	sf_manufacturing/views/mrp_routing_workcenter_view.xml
#	sf_manufacturing/views/mrp_workcenter_views.xml
#	sf_manufacturing/views/mrp_workorder_view.xml
#	sf_mrs_connect/__init__.py
#	sf_mrs_connect/__manifest__.py
#	sf_mrs_connect/controllers/controllers.py
#	sf_mrs_connect/data/ir_cron_data.xml
#	sf_mrs_connect/models/__init__.py
#	sf_mrs_connect/models/ftp_operate.py
#	sf_mrs_connect/models/res_config_setting.py
#	sf_mrs_connect/models/sync_common.py
#	sf_mrs_connect/views/res_config_settings_views.xml
#	sf_sale/__init__.py
#	sf_sale/__manifest__.py
#	sf_sale/models/__init__.py
#	sf_sale/models/sale_order.py
#	sf_sale/views/sale_order_view.xml
#	yizuo_login_background_and_styles/__manifest__.py
#	yizuo_login_background_and_styles/controllers/main.py
#	yizuo_login_background_and_styles/models/login_image.py
#	yizuo_login_background_and_styles/models/res_config_settings.py
2024-05-18 22:39:05 +08:00
马广威
5fccd41e07 Accept Merge Request #1035: (feature/制造代码优化 -> develop)
Merge Request: 修复库存岗位验证入库无权限问题

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1035?initial=true
2024-05-18 21:57:50 +08:00
mgw
0ae0ad2ba8 修复库存岗位验证入库无权限问题 2024-05-18 21:56:43 +08:00
马广威
afb5316d8a Accept Merge Request #1034: (feature/制造代码优化 -> develop)
Merge Request: 优化同步代码

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1034?initial=true
2024-05-18 21:33:47 +08:00
mgw
585e7236a1 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造代码优化 2024-05-18 21:33:04 +08:00
mgw
3afa76b79c 优化同步代码 2024-05-18 21:32:24 +08:00
马广威
6d503968a5 Accept Merge Request #1033: (feature/制造代码优化 -> develop)
Merge Request: 修复value bug

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1033?initial=true
2024-05-18 20:03:02 +08:00
mgw
cc7963cb5a 修复value bug 2024-05-18 20:02:32 +08:00
马广威
e2fdfbf1ee Accept Merge Request #1032: (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/1032#mr-1032-review-151369
2024-05-18 19:54:48 +08:00
马广威
cdf164ec2c Accept Merge Request #1031: (feature/制造代码优化 -> develop)
Merge Request: cnc工单和解除装夹工单隐藏【工序状态】字段,装夹预调工单已完成后【工序状态】字段显示“装夹预调完成”

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1031?initial=true
2024-05-18 19:53:25 +08:00
jinling.yang
f9cca591bb 修复表面工艺工单完成时对应库存(stock.move)的状态为完成并跳过标记完成方法的检查 2024-05-18 19:52:58 +08:00
mgw
6f3d423da1 cnc工单和解除装夹工单隐藏【工序状态】字段,装夹预调工单已完成后【工序状态】字段显示“装夹预调完成” 2024-05-18 19:52:27 +08:00
jinling.yang
1bcd1f2e33 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-05-18 19:48:47 +08:00
马广威
9284701ed4 Accept Merge Request #1030: (feature/制造代码优化 -> develop)
Merge Request: 优化刀具弹窗

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1030?initial=true
2024-05-18 18:42:57 +08:00
mgw
2c5cc91ef7 优化刀具弹窗 2024-05-18 18:42:20 +08:00
jinling.yang
c6039215a3 Merge branch 'feature/修复销售确认按钮' into develop 2024-05-18 17:32:03 +08:00
杨金灵
21027a8cb6 Accept Merge Request #1029: (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/1029
2024-05-18 17:31:39 +08:00
jinling.yang
80cd385df4 修复工单完成时制造订单未完成 2024-05-18 17:30:22 +08:00
jinling.yang
d7bcb0e4f3 修复销售确认按钮 2024-05-18 17:22:02 +08:00
jinling.yang
f62994878c Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-05-18 17:20:55 +08:00
马广威
50b0c37e46 Accept Merge Request #1028: (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/1028#mr-1028-review-151363
2024-05-18 17:11:30 +08:00
yuxianghui
dc381faae6 Merge branch 'feature/刀具模块优化' into feature/解决刀具组装时搜不到功能刀具产品问题 2024-05-18 17:07:17 +08:00
yuxianghui
f05301320c 1、组装单添加按是否组装排序;2、修改功能刀具组装流程中搜索功能刀具产品的条件 2024-05-18 17:05:39 +08:00
jinling.yang
d87fcb35ba Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-05-18 16:48:24 +08:00
jinling.yang
be69e76d2f Merge branch 'feature/修复采购总监权限' into develop 2024-05-18 16:47:43 +08:00
jinling.yang
debd788864 修复快速订单 2024-05-18 16:46:59 +08:00
杨金灵
2546dd73b2 Accept Merge Request #1027: (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/1027
2024-05-18 16:45:47 +08:00
jinling.yang
9f2415cfac 修复工件装夹岗无法创建采购订单 2024-05-18 16:45:01 +08:00
jinling.yang
2e151a2292 中控调用日志排序 2024-05-18 16:14:05 +08:00
jinling.yang
9b2ff6268c 修复采购总监权限 2024-05-18 16:08:39 +08:00
杨金灵
d1c08023b9 Accept Merge Request #1026: (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/1026?initial=true
2024-05-18 15:28:53 +08:00
jinling.yang
5f02de8b0c Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/修复表面工艺工单
# Conflicts:
#	sf_manufacturing/models/mrp_workorder.py
2024-05-18 15:27:17 +08:00
jinling.yang
0f5b94e8ca 1.修复销售订单确认按钮确认后不可见2.修复表面工艺工单完成3.下发编程单工件配送优化 2024-05-18 15:26:32 +08:00
马广威
795b4d43a8 Accept Merge Request #1025: (feature/制造代码优化 -> develop)
Merge Request: 修复仓储岗位权限遗留问题,暂时去掉根据CNC工单是否有编程单来控制装夹预调工单状态的功能,待重议此需求逻辑

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1025?initial=true
2024-05-18 15:15:46 +08:00
mgw
d9d1f86407 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造代码优化 2024-05-18 15:14:38 +08:00
mgw
337fecb4cb 暂时去掉根据CNC工单是否有编程单来控制装夹预调工单状态的功能,待重议此需求逻辑 2024-05-18 15:01:09 +08:00
马广威
4be83d8aab Accept Merge Request #1024: (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/1024#mr-1024-review-151348
2024-05-18 11:57:27 +08:00
yuxianghui
049521064b Merge branch 'feature/oca审批修改' into feature/刀具模块优化 2024-05-18 11:55:20 +08:00
yuxianghui
9e2091597b 1、注释action_apply_inventory()方法;2、功能刀具安全库存添加one2many类型的功能刀具废弃many2many类型的关联字段并优化代码,并在功能刀具模型添加功能按钮用来绑定功能刀具和对应的功能刀具安全库存;3、优化根据库存创建功能刀具组装单的方法断定条件;4、优化刀具注册接口的报错处理;5、优化组装流程 2024-05-18 11:54:23 +08:00
杨金灵
01f82fcb5d Accept Merge Request #1023: (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/1023?initial=true
2024-05-18 11:18:46 +08:00
jinling.yang
939aa974e6 修复外协位置 2024-05-18 11:17:11 +08:00
jinling.yang
829c971848 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-05-18 10:29:43 +08:00
mgw
a4b2d5bb42 修复仓储岗位权限遗留问题 2024-05-18 10:29:02 +08:00
jinling.yang
ebdc1ec2ff Merge branch 'feature/修改表面工艺外协位置' into develop 2024-05-18 10:27:50 +08:00
杨金灵
f0d66b7730 Accept Merge Request #1022: (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/1022
2024-05-18 10:27:18 +08:00
jinling.yang
a935112134 外协代码还原 2024-05-18 10:24:31 +08:00
jinling.yang
70bc2ed718 修改表面工艺外协位置 2024-05-18 10:21:09 +08:00
jinling.yang
c230e550a9 Merge branch 'feature/修复表面工艺工单完成问题' into develop 2024-05-17 17:26:20 +08:00
jinling.yang
297bf76809 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/修复表面工艺工单完成问题 2024-05-17 16:34:23 +08:00
jinling.yang
2dc9866735 1.修复表面工艺工单完成报错2.外协位置改为供应商位置3.采购总监添加创建制造订单权限 2024-05-17 16:26:15 +08:00
禹翔辉
305e91e7f1 Accept Merge Request #1020: (feature/oca审批修改 -> develop)
Merge Request: 1、优化组装流程;2、添加权限;3、oca审批优化

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1020?initial=true
2024-05-17 15:00:58 +08:00
yuxianghui
a5b9b63b41 Merge branch 'feature/主流程bug处理' into feature/oca审批修改 2024-05-17 14:57:46 +08:00
yuxianghui
1a0e96bbdd 1、采购审批流程中的英文提示信息改为中文提示;当审批完成后隐藏取消审批按键;2、刀具注册接口添加调用接口提示; 2024-05-17 14:56:15 +08:00
马广威
095c15cba4 Accept Merge Request #1019: (feature/制造代码优化 -> develop)
Merge Request: 显示优化,物流下单增补逻辑

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1019?initial=true
2024-05-17 11:48:21 +08:00
mgw
fe6bb46d54 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造代码优化 2024-05-17 11:47:44 +08:00
mgw
432a012596 显示优化,物流下单增补逻辑 2024-05-17 11:47:26 +08:00
杨金灵
27b5e92563 Accept Merge Request #1018: (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/1018?initial=true
2024-05-17 11:32:45 +08:00
jinling.yang
e5164454f2 修复销售确认按钮 2024-05-17 11:31:07 +08:00
jinling.yang
b79cdea926 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-05-17 11:10:49 +08:00
jinling.yang
836a598cf8 Merge branch 'feature/表面工艺外协修复' into develop 2024-05-17 11:10:02 +08:00
杨金灵
3a0a4b0f15 Accept Merge Request #1017: (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/1017?initial=true
2024-05-17 11:07:46 +08:00
jinling.yang
cda0b6d345 还原OCC代码 2024-05-17 11:04:35 +08:00
jinling.yang
7e31869d1f Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/表面工艺外协修复
# Conflicts:
#	sf_sale/views/sale_order_view.xml
2024-05-17 11:00:58 +08:00
jinling.yang
ea1ed43c92 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/表面工艺外协修复 2024-05-17 11:00:00 +08:00
yuxianghui
eb83114e54 1、优化组装流程;2、添加权限 2024-05-16 17:34:38 +08:00
禹翔辉
af45775040 Accept Merge Request #1016: (feature/主流程bug处理 -> develop)
Merge Request: 主流程bug处理

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1016
2024-05-16 14:16:06 +08:00
yuxianghui
03f5027f49 Merge branch 'feature/优化产品' into feature/主流程bug处理 2024-05-16 14:13:40 +08:00
yuxianghui
20ecd9d949 1、将和刀具相关的模型的刀具直径字段类型从int改为float;2、标准刀库tree视图添加Rfid字段;优化从中控获取标准刀库信息接口; 2024-05-16 14:04:35 +08:00
马广威
ed2c119803 Accept Merge Request #1015: (feature/制造代码优化 -> develop)
Merge Request: 处理物流处修改销售订单状态问题

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1015?initial=true
2024-05-16 09:44:13 +08:00
yuxianghui
37cf4ca435 1、新增sf_oca模块 2024-05-16 09:42:35 +08:00
yuxianghui
1c441d388f 1、序列号模型添加Rfid字段的重复校验;功能刀具模型优化搜索视图的字段;优功能刀具组装流程的值 2024-05-16 09:41:57 +08:00
mgw
d244409d9c 处理物流处修改销售订单状态问题 2024-05-15 14:09:44 +08:00
禹翔辉
1a66d6728e Accept Merge Request #1014: (feature/优化产品 -> develop)
Merge Request: 1、优化CNC加工流程

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1014
2024-05-14 15:57:26 +08:00
yuxianghui
002c5c06b4 1、优化CNC加工流程 2024-05-14 15:55:34 +08:00
禹翔辉
de62801ef1 Accept Merge Request #1013: (feature/优化产品 -> develop)
Merge Request: 1、关闭产品、采购单中关联字段的创建功能;2、优化采购入库验证条件;

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1013
2024-05-14 14:20:10 +08:00
yuxianghui
62ec721c69 1 2024-05-14 14:19:44 +08:00
yuxianghui
2194374aa2 Merge branch 'feature/解决bug、优化权限' into feature/优化产品 2024-05-14 14:11:39 +08:00
yuxianghui
eff6ce9f1b 1、关闭产品、采购单中关联字段的创建功能;2、优化采购入库验证条件; 2024-05-14 14:09:42 +08:00
杨金灵
9fcfaee8d1 Accept Merge Request #1012: (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/1012?initial=true
2024-05-14 11:02:27 +08:00
jinling.yang
90aae4dad9 销售订单按创建时间倒序 2024-05-14 10:59:35 +08:00
jinling.yang
934b59d213 坯料新增路线 2024-05-14 10:51:38 +08:00
jinling.yang
ed9b575fb5 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-05-13 14:04:30 +08:00
禹翔辉
bcd7ae9dd1 Accept Merge Request #1011: (feature/解决bug、优化权限 -> develop)
Merge Request: 1、添加权限;2、设备的设备检修标准字段关闭创建功能;3、解决 销售经理创建报价单选择产品后没有把模型和价格带过去 的bug;4、关闭仓库创建按钮

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1011
2024-05-13 12:01:16 +08:00
yuxianghui
48782cb62b Merge branch 'feature/解决主流程bug' into feature/解决bug、优化权限 2024-05-13 11:55:55 +08:00
yuxianghui
0f47f7c3b8 1、添加权限;2、设备的设备检修标准字段关闭创建功能;3、解决 销售经理创建报价单选择产品后没有把模型和价格带过去 的bug;4、关闭仓库创建按钮 2024-05-13 11:54:03 +08:00
jinling.yang
d9f339c4b0 Merge branch 'feature/修复表面工艺外协' into develop 2024-05-13 09:25:44 +08:00
杨金灵
d4b22d228b Accept Merge Request #1010: (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/1010
2024-05-13 09:24:12 +08:00
jinling.yang
e916e4e155 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/修复表面工艺外协 2024-05-12 21:33:57 +08:00
jinling.yang
9dccee84c4 1.优化同一个产品多个制造订单的编程单合并到一张编程单和一个模型库至cloud 2024-05-12 21:33:43 +08:00
马广威
97ce5d81c8 Accept Merge Request #1009: (feature/制造代码优化 -> develop)
Merge Request: 修复工单预计结束时间会取实际结束时间的问题

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1009?initial=true
2024-05-12 13:16:40 +08:00
mgw
c6638ac3f3 修复工单预计结束时间会取实际结束时间的问题 2024-05-12 13:14:53 +08:00
马广威
05e8507000 Accept Merge Request #1008: (feature/制造代码优化 -> develop)
Merge Request: 去掉销售订单加工状态

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1008?initial=true
2024-05-11 23:38:58 +08:00
mgw
1afc48a20e 去掉销售订单加工状态 2024-05-11 23:36:18 +08:00
马广威
053cd5fd89 Accept Merge Request #1007: (feature/制造代码优化 -> develop)
Merge Request: 修复制造订单加工无状态问题

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1007?initial=true
2024-05-11 22:44:30 +08:00
mgw
391a172016 修复制造订单加工无状态问题 2024-05-11 22:42:58 +08:00
禹翔辉
d3677b9c89 Accept Merge Request #1006: (feature/解决主流程bug -> develop)
Merge Request: 1、等待组件状态的装夹预调工单点击【开始】时,没有组件给提示信息;2、隐藏装夹预调工单列表看板,3、库存-内部调拨-产品是刀具物料除刀柄外,其他刀具物料目标货位添加必填校验;

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1006?initial=true
2024-05-11 16:41:01 +08:00
yuxianghui
384b032ef3 Merge branch 'feature/解决坯料外协不生成外协出库单bug' into feature/解决主流程bug 2024-05-11 16:36:17 +08:00
yuxianghui
0ddf2ce1bd 1、等待组件状态的装夹预调工单点击【开始】时,没有组件给提示信息;2、隐藏装夹预调工单列表看板,3、库存-内部调拨-产品是刀具物料除刀柄外,其他刀具物料目标货位添加必填校验; 2024-05-11 16:34:29 +08:00
杨金灵
05307fa25e Accept Merge Request #1005: (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/1005?initial=true
2024-05-11 15:42:31 +08:00
jinling.yang
76b7d00643 1.模版类型的成品,坯料,表面工艺模版的工序新增过滤2.表面工艺可选参数的适用材料修改为可编辑 2024-05-11 15:35:15 +08:00
jinling.yang
0f8c4e8765 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-05-11 14:04:15 +08:00
jinling.yang
a1081fe8cc Merge branch 'feature/优化制造订单和编程' into develop 2024-05-11 14:03:31 +08:00
杨金灵
47ed09152d Accept Merge Request #1004: (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/1004
2024-05-11 14:02:09 +08:00
jinling.yang
50dc25a8b7 还原注释代码 2024-05-11 13:51:52 +08:00
jinling.yang
b80df25e0a 注释代码为第二期同一产品多个编程单需求 2024-05-11 13:44:30 +08:00
jinling.yang
999959b6a1 1.修复工件配送向导制造订单号能显示5个
2工件配送向导页面优化
3.生成制造订单同时工件配送默认路线
2024-05-11 13:38:57 +08:00
jinling.yang
ec01b89242 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化制造订单和编程
# Conflicts:
#	sf_manufacturing/models/mrp_workorder.py
2024-05-11 00:20:08 +08:00
jinling.yang
c932fa035a Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-05-11 00:17:54 +08:00
jinling.yang
2bffc5964a 1.1修复排程工件配送没有目的生产线2.注释获取cnc程序最新代码(第二期)3.优化部分页面 2024-05-11 00:17:43 +08:00
禹翔辉
7d7096a36c Accept Merge Request #1003: (feature/解决坯料外协不生成外协出库单bug -> develop)
Merge Request: 1、解决 坯料外协不生成外协出库单 的bug;2、添加工件装夹岗对sf.functional.cutting.tool.entity模型的查询权限

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1003?initial=true
2024-05-10 16:45:56 +08:00
yuxianghui
6c70f43b1b Merge branch 'feature/解决工单Rfid相关bug' into feature/解决坯料外协不生成外协出库单bug 2024-05-10 16:43:55 +08:00
yuxianghui
016a279453 1、解决 坯料外协不生成外协出库单 的bug;2、添加工件装夹岗对sf.functional.cutting.tool.entity模型的查询权限 2024-05-10 16:42:54 +08:00
马广威
52bbc6f6cd Accept Merge Request #1002: (feature/制造代码优化 -> develop)
Merge Request: 在工单完成处控制状态,来避免非正常操作导致的制造订单状态异常问题

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1002?initial=true
2024-05-10 16:07:55 +08:00
mgw
1dfa890a4d Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造代码优化 2024-05-10 16:06:51 +08:00
mgw
d9543d42fd 在工单完成处控制状态,来避免非正常操作导致的制造订单状态异常问题 2024-05-10 16:01:33 +08:00
jinling.yang
b5c9387d8c Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化制造订单和编程 2024-05-09 21:58:45 +08:00
jinling.yang
c4609e35a5 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-05-09 18:29:45 +08:00
jinling.yang
3d56e54fb2 优化制造订单的获取cnc程序 2024-05-09 18:29:30 +08:00
马广威
494b6c69da Accept Merge Request #1001: (feature/制造代码优化 -> develop)
Merge Request: 为工单预计时间增加初始值

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1001?initial=true
2024-05-09 14:52:39 +08:00
mgw
e2d3b36683 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造代码优化 2024-05-09 14:51:42 +08:00
mgw
138e22255e 为工单预计时间增加初始值 2024-05-09 14:51:21 +08:00
黄焱
7200d03733 Accept Merge Request #1000: (feature/前端样式修改 -> develop)
Merge Request: // 修改 【批量排程】【配送】 按钮UI

Created By: @黄焱
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @黄焱
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1000?initial=true
2024-05-09 11:20:06 +08:00
黄焱
f3c98c9727 // 修改 【批量排程】【配送】 按钮UI 2024-05-09 11:18:01 +08:00
jinling.yang
6a4fd4d922 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化制造订单和编程 2024-05-09 11:11:16 +08:00
jinling.yang
38298f4a32 Merge branch 'feature/优化agv调用中控接驳站接口' into develop 2024-05-09 11:08:17 +08:00
杨金灵
41603f4caa Accept Merge Request #999: (feature/优化agv调用中控接驳站接口 -> develop)
Merge Request: 优化agv调用中控接驳站接口

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/999?initial=true
2024-05-09 11:08:02 +08:00
jinling.yang
1a63f82f4b 优化agv调用中控接驳站接口 2024-05-09 11:04:59 +08:00
jinling.yang
0ea3d6f0b3 优化agv接驳站 2024-05-09 11:04:07 +08:00
jinling.yang
54a08802d9 修复装夹工单状态 2024-05-09 10:45:14 +08:00
jinling.yang
388b0b4e4c 优化制造 2024-05-09 10:37:41 +08:00
马广威
db7382826e Accept Merge Request #998: (feature/制造代码优化 -> develop)
Merge Request: sf_t切换数据库

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/998?initial=true
2024-05-09 10:10:01 +08:00
mgw
07cf13dce7 sf_t切换数据库 2024-05-09 10:09:26 +08:00
马广威
28f4adb293 Accept Merge Request #997: (feature/制造代码优化 -> develop)
Merge Request: 修复排程选择生产线bug

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/997?initial=true
2024-05-09 09:45:07 +08:00
mgw
ac3081d3bf Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造代码优化 2024-05-09 09:44:23 +08:00
mgw
4e84562ba7 修复排程选择生产线bug 2024-05-09 09:43:54 +08:00
马广威
72d59c11db Accept Merge Request #996: (feature/制造代码优化 -> develop)
Merge Request: 处理工单可能为记录集的问题,增加邮件发送公用类

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/996?initial=true
2024-05-09 09:28:18 +08:00
mgw
8c279828be Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造代码优化
# Conflicts:
#	sf_manufacturing/models/mrp_workorder.py
2024-05-09 09:25:19 +08:00
马广威
95a77574ae Accept Merge Request #995: (feature/解决工单Rfid相关bug -> develop)
Merge Request: 1、解决 装夹预调工单扫描Rfid码时提示语的制造订单号显示重复了 的缺陷, 添加工单扫描的Rfid不是托盘是其他物料的提示;2、解决刀具管理模块的菜单依赖问题;3、管理员添加货位的操作权限

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/995#mr-995-review-148238
2024-05-09 09:21:45 +08:00
mgw
5c68dd45ee 补充提交代码 2024-05-09 09:21:13 +08:00
mgw
af822c9039 增加邮件发送公用类 2024-05-08 21:49:04 +08:00
yuxianghui
92ea9b2a4f Merge branch 'feature/坯料采购-外协与自加工流程BUG' into feature/解决工单Rfid相关bug 2024-05-08 16:53:00 +08:00
yuxianghui
5eacc01755 1、解决 装夹预调工单扫描Rfid码时提示语的制造订单号显示重复了 的缺陷, 添加工单扫描的Rfid不是托盘是其他物料的提示;2、解决刀具管理模块的菜单依赖问题;3、管理员添加货位的操作权限 2024-05-08 16:50:13 +08:00
杨金灵
72ea4c8a2c Accept Merge Request #994: (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/994?initial=true
2024-05-08 15:08:57 +08:00
jinling.yang
4bea41b8fd 修复快速订单没有权限问题 2024-05-08 15:07:31 +08:00
jinling.yang
f6373366b6 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-05-08 14:29:35 +08:00
jinling.yang
339c6ec2ee Merge branch 'feature/优化模型上色路径' into develop 2024-05-08 14:29:19 +08:00
杨金灵
e7ca8ebd9b Accept Merge Request #993: (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/993
2024-05-08 14:29:05 +08:00
jinling.yang
d964a9feb1 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化模型上色路径 2024-05-08 11:20:07 +08:00
杨金灵
da139ad30b Accept Merge Request #992: (feature/修复工件配送rfidcode没有值 -> develop)
Merge Request: 修复工件配送rfidcode没有值

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/992?initial=true
2024-05-08 09:33:15 +08:00
jinling.yang
30053c5493 修复工件配送rfidcode没有值 2024-05-08 09:27:24 +08:00
jinling.yang
fda43deeea 1.优化根据同一个产品,生成的制造订单绑定同一个编程单需求 2024-05-07 17:33:23 +08:00
jinling.yang
cb5cfec82a Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-05-07 15:17:10 +08:00
jinling.yang
8b533475b8 Merge branch 'feature/采购去掉审核' into develop 2024-05-07 15:16:57 +08:00
杨金灵
7cbe1cb35d Accept Merge Request #991: (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/991?initial=true
2024-05-07 15:16:31 +08:00
jinling.yang
b8196cc4a9 1.修复销售岗位创建快速订单没有生成销售订单问题2.工件配送Tree视图默认上产线 2024-05-07 14:44:58 +08:00
jinling.yang
ed41667477 1.采购去掉审核2.装夹预调工单验证优化 2024-05-06 16:29:54 +08:00
jinling.yang
ca3cdd9132 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-05-06 14:17:16 +08:00
杨金灵
5551cc51e2 Accept Merge Request #990: (feature/修复工件配送 -> develop)
Merge Request: 优化最新AGV及新增中控日志接口调用

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/990
2024-05-06 11:51:54 +08:00
jinling.yang
178edd645e Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-05-06 11:49:11 +08:00
jinling.yang
848fe9f2f0 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/修复工件配送 2024-05-06 11:48:53 +08:00
jinling.yang
2cd666c4ae 优化agv及新增中控日志调用 2024-05-06 11:48:34 +08:00
mgw
9d157d6083 处理工单可能为记录集的问题 2024-05-06 11:29:17 +08:00
禹翔辉
ab73deeeaa Accept Merge Request #989: (feature/添加人工编程标签 -> develop)
Merge Request: 产品添加人工编程的筛选项;制造订单from视图添加人工编程字段;

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/989?initial=true
2024-04-30 17:19:10 +08:00
yuxianghui
68c4c18642 Merge branch 'feature/拆解单优化' into feature/添加人工编程标签 2024-04-30 17:11:56 +08:00
yuxianghui
402a4e1740 1、产品添加人工编程的筛选项;制造订单from视图添加人工编程字段; 2024-04-30 17:09:46 +08:00
jinling.yang
68d8c36755 优化工件配送 2024-04-30 17:05:09 +08:00
jinling.yang
b45ffcc0d2 1.优化工件配送向导2.增加rfid码字段 2024-04-29 17:34:00 +08:00
马广威
58e1754ae2 Accept Merge Request #988: (feature/拆解单优化 -> develop)
Merge Request: 解决  工具装夹岗在工件装夹单详情扫描rfid码报错  bug

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/988#mr-988-review-146501
2024-04-29 17:28:20 +08:00
yuxianghui
a1f0a580a6 解决 工具装夹岗在工件装夹单详情扫描rfid码报错 bug 2024-04-29 17:25:29 +08:00
jinling.yang
952c24eb26 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-04-29 16:42:37 +08:00
禹翔辉
fbfd932561 Accept Merge Request #987: (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/987
2024-04-29 16:04:38 +08:00
yuxianghui
cc590134f5 1 2024-04-29 16:00:27 +08:00
yuxianghui
338d620d5b 解决 采购入库到具体库位的数量显示不对 问题 2024-04-29 15:53:40 +08:00
jinling.yang
58344b09d2 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/修复工件配送
# Conflicts:
#	sf_manufacturing/models/mrp_workorder.py
2024-04-29 15:33:41 +08:00
jinling.yang
3d76d5f0b9 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-04-29 15:32:04 +08:00
jinling.yang
b1d42b9421 优化工件配送和空料架 2024-04-29 15:31:46 +08:00
禹翔辉
cec066c082 Accept Merge Request #986: (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/986?initial=true
2024-04-29 14:24:06 +08:00
yuxianghui
239be9f4ea Merge branch 'feature/功能刀具组装添加校验' into feature/拆解单优化 2024-04-29 14:20:26 +08:00
yuxianghui
537e890926 1、优化功能刀具拆解后,不同情况下生成的刀具物料的位移记录;添加拆解成功后拆解成功的刀具日志;添加当拆解原因是【更换为其他刀具】时,目标货位必填;2、货位看板添加按产品搜索功能 2024-04-29 14:19:22 +08:00
马广威
9e10382dd7 Accept Merge Request #985: (feature/制造代码优化 -> develop)
Merge Request: 恢复工单按钮展示及实际开始时间

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/985
2024-04-29 12:20:11 +08:00
mgw
3a22c817df 修复工单实际开始时间 2024-04-29 12:15:54 +08:00
mgw
782e266988 恢复工单按钮展示 2024-04-29 11:36:29 +08:00
禹翔辉
f3f138e93c Accept Merge Request #984: (feature/功能刀具组装添加校验 -> develop)
Merge Request: 1、刀具物料刀柄、夹具物料托盘搜索模型的序列号对象字段的list列表添加Rfid字段;2、制造单搜索看板添加记数功能;3、刀具组装岗添加货位的读写权限;4、功能刀具组装单弹窗添加手动输入五种物料的货位编码时,对该货位进行多项校验;

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/984?initial=true
2024-04-29 10:39:15 +08:00
yuxianghui
8c78a24f7e Merge branch 'feature/接口优化' into feature/功能刀具组装添加校验 2024-04-29 10:36:35 +08:00
jinling.yang
45220d75af 新增运送空料架 2024-04-28 17:34:41 +08:00
yuxianghui
9d9b454ba5 1、刀具物料刀柄、夹具物料托盘搜索模型的序列号对象字段的list列表添加Rfid字段;2、制造单搜索看板添加记数功能;3、刀具组装岗添加货位的读写权限;4、功能刀具组装单弹窗添加手动输入五种物料的货位编码时,对该货位进行多项校验; 2024-04-28 17:20:11 +08:00
jinling.yang
bc2c0f7fb0 修复站点状态未变,及页面筛选框优化 2024-04-28 15:17:41 +08:00
jinling.yang
809f5a196e Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-04-28 14:57:28 +08:00
jinling.yang
281b041a31 Merge branch 'feature/修复工件配送时间' into develop 2024-04-28 11:45:17 +08:00
杨金灵
1fa45a36f1 Accept Merge Request #983: (feature/修复工件配送时间 -> develop)
Merge Request: 优化agv需求

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/983?initial=true
2024-04-28 11:45:00 +08:00
jinling.yang
24722f474c 修复配送时agv站点验证 2024-04-28 11:43:11 +08:00
jinling.yang
5eb946d936 优化agv需求 2024-04-28 11:34:17 +08:00
jinling.yang
b7f7f0cdff Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/修复工件配送时间 2024-04-26 18:04:49 +08:00
jinling.yang
42178e3e1e 上下产线接口新增IsComplete判断 2024-04-26 18:04:30 +08:00
马广威
2ebd543d39 Accept Merge Request #982: (feature/制造代码优化 -> develop)
Merge Request: 增加日志打印

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/982?initial=true
2024-04-26 15:55:44 +08:00
mgw
f452f0018f Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造代码优化 2024-04-26 15:54:20 +08:00
mgw
cd3feb787f 增加日志打印 2024-04-26 15:53:26 +08:00
杨金灵
d0e7dbefc9 Accept Merge Request #981: (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/981?initial=true
2024-04-26 15:36:30 +08:00
jinling.yang
8f9781dd13 修复工件配送时间 2024-04-26 14:44:26 +08:00
杨金灵
50aae8a1e8 Accept Merge Request #980: (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/980?initial=true
2024-04-26 14:00:35 +08:00
jinling.yang
41e551fe48 优化工件配送 2024-04-26 13:57:47 +08:00
jinling.yang
1293c327c6 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-04-26 10:01:03 +08:00
jinling.yang
74deef3d41 Merge branch 'feature/优化agv及工件配送' into develop 2024-04-26 10:00:47 +08:00
杨金灵
466dfa4889 Accept Merge Request #979: (feature/优化agv及工件配送 -> develop)
Merge Request: 优化agv及工件配送

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/979?initial=true
2024-04-26 10:00:30 +08:00
jinling.yang
66dcc2f982 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化agv及工件配送 2024-04-26 09:57:21 +08:00
jinling.yang
9362a78f2a Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-04-26 09:56:59 +08:00
jinling.yang
9eb891dd0b 优化agv及工件配送 2024-04-26 09:56:42 +08:00
禹翔辉
84b82cac1d Accept Merge Request #978: (feature/接口优化 -> develop)
Merge Request: 1、优化机床刀位接口,数据写入到机床的刀位的方法

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/978
2024-04-25 17:17:58 +08:00
yuxianghui
761efbceaa Merge branch 'feature/中控接口注释及工厂数据处理' into feature/接口优化 2024-04-25 17:16:11 +08:00
yuxianghui
4bcd6b9944 1、优化机床刀位接口,数据写入到机床的刀位的方法;2、制造-刀具管理菜单下的机床换刀申请菜单隐藏,功能菜单改为作业单;3、刀柄和托盘采购入库时,对录入的Rfid做唯一性校验; 2024-04-25 17:14:30 +08:00
jinling.yang
da22015d35 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop 2024-04-25 15:46:32 +08:00
jinling.yang
eb2b37eb06 Merge branch 'feature/优化工件配送' into develop 2024-04-25 15:46:18 +08:00
杨金灵
cd6a4b0a38 Accept Merge Request #977: (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/977
2024-04-25 15:45:22 +08:00
jinling.yang
8bbb18b6f4 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化工件配送 2024-04-25 15:43:30 +08:00
jinling.yang
11864feaa4 优化工件配送 2024-04-25 15:43:02 +08:00
禹翔辉
735f602f2d Accept Merge Request #976: (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/976
2024-04-25 14:14:12 +08:00
yuxianghui
53ed0d76a6 Merge branch 'feature/中控对接接口优化' into feature/中控接口注释及工厂数据处理
# Conflicts:
#	sf_manufacturing/views/mrp_production_addional_change.xml
2024-04-25 14:10:22 +08:00
yuxianghui
9d96e0681c 1、注释MES装刀指令接口,删除所以该接口的调用;注释质量检测失败时对零件特采接口的调用;2、序列号模型from界面临时添加按钮,解决工厂录入的有些刀柄序列号存在位数少于10的问题(通过SQL在左侧补零)(已隐藏); 2024-04-25 14:02:51 +08:00
马广威
961f7bfdc5 Accept Merge Request #975: (feature/制造代码优化 -> develop)
Merge Request: 中控接口恢复权限认证

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/975?initial=true
2024-04-25 12:27:45 +08:00
mgw
9374bab0ce Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造代码优化 2024-04-25 12:26:52 +08:00
mgw
780944047f 中控接口增加权限认证 2024-04-25 12:26:31 +08:00
马广威
edd1f9a1ee Accept Merge Request #974: (feature/制造代码优化 -> develop)
Merge Request: 优化制造订单号界面展示效果

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/974?initial=true
2024-04-25 09:36:40 +08:00
mgw
be18124186 优化制造订单号界面展示效果 2024-04-25 09:36:05 +08:00
马广威
a400beb0a4 Accept Merge Request #973: (feature/制造代码优化 -> develop)
Merge Request: 增加工单处的尺寸赋值

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/973?initial=true
2024-04-25 09:03:56 +08:00
mgw
f4433ac3cc 增加工单处的尺寸赋值 2024-04-25 09:03:17 +08:00
马广威
b237e353a9 Accept Merge Request #972: (feature/制造代码优化 -> develop)
Merge Request: 优化制造订单状态问题

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/972?initial=true
2024-04-24 22:55:52 +08:00
mgw
0bcd077914 优化制造订单状态问题 2024-04-24 22:53:48 +08:00
mgw
212f2df830 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造代码优化 2024-04-24 22:53:11 +08:00
马广威
192aecb17d Accept Merge Request #971: (feature/制造代码优化 -> develop)
Merge Request: 优化获取线边刀架、料架信息接口

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/971?initial=true
2024-04-24 22:14:59 +08:00
yuxianghui
6f2043e7e7 1、工单tree视图、from视图添加人工编程字段,及人工编程筛选项;2、序列号模型添加根据Rfid搜索的选项;3、修改零件特采接口,优化MES装刀申请接口、优化机床刀库接口; 2024-04-24 18:33:15 +08:00
mgw
0473e79fcf 优化获取线边刀架、料架信息接口 2024-04-24 17:27:39 +08:00
马广威
6ecc69590b Accept Merge Request #970: (feature/制造代码优化 -> develop)
Merge Request: 装夹预调工单,要带图纸和模型文件及坯料长宽高参数的优化需求

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/970
2024-04-24 14:35:01 +08:00
mgw
f570eb9b50 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造代码优化 2024-04-24 14:24:59 +08:00
马广威
d4aa71975e Accept Merge Request #969: (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/969#mr-969-review-144848
2024-04-24 14:24:36 +08:00
mgw
3e7bef095c 整理代码 2024-04-24 14:23:37 +08:00
jinling.yang
673a1c6448 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化工件配送 2024-04-24 14:20:42 +08:00
jinling.yang
231536089d 工件装夹岗扫描托盘RFID,识别校验并带出【同运工件】名称,并自动校验【目的生产线】一致
【同运工件】校验目的生产线不一致,则弹窗提示:工件生产线不一致,请重新确认;
2024-04-24 14:20:26 +08:00
mgw
347e7f3efe 增加自动备份模块 2024-04-24 13:58:24 +08:00
mgw
31e3f53873 制造订单状态优化 2024-04-24 13:48:56 +08:00
mgw
b6334caf97 优化同步库区功能(线边刀、线边料) 2024-04-24 11:26:52 +08:00
mgw
a83b8bcd14 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造代码优化 2024-04-24 08:58:54 +08:00
mgw
59c8631c15 库位变更接口优化 2024-04-24 08:58:15 +08:00
禹翔辉
24d8c2426c Accept Merge Request #968: (feature/中控对接接口优化 -> develop)
Merge Request: 1、俩个刀具组接口加上token验证;2、新增功能刀具组装完成时,如果单据来源于CAM用刀,则调用MES装刀指令接口;

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/968
2024-04-23 19:56:58 +08:00
yuxianghui
9778245ee7 1 2024-04-23 19:56:10 +08:00
yuxianghui
228770cb76 Merge branch 'feature/新增功能刀具拆解单模型' into feature/中控对接接口优化 2024-04-23 19:52:20 +08:00
yuxianghui
b80ba02c8f 1、俩个刀具组接口加上token验证;2、新增功能刀具组装完成时,如果单据来源于CAM用刀,则调用MES装刀指令接口; 2024-04-23 19:50:33 +08:00
杨金灵
1344bd7219 Accept Merge Request #967: (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/967
2024-04-23 19:21:38 +08:00
jinling.yang
90d1f96857 优化工件配送添加编码字段 2024-04-23 17:20:32 +08:00
mgw
ca01b055b8 修复:计划排程时计划开始时间选择明天-执行排程后时间变成今天了 问题 2024-04-23 16:03:03 +08:00
黄焱
1d54066664 Accept Merge Request #966: (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/966?initial=true
2024-04-23 15:42:49 +08:00
黄焱
5488846c85 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/前端样式修改 2024-04-23 15:39:15 +08:00
黄焱
d2f59f6c6a 设置【已完成】【报废】等状态的水印 2024-04-23 15:38:32 +08:00
禹翔辉
c1addd823e Accept Merge Request #965: (feature/新增功能刀具拆解单模型 -> develop)
Merge Request: 1、接口优化

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/965
2024-04-23 15:37:07 +08:00
yuxianghui
c56886aebe 1、接口优化 2024-04-23 15:33:59 +08:00
禹翔辉
1979b33c32 Accept Merge Request #964: (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/964?initial=true
2024-04-23 14:21:50 +08:00
yuxianghui
1fa5ebd80a 1 2024-04-23 14:19:55 +08:00
yuxianghui
f1dad7701f Merge branch 'feature/库存模块优化' into feature/新增功能刀具拆解单模型 2024-04-23 14:18:34 +08:00
yuxianghui
8bc23f5136 1、优化功能刀具拆解单拆解原因为更换其他刀具时的扫码录入刀具物料货位信息功能;2、优化功能刀具拆解单确认拆解后的刀具物料拆解流程,及其刀具物料功能刀具的数量统计等;3、功能刀具拆解、功能刀具模块添加动作过滤已拆解状态的记录,并添加筛选方法;4、功能刀具组装流程添加刀具物料货位数量校验; 2024-04-23 14:15:45 +08:00
杨金灵
bcff104013 Accept Merge Request #963: (feature/优化agv -> develop)
Merge Request: 优化agv及站点位置和对应接口

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/963?initial=true
2024-04-23 14:09:10 +08:00
jinling.yang
307e052740 优化agv及站点位置和对应接口 2024-04-23 14:05:10 +08:00
mgw
f236b149d8 Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造代码优化
# Conflicts:
#	sf_manufacturing/models/mrp_production.py
#	sf_manufacturing/views/mrp_workorder_view.xml
2024-04-23 10:56:57 +08:00
mgw
9cfc084c5c 装夹预调工单,要带图纸和模型文件及坯料长宽高参数的优化需求2:状态变化逻辑 2024-04-23 10:54:04 +08:00
mgw
49e4f92a91 装夹预调工单,要带图纸和模型文件及坯料长宽高参数的优化需求 2024-04-22 20:03:16 +08:00
yuxianghui
080a3d7175 1、优化扫码的托盘已绑定工单的提示信息;2、优化采购订单未审核时,金额数据不能改变的bug;3、功能刀具拆解单新增根据不同情况扫库位码录入五种物料的目的库位的信息;4、功能刀具拆解单新增拆解后的Rfid字段,新增拆解单状态字段,新增刀具物料库位信息搜索过滤;5、功能刀具拆解单新增确认拆解按钮,完成点击确认拆解后的刀具物料是否报废、磨削或者更换的不同条件进行拆解后物料的不同去向的功能及生成移动历史,解除功能刀具Rfid的绑定并更改状态等。6、优化功能刀具管理下的菜单目录布局;7、 2024-04-22 17:59:48 +08:00
yuxianghui
857d5d8916 1、新增功能刀具拆解模型及其前端的tree、form视图,新增扫码录入需要拆解的功能刀具功能; 2024-04-19 20:12:32 +08:00
qihao.gong@jikimo.com
0077d97cfa master-sf1.0 2023-05-06 16:03:00 +08:00
270 changed files with 12744 additions and 1272 deletions

View File

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

View File

@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
{
'name': "Database auto-backup",
'summary': 'Automated backups',
'description': """
The Database Auto-Backup module enables the user to make configurations for the automatic backup of the database.
Backups can be taken on the local system or on a remote server, through SFTP.
You only have to specify the hostname, port, backup location and databasename (all will be pre-filled by default with correct data.
If you want to write to an external server with SFTP you will need to provide the IP, username and password for the remote backups.
The base of this module is taken from Odoo SA V6.1 (https://www.odoo.com/apps/modules/6.0/auto_backup/) and then upgraded and heavily expanded.
This module is made and provided by Yenthe Van Ginneken (Oocademy).
Automatic backup for all such configured databases can then be scheduled as follows:
1) Go to Settings / Technical / Automation / Scheduled actions.
2) Search the action 'Backup scheduler'.
3) Set it active and choose how often you wish to take backups.
4) If you want to write backups to a remote location you should fill in the SFTP details.
""",
'author': "Yenthe Van Ginneken",
'website': "http://www.odoo.yenthevg.com",
'category': 'Administration',
'version': '16.0.0.1',
'installable': True,
'license': 'LGPL-3',
# any module necessary for this one to work correctly
'depends': ['base'],
# always loaded
'data': [
'security/user_groups.xml',
'security/ir.model.access.csv',
'views/backup_view.xml',
'data/backup_data.xml',
],
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" ?>
<odoo>
<data noupdate="1">
<record id="backup_scheduler" model="ir.cron">
<field name="interval_type">days</field>
<field name="name">Backup scheduler</field>
<field name="numbercall">-1</field>
<field name="priority">5</field>
<field name="doall">False</field>
<field name="active">False</field>
<field name="interval_number">1</field>
<field name="model_id" ref="model_db_backup"/>
<field name="state">code</field>
<field name="code">model.schedule_backup()</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,314 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * auto_backup
#
# SaFi J. <info@daleeltech.com>, 2015.
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-03-26 14:17+0000\n"
"PO-Revision-Date: 2015-12-13 10:46+0300\n"
"Last-Translator: SaFi J. <info@daleeltech.com>\n"
"Language-Team: team@daleeltech.com\n"
"Language: ar\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
#. module: auto_backup
#: code:addons/auto_backup/backup_scheduler.py:137
#, python-format
msgid "%s"
msgstr "%s"
#. module: auto_backup
#: help:db.backup,bkp_dir:0
msgid "Absolute path for storing the backups"
msgstr "المسار الكامل لحفظ النسخ الاحتياطي"
#. module: auto_backup
#: field:db.backup,sendmailsftpfail:0
msgid "Auto. E-mail on backup fail"
msgstr "إرسال بريد إلكتروني تلقائياً في حالة فشل النسخ الاحتياطي"
#. module: auto_backup
#: field:db.backup,autoremove:0
msgid "Auto. Remove Backups"
msgstr "إزالة النسخ الاحتياطية تلقائياً"
#. module: auto_backup
#: view:db.backup:auto_backup.view_backup_conf_form
msgid "Automatic backups of the database can be scheduled as follows:"
msgstr "النسخ الاحتياطي التلقائي لقاعدة البيانات يمكن جدولته كالتالي"
#. module: auto_backup
#: field:db.backup,bkp_dir:0
msgid "Backup Directory"
msgstr "دليل النسخ الاحتياطي"
#. module: auto_backup
#: view:db.backup:auto_backup.view_backup_conf_tree
msgid "Backups"
msgstr "النسخ الاحتياطية"
#. module: auto_backup
#: help:db.backup,daystokeepsftp:0
msgid "Choose after how many days the backup should be deleted from the FTP server. For example:\n"
"If you fill in 5 the backups will be removed after 5 days from the FTP server."
msgstr ""
"اختر بعد كم من الأيام سيتم حذف النسخ الاحتياطي من خادم FTP مثلاً :\n إذا أدخلت "
"5 فإن النسخ الاحتياطية سيتم إزالتها من خادم FTP بعد 5 أيام."
#. module: auto_backup
#: help:db.backup,daystokeep:0
msgid "Choose after how many days the backup should be deleted. For example:\n"
"If you fill in 5 the backups will be removed after 5 days."
msgstr ""
"اختر بعد كم من الأيام سيتم حذف النسخ الاحتياطي مثلاً :\n إذا أدخلت 5 فإن النسخ "
"الاحتياطية سيتم إزالتها بعد 5 أيام."
#. module: auto_backup
#: model:ir.actions.act_window,name:auto_backup.action_backup_conf_form
#: model:ir.ui.menu,name:auto_backup.backup_conf_menu
msgid "Configure Backup"
msgstr "إعدادات النسخ الإحتياطي"
#. module: auto_backup
#: view:db.backup:auto_backup.view_backup_conf_form
msgid "Contact us!"
msgstr "اتصل بنا!"
#. module: auto_backup
#: field:db.backup,create_uid:0
msgid "Created by"
msgstr "تم ألإنشاء بواسطة"
#. module: auto_backup
#: field:db.backup,create_date:0
msgid "Created on"
msgstr "تم ألإنشاء في"
#. module: auto_backup
#: field:db.backup,name:0
msgid "Database"
msgstr "قاعدة البيانات"
#. module: auto_backup
#: help:db.backup,name:0
msgid "Database you want to schedule backups for"
msgstr "قاعدة البيانات التي تريد جدولة النسخ الاحتياطي لها"
#. module: auto_backup
#: field:db.backup,emailtonotify:0
msgid "E-mail to notify"
msgstr "تنبيه البريد الإلكتروني"
#. module: auto_backup
#: code:addons/auto_backup/backup_scheduler.py:106
#: constraint:db.backup:0
#, python-format
msgid "Error ! No such database exists!"
msgstr "خطأ ! لا وجود لقاعدة البيانات هذه !"
#. module: auto_backup
#: help:db.backup,emailtonotify:0
msgid "Fill in the e-mail where you want to be notified that the backup failed on the FTP."
msgstr ""
"ادخل عنوان البريد الإلكتروني الذي تريد تنبيهك من خلاله عند فشل النسخ "
"الاحتياطي على FTP."
#. module: auto_backup
#: view:db.backup:auto_backup.view_backup_conf_form
msgid "For example: /odoo/backups/"
msgstr "مثلاً : /odoo/backups/"
#. module: auto_backup
#: view:db.backup:auto_backup.view_backup_conf_form
msgid "Go to Settings / Technical / Automation / Scheduled Actions."
msgstr "اذهب إلى الإعدادات / التقني / ألأتمته / جدولة الإخزاءات."
#. module: auto_backup
#: view:db.backup:auto_backup.view_backup_conf_form
msgid "Help"
msgstr "المساعدة"
#. module: auto_backup
#: field:db.backup,host:0
msgid "Host"
msgstr "المضيف"
#. module: auto_backup
#: field:db.backup,id:0
msgid "ID"
msgstr "المعرف"
#. module: auto_backup
#: field:db.backup,sftpip:0
msgid "IP Address SFTP Server"
msgstr "عنوان بروتوكول الأنترنت لخادم SFTP"
#. module: auto_backup
#: help:db.backup,sendmailsftpfail:0
msgid "If you check this option you can choose to automaticly get e-mailed when the backup to the external server failed."
msgstr ""
"إذا قمت بتأشير هذا الخيار ستستطيع اختيار استلام البريد الإلكتروني تلقائياً "
"عند فشل النسخ الاحتياطي للخادوم الخارجي."
#. module: auto_backup
#: help:db.backup,autoremove:0
msgid "If you check this option you can choose to automaticly remove the backup after xx days"
msgstr ""
"إذا قمت بتأشير هذا الخيار ستستطيع اختيار الإزالة التلقائية للنسخ الاحتياطي "
"بعد س من الأيام"
#. module: auto_backup
#: help:db.backup,sftpwrite:0
msgid "If you check this option you can specify the details needed to write to a remote server with SFTP."
msgstr ""
"إذا قمت بتأشير هذا الخيار ستستطيع تحديد التفاصيل المطلوبة للكتابة على الخادم "
"البعيد من خلال SFTP."
#. module: auto_backup
#: field:db.backup,write_uid:0
msgid "Last Updated by"
msgstr "آخر تحديث بواسطة"
#. module: auto_backup
#: field:db.backup,write_date:0
msgid "Last Updated on"
msgstr "آخر تحديث في"
#. module: auto_backup
#: view:db.backup:auto_backup.view_backup_conf_form
msgid "Local backup configuration"
msgstr "إعدادات النسخ الاحتياطي المحلي"
#. module: auto_backup
#: view:db.backup:auto_backup.view_backup_conf_form
msgid "Need more help?"
msgstr "هل تحتاج لمزيد من المساعدة ؟"
#. module: auto_backup
#: field:db.backup,sftppassword:0
msgid "Password User SFTP Server"
msgstr "كلمة المرور لمستخدم خادم SFTP"
#. module: auto_backup
#: field:db.backup,sftppath:0
msgid "Path external server"
msgstr "المسار الخارجي للخادم"
#. module: auto_backup
#: field:db.backup,port:0
msgid "Port"
msgstr "المنفذ"
#. module: auto_backup
#: field:db.backup,daystokeepsftp:0
msgid "Remove SFTP after x days"
msgstr "الإزالة من خادم SFTP بعد س من الأيام"
#. module: auto_backup
#: field:db.backup,daystokeep:0
msgid "Remove after x days"
msgstr "الإزالة بعد س من الأيام"
#. module: auto_backup
#: view:db.backup:auto_backup.view_backup_conf_form
msgid "SFTP"
msgstr "SFTP"
#. module: auto_backup
#: field:db.backup,sftpport:0
msgid "SFTP Port"
msgstr "منفذ SFTP"
#. module: auto_backup
#: view:db.backup:auto_backup.view_backup_conf_search
msgid "Search options"
msgstr "خيارات البحث"
#. module: auto_backup
#: view:db.backup:auto_backup.view_backup_conf_form
msgid "Search the action named 'Backup scheduler'."
msgstr "ابحث عن الأجزاء المسمى 'Backup scheduler'."
#. module: auto_backup
#: view:db.backup:auto_backup.view_backup_conf_form
msgid "Set the scheduler to active and fill in how often you want backups generated."
msgstr "قم بتفعيل الجدولة واملأ كم تربد عادة توليد النسخ الاحتياطي."
#. module: auto_backup
#: view:db.backup:auto_backup.view_backup_conf_form
msgid "Test"
msgstr "اختبار"
#. module: auto_backup
#: view:db.backup:auto_backup.view_backup_conf_form
msgid "Test SFTP Connection"
msgstr "اختبار توصيل SFTP"
#. module: auto_backup
#: help:db.backup,sftpip:0
msgid "The IP address from your remote server. For example 192.168.0.1"
msgstr "عنوان بروتوكول الإنترنت من خادومك البعيد. مثلاً 192.168.0.1"
#. module: auto_backup
#: help:db.backup,sftppath:0
msgid "The location to the folder where the dumps should be written to. For example /odoo/backups/.\n"
"Files will then be written to /odoo/backups/ on your remote server."
msgstr ""
"موقع المجلد التي يجب كتابة ملفات النسخ عليه. مثلاً /odoo/backups/.\n"
"وحينها سيتم كتابة الملفات إلى /odoo/backups/ على خادومك البعيد."
#. module: auto_backup
#: help:db.backup,sftppassword:0
msgid "The password from the user where the SFTP connection should be made with. This is the password from the user on the external server."
msgstr ""
"كلمة المرور الذي من المفترض عمل اتصال SFTP من بها. هذا هي كلمة المرور على "
"الخادم الخارجي."
#. module: auto_backup
#: help:db.backup,sftpport:0
msgid "The port on the FTP server that accepts SSH/SFTP calls."
msgstr "المنفذ على خادم FTP الذي يقبل طلبات SSH/SFTP."
#. module: auto_backup
#: help:db.backup,sftpusername:0
msgid "The username where the SFTP connection should be made with. This is the user on the external server."
msgstr ""
"اسم المستخدم الذي من المفترض عمل اتصال SFTP من به. هذا هو المستخدم على "
"الخادم الخارجي."
#. module: auto_backup
#: view:db.backup:auto_backup.view_backup_conf_form
msgid "This configures the scheduler for automatic backup of the given database running on given host at given port on regular intervals."
msgstr ""
"هذا الإعداد سيجدول النسخ الاحتياطي التلقائي لقاعدة بيانات معينة وسينفذ على "
"مضيف معين بمنفذ معين خلال فترات متتابعة. "
#. module: auto_backup
#: view:db.backup:auto_backup.view_backup_conf_form
msgid "Use SFTP with caution! This writes files to external servers under the path you specify."
msgstr ""
"استخدم SFTP بحذر ! لأن هذا يؤدي لكتابة ملفات لخوادم خارجية على المسار الذي "
"حددته."
#. module: auto_backup
#: field:db.backup,sftpusername:0
msgid "Username SFTP Server"
msgstr "أسم المستخدم لخادم SFTP"
#. module: auto_backup
#: view:db.backup:auto_backup.view_backup_conf_form
msgid "Warning:"
msgstr "تحذير :"
#. module: auto_backup
#: field:db.backup,sftpwrite:0
msgid "Write to external server with sftp"
msgstr "الكتابة على خادم خارجي من خلال sftp"

View File

@@ -0,0 +1,158 @@
# Bulgarian translation for openobject-addons
# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2009-11-24 13:49+0000\n"
"PO-Revision-Date: 2011-03-30 07:20+0000\n"
"Last-Translator: Dimitar Markov <dimitar.markov@gmail.com>\n"
"Language-Team: Bulgarian <bg@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2013-04-20 05:35+0000\n"
"X-Generator: Launchpad (build 16567)\n"
#. module: auto_backup
#: help:db.backup,name:0
msgid "Database you want to schedule backups for"
msgstr ""
#. module: auto_backup
#: constraint:ir.model:0
msgid ""
"The Object name must start with x_ and not contain any special character !"
msgstr ""
"Името на обекта трябва да започва с x_ и не може да никакви специални знаци !"
#. module: auto_backup
#: constraint:ir.actions.act_window:0
msgid "Invalid model name in the action definition."
msgstr "Невалидно име на модел при задаване на действие"
#. module: auto_backup
#: model:ir.model,name:auto_backup.model_db_backup
msgid "db.backup"
msgstr ""
#. module: auto_backup
#: view:db.backup:0
msgid ""
"1) Go to Administration / Configuration / Scheduler / Scheduled Actions"
msgstr ""
#. module: auto_backup
#: model:ir.actions.act_window,name:auto_backup.action_backup_conf_form
#: model:ir.ui.menu,name:auto_backup.backup_conf_menu
msgid "Configure Backup"
msgstr ""
#. module: auto_backup
#: view:db.backup:0
msgid "Test"
msgstr "Тестване"
#. module: auto_backup
#: view:db.backup:0
msgid "IP Configuration"
msgstr ""
#. module: auto_backup
#: help:db.backup,bkp_dir:0
msgid "Absolute path for storing the backups"
msgstr ""
#. module: auto_backup
#: model:ir.module.module,shortdesc:auto_backup.module_meta_information
msgid "Database Auto-Backup"
msgstr ""
#. module: auto_backup
#: view:db.backup:0
msgid "Database Configuration"
msgstr ""
#. module: auto_backup
#: view:db.backup:0
msgid "4) Set other values as per your preference"
msgstr ""
#. module: auto_backup
#: field:db.backup,host:0
msgid "Host"
msgstr "Хост"
#. module: auto_backup
#: view:db.backup:0
msgid ""
"Automatic backup of all the databases under this can be scheduled as "
"follows: "
msgstr ""
#. module: auto_backup
#: constraint:ir.ui.view:0
msgid "Invalid XML for View Architecture!"
msgstr ""
#. module: auto_backup
#: field:db.backup,bkp_dir:0
msgid "Backup Directory"
msgstr ""
#. module: auto_backup
#: field:db.backup,name:0
msgid "Database"
msgstr "База данни"
#. module: auto_backup
#: view:db.backup:0
msgid "2) Schedule new action(create a new record)"
msgstr ""
#. module: auto_backup
#: model:ir.module.module,description:auto_backup.module_meta_information
msgid ""
"The generic Open ERP Database Auto-Backup system enables the user to make "
"configurations for the automatic backup of the database.\n"
"User simply requires to specify host & port under IP Configuration & "
"database(on specified host running at specified port) and backup "
"directory(in which all the backups of the specified database will be stored) "
"under Database Configuration.\n"
"\n"
"Automatic backup for all such configured databases under this can then be "
"scheduled as follows: \n"
" \n"
"1) Go to Administration / Configuration / Scheduler / Scheduled Actions\n"
"2) Schedule new action(create a new record)\n"
"3) Set 'Object' to 'db.backup' and 'Function' to 'schedule_backup' under "
"page 'Technical Data'\n"
"4) Set other values as per your preference"
msgstr ""
#. module: auto_backup
#: view:db.backup:0
msgid ""
"3) Set 'Object' to 'db.backup' and 'Function' to 'schedule_backup' under "
"page 'Technical Data'"
msgstr ""
#. module: auto_backup
#: view:db.backup:0
msgid "Help"
msgstr "Помощ"
#. module: auto_backup
#: view:db.backup:0
msgid ""
"This configures the scheduler for automatic backup of the given database "
"running on given host at given port on regular intervals."
msgstr ""
#. module: auto_backup
#: field:db.backup,port:0
msgid "Port"
msgstr "Порт"

View File

@@ -0,0 +1,157 @@
# Catalan translation for openobject-addons
# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2009-11-24 13:49+0000\n"
"PO-Revision-Date: 2014-10-20 06:41+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Catalan <ca@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-10-21 06:30+0000\n"
"X-Generator: Launchpad (build 17203)\n"
#. module: auto_backup
#: help:db.backup,name:0
msgid "Database you want to schedule backups for"
msgstr ""
#. module: auto_backup
#: constraint:ir.model:0
msgid ""
"The Object name must start with x_ and not contain any special character !"
msgstr ""
#. module: auto_backup
#: constraint:ir.actions.act_window:0
msgid "Invalid model name in the action definition."
msgstr ""
#. module: auto_backup
#: model:ir.model,name:auto_backup.model_db_backup
msgid "db.backup"
msgstr ""
#. module: auto_backup
#: view:db.backup:0
msgid ""
"1) Go to Administration / Configuration / Scheduler / Scheduled Actions"
msgstr ""
#. module: auto_backup
#: model:ir.actions.act_window,name:auto_backup.action_backup_conf_form
#: model:ir.ui.menu,name:auto_backup.backup_conf_menu
msgid "Configure Backup"
msgstr ""
#. module: auto_backup
#: view:db.backup:0
msgid "Test"
msgstr ""
#. module: auto_backup
#: view:db.backup:0
msgid "IP Configuration"
msgstr ""
#. module: auto_backup
#: help:db.backup,bkp_dir:0
msgid "Absolute path for storing the backups"
msgstr ""
#. module: auto_backup
#: model:ir.module.module,shortdesc:auto_backup.module_meta_information
msgid "Database Auto-Backup"
msgstr ""
#. module: auto_backup
#: view:db.backup:0
msgid "Database Configuration"
msgstr ""
#. module: auto_backup
#: view:db.backup:0
msgid "4) Set other values as per your preference"
msgstr ""
#. module: auto_backup
#: field:db.backup,host:0
msgid "Host"
msgstr ""
#. module: auto_backup
#: view:db.backup:0
msgid ""
"Automatic backup of all the databases under this can be scheduled as "
"follows: "
msgstr ""
#. module: auto_backup
#: constraint:ir.ui.view:0
msgid "Invalid XML for View Architecture!"
msgstr ""
#. module: auto_backup
#: field:db.backup,bkp_dir:0
msgid "Backup Directory"
msgstr ""
#. module: auto_backup
#: field:db.backup,name:0
msgid "Database"
msgstr ""
#. module: auto_backup
#: view:db.backup:0
msgid "2) Schedule new action(create a new record)"
msgstr ""
#. module: auto_backup
#: model:ir.module.module,description:auto_backup.module_meta_information
msgid ""
"The generic Open ERP Database Auto-Backup system enables the user to make "
"configurations for the automatic backup of the database.\n"
"User simply requires to specify host & port under IP Configuration & "
"database(on specified host running at specified port) and backup "
"directory(in which all the backups of the specified database will be stored) "
"under Database Configuration.\n"
"\n"
"Automatic backup for all such configured databases under this can then be "
"scheduled as follows: \n"
" \n"
"1) Go to Administration / Configuration / Scheduler / Scheduled Actions\n"
"2) Schedule new action(create a new record)\n"
"3) Set 'Object' to 'db.backup' and 'Function' to 'schedule_backup' under "
"page 'Technical Data'\n"
"4) Set other values as per your preference"
msgstr ""
#. module: auto_backup
#: view:db.backup:0
msgid ""
"3) Set 'Object' to 'db.backup' and 'Function' to 'schedule_backup' under "
"page 'Technical Data'"
msgstr ""
#. module: auto_backup
#: view:db.backup:0
msgid "Help"
msgstr ""
#. module: auto_backup
#: view:db.backup:0
msgid ""
"This configures the scheduler for automatic backup of the given database "
"running on given host at given port on regular intervals."
msgstr ""
#. module: auto_backup
#: field:db.backup,port:0
msgid "Port"
msgstr ""

View File

@@ -0,0 +1,166 @@
# Spanish translation for openobject-addons
# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2009-11-24 13:49+0000\n"
"PO-Revision-Date: 2011-08-23 19:48+0000\n"
"Last-Translator: mgaja (GrupoIsep.com) <Unknown>\n"
"Language-Team: Spanish <es@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2013-04-20 05:35+0000\n"
"X-Generator: Launchpad (build 16567)\n"
#. module: auto_backup
#: help:db.backup,name:0
msgid "Database you want to schedule backups for"
msgstr "Base de datos que desea programar copias de seguridad para"
#. module: auto_backup
#: constraint:ir.model:0
msgid ""
"The Object name must start with x_ and not contain any special character !"
msgstr ""
"¡El objeto debe empezar con x_ y no puede contener ningún carácter especial!"
#. module: auto_backup
#: constraint:ir.actions.act_window:0
msgid "Invalid model name in the action definition."
msgstr "Nombre del modelo inválido en la definición de acción."
#. module: auto_backup
#: model:ir.model,name:auto_backup.model_db_backup
msgid "db.backup"
msgstr "backup.BBDD"
#. module: auto_backup
#: view:db.backup:0
msgid ""
"1) Go to Administration / Configuration / Scheduler / Scheduled Actions"
msgstr ""
"1) Vaya a Administración / Configuración / Programador / Acciones programadas"
#. module: auto_backup
#: model:ir.actions.act_window,name:auto_backup.action_backup_conf_form
#: model:ir.ui.menu,name:auto_backup.backup_conf_menu
msgid "Configure Backup"
msgstr "Configurar copia de seguridad"
#. module: auto_backup
#: view:db.backup:0
msgid "Test"
msgstr "Prueba"
#. module: auto_backup
#: view:db.backup:0
msgid "IP Configuration"
msgstr "Configuración IP"
#. module: auto_backup
#: help:db.backup,bkp_dir:0
msgid "Absolute path for storing the backups"
msgstr "Ruta absoluta para el almacenamiento de las copias de seguridad"
#. module: auto_backup
#: model:ir.module.module,shortdesc:auto_backup.module_meta_information
msgid "Database Auto-Backup"
msgstr "Copia de seguridad automática de Base de datos"
#. module: auto_backup
#: view:db.backup:0
msgid "Database Configuration"
msgstr "Configuración de Base de Datos"
#. module: auto_backup
#: view:db.backup:0
msgid "4) Set other values as per your preference"
msgstr "4) Establecer los demás valores según su preferencia"
#. module: auto_backup
#: field:db.backup,host:0
msgid "Host"
msgstr "Host"
#. module: auto_backup
#: view:db.backup:0
msgid ""
"Automatic backup of all the databases under this can be scheduled as "
"follows: "
msgstr ""
"Copia de seguridad automática de las bases de datos en virtud de este puede "
"ser programado de la siguiente manera: "
#. module: auto_backup
#: constraint:ir.ui.view:0
msgid "Invalid XML for View Architecture!"
msgstr "¡XML inválido para la definición de la vista!"
#. module: auto_backup
#: field:db.backup,bkp_dir:0
msgid "Backup Directory"
msgstr "Directorio de la copia de seguridad"
#. module: auto_backup
#: field:db.backup,name:0
msgid "Database"
msgstr "Base de datos"
#. module: auto_backup
#: view:db.backup:0
msgid "2) Schedule new action(create a new record)"
msgstr "2) Lista de nuevas acciones (crear un nuevo registro)"
#. module: auto_backup
#: model:ir.module.module,description:auto_backup.module_meta_information
msgid ""
"The generic Open ERP Database Auto-Backup system enables the user to make "
"configurations for the automatic backup of the database.\n"
"User simply requires to specify host & port under IP Configuration & "
"database(on specified host running at specified port) and backup "
"directory(in which all the backups of the specified database will be stored) "
"under Database Configuration.\n"
"\n"
"Automatic backup for all such configured databases under this can then be "
"scheduled as follows: \n"
" \n"
"1) Go to Administration / Configuration / Scheduler / Scheduled Actions\n"
"2) Schedule new action(create a new record)\n"
"3) Set 'Object' to 'db.backup' and 'Function' to 'schedule_backup' under "
"page 'Technical Data'\n"
"4) Set other values as per your preference"
msgstr ""
#. module: auto_backup
#: view:db.backup:0
msgid ""
"3) Set 'Object' to 'db.backup' and 'Function' to 'schedule_backup' under "
"page 'Technical Data'"
msgstr ""
"3) Ajuste \"objeto\" a \"db.backup\" y \"función\" a \"programar copias de "
"seguridad\" en la página \"Datos Técnicos\""
#. module: auto_backup
#: view:db.backup:0
msgid "Help"
msgstr "Ayuda"
#. module: auto_backup
#: view:db.backup:0
msgid ""
"This configures the scheduler for automatic backup of the given database "
"running on given host at given port on regular intervals."
msgstr ""
"Esto configura el planificador de copia de seguridad automática de la base "
"de datos dado que se ejecutan en el host dado en el puerto en intervalos "
"regulares."
#. module: auto_backup
#: field:db.backup,port:0
msgid "Port"
msgstr "Puerto"

View File

@@ -0,0 +1,395 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * auto_backup
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0-20180205\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-02-08 03:22+0000\n"
"PO-Revision-Date: 2018-02-08 12:47+0900\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 2.0.6\n"
"Last-Translator: \n"
"Language: ko_KR\n"
#. module: auto_backup
#: model:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid ""
"<b>Warning:</b>\n"
" Use SFTP with caution! This writes files to external "
"servers under the path you specify."
msgstr ""
"<b>경고:</b>\n"
" 조심해서 SFTP를 사용하세요! 이것은 당신이 지정한 경로"
"밑에 외부서버들로 파일들을 쓰게됩니다."
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup_folder
msgid "Absolute path for storing the backups"
msgstr "백업저장용 절대경로명"
#. module: auto_backup
#: model:ir.module.category,name:auto_backup.module_management
msgid "Auto backup access"
msgstr "자동으로 백업 액세스하기"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_send_mail_sftp_fail
msgid "Auto. E-mail on backup fail"
msgstr "백업실패시에 이메일로 알려주기 (자동)"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_autoremove
msgid "Auto. Remove Backups"
msgstr "백업제거하기 (자동)"
#. module: auto_backup
#: model:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Back-up view"
msgstr "백업보기"
#. module: auto_backup
#: model:ir.ui.menu,name:auto_backup.auto_backup_menu
msgid "Back-ups"
msgstr "백업"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_folder
msgid "Backup Directory"
msgstr "백업디렉토리"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_backup_type
msgid "Backup Type"
msgstr "백업타입"
#. module: auto_backup
#: model:ir.actions.server,name:auto_backup.backup_scheduler_ir_actions_server
#: model:ir.cron,cron_name:auto_backup.backup_scheduler
#: model:ir.cron,name:auto_backup.backup_scheduler
msgid "Backup scheduler"
msgstr "백업스케줄러"
#. module: auto_backup
#: model:ir.ui.view,arch_db:auto_backup.view_backup_config_tree
msgid "Backups"
msgstr "백업"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup_days_to_keep_sftp
msgid ""
"Choose after how many days the backup should be deleted from the FTP server. "
"For example:\n"
"If you fill in 5 the backups will be removed after 5 days from the FTP "
"server."
msgstr ""
"FTP서버로부터 몇일지난 백업본을 삭제할지 선택하세요. 예를들면:\n"
"만약 5를 기입하면 그 백업본들은 FTP서버에서 5일후에 삭제됩니다."
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup_days_to_keep
msgid ""
"Choose after how many days the backup should be deleted. For example:\n"
"If you fill in 5 the backups will be removed after 5 days."
msgstr ""
"FTP서버로부터 몇일지난 백업본을 삭제할지 선택하세요. 예를들면:\n"
"만약 5를 기입하면 그 백업본들은 FTP서버에서 5일후에 삭제됩니다."
#. module: auto_backup
#: model:ir.actions.act_window,name:auto_backup.action_backup
#: model:ir.ui.menu,name:auto_backup.backup_conf_menu
msgid "Configure back-ups"
msgstr "백업구성하기"
#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:129
#, python-format
msgid "Connection Test Failed!"
msgstr "연결테스트실패~"
#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:124
#, python-format
msgid ""
"Connection Test Succeeded!\n"
"Everything seems properly set up for FTP back-ups!"
msgstr ""
"연결테스트성공!\n"
"모든게 적절히 세팅된거 같습니다!"
#. module: auto_backup
#: model:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Contact me!"
msgstr "저에게 연락하세요~"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_create_uid
msgid "Created by"
msgstr "생성됨"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_create_date
msgid "Created on"
msgstr "생성됨"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_name
msgid "Database"
msgstr "데이터베이스"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup_name
msgid "Database you want to schedule backups for"
msgstr "백업을 스케줄하기 원하는 데이터베이스"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_display_name
msgid "Display Name"
msgstr "표시이름"
#. module: auto_backup
#: selection:db.backup,backup_type:0
msgid "Dump"
msgstr "덤프"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_email_to_notify
msgid "E-mail to notify"
msgstr "통보할 이메일주소"
#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:98 constraint:db.backup:0
#, python-format
msgid "Error ! No such database exists!"
msgstr "에러~ 그런 데이터베이스가 존재하지 않습니다"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup_email_to_notify
msgid ""
"Fill in the e-mail where you want to be notified that the backup failed on "
"the FTP."
msgstr "FTP서버상에서 백업실패할때 알림받기원하는 이메일주소를 채워넣으세요"
#. module: auto_backup
#: model:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "For example: /odoo/backups/"
msgstr "예: /odoo/backups/"
#. module: auto_backup
#: model:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Go to Settings / Technical / Automation / Scheduled Actions."
msgstr "Settings / Technical / Automation / Scheduled Actions 으로 가세요."
#. module: auto_backup
#: model:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Help"
msgstr "도움말"
#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:132
#, python-format
msgid "Here is what we got instead:\n"
msgstr "우리가 대신하는것이 여기있습니다:\n"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_host
msgid "Host"
msgstr "호스트"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_id
msgid "ID"
msgstr "ID"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_sftp_host
msgid "IP Address SFTP Server"
msgstr "SFTP서버 IP주소"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup_send_mail_sftp_fail
msgid ""
"If you check this option you can choose to automaticly get e-mailed when the "
"backup to the external server failed."
msgstr ""
"이 옵션을 체크하면 외부서버에서 백업실패할때 자동적으로 이메일하도록 선택할 "
"수 있습니다."
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup_autoremove
msgid ""
"If you check this option you can choose to automaticly remove the backup "
"after xx days"
msgstr ""
"이 옵션을 선택하면 자동적으로 며칠(xx days)후에 백업을 제거하도록 선택할 수 "
"있습니다"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup_sftp_write
msgid ""
"If you check this option you can specify the details needed to write to a "
"remote server with SFTP."
msgstr ""
"이 옵션을 선택하면 SFTP 원격서버로 쓰려고 할때 요구되는 정보를 지정할 수 있습"
"니다."
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup___last_update
msgid "Last Modified on"
msgstr "최근수정일자"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_write_uid
msgid "Last Updated by"
msgstr "최근수정자"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_write_date
msgid "Last Updated on"
msgstr "최근업데이트일자"
#. module: auto_backup
#: model:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Local backup configuration"
msgstr "로컬백업환경설정"
#. module: auto_backup
#: model:res.groups,name:auto_backup.group_manager
msgid "Manager"
msgstr "관리자"
#. module: auto_backup
#: model:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Need more help?"
msgstr "더 도움이 필요하세요?"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_sftp_password
msgid "Password User SFTP Server"
msgstr "SFTP서버 사용자 패스워드"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_sftp_path
msgid "Path external server"
msgstr "외부서버경로"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_port
msgid "Port"
msgstr "포트"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_days_to_keep_sftp
msgid "Remove SFTP after x days"
msgstr "몇일후에 SFTP 제거"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_days_to_keep
msgid "Remove after x days"
msgstr "몇일후에 제거하기"
#. module: auto_backup
#: model:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "SFTP"
msgstr "SFTP"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_sftp_port
msgid "SFTP Port"
msgstr "SFTP 포트"
#. module: auto_backup
#: model:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Search the action named 'Backup scheduler'."
msgstr "'백업스케줄러'라고 명명된 액션을 검색하세요"
#. module: auto_backup
#: model:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid ""
"Set the scheduler to active and fill in how often you want backups generated."
msgstr "스케줄러를 활성화로 세팅하고 백업주기를 채워넣으세요"
#. module: auto_backup
#: model:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Test SFTP Connection"
msgstr "SFTP연결테스트"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup_sftp_host
msgid "The IP address from your remote server. For example 192.168.0.1"
msgstr "원격서버로부터의 IP주소. 예를들자면, 192.168.0.1"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup_sftp_path
msgid ""
"The location to the folder where the dumps should be written to. For "
"example /odoo/backups/.\n"
"Files will then be written to /odoo/backups/ on your remote server."
msgstr ""
"덤프파일이 씌어져야하는 폴더위치. 예를들어보자면 /odoo/backups/.\n"
"그 다음에 파일들은 당신서버상의 /odoo/backups/ 로 쓰여질겁니다."
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup_sftp_password
msgid ""
"The password from the user where the SFTP connection should be made with. "
"This is the password from the user on the external server."
msgstr ""
"SFTP연결이 만들어져야하는 그 사용자의 비밀번호. 이것은 외부서버상의 그 사용자"
"의 비밀번호예요~"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup_sftp_port
msgid "The port on the FTP server that accepts SSH/SFTP calls."
msgstr "SSH/SFTP접속을 허용하는 FTP서버상의 포트"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup_sftp_user
msgid ""
"The username where the SFTP connection should be made with. This is the user "
"on the external server."
msgstr "SFTP연결할 사용자명. 외부서버상의 사용자명이예요~ "
#. module: auto_backup
#: model:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid ""
"This configures the scheduler for automatic backup of the given database "
"running on given host\n"
" at given port on regular intervals.\n"
" <br/>\n"
" Automatic backups of the database can be scheduled "
"as follows:"
msgstr ""
"이것은 정기적인 간격으로 해당 호스트상에서 실행중인 데이터베이스의 자동백업용"
"으로 스케줄러를 구성합니다. 그 데이터베이스의 자동백업들은 아래처럼 스케줄될 "
"수 있습니다:"
#. module: auto_backup
#: model:ir.module.category,description:auto_backup.module_management
msgid "User access level for this module"
msgstr "이 모듈용 사용자접근레벨"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_sftp_user
msgid "Username SFTP Server"
msgstr "SFTP서버 사용자명"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup_sftp_write
msgid "Write to external server with sftp"
msgstr "sftp와 함께 외부서버로 쓰기"
#. module: auto_backup
#: selection:db.backup,backup_type:0
msgid "Zip"
msgstr "Zip"
#. module: auto_backup
#: model:ir.model,name:auto_backup.model_db_backup
msgid "db.backup"
msgstr "db.backup"

View File

@@ -0,0 +1,380 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * auto_backup
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 13.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-10-20 13:29+0000\n"
"PO-Revision-Date: 2019-10-20 13:29+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid ""
"<b>Warning:</b>\n"
" Use SFTP with caution! This writes files to external servers under the path you specify."
msgstr "<b>Waarschuwing:</b>\n"
" Gebruik SFTP voorzichtig! Dit schrijft bestanden naar externe servers onder het pad dat u opgeeft."
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__folder
msgid "Absolute path for storing the backups"
msgstr "Absoluut pad om backups te bewaren"
#. module: auto_backup
#: model:ir.module.category,name:auto_backup.module_management
msgid "Auto backup access"
msgstr "Auto backup toegang"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__send_mail_sftp_fail
msgid "Auto. E-mail on backup fail"
msgstr "Auto. e-mail bij mislukte back-up"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__autoremove
msgid "Auto. Remove Backups"
msgstr "Auto. e-mailen wanneer backup mislukt"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Back-up view"
msgstr "Back-up weergave"
#. module: auto_backup
#: model:ir.ui.menu,name:auto_backup.auto_backup_menu
msgid "Back-ups"
msgstr "Back-ups"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__folder
msgid "Backup Directory"
msgstr "Backup folder"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__backup_type
msgid "Backup Type"
msgstr "Soort back-up"
#. module: auto_backup
#: model:ir.model,name:auto_backup.model_db_backup
msgid "Backup configuration record"
msgstr "Back-up configuratie"
#. module: auto_backup
#: model:ir.actions.server,name:auto_backup.backup_scheduler_ir_actions_server
#: model:ir.cron,cron_name:auto_backup.backup_scheduler
#: model:ir.cron,name:auto_backup.backup_scheduler
msgid "Backup scheduler"
msgstr "Backup planner"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_tree
msgid "Backups"
msgstr "Back-ups"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__days_to_keep_sftp
msgid ""
"Choose after how many days the backup should be deleted from the FTP server. For example:\n"
"If you fill in 5 the backups will be removed after 5 days from the FTP server."
msgstr "Kies na hoeveel dagen de backups verwijderd moeten worden van de FTP server. Bijvoorbeeld:\n"
"Als u 5 invult zal de backup na 5 dagen verwijderd worden van de FTP server."
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__days_to_keep
msgid ""
"Choose after how many days the backup should be deleted. For example:\n"
"If you fill in 5 the backups will be removed after 5 days."
msgstr "Kies na hoeveel dagen de backups verwijderd moeten worden van de FTP server. Bijvoorbeeld:\n"
"Als u 5 invult zal de backup na 5 dagen verwijderd worden van de FTP server."
#. module: auto_backup
#: model:ir.actions.act_window,name:auto_backup.action_backup
#: model:ir.ui.menu,name:auto_backup.backup_conf_menu
msgid "Configure back-ups"
msgstr "Configureer back-ups"
#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:0
#, python-format
msgid "Connection Test Failed!"
msgstr "Connectie test mislukt!"
#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:0
#, python-format
msgid ""
"Connection Test Succeeded!\n"
"Everything seems properly set up for FTP back-ups!"
msgstr "Connectie test succesvol!\n"
"Alles lijkt correct opgezet voor FTP back-ups!"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Contact me!"
msgstr "Contacteer mij!"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__create_uid
msgid "Created by"
msgstr "Aangemaakt door"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__create_date
msgid "Created on"
msgstr "Aangemaakt op"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__name
msgid "Database"
msgstr "Database"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__name
msgid "Database you want to schedule backups for"
msgstr "Dataabse waar u back-ups voor wilt plannen"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__display_name
msgid "Display Name"
msgstr "Schermnaam"
#. module: auto_backup
#: model:ir.model.fields.selection,name:auto_backup.selection__db_backup__backup_type__dump
msgid "Dump"
msgstr "Dump"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__email_to_notify
msgid "E-mail to notify"
msgstr "E-mail om te verwittigen"
#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:0
#, python-format
msgid "Error ! No such database exists!"
msgstr "Fout! Deze database bestaat niet!"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__email_to_notify
msgid ""
"Fill in the e-mail where you want to be notified that the backup failed on "
"the FTP."
msgstr "Vul de e-mail in waarop u wilt verwittigd worden als de backup mislukt op de FTP."
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "For example: /odoo/backups/"
msgstr "Bijvoorbeeld: /odoo/backups/"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Go to Settings / Technical / Automation / Scheduled Actions."
msgstr "Ga naar Instellingen / Technisch / Automatisering / Geplande acties."
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Help"
msgstr "Help"
#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:0
#, python-format
msgid "Here is what we got instead:\n"
msgstr "Hier is wat we in de plaats terugkregen:\n"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__host
msgid "Host"
msgstr "Host"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__id
msgid "ID"
msgstr "ID"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__sftp_host
msgid "IP Address SFTP Server"
msgstr "IP adres SFTP server"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__send_mail_sftp_fail
msgid ""
"If you check this option you can choose to automaticly get e-mailed when the"
" backup to the external server failed."
msgstr "Als u deze optie aanvinkt kan u kiezen om automatisch een e-mail aan te krijgen als de backuaar de externe server mislukt."
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__autoremove
msgid ""
"If you check this option you can choose to automaticly remove the backup "
"after xx days"
msgstr "Als u deze optie aanvinkt kan u kiezen om automatisch backups te verwijderen "
"na xx dagen"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__sftp_write
msgid ""
"If you check this option you can specify the details needed to write to a "
"remote server with SFTP."
msgstr "Als u deze optie aanvinkt kan u de details invullen die nodig zijn om te connecteren met de "
" externe SFTP server"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup____last_update
msgid "Last Modified on"
msgstr "Laatst gewijzigd op"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__write_uid
msgid "Last Updated by"
msgstr "Laatst bijgewerkt door"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__write_date
msgid "Last Updated on"
msgstr "Laatst bijgewerkt op"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Local backup configuration"
msgstr "Lokale back-up configuratie"
#. module: auto_backup
#: model:res.groups,name:auto_backup.group_manager
msgid "Manager"
msgstr "Beheerder"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Need more help?"
msgstr "Meer hulp nodig?"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__sftp_password
msgid "Password User SFTP Server"
msgstr "Wachtwoord gebruiker SFTP server"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__sftp_path
msgid "Path external server"
msgstr "Pad externe server"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__port
msgid "Port"
msgstr "Poort"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__days_to_keep_sftp
msgid "Remove SFTP after x days"
msgstr "Verwijderd SFTP na x dagen"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__days_to_keep
msgid "Remove after x days"
msgstr "Verwijder na x dagen"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "SFTP"
msgstr "SFTP"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__sftp_port
msgid "SFTP Port"
msgstr "SFTP poort"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Search the action named 'Backup scheduler'."
msgstr "Zoek de actie met de naam 'Backup planner'."
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid ""
"Set the scheduler to active and fill in how often you want backups "
"generated."
msgstr "Zet de planner actief en vul in hoe vaak u wilt dat er backups gegenereerd "
"worden."
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Test SFTP Connection"
msgstr "Test SFTP verbinding"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__sftp_host
msgid "The IP address from your remote server. For example 192.168.0.1"
msgstr "Het IP adres van uw externe server. Bijvoorbeeld: 192.168.0.1"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__sftp_path
msgid ""
"The location to the folder where the dumps should be written to. For example /odoo/backups/.\n"
"Files will then be written to /odoo/backups/ on your remote server."
msgstr "De locatie naar de folder waar de backup naar toe moet geschreven worden. Bijvoorbeeld odoo/backups/\n"
"Bestanden worden dan naar /odoo/backups/ geschreven op de externe server"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__sftp_password
msgid ""
"The password from the user where the SFTP connection should be made with. "
"This is the password from the user on the external server."
msgstr "Het wachtwoord van de gebruiker waar de SFTP connectie mee moet gemaakt worden. "
"Dit is het wachtwoord van de gebruiker op de externe server."
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__sftp_port
msgid "The port on the FTP server that accepts SSH/SFTP calls."
msgstr "De poort op de FTP server die SSH/SFTP accepteert."
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__sftp_user
msgid ""
"The username where the SFTP connection should be made with. This is the user"
" on the external server."
msgstr "De gebruikersnaam waar de SFTP connectie mee gemaakt moet worden. Dit is de gebruiker"
" op de externe server."
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid ""
"This configures the scheduler for automatic backup of the given database running on given host\n"
" at given port on regular intervals.\n"
" <br/>\n"
" Automatic backups of the database can be scheduled as follows:"
msgstr "Dit configureert de planner om automatische backups van de opgegeven database te maken die op deze host,\n"
" op een bepaalde poort draaien, op regelmatige intervals.\n"
" <br/>\n"
" Automatische backups kunnen als volgt ingepland worden:"
#. module: auto_backup
#: model:ir.module.category,description:auto_backup.module_management
msgid "User access level for this module"
msgstr "Gebruikerstoegang voor deze module"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__sftp_user
msgid "Username SFTP Server"
msgstr "Gebruikersnaam SFTP server"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__sftp_write
msgid "Write to external server with sftp"
msgstr "Schrijf naar externe server met SFTP"
#. module: auto_backup
#: model:ir.model.fields.selection,name:auto_backup.selection__db_backup__backup_type__zip
msgid "Zip"
msgstr "ZIP"

View File

@@ -0,0 +1,380 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * auto_backup
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 13.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-10-20 13:29+0000\n"
"PO-Revision-Date: 2019-10-20 13:29+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid ""
"<b>Warning:</b>\n"
" Use SFTP with caution! This writes files to external servers under the path you specify."
msgstr "<b>Waarschuwing:</b>\n"
" Gebruik SFTP voorzichtig! Dit schrijft bestanden naar externe servers onder het pad dat u opgeeft."
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__folder
msgid "Absolute path for storing the backups"
msgstr "Absoluut pad om backups te bewaren"
#. module: auto_backup
#: model:ir.module.category,name:auto_backup.module_management
msgid "Auto backup access"
msgstr "Auto backup toegang"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__send_mail_sftp_fail
msgid "Auto. E-mail on backup fail"
msgstr "Auto. e-mail bij mislukte back-up"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__autoremove
msgid "Auto. Remove Backups"
msgstr "Auto. e-mailen wanneer backup mislukt"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Back-up view"
msgstr "Back-up weergave"
#. module: auto_backup
#: model:ir.ui.menu,name:auto_backup.auto_backup_menu
msgid "Back-ups"
msgstr "Back-ups"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__folder
msgid "Backup Directory"
msgstr "Backup folder"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__backup_type
msgid "Backup Type"
msgstr "Soort back-up"
#. module: auto_backup
#: model:ir.model,name:auto_backup.model_db_backup
msgid "Backup configuration record"
msgstr "Back-up configuratie"
#. module: auto_backup
#: model:ir.actions.server,name:auto_backup.backup_scheduler_ir_actions_server
#: model:ir.cron,cron_name:auto_backup.backup_scheduler
#: model:ir.cron,name:auto_backup.backup_scheduler
msgid "Backup scheduler"
msgstr "Backup planner"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_tree
msgid "Backups"
msgstr "Back-ups"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__days_to_keep_sftp
msgid ""
"Choose after how many days the backup should be deleted from the FTP server. For example:\n"
"If you fill in 5 the backups will be removed after 5 days from the FTP server."
msgstr "Kies na hoeveel dagen de backups verwijderd moeten worden van de FTP server. Bijvoorbeeld:\n"
"Als u 5 invult zal de backup na 5 dagen verwijderd worden van de FTP server."
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__days_to_keep
msgid ""
"Choose after how many days the backup should be deleted. For example:\n"
"If you fill in 5 the backups will be removed after 5 days."
msgstr "Kies na hoeveel dagen de backups verwijderd moeten worden van de FTP server. Bijvoorbeeld:\n"
"Als u 5 invult zal de backup na 5 dagen verwijderd worden van de FTP server."
#. module: auto_backup
#: model:ir.actions.act_window,name:auto_backup.action_backup
#: model:ir.ui.menu,name:auto_backup.backup_conf_menu
msgid "Configure back-ups"
msgstr "Configureer back-ups"
#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:0
#, python-format
msgid "Connection Test Failed!"
msgstr "Connectie test mislukt!"
#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:0
#, python-format
msgid ""
"Connection Test Succeeded!\n"
"Everything seems properly set up for FTP back-ups!"
msgstr "Connectie test succesvol!\n"
"Alles lijkt correct opgezet voor FTP backups!"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Contact me!"
msgstr "Contacteer mij!"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__create_uid
msgid "Created by"
msgstr "Aangemaakt door"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__create_date
msgid "Created on"
msgstr "Aangemaakt op"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__name
msgid "Database"
msgstr "Database"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__name
msgid "Database you want to schedule backups for"
msgstr "Dataabse waar u back-ups voor wilt plannen"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__display_name
msgid "Display Name"
msgstr "Schermnaam"
#. module: auto_backup
#: model:ir.model.fields.selection,name:auto_backup.selection__db_backup__backup_type__dump
msgid "Dump"
msgstr "Dump"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__email_to_notify
msgid "E-mail to notify"
msgstr "E-mail om te verwittigen"
#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:0
#, python-format
msgid "Error ! No such database exists!"
msgstr "Fout! Deze database bestaat niet!"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__email_to_notify
msgid ""
"Fill in the e-mail where you want to be notified that the backup failed on "
"the FTP."
msgstr "Vul de e-mail in waarop u wilt verwittigd worden als de backup mislukt op de FTP."
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "For example: /odoo/backups/"
msgstr "Bijvoorbeeld: /odoo/backups/"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Go to Settings / Technical / Automation / Scheduled Actions."
msgstr "Ga naar Instellingen / Technisch / Automatisering / Geplande acties."
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Help"
msgstr "Help"
#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:0
#, python-format
msgid "Here is what we got instead:\n"
msgstr "Hier is wat we in de plaats terugkregen:\n"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__host
msgid "Host"
msgstr "Host"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__id
msgid "ID"
msgstr "ID"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__sftp_host
msgid "IP Address SFTP Server"
msgstr "IP adres SFTP server"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__send_mail_sftp_fail
msgid ""
"If you check this option you can choose to automaticly get e-mailed when the"
" backup to the external server failed."
msgstr "Als u deze optie aanvinkt kan u kiezen om automatisch een e-mail aan te krijgen als de backuaar de externe server mislukt."
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__autoremove
msgid ""
"If you check this option you can choose to automaticly remove the backup "
"after xx days"
msgstr "Als u deze optie aanvinkt kan u kiezen om automatisch backups te verwijderen "
"na xx dagen"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__sftp_write
msgid ""
"If you check this option you can specify the details needed to write to a "
"remote server with SFTP."
msgstr "Als u deze optie aanvinkt kan u de details invullen die nodig zijn om te connecteren met de "
" externe SFTP server"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup____last_update
msgid "Last Modified on"
msgstr "Laatst gewijzigd op"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__write_uid
msgid "Last Updated by"
msgstr "Laatst bijgewerkt door"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__write_date
msgid "Last Updated on"
msgstr "Laatst bijgewerkt op"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Local backup configuration"
msgstr "Lokale back-up configuratie"
#. module: auto_backup
#: model:res.groups,name:auto_backup.group_manager
msgid "Manager"
msgstr "Beheerder"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Need more help?"
msgstr "Meer hulp nodig?"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__sftp_password
msgid "Password User SFTP Server"
msgstr "Wachtwoord gebruiker SFTP server"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__sftp_path
msgid "Path external server"
msgstr "Pad externe server"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__port
msgid "Port"
msgstr "Poort"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__days_to_keep_sftp
msgid "Remove SFTP after x days"
msgstr "Verwijderd SFTP na x dagen"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__days_to_keep
msgid "Remove after x days"
msgstr "Verwijder na x dagen"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "SFTP"
msgstr "SFTP"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__sftp_port
msgid "SFTP Port"
msgstr "SFTP poort"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Search the action named 'Backup scheduler'."
msgstr "Zoek de actie met de naam 'Backup planner'."
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid ""
"Set the scheduler to active and fill in how often you want backups "
"generated."
msgstr "Zet de planner actief en vul in hoe vaak u wilt dat er backups gegenereerd "
"worden."
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Test SFTP Connection"
msgstr "Test SFTP verbinding"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__sftp_host
msgid "The IP address from your remote server. For example 192.168.0.1"
msgstr "Het IP adres van uw externe server. Bijvoorbeeld: 192.168.0.1"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__sftp_path
msgid ""
"The location to the folder where the dumps should be written to. For example /odoo/backups/.\n"
"Files will then be written to /odoo/backups/ on your remote server."
msgstr "De locatie naar de folder waar de backup naar toe moet geschreven worden. Bijvoorbeeld odoo/backups/\n"
"Bestanden worden dan naar /odoo/backups/ geschreven op de externe server"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__sftp_password
msgid ""
"The password from the user where the SFTP connection should be made with. "
"This is the password from the user on the external server."
msgstr "Het wachtwoord van de gebruiker waar de SFTP connectie mee moet gemaakt worden. "
"Dit is het wachtwoord van de gebruiker op de externe server."
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__sftp_port
msgid "The port on the FTP server that accepts SSH/SFTP calls."
msgstr "De poort op de FTP server die SSH/SFTP accepteert."
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__sftp_user
msgid ""
"The username where the SFTP connection should be made with. This is the user"
" on the external server."
msgstr "De gebruikersnaam waar de SFTP connectie mee gemaakt moet worden. Dit is de gebruiker"
" op de externe server."
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid ""
"This configures the scheduler for automatic backup of the given database running on given host\n"
" at given port on regular intervals.\n"
" <br/>\n"
" Automatic backups of the database can be scheduled as follows:"
msgstr "Dit configureert de planner om automatische backups van de opgegeven database te maken die op deze host,\n"
" op een bepaalde poort draaien, op regelmatige intervals.\n"
" <br/>\n"
" Automatische backups kunnen als volgt ingepland worden:"
#. module: auto_backup
#: model:ir.module.category,description:auto_backup.module_management
msgid "User access level for this module"
msgstr "Gebruikerstoegang voor deze module"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__sftp_user
msgid "Username SFTP Server"
msgstr "Gebruikersnaam SFTP server"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__sftp_write
msgid "Write to external server with sftp"
msgstr "Schrijf naar externe server met SFTP"
#. module: auto_backup
#: model:ir.model.fields.selection,name:auto_backup.selection__db_backup__backup_type__zip
msgid "Zip"
msgstr "ZIP"

View File

@@ -0,0 +1,178 @@
# Polish translation for openobject-addons
# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2009-11-24 13:49+0000\n"
"PO-Revision-Date: 2011-02-15 15:01+0000\n"
"Last-Translator: OpenERP Administrators <Unknown>\n"
"Language-Team: Polish <pl@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2013-04-20 05:35+0000\n"
"X-Generator: Launchpad (build 16567)\n"
#. module: auto_backup
#: help:db.backup,name:0
msgid "Database you want to schedule backups for"
msgstr "Baza danych, dla której chcesz zaplanować robienie kopii zapasowej"
#. module: auto_backup
#: constraint:ir.model:0
msgid ""
"The Object name must start with x_ and not contain any special character !"
msgstr ""
"Nazwa obiektu musi zaczynać się od x_ oraz nie może zawierać znaków "
"specjalnych !"
#. module: auto_backup
#: constraint:ir.actions.act_window:0
msgid "Invalid model name in the action definition."
msgstr "Nieprawidłowa nazwa modelu w definicji akcji."
#. module: auto_backup
#: model:ir.model,name:auto_backup.model_db_backup
msgid "db.backup"
msgstr ""
#. module: auto_backup
#: view:db.backup:0
msgid ""
"1) Go to Administration / Configuration / Scheduler / Scheduled Actions"
msgstr "1) Idź do Administracja / Konfirguracja / Planista / Planowane akcje"
#. module: auto_backup
#: model:ir.actions.act_window,name:auto_backup.action_backup_conf_form
#: model:ir.ui.menu,name:auto_backup.backup_conf_menu
msgid "Configure Backup"
msgstr "Konfiguruj kopie zapasowe"
#. module: auto_backup
#: view:db.backup:0
msgid "Test"
msgstr "Przetestuj"
#. module: auto_backup
#: view:db.backup:0
msgid "IP Configuration"
msgstr "Konfiguracja adresu IP"
#. module: auto_backup
#: help:db.backup,bkp_dir:0
msgid "Absolute path for storing the backups"
msgstr "Pełna ścieżka dla kopii zapasowych"
#. module: auto_backup
#: model:ir.module.module,shortdesc:auto_backup.module_meta_information
msgid "Database Auto-Backup"
msgstr "Automatyczne kopie zapasowe bazy danych"
#. module: auto_backup
#: view:db.backup:0
msgid "Database Configuration"
msgstr "Konfiguracja bazy danych"
#. module: auto_backup
#: view:db.backup:0
msgid "4) Set other values as per your preference"
msgstr "4) Ustaw inne dane według uznania"
#. module: auto_backup
#: field:db.backup,host:0
msgid "Host"
msgstr "Host"
#. module: auto_backup
#: view:db.backup:0
msgid ""
"Automatic backup of all the databases under this can be scheduled as "
"follows: "
msgstr ""
"Automatyczne kopie wszystkich baz danych mogą być zaplanowane następująco: "
#. module: auto_backup
#: constraint:ir.ui.view:0
msgid "Invalid XML for View Architecture!"
msgstr "XML niewłaściwy dla tej architektury wyświetlania!"
#. module: auto_backup
#: field:db.backup,bkp_dir:0
msgid "Backup Directory"
msgstr "Katalog kopii zapasowych"
#. module: auto_backup
#: field:db.backup,name:0
msgid "Database"
msgstr "Baza danych"
#. module: auto_backup
#: view:db.backup:0
msgid "2) Schedule new action(create a new record)"
msgstr "2) Zaplanuj nową akcję (utwórz nowy rekord)"
#. module: auto_backup
#: model:ir.module.module,description:auto_backup.module_meta_information
msgid ""
"The generic Open ERP Database Auto-Backup system enables the user to make "
"configurations for the automatic backup of the database.\n"
"User simply requires to specify host & port under IP Configuration & "
"database(on specified host running at specified port) and backup "
"directory(in which all the backups of the specified database will be stored) "
"under Database Configuration.\n"
"\n"
"Automatic backup for all such configured databases under this can then be "
"scheduled as follows: \n"
" \n"
"1) Go to Administration / Configuration / Scheduler / Scheduled Actions\n"
"2) Schedule new action(create a new record)\n"
"3) Set 'Object' to 'db.backup' and 'Function' to 'schedule_backup' under "
"page 'Technical Data'\n"
"4) Set other values as per your preference"
msgstr ""
"System Open ERP Database Auto-Backup pozwala użytkownikowi utworzyć "
"konfiguracje dla automatycznego zachowywania kopii zapasowych baz danych.\n"
"Użytkownik musi tylko podać host i port w Konfiguracji IP i bazę danych "
"oraz katalog dla kopii zapasowej Konfiguracji bazy danych.\n"
"\n"
"Automatyczne kopie wszystkich baz danych mogą być zaplanowane następująco: "
"\n"
" \n"
"1) Idź do Administracja / Konfirguracja / Planista / Planowane akcje\n"
"2) Zaplanuj nową akcję (utwórz nowy rekord)\n"
"3) Ustaw 'Obiekt' na 'db.backup' i 'Funkcja' na 'schedule_backup' na stronie "
"'Dane techniczne'.\n"
"4) Ustaw inne dane według uznania"
#. module: auto_backup
#: view:db.backup:0
msgid ""
"3) Set 'Object' to 'db.backup' and 'Function' to 'schedule_backup' under "
"page 'Technical Data'"
msgstr ""
"3) Ustaw 'Obiekt' na 'db.backup' i 'Funkcja' na 'schedule_backup' na stronie "
"'Dane techniczne'."
#. module: auto_backup
#: view:db.backup:0
msgid "Help"
msgstr "Pomoc"
#. module: auto_backup
#: view:db.backup:0
msgid ""
"This configures the scheduler for automatic backup of the given database "
"running on given host at given port on regular intervals."
msgstr ""
"Tu konfigurujesz planistę do automatycznego zapisywania kopii zapasowych dla "
"określonej bazy danych na na określonym hoście (komputerze) na określonym "
"porcie w regularnych terminach."
#. module: auto_backup
#: field:db.backup,port:0
msgid "Port"
msgstr "Port"

View File

@@ -0,0 +1,183 @@
# Brazilian Portuguese translation for openobject-addons
# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2009-11-24 13:49+0000\n"
"PO-Revision-Date: 2013-07-20 09:39+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2013-07-22 05:50+0000\n"
"X-Generator: Launchpad (build 16696)\n"
#. module: auto_backup
#: help:db.backup,name:0
msgid "Database you want to schedule backups for"
msgstr "Banco de dados que você deseja agendar backups para"
#. module: auto_backup
#: constraint:ir.model:0
msgid ""
"The Object name must start with x_ and not contain any special character !"
msgstr ""
"O nome do objeto deve iniciar com x_ e não conter qualquer caractere "
"especial!"
#. module: auto_backup
#: constraint:ir.actions.act_window:0
msgid "Invalid model name in the action definition."
msgstr "Nome do modelo inválida na definição da ação."
#. module: auto_backup
#: model:ir.model,name:auto_backup.model_db_backup
msgid "db.backup"
msgstr "db.backup"
#. module: auto_backup
#: view:db.backup:0
msgid ""
"1) Go to Administration / Configuration / Scheduler / Scheduled Actions"
msgstr ""
"1) Vá para Administração / Configuração / Programador / Ações Programadas"
#. module: auto_backup
#: model:ir.actions.act_window,name:auto_backup.action_backup_conf_form
#: model:ir.ui.menu,name:auto_backup.backup_conf_menu
msgid "Configure Backup"
msgstr "Configurar backup"
#. module: auto_backup
#: view:db.backup:0
msgid "Test"
msgstr "Teste"
#. module: auto_backup
#: view:db.backup:0
msgid "IP Configuration"
msgstr "Configuração de IP"
#. module: auto_backup
#: help:db.backup,bkp_dir:0
msgid "Absolute path for storing the backups"
msgstr "Caminho absoluto para armazenar os backups"
#. module: auto_backup
#: model:ir.module.module,shortdesc:auto_backup.module_meta_information
msgid "Database Auto-Backup"
msgstr "Banco de Dados Auto-Backup"
#. module: auto_backup
#: view:db.backup:0
msgid "Database Configuration"
msgstr "Configuração do Banco de Dados"
#. module: auto_backup
#: view:db.backup:0
msgid "4) Set other values as per your preference"
msgstr "4) Defina outros valores como por sua preferência"
#. module: auto_backup
#: field:db.backup,host:0
msgid "Host"
msgstr "Host"
#. module: auto_backup
#: view:db.backup:0
msgid ""
"Automatic backup of all the databases under this can be scheduled as "
"follows: "
msgstr ""
"Backup automático de todos os bancos de dados sob este pode ser programado "
"como segue: "
#. module: auto_backup
#: constraint:ir.ui.view:0
msgid "Invalid XML for View Architecture!"
msgstr "Inválido XML para Ver Arquitetura!"
#. module: auto_backup
#: field:db.backup,bkp_dir:0
msgid "Backup Directory"
msgstr "Diretório de backup"
#. module: auto_backup
#: field:db.backup,name:0
msgid "Database"
msgstr "Banco de Dados"
#. module: auto_backup
#: view:db.backup:0
msgid "2) Schedule new action(create a new record)"
msgstr "2) Programe nova ação (criar um novo registro)"
#. module: auto_backup
#: model:ir.module.module,description:auto_backup.module_meta_information
msgid ""
"The generic Open ERP Database Auto-Backup system enables the user to make "
"configurations for the automatic backup of the database.\n"
"User simply requires to specify host & port under IP Configuration & "
"database(on specified host running at specified port) and backup "
"directory(in which all the backups of the specified database will be stored) "
"under Database Configuration.\n"
"\n"
"Automatic backup for all such configured databases under this can then be "
"scheduled as follows: \n"
" \n"
"1) Go to Administration / Configuration / Scheduler / Scheduled Actions\n"
"2) Schedule new action(create a new record)\n"
"3) Set 'Object' to 'db.backup' and 'Function' to 'schedule_backup' under "
"page 'Technical Data'\n"
"4) Set other values as per your preference"
msgstr ""
"O sistema Auto-Backup genérico Aberto ERP banco de dados permite que o "
"usuário faça configurações para o backup automático do banco de dados. \n"
"usuário requer simplesmente para especificar anfitrião e porta em "
"Configuração do IP e banco de dados (on especificado host executando na "
"porta especificada) eo diretório de backup (em . que todos os backups de "
"banco de dados especificado serão armazenados) em Configuração do banco de "
"dados \n"
"de backup automático de todos esses bancos de dados configurados sob este "
"pode ser programado como segue:\n"
" \n"
" \n"
"1) Vá para Administração / Configuração / Programador / Ações Programadas \n"
"2) Programe nova ação (criar um novo registro) \n"
"3) Defina 'objeto' para 'db.backup' e 'função' para 'schedule_backup' em "
"página \"Dados Técnicos\" \n"
"4 ) Defina outros valores de acordo com sua preferência"
#. module: auto_backup
#: view:db.backup:0
msgid ""
"3) Set 'Object' to 'db.backup' and 'Function' to 'schedule_backup' under "
"page 'Technical Data'"
msgstr ""
"3) Defina 'objeto' para 'db.backup' e 'função' para 'schedule_backup \"em\" "
"Dados Técnicos \"página"
#. module: auto_backup
#: view:db.backup:0
msgid "Help"
msgstr "Ajudar"
#. module: auto_backup
#: view:db.backup:0
msgid ""
"This configures the scheduler for automatic backup of the given database "
"running on given host at given port on regular intervals."
msgstr ""
"Isso configura o agendador de backup automático de um determinado banco de "
"dados rodando em determinado host em determinada porta em intervalos "
"regulares."
#. module: auto_backup
#: field:db.backup,port:0
msgid "Port"
msgstr "Porto"

View File

@@ -0,0 +1,358 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * auto_backup
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-19 08:18+0000\n"
"PO-Revision-Date: 2024-04-19 08:18+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid ""
"<b>Warning:</b>\n"
" Use SFTP with caution! This writes files to external servers under the path you specify."
msgstr "谨慎使用SFTP这会将文件写入您指定路径下的外部服务器。"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__folder
msgid "Absolute path for storing the backups"
msgstr "备份绝对路径"
#. module: auto_backup
#: model:ir.module.category,name:auto_backup.module_management
msgid "Auto backup access"
msgstr "自动备份权限"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__send_mail_sftp_fail
msgid "Auto. E-mail on backup fail"
msgstr "FTP备份失败自动邮件通知你"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__autoremove
msgid "Auto. Remove Backups"
msgstr "自动删除备份"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Back-up view"
msgstr "备份视图"
#. module: auto_backup
#: model:ir.ui.menu,name:auto_backup.auto_backup_menu
msgid "Back-ups"
msgstr "备份"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__folder
msgid "Backup Directory"
msgstr "备份目录"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__backup_type
msgid "Backup Type"
msgstr "备份类型"
#. module: auto_backup
#: model:ir.model,name:auto_backup.model_db_backup
msgid "Backup configuration record"
msgstr "备份配置记录"
#. module: auto_backup
#: model:ir.actions.server,name:auto_backup.backup_scheduler_ir_actions_server
#: model:ir.cron,cron_name:auto_backup.backup_scheduler
#: model:ir.cron,name:auto_backup.backup_scheduler
msgid "Backup scheduler"
msgstr "备份调度器"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__days_to_keep_sftp
msgid ""
"Choose after how many days the backup should be deleted from the FTP server. For example:\n"
"If you fill in 5 the backups will be removed after 5 days from the FTP server."
msgstr "选择备份应在FTP服务器上删除的天数。例如\n如果您填写5则备份将在5天后从FTP服务器上删除。"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__days_to_keep
msgid ""
"Choose after how many days the backup should be deleted. For example:\n"
"If you fill in 5 the backups will be removed after 5 days."
msgstr "选择备份应删除的天数。例如:\n如果您填写5则备份将在5天后删除。"
#. module: auto_backup
#: model:ir.actions.act_window,name:auto_backup.action_backup
#: model:ir.ui.menu,name:auto_backup.backup_conf_menu
msgid "Configure back-ups"
msgstr "配置备份"
#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:0
#, python-format
msgid "Connection Test Failed!"
msgstr "连接测试失败!"
#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:0
#, python-format
msgid ""
"Connection Test Succeeded!\n"
"Everything seems properly set up for FTP back-ups!"
msgstr "连接测试成功!\nFTP备份似乎已正确设置"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Contact me!"
msgstr "联系我!"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__create_uid
msgid "Created by"
msgstr "创建者"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__create_date
msgid "Created on"
msgstr "创建日期"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__name
msgid "Database"
msgstr "数据库"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__name
msgid "Database you want to schedule backups for"
msgstr "您要安排备份的数据库"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__display_name
msgid "Display Name"
msgstr "显示名称"
#. module: auto_backup
#: model:ir.model.fields.selection,name:auto_backup.selection__db_backup__backup_type__dump
msgid "Dump"
msgstr ""
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__email_to_notify
msgid "E-mail to notify"
msgstr "通知邮件"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__email_to_notify
msgid ""
"Fill in the e-mail where you want to be notified that the backup failed on "
"the FTP."
msgstr "填写您希望在FTP上备份失败时收到通知的电子邮件"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "For example: /odoo/backups/"
msgstr "例如:/odoo/backups/"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Go to Settings / Technical / Automation / Scheduled Actions."
msgstr "前往 设置/技术/自动化/计划任务。"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Help"
msgstr "帮助"
#. module: auto_backup
#: code:addons/auto_backup/models/db_backup.py:0
#, python-format
msgid "Here is what we got instead:\n"
msgstr "这是我们得到的内容:\n"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__host
msgid "Host"
msgstr "主机"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__id
msgid "ID"
msgstr "标识"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__sftp_host
msgid "IP Address SFTP Server"
msgstr "SFTP服务器的IP地址"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__send_mail_sftp_fail
msgid ""
"If you check this option you can choose to automaticly get e-mailed when the"
" backup to the external server failed."
msgstr "如果您选中此选项,当备份到外部服务器失败时,您可以选择自动接收邮件通知。"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__autoremove
msgid ""
"If you check this option you can choose to automaticly remove the backup "
"after xx days"
msgstr "如果您选中此选项您可以选择在xx天后自动删除备份"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__sftp_write
msgid ""
"If you check this option you can specify the details needed to write to a "
"remote server with SFTP."
msgstr "如果您选中此选项您可以指定使用SFTP向远程服务器写入所需的详细信息。"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup____last_update
msgid "Last Modified on"
msgstr "最后修改时间"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__write_uid
msgid "Last Updated by"
msgstr "最后更新者"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__write_date
msgid "Last Updated on"
msgstr "最后更新时间"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Local backup configuration"
msgstr "本地备份配置"
#. module: auto_backup
#: model:res.groups,name:auto_backup.group_manager
msgid "Manager"
msgstr "管理者"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Need more help?"
msgstr "需要更多帮助?"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__sftp_password
msgid "Password User SFTP Server"
msgstr "SFTP服务器用户密码"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__sftp_path
msgid "Path external server"
msgstr "外部服务器路径"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__port
msgid "Port"
msgstr "端口"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__days_to_keep_sftp
msgid "Remove SFTP after x days"
msgstr "x天后删除SFTP"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__days_to_keep
msgid "Remove after x days"
msgstr "x天后删除"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "SFTP"
msgstr ""
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__sftp_port
msgid "SFTP Port"
msgstr "SFTP端口"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Search the action named 'Backup scheduler'."
msgstr "搜索名为'备份调度器'的操作。"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid ""
"Set the scheduler to active and fill in how often you want backups "
"generated."
msgstr "将调度器设置为激活状态,并填写您希望生成备份的频率。"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid "Test SFTP Connection"
msgstr "测试SFTP连接"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__sftp_host
msgid "The IP address from your remote server. For example 192.168.0.1"
msgstr "远程服务器的IP地址。例如192.168.0.1"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__sftp_path
msgid ""
"The location to the folder where the dumps should be written to. For example /odoo/backups/.\n"
"Files will then be written to /odoo/backups/ on your remote server."
msgstr "转储文件应该写入的文件夹位置。例如 /odoo/backups/。\n文件将被写入远程服务器上的 /odoo/backups/。"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__sftp_password
msgid ""
"The password from the user where the SFTP connection should be made with. "
"This is the password from the user on the external server."
msgstr "应与之建立SFTP连接的用户的密码。\n这是外部服务器上用户的密码。"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__sftp_port
msgid "The port on the FTP server that accepts SSH/SFTP calls."
msgstr "FTP服务器上接受SSH/SFTP调用的端口。"
#. module: auto_backup
#: model:ir.model.fields,help:auto_backup.field_db_backup__sftp_user
msgid ""
"The username where the SFTP connection should be made with. This is the user"
" on the external server."
msgstr "应与之建立SFTP连接的用户名。这是外部服务器上的用户。"
#. module: auto_backup
#: model_terms:ir.ui.view,arch_db:auto_backup.view_backup_config_form
msgid ""
"This configures the scheduler for automatic backup of the given database running on given host\n"
" at given port on regular intervals.\n"
" <br/>\n"
" Automatic backups of the database can be scheduled as follows:"
msgstr "这将配置调度器,以定期自动备份在指定主机上运行的给定数据库\n"
" 在给定端口上。\n"
" <br/>\n"
" 可以如下安排数据库的自动备份:"
#. module: auto_backup
#: model:ir.module.category,description:auto_backup.module_management
msgid "User access level for this module"
msgstr "此模块的用户访问级别"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__sftp_user
msgid "Username SFTP Server"
msgstr "SFTP服务器用户名"
#. module: auto_backup
#: model:ir.model.fields,field_description:auto_backup.field_db_backup__sftp_write
msgid "Write to external server with sftp"
msgstr "通过SFTP向外部服务器写入"
#. module: auto_backup
#: model:ir.model.fields.selection,name:auto_backup.selection__db_backup__backup_type__zip
msgid "Zip"
msgstr ""

View File

@@ -0,0 +1,2 @@
from . import db_backup

View File

@@ -0,0 +1,328 @@
import os
import datetime
import time
import shutil
import json
import tempfile
from odoo import models, fields, api, tools, _
from odoo.exceptions import Warning, AccessDenied
import odoo
import logging
_logger = logging.getLogger(__name__)
try:
import paramiko
except ImportError:
raise ImportError(
'This module needs paramiko to automatically write backups to the FTP through SFTP. '
'Please install paramiko on your system. (sudo pip3 install paramiko)')
class DbBackup(models.Model):
_name = 'db.backup'
_description = 'Backup configuration record'
def _get_db_name(self):
dbName = self._cr.dbname
return dbName
# Columns for local server configuration
host = fields.Char('Host', required=True, default='localhost')
port = fields.Char('Port', required=True, default=8069)
name = fields.Char('Database', required=True, help='Database you want to schedule backups for',
default=_get_db_name)
folder = fields.Char('Backup Directory', help='Absolute path for storing the backups', required='True',
default='/odoo/backups')
backup_type = fields.Selection([('zip', 'Zip'), ('dump', 'Dump')], 'Backup Type', required=True, default='zip')
autoremove = fields.Boolean('Auto. Remove Backups',
help='If you check this option you can choose to automaticly remove the backup '
'after xx days')
days_to_keep = fields.Integer('Remove after x days',
help="Choose after how many days the backup should be deleted. For example:\n"
"If you fill in 5 the backups will be removed after 5 days.",
required=True)
# Columns for external server (SFTP)
sftp_write = fields.Boolean('Write to external server with sftp',
help="If you check this option you can specify the details needed to write to a remote "
"server with SFTP.")
sftp_path = fields.Char('Path external server',
help='The location to the folder where the dumps should be written to. For example '
'/odoo/backups/.\nFiles will then be written to /odoo/backups/ on your remote server.')
sftp_host = fields.Char('IP Address SFTP Server',
help='The IP address from your remote server. For example 192.168.0.1')
sftp_port = fields.Integer('SFTP Port', help='The port on the FTP server that accepts SSH/SFTP calls.', default=22)
sftp_user = fields.Char('Username SFTP Server',
help='The username where the SFTP connection should be made with. This is the user on the '
'external server.')
sftp_password = fields.Char('Password User SFTP Server',
help='The password from the user where the SFTP connection should be made with. This '
'is the password from the user on the external server.')
days_to_keep_sftp = fields.Integer('Remove SFTP after x days',
help='Choose after how many days the backup should be deleted from the FTP '
'server. For example:\nIf you fill in 5 the backups will be removed after '
'5 days from the FTP server.',
default=30)
send_mail_sftp_fail = fields.Boolean('Auto. E-mail on backup fail',
help='If you check this option you can choose to automaticly get e-mailed '
'when the backup to the external server failed.')
email_to_notify = fields.Char('E-mail to notify',
help='Fill in the e-mail where you want to be notified that the backup failed on '
'the FTP.')
def test_sftp_connection(self, context=None):
self.ensure_one()
# Check if there is a success or fail and write messages
message_title = ""
message_content = ""
error = ""
has_failed = False
for rec in self:
ip_host = rec.sftp_host
port_host = rec.sftp_port
username_login = rec.sftp_user
password_login = rec.sftp_password
# Connect with external server over SFTP, so we know sure that everything works.
try:
s = paramiko.SSHClient()
s.set_missing_host_key_policy(paramiko.AutoAddPolicy())
s.connect(ip_host, port_host, username_login, password_login, timeout=10)
sftp = s.open_sftp()
sftp.close()
message_title = _("Connection Test Succeeded!\nEverything seems properly set up for FTP back-ups!")
except Exception as e:
_logger.critical('There was a problem connecting to the remote ftp: %s', str(e))
error += str(e)
has_failed = True
message_title = _("Connection Test Failed!")
if len(rec.sftp_host) < 8:
message_content += "\nYour IP address seems to be too short.\n"
message_content += _("Here is what we got instead:\n")
finally:
if s:
s.close()
if has_failed:
raise Warning(message_title + '\n\n' + message_content + "%s" % str(error))
else:
raise Warning(message_title + '\n\n' + message_content)
@api.model
def schedule_backup(self):
conf_ids = self.search([])
for rec in conf_ids:
try:
if not os.path.isdir(rec.folder):
os.makedirs(rec.folder)
except:
raise
# Create name for dumpfile.
bkp_file = '%s_%s.%s' % (time.strftime('%Y_%m_%d_%H_%M_%S'), rec.name, rec.backup_type)
file_path = os.path.join(rec.folder, bkp_file)
try:
# try to backup database and write it away
fp = open(file_path, 'wb')
self._take_dump(rec.name, fp, 'db.backup', rec.backup_type)
fp.close()
except Exception as error:
_logger.debug(
"Couldn't backup database %s. Bad database administrator password for server running at "
"http://%s:%s" % (rec.name, rec.host, rec.port))
_logger.debug("Exact error from the exception: %s", str(error))
continue
# Check if user wants to write to SFTP or not.
if rec.sftp_write is True:
try:
# Store all values in variables
dir = rec.folder
path_to_write_to = rec.sftp_path
ip_host = rec.sftp_host
port_host = rec.sftp_port
username_login = rec.sftp_user
password_login = rec.sftp_password
_logger.debug('sftp remote path: %s', path_to_write_to)
try:
s = paramiko.SSHClient()
s.set_missing_host_key_policy(paramiko.AutoAddPolicy())
s.connect(ip_host, port_host, username_login, password_login, timeout=20)
sftp = s.open_sftp()
except Exception as error:
_logger.critical('Error connecting to remote server! Error: %s', str(error))
try:
sftp.chdir(path_to_write_to)
except IOError:
# Create directory and subdirs if they do not exist.
current_directory = ''
for dirElement in path_to_write_to.split('/'):
current_directory += dirElement + '/'
try:
sftp.chdir(current_directory)
except:
_logger.info('(Part of the) path didn\'t exist. Creating it now at %s',
current_directory)
# Make directory and then navigate into it
sftp.mkdir(current_directory, 777)
sftp.chdir(current_directory)
pass
sftp.chdir(path_to_write_to)
# Loop over all files in the directory.
for f in os.listdir(dir):
if rec.name in f:
fullpath = os.path.join(dir, f)
if os.path.isfile(fullpath):
try:
sftp.stat(os.path.join(path_to_write_to, f))
_logger.debug(
'File %s already exists on the remote FTP Server ------ skipped', fullpath)
# This means the file does not exist (remote) yet!
except IOError:
try:
sftp.put(fullpath, os.path.join(path_to_write_to, f))
_logger.info('Copying File % s------ success', fullpath)
except Exception as err:
_logger.critical(
'We couldn\'t write the file to the remote server. Error: %s', str(err))
# Navigate in to the correct folder.
sftp.chdir(path_to_write_to)
_logger.debug("Checking expired files")
# Loop over all files in the directory from the back-ups.
# We will check the creation date of every back-up.
for file in sftp.listdir(path_to_write_to):
if rec.name in file:
# Get the full path
fullpath = os.path.join(path_to_write_to, file)
# Get the timestamp from the file on the external server
timestamp = sftp.stat(fullpath).st_mtime
createtime = datetime.datetime.fromtimestamp(timestamp)
now = datetime.datetime.now()
delta = now - createtime
# If the file is older than the days_to_keep_sftp (the days to keep that the user filled in
# on the Odoo form it will be removed.
if delta.days >= rec.days_to_keep_sftp:
# Only delete files, no directories!
if ".dump" in file or '.zip' in file:
_logger.info("Delete too old file from SFTP servers: %s", file)
sftp.unlink(file)
# Close the SFTP session.
sftp.close()
s.close()
except Exception as e:
try:
sftp.close()
s.close()
except:
pass
_logger.error('Exception! We couldn\'t back up to the FTP server. Here is what we got back '
'instead: %s', str(e))
# At this point the SFTP backup failed. We will now check if the user wants
# an e-mail notification about this.
if rec.send_mail_sftp_fail:
try:
ir_mail_server = self.env['ir.mail_server'].search([], order='sequence asc', limit=1)
message = "Dear,\n\nThe backup for the server " + rec.host + " (IP: " + rec.sftp_host + \
") failed. Please check the following details:\n\nIP address SFTP server: " + \
rec.sftp_host + "\nUsername: " + rec.sftp_user + \
"\n\nError details: " + tools.ustr(e) + \
"\n\nWith kind regards"
catch_all_domain = self.env["ir.config_parameter"].sudo().get_param("mail.catchall.domain")
response_mail = "auto_backup@%s" % catch_all_domain if catch_all_domain else self.env.user.partner_id.email
msg = ir_mail_server.build_email(response_mail, [rec.email_to_notify],
"Backup from " + rec.host + "(" + rec.sftp_host +
") failed",
message)
ir_mail_server.send_email(msg)
except Exception:
pass
# Remove all old files (on local server) in case this is configured..
if rec.autoremove:
directory = rec.folder
# Loop over all files in the directory.
for f in os.listdir(directory):
fullpath = os.path.join(directory, f)
# Only delete the ones wich are from the current database
# (Makes it possible to save different databases in the same folder)
if rec.name in fullpath:
timestamp = os.stat(fullpath).st_ctime
createtime = datetime.datetime.fromtimestamp(timestamp)
now = datetime.datetime.now()
delta = now - createtime
if delta.days >= rec.days_to_keep:
# Only delete files (which are .dump and .zip), no directories.
if os.path.isfile(fullpath) and (".dump" in f or '.zip' in f):
_logger.info("Delete local out-of-date file: %s", fullpath)
os.remove(fullpath)
# This is more or less the same as the default Odoo function at
# https://github.com/odoo/odoo/blob/e649200ab44718b8faefc11c2f8a9d11f2db7753/odoo/service/db.py#L209
# The main difference is that we do not do have a wrapper for the function check_db_management_enabled here and
# that we authenticate based on the cron its user id and by checking if we have 'db.backup' defined in the function
# call. Since this function is called from the cron and since we have these security checks on model and on user_id
# its pretty impossible to hack any way to take a backup. This allows us to disable the Odoo database manager
# which is a MUCH safer way
def _take_dump(self, db_name, stream, model, backup_format='zip'):
"""Dump database `db` into file-like object `stream` if stream is None
return a file object with the dump """
cron_user_id = self.env.ref('jikimo_auto_backup.backup_scheduler').user_id.id
if self._name != 'db.backup' or cron_user_id != self.env.user.id:
_logger.error('Unauthorized database operation. Backups should only be available from the cron job.')
raise AccessDenied()
_logger.info('DUMP DB: %s format %s', db_name, backup_format)
cmd = ['pg_dump', '--no-owner']
cmd.append(db_name)
if backup_format == 'zip':
with tempfile.TemporaryDirectory() as dump_dir:
filestore = odoo.tools.config.filestore(db_name)
if os.path.exists(filestore):
shutil.copytree(filestore, os.path.join(dump_dir, 'filestore'))
with open(os.path.join(dump_dir, 'manifest.json'), 'w') as fh:
db = odoo.sql_db.db_connect(db_name)
with db.cursor() as cr:
json.dump(self._dump_db_manifest(cr), fh, indent=4)
cmd.insert(-1, '--file=' + os.path.join(dump_dir, 'dump.sql'))
odoo.tools.exec_pg_command(*cmd)
if stream:
odoo.tools.osutil.zip_dir(dump_dir, stream, include_dir=False, fnct_sort=lambda file_name: file_name != 'dump.sql')
else:
t=tempfile.TemporaryFile()
odoo.tools.osutil.zip_dir(dump_dir, t, include_dir=False, fnct_sort=lambda file_name: file_name != 'dump.sql')
t.seek(0)
return t
else:
cmd.insert(-1, '--format=c')
stdin, stdout = odoo.tools.exec_pg_command_pipe(*cmd)
if stream:
shutil.copyfileobj(stdout, stream)
else:
return stdout
def _dump_db_manifest(self, cr):
pg_version = "%d.%d" % divmod(cr._obj.connection.server_version / 100, 100)
cr.execute("SELECT name, latest_version FROM ir_module_module WHERE state = 'installed'")
modules = dict(cr.fetchall())
manifest = {
'odoo_dump': '1',
'db_name': cr.dbname,
'version': odoo.release.version,
'version_info': odoo.release.version_info,
'major_version': odoo.release.major_version,
'pg_version': pg_version,
'modules': modules,
}
return manifest

View File

@@ -0,0 +1 @@
# pip3 install paramiko

View File

@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
admin_access, db_backup admin access,model_db_backup,base.group_no_one,1,1,1,1
admin_security_rule, Model db_backup admin access,model_db_backup,jikimo_auto_backup.group_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 admin_access db_backup admin access model_db_backup base.group_no_one 1 1 1 1
3 admin_security_rule Model db_backup admin access model_db_backup jikimo_auto_backup.group_manager 1 1 1 1

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record model="ir.module.category" id="module_management">
<field name="name">Auto backup access</field>
<field name="description">User access level for this module</field>
<field name="sequence">3</field>
</record>
<record id="group_manager" model="res.groups">
<field name="name">Manager</field>
<field name="category_id" ref="jikimo_auto_backup.module_management"/>
</record>
</data>
</odoo>

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -0,0 +1,95 @@
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<h2 class="oe_slogan">Automated backups</h2>
<h3 class="oe_slogan">A tool for all your back-ups, internal and external!</h3>
</div>
<div class="oe_span6">
<div class="oe_demo oe_picture oe_screenshot">
<img src="overview.png">
</div>
</div>
<div class="oe_span6">
<p class="oe_mt32">
Keep your Odoo data safe with this module. Take automated back-ups, remove them automatically
and even write them to an external server through an encrypted tunnel.
You can even specify how long local backups and external backups should be kept, automatically!
</p>
<div class="oe_centeralign oe_websiteonly">
<a href="http://www.openerp.com/start?app=mail" class="oe_button oe_big oe_tacky">Start your <span class="oe_emph">free</span> trial</a>
</div>
</div>
</div>
</section>
<!-- Second block -->
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<h2 class="oe_slogan">Connect with an FTP Server</h2>
<h3 class="oe_slogan">Keep your data safe, through an SSH tunnel!</h3>
<div class="oe_span6">
<p class="oe_mt32">
Want to go even further and write your backups to an external server?
You can with this module! Specify the credentials to the server, specify a path and everything will be backed up automatically. This is done through an SSH (encrypted) tunnel, thanks to pysftp, so your data is safe!
</p>
</div>
<div class="oe_span6">
<div class="oe_row_img oe_centered">
<img class="oe_picture oe_screenshot" src="terminalssh.png">
</div>
</div>
</div>
</section>
<!--Third block -->
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<h2 class="oe_slogan">Test connection</h2>
<h3 class="oe_slogan">Checks your credentials in one click</h3>
</div>
<div class="oe_span6">
<div class="oe_demo oe_picture oe_screenshot">
<img src="testconnection.png">
<img src="testconnectionfailed.png">
</div>
</div>
<div class="oe_span6">
<p class="oe_mt32">
Want to make sure if the connection details are correct and if Odoo can automatically write them to the remote server? Simply click on the 'Test SFTP Connection' button and you will get message telling you if everything is OK, or what is wrong!
</p>
</div>
</div>
</section>
<!-- Fourth block -->
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<h2 class="oe_slogan">E-mail on backup failure</h2>
<h3 class="oe_slogan">Stay informed of problems, automatically!</h3>
<div class="oe_span6">
<p class="oe_mt32">
Do you want to know if the database backup failed? Check the checkbox 'Auto. E-mail on backup fail' and fill in your e-mail.
Every time a backup fails you will get an e-mail in your mailbox with technical details.
</p>
</div>
<div class="oe_span6">
<div class="oe_row_img oe_centered">
<img class="oe_picture oe_screenshot" src="emailnotification.png">
</div>
</div>
</div>
</section>
<!--Fifth block -->
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<h2 class="oe_slogan">Contact / Support</h2>
<h3 class="oe_slogan">Need help or want extra features?</h3>
</div>
<div class="oe_span6">
<p class="oe_mt32">
Need help with the configuration or want this module to have more functionalities?
Please create a bug report <a href="https://github.com/Yenthe666/auto_backup/issues">on the Github issue tracker</a>
</p>
</div>
</div>
</section>

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1,105 @@
<odoo>
<record id="view_backup_config_form" model="ir.ui.view">
<field name="name">db.backup.form</field>
<field name="model">db.backup</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Back-up view">
<sheet>
<group col="4" colspan="4">
<separator col="2" string="Local backup configuration"/>
</group>
<group name="configuration">
<field name="host" colspan="2"/>
<field name="name"/>
<field name="port"/>
<field name="backup_type"/>
<field name="folder"/>
<field name="autoremove"/>
<field name="days_to_keep" attrs="{'invisible': [('autoremove','=',False)]}"/>
</group>
<group name="allow_stfp" col="4" colspan="4">
<separator col="2" string="SFTP"/>
</group>
<div style="width:50%;border-radius:10px;margin: 10px 0px;padding:15px 10px 15px 10px;
background-repeat: no-repeat;background-position: 10px center;color: #9F6000;
background-color: #FEEFB3;"
attrs="{'invisible': [('sftp_write','=',False)]}">
<b>Warning:</b>
Use SFTP with caution! This writes files to external servers under the path you specify.
</div>
<group name="sftp_configuration">
<field name="sftp_write"/>
<field name="sftp_host"
attrs="{'invisible':[('sftp_write', '=', False)],'required':[('sftp_write', '=', True)]}"/>
<field name="sftp_port"
attrs="{'invisible':[('sftp_write', '=', False)],'required':[('sftp_write', '=', True)]}"/>
<field name="sftp_user"
attrs="{'invisible':[('sftp_write', '=', False)], 'required':[('sftp_write', '=', True)]}"/>
<field name="sftp_password"
attrs="{'invisible':[('sftp_write', '=', False)],'required': [('sftp_write', '=', True)]}"
password="True"/>
<field name="sftp_path"
attrs="{'invisible':[('sftp_write', '==', False)],'required':[('sftp_write', '==', True)]}"
placeholder="For example: /odoo/backups/"/>
<field name="days_to_keep_sftp"
attrs="{'invisible': [('sftp_write', '=', False)], 'required': [('sftp_write', '=', True)]}"/>
<field name="send_mail_sftp_fail" attrs="{'invisible': [('sftp_write','=',False)]}"/>
<field name="email_to_notify"
attrs="{'invisible':['|',('send_mail_sftp_fail', '==', False), ('sftp_write', '=', False)], 'required': [('send_mail_sftp_fail', '=', True)]}"/>
<button name="test_sftp_connection" type="object"
attrs="{'invisible': [('sftp_write','=',False)]}" string="Test SFTP Connection"/>
</group>
<separator string="Help" colspan="2"/>
<div name="configuration_details">
This configures the scheduler for automatic backup of the given database running on given host
at given port on regular intervals.
<br/>
Automatic backups of the database can be scheduled as follows:
<ol>
<li>
Go to Settings / Technical / Automation / Scheduled Actions.
</li>
<li>
Search the action named 'Backup scheduler'.
</li>
<li>
Set the scheduler to active and fill in how often you want backups generated.
</li>
</ol>
<p style="font-size:18px;">
Need more help?
<a href="https://github.com/Yenthe666/auto_backup/issues">Contact me!</a>
</p>
</div>
</sheet>
</form>
</field>
</record>
<record id="view_backup_config_tree" model="ir.ui.view">
<field name="name">db.backup.tree</field>
<field name="model">db.backup</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree>
<field name='host'/>
<field name='port'/>
<field name='name'/>
<field name='folder'/>
<field name="autoremove"/>
<field name="sftp_host"/>
</tree>
</field>
</record>
<record id="action_backup" model="ir.actions.act_window">
<field name="name">Configure back-ups</field>
<field name="res_model">db.backup</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_backup_config_tree"/>
</record>
<menuitem id="auto_backup_menu" name="Back-ups" parent="base.menu_custom"/>
<menuitem parent="auto_backup_menu" action="action_backup" id="backup_conf_menu"/>
</odoo>

View File

@@ -5,6 +5,7 @@ import {patch} from '@web/core/utils/patch';
import {_t} from "@web/core/l10n/translation";
import {FormStatusIndicator} from "@web/views/form/form_status_indicator/form_status_indicator";
import {ListRenderer} from "@web/views/list/list_renderer";
import {StatusBarField} from "@web/views/fields/statusbar/statusbar_field";
import {Field} from "@web/views/fields/field";
@@ -150,6 +151,37 @@ patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
}
})
// 根据进度条设置水印
const statusbar_params = {
'已完工': 'bg-primary',
'完成': 'bg-primary',
'采购订单': 'bg-primary',
'作废': 'bg-danger',
'封存(报废)': 'bg-danger',
}
patch(StatusBarField.prototype, 'jikimo_frontend.StatusBarField', {
setup() {
owl.onMounted(this.ribbons);
return this._super(...arguments);
},
ribbons() {
try {
const dom = $('.o_form_sheet.position-relative')
const status = statusbar_params[this.currentName]
if(status && dom.length) {
dom.prepend(`<div class="o_widget o_widget_web_ribbon">
<div class="ribbon ribbon-top-right">
<span class="bg-opacity-75 ${status}" title="">${this.currentName}</span>
</div>
</div>`)
}
} catch (e) {
console.log(e)
}
}
})
$(function () {
document.addEventListener('click', function () {
const dom = $('.o_form_status_indicator_buttons ')

View File

@@ -490,5 +490,17 @@ div:has(.o_required_modifier) > label::before {
transform: translateY(-50%);
}
.o_list_renderer .o_list_table tfoot .o_list_number {
text-align: left;
text-align: left !important;
}
.o_field_widget.o_readonly_modifier.o_required_modifier.o_field_many2one[name=production_id] .o_form_uri {
font-weight: bold;
font-size: 20px;
}
// 修改 【批量排程】【配送】 按钮UI
.btn-secondary {
color: #fff;
background-color: #4A4F59;
border-color: #4A4F59;
}

View File

@@ -4,7 +4,8 @@ csv_internal_sep = ,
data_dir = /var/lib/odoo
db_host = 172.17.0.2
db_maxconn = 64
db_name = sf_t2cs_003
# db_name = sf_t_0430
db_name = t_240509
db_password = sf
db_port = 5432
db_sslmode = prefer

27
sf_base/commons/Email.py Normal file
View File

@@ -0,0 +1,27 @@
from odoo import api, models
class MailSender(models.Model):
_name = 'mail.sender'
_description = 'Mail Sender'
def mail_sender(self, mail_server_name, subject_name, email_to, error_message):
mail_server = self.env['ir.mail_server'].sudo().search([('name', '=', mail_server_name)], limit=1)
if not mail_server:
_logger.info('管理员还未配置名称为 %s 的smtp服务器信息请联系管理员配置' % mail_server_name)
return
if not mail_server.smtp_user or not mail_server.smtp_pass:
_logger.info("邮件发送账号未正确设置,请联系管理员!")
return
mail_values = {
'message_type': 'email', 'subject': subject_name,
'mail_server_id': mail_server.id, 'email_from': mail_server.smtp_user,
'email_to': email_to
}
temp_mail_html = """
Hi, <p> 这里是异常提醒通知: </p>
"""
temp_mail_html += """ %s """ % error_message
mail_values['body_html'] = temp_mail_html
self.env['mail.mail'].sudo().create(mail_values).send(auto_commit=True)

View File

@@ -1,2 +1,3 @@
from . import common
from . import Printer
from . import Email

View File

@@ -8,7 +8,7 @@ from odoo.http import request
class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/MachineToolGroup', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/MachineToolGroup', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
def get_maintenance_tool_groups_Info(self, **kw):
"""

View File

@@ -329,7 +329,7 @@ class ToolInventory(models.Model):
blade_number = fields.Integer('刃数(个)')
extension = fields.Float('伸出长度(mm)')
work_material = fields.Selection([('', ''), ('', '')], string='加工材料')
life_span = fields.Float('寿命(h)')
life_span = fields.Float('寿命(min)')
tool_groups_id = fields.Many2one('sf.tool.groups', string='刀具组')

View File

@@ -84,6 +84,17 @@
<field name="category_id" ref="base.module_category_inventory_purchase"/>
</record>
<record id="group_sf_stock_user" model="res.groups">
<field name="name">仓储岗</field>
<field name="category_id" ref="base.module_category_inventory_inventory"/>
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
</record>
<record id="group_sf_stock_manager" model="res.groups">
<field name="name">仓储总监</field>
<field name="category_id" ref="base.module_category_inventory_inventory"/>
<field name="implied_ids" eval="[(4, ref('group_sf_stock_user'))]"/>
</record>
</data>
</odoo>

View File

@@ -11,7 +11,8 @@ access_sf_machine_control_system,sf_machine_control_system,model_sf_machine_cont
access_sf_machine_control_system_admin,sf_machine_control_system_admin,model_sf_machine_control_system,base.group_system,1,1,1,0
access_sf_production_process_group_sale_director,sf_production_process_group_sale_director,model_sf_production_process,sf_base.group_sale_director,1,0,0,0
access_sf_production_process_group_sale_salemanager,sf_production_process_group_sale_salemanager,model_sf_production_process,sf_base.group_sale_salemanager,1,0,0,0
access_res_partner_category_group_sale_salemanager,res_partner_category_group_sale_salemanager,base.model_res_partner_category,sf_base.group_sale_salemanager,1,0,1,0
access_res_partner_category_group_sale_director,res_partner_category_group_sale_director,base.model_res_partner_category,sf_base.group_sale_director,1,0,1,0
access_sf_production_process,sf_production_process,model_sf_production_process,base.group_user,1,1,1,0
access_sf_production_process_admin,sf_production_process_admin,model_sf_production_process,base.group_system,1,1,1,0
access_sf_production_materials,sf_production_materials,model_sf_production_materials,base.group_user,1,1,1,0
@@ -48,6 +49,7 @@ access_sf_functional_cutting_tool_admin,sf_functional_cutting_tool_admin,model_s
access_sf_functional_cutting_tool_model,sf_functional_cutting_tool_model,model_sf_functional_cutting_tool_model,base.group_user,1,1,1,0
access_sf_functional_cutting_tool_model_admin,sf_functional_cutting_tool_model_admin,model_sf_functional_cutting_tool_model,base.group_system,1,1,1,0
access_sf_fixture_material,sf_fixture_material,model_sf_fixture_material,base.group_user,1,1,1,0
access_sf_fixture_material_group_sf_stock_user,sf_fixture_material_group_sf_stock_user,model_sf_fixture_material,sf_base.group_sf_stock_user,1,1,1,0
access_sf_fixture_material_admin,sf_fixture_material_admin,model_sf_fixture_material,base.group_system,1,1,1,0
access_sf_fixture_materials_basic_parameters,sf_fixture_materials_basic_parameters,model_sf_fixture_materials_basic_parameters,base.group_user,1,1,1,0
access_sf_fixture_materials_basic_parameters_admin,sf_fixture_materials_basic_parameters_admin,model_sf_fixture_materials_basic_parameters,base.group_system,1,1,1,0
@@ -227,3 +229,16 @@ access_sf_fixture_model_group_sale_director,sf_fixture_model_group_sale_director
access_sf_fixture_materials_basic_parameters_group_sale_director,sf_fixture_materials_basic_parameters_group_sale_director,model_sf_fixture_materials_basic_parameters,sf_base.group_sale_director,1,0,0,0
access_sf_machine_tool_type_group_sale_director,sf_machine_tool_type_group_sale_director,model_sf_machine_tool_type,sf_base.group_sale_director,1,0,0,0
access_sf_fixture_material_group_plan_director,sf_fixture_material_group_plan_director,model_sf_fixture_material,sf_base.group_plan_director,1,0,0,0
access_sf_fixture_model_group_plan_director,sf_fixture_model_group_plan_director,model_sf_fixture_model,sf_base.group_plan_director,1,0,0,0
access_sf_fixture_materials_basic_parameters_group_plan_director,sf_fixture_materials_basic_parameters_group_plan_director,model_sf_fixture_materials_basic_parameters,sf_base.group_plan_director,1,0,0,0
access_sf_multi_mounting_type_group_plan_director,sf_multi_mounting_type_group_plan_director,model_sf_multi_mounting_type,sf_base.group_plan_director,1,0,0,0
access_sf_production_materials_group_sf_stock_manager,sf_production_materials_group_sf_stock_manager,model_sf_production_materials,sf_base.group_sf_stock_manager,1,0,0,0
access_sf_materials_model_group_sf_stock_manager,sf_materials_model_group_sf_stock_manager,model_sf_materials_model,sf_base.group_sf_stock_manager,1,0,0,0
access_sf_fixture_material_group_sf_stock_manager,sf_fixture_material_group_sf_stock_manager,model_sf_fixture_material,sf_base.group_sf_stock_manager,1,0,0,0
access_sf_fixture_model_group_sf_stock_manager,sf_fixture_model_group_sf_stock_manager,model_sf_fixture_model,sf_base.group_sf_stock_manager,1,0,0,0
access_sf_fixture_materials_basic_parameters_group_sf_stock_manager,sf_fixture_materials_basic_parameters_group_sf_stock_manager,model_sf_fixture_materials_basic_parameters,sf_base.group_sf_stock_manager,1,0,0,0
access_sf_multi_mounting_type_group_sf_stock_manager,sf_multi_mounting_type_group_sf_stock_manager,model_sf_multi_mounting_type,sf_base.group_sf_stock_manager,1,0,0,0
access_sf_machine_brand_group_sf_stock_manager,sf_machine_brand_group_sf_stock_manager,model_sf_machine_brand,sf_base.group_sf_stock_manager,1,0,0,0
access_sf_cutting_tool_type_group_sf_stock_manager,sf_cutting_tool_type_group_sf_stock_manager,model_sf_cutting_tool_type,sf_base.group_sf_stock_manager,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
11 access_sf_machine_control_system_admin sf_machine_control_system_admin model_sf_machine_control_system base.group_system 1 1 1 0
12 access_sf_production_process_group_sale_director sf_production_process_group_sale_director model_sf_production_process sf_base.group_sale_director 1 0 0 0
13 access_sf_production_process_group_sale_salemanager sf_production_process_group_sale_salemanager model_sf_production_process sf_base.group_sale_salemanager 1 0 0 0
14 access_sf_production_process access_res_partner_category_group_sale_salemanager sf_production_process res_partner_category_group_sale_salemanager model_sf_production_process base.model_res_partner_category base.group_user sf_base.group_sale_salemanager 1 1 0 1 0
15 access_res_partner_category_group_sale_director res_partner_category_group_sale_director base.model_res_partner_category sf_base.group_sale_director 1 0 1 0
16 access_sf_production_process_admin access_sf_production_process sf_production_process_admin sf_production_process model_sf_production_process base.group_system base.group_user 1 1 1 0
17 access_sf_production_materials access_sf_production_process_admin sf_production_materials sf_production_process_admin model_sf_production_materials model_sf_production_process base.group_user base.group_system 1 1 1 0
18 access_sf_production_materials_group_sale_director access_sf_production_materials sf_production_materials_group_sale_director sf_production_materials model_sf_production_materials sf_base.group_sale_director base.group_user 1 0 1 0 1 0
49 access_sf_fixture_materials_basic_parameters access_sf_fixture_material_group_sf_stock_user sf_fixture_materials_basic_parameters sf_fixture_material_group_sf_stock_user model_sf_fixture_materials_basic_parameters model_sf_fixture_material base.group_user sf_base.group_sf_stock_user 1 1 1 0
50 access_sf_fixture_materials_basic_parameters_admin access_sf_fixture_material_admin sf_fixture_materials_basic_parameters_admin sf_fixture_material_admin model_sf_fixture_materials_basic_parameters model_sf_fixture_material base.group_system 1 1 1 0
51 access_sf_multi_mounting_type access_sf_fixture_materials_basic_parameters sf_multi_mounting_type sf_fixture_materials_basic_parameters model_sf_multi_mounting_type model_sf_fixture_materials_basic_parameters base.group_user 1 1 1 0
52 access_sf_fixture_materials_basic_parameters_admin sf_fixture_materials_basic_parameters_admin model_sf_fixture_materials_basic_parameters base.group_system 1 1 1 0
53 access_sf_multi_mounting_type_admin access_sf_multi_mounting_type sf_multi_mounting_type_admin sf_multi_mounting_type model_sf_multi_mounting_type base.group_system base.group_user 1 1 1 0
54 access_sf_fixture_model access_sf_multi_mounting_type_admin sf_fixture_model sf_multi_mounting_type_admin model_sf_fixture_model model_sf_multi_mounting_type base.group_user base.group_system 1 1 1 0
55 access_sf_fixture_model_admin access_sf_fixture_model sf_fixture_model_admin sf_fixture_model model_sf_fixture_model base.group_system base.group_user 1 1 1 0
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244

View File

@@ -33,7 +33,7 @@
</group>
<notebook>
<page string="适用材料">
<field name="materials_model_ids" readonly="1"></field>
<field name="materials_model_ids"></field>
</page>
</notebook>
</sheet>

View File

@@ -52,7 +52,7 @@ class Sf_Bf_Connect(http.Controller):
self_machining_embryo = request.env['product.template'].sudo().no_bom_product_create(
self_machining_id,
item,
order_id, 'self_machining', i)
order_id, 'self_machining', i, product)
# 创建坯料的bom
self_machining_bom = request.env['mrp.bom'].with_user(
request.env.ref("base.user_admin")).bom_create(
@@ -78,7 +78,7 @@ class Sf_Bf_Connect(http.Controller):
item,
order_id,
'subcontract',
i)
i, product)
if outsource_embryo == -3:
res['status'] = -3
res['message'] = '该订单模型的材料型号在您分配的工厂里暂未设置获取方式和供应商,请先配置再进行分配'
@@ -105,17 +105,19 @@ class Sf_Bf_Connect(http.Controller):
purchase_embryo = request.env['product.template'].sudo().no_bom_product_create(purchase_id,
item,
order_id,
'purchase', i)
'purchase', i,
product)
if purchase_embryo == -3:
res['status'] = -3
res['message'] = '该订单模型的材料型号在您分配的工厂里暂未设置获取方式和供应商,请先配置再进行分配'
request.cr.rollback()
return json.JSONEncoder().encode(res)
# 产品配置bom
product_bom_purchase = request.env['mrp.bom'].with_user(
request.env.ref("base.user_admin")).bom_create(product, 'normal', False)
product_bom_purchase.with_user(request.env.ref("base.user_admin")).bom_create_line_has(
purchase_embryo)
else:
# 产品配置bom
product_bom_purchase = request.env['mrp.bom'].with_user(
request.env.ref("base.user_admin")).bom_create(product, 'normal', False)
product_bom_purchase.with_user(request.env.ref("base.user_admin")).bom_create_line_has(
purchase_embryo)
order_id.with_user(request.env.ref("base.user_admin")).sale_order_create_line(product, item)
i += 1
res['factory_order_no'] = order_id.name
@@ -165,5 +167,5 @@ class jdElcp(http.Controller):
return json.dumps({'statusCode': 415, 'statusMessage': '订单号不能为空'}, ensure_ascii=False)
aa = request.env['sale.order'].sudo().search([('name', '=', kw['orderNo'])])
if aa:
aa.schedule_status = 'received'
# aa.schedule_status = 'received'
return json.dumps({'statusCode': 200, 'statusMessage': '修改成功'}, ensure_ascii=False)

View File

@@ -56,7 +56,7 @@ class JdEclp(models.Model):
"""
res = super(JdEclp, self).button_validate()
if self.check_out == 'OUT':
if self.logistics_way != '自提':
if self.logistics_way != '自提' and self.logistics_way:
if self.logistics_status != '2':
raise ValidationError('非自提订单,必须先下物流单,并获取物流面单后才可出库!')
return res
@@ -164,8 +164,8 @@ class JdEclp(models.Model):
self.is_bill = True
self.logistics_status = '1'
# 京东物流下单后,销售订单状态改为待收货
self.env['sale.order'].search([('name', '=', self.origin)]).write({'scheduled_status': 'to receive'})
# # 京东物流下单后,销售订单状态改为待收货
# self.env['sale.order'].search([('name', '=', self.origin)]).write({'scheduled_status': 'to receive'})
# else:
# raise UserError("选择京东物流才能下单呦")

View File

@@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-
from odoo import models, fields
import logging
import re
from odoo import models, fields, api
class ResProductCategory(models.Model):
@@ -28,6 +31,20 @@ class ResMrpBomMo(models.Model):
_inherit = 'mrp.bom'
subcontractor_id = fields.Many2one('res.partner', string='外包商')
subcontractor_name = fields.Char('', compute='_compute_subcontractor_ids', store=True)
@api.depends('subcontractor_id')
def _compute_subcontractor_ids(self):
for item in self:
if item.subcontractor_id:
logging.info("subcontractor_ids: %s" % item.subcontractor_ids.ids)
subcontractor_id = int(re.sub(r"\D", "", str(item.subcontractor_id.id)))
item.subcontractor_ids = [subcontractor_id]
item.subcontractor_name = item.subcontractor_id.name
logging.info("subcontractor_ids: %s" % item.subcontractor_ids.ids)
else:
item.subcontractor_ids = []
item.subcontractor_name = ''
def bom_create_line_has(self, embryo):
vals = {
@@ -37,7 +54,7 @@ class ResMrpBomMo(models.Model):
'product_qty': 1,
'product_uom_id': 1
}
return self.env['mrp.bom.line'].create(vals)
return self.env['mrp.bom.line'].sudo().create(vals)
# 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品后再次进行创建bom
def bom_create(self, product, bom_type, product_type):
@@ -64,7 +81,7 @@ class ResMrpBomMo(models.Model):
qty = 1
if round(embryo.volume * raw_bom_line.materials_type_id.density / 1000000) > 1:
qty = round(embryo.volume * raw_bom_line.materials_type_id.density / 1000000)
bom_line = self.env['mrp.bom.line'].create({
bom_line = self.env['mrp.bom.line'].sudo().create({
'bom_id': self.id,
'product_id': raw_bom_line.id,
'product_tmpl_id': raw_bom_line.product_tmpl_id.id,

View File

@@ -0,0 +1,292 @@
from odoo import models, fields
import logging
import base64
class ResProductTemplate(models.Model):
_inherit = 'product.template'
# 模型的长,宽,高,体积,精度,材料
model_name = fields.Char('模型名称')
categ_type = fields.Selection(
[("成品", "成品"), ("胚料", "胚料"), ("原材料", "原材料")], string='产品的类别', related='categ_id.type', store=True)
model_long = fields.Float('模型长[mm]', digits=(16, 3))
model_width = fields.Float('模型宽[mm]', digits=(16, 3))
model_height = fields.Float('模型高[mm]', digits=(16, 3))
model_volume = fields.Float('模型体积[m³]')
model_machining_precision = fields.Selection([
('0.10', '±0.10mm'),
('0.05', '±0.05mm'),
('0.03', '±0.03mm'),
('0.02', '±0.02mm'),
('0.01', '±0.01mm')], string='加工精度')
product_model_type_id = fields.Many2one('sf.model.type', string='产品模型类型')
embryo_model_type_id = fields.Many2one('sf.model.type', string='胚料模型类型')
model_processing_panel = fields.Char('模型加工面板')
model_surface_process_id = fields.Many2one('sf.production.process', string='表面工艺')
model_process_parameters_id = fields.Many2one('sf.processing.technology', string='工艺参数')
# model_price = fields.Float('模型单价', digits=(16, 3))
model_remark = fields.Char('模型备注说明')
length = fields.Float('长[mm]', digits=(16, 3))
width = fields.Float('宽[mm]', digits=(16, 3))
height = fields.Float('高[mm]', digits=(16, 3))
materials_id = fields.Many2one('sf.production.materials', string='材料')
materials_type_id = fields.Many2one('sf.materials.model', string='材料型号')
single_manufacturing = fields.Boolean(string="单个制造")
upload_model_file = fields.Many2many('ir.attachment', 'upload_model_file_attachment_ref', string='上传模型文件')
model_code = fields.Char('模型编码')
is_bfm = fields.Boolean('业务平台是否自动创建', default=False)
def _get_volume_uom_id_from_ir_config_parameter(self):
product_length_in_feet_param = self.env['ir.config_parameter'].sudo().get_param('product.volume_in_cubic_feet')
if product_length_in_feet_param == '1':
return self.env.ref('uom.product_uom_cubic_foot')
else:
return self.env.ref('sf_dlm.product_uom_cubic_millimeter')
# model_file = fields.Binary('模型文件')
# 胚料的库存路线设置
# def _get_routes(self, route_type):
# route_manufacture = self.env.ref('mrp.route_warehouse0_manufacture', raise_if_not_found=False).sudo()
# route_mto = self.env.ref('stock.route_warehouse0_mto', raise_if_not_found=False).sudo()
# route_purchase = self.env.ref('purchase_stock.route_warehouse0_buy', raise_if_not_found=False).sudo()
# if route_manufacture and route_mto:
# # 外协
# if route_type == 'subcontract':
# route_subcontract = self.env.ref('mrp_subcontracting.route_resupply_subcontractor_mto',
# raise_if_not_found=False).sudo()
# return [route_mto.id, route_purchase.id, route_subcontract.id]
# elif route_type == 'purchase':
# # 采购
# return [route_mto.id, route_purchase.id]
# else:
# return [route_mto.id, route_manufacture.id]
# return []
# route_ids = fields.Many2many(default=lambda self: self._get_route())
# 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品
def product_create(self, product_id, item, order_id, order_number, i):
copy_product_id = product_id.with_user(self.env.ref("base.user_admin")).copy()
copy_product_id.product_tmpl_id.active = True
model_type = self.env['sf.model.type'].search([], limit=1)
attachment = self.attachment_create(item['model_name'], item['model_data'])
vals = {
'name': '%s-%s-%s' % ('P', order_id.name, i),
'model_long': item['model_long'] + model_type.embryo_tolerance,
'model_width': item['model_width'] + model_type.embryo_tolerance,
'model_height': item['model_height'] + model_type.embryo_tolerance,
'model_volume': (item['model_long'] + model_type.embryo_tolerance) * (
item['model_width'] + model_type.embryo_tolerance) * (
item['model_height'] + model_type.embryo_tolerance),
'product_model_type_id': model_type.id,
'model_processing_panel': 'R',
'model_machining_precision': item['model_machining_precision'],
'model_code': item['barcode'],
'length': item['model_long'],
'width': item['model_width'],
'height': item['model_height'],
'volume': item['model_long'] * item['model_width'] * item['model_height'],
'model_file': '' if not item['model_file'] else base64.b64decode(item['model_file']),
'model_name': attachment.name,
'upload_model_file': [(6, 0, [attachment.id])],
# 'single_manufacturing': True,
# 'tracking': 'serial',
'list_price': item['price'],
# 'categ_id': self.env.ref('sf_dlm.product_category_finished_sf').id,
'materials_id': self.env['sf.production.materials'].search(
[('materials_no', '=', item['texture_code'])]).id,
'materials_type_id': self.env['sf.materials.model'].search(
[('materials_no', '=', item['texture_type_code'])]).id,
'model_surface_process_id': self.env['sf.production.process'].search(
[('process_encode', '=', item['surface_process_code'])]).id,
# 'model_process_parameters_id': self.env['sf.processing.technology'].search(
# [('process_encode', '=', item['process_parameters_code'])]).id,
'model_remark': item['remark'],
'default_code': '%s-%s' % (order_number, i),
# 'barcode': item['barcode'],
'active': True,
# 'route_ids': self._get_routes('')
}
copy_product_id.sudo().write(vals)
# product_id.product_tmpl_id.active = False
return copy_product_id
def attachment_create(self, name, data):
attachment = self.env['ir.attachment'].create({
'datas': base64.b64decode(data),
'type': 'binary',
'public': True,
'description': '模型文件',
'name': name
})
return attachment
# 创建胚料
def no_bom_product_create(self, product_id, item, order_id, route_type, i):
no_bom_copy_product_id = product_id.with_user(self.env.ref("base.user_admin")).copy()
no_bom_copy_product_id.product_tmpl_id.active = True
materials_id = self.env['sf.production.materials'].search(
[('materials_no', '=', item['texture_code'])])
materials_type_id = self.env['sf.materials.model'].search(
[('materials_no', '=', item['texture_type_code'])])
model_type = self.env['sf.model.type'].search([], limit=1)
supplier = self.env['mrp.bom'].get_supplier(materials_type_id)
logging.info('no_bom_copy_product_supplier-vals:%s' % supplier)
vals = {
'name': '%s-%s-%s [%s %s-%s * %s * %s]' % ('R',
order_id.name, i, materials_id.name, materials_type_id.name,
item['model_long'] + model_type.embryo_tolerance,
item['model_width'] + model_type.embryo_tolerance,
item['model_height'] + model_type.embryo_tolerance),
'length': item['model_long'] + model_type.embryo_tolerance,
'width': item['model_width'] + model_type.embryo_tolerance,
'height': item['model_height'] + model_type.embryo_tolerance,
'volume': (item['model_long'] + model_type.embryo_tolerance) * (
item['model_width'] + model_type.embryo_tolerance) * (
item['model_height'] + model_type.embryo_tolerance),
'embryo_model_type_id': model_type.id,
'list_price': item['price'],
'materials_id': materials_id.id,
'materials_type_id': materials_type_id.id,
'is_bfm': True,
# 'route_ids': self._get_routes(route_type),
# 'categ_id': self.env.ref('sf_dlm.product_category_embryo_sf').id,
# 'model_surface_process_id': self.env['sf.production.process'].search(
# [('process_encode', '=', item['surface_process_code'])]).id,
# 'model_process_parameters_id': self.env['sf.processing.technology'].search(
# [('process_encode', '=', item['process_parameters_code'])]).id,
'active': True
}
# 外协和采购生成的胚料需要根据材料型号绑定供应商
if route_type == 'subcontract' or route_type == 'purchase':
no_bom_copy_product_id.purchase_ok = True
no_bom_copy_product_id.seller_ids = [
(0, 0, {'partner_id': supplier.partner_id.id, 'delay': 1.0})]
if route_type == 'subcontract':
partner = self.env['res.partner'].search([('id', '=', supplier.partner_id.id)])
partner.is_subcontractor = True
no_bom_copy_product_id.write(vals)
logging.info('no_bom_copy_product_id-vals:%s' % vals)
# product_id.product_tmpl_id.active = False
return no_bom_copy_product_id
# @api.onchange('upload_model_file')
# def onchange_model_file(self):
# for item in self:
# if len(item.upload_model_file) > 1:
# raise ValidationError('只允许上传一个文件')
# if item.upload_model_file:
# file_attachment_id = item.upload_model_file[0]
# item.model_name = file_attachment_id.name
# # 附件路径
# report_path = file_attachment_id._full_path(file_attachment_id.store_fname)
# shapes = read_step_file(report_path)
# output_file = get_resource_path('sf_dlm', 'static/file', 'out.stl')
# write_stl_file(shapes, output_file, 'binary', 0.03, 0.5)
# # 转化为glb
# output_glb_file = get_resource_path('sf_dlm', 'static/file', 'out.glb')
# util_path = get_resource_path('sf_dlm', 'static/util')
# cmd = 'python %s/stl2gltf.py %s %s -b' % (util_path, output_file, output_glb_file)
# os.system(cmd)
# # 转base64
# with open(output_glb_file, 'rb') as fileObj:
# image_data = fileObj.read()
# base64_data = base64.b64encode(image_data)
# item.model_file = base64_data
class ResMrpBom(models.Model):
_inherit = 'mrp.bom'
subcontractor_id = fields.Many2one('res.partner', string='外包商')
def bom_create_line_has(self, embryo):
vals = {
'bom_id': self.id,
'product_id': embryo.id,
'product_tmpl_id': embryo.product_tmpl_id.id,
'product_qty': 1,
'product_uom_id': 1
}
return self.env['mrp.bom.line'].create(vals)
# 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品后再次进行创建bom
def bom_create(self, product, bom_type, product_type):
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
})
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
# 胚料BOM组件选取当前胚料原材料
# 然后根据当前的胚料的体积得出需要的原材料重量立方米m³ *材料密度 * 1000 = 所需原材料重量KG公斤
# 胚料所需原材料公式当前的胚料的体积立方米m³ *材料密度 * 1000 = 所需原材料重量KG公斤
def bom_create_line(self, embryo):
# 选取当前胚料原材料
raw_bom_line = self.get_raw_bom(embryo)
if raw_bom_line:
bom_line = self.env['mrp.bom.line'].create({
'bom_id': self.id,
'product_id': raw_bom_line.id,
'product_tmpl_id': raw_bom_line.product_tmpl_id.id,
'product_qty': round(embryo.volume * raw_bom_line.materials_type_id.density / 1000000),
'product_uom_id': raw_bom_line.uom_id.id,
})
return bom_line
else:
return False
# 查询材料型号默认排第一的供应商
def get_supplier(self, materials_type):
seller_id = self.env['sf.supplier.sort'].search(
[('materials_model_id', '=', materials_type.id)],
limit=1,
order='sequence asc')
return seller_id
# 匹配bom
def get_bom(self, product):
embryo_has = self.env['product.product'].search(
[('categ_id.type', '=', '胚料'), ('materials_type_id', '=', product.materials_type_id.id),
('length', '>', product.length), ('width', '>', product.width),
('height', '>', product.height), ('is_bfm', '=', False)
],
limit=1,
order='volume desc'
)
logging.info('get_bom-vals:%s' % embryo_has)
if embryo_has:
rate_of_waste = ((embryo_has.volume - product.model_volume) % embryo_has.volume) * 100
if rate_of_waste <= 20:
return embryo_has
else:
return
# 查bom的原材料
def get_raw_bom(self, product):
raw_bom = self.env['product.product'].search(
[('categ_id.type', '=', '原材料'), ('materials_type_id', '=', product.materials_type_id.id)])
return raw_bom
class ResProductCategory(models.Model):
_inherit = "product.category"
type = fields.Selection(
[("成品", "成品"), ("胚料", "胚料"), ("原材料", "原材料")],
default="", string="类型")
# @api.constrains('type')
# def _check_type(self):
# category = self.env['product.category'].search(
# [('type', '=', self.type)])
# if category:
# raise ValidationError("该类别已存在,请选择其他类别")

View File

@@ -0,0 +1,13 @@
from odoo import api, fields, models
class ResMrpWorkOrder(models.Model):
_inherit = 'mrp.workorder'
_order = 'sequence'
product_tmpl_id_length = fields.Float(related='production_id.product_tmpl_id.length', readonly=True, store=True, check_company=True, string="胚料长度(mm)")
product_tmpl_id_width = fields.Float(related='production_id.product_tmpl_id.width', readonly=True, store=True, check_company=True, string="胚料宽度(mm)")
product_tmpl_id_height = fields.Float(related='production_id.product_tmpl_id.height', readonly=True, store=True, check_company=True, string="胚料高度(mm)")
product_tmpl_id_materials_id = fields.Many2one(related='production_id.product_tmpl_id.materials_id', readonly=True, store=True, check_company=True, string="材料")
product_tmpl_id_materials_type_id = fields.Many2one(related='production_id.product_tmpl_id.materials_type_id', readonly=True, store=True, check_company=True, string="型号")

BIN
sf_dlm/static/file/out.glb Normal file

Binary file not shown.

BIN
sf_dlm/static/file/out.stl Normal file

Binary file not shown.

View File

@@ -0,0 +1,277 @@
import os
def stl_to_gltf(binary_stl_path, out_path, is_binary):
import struct
gltf2 = '''
{
"scenes" : [
{
"nodes" : [ 0 ]
}
],
"nodes" : [
{
"mesh" : 0
}
],
"meshes" : [
{
"primitives" : [ {
"attributes" : {
"POSITION" : 1
},
"indices" : 0
} ]
}
],
"buffers" : [
{
%s
"byteLength" : %d
}
],
"bufferViews" : [
{
"buffer" : 0,
"byteOffset" : 0,
"byteLength" : %d,
"target" : 34963
},
{
"buffer" : 0,
"byteOffset" : %d,
"byteLength" : %d,
"target" : 34962
}
],
"accessors" : [
{
"bufferView" : 0,
"byteOffset" : 0,
"componentType" : 5125,
"count" : %d,
"type" : "SCALAR",
"max" : [ %d ],
"min" : [ 0 ]
},
{
"bufferView" : 1,
"byteOffset" : 0,
"componentType" : 5126,
"count" : %d,
"type" : "VEC3",
"min" : [%f, %f, %f],
"max" : [%f, %f, %f]
}
],
"asset" : {
"version" : "2.0"
}
}
'''
header_bytes = 80
unsigned_long_int_bytes = 4
float_bytes = 4
vec3_bytes = 4 * 3
spacer_bytes = 2
num_vertices_in_face = 3
vertices = {}
indices = []
if not is_binary:
out_bin = os.path.join(out_path, "out.bin")
out_gltf = os.path.join(out_path, "out.gltf")
else:
out_bin = out_path
unpack_face = struct.Struct("<12fH").unpack
face_bytes = float_bytes*12 + 2
with open(path_to_stl, "rb") as f:
f.seek(header_bytes) # skip 80 bytes headers
num_faces_bytes = f.read(unsigned_long_int_bytes)
number_faces = struct.unpack("<I", num_faces_bytes)[0]
# the vec3_bytes is for normal
stl_assume_bytes = header_bytes + unsigned_long_int_bytes + number_faces * (vec3_bytes*3 + spacer_bytes + vec3_bytes)
assert stl_assume_bytes == os.path.getsize(path_to_stl), "stl is not binary or ill formatted"
minx, maxx = [9999999, -9999999]
miny, maxy = [9999999, -9999999]
minz, maxz = [9999999, -9999999]
vertices_length_counter = 0
data = struct.unpack("<" + "12fH"*number_faces, f.read())
len_data = len(data)
for i in range(0, len_data, 13):
for j in range(3, 12, 3):
x, y, z = data[i+j:i+j+3]
x = int(x*100000)/100000
y = int(y*100000)/100000
z = int(z*100000)/100000
tuple_xyz = (x, y, z);
try:
indices.append(vertices[tuple_xyz])
except KeyError:
vertices[tuple_xyz] = vertices_length_counter
vertices_length_counter += 1
indices.append(vertices[tuple_xyz])
if x < minx: minx = x
if x > maxx: maxx = x
if y < miny: miny = y
if y > maxy: maxy = y
if z < minz: minz = z
if z > maxz: maxz = z
# f.seek(spacer_bytes, 1) # skip the spacer
number_vertices = len(vertices)
vertices_bytelength = number_vertices * vec3_bytes # each vec3 has 3 floats, each float is 4 bytes
unpadded_indices_bytelength = number_vertices * unsigned_long_int_bytes
out_number_vertices = len(vertices)
out_number_indices = len(indices)
unpadded_indices_bytelength = out_number_indices * unsigned_long_int_bytes
indices_bytelength = (unpadded_indices_bytelength + 3) & ~3
out_bin_bytelength = vertices_bytelength + indices_bytelength
if is_binary:
out_bin_uir = ""
else:
out_bin_uir = '"uri": "out.bin",'
gltf2 = gltf2 % ( out_bin_uir,
#buffer
out_bin_bytelength,
# bufferViews[0]
indices_bytelength,
# bufferViews[1]
indices_bytelength,
vertices_bytelength,
# accessors[0]
out_number_indices,
out_number_vertices - 1,
# accessors[1]
out_number_vertices,
minx, miny, minz,
maxx, maxy, maxz
)
glb_out = bytearray()
if is_binary:
gltf2 = gltf2.replace(" ", "")
gltf2 = gltf2.replace("\n", "")
scene = bytearray(gltf2.encode())
scene_len = len(scene)
padded_scene_len = (scene_len + 3) & ~3
body_offset = padded_scene_len + 12 + 8
file_len = body_offset + out_bin_bytelength + 8
# 12-byte header
glb_out.extend(struct.pack('<I', 0x46546C67)) # magic number for glTF
glb_out.extend(struct.pack('<I', 2))
glb_out.extend(struct.pack('<I', file_len))
# chunk 0
glb_out.extend(struct.pack('<I', padded_scene_len))
glb_out.extend(struct.pack('<I', 0x4E4F534A)) # magic number for JSON
glb_out.extend(scene)
while len(glb_out) < body_offset:
glb_out.extend(b' ')
# chunk 1
glb_out.extend(struct.pack('<I', out_bin_bytelength))
glb_out.extend(struct.pack('<I', 0x004E4942)) # magin number for BIN
# print('<%dI' % len(indices))
# print(struct.pack('<%dI' % len(indices), *indices))
glb_out.extend(struct.pack('<%dI' % len(indices), *indices))
for i in range(indices_bytelength - unpadded_indices_bytelength):
glb_out.extend(b' ')
vertices = dict((v, k) for k,v in vertices.items())
# glb_out.extend(struct.pack('f',
# print([each_v for vertices[v_counter] for v_counter in range(number_vertices)]) # magin number for BIN
vertices = [vertices[i] for i in range(number_vertices)]
flatten = lambda l: [item for sublist in l for item in sublist]
# for v_counter in :
# v_3f = vertices[v_counter]
# all_floats_in_vertices.append(v_3f[0])
# all_floats_in_vertices.append(v_3f[1])
# all_floats_in_vertices.append(v_3f[2])
# for v_counter in range(number_vertices):
glb_out.extend(struct.pack('%df' % number_vertices*3, *flatten(vertices))) # magin number for BIN
# for v_counter in range(number_vertices):
# glb_out.extend(struct.pack('3f', *vertices[v_counter])) # magin number for BIN
# for (v_x, v_y, v_z), _ in sorted(vertices.items(), key=lambda x: x[1]):
# glb_out.extend(struct.pack('3f', v_x, v_y, v_z)) # magin number for BIN
# # glb_out.extend(struct.pack('f', v_y)) # magin number for BIN
# # glb_out.extend(struct.pack('f', v_z)) # magin number for BIN
with open(out_bin, "wb") as out:
out.write(glb_out)
if not is_binary:
with open(out_gltf, "w") as out:
out.write(gltf2)
print("Done! Exported to %s" %out_path)
if __name__ == '__main__':
import sys
if len(sys.argv) < 3:
print("use it like python3 stl_to_gltf.py /path/to/stl /path/to/gltf/folder")
print("or python3 stl_to_gltf.py /path/to/stl /path/to/glb/file -b")
sys.exit(1)
path_to_stl = sys.argv[1]
out_path = sys.argv[2]
if len(sys.argv) > 3:
is_binary = True
else:
is_binary = False
if out_path.lower().endswith(".glb"):
print("Use binary mode since output file has glb extension")
is_binary = True
else:
if is_binary:
print("output file should have glb extension but not %s", out_path)
if not os.path.exists(path_to_stl):
print("stl file does not exists %s" % path_to_stl)
if not is_binary:
if not os.path.isdir(out_path):
os.mkdir(out_path)
stl_to_gltf(path_to_stl, out_path, is_binary)

View File

@@ -55,7 +55,7 @@
</page>
<page name="inventory" position="attributes">
<attribute name="groups">
stock.group_stock_user,product.group_stock_packaging,sf_base.group_sale_salemanager,sf_base.group_sale_director,sf_base.group_purchase,sf_base.group_purchase_director
stock.group_stock_user,product.group_stock_packaging,sf_base.group_sale_salemanager,sf_base.group_sale_director,sf_base.group_purchase,sf_base.group_purchase_director,sf_base.group_plan_dispatch
</attribute>
</page>
</field>
@@ -110,6 +110,9 @@
<field name="subcontractor_id"
attrs="{'invisible': [('type', '!=', 'subcontract')], 'required': [('type', '=', 'subcontract')]}"/>
</field>
<xpath expr="//group" position="after">
<field name="subcontractor_name" attrs="{'invisible': True}"/>
</xpath>
</field>
</record>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_mrp_production_workorder_tray_form_inherit_sf1" model="ir.ui.view">
<field name="name">production.workorder.dlm</field>
<field name="model">mrp.workorder</field>
<field name="inherit_id" ref="sf_manufacturing.view_mrp_production_workorder_tray_form_inherit_sf"/>
<field name="arch" type="xml">
<xpath expr="//page[1]" position="before">
<page string="开料要求" attrs='{"invisible": [("routing_type","!=","切割")]}'>
<group>
<group>
<field name="product_tmpl_id_materials_id" widget="many2one"/>
<field name="product_tmpl_id_materials_type_id" widget="many2one"/>
</group>
<group>
<field name="product_tmpl_id_length"/>
<field name="product_tmpl_id_width"/>
<field name="product_tmpl_id_height"/>
</group>
</group>
</page>
</xpath>
</field>
</record>
</odoo>

View File

@@ -32,6 +32,7 @@
attrs="{'invisible': [('categ_type', '!=', '刀具')],'required': [('categ_type', '=', '刀具')],'readonly': [('id', '!=', False)]}"
placeholder="请选择"/>
<field name="cutting_tool_model_id" placeholder="请选择" class="custom_required"
options="{'no_create': True}"
domain="[('cutting_tool_material_id','=',cutting_tool_material_id)]"
context="{'default_cutting_tool_material_id': cutting_tool_material_id}"
attrs="{'invisible': [('categ_type', '!=', '刀具')],'required': [('categ_type', '=', '刀具')],'readonly': [('id', '!=', False)]}">
@@ -39,6 +40,7 @@
<!-- <button name="%(sf_base.action_sf_cutting_tool_standard_library)d" string="新建" type="action"-->
<!-- context="{'default_cutting_tool_material_id': cutting_tool_material_id}"/>-->
<field name="specification_id" placeholder="请选择" class="custom_required"
options="{'no_create': True}"
context="{'default_cutting_tool_type': cutting_tool_type,'default_standard_library_id':cutting_tool_model_id}"
attrs="{'invisible': [('categ_type', '!=', '刀具')],'required': [('categ_type', '=', '刀具')],'readonly': [('id', '!=', False)]}"
domain="[('standard_library_id','=',cutting_tool_model_id)]"/>
@@ -436,7 +438,7 @@
<field name="cutting_speed_ids" string="" widget="one2many"
attrs="{'readonly': [('id', '!=', False)]}" class="fixTableCss">
<tree editable="bottom">
<field name="execution_standard_id"/>
<field name="execution_standard_id" options="{'no_create': True}"/>
<field name="material_code"/>
<field name="material_grade"/>
<field name="tensile_strength"/>
@@ -453,7 +455,7 @@
<tree editable="bottom">
<!-- <field name="cutting_speed"-->
<!-- attrs="{'readonly': [('materials_type_id','!=',False)]}"/>-->
<field name="materials_type_id" placeholder="请选择"/>
<field name="materials_type_id" options="{'no_create': True}" placeholder="请选择"/>
<field name="blade_diameter"/>
<field name="feed_per_tooth"/>
</tree>
@@ -464,8 +466,8 @@
<page string="夹具物料参数" attrs="{'invisible': [('categ_type', '!=', '夹具')]}">
<group>
<group>
<field name="brand_id" placeholder="请选择"/>
<field name="multi_mounting_type_id" placeholder="请选择"
<field name="brand_id" placeholder="请选择" options="{'no_create': True}"/>
<field name="multi_mounting_type_id" placeholder="请选择" options="{'no_create': True}"
attrs="{'required': [('categ_type', '=', '夹具')]}"/>
<field name="length" string="长度(mm)"/>
<field name="width" string="宽度(mm)"/>
@@ -553,14 +555,16 @@
<field name="boolean_chip_blowing_function" string="是否有吹屑功能"
attrs="{'invisible': [('fixture_material_type', 'not in', ['零点卡盘','零点托盘'])]}"/>
<field name="materials_model_id" placeholder="请选择"/>
<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>

View File

@@ -0,0 +1,82 @@
/** @odoo-module **/
import { browser } from "@web/core/browser/browser";
import { Dialog } from "@web/core/dialog/dialog";
import { _lt } from "@web/core/l10n/translation";
import { useChildRef, useOwnedDialogs, useService } from "@web/core/utils/hooks";
import { sprintf } from "@web/core/utils/strings";
import { isMobileOS } from "@web/core/browser/feature_detection";
import * as BarcodeScanner from "@web/webclient/barcode/barcode_scanner";
const {xml, Component} = owl;
import { standardFieldProps } from "@web/views/fields/standard_field_props";
// Import the registry
import {registry} from "@web/core/registry";
export class CodeField extends Component {
setup() {
super.setup();
}
async onBarcodeBtnClick() {
const barcode = await BarcodeScanner.scanBarcode();
if (barcode) {
await this.onBarcodeScanned(barcode);
if ("vibrate" in browser.navigator) {
browser.navigator.vibrate(100);
}
} else {
this.notification.add(this.env._t("Please, scan again !"), {
type: "warning",
});
}
}
async search(barcode) {
const results = await this.orm.call("sf.tray", "name_search", [code], {
name: barcode,
args: this.getDomain(),
operator: "ilike",
limit: 2, // If one result we set directly and if more than one we use normal flow so no need to search more
context: this.context,
});
return results.map((result) => {
const [id, displayName] = result;
return {
id,
name: displayName,
};
});
}
async onBarcodeScanned(barcode) {
const results = await this.search(barcode);
const records = results.filter((r) => !!r.id);
if (records.length === 1) {
this.update([{ id: records[0].id, name: records[0].name }]);
} else {
const searchInput = this.autocompleteContainerRef.el.querySelector("input");
searchInput.value = barcode;
searchInput.dispatchEvent(new Event("input"));
if (this.env.isSmall) {
searchInput.click();
}
}
}
}
CodeField.template = xml`
<button
t-on-click="onBarcodeBtnClick"
type="button"
class="btn ms-3 o_barcode"
tabindex="-1"
draggable="false"
aria-label="Scan barcode"
title="Scan barcode"
data-tooltip="Scan barcode"
/>
`;
// CodeField.template = 'sf_machine_connect.CodeField';
CodeField.props = standardFieldProps;
// Add the field to the correct category
registry.category("fields").add("code", CodeField);

View File

@@ -10,6 +10,7 @@
<searchpanel>
<field name="routing_type" select="multi" string="工序类型" icon="fa-building" enable_counters="1"/>
<field name="state" select="multi" string="状态" icon="fa-building" enable_counters="1"/>
<!-- <field name="manual_quotation" select="multi" string="" icon="fa-building" enable_counters="1"/>-->
</searchpanel>
<!-- <field name="name" filter_domain="['|', '|', ('明确的字段内容', 'ilike', self), ('shortdesc', 'ilike', self), ('name', 'ilike', self)]" string="Theme"/>-->
@@ -19,7 +20,11 @@
<!-- <xpath expr="//search//group//filter[@name='product']" position="before">-->
<!-- <filter string="Tray code" name="traycode" domain="[]" context="{'group_by': 'tray_code'}"/>-->
<!-- </xpath>-->
<xpath expr="//filter[@name='date_start_filter']" position="after">
<separator/>
<filter string="人工编程" name="manual_quotation" domain="[('manual_quotation', '=', True)]"/>
<filter string="自动编程" name="no_manual_quotation" domain="[('manual_quotation', '=', False)]"/>
</xpath>
</field>
</record>
</odoo>

View File

@@ -1,5 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_equipment_maintenance_standards_manager,equipment_maintenance_standards,model_equipment_maintenance_standards,sf_maintenance.sf_group_equipment_manager,1,1,1,0
access_equipment_maintenance_standards_manager,equipment_maintenance_standards,model_equipment_maintenance_standards,sf_maintenance.sf_group_equipment_manager,1,1,1,1
access_equipment_maintenance_standards,equipment_maintenance_standards,model_equipment_maintenance_standards,sf_maintenance.sf_group_equipment_user,1,0,0,0
access_sf_maintenance_logs_sf_group_equipment_user,sf_maintenance_logs,model_sf_maintenance_logs,sf_maintenance.sf_group_equipment_user,1,0,0,0
access_sf_maintenance_logs_sf_group_equipment_manager,sf_maintenance_logs,model_sf_maintenance_logs,sf_maintenance.sf_group_equipment_manager,1,1,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_equipment_maintenance_standards_manager equipment_maintenance_standards model_equipment_maintenance_standards sf_maintenance.sf_group_equipment_manager 1 1 1 0 1
3 access_equipment_maintenance_standards equipment_maintenance_standards model_equipment_maintenance_standards sf_maintenance.sf_group_equipment_user 1 0 0 0
4 access_sf_maintenance_logs_sf_group_equipment_user sf_maintenance_logs model_sf_maintenance_logs sf_maintenance.sf_group_equipment_user 1 0 0 0
5 access_sf_maintenance_logs_sf_group_equipment_manager sf_maintenance_logs model_sf_maintenance_logs sf_maintenance.sf_group_equipment_manager 1 1 1 0

View File

@@ -988,7 +988,7 @@
<xpath expr="//div[hasclass('o_row')][field[@name='maintenance_duration']]" position="after">
<field name='overhaul_id'/>
<field name='overhaul_id' options="{'no_create':True}"/>
<field name="overhaul_date" string="下次预防检修"/>
<label for="overhaul_period" string="预防检修频次"/>
<div class="o_row">

View File

@@ -13,6 +13,7 @@
'depends': ['sf_base', 'sf_maintenance', 'web_widget_model_viewer', 'sf_warehouse'],
'data': [
'data/stock_data.xml',
'data/empty_racks_data.xml',
'security/group_security.xml',
'security/ir.model.access.csv',
'wizard/workpiece_delivery_views.xml',

View File

@@ -8,7 +8,7 @@ from odoo.http import request
class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/GetWoInfo', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/GetWoInfo', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def get_Work_Info(self, **kw):
"""
@@ -21,6 +21,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True, 'Datas': []}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/GetWoInfo'})
logging.info('RfidCode:%s' % ret['RfidCode'])
if 'RfidCode' in ret:
workorder = request.env['mrp.workorder'].sudo().search([('rfid_code', '=', ret['RfidCode'])])
@@ -47,7 +49,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('get_Work_Info error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/GetShiftPlan', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/GetShiftPlan', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
def get_ShiftPlan(self, **kw):
"""
@@ -60,11 +62,14 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True, 'Datas': []}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/GetShiftPlan'})
if 'ProductionLine' in ret:
workorder_ids = request.env['mrp.workorder'].sudo().get_plan_workorder(ret['ProductionLine'])
else:
ProductionLine = False
workorder_ids = request.env['mrp.workorder'].sudo().get_plan_workorder(ProductionLine)
# todo 需要筛选出CNC工单
logging.info('RfidCode:%s' % ret)
logging.info('workorder_ids:%s' % workorder_ids)
workorder = request.env['mrp.workorder'].sudo().search(workorder_ids)
@@ -112,6 +117,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True, 'Datas': []}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/QcCheck'})
logging.info('RfidCode:%s' % ret['RfidCode'])
if 'RfidCode' in ret:
workorder = request.env['mrp.workorder'].sudo().search(
@@ -136,7 +143,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('get_qcCheck error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/FeedBackStart', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/FeedBackStart', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
def button_Work_START(self, **kw):
"""
@@ -149,6 +156,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True, 'Datas': ['工单已开始']}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/FeedBackStart'})
production_id = ret['BillId']
routing_type = ret['CraftId']
equipment_id = ret["DeviceId"]
@@ -179,7 +188,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('button_Work_START error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/FeedBackEnd', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/FeedBackEnd', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
def button_Work_End(self, **kw):
"""
@@ -192,6 +201,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True, 'Datas': ['工单已结束']}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/FeedBackEnd'})
production_id = ret['BillId']
routing_type = ret['CraftId']
workorder = request.env['mrp.workorder'].sudo().search(
@@ -203,6 +214,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '该工单未开始'}
return json.JSONEncoder().encode(res)
workorder.button_finish()
# workorder.process_state = '待解除装夹'
# workorder.sudo().production_id.process_state = '待解除装夹'
# 根据工单的实际结束时间修改排程单的结束时间、状态,同时修改销售订单的状态
if workorder.date_finished:
@@ -213,15 +226,15 @@ class Manufacturing_Connect(http.Controller):
if production_obj:
production_obj.sudo().work_order_state = '已完成'
production_obj.write({'state': 'completed'})
request.env['sale.order'].sudo().search(
[('name', '=', production_obj.origin)]).write({'schedule_status': 'to deliver'})
# request.env['sale.order'].sudo().search(
# [('name', '=', production_obj.origin)]).write({'schedule_status': 'to deliver'})
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
logging.info('button_Work_End error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/PartQualityInspect', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/PartQualityInspect', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
def PartQualityInspect(self, **kw):
"""
@@ -234,6 +247,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/PartQualityInspect'})
production_id = ret['BillId']
routing_type = ret['CraftId']
workorder = request.env['mrp.workorder'].sudo().search(
@@ -290,6 +305,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True, 'Datas': []}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/CMMProgDolod'})
if 'RfidCode' in ret:
logging.info('RfidCode:%s' % ret['RfidCode'])
workorder = request.env['mrp.workorder'].sudo().search(
@@ -328,6 +345,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True, 'Datas': []}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/NCProgDolod'})
if 'RfidCode' in ret:
logging.info('RfidCode:%s' % ret['RfidCode'])
workorder = request.env['mrp.workorder'].sudo().search(
@@ -354,7 +373,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('NCProgDolod error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/LocationChange', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/LocationChange', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
def LocationChange(self, **kw):
"""
@@ -367,6 +386,9 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True, 'Datas': []}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/LocationChange'})
logging.info('LocationChange_ret===========:%s' % ret)
RfidCode = ret['RfidCode']
ChangeType = ret['ChangeType']
OldDeciveId = ret['OldDeciveId']
@@ -375,47 +397,41 @@ class Manufacturing_Connect(http.Controller):
NewPosition = ret['NewPosition']
OldDeciveStart = ret['OldDeciveStart']
OldDeciveEnd = ret['OldDeciveEnd']
# Part、Tool
if ChangeType == 'Part':
workorder = request.env['mrp.workorder'].sudo().search(
[('rfid_code', '=', RfidCode)], limit=1)
if not workorder:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '未根据RfidCode找到该工单'}
return json.JSONEncoder().encode(res)
old_localtion = request.env['sf.shelf.location'].sudo().search(
[('barcode', '=', OldPosition)], limit=1)
new_localtion = request.env['sf.shelf.location'].sudo().search(
[('barcode', '=', NewPosition)], limit=1)
if not new_localtion:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '没有该目标位置'}
return json.JSONEncoder().encode(res)
if old_localtion:
old_localtion.location_status = '空闲'
old_localtion.production_id = False
new_localtion.location_status = '占用'
new_localtion.production_id = workorder.production_id.id
if ChangeType == 'Tool':
old_localtion = request.env['sf.shelf.location'].sudo().search(
[('barcode', '=', OldPosition)], limit=1)
equipment_id = request.env['maintenance.equipment'].sudo().search(
[('name', '=', NewPosition)], limit=1)
equipment_id.register_equipment_tool()
if not equipment_id:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '没有该目标位置'}
return json.JSONEncoder().encode(res)
if old_localtion:
old_localtion.location_status = '空闲'
old_localtion.production_id = False
# return json.JSONEncoder().encode(res)
# else:
# res = {'Succeed': False, 'ErrorCode': 201, 'Error': '未传RfidCode字段'}
temp_val_sn_id = None
old_localtion = None
# if ChangeType == 'Part' or ChangeType == 'Tool':
stock_lot_obj = request.env['stock.lot'].sudo().search(
[('rfid', '=', RfidCode)], limit=1)
logging.info('stock_lot_obj===========:%s' % stock_lot_obj)
if not stock_lot_obj:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '未根据RfidCode找到该产品'}
return json.JSONEncoder().encode(res)
if OldPosition:
old_localtion = request.env['sf.shelf.location'].sudo().search(
[('barcode', '=', OldPosition)], limit=1)
logging.info('old_localtion===========:%s' % old_localtion)
new_localtion = request.env['sf.shelf.location'].sudo().search(
[('barcode', '=', NewPosition)], limit=1)
logging.info('new_localtion===========:%s' % new_localtion)
if not new_localtion:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '没有该目标位置'}
return json.JSONEncoder().encode(res)
if old_localtion:
temp_val_sn_id = old_localtion.product_sn_id
logging.info('temp_val_sn_id===========:%s' % temp_val_sn_id)
old_localtion.product_sn_id = None
new_localtion.product_sn_id = temp_val_sn_id
logging.info('====1======')
else:
new_localtion.product_sn_id = stock_lot_obj.id
logging.info('=====2======')
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
logging.info('LocationChange error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/AGVToProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/AGVToProduct', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
def AGVToProduct(self, **kw):
"""
@@ -428,20 +444,38 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/AGVToProduct'})
logging.info('ret:%s' % ret)
if 'DeviceId' in ret:
logging.info('DeviceId:%s' % ret['DeviceId'])
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
[('feeder_station_start_id.name', '=', ret['DeviceId']),
('status', '=', '待配送')], limit=1, order='id asc')
if workpiece_delivery:
for wd in workpiece_delivery:
logging.info('wd.production_id:%s' % wd.production_id.name)
if wd.workorder_id.state == 'done' and wd.production_id.production_line_state == '待上产线':
logging.info('wd.production_line_state:%s' % wd.production_id.production_line_state)
wd.production_id.write({'production_line_state': '已上产线'})
wd.write({'production_line_state': '已上产线'})
if 'IsComplete' in ret:
if ret['IsComplete'] is True or ret['IsComplete'] is False:
for i in range(1, 5):
logging.info('F-RfidCode:%s' % i)
if f'RfidCode{i}' in ret:
rfid_code = ret[f'RfidCode{i}']
logging.info('RfidCode:%s' % rfid_code)
domain = [
('feeder_station_destination_id.name', '=', ret['DeviceId']),
('workorder_id.rfid_code', '=', rfid_code),
('status', '=', '已配送'),
('type', '=', '上产线')
]
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(domain,
order='id asc')
if workpiece_delivery:
for wd in workpiece_delivery:
logging.info('wd.production_id:%s' % wd.production_id.name)
if wd.workorder_id.state == 'done' and wd.production_id.production_line_state == '待上产线':
logging.info(
'wd.production_line_state:%s' % wd.production_id.production_line_state)
wd.production_id.write({'production_line_state': '已上产线'})
else:
res = {'Succeed': False, 'ErrorCode': 204,
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}
else:
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '该DeviceId没有对应的工件配送数据'}
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '未传IsComplete字段'}
else:
res = {'Succeed': False, 'ErrorCode': 201, 'Error': '未传DeviceId字段'}
except Exception as e:
@@ -449,7 +483,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('AGVToProduct error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/AGVDownProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/AGVDownProduct', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
def AGVDownProduct(self, **kw):
"""
@@ -463,41 +497,56 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/AGVDownProduct'})
logging.info('ret:%s' % ret)
if 'DeviceId' in ret:
logging.info('DeviceId:%s' % ret['DeviceId'])
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
[('feeder_station_destination_id.name', '=', ret['DeviceId']),
('status', '=', '待配送')], limit=1, order='id asc')
if workpiece_delivery:
for wd in workpiece_delivery:
logging.info('wd.production_id:%s' % wd.production_id.name)
if wd.workorder_id.state == 'done' and wd.production_id.production_line_state == '已上产线':
logging.info('wd.production_line_state:%s' % wd.production_id.production_line_state)
workpiece_delivery_off = request.env['sf.workpiece.delivery'].sudo().create({
'production_id': wd.production_id.id,
'feeder_station_start_id': workpiece_delivery.feeder_station_start_id.id,
'feeder_station_destination_id': '',
'workorder_id': workpiece_delivery.workorder_id.id,
'workpiece_code': workpiece_delivery.workpiece_code,
'production_line_id': workpiece_delivery.production_line_id.id,
'task_delivery_time': datetime.now(),
'production_line_state': '已下产线'
})
wd.production_id.write({'production_line_state': '产线'})
logging.info('开始向agv下发下产线任务')
workpiece_delivery_off._delivery_avg()
logging.info('agv下发下产线任务已配送')
delivery_Arr = []
if 'IsComplete' in ret:
if ret['IsComplete'] is True or ret['IsComplete'] is False:
for i in range(1, 5):
logging.info('F-RfidCode:%s' % i)
if f'RfidCode{i}' in ret:
rfid_code = ret[f'RfidCode{i}']
logging.info('RfidCode:%s' % rfid_code)
domain = [
('workorder_id.rfid_code', '=', rfid_code),
('status', '=', '待下发'),
('type', '=', '下产线')
]
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(domain,
order='id asc')
if workpiece_delivery:
for wd in workpiece_delivery:
logging.info('wd.production_id:%s' % wd.production_id.name)
if wd.workorder_id.state == 'done' and wd.production_id.production_line_state == '产线':
logging.info(
'wd.production_line_state:%s' % wd.production_id.production_line_state)
wd.production_id.write({'production_line_state': '已下产线'})
delivery_Arr.append(wd.id)
else:
res = {'Succeed': False, 'ErrorCode': 204,
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}
if delivery_Arr:
logging.info('delivery_Arr:%s' % delivery_Arr)
delivery_workpiece = request.env['sf.workpiece.delivery'].sudo().search(
[('id', 'in', delivery_Arr)])
if delivery_workpiece:
logging.info('开始向agv下发下产线任务')
is_free = delivery_workpiece._check_avgsite_state()
if is_free is True:
delivery_workpiece._delivery_avg()
logging.info('agv下发下产线任务下发完成')
else:
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '该DeviceId没有对应的工件配送数据'}
else:
res = {'Succeed': False, 'ErrorCode': 201, 'Error': '未传DeviceId字段'}
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '未传IsComplete字段'}
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
logging.info('AGVDownProduct error:%s' % e)
return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/EquipmentBaseCoordinate', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/EquipmentBaseCoordinate', type='json', auth='sf_token', methods=['GET', 'POST'],
csrf=False,
cors="*")
def PutEquipmentBaseCoordinate(self, **kw):
"""
@@ -510,6 +559,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/EquipmentBaseCoordinate'})
if 'DeviceId' in ret:
equipment = request.env['maintenance.equipment'].sudo().search('name', '=', ret['DeviceId'])
if equipment:

View File

@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import logging
import json
from datetime import datetime
from odoo import http
from odoo.http import request
@@ -24,12 +25,15 @@ class Workpiece(http.Controller):
if 'reqCode' in ret:
if 'method' in ret:
if ret['method'] == 'end':
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
[('production_id.name', '=', ret['reqCode']), ('agv_task_code'), '=', ret['taskCode']])
if workpiece_delivery:
workpiece_delivery.write({'status': '已配送', 'task_completion_time': ret['reqTime']})
else:
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '该reqCode暂未查到对应的工件配送记录'}
req_codes = ret['reqCode'].split(',')
for req_code in req_codes:
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
[('name', '=', req_code.strip()), ('task_completion_time', '=', False)])
if workpiece_delivery:
workpiece_delivery.write({'status': '已配送', 'task_completion_time': datetime.now()})
else:
res = {'Succeed': False, 'ErrorCode': 203,
'Error': '该reqCode暂未查到对应的工件配送记录'}
else:
res = {'Succeed': False, 'ErrorCode': 204, 'Error': '未传method字段'}
else:

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="sequence_workpiece_delivery" model="ir.sequence">
<field name="name">工件配送</field>
<field name="code">sf.workpiece.delivery</field>
<field name="prefix">WDO%(year)s%(month)s%(day)s</field>
<field name="padding">4</field>
<field name="company_id" eval="False"/>
</record>
<record id="workpiece_delivery_empty_racks_1" model="sf.workpiece.delivery">
<field name="name">运送空料架路线:C01-A01</field>
<field name="type">运送空料架</field>
<field name="route_id"
search="[('start_site_id.name','=','C01'),('end_site_id.name','=','A01')]"/>
<field name="feeder_station_start_id" search="[('name','=','C01')]"/>
<field name="feeder_station_destination_id" search="[('name','=','A01')]"/>
</record>
<record id="workpiece_delivery_empty_racks_2" model="sf.workpiece.delivery">
<field name="name">运送空料架路线:B01-B02</field>
<field name="type">运送空料架</field>
<field name="route_id"
search="[('start_site_id.name','=','B01'),('end_site_id.name','=','B02')]"/>
<field name="feeder_station_start_id" search="[('name','=','B01')]"/>
<field name="feeder_station_destination_id" search="[('name','=','B02')]"/>
</record>
<record id="workpiece_delivery_empty_racks_3" model="sf.workpiece.delivery">
<field name="name">运送空料架路线:B01-A01</field>
<field name="type">运送空料架</field>
<field name="route_id"
search="[('start_site_id.name','=','B01'),('end_site_id.name','=','A01')]"/>
<field name="feeder_station_start_id" search="[('name','=','B01')]"/>
<field name="feeder_station_destination_id" search="[('name','=','A01')]"/>
</record>
<record id="workpiece_delivery_empty_racks_4" model="sf.workpiece.delivery">
<field name="name">运送空料架路线:C01-B02</field>
<field name="type">运送空料架</field>
<field name="route_id"
search="[('start_site_id.name','=','C01'),('end_site_id.name','=','B02')]"/>
<field name="feeder_station_start_id" search="[('name','=','C01')]"/>
<field name="feeder_station_destination_id" search="[('name','=','B02')]"/>
</record>
</data>
</odoo>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="sequence_routing_workcenter" model="ir.sequence">
<record id="sequence_routing_workcenter" model="ir.sequence">
<field name="name">工序编码规则</field>
<field name="code">mrp.routing.workcenter</field>
<field name="padding">4</field>
@@ -74,14 +74,25 @@
<field name="active">True</field>
<field name="sequence">11</field>
</record>
<!-- <record id="route_surface_technology_outsourcing" model="stock.rule">-->
<!-- <field name="name">外协出库单</field>-->
<!-- <field name="action">push</field>-->
<!-- <field name="pick_type_id" ref="outcontract_picking_out"></field>-->
<!-- <field name="location_src_id" ref=""/>-->
<!-- <field name="location_dest_id" ref="stock_location_locations_virtual_outcontract"/>-->
<!-- <field name="active">True</field>-->
<!-- <field name="sequence">11</field>-->
<!-- </record>-->
<!-- <record id="rule_surface_technology_in" model="stock.rule">-->
<!-- <field name="name">外协出库单</field>-->
<!-- <field name="route_id" ref="route_surface_technology_outsourcing"/>-->
<!--&lt;!&ndash; <field name="action">push</field>&ndash;&gt;-->
<!-- <field name="pick_type_id" ref="outcontract_picking_in"/>-->
<!-- &lt;!&ndash; <field name="location_src_id" search="[('barcode','=','WH-PREPRODUCTION')]"/>&ndash;&gt;-->
<!-- &lt;!&ndash; <field name="location_dest_id" ref="stock_location_locations_virtual_outcontract"/>&ndash;&gt;-->
<!-- &lt;!&ndash; <field name="active">True</field>&ndash;&gt;-->
<!-- &lt;!&ndash; <field name="sequence">11</field>&ndash;&gt;-->
<!-- </record>-->
<!-- <record id="rule_surface_technology_in" model="stock.rule">-->
<!-- <field name="name">外协入库单</field>-->
<!-- <field name="route_id" ref="route_surface_technology_outsourcing"/>-->
<!-- <field name="action">pull</field>-->
<!-- <field name="pick_type_id" ref="outcontract_picking_out"/>-->
<!-- <field name="location_src_id" ref="stock_location_locations_virtual_outcontract"/>-->
<!-- <field name="location_dest_id" search="[('barcode','=','WH-PREPRODUCTION')]"/>-->
<!-- <field name="active">True</field>-->
<!-- <field name="sequence">11</field>-->
<!-- </record>-->
</data>
</odoo>

View File

@@ -1,4 +1,7 @@
# -*- coding: utf-8 -*-
import requests
import logging
import time
from odoo import fields, models
@@ -6,13 +9,37 @@ class AgvSetting(models.Model):
_name = 'sf.agv.site'
_description = 'agv站点'
number = fields.Integer('序号')
name = fields.Char('位置编号')
owning_region = fields.Char('所属区域')
state = fields.Selection([
('占用', '占用'),
('空闲', '空闲')], string='状态')
divide_the_work = fields.Char('主要分工')
active = fields.Boolean('有效', default=True)
def update_site_state(self):
# 调取中控的接驳站接口并修改对应站点的状态
config = self.env['res.config.settings'].get_values()
# token = sf_sync_config['token'Ba F2CF5DCC-1A00-4234-9E95-65603F70CC8A]
headers = {'Authorization': config['center_control_Authorization']}
center_control_url = config['center_control_url'] + "/AutoDeviceApi/GetAgvStationState?date="
timestamp = int(time.time())
center_control_url += str(timestamp)
logging.info('工件配送-请求中控地址:%s' % center_control_url)
center_control_r = requests.get(center_control_url, params={}, headers=headers)
ret = center_control_r.json()
logging.info('工件配送-请求中控站点信息:%s' % ret)
self.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/GetAgvStationState?date=%s' % str(timestamp)})
if ret['Succeed'] is True:
datas = ret['Datas']
for item in self:
for da in datas:
if da['DeviceId'] == item.name:
if da['AtHome'] is True:
item.state = '占用'
else:
item.state = '空闲'
class AgvTaskRoute(models.Model):
@@ -21,15 +48,21 @@ class AgvTaskRoute(models.Model):
name = fields.Char('名称')
type = fields.Selection([
('F01', '搬运'), ], string='类型', default="F01")
('F01', '搬运'), ], string='任务类型', default="F01")
route_type = fields.Selection([
('上产线', '上产线'), ('下产线', '下产线'), ('运送空料架', '运送空料架')], string='类型')
start_site_id = fields.Many2one('sf.agv.site', '起点接驳站位置编号')
end_site_id = fields.Many2one('sf.agv.site', '终点接驳站位置编号')
destination_production_line_id = fields.Many2one('sf.production.line', '目的生产线')
priority = fields.Selection([
('0', '正常'),
('1', ''),
('2', ''),
('3', ''),
('4', '紧急'),
], string='优先级', default='0')
active = fields.Boolean('有效', default=True)
class Center_controlInterfaceLog(models.Model):
_name = 'center_control.interface.log'
_description = '中控接口调用日志'
_order = 'id desc'
name = fields.Char('接口名称')
content = fields.Char('接口内容')
interface_call_date = fields.Datetime("调用时间", default=fields.Datetime.now, readonly=True)
active = fields.Boolean('有效', default=True)

View File

@@ -21,7 +21,8 @@ class ProductModelTypeRoutingSort(models.Model):
_description = '成品工序排序'
sequence = fields.Integer('Sequence')
route_workcenter_id = fields.Many2one('mrp.routing.workcenter')
route_workcenter_id = fields.Many2one('mrp.routing.workcenter',
domain=[('routing_type', 'in', ['装夹预调', 'CNC加工', '解除装夹'])])
is_repeat = fields.Boolean('重复', related='route_workcenter_id.is_repeat')
# routing_type = fields.Selection([
@@ -54,7 +55,7 @@ class EmbryoModelTypeRoutingSort(models.Model):
_description = '坯料工序排序'
sequence = fields.Integer('Sequence')
route_workcenter_id = fields.Many2one('mrp.routing.workcenter')
route_workcenter_id = fields.Many2one('mrp.routing.workcenter', domain=[('routing_type', 'in', ['切割'])])
is_repeat = fields.Boolean('重复', related='route_workcenter_id.is_repeat')
# routing_type = fields.Selection([
@@ -87,7 +88,7 @@ class SurfaceTechnicsModelTypeRoutingSort(models.Model):
_description = '表面工艺工序排序'
sequence = fields.Integer('Sequence')
route_workcenter_id = fields.Many2one('mrp.routing.workcenter')
route_workcenter_id = fields.Many2one('mrp.routing.workcenter', domain=[('routing_type', 'in', ['表面工艺'])])
is_repeat = fields.Boolean('重复', related='route_workcenter_id.is_repeat')
# routing_type = fields.Selection([

View File

@@ -3,8 +3,9 @@ import base64
import logging
import re
import requests
from itertools import groupby
from odoo import api, fields, models, _
from odoo.exceptions import UserError
from odoo.exceptions import UserError,ValidationError
from odoo.addons.sf_base.commons.common import Common
from odoo.tools import float_compare, float_round, float_is_zero, format_datetime
@@ -32,7 +33,9 @@ class MrpProduction(models.Model):
('draft', 'Draft'),
('confirmed', 'Confirmed'),
('progress', '待排程'),
('pending_cam', '待装夹'),
('pending_processing', '待加工'),
('pending_era_cam', '待解除装夹'),
('completed', '已完工'),
('to_close', 'To Close'),
('done', 'Done'),
@@ -57,12 +60,27 @@ class MrpProduction(models.Model):
production_line_state = fields.Selection(
[('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')],
string='上/下产线', default='待上产线')
# 工序状态
# Todo 研究下用法
process_state = fields.Selection([
('待装夹', '待装夹'),
('待检测', '待检测'),
('待加工', '待加工'),
('待解除装夹', '待解除装夹'),
('已完工', '已完工'),
], string='工序状态', default='待装夹')
# 零件图号
part_number = fields.Char('零件图号')
# 上传零件图纸
part_drawing = fields.Binary('零件图纸')
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
@api.depends(
'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state',
'workorder_ids.state', 'product_qty', 'qty_producing', 'schedule_state')
'workorder_ids.state', 'product_qty', 'qty_producing', 'schedule_state', 'process_state')
def _compute_state(self):
for production in self:
if not production.state or not production.product_uom_id:
@@ -94,11 +112,15 @@ class MrpProduction(models.Model):
production.state = 'progress'
# 新添加的状态逻辑
if production.state == 'progress' and production.schedule_state == '已排':
if production.state == 'progress' and production.schedule_state == '已排' and production.process_state == '待装夹':
# production.state = 'pending_processing'
production.state = 'pending_cam'
if production.state == 'progress' and production.schedule_state == '已排' and production.process_state == '待加工':
# if production.state == 'pending_cam' and production.process_state == '待加工':
production.state = 'pending_processing'
# elif production.state == 'progress' and production.schedule_state == '已完成':
# production.state = 'completed'
elif production.state == 'pending_processing' and production.work_order_state == '已完':
elif production.state == 'progress' and production.process_state == '待解除装夹':
production.state = 'pending_era_cam'
elif production.state == 'progress' and production.process_state == '已完':
production.state = 'completed'
elif production.state == 'progress' and production.work_order_state == '已完成':
production.state = 'completed'
@@ -133,7 +155,7 @@ class MrpProduction(models.Model):
production.maintenance_count = len(production.request_ids)
# cnc程序获取
def fetchCNC(self):
def fetchCNC(self, production_names):
cnc = self.env['mrp.production'].search([('id', '=', self.id)])
quick_order = self.env['quick.easy.order'].search(
[('name', '=', cnc.product_id.default_code.rsplit('-', 1)[0])])
@@ -145,26 +167,31 @@ class MrpProduction(models.Model):
if quick_order:
programme_way = 'manual operation'
try:
res = {'model_code': '' if not cnc.product_id.model_code else cnc.product_id.model_code,
'production_no': cnc.name,
'machine_tool_code': "",
'material_code': self.env['sf.production.materials'].search(
[('id', '=', cnc.product_id.materials_id.id)]).materials_no,
'material_type_code': self.env['sf.materials.model'].search(
[('id', '=', cnc.product_id.materials_type_id.id)]).materials_no,
'machining_processing_panel': cnc.product_id.model_processing_panel,
'machining_precision': cnc.product_id.model_machining_precision,
'embryo_long': cnc.product_id.bom_ids.bom_line_ids.product_id.length,
'embryo_height': cnc.product_id.bom_ids.bom_line_ids.product_id.height,
'embryo_width': cnc.product_id.bom_ids.bom_line_ids.product_id.width,
'order_no': cnc.origin,
'model_order_no': cnc.product_id.default_code,
'user': cnc.env.user.name,
'programme_way': programme_way,
'model_file': '' if not cnc.product_id.model_file else base64.b64encode(
cnc.product_id.model_file).decode('utf-8')
}
logging.info('res:%s' % res)
res = {
'production_no': production_names,
'machine_tool_code': '',
'product_name': cnc.product_id.name,
'model_code': cnc.product_id.model_code,
'material_code': self.env['sf.production.materials'].search(
[('id', '=', cnc.product_id.materials_id.id)]).materials_no,
'material_type_code': self.env['sf.materials.model'].search(
[('id', '=', cnc.product_id.materials_type_id.id)]).materials_no,
'machining_processing_panel': cnc.product_id.model_processing_panel,
'machining_precision': cnc.product_id.model_machining_precision,
'embryo_long': cnc.product_id.bom_ids.bom_line_ids.product_id.length,
'embryo_height': cnc.product_id.bom_ids.bom_line_ids.product_id.height,
'embryo_width': cnc.product_id.bom_ids.bom_line_ids.product_id.width,
'order_no': cnc.origin,
'model_order_no': cnc.product_id.default_code,
'user': cnc.env.user.name,
'programme_way': programme_way,
'model_file': '' if not cnc.product_id.model_file else base64.b64encode(
cnc.product_id.model_file).decode('utf-8')
}
# 打印出除了 model_file 之外的所有键值对
for key, value in res.items():
if key != 'model_file':
logging.info('%s: %s' % (key, value))
configsettings = self.env['res.config.settings'].get_values()
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
url = '/api/intelligent_programming/create'
@@ -242,11 +269,18 @@ class MrpProduction(models.Model):
# 其他规则限制: 默认只分配给工作中心状态为非故障的工作中心;
def _create_workorder3(self):
# 根据product_id对self进行分组
grouped_product_ids = {k: list(g) for k, g in groupby(self, key=lambda x: x.product_id.id)}
# 初始化一个字典来存储每个product_id对应的生产订单名称列表
product_id_to_production_names = {}
# 对于每个product_id获取其所有生产订单的名称
for product_id, productions in grouped_product_ids.items():
# 为同一个product_id创建一个生产订单名称列表
product_id_to_production_names[product_id] = [production.name for production in productions]
for production in self:
if not production.bom_id or not production.product_id:
continue
workorders_values = []
product_qty = production.product_uom_id._compute_quantity(production.product_qty,
production.bom_id.product_uom_id)
exploded_boms, dummy = production.bom_id.explode(production.product_id,
@@ -270,8 +304,19 @@ class MrpProduction(models.Model):
'state': 'pending',
}]
if production.product_id.categ_id.type == '成品':
production.fetchCNC()
# 根据加工面板的面数及对应的工序模板生成工单
if production.product_id.id in product_id_to_production_names:
# # 同一个产品多个制造订单对应一个编程单和模型库
# # 只调用一次fetchCNC并将所有生产订单的名称作为字符串传递
if not production.programming_no:
production_programming = self.search(
[('product_id.id', '=', production.product_id.id), ('origin', '=', production.origin)],
limit=1, order='id asc')
if not production_programming.programming_no:
production.fetchCNC(', '.join(product_id_to_production_names[production.product_id.id]))
else:
production.write({'programming_no': production_programming.programming_no,
'programming_state': '编程中'})
# # 根据加工面板的面数及对应的工序模板生成工单
i = 0
processing_panel_len = len(production.product_id.model_processing_panel.split(','))
for k in (production.product_id.model_processing_panel.split(',')):
@@ -334,48 +379,6 @@ class MrpProduction(models.Model):
workorders_values.append(
self.env['mrp.workorder'].json_workorder_str('', production, route))
production.workorder_ids = workorders_values
process_parameter_workorder = self.env['mrp.workorder'].search(
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id),
('is_subcontract', '=', True)])
if process_parameter_workorder:
is_pick = False
consecutive_workorders = []
m = 0
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.id)
for i in range(len(sorted_workorders) - 1):
if m == 0:
is_pick = False
if sorted_workorders[i].supplier_id.id == sorted_workorders[i + 1].supplier_id.id and \
sorted_workorders[i].is_subcontract == sorted_workorders[i + 1].is_subcontract and \
sorted_workorders[i].id == sorted_workorders[i + 1].id - 1:
if sorted_workorders[i] not in consecutive_workorders:
consecutive_workorders.append(sorted_workorders[i])
consecutive_workorders.append(sorted_workorders[i + 1])
m += 1
continue
else:
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production)
if sorted_workorders[i] in consecutive_workorders:
is_pick = True
consecutive_workorders = []
m = 0
# 当前面的连续工序生成对应的外协出入库单再生成当前工序的外协出入库单
if is_pick is False:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production)
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production)
if sorted_workorders[i] in consecutive_workorders:
is_pick = True
consecutive_workorders = []
m = 0
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production)
if is_pick is False and m == 0:
if len(sorted_workorders) == 1:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production)
else:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production)
for workorder in production.workorder_ids:
workorder.duration_expected = workorder._get_duration_expected()
@@ -401,20 +404,6 @@ class MrpProduction(models.Model):
'user_id': production.user_id.id}
return production_values_str
def _get_stock_move_values_Res(self, item, location_src_id, location_dest_id, picking_type_id):
move_values = {
'name': item.name if item.name else '/',
'company_id': item.company_id.id,
'product_id': item.bom_id.bom_line_ids.product_id.id,
'product_uom': item.bom_id.bom_line_ids.product_uom_id.id,
'product_uom_qty': 1.0,
'location_id': location_src_id,
'location_dest_id': location_dest_id,
'origin': item.origin,
'picking_type_id': picking_type_id,
}
return move_values
# 工单排序
def _reset_work_order_sequence1(self, k):
sequen = 0
@@ -494,10 +483,49 @@ class MrpProduction(models.Model):
def _reset_work_order_sequence(self):
for rec in self:
current_sequence = 1
sequence_list = {}
model_type_id = rec.product_id.product_model_type_id
if model_type_id:
tmpl_num = 1
# 成品工序
product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids
if product_routing_tmpl_ids:
for tmpl_id in product_routing_tmpl_ids:
sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
tmpl_num += 1
# 表面工艺工序
# 模型类型的表面工艺工序模版
surface_tmpl_ids = model_type_id.surface_technics_routing_tmpl_ids
# 产品选择的表面工艺
model_process_parameters_ids = rec.product_id.model_process_parameters_ids
process_dict = {}
if model_process_parameters_ids:
for process_parameters_id in model_process_parameters_ids:
process_id = process_parameters_id.process_id
for surface_tmpl_id in surface_tmpl_ids:
if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id:
surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name
process_dict.update({int(process_id.category_id.code): '%s-%s' % (
surface_tmpl_name, process_parameters_id.name)})
process_list = sorted(process_dict.keys())
for process_num in process_list:
sequence_list.update({process_dict.get(process_num): tmpl_num})
tmpl_num += 1
# 坯料工序
tmpl_num = 1
embryo_routing_tmpl_ids = model_type_id.embryo_routing_tmpl_ids
if embryo_routing_tmpl_ids:
for tmpl_id in embryo_routing_tmpl_ids:
sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
tmpl_num += 1
else:
raise ValidationError('该产品没有选择【模版类型】!')
for work in rec.workorder_ids:
work.sequence = current_sequence
current_sequence += 1
if sequence_list.get(work.name):
work.sequence = sequence_list[work.name]
else:
raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
# if work.name == '获取CNC加工程序':
# work.button_start()
# #work.fetchCNC()
@@ -511,7 +539,8 @@ class MrpProduction(models.Model):
# 修改标记已完成方法
def button_mark_done1(self):
self._button_mark_done_sanity_checks()
if not self.workorder_ids.filtered(lambda w: w.routing_type not in ['表面工艺']):
self._button_mark_done_sanity_checks()
if not self.env.context.get('button_mark_done_production_ids'):
self = self.with_context(button_mark_done_production_ids=self.ids)

View File

@@ -109,8 +109,29 @@ class ResMrpWorkOrder(models.Model):
glb_file = fields.Binary("glb模型文件", related='production_id.model_file')
is_subcontract = fields.Boolean(string='是否外协')
surface_technics_parameters_id = fields.Many2one('sf.production.process.parameter', string="表面工艺可选参数")
picking_in_id = fields.Many2one('stock.picking', string='外协入库单')
picking_out_id = fields.Many2one('stock.picking', string='外协出库单')
picking_ids = fields.Many2many('stock.picking', string='外协入库单')
surface_technics_picking_count = fields.Integer("外协出入库", compute='_compute_surface_technics_picking_ids')
@api.depends('name', 'production_id.name')
def _compute_surface_technics_picking_ids(self):
for order in self:
picking_ids = self.env['stock.picking'].search([('id', 'in', order.picking_ids.ids)])
order.surface_technics_picking_count = len(picking_ids)
def action_view_surface_technics_picking(self):
self.ensure_one()
action = self.env["ir.actions.actions"]._for_xml_id("stock.action_picking_tree_all")
if len(self.picking_ids) > 1:
action['domain'] = [('id', 'in', self.picking_ids.ids)]
elif self.picking_ids:
# action['name'] = '工艺外协'
action['res_id'] = self.picking_ids.id
action['views'] = [(self.env.ref('stock.view_picking_form').id, 'form')]
if 'views' in action:
action['views'] += [(state, view) for state, view in action['views'] if view != 'form']
action['context'] = dict(self._context, default_origin=self.name)
return action
supplier_id = fields.Many2one('res.partner', string='外协供应商')
equipment_id = fields.Many2one('maintenance.equipment', string='加工设备')
is_ok = fields.Boolean(string='是否合格')
@@ -123,6 +144,23 @@ class ResMrpWorkOrder(models.Model):
# 获取数据状态
data_state = fields.Boolean(string='获取数据状态', default=False)
# 坯料长宽高
material_length = fields.Float(string='')
material_width = fields.Float(string='')
material_height = fields.Float(string='')
# 零件图号
part_number = fields.Char(string='零件图号')
# 工序状态
process_state = fields.Selection([
('待装夹', '待装夹'),
('待检测', '待检测'),
('待加工', '装夹预调完工'),
('待解除装夹', '待解除装夹'),
('已完工', '已完工'),
], string='工序状态', default='待装夹', readonly='True')
# 加工图纸
processing_drawing = fields.Binary(string='加工图纸', related='production_id.part_drawing')
@api.depends('production_id')
def _compute_save_name(self):
"""
@@ -155,6 +193,7 @@ class ResMrpWorkOrder(models.Model):
is_delivery = fields.Boolean('是否配送完成', default=False)
rfid_code = fields.Char('RFID码')
rfid_code_old = fields.Char('RFID码(已解除)')
production_line_id = fields.Many2one('sf.production.line', related='production_id.production_line_id',
string='生产线', store=True)
production_line_state = fields.Selection(related='production_id.production_line_state',
@@ -162,6 +201,11 @@ class ResMrpWorkOrder(models.Model):
detection_report = fields.Binary('检测报告', readonly=True)
is_remanufacture = fields.Boolean(string='是否重新生成制造订单', default=True)
@api.onchange('rfid_code')
def _onchange(self):
if self.rfid_code and self.state == 'progress':
self.workpiece_delivery_ids[0].write({'rfid_code': self.rfid_code})
def get_plan_workorder(self, production_line):
tomorrow = (date.today() + timedelta(days=+1)).strftime("%Y-%m-%d")
tomorrow_start = tomorrow + ' 00:00:00'
@@ -389,7 +433,11 @@ class ResMrpWorkOrder(models.Model):
work = workorder.production_id.workorder_ids
work.compensation_value_x = eval(self.material_center_point)[0]
work.compensation_value_y = eval(self.material_center_point)[1]
# work.process_state = '待加工'
# self.sudo().production_id.process_state = '待加工'
self.date_finished = datetime.now()
workorder.button_finish()
except Exception as e:
# 重新抛出捕获到的异常信息
raise UserError(str(e))
@@ -399,22 +447,23 @@ class ResMrpWorkOrder(models.Model):
for item in self.workpiece_delivery_ids:
if not item.route_id:
raise UserError('【工件配送】明细中请选择【任务路线】')
# if not item.workpiece_code:
# raise UserError('请对【同运工件】进行扫描')
else:
if self.cnc_program_down_state == '已下发':
if item.status == '待下发':
return {
'name': _('确认'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'sf.workpiece.delivery.wizard',
'target': 'new',
'context': {
'default_workorder_id': self.id,
}}
if self.state == 'done':
if item.is_cnc_program_down is True:
if item.status == '待下发':
return {
'name': _('确认'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'sf.workpiece.delivery.wizard',
'target': 'new',
'context': {
'default_workorder_id': self.id,
}}
else:
raise UserError(_("该制造订单还未下发CNC程序单无法进行工件配送"))
else:
raise UserError(_("制造订单还未下发CNC程序单,无法进行工件配送"))
raise UserError(_("工单暂未完成,无法进行工件配送"))
# 拼接工单对象属性值
def json_workorder_str(self, k, production, route):
@@ -454,16 +503,27 @@ class ResMrpWorkOrder(models.Model):
'workcenter_id': self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids,
route.routing_type,
production.product_id),
'date_planned_start': False,
'date_planned_finished': False,
# 设定初始化值避免出现变成bool问题
'date_planned_start': datetime.now(),
'date_planned_finished': datetime.now() + timedelta(days=1),
'duration_expected': duration_expected,
'duration': 0,
'workpiece_delivery_ids': False if not route.routing_type == '装夹预调' else self.env[
'sf.workpiece.delivery'].create(
{'production_id': production.id})
'workpiece_delivery_ids': False if not route.routing_type == '装夹预调' else self._json_workpiece_delivery_list(
production)
}]
return workorders_values_str
def _json_workpiece_delivery_list(self, production):
up_route = self.env['sf.agv.task.route'].search([('route_type', '=', '上产线')], limit=1, order='id asc')
down_route = self.env['sf.agv.task.route'].search([('route_type', '=', '下产线')], limit=1, order='id asc')
return [
[0, '', {'production_id': production.id, 'type': '上产线', 'route_id': up_route.id,
'feeder_station_start_id': up_route.start_site_id.id,
'feeder_station_destination_id': up_route.end_site_id.id}],
[0, '', {'production_id': production.id, 'type': '下产线', 'route_id': down_route.id,
'feeder_station_start_id': down_route.start_site_id.id,
'feeder_station_destination_id': down_route.end_site_id.id}]]
# 拼接工单对象属性值(表面工艺)
def _json_workorder_surface_process_str(self, production, route, process_parameter, supplier_id):
workorders_values_str = [0, '', {
@@ -482,8 +542,8 @@ class ResMrpWorkOrder(models.Model):
self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids,
route.routing_type,
production.product_id),
'date_planned_start': False,
'date_planned_finished': False,
'date_planned_start': datetime.now(),
'date_planned_finished': datetime.now() + timedelta(days=1),
'duration_expected': 60,
'duration': 0
}]
@@ -664,8 +724,8 @@ class ResMrpWorkOrder(models.Model):
'workcenter_id': self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids,
route.routing_type,
production.product_id),
'date_planned_start': False,
'date_planned_finished': False,
'date_planned_start': datetime.now(),
'date_planned_finished': datetime.now() + timedelta(days=1),
'duration_expected': 60,
'duration': 0,
@@ -674,13 +734,47 @@ class ResMrpWorkOrder(models.Model):
# 重写工单开始按钮方法
def button_start(self):
if self.routing_type == '装夹预调' and self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name:
if self.routing_type == '装夹预调':
# 判断是否有坯料的序列号信息
boolean = False
if self.production_id.move_raw_ids[0].move_line_ids:
if self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name:
boolean = True
if not boolean:
raise UserError('制造订单【%s】缺少组件的序列号信息!' % self.production_id.name)
self.pro_code = self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name
# 外协出库单,从“正在等待”变为“就绪”状态
if self.is_subcontract is True:
picking_out = self.env['stock.picking'].search([('id', '=', self.picking_out_id.id)])
if picking_out.state == 'confirmed':
picking_out.write({'state': 'assigned'})
# cnc校验
cnc_workorder = self.search(
[('production_id', '=', self.production_id.id), ('routing_type', '=', 'CNC加工')],
limit=1, order='id asc')
if not cnc_workorder.cnc_ids:
raise UserError(_('该制造订单还未下发CNC程序请稍后再试'))
else:
for item in cnc_workorder.cnc_ids:
functional_cutting_tool = self.env['sf.functional.cutting.tool.entity'].search(
[('tool_name_id.name', '=', item.cutting_tool_name)])
if not functional_cutting_tool:
raise UserError(_('该制造订单的CNC程序为%s没有对应的功能刀具' % item.cutting_tool_name))
if self.routing_type == '解除装夹':
'''
记录开始时间
'''
self.date_start = datetime.now()
# 表面工艺外协出库单
if self.routing_type == '表面工艺':
if self.is_subcontract is True:
move_out = self.env['stock.move'].search(
[('location_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
('location_dest_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'VL-SPOC')]).id),
('origin', '=', self.production_id.name)])
purchase = self.env['purchase.order'].search([('origin', '=', self.production_id.name)])
if purchase and move_out:
move_out.write({'state': 'assigned'})
self.env['stock.move.line'].create(move_out.get_move_line(purchase, self))
# move_out._action_assign()
if self.state == 'waiting' or self.state == 'ready' or self.state == 'progress':
self.move_raw_ids = self.production_id.move_raw_ids
self.move_raw_ids[0].write({
@@ -688,6 +782,11 @@ class ResMrpWorkOrder(models.Model):
'materiel_width': self.move_raw_ids[0].product_id.width,
'materiel_height': self.move_raw_ids[0].product_id.height
})
self.write({
'material_length': self.move_raw_ids[0].product_id.length,
'material_width': self.move_raw_ids[0].product_id.width,
'material_height': self.move_raw_ids[0].product_id.height
})
self.ensure_one()
if any(not time.date_end for time in self.time_ids.filtered(lambda t: t.user_id.id == self.env.user.id)):
@@ -729,53 +828,93 @@ class ResMrpWorkOrder(models.Model):
else:
if self.date_planned_start > start_date:
vals['date_planned_start'] = start_date
if self.date_planned_finished and self.date_planned_finished < start_date:
vals['date_planned_finished'] = start_date
# if self.date_planned_finished and self.date_planned_finished < start_date:
# vals['date_planned_finished'] = start_date
return self.write(vals)
else:
raise UserError(_('请先完成上一步工单'))
def button_finish(self):
if self.routing_type == '装夹预调':
if not self.material_center_point and self.X_deviation_angle > 0:
raise UserError("请对前置三元检测定位参数进行计算定位")
if not self.rfid_code:
raise UserError("请扫RFID码进行绑定")
if self.picking_out_id:
picking_out = self.env['stock.picking'].search([('id', '=', self.picking_out_id.id)])
if picking_out.workorder_out_id:
order_line_ids = []
for item in picking_out.workorder_out_id:
server_product = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', item.surface_technics_parameters_id.id),
('detailed_type', '=', 'service')])
if server_product:
order_line_ids.append((0, 0, {
'product_id': server_product.product_variant_id.id,
'product_qty': 1,
'product_uom': server_product.uom_id.id
}))
else:
raise UserError(
'请先在产品中配置表面工艺为%s相关的外协服务产品' % item.surface_technics_parameters_id.name)
self.env['purchase.order'].create({
'partner_id': server_product.seller_ids.partner_id.id,
'state': 'draft',
'order_line': order_line_ids,
for record in self:
if record.routing_type == '装夹预调':
if not record.material_center_point and record.X_deviation_angle > 0:
raise UserError("请对前置三元检测定位参数进行计算定位")
if not record.rfid_code:
raise UserError("请扫RFID码进行绑定")
record.process_state = '待加工'
# record.write({'process_state': '待加工'})
record.production_id.process_state = '待加工'
if record.routing_type == 'CNC加工':
record.process_state = '待解除装夹'
# record.write({'process_state': '待加工'})
record.production_id.process_state = '待解除装夹'
if record.routing_type == '解除装夹':
'''
记录结束时间
'''
record.date_finished = datetime.now()
if record.routing_type == '表面工艺':
logging.info('record.picking_ids:%s' % record.picking_ids)
logging.info('record.picking_out:%s' % record.picking_ids[0])
if record.picking_ids:
for pick_item in record.picking_ids:
if pick_item.state not in ['done']:
raise UserError(
'请先完成该工单的工艺外协再进行操作')
picking_out = record.env['stock.move.line'].search(
[('picking_id', '=', record.picking_ids[0].id)])
logging.info('picking_out:%s' % picking_out.picking_id.name)
if picking_out:
order_line_ids = []
logging.info('surface_technics_parameters_id:%s' % record.surface_technics_parameters_id.name)
server_product = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', record.surface_technics_parameters_id.id),
('detailed_type', '=', 'service')])
logging.info('server_product:%s' % server_product.name)
if server_product:
order_line_ids.append((0, 0, {
'product_id': server_product.product_variant_id.id,
'product_qty': 1,
'product_uom': server_product.uom_id.id
}))
self.env['purchase.order'].sudo().create({
'partner_id': server_product.seller_ids.partner_id.id,
'origin': record.production_id.name,
'state': 'draft',
'order_line': order_line_ids,
})
else:
raise UserError(
'请先在产品中配置表面工艺为%s相关的外协服务产品' % item.surface_technics_parameters_id.name)
tem_date_planned_finished = record.date_planned_finished
logging.info('routing_type:%s' % record.routing_type)
super().button_finish()
logging.info('date_planned_finished:%s' % record.date_planned_finished)
# 表面工艺工单完成不走该修改
if record.routing_type != '表面工艺':
record.write({
'date_planned_finished': tem_date_planned_finished # 保持原值
})
super().button_finish()
is_production_id = True
for workorder in self.production_id.workorder_ids:
if workorder.state != 'done':
is_production_id = False
if is_production_id == True and self.name == '解除装夹':
for workorder in self.production_id.workorder_ids:
workorder.rfid_code_old = workorder.rfid_code
workorder.rfid_code = None
for move_raw_id in self.production_id.move_raw_ids:
move_raw_id.quantity_done = move_raw_id.product_uom_qty
self.production_id.button_mark_done1()
# self.production_id.state = 'done'
is_production_id = True
for workorder in record.production_id.workorder_ids:
if workorder.state != 'done':
is_production_id = False
if is_production_id is True and record.routing_type in ['解除装夹', '表面工艺']:
for workorder in record.production_id.workorder_ids:
workorder.rfid_code_old = workorder.rfid_code
workorder.rfid_code = None
for move_raw_id in record.production_id.move_raw_ids:
move_raw_id.quantity_done = move_raw_id.product_uom_qty
record.process_state = '已完工'
record.production_id.process_state = '已完工'
if record.routing_type in ['表面工艺']:
raw_move = self.env['stock.move'].sudo().search(
[('origin', '=', record.production_id.name), ('procure_method', '=', 'make_to_order'),
('state', '!=', 'done')])
if raw_move:
raw_move.write({'state': 'done'})
record.production_id.button_mark_done1()
# self.production_id.state = 'done'
# 将FTP的检测报告文件下载到临时目录
def download_reportfile_tmp(self, workorder, reportpath):
@@ -800,13 +939,17 @@ class ResMrpWorkOrder(models.Model):
# 根据中控系统提供的检测文件地址去ftp里对应的制造订单里获取
def get_detection_file(self, workorder, reportPath):
if reportPath.startswith('/'):
reportPath = reportPath[1:]
serverdir = os.path.join('/tmp', reportPath)
# if reportPath.startswith('/'):
# reportPath = reportPath[4:]
# serverdir = os.path.join('/tmp', reportPath)
serverdir = '/tmp' + reportPath
logging.info('get_detection_file-serverdir:%s' % serverdir)
serverdir_prefix = os.path.dirname(serverdir)
logging.info('serverdir_prefix-serverdir:%s' % serverdir_prefix)
for root, dirs, files in os.walk(serverdir_prefix):
for filename in files:
logging.info('filename:%s' % filename)
logging.info('reportPath:%s' % os.path.basename(reportPath))
if filename == os.path.basename(reportPath):
report_file_path = os.path.join(root, filename)
logging.info('get_detection_file-report_file_path:%s' % report_file_path)
@@ -821,7 +964,7 @@ class CNCprocessing(models.Model):
_order = 'sequence_number,id'
cnc_id = fields.Many2one('ir.attachment')
sequence_number = fields.Char('序号')
sequence_number = fields.Integer('序号')
program_name = fields.Char('程序名')
functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型')
cutting_tool_name = fields.Char('刀具名称')
@@ -841,10 +984,12 @@ class CNCprocessing(models.Model):
# mrs下发编程单创建CNC加工
def cnc_processing_create(self, cnc_workorder, ret, program_path, program_path_tmp):
cnc_processing = None
for obj in ret['programming_list']:
workorder = self.env['mrp.workorder'].search([('production_id.name', '=', ret['production_order_no']),
('processing_panel', '=', obj['processing_panel']),
('routing_type', '=', 'CNC加工')])
workorder = self.env['mrp.workorder'].search(
[('production_id.name', '=', cnc_workorder.name),
('processing_panel', '=', obj['processing_panel']),
('routing_type', '=', 'CNC加工')])
logging.info('workorder:%s' % workorder.id)
if obj['program_name'] in program_path:
logging.info('obj:%s' % obj['program_name'])
@@ -865,11 +1010,27 @@ class CNCprocessing(models.Model):
'program_path': program_path.replace('/tmp', '/home/ftp/ftp_root/NC')
})
cnc_processing.get_cnc_processing_file(program_path_tmp, cnc_processing, program_path)
# cnc_workorder.state = 'done'
cnc_workorder.work_state = '已编程'
cnc_workorder.programming_state = '已编程'
# cnc_workorder.time_ids.date_end = datetime.now()
# cnc_workorder.button_finish()
cnc_workorder.write({'programming_state': '已编程', 'work_state': '已编程'})
return cnc_processing
def _json_cnc_processing(self, obj):
cnc_processing_str = (0, 0, {
'sequence_number': obj['sequence_number'],
'program_name': obj['program_name'],
'cutting_tool_name': obj['cutting_tool_name'],
'cutting_tool_no': obj['cutting_tool_no'],
'processing_type': obj['processing_type'],
'margin_x_y': obj['margin_x_y'],
'margin_z': obj['margin_z'],
'depth_of_processing_z': obj['depth_of_processing_z'],
'cutting_tool_extension_length': obj['cutting_tool_extension_length'],
'cutting_tool_handle_type': obj['cutting_tool_handle_type'],
'estimated_processing_time': obj['estimated_processing_time'],
'program_path': obj['program_path'],
'cnc_id': obj['cnc_id'].id,
'remark': obj['remark']
})
return cnc_processing_str
# 根据程序名和加工面匹配到ftp里对应的Nc程序名,可优化为根据cnc_processing.program_path进行匹配
def get_cnc_processing_file(self, serverdir, cnc_processing, program_path):
@@ -952,16 +1113,21 @@ class SfWorkOrderBarcodes(models.Model):
def on_barcode_scanned(self, barcode):
workorder = self.env['mrp.workorder'].browse(self.ids)
# workorder = self.env['mrp.workorder'].search(
# [('routing_type', '=', '装夹预调'), ('production_id', '=', self.production_id.id)])
workorder_old = self.env['mrp.workorder'].search([('rfid_code', '=', barcode)])
if workorder_old:
raise UserError('该托盘已绑定工件,请先解除绑定!!!')
# workorder_preset = self.env['mrp.workorder'].search(
# [('routing_type', '=', '装夹预调'), ('rfid_code', '=', barcode)])
workorder_olds = self.env['mrp.workorder'].search(
[('routing_type', '=', '装夹预调'), ('rfid_code', '=', barcode)])
if workorder_olds:
name = ''
for workorder in workorder_olds:
name = '%s %s' % (name, workorder.production_id.name)
raise UserError('该托盘已绑定【%s】制造订单,请先解除绑定!!!' % name)
if workorder:
if workorder.routing_type == '装夹预调':
if workorder.state in ['done']:
work_state = {'done': '已完工'}
raise UserError('装夹%s,请勿重复扫码' % work_state.get(workorder.state))
lots = self.env['stock.lot'].sudo().search([('rfid', '=', barcode)])
logging.info("托盘信息:%s" % lots)
if lots:
@@ -983,39 +1149,11 @@ class SfWorkOrderBarcodes(models.Model):
for item in workorder_rfid:
item.write({'rfid_code': barcode})
logging.info("Rfid绑定成功")
else:
embryo_stock_lot = self.env['stock.lot'].search([('name', '=', barcode)])
if embryo_stock_lot:
embryo_stock_move_line = self.env['stock.move.line'].search(
[('product_id', '=', embryo_stock_lot.product_id.id),
('reference', '=', workorder.production_id.name),
('lot_id', '=', embryo_stock_lot.id),
('product_category_name', '=', '坯料')])
if embryo_stock_move_line:
bom_production = self.env['mrp.production'].search(
[('product_id', '=', embryo_stock_lot.product_id.id),
('origin', '=', workorder.production_id.name)], limit=1, order='id asc')
workpiece_delivery = self.env['sf.workpiece.delivery'].search(
[('workorder_id', '=', workorder.id)], limit=1, order='id asc')
if workpiece_delivery:
embryo_workpiece_code = workpiece_delivery.workpiece_code
if bom_production:
if workpiece_delivery.workpiece_code and bom_production.name not in \
workpiece_delivery.workpiece_code:
embryo_workpiece_code = workpiece_delivery.workpiece_code + ',' + \
bom_production.name
if not workpiece_delivery.workpiece_code:
embryo_workpiece_code = bom_production.name
workpiece_delivery.write({'workpiece_code': embryo_workpiece_code})
else:
raise UserError('工件生产线不一致,请重新确认')
else:
workorder_rfid = self.env['mrp.workorder'].search(
[('production_id', '=', workorder.production_id.id)])
if workorder_rfid:
for item in workorder_rfid:
if item.state == "progress":
item.write({'rfid_code': barcode})
raise UserError('该Rfid【%s】绑定的是【%s】, 不是托盘!!!' % (barcode, lot.product_id.name))
self.process_state = '待检测'
self.date_start = datetime.now()
else:
raise UserError('该托盘信息不存在!!!')
# stock_move_line = self.env['stock.move.line'].search([('lot_name', '=', barcode)])
# if stock_move_line.product_id.categ_type == '夹具':
@@ -1072,78 +1210,198 @@ class WorkPieceDelivery(models.Model):
_name = "sf.workpiece.delivery"
_description = '工件配送'
name = fields.Char('单据编号')
workorder_id = fields.Many2one('mrp.workorder', string='工单', readonly=True)
production_id = fields.Many2one('mrp.production', string='制造订单', readonly=True)
production_line_id = fields.Many2one('sf.production.line', compute='_compute_production_line_id',
string='目的生产线', readonly=True,
store=True)
workorder_state = fields.Selection(related='workorder_id.state', string='工单状态')
rfid_code = fields.Char(related='workorder_id.rfid_code', string='rfid', store=True)
production_id = fields.Many2one('mrp.production', string='制造订单号', readonly=True)
production_line_id = fields.Many2one('sf.production.line', string='目的生产线')
plan_start_processing_time = fields.Datetime('计划开始加工时间', readonly=True)
workpiece_code = fields.Char('同运工件编码')
route_id = fields.Many2one('sf.agv.task.route', '任务路线')
feeder_station_start_id = fields.Many2one('sf.agv.site', '起点接驳站')
feeder_station_destination_id = fields.Many2one('sf.agv.site', '目的接驳站')
task_delivery_time = fields.Datetime('任务下发时间')
task_completion_time = fields.Datetime('任务完成时间')
type = fields.Selection(
[('上产线', '上产线'), ('下产线', '下产线'), ('运送空料架', '运送空料架')], string='类型')
delivery_duration = fields.Float('配送时长', compute='_compute_delivery_duration')
status = fields.Selection(
[('待下发', '待下发'), ('待配送', '待配送'), ('已配送', '已配送')], string='状态',
default='下发')
production_line_state = fields.Selection(
[('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')],
string='上/下产线', default='待上产线')
cnc_program_down_state = fields.Selection([('待下发', '待下发'), ('已下发', '已下发')],
string='CNC程序下发状态', default='待下发')
agv_task_code = fields.Char('agv任务单号')
[('待下发', '待下发'), ('待配送', '待配送'), ('已配送', '已配送')], string='状态', default='待下发')
is_cnc_program_down = fields.Boolean('程序是否下发', default=False)
active = fields.Boolean(string="有效", default=True)
@api.model
def create(self, vals):
if vals.get('name', '/') == '/' or vals.get('name', '/') is False:
vals['name'] = self.env['ir.sequence'].next_by_code('sf.workpiece.delivery') or '/'
obj = super(WorkPieceDelivery, self).create(vals)
return obj
def action_delivery_history(self):
return {
'name': _('配送历史'),
'type': 'ir.actions.act_window',
'view_mode': 'tree',
'res_model': 'sf.workpiece.delivery',
'view_id': self.env.ref('sf_manufacturing.sf_workpiece_delivery_empty_racks_tree').id,
'domain': [('type', '=', '运送空料架'), ('route_id', '=', self.route_id.id), ('name', 'ilike', 'WDO')]
}
@api.onchange('route_id')
def onchage_route(self):
def onchange_route(self):
if self.route_id:
self.feeder_station_start_id = self.route_id.start_site_id
self.feeder_station_destination_id = self.route_id.end_site_id
self.feeder_station_start_id = self.route_id.start_site_id.id
self.feeder_station_destination_id = self.route_id.end_site_id.id
# 工件配送
def button_delivery(self):
if self.cnc_program_down_state == '待下发':
if self.route_id:
if self.status == '待下发':
return {
'name': _('确认'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'sf.workpiece.delivery.wizard',
'target': 'new',
'context': {
'default_delivery_id': self.id,
}}
delivery_ids = []
production_ids = []
is_cnc_down = 0
is_not_production_line = 0
is_not_route = 0
same_production_line_id = None
same_route_id = None
down_status = '待下发'
production_type = None
num = 0
for item in self:
num += 1
if production_type is None:
production_type = item.type
if item.type == "运送空料架":
if num >= 2:
raise UserError('仅选择一条路线进行配送,请重新选择')
else:
raise UserError('状态为【待下发】的工件记录可进行配送')
delivery_ids.append(item.id)
else:
raise UserError('请选择任务路线再进行配送')
if num > 4:
raise UserError('仅限于配送1-4个制造订单请重新选择')
if item.status in ['待配送', '已配送']:
raise UserError('请选择状态为【待下发】的制造订单进行配送')
if item.route_id:
if same_route_id is None:
same_route_id = item.route_id.id
if item.route_id.id != same_route_id:
is_not_route += 1
# else:
# raise UserError('请选择【任务路线】再进行配送')
if production_type != item.type:
raise UserError('请选择类型为%s的制造订单进行配送' % production_type)
if down_status != item.status:
up_workpiece = self.search([('type', '=', '上产线'), ('production_id', '=', item.production_id),
('status', '=', '待下发')])
if up_workpiece:
raise UserError('您所选择的制造订单暂未上产线,请在上产线后再进行配送')
else:
raise UserError('请选择状态为【待下发】的制造订单进行配送')
if same_production_line_id is None:
same_production_line_id = item.production_line_id.id
if item.production_line_id.id != same_production_line_id:
is_not_production_line += 1
if item.is_cnc_program_down is False:
is_cnc_down += 1
if is_cnc_down == 0 and is_not_production_line == 0 and is_not_route == 0:
delivery_ids.append(item.id)
production_ids.append(item.production_id.id)
if is_cnc_down >= 1:
raise UserError('您所选择制造订单的【CNC程序】暂未下发请在程序下发后再进行配送')
if is_not_production_line >= 1:
raise UserError('您所选择制造订单的【目的生产线】不一致,请重新确认')
if is_not_route >= 1:
raise UserError('您所选择制造订单的【任务路线】不一致,请重新确认')
is_free = self._check_avgsite_state()
if is_free is True:
if delivery_ids:
return {
'name': _('确认'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'sf.workpiece.delivery.wizard',
'target': 'new',
'context': {
'default_delivery_ids': [(6, 0, delivery_ids)],
'default_production_ids': [(6, 0, production_ids)],
'default_destination_production_line_id': same_production_line_id,
'default_route_id': same_route_id,
'default_type': production_type,
}}
else:
raise UserError(_("该制造订单还未下发CNC程序单,无法进行工件配送"))
if production_type == '运送空料架':
raise UserError("您所选择的【任务路线】的【终点接驳站】已占用,请在该接驳站空闲时进行配送")
else:
raise UserError(
"您所选择制造订单的【任务路线】的【终点接驳站】已占用,请在该接驳站空闲时或选择其他路线进行配送")
# 验证agv站点是否可用
def _check_avgsite_state(self):
is_free = False
agv_site = self.env['sf.agv.site'].search([])
if agv_site:
agv_site.update_site_state()
for item in self:
logging.info('工件配送-起点状态:%s-%s' % (
item.feeder_station_start_id.name, item.feeder_station_start_id.state))
logging.info('工件配送-终点状态:%s-%s' % (
item.feeder_station_destination_id.name, item.feeder_station_destination_id.state))
if item.type in ['上产线', '下产线']:
if (
item.feeder_station_start_id.state == '占用' and item.feeder_station_destination_id.state == '空闲') or (
item.feeder_station_start_id.state == '空闲' and item.feeder_station_destination_id.state == '空闲'):
is_free = True
else:
if item.feeder_station_destination_id.state == '空闲':
is_free = True
logging.info('is_free:%s' % is_free)
return is_free
# 配送至avg小车
def _delivery_avg(self):
config = self.env['res.config.settings'].get_values()
positionCode_Arr = []
if self.feeder_station_start_id:
delivery_Arr = []
feeder_station_start = None
feeder_station_destination = None
route_id = None
for item in self:
if route_id is None:
route_id = item.route_id.id
if feeder_station_start is None:
feeder_station_start = item.feeder_station_start_id
if feeder_station_destination is None:
feeder_station_destination = item.feeder_station_destination_id
if item.type in ['上产线', '下产线']:
item.route_id = route_id
item.feeder_station_start_id = feeder_station_start.id
item.feeder_station_destination_id = feeder_station_destination.id
delivery_Arr.append(item.name)
else:
self = self.create(
{'name': self.env['ir.sequence'].next_by_code('sf.workpiece.delivery'),
'route_id': self.route_id.id,
'feeder_station_start_id': self.feeder_station_start_id.id,
'feeder_station_destination_id': self.feeder_station_destination_id.id})
delivery_Arr.append(self.name)
delivery_str = ','.join(map(str, delivery_Arr))
if feeder_station_start is not None:
positionCode_Arr.append({
'positionCode': self.feeder_station_start_id.name,
'positionCode': feeder_station_start.name,
'code': '00'
})
if self.feeder_station_destination_id:
if feeder_station_destination is not None:
positionCode_Arr.append({
'positionCode': self.feeder_station_destination_id.name,
'positionCode': feeder_station_destination.name,
'code': '00'
})
res = {'reqCode': self.production_id.name, 'reqTime': '', 'clientCode': '', 'tokenCode': '',
res = {'reqCode': delivery_str, 'reqTime': '', 'clientCode': '', 'tokenCode': '',
'taskTyp': 'F01', 'ctnrTyp': '', 'ctnrCode': '', 'wbCode': config['wbcode'],
'positionCodePath': positionCode_Arr,
'podCode': '',
'podDir': '', 'materialLot': '', 'priority': '', 'taskCode': '', 'agvCode': '', 'materialLot': '',
'data': ''}
try:
config['agv_rcs_url'] = 'http://172.16.10.114:8182/rcms/services/rest/hikRpcService/genAgvSchedulingTask'
logging.info('AGV请求路径:%s' % config['agv_rcs_url'])
logging.info('AGV-json:%s' % res)
headers = {'Content-Type': 'application/json'}
@@ -1151,20 +1409,22 @@ class WorkPieceDelivery(models.Model):
ret = ret.json()
logging.info('config-ret:%s' % ret)
if ret['code'] == 0:
if self.production_id.name == ret['reqCode']:
self.write(
{'task_delivery_time': fields.Datetime.now(), 'status': '待配送', 'agv_task_code': ret['data']})
req_codes = ret['reqCode'].split(',')
for delivery_item in self:
for req_code in req_codes:
if delivery_item.name == req_code.strip():
logging.info('delivery_item-name:%s' % delivery_item.name)
delivery_item.write({
'task_delivery_time': fields.Datetime.now(),
'status': '待配送'
})
if delivery_item.type == "上产线":
delivery_item.workorder_id.write({'is_delivery': True})
else:
raise UserError(ret['message'])
except Exception as e:
logging.info('config-e:%s' % e)
raise UserError("工件配送请求agv失败")
@api.onchange('production_id.production_line_id')
def _compute_production_line_id(self):
if self.production_id.production_line_id:
self.production_line_id = self.production_id.production_line_id.id
self.plan_start_processing_time = self.production_id.plan_start_processing_time
raise UserError("工件配送请求agv失败:%s" % e)
@api.depends('task_delivery_time', 'task_completion_time')
def _compute_delivery_duration(self):
@@ -1198,10 +1458,12 @@ class CMMprogram(models.Model):
program_path = fields.Char('程序文件路径')
def cmm_program_create(self, ret, program_path, program_path_tmp):
cmm_program = None
for obj in ret['programming_list']:
workorder = self.env['mrp.workorder'].search([('production_id.name', '=', ret['production_order_no']),
('processing_panel', '=', obj['processing_panel']),
('routing_type', '=', 'CNC加工')])
workorder = self.env['mrp.workorder'].search(
[('production_id.name', '=', ret['production_order_no'].split(',')[0]),
('processing_panel', '=', obj['processing_panel']),
('routing_type', '=', 'CNC加工')])
if obj['program_name'] in program_path:
logging.info('obj:%s' % obj['program_name'])
cmm_program = self.sudo().create({
@@ -1221,6 +1483,7 @@ class CMMprogram(models.Model):
'program_path': program_path.replace('/tmp', '')
})
cmm_program.get_cmm_program_file(program_path_tmp, cmm_program, program_path)
return cmm_program
# 根据程序名和加工面匹配到ftp里对应的cmm程序名
def get_cmm_program_file(self, serverdir, cmm_program, program_path):

View File

@@ -7,8 +7,8 @@ import os
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
from odoo.modules import get_resource_path
# from OCC.Extend.DataExchange import read_step_file
# from OCC.Extend.DataExchange import write_stl_file
from OCC.Extend.DataExchange import read_step_file
from OCC.Extend.DataExchange import write_stl_file
class ResProductMo(models.Model):
@@ -179,6 +179,12 @@ class ResProductMo(models.Model):
# ('standard_library_id', '=', self.cutting_tool_model_id.id)],
# }
# def name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
# # if self._context.get('is_sale_order_line'):
# # domain = [('sale_ok', '=', True), ('categ_type', '=', '成品')]
# # return self._search(expression.AND([domain, args]), limit=limit, access_rights_uid=name_get_uid)
# return super(product.template, self)._name_search(name, args, operator, limit, name_get_uid)
@api.onchange('specification_id')
def _onchange_specification(self):
if self.specification_id:
@@ -399,7 +405,7 @@ class ResProductMo(models.Model):
cutting_speed_ids = fields.One2many('sf.cutting.speed', 'product_template_id', string='切削速度Vc')
feed_per_tooth_ids = fields.One2many('sf.feed.per.tooth', 'product_template_id', string='每齿走刀量fz')
cutting_tool_diameter = fields.Float('直径(mm)')
cutting_tool_diameter = fields.Float('刀具直径(mm)')
cutting_tool_rear_angle = fields.Integer('后角(°)')
cutting_tool_main_included_angle = fields.Integer('主偏角(°)')
# 适用夹头型号可以多选
@@ -652,8 +658,12 @@ class ResProductMo(models.Model):
return attachment
# 创建坯料
def no_bom_product_create(self, product_id, item, order_id, route_type, i):
def no_bom_product_create(self, product_id, item, order_id, route_type, i, finish_product):
no_bom_copy_product_id = product_id.with_user(self.env.ref("base.user_admin")).copy()
# if finish_product.model_process_parameters_ids:
# surface_technology = self.env['stock.route'].sudo().search([('name', '=', '表面工艺外协')])
# if surface_technology:
# no_bom_copy_product_id.route_ids |= surface_technology
no_bom_copy_product_id.product_tmpl_id.active = True
materials_id = self.env['sf.production.materials'].search(
[('materials_no', '=', item['texture_code'])])

View File

@@ -202,7 +202,7 @@ class StockRule(models.Model):
productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create(
productions_values)
self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
# self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
'''
创建工单
@@ -213,6 +213,52 @@ class StockRule(models.Model):
(
p.move_dest_ids.procure_method != 'make_to_order' and not
p.move_raw_ids and not p.workorder_ids)).action_confirm()
for production_item in productions:
process_parameter_workorder = self.env['mrp.workorder'].search(
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', production_item.id),
('is_subcontract', '=', True)])
if process_parameter_workorder:
is_pick = False
consecutive_workorders = []
m = 0
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.id)
for i in range(len(sorted_workorders) - 1):
if m == 0:
is_pick = False
if sorted_workorders[i].supplier_id.id == sorted_workorders[i + 1].supplier_id.id and \
sorted_workorders[i].is_subcontract == sorted_workorders[i + 1].is_subcontract and \
sorted_workorders[i].id == sorted_workorders[i + 1].id - 1:
if sorted_workorders[i] not in consecutive_workorders:
consecutive_workorders.append(sorted_workorders[i])
consecutive_workorders.append(sorted_workorders[i + 1])
m += 1
continue
else:
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
production_item)
if sorted_workorders[i] in consecutive_workorders:
is_pick = True
consecutive_workorders = []
m = 0
# 当前面的连续工序生成对应的外协出入库单再生成当前工序的外协出入库单
if is_pick is False:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i],
production_item)
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
production_item)
if sorted_workorders[i] in consecutive_workorders:
is_pick = True
consecutive_workorders = []
m = 0
if m == len(consecutive_workorders) - 1 and m != 0:
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production_item)
if is_pick is False and m == 0:
if len(sorted_workorders) == 1:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production_item)
else:
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production_item)
for production in productions:
'''
@@ -248,7 +294,7 @@ class StockRule(models.Model):
sale_order = self.env['sale.order'].sudo().search([('name', '=', production.origin)])
if sale_order:
sale_order.write({'schedule_status': 'to schedule'})
# sale_order.write({'schedule_status': 'to schedule'})
self.env['sf.production.plan'].sudo().with_company(company_id).create({
'name': production.name,
'order_deadline': sale_order.deadline_of_delivery,
@@ -269,6 +315,13 @@ class ProductionLot(models.Model):
rfid = fields.Char('Rfid', readonly=True)
product_specification = fields.Char('规格', compute='_compute_product_specification', store=True)
def search_lot_put_rfid(self):
# 使用SQL将所有刀柄Rfid不满十位的值在前方补零
self.env.cr.execute(
'''UPDATE stock_lot SET rfid = LPAD(rfid, 10, '0') WHERE rfid IS NOT NULL AND LENGTH(rfid) < 10'''
)
self.env.cr.commit()
@api.depends('product_id')
def _compute_product_specification(self):
for stock in self:
@@ -418,12 +471,24 @@ class ProductionLot(models.Model):
# 'target': 'self',
# }
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
if vals.get('rfid'):
lots = self.env['stock.lot'].search([('rfid', '=', vals['rfid'])])
if lots:
for lot in lots:
raise ValidationError('Rfid【%s】已被序列号为【%s】的【%s】产品占用!' % (
lot.rfid, lot.name, lot.product_id.name))
records = super(ProductionLot, self).create(vals_list)
return records
class StockPicking(models.Model):
_inherit = 'stock.picking'
workorder_in_id = fields.One2many('mrp.workorder', 'picking_in_id')
workorder_out_id = fields.One2many('mrp.workorder', 'picking_out_id')
# workorder_in_id = fields.One2many('mrp.workorder', 'picking_in_id')
# workorder_out_id = fields.One2many('mrp.workorder', 'picking_out_id')
# 设置外协出入单的名称
def _get_name_Res(self, rescode):
@@ -437,72 +502,84 @@ class StockPicking(models.Model):
return '%s%s' % (rescode, num)
def button_validate(self):
# 出库单验证
if self.workorder_out_id:
workorder_in = self.workorder_out_id.filtered(lambda p: p.state == 'progress' and p.is_subcontract is True)
if workorder_in:
picking_in = self.sudo().search([('id', '=', workorder_in.picking_in_id.id)])
if picking_in:
picking_in.write({'state': 'assigned'})
else:
workorder_subcontract = self.workorder_out_id.filtered(
lambda p: p.state == 'pending' and p.is_subcontract is True)
if workorder_subcontract:
raise UserError(
_('该出库单里源单据内的单号为%s的工单还未开始,不能进行验证操作!' % workorder_subcontract[
0].name))
move_out = self.env['stock.move'].search(
[('location_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
('location_dest_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'VL-SPOC')]).id),
('origin', '=', self.origin)])
if self.id == move_out.picking_id.id:
if move_out.move_line_ids.workorder_id.state not in ['progress']:
raise UserError(
_('该出库单里源单据内的单号为%s的工单还未开始,不能进行验证操作!' % move_out.move_line_ids.workorder_id.name))
# 入库单验证
if self.workorder_in_id:
workorder_out = self.workorder_in_id.filtered(lambda p: p.state == 'progress' and p.is_subcontract is True)
if workorder_out:
picking_out = self.sudo().search([('id', '=', workorder_out.picking_out_id.id)])
if picking_out.state != 'done':
move_in = self.env['stock.move'].search(
[('location_dest_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
('location_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'VL-SPOC')]).id),
('origin', '=', self.origin)])
if self.location_id == move_in.location_id and self.location_dest_id == move_in.location_dest_id:
if move_out.origin == move_in.origin:
if move_out.picking_id.state != 'done':
raise UserError(
_('该入库单对应的单号为%s的出库单还未完成,不能进行验证操作!' % picking_out.name))
_('该入库单对应的单号为%s的出库单还未完成,不能进行验证操作!' % move_out.picking_id.name))
res = super().button_validate()
# 采购单验证(夹具)
# for item in self.move_ids_without_package:
# if item.quantity_done > 0:
# if item.product_id.categ_type == '夹具':
# item._register_fixture()
# elif item.product_id.categ_type == '刀具':
# item._register_cutting_tool()
if res is True:
if self.id == move_out.picking_id.id:
if move_out.move_line_ids.workorder_id.state == 'progress':
move_in = self.env['stock.move'].search(
[('location_dest_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
('location_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'VL-SPOC')]).id),
('origin', '=', self.origin)])
# purchase = self.env['purchase.order'].search([('origin', '=', self.origin)])
if move_in:
move_in.write({'state': 'assigned'})
purchase = self.env['purchase.order'].search([('origin', '=', self.origin)])
self.env['stock.move.line'].create(move_in.get_move_line(purchase, None))
return res
# 创建 外协出库入单
def create_outcontract_picking(self, sorted_workorders_arr, item):
m = 0
for sorted_workorders in sorted_workorders_arr:
pick_ids = []
if m == 0:
outcontract_stock_move = self.env['stock.move'].search(
[('workorder_id', '=', sorted_workorders.id), ('production_id', '=', item.id)])
if not outcontract_stock_move:
location_id = self.env.ref(
'sf_manufacturing.stock_location_locations_virtual_outcontract').id,
new_picking = True
location_id = self.env['stock.location'].search(
[('barcode', 'ilike', 'VL-SPOC')]).id,
location_dest_id = self.env['stock.location'].search(
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id,
outcontract_picking_type_in = self.env.ref(
'sf_manufacturing.outcontract_picking_in').id,
outcontract_picking_type_out = self.env.ref(
'sf_manufacturing.outcontract_picking_out').id,
moves_in = self.env['stock.move'].sudo().create(
item._get_stock_move_values_Res(item, location_id, location_dest_id,
outcontract_picking_type_in))
moves_out = self.env['stock.move'].sudo().create(
item._get_stock_move_values_Res(item, location_dest_id, location_id,
outcontract_picking_type_out))
new_picking = True
picking_in = self.create(
moves_in._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCIN/'))
self.env['stock.move']._get_stock_move_values_Res(item, location_dest_id, location_id,
outcontract_picking_type_out))
picking_out = self.create(
moves_out._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCOUT/'))
moves_in.write({'picking_id': picking_in.id, 'state': 'confirmed'})
moves_out.write({'picking_id': picking_out.id, 'state': 'confirmed'})
moves_in._assign_picking_post_process(new=new_picking)
pick_ids.append(picking_out.id)
moves_out.write(
{'picking_id': picking_out.id, 'state': 'waiting', 'workorder_id': sorted_workorders.id})
moves_out._assign_picking_post_process(new=new_picking)
moves_in = self.env['stock.move'].sudo().create(
self.env['stock.move']._get_stock_move_values_Res(item, location_id, location_dest_id,
outcontract_picking_type_in))
picking_in = self.create(
moves_in._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCIN/'))
pick_ids.append(picking_in.id)
moves_in.write(
{'picking_id': picking_in.id, 'state': 'waiting', 'workorder_id': sorted_workorders.id})
moves_in._assign_picking_post_process(new=new_picking)
m += 1
sorted_workorders.write({'picking_in_id': picking_in.id, 'picking_out_id': picking_out.id})
sorted_workorders.write({'picking_ids': [(6, 0, pick_ids)]})
class ReStockMove(models.Model):
@@ -512,6 +589,23 @@ class ReStockMove(models.Model):
materiel_width = fields.Float(string='物料宽度', digits=(16, 4))
materiel_height = fields.Float(string='物料高度', digits=(16, 4))
def _get_stock_move_values_Res(self, item, location_src_id, location_dest_id, picking_type_id):
route = self.env['stock.route'].sudo().search([('name', '=', '表面工艺外协')])
move_values = {
'name': '',
'company_id': item.company_id.id,
'product_id': item.bom_id.bom_line_ids.product_id.id,
'product_uom': item.bom_id.bom_line_ids.product_uom_id.id,
'product_uom_qty': 1.0,
'location_id': location_src_id,
'location_dest_id': location_dest_id,
'origin': item.name,
# 'route_ids': False if not route else [(4, route.id)],
'date_deadline': datetime.now(),
'picking_type_id': picking_type_id,
}
return move_values
def _get_new_picking_values_Res(self, item, sorted_workorders, rescode):
return {
'name': self.env['stock.picking']._get_name_Res(rescode),
@@ -526,6 +620,22 @@ class ReStockMove(models.Model):
'state': 'confirmed',
}
def get_move_line(self, purchase, sorted_workorders):
return {
'move_id': self.id,
'product_id': self.product_id.id,
'product_uom_id': self.product_uom.id,
'location_id': self.picking_id.location_id.id,
'location_dest_id': self.picking_id.location_dest_id.id,
'picking_id': self.picking_id.id,
'reserved_uom_qty': 1.0,
'lot_id': purchase.picking_ids.move_line_ids.lot_id.id,
'company_id': self.company_id.id,
'workorder_id': '' if not sorted_workorders else sorted_workorders.id,
'production_id': '' if not sorted_workorders else sorted_workorders.production_id.id,
'state': 'assigned',
}
def print_serial_numbers(self):
if not self.next_serial:
raise UserError(_("请先分配序列号再进行打印"))
@@ -605,15 +715,15 @@ class ReStockMove(models.Model):
class ReStockQuant(models.Model):
_inherit = 'stock.quant'
def action_apply_inventory(self):
inventory_diff_quantity = self.inventory_diff_quantity
super(ReStockQuant, self).action_apply_inventory()
if inventory_diff_quantity >= 1:
stock = self.env['stock.move'].search([('product_id', '=', self.product_id.id), ('is_inventory', '=', True),
('reference', '=', '更新的产品数量'), ('state', '=', 'done')],
limit=1, order='id desc')
if self.product_id.categ_type == '夹具':
stock._register_fixture()
elif self.product_id.categ_type == '刀具':
stock._register_cutting_tool()
return True
# def action_apply_inventory(self):
# inventory_diff_quantity = self.inventory_diff_quantity
# super(ReStockQuant, self).action_apply_inventory()
# if inventory_diff_quantity >= 1:
# stock = self.env['stock.move'].search([('product_id', '=', self.product_id.id), ('is_inventory', '=', True),
# ('reference', '=', '更新的产品数量'), ('state', '=', 'done')],
# limit=1, order='id desc')
# if self.product_id.categ_type == '夹具':
# stock._register_fixture()
# elif self.product_id.categ_type == '刀具':
# stock._register_cutting_tool()
# return True

View File

@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
# Part of SmartGo. See LICENSE file for full copyright and licensing details.
import base64
from io import BytesIO
from odoo import api, fields, models, SUPERUSER_ID, _
from pystrich.code128 import Code128Encoder
class Tray(models.Model):
_inherit = 'sf.tray'
_description = '托盘'
qr_image = fields.Binary(string="托盘二维码", compute='compute_qr_image')
production_id = fields.Many2one('mrp.production', string='制造订单',
related='workorder_id.production_id'
)
workorder_id = fields.Many2one('mrp.workorder', string="工单"
)
@api.onchange('production_id')
def updateTrayState(self):
if self.workorder_id != False and self.create_date != False:
self.state = '占用'
else:
self.state = '空闲'
def unclamp(self):
self.workorder_id = False
self.production_id = False
self.state = '空闲'
@api.depends('code')
def compute_qr_image(self):
for item in self:
if not item.code:
item.qr_image = False
continue
# 根据code动态生成二维码图片
# qr = qrcode.QRCode(
# version=1,
# error_correction=qrcode.constants.ERROR_CORRECT_L,
# box_size=10,
# border=4,
# )
# qr.add_data(item.code)
# qr.make(fit=True)
# img = qr.make_image()
# 生成条形码文件
# bar = barcode.get("ean13", "123456789102", writer=ImageWriter())
# a = bar.get_fullcode()
# b = bar.save('occ')
# 生成条形码图片
partner_encoder = Code128Encoder(item.code)
# 转换bytes流
temp = BytesIO()
partner_encoder.save(temp)
# img.save(temp, format='PNG')
qr_image = base64.b64encode(temp.getvalue())
item.qr_image = qr_image

View File

@@ -0,0 +1,74 @@
<odoo>
<data>
<!-- 托盘码打印尺寸-->
<record id="sf_tray1" model="report.paperformat">
<field name="name">Dymo Label Sheet</field>
<field name="default" eval="True"/>
<field name="format">custom</field>
<field name="page_height">100</field>
<field name="page_width">60</field>
<field name="orientation">Landscape</field>
<field name="margin_top">0</field>
<field name="margin_bottom">0</field>
<field name="margin_left">0</field>
<field name="margin_right">0</field>
<field name="disable_shrinking" eval="True"/>
<field name="dpi">96</field>
</record>
<!-- 托盘码打印动作-->
<record id="label_sf_tray_code" model="ir.actions.report">
<field name="name">打印条形码</field>
<field name="model">sf.tray</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">sf_manufacturing.sf_tray_template</field>
<field name="report_file">sf_manufacturing.sf_tray_template</field>
<field name="binding_model_id" ref="model_sf_tray"/>
<field name="binding_type">report</field>
<field name="paperformat_id" ref="sf_manufacturing.sf_tray1"/>
</record>
<!-- 托盘码打印模板-->
<template id="sf_tray_template">
<t t-call="web.html_container">
<t t-call="web.external_layout">
<t t-foreach="docs" t-as="o">
<div class="page">
<div t-field="o.code"
t-options="{'widget': 'barcode', 'width': 600, 'height': 100, 'img_style': 'width:350px;height:60px'}"/>
<div t-field="o.code" style="text-align: center"/>
</div>
</t>
</t>
</t>
</template>
<!-- 产品信息打印动作-->
<record id="label_sf_tray_code1" model="ir.actions.report">
<field name="name">打印产品信息</field>
<field name="model">mrp.workorder</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">sf_manufacturing.sf_tray_template1</field>
<field name="report_file">sf_manufacturing.sf_tray_template1</field>
<field name="binding_model_id" ref="model_mrp_workorder"/>
<field name="binding_type">report</field>
<field name="paperformat_id" ref="sf_manufacturing.sf_tray1"/>
</record>
<!-- 产品信息打印模板-->
<template id="sf_tray_template1">
<t t-call="web.html_container">
<t t-call="web.external_layout">
<t t-foreach="docs" t-as="o">
<div class="page">
<div t-field="o.production_id.name"
t-options="{'widget': 'barcode', 'width': 600, 'height': 100, 'img_style': 'width:350px;height:60px'}"/>
<div t-field="o.production_id" style="text-align: center"/>
</div>
</t>
</t>
</t>
</template>
</data>
</odoo>

View File

@@ -30,7 +30,7 @@ access_mrp_workcenter_group_sf_mrp_user,mrp_workcenter,model_mrp_workcenter,sf_b
access_mrp_workcenter_manager,mrp_workcenter,model_mrp_workcenter,sf_base.group_sf_mrp_manager,1,1,1,0
access_mrp_workcenter_productivity_group_sf_mrp_user,mrp_workcenter_productivity,model_mrp_workcenter_productivity,sf_base.group_sf_mrp_user,1,0,0,0
access_mrp_workcenter_productivity_manager,mrp_workcenter_productivity,model_mrp_workcenter_productivity,sf_base.group_sf_mrp_manager,1,1,1,0
access_sf_workpiece_delivery_group_sf_order_user,sf_workpiece_delivery_group_sf_order_user,model_sf_workpiece_delivery,sf_base.group_sf_order_user,1,1,0,0
access_sf_workpiece_delivery_group_sf_order_user,sf_workpiece_delivery_group_sf_order_user,model_sf_workpiece_delivery,sf_base.group_sf_order_user,1,1,1,0
access_sf_workpiece_delivery_group_sf_equipment_user,sf_workpiece_delivery_group_sf_equipment_user,model_sf_workpiece_delivery,sf_base.group_sf_equipment_user,1,1,0,0
access_sf_workpiece_delivery_manager,sf_workpiece_delivery,model_sf_workpiece_delivery,sf_base.group_sf_mrp_manager,1,1,0,0
@@ -52,8 +52,8 @@ access_mrp_bom_manager_group_sf_mrp_user,mrp.bom.manager,mrp.model_mrp_bom,sf_ba
access_mrp_bom_line_manager_group_sf_mrp_user,mrp.bom.line.manager,mrp.model_mrp_bom_line,sf_base.group_sf_mrp_user,1,1,1,0
access_mrp_bom_line_group_plan_director,mrp_bom_line_group_plan_director,mrp.model_mrp_bom_line,sf_base.group_plan_director,1,1,1,0
access_mrp_bom_line_group_sale_director,mrp_bom_line_group_sale_director,mrp.model_mrp_bom_line,sf_base.group_sale_director,1,1,1,0
access_mrp_bom_line_group_sale_salemanager,mrp_bom_line_group_sale_salemanager,mrp.model_mrp_bom_line,sf_base.group_sale_salemanager,1,0,1,0
access_mrp_bom_line_group_purchase_director,mrp_bom_line_group_purchase_director,mrp.model_mrp_bom_line,sf_base.group_purchase_director,1,1,1,0
access_mrp_bom_byproduct_manager_group_sf_mrp_user,mrp.bom.byproduct manager,mrp.model_mrp_bom_byproduct,sf_base.group_sf_mrp_user,1,1,1,0
access_mrp_production_stock_worker,mrp.production stock_worker,mrp.model_mrp_production,stock.group_stock_user,1,0,0,0
access_product_product_user_group_sf_mrp_user,product.product user,product.model_product_product,sf_base.group_sf_mrp_user,1,0,0,0
@@ -104,7 +104,7 @@ access_mrp_production_split_line_group_sf_mrp_user,access.mrp.production.split.l
access_mrp_workcenter_capacity_manager_group_sf_mrp_user,mrp.workcenter.capacity.manager,mrp.model_mrp_workcenter_capacity,sf_base.group_sf_mrp_user,1,1,1,0
access_mrp_production_group_plan_dispatch,mrp_production,model_mrp_production,sf_base.group_plan_dispatch,1,0,0,0
access_mrp_production_group_plan_dispatch,mrp_production,model_mrp_production,sf_base.group_plan_dispatch,1,1,0,0
access_mrp_workorder,mrp_workorder,model_mrp_workorder,sf_base.group_plan_dispatch,1,1,1,0
access_sf_production_line_group_plan_dispatch,sf.production.line,model_sf_production_line,sf_base.group_plan_dispatch,1,0,0,0
access_sf_production_line_group_plan_director,sf.production.line,model_sf_production_line,sf_base.group_plan_director,1,1,1,0
@@ -126,10 +126,17 @@ access_sf_cnc_processing,sf.cnc.processing,model_sf_cnc_processing,sf_base.group
access_sf_cmm_program_group_plan_dispatch,sf_cmm_program_group_plan_dispatch,model_sf_cmm_program,sf_base.group_plan_dispatch,1,0,0,0
access_mrp_workcenter_productivity,mrp.workcenter.productivity,mrp.model_mrp_workcenter_productivity,sf_base.group_plan_dispatch,1,0,0,0
access_mrp_workcenter_productivity_group_plan_dispatch,mrp.workcenter.productivity.group_plan_dispatch,mrp.model_mrp_workcenter_productivity,sf_base.group_plan_dispatch,1,0,0,0
access_mrp_workcenter_productivity_group_sf_stock_user,mrp.workcenter.productivity.group_sf_stock_user,mrp.model_mrp_workcenter_productivity,sf_base.group_sf_stock_user,1,0,0,0
access_maintenance_equipment_tool_group_plan_dispatch,maintenance.equipment.tool,sf_manufacturing.model_maintenance_equipment_tool,sf_base.group_plan_dispatch,1,0,0,0
access_sf_workpiece_delivery_group_plan_dispatch,sf.workpiece.delivery,sf_manufacturing.model_sf_workpiece_delivery,sf_base.group_plan_dispatch,1,0,0,0
access_sf_workpiece_delivery_group_plan_dispatch,sf_workpiece_delivery_group_plan_dispatch,sf_manufacturing.model_sf_workpiece_delivery,sf_base.group_plan_dispatch,1,1,0,0
access_sf_agv_site_group_sf_order_user,sf_agv_site_group_sf_order_user,model_sf_agv_site,sf_base.group_sf_order_user,1,1,1,0
access_sf_agv_task_route_group_sf_order_user,sf_agv_task_route_group_sf_order_user,model_sf_agv_task_route,sf_base.group_sf_order_user,1,1,1,0
access_sf_agv_task_route_group_sf_order_user,sf_agv_task_route_group_sf_order_user,model_sf_agv_task_route,sf_base.group_sf_order_user,1,1,1,0
access_center_control_interface_log_admin,center_control_interface_log_admin,model_center_control_interface_log,base.group_system,1,1,1,0
access_sf_model_type_group_sf_stock_manager,sf_model_type_group_sf_mrp_manager,model_sf_model_type,sf_base.group_sf_stock_manager,1,0,0,0
access_mrp_bom_byproduct_group_sf_stock_user,mrp_bom_byproduct_group_sf_stock_user,mrp.model_mrp_bom_byproduct,sf_base.group_sf_stock_user,1,0,0,0
access_mrp_bom_byproduct_group_sf_stock_manager,mrp_bom_byproduct_group_sf_mrp_manager,mrp.model_mrp_bom_byproduct,sf_base.group_sf_stock_manager,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
30 access_mrp_workcenter_manager mrp_workcenter model_mrp_workcenter sf_base.group_sf_mrp_manager 1 1 1 0
31 access_mrp_workcenter_productivity_group_sf_mrp_user mrp_workcenter_productivity model_mrp_workcenter_productivity sf_base.group_sf_mrp_user 1 0 0 0
32 access_mrp_workcenter_productivity_manager mrp_workcenter_productivity model_mrp_workcenter_productivity sf_base.group_sf_mrp_manager 1 1 1 0
33 access_sf_workpiece_delivery_group_sf_order_user sf_workpiece_delivery_group_sf_order_user model_sf_workpiece_delivery sf_base.group_sf_order_user 1 1 0 1 0
34 access_sf_workpiece_delivery_group_sf_equipment_user sf_workpiece_delivery_group_sf_equipment_user model_sf_workpiece_delivery sf_base.group_sf_equipment_user 1 1 0 0
35 access_sf_workpiece_delivery_manager sf_workpiece_delivery model_sf_workpiece_delivery sf_base.group_sf_mrp_manager 1 1 0 0
36 access_sf_workpiece_delivery_admin sf_workpiece_delivery_admin model_sf_workpiece_delivery base.group_system 1 1 1 0
52 access_mrp_bom_line_group_plan_director mrp_bom_line_group_plan_director mrp.model_mrp_bom_line sf_base.group_plan_director 1 1 1 0
53 access_mrp_bom_line_group_sale_director mrp_bom_line_group_sale_director mrp.model_mrp_bom_line sf_base.group_sale_director 1 1 1 0
54 access_mrp_bom_line_group_purchase_director access_mrp_bom_line_group_sale_salemanager mrp_bom_line_group_purchase_director mrp_bom_line_group_sale_salemanager mrp.model_mrp_bom_line sf_base.group_purchase_director sf_base.group_sale_salemanager 1 1 0 1 0
55 access_mrp_bom_line_group_purchase_director mrp_bom_line_group_purchase_director mrp.model_mrp_bom_line sf_base.group_purchase_director 1 1 1 0
56 access_mrp_bom_byproduct_manager_group_sf_mrp_user mrp.bom.byproduct manager mrp.model_mrp_bom_byproduct sf_base.group_sf_mrp_user 1 1 1 0
access_mrp_production_stock_worker mrp.production stock_worker mrp.model_mrp_production stock.group_stock_user 1 0 0 0
57 access_product_product_user_group_sf_mrp_user access_mrp_production_stock_worker product.product user mrp.production stock_worker product.model_product_product mrp.model_mrp_production sf_base.group_sf_mrp_user stock.group_stock_user 1 0 0 0
58 access_product_template_user_group_sf_mrp_user access_product_product_user_group_sf_mrp_user product.template user product.product user product.model_product_template product.model_product_product sf_base.group_sf_mrp_user 1 0 0 0
59 access_uom_uom_user access_product_template_user_group_sf_mrp_user uom.uom user_group_sf_mrp_user product.template user uom.model_uom_uom product.model_product_template sf_base.group_sf_mrp_user 1 0 0 0
104 access_mrp_workorder access_mrp_production_group_plan_dispatch mrp_workorder mrp_production model_mrp_workorder model_mrp_production sf_base.group_plan_dispatch 1 1 1 0 0
105 access_sf_production_line_group_plan_dispatch access_mrp_workorder sf.production.line mrp_workorder model_sf_production_line model_mrp_workorder sf_base.group_plan_dispatch 1 0 1 0 1 0
106 access_sf_production_line_group_plan_director access_sf_production_line_group_plan_dispatch sf.production.line model_sf_production_line sf_base.group_plan_director sf_base.group_plan_dispatch 1 1 0 1 0 0
107 access_sf_production_line access_sf_production_line_group_plan_director sf.production.line model_sf_production_line sf_maintenance.sf_group_equipment_user sf_base.group_plan_director 1 1 1 0
108 access_mrp_workcenter access_sf_production_line mrp_workcenter sf.production.line model_mrp_workcenter model_sf_production_line sf_base.group_plan_dispatch sf_maintenance.sf_group_equipment_user 1 1 1 0
109 access_mrp_bom_group_plan_dispatch access_mrp_workcenter mrp.bom mrp_workcenter mrp.model_mrp_bom model_mrp_workcenter sf_base.group_plan_dispatch 1 0 1 0 1 0
110 access_mrp_bom_line access_mrp_bom_group_plan_dispatch mrp.bom.line mrp.bom mrp.model_mrp_bom_line mrp.model_mrp_bom sf_base.group_plan_dispatch 1 0 0 0
126 access_sf_agv_task_route_group_sf_order_user access_sf_workpiece_delivery_group_plan_dispatch sf_agv_task_route_group_sf_order_user sf_workpiece_delivery_group_plan_dispatch model_sf_agv_task_route sf_manufacturing.model_sf_workpiece_delivery sf_base.group_sf_order_user sf_base.group_plan_dispatch 1 1 1 0 0
127 access_sf_agv_site_group_sf_order_user sf_agv_site_group_sf_order_user model_sf_agv_site sf_base.group_sf_order_user 1 1 1 0
128 access_sf_agv_task_route_group_sf_order_user sf_agv_task_route_group_sf_order_user model_sf_agv_task_route sf_base.group_sf_order_user 1 1 1 0
129 access_center_control_interface_log_admin center_control_interface_log_admin model_center_control_interface_log base.group_system 1 1 1 0
130 access_sf_model_type_group_sf_stock_manager sf_model_type_group_sf_mrp_manager model_sf_model_type sf_base.group_sf_stock_manager 1 0 0 0
131 access_mrp_bom_byproduct_group_sf_stock_user mrp_bom_byproduct_group_sf_stock_user mrp.model_mrp_bom_byproduct sf_base.group_sf_stock_user 1 0 0 0
132 access_mrp_bom_byproduct_group_sf_stock_manager mrp_bom_byproduct_group_sf_mrp_manager mrp.model_mrp_bom_byproduct sf_base.group_sf_stock_manager 1 0 0 0
133
134
135
136
137
138
139
140
141
142

View File

@@ -7,7 +7,6 @@
<field name="model">sf.agv.site</field>
<field name="arch" type="xml">
<tree editable="bottom">
<field name="number" required="1"/>
<field name="name" required="1"/>
<field name="owning_region" required="1"/>
<field name="state" required="1"/>
@@ -26,7 +25,8 @@
parent="sf_base.menu_sf_base"
name="AGV站点"
sequence="12"
action="action_agv_site_form"/>
action="action_agv_site_form"
groups="sf_base.group_sf_order_user,sf_base.group_plan_dispatch,sf_base.group_plan_director,sf_base.group_sf_mrp_manager"/>
<!-- agv任务路线 -->
<record id="view_agv_task_route_tree" model="ir.ui.view">
@@ -35,11 +35,11 @@
<field name="arch" type="xml">
<tree editable="bottom">
<field name="name" required="1"/>
<field name="type" readonly="1"/>
<field name="type" readonly="1" string="任务类型"/>
<field name="route_type" string="类型"/>
<field name="start_site_id" required="1" options="{'no_create': True}" string="起点接驳站"/>
<field name="end_site_id" required="1" options="{'no_create': True}" string="终点接驳站"/>
<field name="destination_production_line_id" required="1" options="{'no_create': True}"/>
<field name="priority" widget="priority"/>
</tree>
</field>
</record>
@@ -56,4 +56,36 @@
sequence="13"
action="action_agv_task_route_form"/>
</data>
<record model="ir.ui.view" id="center_control_interface_logging_tree_view">
<field name="model">center_control.interface.log</field>
<field name="arch" type="xml">
<tree string="Logs" create="0" edit="0" delete="0">
<field name="name"/>
<field name="content"/>
<field name="interface_call_date"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="center_control_interface_logging_search_view">
<field name="model">center_control.interface.log</field>
<field name="arch" type="xml">
<search string="Logs">
<field name="name"/>
<group expand="0" string="分组">
<field name="interface_call_date"/>
</group>
</search>
</field>
</record>
<record model="ir.actions.act_window" id="center_control_interface_logging_act">
<field name="name">中控调用日志</field>
<field name="res_model">center_control.interface.log</field>
<field name="view_mode">tree</field>
</record>
<menuitem parent="base.next_id_9" id="center_control_interface_logging_menu"
action="center_control_interface_logging_act"/>
</odoo>

View File

@@ -37,7 +37,7 @@
<field name='product_routing_tmpl_ids'>
<tree editable='bottom'>
<field name="sequence" widget="handle" string="序号"/>
<field name="route_workcenter_id" string="工序"/>
<field name="route_workcenter_id" string="工序" options="{'no_create': True}"/>
<field name="routing_type" string="类型"/>
<field name="is_repeat" string="重复"/>
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
@@ -48,18 +48,18 @@
<field name='embryo_routing_tmpl_ids'>
<tree editable='bottom'>
<field name="sequence" widget="handle" string="序号"/>
<field name="route_workcenter_id" string="工序"/>
<field name="route_workcenter_id" string="工序" options="{'no_create': True}"/>
<field name="routing_type" string="类型"/>
<field name="is_repeat" string="重复"/>
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
</tree>
</field>
</group>
<group>
<group>
<field name='surface_technics_routing_tmpl_ids' style="white-space: pre-wrap;">
<tree editable='bottom'>
<field name="sequence" widget="handle" string="序号"/>
<field name="route_workcenter_id" string="工序"/>
<field name="route_workcenter_id" string="工序" options="{'no_create': True}"/>
<field name="routing_type" string="类型"/>
<field name="is_repeat" string="重复"/>
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>

View File

@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="maintenance_equipment_view_form_inherit_mrp" model="ir.ui.view">
<field name="name">maintenance.equipment.view.form.inherit.mrp</field>
<field name="model">maintenance.equipment</field>
<field name="inherit_id" ref="maintenance.hr_equipment_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<button name="button_mrp_workcenter" type="object" class="oe_stat_button"
icon="fa-cogs" string="Work Center" attrs="{'invisible': [('workcenter_id', '=', False)]}"
groups="mrp.group_mrp_routings">
</button>
</xpath>
<xpath expr="//field[@name='location']" position="after">
<field name="workcenter_id" context="{'default_company_id':company_id}"
groups="mrp.group_mrp_routings"/>
</xpath>
<xpath expr="//group[@name='maintenance']" position="after">
<group name="statistics">
<label for="expected_mtbf" string="Expected Mean Time Between Failure"/>
<div class="o_row">
<field name="expected_mtbf"/>
days
</div>
<label for="mtbf" string="Mean Time Between Failure"/>
<div class="o_row">
<field name="mtbf"/>
days
</div>
<label for="estimated_next_failure" string="Estimated Next Failure"/>
<div class="o_row">
<field name="estimated_next_failure"/>
</div>
<field name="latest_failure_date" string="Latest Failure"/>
<label for="mttr" string="Mean Time To Repair"/>
<div class="o_row">
<field name="mttr"/>
days
</div>
</group>
</xpath>
</field>
</record>
<record id="maintenance_request_view_form_inherit_mrp" model="ir.ui.view">
<field name="name">maintenance.request.view.form.inherit.mrp</field>
<field name="model">maintenance.request</field>
<field name="inherit_id" ref="maintenance.hr_equipment_request_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='maintenance_type']" position="after">
<field name="production_company_id" invisible="1"/>
<field name="workorder_id" invisible="1"/>
<field name="production_id" options="{'no_create': True, 'no_open': True}"/>
<field name="workorder_id" attrs="{'invisible': [('production_id', '=', False)]}"
options="{'no_create': True, 'no_open': True}" domain="[('production_id', '=', production_id)]"
groups="mrp.group_mrp_routings"/>
<!-- <field name="repair_id"/> -->
</xpath>
<xpath expr="//div[hasclass('oe_chatter')]" position="after">
<div invisible="not context.get('discard_on_footer_button', False)">
<footer class="oe_edit_only">
<button special="save" data-hotkey="v" string="Save" class="oe_highlight"/>
<button string="Discard" special="cancel" data-hotkey="z"/>
</footer>
</div>
</xpath>
<field name="equipment_id" position="attributes">
<attribute name="domain">['|', (not workorder_id and 1 or 0, '=', 1), '|', ('workcenter_id', '=',
False), ('workcenter_id.order_ids', 'in', workorder_id)]
</attribute>
</field>
</field>
</record>
<record id="maintenance_request_view_search_inherit_mrp" model="ir.ui.view">
<field name="name">maintenence.request.view.search.inherit.mrp</field>
<field name="model">maintenance.request</field>
<field name="inherit_id" ref="maintenance.hr_equipment_request_view_search"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='maintenance_team_id']" position="after">
<field name="production_id" string="Operation"
filter_domain="['|', ('production_id', 'ilike', self), ('workorder_id', 'ilike', self)]"/>
</xpath>
</field>
</record>
<menuitem
id="maintenance.menu_equipment_form"
name="Equipments"
parent="maintenance.menu_maintenance_title"
groups="maintenance.group_equipment_manager,base.group_user"
sequence="2"/>
<menuitem id="menu_workcenter_tree"
action="mrp.mrp_workcenter_action"
groups="mrp.group_mrp_routings"
parent="maintenance.menu_equipment_form"
sequence="1"/>
<menuitem
id="menu_equipment_dashboard"
name="Machines &amp; Tools"
parent="maintenance.menu_equipment_form"
action="maintenance.hr_equipment_action"
sequence="2"/>
</odoo>

View File

@@ -62,17 +62,33 @@
<field name="inherit_id" ref="mrp.mrp_production_form_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='state']" position="attributes">
<attribute name="statusbar_visible">draft,confirmed,progress,pending_processing,completed,done
<!-- <attribute name="statusbar_visible">draft,confirmed,progress,pending_processing,completed,done -->
<!-- </attribute> -->
<attribute name="statusbar_visible">progress,pending_cam,pending_processing,pending_era_cam,completed,done
</attribute>
</xpath>
<xpath expr="//field[@name='user_id']" position="after">
<xpath expr="//sheet//group//group[2]//label" position="before">
<!-- <field name="process_state"/> -->
<field name="state"/>
<!-- <field name="process_state"/> -->
</xpath>
<xpath expr="//sheet//group//group//div[3]" position="after">
<field name="programming_no" readonly="1"/>
<field name="work_state" invisible="1"/>
<field name="schedule_state" invisible='1'/>
<field name="manual_quotation" readonly="1"/>
<field name="programming_state" readonly="1"/>
<field name="production_line_id" readonly="1"/>
</xpath>
<xpath expr="//field[@name='user_id']" position="before">
<field name="plan_start_processing_time" readonly="1"/>
</xpath>
<xpath expr="//field[@name='user_id']" position="after">
<field name="production_line_id" readonly="1"/>
<field name="production_line_state" readonly="1"/>
<field name="part_number"/>
<field name="part_drawing"/>
</xpath>
<xpath expr="//header//button[@name='action_cancel']" position="replace">
<button name="action_cancel" type="object" string="取消" data-hotkey="z"
@@ -243,6 +259,18 @@
</field>
</record>
<record id="product_template_search_view_manual_quotation" model="ir.ui.view">
<field name="name">product.template.search</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_search_view"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='consumable']" position="after">
<separator/>
<filter string="人工编程" name="manual_quotation" domain="[('manual_quotation', '=', True)]"/>
</xpath>
</field>
</record>
<!-- 工单的操作按钮只让制造用户可见 -->
<record id="sf_mrp_production_workorder_tree_editable_view" model="ir.ui.view">
<field name="name">sf.mrp.production.workorder.tree.editable</field>
@@ -365,7 +393,7 @@
<field name="arch" type="xml">
<xpath expr="//search" position="inside">
<searchpanel class="account_root">
<field name="state" icon="fa-filter"/>
<field name="state" icon="fa-filter" enable_counters="1"/>
</searchpanel>
</xpath>
<filter name='todo' position="replace"/>

View File

@@ -23,6 +23,9 @@
<field name="product_id" position="after">
<field name="equipment_id" optional="hide"/>
</field>
<xpath expr="//field[@name='qty_remaining']" position="after">
<field name="manual_quotation" optional="show"/>
</xpath>
<xpath expr="//field[@name='date_planned_start']" position="replace">
<field name="date_planned_start" string="计划开始日期" optional="show"/>
</xpath>
@@ -40,7 +43,7 @@
<attribute name="attrs">{'invisible':
['|',("user_permissions","=",False),("name","=","获取CNC加工程序")]}
</attribute>
<attribute name="string">停工</attribute>
<!-- <attribute name="string">停工</attribute> -->
</xpath>
<xpath expr="//button[@name='action_open_wizard']" position="attributes">
<attribute name="invisible">1</attribute>
@@ -74,10 +77,10 @@
<field name="name">工单</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mrp.workorder</field>
<field name="view_mode">tree,kanban,form</field>
<field name="view_mode">tree,form</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('mrp.mrp_production_workorder_tree_editable_view')}),
(0, 0, {'view_mode': 'kanban', 'view_id': ref('mrp.workcenter_line_kanban')}) ]"/>
(0, 0, {'view_mode': 'tree', 'view_id': ref('mrp.mrp_production_workorder_tree_editable_view')}) ]"/>
<!-- (0, 0, {'view_mode': 'kanban', 'view_id': ref('mrp.workcenter_line_kanban')})-->
<!-- <field name="target">fullscreen</field>-->
<field name="target">current</field>
<field name="domain">[('state', '!=', 'cancel'),('schedule_state', '=', '已排')]</field>
@@ -103,31 +106,52 @@
<field name="model">mrp.workorder</field>
<field name="inherit_id" ref="mrp.mrp_production_workorder_form_view_inherit"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<button type="object" name="action_view_surface_technics_picking" class="oe_stat_button" icon="fa-truck"
groups="base.group_user,sf_base.group_sf_order_user"
attrs="{'invisible': [('surface_technics_picking_count', '=', 0)]}">
<field name="surface_technics_picking_count" widget="statinfo" string="工艺外协"/>
</button>
</xpath>
<xpath expr="//field[@name='state']" position="before">
<field name='user_permissions' invisible="1"/>
<field name='name' invisible="1"/>
<field name='is_delivery' invisible="1"/>
<!-- 工单form页面的开始停工按钮等 -->
<!-- <button name="button_start" type="object" string="开始" class="btn-success" -->
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'not in', ('pending_processing', 'pending_cam', 'pending_era_cam')), ('state','!=','ready'), ('routing_type', 'not in', ('装夹预调', 'CNC加工', '解除装夹'))]}" -->
<!-- groups="sf_base.group_sf_mrp_user"/> -->
<!-- <button name="button_pending" type="object" string="暂停" class="btn-warning" -->
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'not in', ('pending_processing', 'pending_cam', 'pending_era_cam')), ('state','!=','progress'), ('routing_type', 'not in', ('装夹预调', 'CNC加工', '解除装夹'))]}" -->
<!-- groups="sf_base.group_sf_mrp_user"/> -->
<!-- <button name="button_finish" type="object" string="完成" class="btn-success" -->
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'not in', ('pending_processing', 'pending_cam', 'pending_era_cam')), ('state','!=','progress'), ('routing_type', 'not in', ('装夹预调', 'CNC加工', '解除装夹'))]}" -->
<!-- groups="sf_base.group_sf_mrp_user" confirm="是否确认完工"/> -->
<button name="button_start" type="object" string="开始" class="btn-success"
attrs="{'invisible': ['|', ('production_state', '!=', 'pending_processing'), ('state','!=','ready')]}"
groups="sf_base.group_sf_mrp_user"/>
attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')), ('is_user_working', '!=', False)]}"/>
<button name="button_pending" type="object" string="暂停" class="btn-warning"
attrs="{'invisible': ['|', ('production_state', '!=', 'pending_processing'), ('state','!=','progress')]}"
groups="sf_base.group_sf_mrp_user"/>
<button name="button_finish" type="object" string="完成" class="btn-success"
attrs="{'invisible': ['|', ('production_state', '!=', 'pending_processing'), ('state','!=','progress')]}"
groups="sf_base.group_sf_mrp_user" confirm="是否确认完工"/>
<button name="%(mrp.act_mrp_block_workcenter_wo)d" type="action" string="停工"
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>
<button name="button_finish" type="object" string="完成" class="btn-success" confirm="是否确认完工"
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>
<button name="%(mrp.act_mrp_block_workcenter_wo)d" type="action" string="阻塞"
context="{'default_workcenter_id': workcenter_id}" class="btn-danger"
groups="sf_base.group_sf_mrp_user"
attrs="{'invisible': ['|', ('production_state', '!=', 'pending_processing'), ('state','!=','progress')]}"/>
<button name="button_unblock" type="object" string="Unblock"
attrs="{'invisible': ['|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked')]}"/>
<button name="button_unblock" type="object" string="取消阻塞"
context="{'default_workcenter_id': workcenter_id}" class="btn-danger"
groups="sf_base.group_sf_mrp_user"
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked'),('state','=','done')]}"/>
attrs="{'invisible': ['|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked')]}"/>
<!-- <button name="%(mrp.act_mrp_block_workcenter_wo)d" type="action" string="停工" -->
<!-- context="{'default_workcenter_id': workcenter_id}" class="btn-danger" -->
<!-- groups="sf_base.group_sf_mrp_user" -->
<!-- attrs="{'invisible': ['|', ('production_state', '!=', 'pending_processing'), ('state','!=','progress')]}"/> -->
<!-- <button name="button_unblock" type="object" string="Unblock" -->
<!-- context="{'default_workcenter_id': workcenter_id}" class="btn-danger" -->
<!-- groups="sf_base.group_sf_mrp_user" -->
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked'),('state','=','done')]}"/> -->
<button name="button_workpiece_delivery" type="object" string="工件配送" class="btn-primary"
attrs="{'invisible': ['|',('routing_type','!=','装夹预调'),('is_delivery','=',True)]}"/>
</xpath>
<xpath expr="//page[1]" position="before">
<page string="开料要求" attrs='{"invisible": [("routing_type","!=","切割")]}'>
@@ -147,6 +171,7 @@
</xpath>
<xpath expr="//label[1]" position="before">
<field name='routing_type'/>
<field name='process_state' attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/>
</xpath>
<xpath expr="//label[1]" position="attributes">
<attribute name="string">计划加工时间</attribute>
@@ -173,10 +198,14 @@
attrs="{'invisible': [('production_state','=', 'draft')], 'readonly': [('is_user_working', '=', True)]}"
sum="real duration"/>
<field name="glb_file" readonly="1" widget="Viewer3D" string="加工模型"/>
<field name="manual_quotation" readonly="1"
attrs="{'invisible': [('routing_type', '!=', 'CNC加工')]}"/>
<field name="processing_panel" readonly="1"
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
<field name="equipment_id"
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
<field name="production_line_id"
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
<field name="production_line_state"
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
<field name="functional_fixture_id"
@@ -185,7 +214,6 @@
attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/>
<field name="functional_fixture_type_id"
attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/>
<field name="rfid_code" force_save="1" readonly="1" cache="True"
attrs="{'invisible': [('rfid_code_old', '!=', False)]}"/>
<field name="rfid_code_old" readonly="1" attrs="{'invisible': [('rfid_code_old', '=', False)]}"/>
@@ -244,13 +272,13 @@
<page string="工件装夹" attrs='{"invisible": [("routing_type","!=","装夹预调")]}'>
<group>
<field name="_barcode_scanned" widget="barcode_handler"/>
<group string="卡盘">
<field name="chuck_serial_number"/>
<field name="chuck_name"/>
<field name="chuck_brand_id"/>
<field name="chuck_type_id"/>
<field name="chuck_model_id"/>
</group>
<!-- <group string="卡盘">-->
<!-- <field name="chuck_serial_number"/>-->
<!-- <field name="chuck_name"/>-->
<!-- <field name="chuck_brand_id"/>-->
<!-- <field name="chuck_type_id"/>-->
<!-- <field name="chuck_model_id"/>-->
<!-- </group>-->
<group string="托盘">
<field name="tray_serial_number" readonly="1" string="序列号"/>
<field name="tray_product_id" readonly="1" string="名称"/>
@@ -263,6 +291,10 @@
<field name="preset_program_information" colspan="2" nolabel="1"
placeholder="如有预调程序信息请在此处输入....."/>
</group>
<group string="加工图纸">
<!-- 隐藏加工图纸字段名 -->
<field name="processing_drawing" widget="pdf_viewer" string=""/>
</group>
</page>
<page string="前置三元检测定位参数" attrs='{"invisible": [("routing_type","!=","装夹预调")]}'>
@@ -420,18 +452,19 @@
<field name='X_deviation_angle' readonly="1"/>
</group>
</page>
<page string="工件配送" attrs='{"invisible": [("routing_type","!=","装夹预调")]}'>
<page string="工件配送"
attrs="{'invisible': [('routing_type','!=','装夹预调')]}">
<field name="workpiece_delivery_ids">
<tree editable="bottom">
<field name="production_id" invisible="1"/>
<field name="workpiece_code"/>
<field name="route_id" options="{'no_create': True}"/>
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
<field name="feeder_station_destination_id" readonly="1" force_save="1"/>
<field name="cnc_program_down_state" readonly="1"/>
<field name="production_line_id"/>
<field name="is_cnc_program_down" readonly="1"/>
<field name="production_line_id" readonly="1"/>
<field name="task_delivery_time" readonly="1"/>
<field name="task_completion_time" readonly="1"/>
<field name="status" readonly="1"/>
</tree>
</field>
</page>
@@ -529,6 +562,18 @@
<field name="inspection_user_id"/>
<field name="save_name" widget="CopyClipboardChar"
attrs="{'invisible':[('routing_type','!=','装夹预调')]}"/>
<label for="material_length" string="物料尺寸"/>
<div class="o_address_format">
<label for="material_length" string="长"/>
<field name="material_length" class="o_address_zip"/>
<span>&amp;nbsp;</span>
<label for="material_width" string="宽"/>
<field name="material_width" class="o_address_zip"/>
<span>&amp;nbsp;</span>
<label for="material_height" string="高"/>
<field name="material_height" class="o_address_zip"/>
</div>
<field name="part_number"/>
</xpath>
</field>
</record>
@@ -567,13 +612,16 @@
decoration-success="status == '已配送'"
decoration-warning="status == '待下发'"
decoration-danger="status == '待配送'"/>
<field name="production_id" string="工件编码"/>
<field name="workpiece_code"/>
<field name="production_line_id" options="{'no_create': True}"/>
<field name="route_id" options="{'no_create': True}"/>
<field name="production_id"/>
<field name="type" readonly="1"/>
<field name="production_line_id" options="{'no_create': True}" readonly="1"/>
<field name="route_id" options="{'no_create': True}"
domain="[('route_type','in',['上产线','下产线'])]"/>
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
<field name="feeder_station_destination_id" readonly="1" force_save="1"/>
<field name="cnc_program_down_state" readonly="1"/>
<field name="is_cnc_program_down" readonly="1"/>
<!-- <field name="rfid_code"/>-->
<field name="task_delivery_time" readonly="1"/>
<field name="task_completion_time" readonly="1"/>
<field name="delivery_duration" widget="float_time"/>
@@ -586,8 +634,10 @@
<field name="model">sf.workpiece.delivery</field>
<field name="arch" type="xml">
<search string="工件配送">
<filter string="上产线" name="on_up" domain="[('type', '=', '上产线')]"/>
<filter string="下产线" name="on_down" domain="[('type', '=', '下产线' )]"/>
<field name="rfid_code"/>
<field name="production_id"/>
<field name="workpiece_code"/>
<field name="feeder_station_start_id"/>
<field name="production_line_id"/>
<field name="task_delivery_time"/>
@@ -598,9 +648,6 @@
<searchpanel>
<field name="production_line_id" icon="fa-building" enable_counters="1"/>
<field name="status" icon="fa-building" enable_counters="1"/>
<field name="production_line_state" icon="fa-building" enable_counters="1"/>
<field name="cnc_program_down_state" icon="fa-building" enable_counters="1"/>
</searchpanel>
</search>
</field>
@@ -609,7 +656,69 @@
<record id="sf_workpiece_delivery_act" model="ir.actions.act_window">
<field name="name">工件配送</field>
<field name="res_model">sf.workpiece.delivery</field>
<field name="search_view_id" ref="sf_workpiece_delivery_search"/>
<field name="context">{'search_default_on_up':1}</field>
<field name="view_mode">tree,search</field>
<field name="domain">[('type','in',['上产线','下产线']),('workorder_state','=','done')]</field>
</record>
<!--=========================================运送空料架列表======================================-->
<record id="sf_workpiece_delivery_empty_racks_template_tree" model="ir.ui.view">
<field name="name">空料架配送</field>
<field name="model">sf.workpiece.delivery</field>
<field name="arch" type="xml">
<tree string="工件配送" class="center" create="0" edit="0" delete="0">
<header>
<button name="button_delivery" type="object" string="配送" class="oe_highlight"/>
</header>
<field name="name" string="路线名称" readonly="1"/>
<field name="route_id" options="{'no_create': True}"/>
<field name="feeder_station_start_id" readonly="1"/>
<field name="feeder_station_destination_id" readonly="1"/>
<button name="action_delivery_history" type="object" class="btn btn-link text-info" icon="fa-history"
string="历史"/>
</tree>
</field>
</record>
<record id="sf_workpiece_delivery_empty_racks_tree" model="ir.ui.view">
<field name="name">空料架配送</field>
<field name="model">sf.workpiece.delivery</field>
<field name="arch" type="xml">
<tree string="运送空料架" class="center" create="0" delete="0" edit="0">
<field name="status"/>
<field name="route_id"/>
<field name="feeder_station_start_id"/>
<field name="feeder_station_destination_id"/>
<field name="task_delivery_time"/>
<field name="task_completion_time"/>
<field name="delivery_duration" widget="float_time"/>
</tree>
</field>
</record>
<record id="sf_workpiece_delivery_empty_racks_search" model="ir.ui.view">
<field name="name">空料架配送</field>
<field name="model">sf.workpiece.delivery</field>
<field name="arch" type="xml">
<search string="运送空料架">
<field name="route_id"/>
<field name="feeder_station_start_id"/>
<field name="feeder_station_destination_id"/>
</search>
</field>
</record>
<record id="sf_workpiece_delivery_empty_racks_act" model="ir.actions.act_window">
<field name="name">空料架配送</field>
<field name="res_model">sf.workpiece.delivery</field>
<field name="view_id" ref="sf_workpiece_delivery_empty_racks_template_tree"/>
<field name="search_view_id" ref="sf_workpiece_delivery_empty_racks_search"/>
<field name="view_mode">tree</field>
<field name="domain">[('type','in',['运送空料架']),('name','not ilike','WDO')]</field>
</record>
</odoo>

View File

@@ -19,6 +19,31 @@
<xpath expr="//field[@name='product_id']" position="after">
<field name="product_specification"/>
</xpath>
<xpath expr="//field[@name='qr_code_image']" position="after">
<button name="search_lot_put_rfid" string="Rfid补零" type="object" invisible="1"/>
</xpath>
</field>
</record>
<record id="search_product_lot_view" model="ir.ui.view">
<field name="name">stock.production.lot.view</field>
<field name="model">stock.lot</field>
<field name="inherit_id" ref="stock.search_product_lot_filter"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='product_id']" position="after">
<field name="rfid"/>
</xpath>
</field>
</record>
<record id="view_production_lot_view_tree_tree" model="ir.ui.view">
<field name="name">stock.production.lot.tree.inherit.product.expiry</field>
<field name="model">stock.lot</field>
<field name="inherit_id" ref="stock.view_production_lot_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='create_date']" position="after">
<field name="rfid" invisible="1"/>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="sf_tray_form_inherit" model="ir.ui.view">
<field name="name">托盘条形码生成</field>
<field name="model">sf.tray</field>
<field name="inherit_id" ref="sf_base.sf_tray_form"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='group1']" position="after">
<notebook>
<page string="生成条形码">
<field name='qr_image' widget="image"/>
<group>
<field name='production_id' readonly="1"
attrs='{"invisible": [("production_id","=",False)]}'/>
<field name="workorder_id"/>
</group>
<div class="col
-12 col-lg-6 o_setting_box">
<button type="object" class="oe_highlight" name="unclamp" string="解除装夹"
attrs='{"invisible": [("state","=","空闲")]}'/>
</div>
</page>
</notebook>
</xpath>
</field>
</record>
</data>
</odoo>

View File

@@ -5,20 +5,32 @@
<field name="model">sf.workpiece.delivery.wizard</field>
<field name="arch" type="xml">
<form>
<group>
<field name="delivery_id" invisible="True"/>
<sheet>
<field name="delivery_ids" invisible="True"/>
<field name="workorder_id" invisible="True"/>
<field name="route_id" required="1" options="{'no_create': True}"/>
</group>
<div>
<field name="is_ok"/>
确认上述信息正确无误.
</div>
<footer>
<button string="确认配送" name="confirm" type="object" class="oe_highlight"
attrs="{'invisible':[('is_ok','=',False)]}"/>
<button string="取消" class="btn btn-secondary" special="cancel"/>
</footer>
<field name="type" invisible="True"/>
<group attrs="{'invisible': [('type', 'in', ['运送空料架'])]}">
<field name="production_ids" readonly="1" widget="many2many_tags" string="制造订单号"/>
<div class="o_address_format">
<lable for="rfid_code"></lable>
<field name="rfid_code" class="o_address_zip"/>
<button name="recognize_production" string="识别" type="object" class="oe_highlight"/>
</div>
<field name="destination_production_line_id" readonly="1"/>
<field name="route_id"/>
</group>
<group attrs="{'invisible': [('type', 'in', ['运送空料架'])]}">
<field name="feeder_station_start_id" focesave="1" readonly="1"/>
<field name="feeder_station_destination_id" focesave="1" readonly="1"/>
</group>
<div attrs="{'invisible': [('type', 'in', ['上产线','下产线'])]}">
是否确定配送
</div>
<footer>
<button string="配送" name="confirm" type="object" class="oe_highlight"/>
<button string="取消" class="btn btn-secondary" special="cancel"/>
</footer>
</sheet>
</form>
</field>
</record>

View File

@@ -1,21 +1,88 @@
# -*- coding: utf-8 -*-
# Part of YiZuo. See LICENSE file for full copyright and licensing details.
import logging
from odoo.exceptions import UserError, ValidationError
from datetime import datetime
from odoo import models, api, fields
from odoo import models, api, fields, _
class WorkpieceDeliveryWizard(models.TransientModel):
_name = 'sf.workpiece.delivery.wizard'
_description = '工件配送'
delivery_id = fields.Many2one('sf.workpiece.delivery', string='配送')
delivery_ids = fields.Many2many('sf.workpiece.delivery', string='配送')
rfid_code = fields.Char('rfid码')
workorder_id = fields.Many2one('mrp.workorder', string='工单')
route_id = fields.Many2one('sf.agv.task.route', '任务路线')
is_ok = fields.Boolean('确认上述信息正确无误.')
production_ids = fields.Many2many('mrp.production', string='制造订单号')
destination_production_line_id = fields.Many2one('sf.production.line', '目的生产线')
route_id = fields.Many2one('sf.agv.task.route', '任务路线', domain=[('route_type', 'in', ['上产线', '下产线'])])
feeder_station_start_id = fields.Many2one('sf.agv.site', '起点接驳站')
feeder_station_destination_id = fields.Many2one('sf.agv.site', '目的接驳站')
type = fields.Selection(
[('上产线', '上产线'), ('下产线', '下产线'), ('运送空料架', '运送空料架')], string='类型')
def confirm(self):
if self.type != '运送空料架':
if not self.route_id:
raise UserError('请选择路线')
if self.workorder_id:
self.workorder_id.workpiece_delivery_ids._delivery_avg()
self.workorder_id.workpiece_delivery_ids[0]._delivery_avg()
else:
self.delivery_id._delivery_avg()
is_not_production_line = 0
same_production_line_id = None
notsame_production_line_arr = []
for item in self.production_ids:
if same_production_line_id is None:
same_production_line_id = item.production_line_id.id
if item.production_line_id.id != same_production_line_id:
notsame_production_line_arr.append(item.name)
notsame_production_line_str = ','.join(map(str, notsame_production_line_arr))
if is_not_production_line >= 1:
raise UserError('制造订单号为%s的目的生产线不一致' % notsame_production_line_str)
else:
self.delivery_ids._delivery_avg()
def recognize_production(self):
# production_ids = []
# delivery_ids = []
# aa = self.production_ids.workorder_ids.filtered(
# lambda b: b.routing_type == "装夹预调").workpiece_delivery_ids.filtered(
# lambda c: c.rfid_code == self.rfid_code)
# logging.info('aa:%s' % aa)
if len(self.production_ids) == 4:
raise UserError('只能配送四个制造订单')
else:
if self.rfid_code:
wd = self.env['sf.workpiece.delivery'].search(
[('type', '=', self.delivery_ids[0].type), ('rfid_code', '=', self.rfid_code),
('status', '=', self.delivery_ids[0].status)])
if wd:
if wd.production_line_id.id == self.delivery_ids[0].production_line_id.id:
# production_ids.append(wd.production_id)
# delivery_ids.append(wd.id)
# 将对象添加到对应的同模型且是多对多类型里
self.production_ids |= wd.production_id
self.delivery_ids |= wd
self.rfid_code = False
# self.production_ids = [(6, 0, production_ids)]
# self.delivery_ids = [(6, 0, delivery_ids)]
else:
raise UserError('该rfid对应的制造订单号为%s的目的生产线不一致' % wd.production_id.name)
return {
'name': _('确认'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'sf.workpiece.delivery.wizard',
'target': 'new',
'context': {
'default_delivery_ids': [(6, 0, self.delivery_ids.ids)],
'default_production_ids': [(6, 0, self.production_ids.ids)],
'default_route_id': self.delivery_ids[0].route_id.id,
'default_type': self.delivery_ids[0].type
}}
@api.onchange('route_id')
def onchange_route(self):
if self.route_id:
self.feeder_station_start_id = self.route_id.start_site_id.id
self.feeder_station_destination_id = self.route_id.end_site_id.id

View File

@@ -26,14 +26,17 @@ class Sf_Mrs_Connect(http.Controller):
logging.info('下发编程单:%s' % ret)
# 查询状态为进行中且类型为获取CNC加工程序的工单
cnc_production = request.env['mrp.production'].with_user(
request.env.ref("base.user_admin")).search([('name', '=', ret['production_order_no'])])
request.env.ref("base.user_admin")).search([('name', '=', ret['production_order_no'].split(',')[0])])
cnc_program = request.env['mrp.production'].with_user(
request.env.ref("base.user_admin")).search(
[('programming_no', '=', cnc_production.programming_no), ('id', '!=', cnc_production.id)])
# cnc_program = request.env['mrp.production'].with_user(
# request.env.ref("base.user_admin")).search([('programming_no', '=', cnc_production.programming_no)])
logging.info('制造订单号:%s' % cnc_production.name)
if cnc_production:
if ret['glb_file']:
cnc_production.glb_file = base64.b64encode(ret['glb_file'])
# if ret['glb_file']:
# cnc_production.glb_file = base64.b64encode(ret['glb_file'])
# 拉取所有加工面的程序文件
# i = 1
for r in ret['processing_panel']:
download_state = request.env['sf.cnc.processing'].with_user(
request.env.ref("base.user_admin")).download_file_tmp(
@@ -46,16 +49,35 @@ class Sf_Mrs_Connect(http.Controller):
program_path_tmp = os.path.join('/tmp', ret['folder_name'], 'return', r)
# program_path_tmp = "C://Users//43484//Desktop//机企猫工作文档//其他//model_analysis"
files = os.listdir(program_path_tmp)
cnc_processing_arr = []
for f in files:
program_path = os.path.join(program_path_tmp, f)
logging.info('cnc程序路径 :%s' % program_path)
if f.endswith(".doc"):
# 插入cmm程序数据
request.env['sf.cmm.program'].with_user(
cmm_program = request.env['sf.cmm.program'].with_user(
request.env.ref("base.user_admin")).cmm_program_create(ret, program_path, program_path_tmp)
request.env['sf.cnc.processing'].with_user(
cnc_processing = request.env['sf.cnc.processing'].with_user(
request.env.ref("base.user_admin")).cnc_processing_create(cnc_production, ret, program_path,
program_path_tmp)
logging.info('cnc_processing111:%s' % cnc_processing)
if cnc_processing:
cnc_processing_arr.append(cnc_processing._json_cnc_processing(cnc_processing))
if (cnc_program and cnc_processing_arr) or (not cnc_program and cnc_processing_arr):
logging.info('cnc_processing_arr:%s' % cnc_processing_arr)
if cnc_program and cnc_processing_arr:
cnc_program.write({'programming_state': '已编程', 'work_state': '已编程'})
cnc_program.workorder_ids.filtered(lambda b: b.routing_type == 'CNC加工').write(
{'cnc_ids': cnc_processing_arr, 'cnc_worksheet': cnc_production.workorder_ids.filtered(
lambda b: b.routing_type == 'CNC加工').cnc_worksheet})
cnc_program |= cnc_production
if not cnc_program and cnc_processing_arr:
cnc_program = cnc_production
cnc_program_ids = [item.id for item in cnc_program]
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
[('production_id', 'in', cnc_program_ids)])
if workpiece_delivery:
workpiece_delivery.write({'is_cnc_program_down': True})
return json.JSONEncoder().encode(res)
else:
res = {'status': 0, 'message': '该制造订单暂未开始'}

View File

@@ -24,6 +24,7 @@ class FtpController():
def file_exists(self, path):
# 检查文件是否存在于FTP服务器上
try:
logging.info("dirname:%s" % os.path.dirname(path))
self.ftp.cwd(os.path.dirname(path))
files = self.ftp.nlst()
return os.path.basename(path) in files
@@ -31,6 +32,11 @@ class FtpController():
logging.error(f"Error checking file: {e}")
return False
# # 检测字符串的编码
# def detect_encoding(self, s):
# result = chardet.detect(s)
# return result['encoding']
# 下载目录下的文件
def download_file_tree(self, target_dir, serverdir):
if not os.path.exists(serverdir):
@@ -84,6 +90,9 @@ class FtpController():
return 1
except Exception:
return 0
finally:
self.ftp.quit()
logging.info("ftp已关闭")
# 下载指定目录下的指定文件
def download_file(self, serverfile, remotefile):

View File

@@ -17,6 +17,7 @@ class ResConfigSettings(models.TransientModel):
default='http://172.16.10.114:8182/rcms/services/rest/hikRpcService/genAgvSchedulingTask')
center_control_url = fields.Char(string='中控访问地址',
default='http://172.16.21.50:8001')
center_control_Authorization = fields.Char(string='中控访问认证')
wbcode = fields.Char('地码')
agv_code = fields.Char(string='agv编号')
task_type_no = fields.Char('任务单类型编号')
@@ -102,6 +103,7 @@ class ResConfigSettings(models.TransientModel):
wbcode = config.get_param('wbcode', default='')
agv_code = config.get_param('agv_code', default='')
center_control_url = config.get_param('center_control_url', default='')
center_control_Authorization = config.get_param('center_control_Authorization', default='')
ftp_host = config.get_param('ftp_host', default='')
ftp_port = config.get_param('ftp_port', default='')
ftp_user = config.get_param('ftp_user', default='')
@@ -115,6 +117,7 @@ class ResConfigSettings(models.TransientModel):
wbcode=wbcode,
agv_code=agv_code,
center_control_url=center_control_url,
center_control_Authorization=center_control_Authorization,
ftp_host=ftp_host,
ftp_port=ftp_port,
ftp_user=ftp_user,
@@ -132,6 +135,7 @@ class ResConfigSettings(models.TransientModel):
ir_config.set_param("wbcode", self.wbcode or "")
ir_config.set_param("agv_code", self.agv_code or "")
ir_config.set_param("center_control_url", self.center_control_url or "")
ir_config.set_param("center_control_Authorization", self.center_control_Authorization or "")
ir_config.set_param("ftp_host", self.ftp_host or "")
ir_config.set_param("ftp_port", self.ftp_port or "")
ir_config.set_param("ftp_user", self.ftp_user or "")

View File

@@ -2116,19 +2116,21 @@ class CuttingSpeed(models.Model):
for item in result['feed_per_tooth_all_list']:
feed_per_tooth = self.search([("name", '=', item['name']), ('active', 'in', [True, False])])
if not feed_per_tooth:
self.create({
'name': item['name'],
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', item['standard_library_code'].replace("JKM", result[
'factory_short_name']))]).id,
'materials_type_id': self.env['sf.materials.model'].search(
[('materials_no', '=', item['materials_type_code'])]).id,
'cutting_width_depth_id': self.env['sf.cutting.width.depth'].search(
[('name', '=', item['cutting_width_depth'])]).id,
'blade_diameter': item['blade_diameter'],
'feed_per_tooth': item['feed_per_tooth'],
'active': item['active'],
})
_logger.info("item[standard_library_code] %s" % item.get('standard_library_code'))
if item.get('standard_library_code'):
self.create({
'name': item['name'],
'standard_library_id': self.env['sf.cutting_tool.standard.library'].search(
[('code', '=', item['standard_library_code'].replace("JKM", result[
'factory_short_name']))]).id,
'materials_type_id': self.env['sf.materials.model'].search(
[('materials_no', '=', item['materials_type_code'])]).id,
'cutting_width_depth_id': self.env['sf.cutting.width.depth'].search(
[('name', '=', item['cutting_width_depth'])]).id,
'blade_diameter': item['blade_diameter'],
'feed_per_tooth': item['feed_per_tooth'],
'active': item['active'],
})
else:
feed_per_tooth.write({
'materials_type_id': self.env['sf.materials.model'].search(

View File

@@ -106,6 +106,10 @@
<label for="center_control_url" string="访问地址"/>
<field name="center_control_url"/>
</div>
<div class="text-muted">
<label for="center_control_Authorization" string="身份认证"/>
<field name="center_control_Authorization"/>
</div>
</div>
</div>
</div>

1
sf_oca/__init__.py Normal file
View File

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

23
sf_oca/__manifest__.py Normal file
View File

@@ -0,0 +1,23 @@
# Copyright 2017-19 ForgeFlow S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "sf_oca",
"version": "1.0",
'summary': '智能工厂oca模块',
'sequence': 1,
'description': """
在本模块,进行流程的审核
""",
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
"depends": ["base_tier_validation_forward"],
"data": [
"security/ir.model.access.csv",
"templates/tier_validation_templates.xml",
],
"assets": {},
'license': 'LGPL-3',
'installable': True,
'application': False,
'auto_install': False,
}

View File

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

View File

@@ -0,0 +1,12 @@
# Copyright 2017-19 ForgeFlow S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
class TierValidation(models.AbstractModel):
_inherit = "tier.validation"
def _notify_restarted_review_body(self):
return _("%s 取消审批请求.") % (self.env.user.name)

View File

@@ -0,0 +1 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<template id="tier_validation_buttons_sf"
inherit_id="base_tier_validation.tier_validation_buttons">
<xpath expr="//button[@name='request_validation']" position="attributes">
<attribute name="string">发起审批</attribute>
</xpath>
<xpath expr="//button[@name='restart_validation']" position="attributes">
<attribute name="string">取消审批</attribute>
<attribute name="t-attf-attrs">{'invisible': ['|','|',('review_ids', '=', []),('#{state_field}',
'#{state_operator}', #{state_value}), ('validated', '=', True)]}
</attribute>
</xpath>
</template>
<template id="tier_validation_label_sf"
inherit_id="base_tier_validation_forward.tier_validation_label_forward">
<xpath expr="//p/i[@class='fa fa-info-circle']" position="replace">
<i class="fa fa-info-circle"/>
这个单据需要验证.
</xpath>
<xpath expr="//div[@class='alert alert-success'][@role='alert']/p" position="replace">
<p>
<i class="fa fa-thumbs-up"/>
审批已通过.
</p>
</xpath>
<xpath expr="//button[@name='validate_tier']" position="attributes">
<attribute name="string">验证</attribute>
</xpath>
<xpath expr="//button[@name='reject_tier']" position="attributes">
<attribute name="string">拒绝</attribute>
</xpath>
</template>
</odoo>

View File

@@ -56,7 +56,8 @@ class sf_production_plan(models.Model):
# # 加工时长
# process_time = fields.Float(string='加工时长', digits=(16, 2))
# 实际加工时长、实际开始时间、实际结束时间
actual_process_time = fields.Float(string='实际加工时长(分钟)', digits=(16, 2), compute='_compute_actual_process_time')
actual_process_time = fields.Float(string='实际加工时长(分钟)', digits=(16, 2),
compute='_compute_actual_process_time')
actual_start_time = fields.Datetime(string='实际开始时间')
actual_end_time = fields.Datetime(string='实际结束时间')
shift = fields.Char(string='班次')
@@ -81,6 +82,10 @@ class sf_production_plan(models.Model):
def _compute_production_line_id(self):
for item in self:
item.sudo().production_id.production_line_id = item.production_line_id.id
item.sudo().production_id.workorder_ids.filtered(
lambda b: b.routing_type == "装夹预调").workpiece_delivery_ids.write(
{'production_line_id': item.production_line_id.id,
'plan_start_processing_time': item.date_planned_start})
# item.sudo().production_id.plan_start_processing_time = item.date_planned_start
# @api.onchange('state')
@@ -200,7 +205,7 @@ class sf_production_plan(models.Model):
if item.name == 'CNC加工':
item.date_planned_finished = datetime.now() + timedelta(days=100)
# item.date_planned_start = record.date_planned_start
item.date_planned_start = datetime.now()
item.date_planned_start = self.date_planned_start if self.date_planned_start else datetime.now()
record.sudo().production_id.plan_start_processing_time = item.date_planned_start
item.date_planned_finished = item.date_planned_start + timedelta(
minutes=record.env['mrp.routing.workcenter'].sudo().search(
@@ -214,16 +219,22 @@ class sf_production_plan(models.Model):
record.state = 'done'
# record.production_id.schedule_state = '已排'
record.sudo().production_id.schedule_state = '已排'
record.sudo().production_id.process_state = '待装夹'
# self.env['sale.order'].browse(record.production_id.origin).schedule_status = 'to process'
sale_obj = self.env['sale.order'].search([('name', '=', record.origin)])
if 'S' in sale_obj.name:
sale_obj.schedule_status = 'to process'
# sale_obj = self.env['sale.order'].search([('name', '=', record.origin)])
# if 'S' in sale_obj.name:
# sale_obj.schedule_status = 'to process'
mrp_production_ids = record.production_id._get_children().ids
print('mrp_production_ids', mrp_production_ids)
for i in mrp_production_ids:
record.env['mrp.production'].sudo().browse(i).schedule_state = '已排'
# record.production_id.date_planned_start = record.date_planned_start
# record.production_id.date_planned_finished = record.date_planned_finished
record.sudo().production_id.production_line_id = record.production_line_id.id
record.sudo().production_id.workorder_ids.filtered(
lambda b: b.routing_type == "装夹预调").workpiece_delivery_ids.write(
{'production_line_id': record.production_line_id.id,
'plan_start_processing_time': record.date_planned_start})
else:
raise ValidationError("未找到工单")
# record.date_planned_finished = record.date_planned_start + timedelta(days=3)
@@ -294,16 +305,16 @@ class sf_production_plan(models.Model):
print('时间设置失败,请检查是否为工序分配工作中心,%s' % e)
def cancel_production_schedule(self):
self.date_planned_start = False
self.date_planned_finished = False
self.date_planned_start = None
self.date_planned_finished = None
self.state = 'draft'
self.production_line_id = False
self.production_line_id = None
aa = self.env['mrp.production'].sudo().search([('name', '=', self.name)])
aa.schedule_state = '未排'
# self.env['sale.order'].browse(record.production_id.origin).schedule_status = 'to shedule'
sale_obj = self.env['sale.order'].search([('name', '=', self.origin)])
if 'S' in sale_obj.name:
sale_obj.schedule_status = 'to schedule'
# if 'S' in sale_obj.name:
# sale_obj.schedule_status = 'to schedule'
return self.date_planned_finished
def liucheng_cs(self):

View File

@@ -306,6 +306,14 @@
action="sf_manufacturing.sf_workpiece_delivery_act"
parent="mrp.menu_mrp_manufacturing"
/>
<menuitem
id="sf_workpiece_delivery__empty_racks_menu"
name="空料架配送"
sequence="11"
action="sf_manufacturing.sf_workpiece_delivery_empty_racks_act"
parent="mrp.menu_mrp_manufacturing"
/>
<!-- <menuitem -->
<!-- id="sale_custom_menu" -->
<!-- name="报价单" -->

View File

@@ -12,17 +12,17 @@ class QualityCheck(models.Model):
# ==========零件特采接口==========
def _register_quality_check(self):
# create_url = '/AutoDeviceApi/PartSpecProc'
# sf_sync_config = self.env['res.config.settings'].get_values()
# token = sf_sync_config['token']
# sf_secret_key = sf_sync_config['sf_secret_key']
# headers = Common.get_headers(self, token, sf_secret_key)
headers = {'Authorization': 'Ba F2CF5DCC-1A00-4234-9E95-65603F70CC8A'}
crea_url = "https://x24467i973.zicp.fun/AutoDeviceApi/PartSpecProc"
config = self.env['res.config.settings'].get_values()
# token = sf_sync_config['token'Ba F2CF5DCC-1A00-4234-9E95-65603F70CC8A]
headers = {'Authorization': config['center_control_Authorization']}
crea_url = config['center_control_url'] + "/AutoDeviceApi/PartSpecProc"
origin = self.picking_id.origin
production_id = self.env['mrp.production'].sudo().search([('name', '=', origin)])
rfid = '' if not production_id.workorder_ids else production_id.workorder_ids[-1].rfid_code or ''
val = [rfid]
# todo 需修改
val = ['0037818516']
logging.info('获取到的工单信息%s' % val)
r = requests.post(crea_url, json=val, headers=headers)
ret = r.json()
logging.info('_register_quality_check:%s' % ret)
@@ -31,10 +31,10 @@ class QualityCheck(models.Model):
else:
raise ValidationError("零件特采发送失败")
def do_fail(self):
self.write({
'quality_state': 'fail',
'user_id': self.env.user.id,
'control_date': datetime.now()})
if self.picking_id and 'WH/MO/' in self.picking_id.origin:
self._register_quality_check()
# def do_fail(self):
# self.write({
# 'quality_state': 'fail',
# 'user_id': self.env.user.id,
# 'control_date': datetime.now()})
# if self.picking_id and 'WH/MO/' in self.picking_id.origin:
# self._register_quality_check()

View File

@@ -8,8 +8,8 @@ from datetime import datetime
import requests
from odoo import http
from odoo.http import request
# from OCC.Extend.DataExchange import read_step_file
# from OCC.Extend.DataExchange import write_stl_file
from OCC.Extend.DataExchange import read_step_file
from OCC.Extend.DataExchange import write_stl_file
from odoo import models, fields, api
from odoo.modules import get_resource_path
from odoo.exceptions import ValidationError, UserError
@@ -17,7 +17,6 @@ from odoo.addons.sf_base.commons.common import Common
from . import parser_and_calculate_work_time as pc
class QuickEasyOrder(models.Model):
_name = 'quick.easy.order'
_description = '简易下单'
@@ -55,6 +54,7 @@ class QuickEasyOrder(models.Model):
('success', '成功'),
('fail', '失败')], string='模型上色状态')
processing_time = fields.Integer('加工时长(min)')
sale_order_id = fields.Many2one('sale.order', '销售订单号')
@api.depends('unit_price', 'quantity')
def _compute_total_amount(self):
@@ -89,7 +89,8 @@ class QuickEasyOrder(models.Model):
obj = super(QuickEasyOrder, self).create(vals)
# self.model_coloring(obj)
logging.info('---------开始派单到工厂-------')
self.distribute_to_factory(obj)
sale_order = self.distribute_to_factory(obj)
obj.sale_order_id = sale_order.id
obj.state = '待接单'
return obj

View File

@@ -6,8 +6,8 @@ import os
from datetime import datetime
from stl import mesh
# from OCC.Core.GProp import GProp_GProps
# from OCC.Extend.DataExchange import read_step_file
# from OCC.Extend.DataExchange import write_stl_file
from OCC.Extend.DataExchange import read_step_file
from OCC.Extend.DataExchange import write_stl_file
from odoo.addons.sf_base.commons.common import Common
from odoo import models, fields, api
from odoo.modules import get_resource_path
@@ -51,6 +51,7 @@ class QuickEasyOrder(models.Model):
('success', '成功'),
('fail', '失败')], string='模型上色状态')
processing_time = fields.Integer('加工时长(min)')
sale_order_id = fields.Many2one('sale.order', '销售订单号')
@api.depends('unit_price', 'quantity')
def _compute_total_amount(self):
@@ -79,12 +80,12 @@ class QuickEasyOrder(models.Model):
base64_datas = base64_data.decode('utf-8')
model_code = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest()
report_path = attachment._full_path(attachment.store_fname)
vals['model_file'] = self.transition_glb_file(report_path, model_code)
obj = super(QuickEasyOrder, self).create(vals)
logging.info('---------向cloud生成模型库记录-------')
self.model_coloring(obj)
logging.info('---------开始派单到工厂-------')
self.distribute_to_factory(obj)
sale_order = self.distribute_to_factory(obj)
obj.sale_order_id = sale_order.id
obj.state = '待接单'
return obj
@@ -221,7 +222,7 @@ class QuickEasyOrder(models.Model):
self_machining_id,
item, order_id,
'self_machining',
i)
i, False)
# 创建坯料的bom
self_machining_bom = self.env['mrp.bom'].bom_create(self_machining_embryo, 'normal', False)
# 创建坯料里bom的组件
@@ -236,7 +237,8 @@ class QuickEasyOrder(models.Model):
# 创建坯料
outsource_embryo = self.env['product.template'].sudo().no_bom_product_create(outsource_id, item,
order_id,
'subcontract', i)
'subcontract', i,
False)
if outsource_embryo == -3:
self.cr.rollback()
return UserError(
@@ -255,7 +257,8 @@ class QuickEasyOrder(models.Model):
elif product.materials_type_id.gain_way == '采购':
purchase_embryo = self.env['product.template'].sudo().no_bom_product_create(purchase_id, item,
order_id,
'purchase', i)
'purchase', i,
False)
if purchase_embryo == -3:
self.cr.rollback()
return UserError(
@@ -264,6 +267,7 @@ class QuickEasyOrder(models.Model):
product_bom_purchase = self.env['mrp.bom'].bom_create(product, 'normal', False)
product_bom_purchase.bom_create_line_has(purchase_embryo)
order_id.with_user(self.env.ref("base.user_admin")).sale_order_create_line(product, item)
return order_id
except Exception as e:
# self.cr.rollback()
return UserError('工厂创建销售订单和产品失败,请联系管理员')

View File

@@ -33,10 +33,10 @@ class ReSaleOrder(models.Model):
payments_way = fields.Selection([('现结', '现结'), ('月结', '月结')], '结算方式', default='现结', tracking=True)
pay_way = fields.Selection([('转账', '转账'), ('微信', '微信'), ('支付宝', '支付宝')], '支付方式')
check_status = fields.Selection([('pending', '待审核'), ('approved', '已审核'), ('fail', '不通过')], '审核状态')
schedule_status = fields.Selection(
[('to schedule', '待排程'), ('to process', '待加工'), ('to deliver', '待发货'), ('to receive', '待收货'),
('received', '已收货')],
'进度状态')
# schedule_status = fields.Selection(
# [('to schedule', '待排程'), ('to process', '待加工'), ('to deliver', '待发货'), ('to receive', '待收货'),
# ('received', '已收货')],
# '进度状态')
payment_term_id = fields.Many2one(
comodel_name='account.payment.term',
string="交付条件",
@@ -85,9 +85,10 @@ class ReSaleOrder(models.Model):
self.check_status = 'pending'
def get_customer(self):
partner_tag = self.env['res.partner.category'].search([('name', '=', '业务平台')], limit=1, order='id asc')
partner_tag = self.env['res.partner.category'].sudo().search([('name', '=', '业务平台')], limit=1,
order='id asc')
if not partner_tag:
partner_tag = self.env['res.partner.category'].create({'name': '平台客户'})
partner_tag = self.env['res.partner.category'].sudo().create({'name': '平台客户'})
customer = self.env['res.partner'].search([('name', '=', '业务平台')], limit=1, order='id asc')
if customer:
if not customer.vat:
@@ -138,9 +139,28 @@ class ReSaleOrder(models.Model):
class ResaleOrderLine(models.Model):
_inherit = 'sale.order.line'
model_glb_file = fields.Binary('模型的glb文件')
model_glb_file = fields.Binary('模型的glb文件', compute='_compute_model_glb_file', store=True)
# product_template_id = fields.Many2one(
# string="产品",
# comodel_name='product.template',
# compute='_compute_product_template_id',
# readonly=False,
# search='_search_product_template_id',
# # previously related='product_id.product_tmpl_id'
# # not anymore since the field must be considered editable for product configurator logic
# # without modifying the related product_id when updated.
# domain=[('sale_ok', '=', True), ('categ_type', '=', '成品')])
check_status = fields.Selection(related='order_id.check_status')
@api.depends('product_template_id')
def _compute_model_glb_file(self):
for line in self:
if line.product_template_id:
if not line.model_glb_file:
line.model_glb_file = line.product_id.product_tmpl_id.model_file
if not line.price_unit:
line.price_unit = line.product_id.product_tmpl_id.list_price
class ProductTemplate(models.Model):
_inherit = 'product.template'
@@ -151,23 +171,18 @@ class ProductTemplate(models.Model):
class RePurchaseOrder(models.Model):
_inherit = 'purchase.order'
check_status = fields.Selection([('pending', '待审核'), ('approved', '已审核'), ('fail', '不通过')], '审核状态')
remark = fields.Text('备注')
user_id = fields.Many2one(
'res.users', string='买家', index=True, tracking=True,
compute='_compute_user_id',
store=True)
def button_confirming(self):
self.write({'state': 'purchase', 'check_status': 'pending'})
@api.depends('partner_id')
def _compute_user_id(self):
if not self.user_id:
if self.partner_id:
self.user_id = self.partner_id.purchase_user_id.id
self.check_status = 'pending'
self.state = 'purchase'
# self.state = 'purchase'
else:
self.user_id = self.env.user.id
@@ -190,28 +205,6 @@ class RePurchaseOrder(models.Model):
if not line.taxes_id:
raise UserError('请对【产品】中的【税】进行选择')
def write(self, vals):
if self.env.user.has_group('sf_base.group_purchase_director'):
if vals.get('check_status'):
if vals['check_status'] in ('pending', False):
vals['check_status'] = 'approved'
return super().write(vals)
def button_confirm(self):
for order in self:
if order.state not in ['draft', 'sent', 'purchase']:
continue
order.order_line._validate_analytic_distribution()
order._add_supplier_to_product()
# Deal with double validation process
if order._approval_allowed():
order.button_approve()
else:
order.write({'state': 'to approve'})
if order.partner_id not in order.message_partner_ids:
order.message_subscribe([order.partner_id.id])
return True
@api.onchange('order_line')
def _onchange_order_line(self):
for order in self:

View File

@@ -99,6 +99,26 @@
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_create" eval="1"/>
</record>
<record model="ir.rule" id="quick_easy_order_rule_salemanager">
<field name="name">销售经理只可以查看本人的快速订单</field>
<field name="model_id" ref="model_quick_easy_order"/>
<field name="domain_force">[('create_uid', '=',user.id)]</field>
<field name="groups" eval="[(4, ref('sf_base.group_sale_salemanager'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="0"/>
<field name="perm_create" eval="0"/>
</record>
<record model="ir.rule" id="quick_easy_order_rule_director">
<field name="name">销售总监查看所有快速订单</field>
<field name="model_id" ref="model_quick_easy_order"/>
<field name="domain_force">[(1,'=',1)]</field>
<field name="groups" eval="[(4, ref('sf_base.group_sale_director'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_create" eval="1"/>
</record>
</data>
</odoo>

View File

@@ -32,8 +32,8 @@ access_stock_picking_group_quality_director,stock_picking_group_quality_director
access_account_move_group_sale_salemanager,account_move_group_sale_salemanager,account.model_account_move,sf_base.group_sale_salemanager,1,0,0,0
access_account_move_group_sale_director,account_move_group_sale_director,account.model_account_move,sf_base.group_sale_director,1,0,0,0
access_resource_resource_group_sale_director,resource_resource_group_sale_director,resource.model_resource_resource,sf_base.group_sale_director,1,1,1,0
access_mrp_bom_group_sale_salemanager,mrp_bom_group_sale_salemanager,mrp.model_mrp_bom,sf_base.group_sale_salemanager,1,0,0,0
access_mrp_bom_group_sale_director,mrp_bom_group_sale_director,mrp.model_mrp_bom,sf_base.group_sale_director,1,0,0,0
access_mrp_bom_group_sale_salemanager,mrp_bom_group_sale_salemanager,mrp.model_mrp_bom,sf_base.group_sale_salemanager,1,0,1,0
access_mrp_bom_group_sale_director,mrp_bom_group_sale_director,mrp.model_mrp_bom,sf_base.group_sale_director,1,0,1,0
access_mrp_bom_group_purchase,mrp_bom_group_purchase,mrp.model_mrp_bom,sf_base.group_purchase,1,0,0,0
access_mrp_bom_group_purchase_director,mrp_bom_group_purchase_director,mrp.model_mrp_bom,sf_base.group_purchase_director,1,0,0,0
access_mrp_bom_group_quality,mrp_bom_group_quality,mrp.model_mrp_bom,sf_base.group_quality,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
32 access_account_move_group_sale_director account_move_group_sale_director account.model_account_move sf_base.group_sale_director 1 0 0 0
33 access_resource_resource_group_sale_director resource_resource_group_sale_director resource.model_resource_resource sf_base.group_sale_director 1 1 1 0
34 access_mrp_bom_group_sale_salemanager mrp_bom_group_sale_salemanager mrp.model_mrp_bom sf_base.group_sale_salemanager 1 0 0 1 0
35 access_mrp_bom_group_sale_director mrp_bom_group_sale_director mrp.model_mrp_bom sf_base.group_sale_director 1 0 0 1 0
36 access_mrp_bom_group_purchase mrp_bom_group_purchase mrp.model_mrp_bom sf_base.group_purchase 1 0 0 0
37 access_mrp_bom_group_purchase_director mrp_bom_group_purchase_director mrp.model_mrp_bom sf_base.group_purchase_director 1 0 0 0
38 access_mrp_bom_group_quality mrp_bom_group_quality mrp.model_mrp_bom sf_base.group_quality 1 0 0 0
39 access_mrp_bom_group_quality_director mrp_bom_group_quality_director mrp.model_mrp_bom sf_base.group_quality_director 1 0 0 0

View File

@@ -8,28 +8,15 @@
<field name="arch" type="xml">
<field name="partner_id" position="replace">
<field name="partner_id" widget="res_partner_many2one" context="{'is_supplier': True }"/>
<field name="check_status" invisible="1"/>
</field>
<field name="currency_id" position="after">
<field name="remark" attrs="{'readonly': ['&amp;',('state', 'in', ['purchase']),('check_status','in',
['pending','approved'])]}"/>
<field name="remark" attrs="{'readonly': [('state', 'in', ['purchase'])]}"/>
</field>
<xpath expr="//form/header/button[@name='action_rfq_send'][1]" position="after">
<button name="sf_sale.action_purchase_order_check_wizard" string="审核" type="action"
context="{'default_order_id':active_id}" groups="sf_base.group_purchase_director"
attrs="{'invisible': ['&amp;',('check_status','in', ['approved','fail']),('state', 'in', ['purchase','draft'])]}"
class="oe_highlight"/>
</xpath>
<xpath expr="//form/header/button[@name='button_confirm'][2]" position="replace">
<button name="button_confirm" type="object" context="{'validate_analytic': True}"
string="确认订单" id="draft_confirm"
groups="sf_base.group_purchase,sf_base.group_purchase_director"
attrs="{'invisible': ['|','&amp;','&amp;', ('state', 'in', ['purchase','draft']), ('check_status', 'in', ['approved']), ('date_approve', '!=', False),'&amp;', '&amp;',('state', 'in', ['purchase', 'draft']),('check_status', 'in', [False, 'pending', 'fail']),('date_approve', '=', False)]}"
/>
<button name="button_confirming" type="object"
string="确认订单" groups="sf_base.group_purchase,sf_base.group_purchase_director"
attrs="{'invisible': ['&amp;',('check_status','!=', False),('state', 'not in', ['draft','send'])]}"
attrs="{'invisible': [('state', 'in', ['purchase'])]}"
/>
</xpath>
<xpath expr="//form/header/button[@name='action_rfq_send'][1]" position="replace">
@@ -93,43 +80,38 @@
<attribute name="groups">sf_base.group_purchase,sf_base.group_purchase_director</attribute>
</xpath>
<xpath expr="//field[@name='order_line']" position="attributes">
<attribute name="attrs">{'readonly': ['&amp;',('state', 'in', ['purchase']),('check_status','in',
['pending','approved'])]}
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}
</attribute>
</xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='product_id']" position="attributes">
<attribute name="options">{'no_create': True}</attribute>
</xpath>
<field name="partner_ref" position="attributes">
<attribute name="attrs">{'readonly': ['&amp;',('state', 'in', ['purchase']),('check_status','in',
['pending','approved'])]}
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}
</attribute>
</field>
<field name="date_planned" position="attributes">
<attribute name="attrs">{'readonly': ['&amp;',('state', 'in', ['purchase']),('check_status','in',
['pending','approved'])]}
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}
</attribute>
</field>
<field name="receipt_reminder_email" position="attributes">
<attribute name="attrs">{'readonly': ['&amp;',('state', 'in', ['purchase']),('check_status','in',
['pending','approved'])]}
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}
</attribute>
</field>
<field name="user_id" position="attributes">
<attribute name="attrs">{'readonly': ['&amp;',('state', 'in', ['purchase']),('check_status','in',
['pending','approved'])]}
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}
</attribute>
</field>
<field name="origin" position="attributes">
<attribute name="attrs">{'readonly': ['&amp;',('state', 'in', ['purchase']),('check_status','in',
['pending','approved'])]}
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}
</attribute>
</field>
<field name="payment_term_id" position="attributes">
<attribute name="attrs">{'readonly': ['&amp;',('state', 'in', ['purchase']),('check_status','in',
['pending','approved'])]}
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}
</attribute>
</field>
<field name="fiscal_position_id" position="attributes">
<attribute name="attrs">{'readonly': ['&amp;',('state', 'in', ['purchase']),('check_status','in',
['pending','approved'])]}
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}
</attribute>
</field>
</field>
@@ -153,13 +135,10 @@
<field name="inherit_id" ref="purchase_stock.purchase_order_view_form_inherit"/>
<field name="arch" type="xml">
<field name="incoterm_id" position="attributes">
<attribute name="attrs">{'readonly': ['&amp;',('state', 'in', ['purchase']),('check_status','in',
['pending','approved'])]}
</attribute>
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}</attribute>
</field>
<field name="incoterm_location" position="attributes">
<attribute name="attrs">{'readonly': ['&amp;',('state', 'in', ['purchase']),('check_status','in',
['pending','approved'])]}
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}
</attribute>
</field>
</field>
@@ -171,14 +150,8 @@
<field name="inherit_id" ref="purchase.purchase_order_view_tree"/>
<field name="arch" type="xml">
<tree position="attributes">
<attribute name="default_order">check_status desc,date_approve asc</attribute>
<attribute name="default_order">date_approve asc</attribute>
</tree>
<field name="amount_total" position="after">
<field name="check_status" widget="badge"
decoration-success="check_status == 'approved'"
decoration-warning="check_status == 'pending'"
decoration-danger="check_status == 'fail'"/>
</field>
</field>
</record>

View File

@@ -26,6 +26,7 @@
<field name="quantity"/>
<field name="unit_price"/>
<field name="price"/>
<field name="sale_order_id"/>
<field name="create_uid" string="创建人"/>
<field name="create_date" string="创建时间"/>
<field optional="hide" name="delivery_time"/>
@@ -78,6 +79,8 @@
<field name="quantity" options="{'format': false}"/>
<field name="unit_price"/>
<field name="price" options="{'format': false}"/>
<field name="sale_order_id"
attrs='{"invisible": [("sale_order_id","=",False)],"readonly": [("sale_order_id","!=",False)]}'/>
</group>
</group>
</sheet>

View File

@@ -70,7 +70,6 @@
</xpath>
<xpath expr="//form/header/button[@name='action_confirm']" position="after">
<field name="check_status" invisible="1"/>
<field name="schedule_status" invisible="1"/>
<button name="sf_sale.action_sale_order_check_wizard" string="审核" type="action"
context="{'default_order_id':active_id}" groups="sf_base.group_sale_director"
attrs="{'invisible': ['|',('check_status','in', ['approved',False,'fail']),('state', 'in', ['cancel'])]}"
@@ -84,14 +83,14 @@
<xpath expr="//form/header/button[@name='action_confirm'][2]" position="replace">
<button name="action_confirm" data-hotkey="v"
string="确认" type="object" context="{'validate_analytic': True}"
attrs="{'invisible': ['|','&amp;',('check_status', '!=', 'approved'),('state', 'in', ['draft','cancel']),'&amp;','&amp;',('check_status', '=', 'approved'),('state', 'in', ['sale','cancel']),('schedule_status', 'not in', False)]}"/>
attrs="{'invisible': ['|','&amp;',('check_status', '!=', 'approved'),('state', 'in', ['draft','cancel']),'&amp;','&amp;',('check_status', '=', 'approved'),('state', 'in', ['sale','cancel']),('delivery_status', '!=', False)]}"/>
</xpath>
<xpath expr="//form/header/button[@name='action_cancel']" position="attributes">
<attribute name="attrs">{'invisible': ['|','&amp;',('state', 'in',
['cancel','draft']),('check_status',
'in',
[False,'approved']),'&amp;','&amp;',('check_status', '=', 'approved'),('state', 'in',
['sale','cancel','draft']),('schedule_status', 'not in', False)]}
[False,'approved']),'&amp;',('check_status', '=', 'approved'),('state', 'in',
['sale','cancel','draft'])]}
</attribute>
</xpath>
<xpath expr="//form/header/button[@name='action_draft']" position="attributes">
@@ -112,12 +111,16 @@
<field name="deadline_of_delivery" attrs="{'readonly': [('state', 'in', ('sale','cancel'))]}"/>
<field name="payments_way" attrs="{'readonly': [('state', 'in', ('sale','cancel'))]}"/>
<field name="pay_way" attrs="{'readonly': [('state', 'in', ('sale','cancel'))]}"/>
<field name="schedule_status" readonly="1"/>
<!-- <field name="schedule_status" readonly="1"/> -->
</field>
<xpath expr="//field[@name='order_line']/tree/field[@name='name']" position="before">
<field name="model_glb_file" widget="Viewer3D" optional="show"
string="模型文件" attrs="{'readonly': [('state', 'in', ['draft'])]}"/>
</xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='product_template_id']" position="attributes">
<attribute name="options">{'no_create': True}</attribute>
<attribute name="context">{'is_sale_order_line': True }</attribute>
</xpath>
<xpath expr="//field[@name='order_line']" position="attributes">
<attribute name="attrs">{'readonly': [('state', 'in', ['cancel','sale'])]}</attribute>
</xpath>
@@ -194,7 +197,7 @@
<field name="inherit_id" ref="sale.view_quotation_tree_with_onboarding"/>
<field name="arch" type="xml">
<tree position="attributes">
<attribute name="default_order">check_status desc,create_date asc</attribute>
<attribute name="default_order">create_date desc</attribute>
<attribute name="delete">False</attribute>
</tree>
<field name="name" position="attributes">
@@ -218,20 +221,21 @@
<field name="inherit_id" ref="sale.view_order_tree"/>
<field name="arch" type="xml">
<tree position="attributes">
<attribute name="default_order">schedule_status desc,date_order asc</attribute>
<!-- <attribute name="default_order">schedule_status desc,date_order asc</attribute> -->
<attribute name="default_order">create_date desc</attribute>
<attribute name="create">False</attribute>
</tree>
<field name="name" position="attributes">
<attribute name="string">订单号</attribute>
</field>
<field name="amount_total" position="after">
<field name="schedule_status" widget="badge"
decoration-success="schedule_status == 'received'"
decoration-warning="schedule_status == 'to process'"
decoration-danger="schedule_status == 'to receive'"
decoration-muted="schedule_status == 'to process'"
decoration-info="schedule_status == 'to schedule'"/>
</field>
<!-- <field name="amount_total" position="after"> -->
<!-- <field name="schedule_status" widget="badge" -->
<!-- decoration-success="schedule_status == 'received'" -->
<!-- decoration-warning="schedule_status == 'to process'" -->
<!-- decoration-danger="schedule_status == 'to receive'" -->
<!-- decoration-muted="schedule_status == 'to process'" -->
<!-- decoration-info="schedule_status == 'to schedule'"/> -->
<!-- </field> -->
</field>
</record>

View File

@@ -19,9 +19,9 @@
'views/functional_tool_views.xml',
'views/mrp_workcenter_views.xml',
'views/sf_maintenance_equipment.xml',
'views/menu_view.xml',
'views/tool_material_search.xml',
'views/fixture_material_search_views.xml',
'views/menu_view.xml',
'data/tool_data.xml',
],
'demo': [

View File

@@ -8,7 +8,7 @@ from odoo.http import request
class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/ToolGroup', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/ToolGroup', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
def get_functional_tool_groups_Info(self, **kw):
"""

View File

@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
import re
import logging
from datetime import timedelta
@@ -39,7 +41,7 @@ class MachineTableToolChangingApply(models.Model):
[('BT刀柄式', 'BT刀柄式'), ('SK刀柄式', 'SK刀柄式'), ('HSK刀柄式', 'HSK刀柄式'),
('CAT刀柄式', 'CAT刀柄式'), ('ISO刀盘式', 'ISO刀盘式'), ('DIN刀盘式', 'DIN刀盘式'),
('直装固定式', '直装固定式')], string='刀位接口型号')
diameter = fields.Integer(string='刀具直径(mm)')
diameter = fields.Float(string='刀具直径(mm)')
knife_tip_r_angle = fields.Float(string='刀尖R角(mm)')
max_lifetime_value = fields.Integer(string='最大寿命值(min)')
alarm_value = fields.Integer(string='报警值(min)')
@@ -207,7 +209,7 @@ class CAMWorkOrderProgramKnifePlan(models.Model):
functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型',
compute='_compute_tool_number', store=True)
tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', compute='_compute_tool_number', store=True)
diameter = fields.Integer(string='刀具直径(mm)', compute='_compute_tool_number', store=True)
diameter = fields.Float(string='刀具直径(mm)', compute='_compute_tool_number', store=True)
tool_included_angle = fields.Float(string='刀尖R角(mm)', compute='_compute_tool_number', store=True)
tool_loading_length = fields.Float(string='总长度(mm)', compute='_compute_tool_number', store=True)
extension_length = fields.Float(string='伸出长(mm)')
@@ -256,15 +258,6 @@ class CAMWorkOrderProgramKnifePlan(models.Model):
names = categories._search([], order=order, access_rights_uid=SUPERUSER_ID)
return categories.browse(names)
@api.onchange('plan_execute_status')
def _onchange_plan_execute_status(self):
for record in self:
if record.plan_execute_status == '已完成':
self.env['sf.cnc.processing'].register_cnc_processing(record)
def knife_plan_cnc_processing(self):
self.env['sf.cnc.processing'].register_cnc_processing(self)
def apply_for_tooling(self):
"""
申请装刀
@@ -352,13 +345,13 @@ class CAMWorkOrderProgramKnifePlan(models.Model):
# 创建装刀请求
knife_plan.apply_for_tooling()
else:
logging.info('功能刀具【%s】满足CNC用刀需求')
logging.info('功能刀具【%s】满足CNC用刀需求' % cnc_processing.cutting_tool_name)
class FunctionalToolAssembly(models.Model):
_name = 'sf.functional.tool.assembly'
_description = '功能刀具组装'
_order = 'use_tool_time asc'
_order = 'assemble_status, use_tool_time asc'
@api.depends('functional_tool_name')
def _compute_name(self):
@@ -375,7 +368,7 @@ class FunctionalToolAssembly(models.Model):
functional_tool_name = fields.Char(string='功能刀具名称', readonly=True)
functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', readonly=True,
group_expand='_read_group_functional_tool_type_ids')
functional_tool_diameter = fields.Integer(string='功能刀具直径(mm)', readonly=True)
functional_tool_diameter = fields.Float(string='功能刀具直径(mm)', readonly=True)
knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', readonly=True)
coarse_middle_thin = fields.Selection([("1", ""), ('2', ''), ('3', '')], string='粗/中/精', readonly=True)
new_former = fields.Selection([('0', ''), ('1', '')], string='新/旧', readonly=True)
@@ -423,7 +416,8 @@ class FunctionalToolAssembly(models.Model):
def _compute_integral_product_id(self):
for item in self:
if item.integral_freight_barcode:
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', item.integral_freight_barcode)])
location = self.env['sf.shelf.location'].sudo().search(
[('barcode', '=', item.integral_freight_barcode)])
if location:
item.integral_product_id = location.product_id.id
else:
@@ -442,7 +436,7 @@ class FunctionalToolAssembly(models.Model):
@api.depends('blade_freight_barcode')
def _compute_blade_product_id(self):
for item in self:
if item.integral_freight_barcode:
if item.blade_freight_barcode:
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', item.blade_freight_barcode)])
if location:
item.blade_product_id = location.product_id.id
@@ -462,7 +456,7 @@ class FunctionalToolAssembly(models.Model):
@api.depends('bar_freight_barcode')
def _compute_bar_product_id(self):
for item in self:
if item.integral_freight_barcode:
if item.bar_freight_barcode:
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', item.bar_freight_barcode)])
if location:
item.bar_product_id = location.product_id.id
@@ -482,7 +476,7 @@ class FunctionalToolAssembly(models.Model):
@api.depends('pad_freight_barcode')
def _compute_pad_product_id(self):
for item in self:
if item.integral_freight_barcode:
if item.pad_freight_barcode:
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', item.pad_freight_barcode)])
if location:
item.pad_product_id = location.product_id.id
@@ -523,7 +517,7 @@ class FunctionalToolAssembly(models.Model):
@api.depends('chuck_freight_barcode')
def _compute_chuck_product_id(self):
for item in self:
if item.integral_freight_barcode:
if item.chuck_freight_barcode:
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', item.chuck_freight_barcode)])
if location:
item.chuck_product_id = location.product_id.id
@@ -548,7 +542,7 @@ class FunctionalToolAssembly(models.Model):
after_assembly_functional_tool_name = fields.Char(string='组装后功能刀具名称', readonly=True)
after_assembly_functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model',
string='组装后功能刀具类型', readonly=True)
after_assembly_functional_tool_diameter = fields.Integer(string='组装后功能刀具直径(mm)', readonly=True)
after_assembly_functional_tool_diameter = fields.Float(string='组装后功能刀具直径(mm)', readonly=True)
after_assembly_knife_tip_r_angle = fields.Float(string='组装后刀尖R角(mm)', readonly=True)
after_assembly_new_former = fields.Selection([('0', ''), ('1', '')], string='组装后新/旧', readonly=True)
cut_time = fields.Integer(string='已切削时间(min)', readonly=True)
@@ -631,3 +625,296 @@ class FunctionalToolAssembly(models.Model):
code = self._get_code(obj.loading_task_source)
obj.assembly_order_code = code
return obj
class FunctionalToolDismantle(models.Model):
_name = 'sf.functional.tool.dismantle'
_inherit = ["barcodes.barcode_events_mixin"]
_description = '功能刀具拆解'
def on_barcode_scanned(self, barcode):
"""
扫码
"""
# 对barcode进行校验是否为货位编码
if not re.match(r'^[A-Za-z0-9]+-[A-Za-z0-9]+-\d{3}-\d{3}$', barcode):
tool_id = self.env['sf.functional.cutting.tool.entity'].sudo().search(
[('rfid', '=', barcode), ('functional_tool_status', '!=', '已拆除')])
if tool_id:
self.functional_tool_id = tool_id.id
else:
raise ValidationError('Rfid为【%s】的功能刀具不存在,请重新扫描!' % barcode)
else:
if self.dismantle_cause == '更换为其他刀具':
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', barcode)])
if not location:
raise ValidationError('%s】该货位不存在,请重新扫码!' % barcode)
else:
if not location.product_id:
# 判断功能刀具存在哪些刀具物料需要录入库位
if self.chuck_product_id: # 夹头
if not self.chuck_freight_id:
self.chuck_freight_id = location.id
return True
if self.integral_product_id: # 整体式刀具
if not self.integral_freight_id:
self.integral_freight_id = location.id
return True
elif self.blade_product_id: # 刀片
if not self.blade_freight_id:
self.blade_freight_id = location.id
return True
if self.bar_product_id: # 刀杆
if not self.bar_freight_id:
self.bar_freight_id = location.id
return True
elif self.pad_product_id: # 刀盘
if not self.pad_freight_id:
self.pad_freight_id = location.id
return True
else:
# 判断货位存放的是那个刀具物料产品
if self.integral_product_id == location.product_id: # 整体式刀具
self.integral_freight_id = location.id
elif self.blade_product_id == location.product_id: # 刀片
self.blade_freight_id = location.id
elif self.bar_product_id == location.product_id: # 刀杆
self.bar_freight_id = location.id
elif self.pad_product_id == location.product_id: # 刀盘
self.pad_freight_id = location.id
elif self.chuck_product_id == location.product_id: # 夹头
self.chuck_freight_id = location.id
elif self.dismantle_cause in ['寿命到期报废', '崩刀报废']:
raise ValidationError('该功能刀具因为%s拆解,无需录入库位' % self.dismantle_cause)
else:
raise ValidationError('该功能刀具因为%s拆解,无需录入库位' % self.dismantle_cause)
name = fields.Char('名称', related='functional_tool_id.name')
functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', required=True,
domain=[('functional_tool_status', '!=', '已拆除')])
tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', store=True,
compute='_compute_functional_tool_num')
tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', compute='_compute_functional_tool_num', store=True)
diameter = fields.Float(string='刀具直径(mm)', compute='_compute_functional_tool_num', store=True)
knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', compute='_compute_functional_tool_num', store=True)
rfid = fields.Char('Rfid', compute='_compute_functional_tool_num', store=True)
rfid_dismantle = fields.Char('Rfid(已拆解)', readonly=True)
dismantle_cause = fields.Selection(
[('寿命到期报废', '寿命到期报废'), ('崩刀报废', '崩刀报废'), ('更换为其他刀具', '更换为其他刀具'),
('刀具需磨削', '刀具需磨削')], string='拆解原因', required=True)
dismantle_data = fields.Datetime('拆解日期', readonly=True)
dismantle_person = fields.Char('拆解人', readonly=True)
image = fields.Binary('图片', readonly=True)
scrap_id = fields.Char('报废单号', readonly=True)
grinding_id = fields.Char('磨削单号', readonly=True)
state = fields.Selection([('待拆解', '待拆解'), ('已拆解', '已拆解')], default='待拆解')
active = fields.Boolean('有效', default=True)
# 刀柄
handle_product_id = fields.Many2one('product.product', string='刀柄', compute='_compute_functional_tool_num',
store=True)
handle_type_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀柄型号',
related='handle_product_id.cutting_tool_model_id')
handle_brand_id = fields.Many2one('sf.machine.brand', string='刀柄品牌', related='handle_product_id.brand_id')
handle_rfid = fields.Char(string='刀柄Rfid', compute='_compute_functional_tool_num', store=True)
scrap_boolean = fields.Boolean(string='刀柄是否报废', default=False)
# 整体式
integral_product_id = fields.Many2one('product.product', string='整体式刀具',
compute='_compute_functional_tool_num', store=True)
integral_type_id = fields.Many2one('sf.cutting_tool.standard.library', string='整体式刀具型号',
related='integral_product_id.cutting_tool_model_id')
integral_brand_id = fields.Many2one('sf.machine.brand', string='整体式刀具品牌',
related='integral_product_id.brand_id')
integral_freight_id = fields.Many2one('sf.shelf.location', '整体式刀具目标货位',
domain="[('product_id', 'in', (integral_product_id, False))]")
# 刀片
blade_product_id = fields.Many2one('product.product', string='刀片', compute='_compute_functional_tool_num',
store=True)
blade_type_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀片型号',
related='blade_product_id.cutting_tool_model_id')
blade_brand_id = fields.Many2one('sf.machine.brand', string='刀片品牌', related='blade_product_id.brand_id')
blade_freight_id = fields.Many2one('sf.shelf.location', '刀片目标货位',
domain="[('product_id', 'in', (blade_product_id, False))]")
# 刀杆
bar_product_id = fields.Many2one('product.product', string='刀杆', compute='_compute_functional_tool_num',
store=True)
bar_type_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀杆型号',
related='bar_product_id.cutting_tool_model_id')
bar_brand_id = fields.Many2one('sf.machine.brand', string='刀杆品牌', related='bar_product_id.brand_id')
bar_freight_id = fields.Many2one('sf.shelf.location', '刀杆目标货位',
domain="[('product_id', 'in', (bar_product_id, False))]")
# 刀盘
pad_product_id = fields.Many2one('product.product', string='刀盘', compute='_compute_functional_tool_num',
store=True)
pad_type_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀盘型号',
related='pad_product_id.cutting_tool_model_id')
pad_brand_id = fields.Many2one('sf.machine.brand', string='刀盘品牌', related='pad_product_id.brand_id')
pad_freight_id = fields.Many2one('sf.shelf.location', '刀盘目标货位',
domain="[('product_id', 'in', (pad_product_id, False))]")
# 夹头
chuck_product_id = fields.Many2one('product.product', string='夹头', compute='_compute_functional_tool_num',
store=True)
chuck_type_id = fields.Many2one('sf.cutting_tool.standard.library', string='夹头型号',
related='chuck_product_id.cutting_tool_model_id')
chuck_brand_id = fields.Many2one('sf.machine.brand', string='夹头品牌', related='chuck_product_id.brand_id')
chuck_freight_id = fields.Many2one('sf.shelf.location', '夹头目标货位',
domain="[('product_id', 'in', (chuck_product_id, False))]")
@api.onchange('functional_tool_id')
def _onchange_freight(self):
for item in self:
item.integral_freight_id = False
item.blade_freight_id = False
item.bar_freight_id = False
item.pad_freight_id = False
item.chuck_freight_id = False
@api.depends('functional_tool_id')
def _compute_functional_tool_num(self):
for item in self:
if item.functional_tool_id:
item.tool_groups_id = item.functional_tool_id.tool_groups_id.id
item.tool_type_id = item.functional_tool_id.sf_cutting_tool_type_id.id
item.diameter = item.functional_tool_id.functional_tool_diameter
item.knife_tip_r_angle = item.functional_tool_id.knife_tip_r_angle
item.rfid = item.functional_tool_id.rfid
item.handle_rfid = item.functional_tool_id.rfid
item.handle_product_id = item.functional_tool_id.functional_tool_name_id.handle_product_id.id
item.integral_product_id = item.functional_tool_id.functional_tool_name_id.integral_product_id.id
item.blade_product_id = item.functional_tool_id.functional_tool_name_id.blade_product_id.id
item.bar_product_id = item.functional_tool_id.functional_tool_name_id.bar_product_id.id
item.pad_product_id = item.functional_tool_id.functional_tool_name_id.pad_product_id.id
item.chuck_product_id = item.functional_tool_id.functional_tool_name_id.chuck_product_id.id
else:
item.tool_groups_id = False
item.tool_type_id = False
item.diameter = 0
item.knife_tip_r_angle = 0
item.rfid = ''
item.handle_rfid = ''
item.handle_product_id = False
item.integral_product_id = False
item.blade_product_id = False
item.bar_product_id = False
item.pad_product_id = False
item.chuck_product_id = False
def confirmation_disassembly(self):
logging.info('%s刀具确认开始拆解' % self.dismantle_cause)
if self.functional_tool_id.functional_tool_status == '已拆除':
raise ValidationError('Rfid为【%s】的功能刀具已经拆解,请勿重复操作!' % self.functional_tool_id.rfid_dismantle)
location = self.env['stock.location'].search([('name', '=', '刀具组装位置')])
location_dest = self.env['stock.location'].search([('name', '=', '刀具房')])
# =================刀柄是否[报废]拆解=======
location_dest_scrap = self.env['stock.location'].search([('name', '=', 'Scrap')])
if self.handle_rfid:
lot = self.env['stock.lot'].sudo().search([('rfid', '=', self.handle_rfid)])
if not lot:
raise ValidationError('Rfid为【%s】的功能刀具序列号不存在!' % self.handle_rfid)
functional_tool_assembly = self.functional_tool_id.functional_tool_name_id
if self.scrap_boolean:
# 刀柄报废 入库到Scrap
lot.create_stock_quant(location, location_dest_scrap, functional_tool_assembly.id, '功能刀具拆解',
functional_tool_assembly, functional_tool_assembly.tool_groups_id)
else:
# 刀柄不报废 入库到刀具房
lot.create_stock_quant(location, location_dest, functional_tool_assembly.id, '功能刀具拆解',
functional_tool_assembly, functional_tool_assembly.tool_groups_id)
# ==============功能刀具[报废]拆解================
if self.dismantle_cause in ['寿命到期报废', '崩刀报废']:
# 除刀柄外物料报废 入库到Scrap
if self.integral_product_id:
self.integral_product_id.dismantle_stock_moves(False, location, location_dest_scrap)
elif self.blade_product_id:
self.blade_product_id.dismantle_stock_moves(False, location, location_dest_scrap)
if self.bar_product_id:
self.bar_product_id.dismantle_stock_moves(False, location, location_dest_scrap)
elif self.pad_product_id:
self.pad_product_id.dismantle_stock_moves(False, location, location_dest_scrap)
if self.chuck_product_id:
self.chuck_product_id.dismantle_stock_moves(False, location, location_dest_scrap)
# ===========功能刀具[磨削]拆解==============
elif self.dismantle_cause in ['刀具需磨削']:
location_dest = self.env['stock.location'].search([('name', '=', '磨削房')])
# 除刀柄外物料拆解 入库到具体库位
if self.integral_product_id:
self.integral_product_id.dismantle_stock_moves(False, location, location_dest)
elif self.blade_product_id:
self.blade_product_id.dismantle_stock_moves(False, location, location_dest)
if self.bar_product_id:
self.bar_product_id.dismantle_stock_moves(False, location, location_dest)
elif self.pad_product_id:
self.pad_product_id.dismantle_stock_moves(False, location, location_dest)
if self.chuck_product_id:
self.chuck_product_id.dismantle_stock_moves(False, location, location_dest)
# ==============功能刀具[更换]拆解==============
elif self.dismantle_cause in ['更换为其他刀具']:
# 除刀柄外物料拆解 入库到具体库位
if self.integral_freight_id:
self.integral_product_id.dismantle_stock_moves(self.integral_freight_id.barcode, location,
location_dest)
elif self.blade_freight_id:
self.blade_product_id.dismantle_stock_moves(self.blade_freight_id.barcode, location, location_dest)
if self.bar_freight_id:
self.bar_product_id.dismantle_stock_moves(self.bar_freight_id.barcode, location, location_dest)
elif self.pad_freight_id:
self.pad_product_id.dismantle_stock_moves(self.pad_freight_id.barcode, location, location_dest)
if self.chuck_freight_id:
self.chuck_product_id.dismantle_stock_moves(self.chuck_freight_id.barcode, location, location_dest)
# ===============删除功能刀具的Rfid字段的值 赋值给Rfid(已拆解)字段=====
self.functional_tool_id.write({
'rfid_dismantle': self.functional_tool_id.rfid,
'rfid': '',
'functional_tool_status': '已拆除'
})
# 修改拆解单的值
self.write({
'rfid_dismantle': self.rfid,
'dismantle_data': fields.Datetime.now(),
'dismantle_person': self.env.user.name,
'rfid': '',
'state': '已拆解'
})
logging.info('%s】刀具拆解成功!' % self.name)
class ProductProduct(models.Model):
_inherit = 'product.product'
def dismantle_stock_moves(self, shelf_location_barcode, location_id, location_dest_id):
# 创建功能刀具拆解单产品库存移动记录
stock_move_id = self.env['stock.move'].sudo().create({
'name': '功能刀具拆解',
'product_id': self.id,
'location_id': location_id.id,
'location_dest_id': location_dest_id.id,
'product_uom_qty': 1.00,
'state': 'done'
})
if shelf_location_barcode:
location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', shelf_location_barcode)])
location.product_num = location.product_num + 1
else:
location = self.env['sf.shelf.location']
# 创建移动历史记录
stock_move_line_id = self.env['stock.move.line'].sudo().create({
'product_id': self.id,
'move_id': stock_move_id.id,
'current_location_id': location.id,
'install_tool_time': fields.Datetime.now(),
'qty_done': 1.0,
'state': 'done',
})
return stock_move_id, stock_move_line_id

View File

@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
import logging
import re
from datetime import timedelta
from odoo import SUPERUSER_ID
@@ -11,9 +13,11 @@ class FunctionalCuttingToolEntity(models.Model):
_description = '功能刀具列表'
functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具组装单', readonly=True)
tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', related='functional_tool_name_id.tool_groups_id')
code = fields.Char('编码')
rfid = fields.Char('Rfid', readonly=True)
rfid_dismantle = fields.Char('Rfid(已拆解)', readonly=True)
name = fields.Char('名称')
tool_name_id = fields.Many2one('sf.tool.inventory', '功能刀具名称')
sf_cutting_tool_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀具型号')
@@ -21,7 +25,7 @@ class FunctionalCuttingToolEntity(models.Model):
sf_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型',
group_expand='_read_group_mrs_cutting_tool_type_id', compute_sudo=True)
functional_tool_diameter = fields.Integer(string='刀具直径(mm)', readonly=True)
functional_tool_diameter = fields.Float(string='刀具直径(mm)', readonly=True)
knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', readonly=True)
coarse_middle_thin = fields.Selection([("1", ""), ('2', ''), ('3', '')], string='粗/中/精', readonly=True)
new_former = fields.Selection([('0', ''), ('1', '')], string='新/旧', readonly=True)
@@ -44,7 +48,17 @@ class FunctionalCuttingToolEntity(models.Model):
active = fields.Boolean(string='已归档', default=True)
@api.depends('barcode_id.quant_ids')
safe_inventory_id = fields.Many2one('sf.real.time.distribution.of.functional.tools',
string='功能刀具安全库存', readonly=True)
def button_safe_inventory_id(self):
"""更新功能刀具关联的安全库存记录"""
tool_is = self.search([])
for item in tool_is:
item.safe_inventory_id = self.env['sf.real.time.distribution.of.functional.tools'].search(
[('functional_name_id', '=', item.tool_name_id.id)])[0]
@api.depends('barcode_id.quant_ids', 'functional_tool_status')
def _compute_current_location_id(self):
for record in self:
if record.barcode_id.quant_ids:
@@ -60,6 +74,12 @@ class FunctionalCuttingToolEntity(models.Model):
else:
record.current_location_id = False
record.current_location = False
if record.functional_tool_status == '已拆除':
record.current_location_id = False
record.current_location = False
record.tool_room_num = 0
record.line_edge_knife_library_num = 0
record.machine_knife_library_num = 0
def get_location_num(self):
"""
@@ -128,13 +148,11 @@ class FunctionalCuttingToolEntity(models.Model):
def _compute_maintenance_equipment_image(self):
for record in self:
if record.cutting_tool_integral_model_id:
print(record.cutting_tool_integral_model_id)
record.sudo().suitable_machining_method_ids = record.cutting_tool_integral_model_id.suitable_machining_method_ids.ids
record.sudo().blade_tip_characteristics_id = record.cutting_tool_integral_model_id.blade_tip_characteristics_id.id
record.sudo().handle_type_id = record.cutting_tool_integral_model_id.handle_type_id.id
record.sudo().cutting_direction_ids = record.cutting_tool_integral_model_id.cutting_direction_ids.ids
record.sudo().suitable_coolant_ids = record.cutting_tool_integral_model_id.suitable_coolant_ids.ids
print(record.cutting_tool_integral_model_id.blade_tip_characteristics_id.ids)
elif record.cutting_tool_blade_model_id:
record.sudo().suitable_machining_method_ids = record.cutting_tool_blade_model_id.suitable_machining_method_ids.ids
record.sudo().blade_tip_characteristics_id = record.cutting_tool_blade_model_id.blade_tip_characteristics_id.id
@@ -184,7 +202,8 @@ class FunctionalCuttingToolEntity(models.Model):
stock_location_id = self.env['stock.location'].search([('name', '=', '制造前')])
# 创建功能刀具该批次/序列号 库存移动和移动历史
self.barcode_id.create_stock_quant(location_inventory_id, stock_location_id,
self.functional_tool_name_id.id, '机床装刀', self.functional_tool_name_id)
self.functional_tool_name_id.id, '机床装刀', self.functional_tool_name_id,
self.functional_tool_name_id.tool_groups_id)
# ==========刀具组接口==========
# def _register_functional_tool_groups(self, obj):
@@ -236,7 +255,7 @@ class FunctionalToolWarning(models.Model):
functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具名称')
barcode_id = fields.Many2one('stock.lot', string='功能刀具序列号', related='functional_tool_name_id.barcode_id')
mrs_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型')
diameter = fields.Integer(string='刀具直径(mm)')
diameter = fields.Float(string='刀具直径(mm)')
knife_tip_r_angle = fields.Float(string='刀尖R角(mm)')
# 其他信息
install_tool_time = fields.Datetime("刀具组装时间", related='functional_tool_name_id.tool_loading_time')
@@ -290,7 +309,7 @@ class StockMoveLine(models.Model):
functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', store=True,
group_expand='_read_group_functional_tool_type_id')
functional_tool_name = fields.Char('刀具名称')
diameter = fields.Integer(string='刀具直径(mm)')
diameter = fields.Float(string='刀具直径(mm)')
knife_tip_r_angle = fields.Float(string='刀尖R角(mm)')
install_tool_time = fields.Datetime("刀具组装时间", default=fields.Datetime.now())
code = fields.Char('编码')
@@ -312,7 +331,7 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', readonly=False, required=True)
sf_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', readonly=False,
group_expand='_read_mrs_cutting_tool_type_ids', store=True)
diameter = fields.Integer(string='刀具直径(mm)', readonly=False)
diameter = fields.Float(string='刀具直径(mm)', readonly=False)
knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', readonly=False)
tool_stock_num = fields.Integer(string='刀具房数量')
side_shelf_num = fields.Integer(string='线边刀库数量')
@@ -320,7 +339,8 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
tool_stock_total = fields.Integer(string='当前库存量', readonly=True)
min_stock_num = fields.Integer('最低库存量')
max_stock_num = fields.Integer('最高库存量')
batch_replenishment_num = fields.Integer('批次补货量', readonly=True, compute='_compute_batch_replenishment_num')
batch_replenishment_num = fields.Integer('批次补货量', readonly=True, compute='_compute_batch_replenishment_num',
store=True)
unit = fields.Char('单位')
image = fields.Binary('图片', readonly=False)
@@ -330,28 +350,31 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
suitable_machining_method_ids = fields.Many2many(
'maintenance.equipment.image', 'rel_machining_product_template_distribution', '适合加工方式',
domain=[('type', '=', '加工能力')],
related='sf_functional_cutting_tool_entity_ids.suitable_machining_method_ids')
related='sf_functional_tool_entity_ids.suitable_machining_method_ids')
blade_tip_characteristics_id = fields.Many2one(
'maintenance.equipment.image', '刀尖特征',
domain=[('type', '=', '刀尖特征')],
related='sf_functional_cutting_tool_entity_ids.blade_tip_characteristics_id')
related='sf_functional_tool_entity_ids.blade_tip_characteristics_id')
handle_type_id = fields.Many2one(
'maintenance.equipment.image', '柄部类型',
domain=[('type', '=', '柄部类型')], related='sf_functional_cutting_tool_entity_ids.handle_type_id')
domain=[('type', '=', '柄部类型')], related='sf_functional_tool_entity_ids.handle_type_id')
cutting_direction_ids = fields.Many2many(
'maintenance.equipment.image', 'rel_cutting_product_template_distribution', '走刀方向',
domain=[('type', '=', '走刀方向')], related='sf_functional_cutting_tool_entity_ids.cutting_direction_ids')
domain=[('type', '=', '走刀方向')], related='sf_functional_tool_entity_ids.cutting_direction_ids')
suitable_coolant_ids = fields.Many2many(
'maintenance.equipment.image', 'rel_coolants_product_template_distribution', '适合冷却方式',
domain=[('type', '=', '冷却方式')], related='sf_functional_cutting_tool_entity_ids.suitable_coolant_ids')
domain=[('type', '=', '冷却方式')], related='sf_functional_tool_entity_ids.suitable_coolant_ids')
sf_functional_cutting_tool_entity_ids = fields.Many2many('sf.functional.cutting.tool.entity',
'sf_functional_cutting_tool_entity_ref',
string='功能刀具列表信息', readonly=True)
string='功能刀具列表信息')
sf_functional_tool_assembly_ids = fields.Many2many('sf.functional.tool.assembly', 'sf_functional_tool_assembly_ref',
'功能刀具组装单', readonly=True)
sf_functional_tool_entity_ids = fields.One2many('sf.functional.cutting.tool.entity', 'safe_inventory_id',
string='功能刀具信息')
active = fields.Boolean(string='已归档', default=True)
@api.onchange('functional_name_id')
@@ -382,12 +405,11 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
mrs_cutting_tool_type_ids = categories._search([], order=order, access_rights_uid=SUPERUSER_ID)
return categories.browse(mrs_cutting_tool_type_ids)
@api.depends('sf_functional_cutting_tool_entity_ids', 'min_stock_num', 'max_stock_num')
@api.depends('sf_functional_tool_entity_ids', 'min_stock_num', 'max_stock_num')
def _compute_batch_replenishment_num(self):
for tool in self:
if tool:
# 判断功能刀具组装单是否已经完成
tool.sudo().estimate_functional_tool_assembly_ids(tool)
# 计算刀具房数量、线边刀库数量、机内刀库数量
tool.sudo().get_stock_num(tool)
# 计算当前库存量
tool.sudo().tool_stock_total = tool.tool_stock_num + tool.side_shelf_num + tool.on_tool_stock_num
@@ -399,9 +421,10 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
计算批次补货量
"""
if tool.tool_stock_total < tool.min_stock_num:
tool.sudo().batch_replenishment_num = tool.max_stock_num - tool.tool_stock_total
# 根据判断创建功能刀具组装单
if not tool.sf_functional_tool_assembly_ids and re.match(r'^\d+$', str(tool.id)):
if not tool.sf_functional_tool_assembly_ids.filtered(lambda item: item.assemble_status == '0') and re.match(
r'^\d+$', str(tool.id)):
tool.sudo().batch_replenishment_num = tool.max_stock_num - tool.tool_stock_total
for i in range(tool.batch_replenishment_num):
tool.sudo().create_functional_tool_assembly(tool)
print(i, ": ", tool.sf_functional_tool_assembly_ids)
@@ -428,15 +451,6 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
})
tool.sudo().sf_functional_tool_assembly_ids = [(4, functional_tool_assembly.id)]
def estimate_functional_tool_assembly_ids(self, tool):
"""
判断功能刀具组装单是否完成如果全部完成清空sf_functional_tool_assembly_ids的数据
"""
for sf_functional_tool_assembly_id in tool.sf_functional_tool_assembly_ids:
if sf_functional_tool_assembly_id.assemble_status == '0':
return False
tool.sudo().sf_functional_tool_assembly_ids = []
def get_stock_num(self, tool):
"""
计算刀具房数量、线边刀库数量、机内刀库数量
@@ -445,30 +459,31 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
tool.tool_stock_num = 0
tool.side_shelf_num = 0
tool.on_tool_stock_num = 0
if tool.sf_functional_cutting_tool_entity_ids:
for cutting_tool in tool.sf_functional_cutting_tool_entity_ids:
if tool.sf_functional_tool_entity_ids:
for cutting_tool in tool.sf_functional_tool_entity_ids:
if cutting_tool.tool_room_num > 0:
tool.tool_stock_num += 1
elif cutting_tool.line_edge_knife_library_num > 0:
tool.side_shelf_num += 1
elif cutting_tool.machine_knife_library_num > 0:
tool.on_tool_stock_num += 1
else:
tool.tool_stock_num = 0
tool.side_shelf_num = 0
tool.on_tool_stock_num = 0
def create_or_edit_safety_stock(self, vals, sf_functional_cutting_tool_entity_ids):
def create_or_edit_safety_stock(self, vals, sf_functional_tool_entity_ids):
"""
根据传入的信息新增或者更新功能刀具安全库存的信息
"""
# 根据功能刀具名称、刀具组、直径或尖刀R角、粗/中/精查询该功能刀具是否已经存在
record = self.env['sf.real.time.distribution.of.functional.tools'].search(
[('functional_name_id', '=', vals['functional_name_id']),
('sf_cutting_tool_type_id', '=', vals['sf_cutting_tool_type_id']),
('diameter', '=', vals['diameter']), ('knife_tip_r_angle', '=', vals['knife_tip_r_angle']),
('coarse_middle_thin', '=', vals['coarse_middle_thin']), ('tool_groups_id', '=', vals['tool_groups_id'])])
[('functional_name_id', '=', vals['functional_name_id'])])
if len(record) > 0:
for obj in record:
obj.write({'sf_functional_cutting_tool_entity_ids': [(4, sf_functional_cutting_tool_entity_ids.id)]})
obj.write({'sf_functional_tool_entity_ids': [(4, sf_functional_tool_entity_ids.id)]})
else:
vals['sf_functional_cutting_tool_entity_ids'] = sf_functional_cutting_tool_entity_ids.ids
vals['sf_functional_tool_entity_ids'] = sf_functional_tool_entity_ids.ids
self.env['sf.real.time.distribution.of.functional.tools'].create(vals)
status_create = fields.Boolean('是否是新增状态', default=True)

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