diff --git a/jikimo_frontend/static/src/js/custom_form_status_indicator.js b/jikimo_frontend/static/src/js/custom_form_status_indicator.js
index 306142d4..912c8efa 100644
--- a/jikimo_frontend/static/src/js/custom_form_status_indicator.js
+++ b/jikimo_frontend/static/src/js/custom_form_status_indicator.js
@@ -5,7 +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 {StatusBarField} from "@web/views/fields/statusbar/statusbar_field";
import {Field} from "@web/views/fields/field";
@@ -153,34 +153,34 @@ 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(`
`)
- }
- } catch (e) {
- console.log(e)
- }
- }
-})
+// 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(``)
+// }
+// } catch (e) {
+// console.log(e)
+// }
+// }
+// })
$(function () {
document.addEventListener('click', function () {
diff --git a/jikimo_frontend/static/src/scss/custom_style.scss b/jikimo_frontend/static/src/scss/custom_style.scss
index 0b6fb8bb..d7e6414c 100644
--- a/jikimo_frontend/static/src/scss/custom_style.scss
+++ b/jikimo_frontend/static/src/scss/custom_style.scss
@@ -530,4 +530,5 @@ div:has(.o_required_modifier) > label::before {
// 修复表格内容覆盖表头bug
.o_list_renderer .o_list_table tbody th {
position: unset;
-}
\ No newline at end of file
+}
+
diff --git a/jikimo_system_order/__init__.py b/jikimo_system_order/__init__.py
new file mode 100644
index 00000000..bbc55806
--- /dev/null
+++ b/jikimo_system_order/__init__.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+
+from . import controllers
+from . import models
+from . import wizard
diff --git a/jikimo_system_order/__manifest__.py b/jikimo_system_order/__manifest__.py
new file mode 100644
index 00000000..7f85ed57
--- /dev/null
+++ b/jikimo_system_order/__manifest__.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+{
+ 'name': "jikimo_system_order",
+
+ 'summary': """
+ 系统工单""",
+
+ 'description': """
+ 用于处理针对系统的工作任务;
+ 员工可以通过系统工单发起申请,由维护人员处理以后,填写处理结果。
+ """,
+
+ 'author': "机企猫",
+ 'website': "http://www.jikimo.com",
+
+ # Categories can be used to filter modules in modules listing
+ # Check https://github.com/odoo/odoo/blob/master/odoo/addons/base/module/module_data.xml
+ # for the full list
+ 'category': 'Uncategorized',
+ 'version': '0.1',
+
+ # any module necessary for this one to work correctly
+ 'depends': ['base','mail'],
+
+ # always loaded
+ 'data': [
+ 'security/account_security.xml',
+ 'security/ir.model.access.csv',
+ 'wizard/order_wizard.xml',
+ 'views/notice_user_config.xml',
+ 'views/yizuo_system_order_view.xml',
+ 'views/work_order_number.xml',
+ 'views/res_config_settings_views.xml',
+ ],
+ # only loaded in demonstration mode
+ 'demo': [
+ 'demo/demo.xml',
+ ],
+}
\ No newline at end of file
diff --git a/jikimo_system_order/controllers/__init__.py b/jikimo_system_order/controllers/__init__.py
new file mode 100644
index 00000000..457bae27
--- /dev/null
+++ b/jikimo_system_order/controllers/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import controllers
\ No newline at end of file
diff --git a/jikimo_system_order/controllers/controllers.py b/jikimo_system_order/controllers/controllers.py
new file mode 100644
index 00000000..cfcad7d0
--- /dev/null
+++ b/jikimo_system_order/controllers/controllers.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+from odoo import http
+
+# class TopSystemOrder(http.Controller):
+# @http.route('/jikimo_system_order/jikimo_system_order/', auth='public')
+# def index(self, **kw):
+# return "Hello, world"
+
+# @http.route('/jikimo_system_order/jikimo_system_order/objects/', auth='public')
+# def list(self, **kw):
+# return http.request.render('jikimo_system_order.listing', {
+# 'root': '/jikimo_system_order/jikimo_system_order',
+# 'objects': http.request.env['jikimo_system_order.jikimo_system_order'].search([]),
+# })
+
+# @http.route('/jikimo_system_order/jikimo_system_order/objects//', auth='public')
+# def object(self, obj, **kw):
+# return http.request.render('jikimo_system_order.object', {
+# 'object': obj
+# })
\ No newline at end of file
diff --git a/jikimo_system_order/demo/demo.xml b/jikimo_system_order/demo/demo.xml
new file mode 100644
index 00000000..b167f9be
--- /dev/null
+++ b/jikimo_system_order/demo/demo.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jikimo_system_order/models/__init__.py b/jikimo_system_order/models/__init__.py
new file mode 100644
index 00000000..87b51a43
--- /dev/null
+++ b/jikimo_system_order/models/__init__.py
@@ -0,0 +1,7 @@
+# -*- coding: utf-8 -*-
+
+from . import constant
+from . import order_classify
+from . import system_work_order
+from . import work_order_template
+from . import res_config_setting
diff --git a/jikimo_system_order/models/constant.py b/jikimo_system_order/models/constant.py
new file mode 100644
index 00000000..414371b9
--- /dev/null
+++ b/jikimo_system_order/models/constant.py
@@ -0,0 +1,7 @@
+# -*- coding: utf-8 -*-
+
+# 工单状态
+STATE_SELECTION = [('draft', u'草稿'), ('unconfirmed', u'待确认'), ('pending', u'待处理'),
+ ('processed', u'已处理待评分'), ('completed', u'已完成'), ('closed', u'已关闭')]
+
+GRADE = [('1', '1非常不满意'), ('2', '2不满意'), ('3', '3一般'), ('4', '4满意'), ('5', '5非常满意')]
diff --git a/jikimo_system_order/models/order_classify.py b/jikimo_system_order/models/order_classify.py
new file mode 100644
index 00000000..d143487d
--- /dev/null
+++ b/jikimo_system_order/models/order_classify.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+
+from odoo import models, fields, api
+from odoo.exceptions import ValidationError
+
+
+class OrderClassify(models.Model):
+ _name = 'order.classify'
+ _order = 'sequence, name'
+
+
+ @api.constrains('name')
+ def check_base_name(self):
+ """类型名称唯一"""
+ name_obj = self.env['order.classify'].search([('name', '=', self.name)])
+ if len(name_obj) >= 2:
+ raise ValidationError(u'该类型已存在')
+
+ # 名称
+ name = fields.Char(string=u'名称', size=20)
+ # 排序
+ sequence = fields.Integer(default=10)
+ # 是否有效
+ state = fields.Boolean(default=True, string='是否有效')
+
diff --git a/jikimo_system_order/models/res_config_setting.py b/jikimo_system_order/models/res_config_setting.py
new file mode 100644
index 00000000..44a9a1d7
--- /dev/null
+++ b/jikimo_system_order/models/res_config_setting.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+
+import logging
+from odoo import api, fields, models, _
+
+_logger = logging.getLogger(__name__)
+
+
+class ResModelWeConfigSettings(models.TransientModel):
+ _inherit = 'res.config.settings'
+
+ lost_agent_id = fields.Char('企微通知应用ID')
+
+ @api.model
+ def get_values(self):
+ """
+ 重载获取参数的方法,参数都存在系统参数中
+ :return:
+ """
+ values = super(ResModelWeConfigSettings, self).get_values()
+ config = self.env['ir.config_parameter'].sudo()
+ lost_agent_id = config.get_param('lost_agent_id', default='')
+ values.update(
+ lost_agent_id=lost_agent_id,
+ )
+ return values
+
+ def set_values(self):
+ super(ResModelWeConfigSettings, self).set_values()
+ ir_config = self.env['ir.config_parameter'].sudo()
+ ir_config.set_param("lost_agent_id", self.lost_agent_id or "")
+
diff --git a/jikimo_system_order/models/system_work_order.py b/jikimo_system_order/models/system_work_order.py
new file mode 100644
index 00000000..1a7bff5e
--- /dev/null
+++ b/jikimo_system_order/models/system_work_order.py
@@ -0,0 +1,183 @@
+# -*- coding: utf-8 -*-
+
+from odoo import models, fields, api
+from odoo.exceptions import ValidationError
+from odoo import exceptions
+from .constant import STATE_SELECTION, GRADE
+import datetime
+import logging
+
+
+class SystemWorkOrder(models.Model):
+ _name = 'system.work.order'
+ _inherit = ['mail.thread', 'mail.activity.mixin']
+ _order = 'date desc'
+ _description = u'系统工单'
+ _rec_name = 'order_number'
+
+ def get_is_technicist(self):
+ self._cr.execute(
+ "select u.id from res_users u left join res_groups_users_rel r on r.uid = u.id where r.gid in (select g.id from res_groups g where g.name = '技术员权限') and u.id ='%s'",
+ (self.env.user.id,))
+ hr = self._cr.dictfetchall()
+ if len(hr) > 0:
+ return True
+ else:
+ return False
+
+ # def get_user_department_id(self):
+ # """根据用户id系统员工id"""
+ # employee = self.env['hr.employee'].sudo().search([('user_id', '=', self.env.uid)], limit=1)
+ # if employee:
+ # if len(employee) > 0:
+ # if not employee.department_id:
+ # raise exceptions.Warning(u'您当前使用的用户没有所属部门')
+ # return employee.department_id
+ # else:
+ # return False
+ # else:
+ # raise exceptions.Warning(u'您当前使用的用户没有关联员工')
+
+ @api.onchange('order_template_id')
+ def get_title(self):
+ """选择模板自动填充"""
+ if self.order_template_id:
+ self.title = self.order_template_id.title_template
+ self.text = self.order_template_id.text_template
+
+ # 工单编号
+ order_number = fields.Char(string=u'工单编号', default='/')
+ # 紧急程度
+ urgency_degree = fields.Selection([('0', u'0星'), ('1', u'一星'), ('2', u'二星'), ('3', u'三星'), ('4', u'四星'),
+ ('5', u'五星')], string=u'紧急程度', help='五星为最紧急!', default='5')
+ # 工单分类(可以配置,并调整优先级)
+ order_type = fields.Many2one('order.classify', string=u'工单分类', domain=[('state', '=', True)])
+ # 发起人所属公司(res.company)
+ initiator_company_id = fields.Many2one('res.company', string=u'发起人所属公司', default=lambda self: self.env.user.company_id)
+ # 发起人部门(hr.department)
+ # initiator_department_id = fields.Many2one('hr.department', string=u'发起人部门', default=get_user_department_id)
+ # 发起人(hr.employee)
+ initiator_id = fields.Many2one('res.users', string=u'发起人', default=lambda self: self.env.user)
+ # 发起时间
+ date = fields.Datetime(string=u'发起时间', default=lambda self: fields.datetime.now())
+ # 确认人
+ confirm_id = fields.Many2one('res.users', string=u'确认人')
+ # 确认日期
+ confirmation_date = fields.Datetime(string=u'确认时间')
+ # 模板
+ order_template_id = fields.Many2one('work.order.template', string=u'模板', domain=[('state', '=', True)])
+ # 标题
+ title = fields.Char(string=u'标题')
+ # 正文
+ text = fields.Html(string=u'正文')
+ # 状态[草稿\待确认\待处理\已处理\已关闭]
+ state = fields.Selection(STATE_SELECTION, default='draft', string=u'状态')
+ # 关闭原因
+ close_cause = fields.Text(string=u'关闭问题原因')
+ # 关闭时间
+ close_time = fields.Datetime(string=u'关闭问题时间')
+ # 关闭人
+ close_user_id = fields.Many2one('res.users', string=u'关闭人')
+ # 解决人
+ solve_people_id = fields.Many2one('res.users', string=u'解决人')
+ # 用户实际问题
+ users_problem = fields.Text(string=u'用户实际问题')
+ # 最终解决方案
+ solution = fields.Text(string=u'最终解决方案')
+ # 判断是否为技术人员
+ # is_technicist = fields.Boolean(string=u'是否为技术人员', default=get_is_technicist)
+ # 打分
+ grade = fields.Selection(GRADE, string=u'评分')
+ # 评价按钮的显示
+ is_display = fields.Boolean('控制显示评价按钮', compute='compute_is_display')
+
+ def compute_is_display(self):
+ for item in self:
+ if item.state == 'processed' and self.env.user.id == item.initiator_id.id:
+ item.is_display = True
+ else:
+ item.is_display = False
+
+ @api.onchange('order_type')
+ def _onchange_order_type(self):
+ self.order_template_id = None
+ self.title = None
+ self.text = None
+
+ @api.model
+ def create(self, vals):
+ # 创建编号
+ if vals.get('order_number', '/') == '/':
+ vals['order_number'] = self.env['ir.sequence'].get('system.work.order') or '/'
+ return super(SystemWorkOrder, self).create(vals)
+
+ def do_draft(self, order=None):
+ """状态草稿"""
+ bill = self
+ if order:
+ bill = order
+ if bill.state == 'unconfirmed':
+ state_remark = u'待确认 --> 草稿'
+ # bill.message_post(u'操作人:%s,操作时间:%s,状态变更过程:%s' % (self.env.user.name,
+ # (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
+ bill.state = 'draft'
+
+ def do_unconfirmed(self):
+ """状态待确认"""
+ if self.state == 'draft':
+ state_remark = u'草稿 --> 待确认'
+ # self.message_post(u'操作人:%s,操作时间:%s,状态变更过程:%s' % (
+ # self.env.user.name,
+ # (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
+ self.state = 'unconfirmed'
+ # 获取通知人
+ objs = self.env['system.order.notice'].search([])
+ user_ids = objs.notice_user_ids.filtered(lambda item: item.we_employee_id not in ['', False])
+ we_employee_ids = user_ids.mapped('we_employee_id')
+ lost_agent_id = self.env['ir.config_parameter'].sudo().get_param('lost_agent_id')
+ wechat = self.env['we.config'].sudo().get_wechat(agent_id=lost_agent_id)
+ # agent_id, user_ids, content
+ content = """您有一张工单待处理:**工单标题:{2}**
+ >创建人:{1}
+ >提交时间:{3}
+ >紧急程度:{0}星
+ 请查看工单消息,并及时处理!
+ """.format(self.urgency_degree,
+ self.initiator_id.name, self.title, (self.date + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M'))
+ for we_employee_id in we_employee_ids:
+ try:
+ wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_id, content=content)
+ except Exception as e:
+ logging.error('工单处理发送消息异常%s' % str(e))
+
+ return True
+
+ def do_pending(self):
+ """状态待处理"""
+ if self.state == 'unconfirmed':
+ state_remark = u'待确认 --> 待处理'
+ # self.message_post(u'操作人:%s,操作时间:%s,状态变更过程:%s' % (
+ # self.env.user.name,
+ # (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
+ self.state = 'pending'
+ self.confirm_id = self.env.user
+ self.confirmation_date = fields.datetime.now()
+ return True
+
+ def urned_off(self):
+ """状态关闭"""
+ if self.close_cause:
+ self.state = 'closed'
+ self.close_time = fields.datetime.now()
+ else:
+ raise ValidationError(u'请注明关闭原因')
+ return True
+
+ def unlink(self):
+ for item in self:
+ if item.state != "draft":
+ raise ValidationError(u'只能删除状态为【草稿】的工单。')
+ elif item.env.uid != item.initiator_id.id:
+ raise ValidationError(u'非本人不能删除')
+ else:
+ super(SystemWorkOrder, item).unlink()
diff --git a/jikimo_system_order/models/work_order_template.py b/jikimo_system_order/models/work_order_template.py
new file mode 100644
index 00000000..f50fd42d
--- /dev/null
+++ b/jikimo_system_order/models/work_order_template.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+
+from odoo import models, fields, api
+
+
+class WorkOrderTemplate(models.Model):
+ _name = 'work.order.template'
+ _order = 'num'
+
+ # 编号
+ num = fields.Char(string=u'编号', default='/')
+ # 名称
+ name = fields.Char(string=u'模板名称', required="1")
+ # 分类
+ work_order_type = fields.Many2one('order.classify', string=u'系统工单分类', domain=[('state', '=', True)])
+ # 模板标题
+ title_template = fields.Char(string=u'模板标题')
+ # 模板正文
+ text_template = fields.Html(string=u'模板正文')
+ # 模板说明
+ template_explain = fields.Text(string=u'模板说明')
+ # 是否有效
+ state = fields.Boolean(default=True, string=u'是否有效')
+
+ @api.model
+ def create(self, vals):
+ # 创建编号
+ if vals.get('num', '/') == '/':
+ vals['num'] = self.env['ir.sequence'].get('work.order.template') or '/'
+ return super(WorkOrderTemplate, self).create(vals)
+
+
+class SystemOrderNotice(models.Model):
+ _name = 'system.order.notice'
+ _description = '工单处理人设置'
+
+ notice_user_ids = fields.Many2many('res.users', string='工单处理人')
+
diff --git a/jikimo_system_order/security/account_security.xml b/jikimo_system_order/security/account_security.xml
new file mode 100644
index 00000000..8356964e
--- /dev/null
+++ b/jikimo_system_order/security/account_security.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ 运维权限
+
+
+
+ 用户访问工单信息
+
+
+ [('initiator_id', '=', user.id)]
+
+
+
+ 运维访问工单信息
+
+
+ [(1, '=', 1)]
+
+
+
+
diff --git a/jikimo_system_order/security/ir.model.access.csv b/jikimo_system_order/security/ir.model.access.csv
new file mode 100644
index 00000000..5c76352d
--- /dev/null
+++ b/jikimo_system_order/security/ir.model.access.csv
@@ -0,0 +1,16 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+
+inside_system_order_classify_r,jikimo_system_order.order_classify,model_order_classify,,1,1,1,1
+inside_system_work_order_rc,jikimo_system_order.system_work_order,model_system_work_order,,1,1,1,1
+inside_work_order_template_r,jikimo_system_order.work_order_template,model_work_order_template,,1,1,1,1
+
+inside_system_order_classify_rwc,jikimo_system_order.order_classify,model_order_classify,group_operations_permissions_rwc,1,1,1,0
+inside_system_work_order_rwc,jikimo_system_order.system_work_order,model_system_work_order,group_operations_permissions_rwc,1,1,1,0
+inside_work_order_template_rwc,jikimo_system_order.work_order_template,model_work_order_template,group_operations_permissions_rwc,1,1,1,0
+
+order_close_wizard_group_user,jikimo_system_order.order_close_wizard,model_order_close_wizard,base.group_user,1,1,1,1
+order_other_wizard_group_user,jikimo_system_order.order_other_wizard,model_order_other_wizard,base.group_user,1,1,1,1
+order_technician_wizard_group_user,jikimo_system_order.order_technician_wizard,model_order_technician_wizard,base.group_user,1,1,1,1
+system_work_order_wizard_group_user,jikimo_system_order.system_work_order_wizard,model_system_work_order_wizard,base.group_user,1,1,1,1
+
+system_order_notice_group_user,jikimo_system_order.system_order_notice,model_system_order_notice,base.group_user,1,1,1,1
\ No newline at end of file
diff --git a/jikimo_system_order/static/description/icon.png b/jikimo_system_order/static/description/icon.png
new file mode 100644
index 00000000..24aa8fc8
Binary files /dev/null and b/jikimo_system_order/static/description/icon.png differ
diff --git a/jikimo_system_order/views/notice_user_config.xml b/jikimo_system_order/views/notice_user_config.xml
new file mode 100644
index 00000000..353afb96
--- /dev/null
+++ b/jikimo_system_order/views/notice_user_config.xml
@@ -0,0 +1,58 @@
+
+
+
+ # ---------- 工单通知处理人设置 ------------
+
+
+ tree.system.order.notice
+ system.order.notice
+
+
+
+
+
+
+
+
+
+ search.system.order.notice
+ system.order.notice
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 工单处理人
+ system.order.notice
+ tree
+ []
+ {}
+
+
+ [工单处理人] 还没有哦!点左上角的[创建]按钮,沙发归你了!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jikimo_system_order/views/res_config_settings_views.xml b/jikimo_system_order/views/res_config_settings_views.xml
new file mode 100644
index 00000000..40cdc443
--- /dev/null
+++ b/jikimo_system_order/views/res_config_settings_views.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ res.config.settings.we.view.form.inherit.bpm
+ res.config.settings
+
+
+
+
+
+
+
+
+
diff --git a/jikimo_system_order/views/work_order_number.xml b/jikimo_system_order/views/work_order_number.xml
new file mode 100644
index 00000000..63f0d5a3
--- /dev/null
+++ b/jikimo_system_order/views/work_order_number.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ seq_work_order
+
+ system.work.order
+ SO%(year)s%(month)s%(day)s
+ 1
+
+
+
+
+ seq_order_template
+
+ work.order.template
+ TL
+ 1
+
+
+
\ No newline at end of file
diff --git a/jikimo_system_order/views/yizuo_system_order_view.xml b/jikimo_system_order/views/yizuo_system_order_view.xml
new file mode 100644
index 00000000..006a085d
--- /dev/null
+++ b/jikimo_system_order/views/yizuo_system_order_view.xml
@@ -0,0 +1,243 @@
+
+
+
+
+
+
+ 工单信息
+ system.work.order
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 新建系统工单
+ system.work.order
+
+
+
+
+
+
+
+ 搜索工单
+ system.work.order
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 工单图表
+ system.work.order
+
+
+
+
+
+
+
+
+
+
+
+ 工单
+ system.work.order
+ tree,form,search,graph,pivot
+
+
+
+
+ 工单模板信息
+ work.order.template
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 新建系统工单模板
+ work.order.template
+
+
+
+
+
+
+
+ 工单模板
+ work.order.template
+ tree,form
+
+
+
+
+ 工单分类信息
+ order.classify
+
+
+
+
+
+
+
+
+
+
+
+ 新建系统分类信息
+ order.classify
+
+
+
+
+
+
+
+ 工单分类
+ order.classify
+ tree,form
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jikimo_system_order/wizard/__init__.py b/jikimo_system_order/wizard/__init__.py
new file mode 100644
index 00000000..8cf586ce
--- /dev/null
+++ b/jikimo_system_order/wizard/__init__.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+
+from . import order_other_wizard
+from . import order_technician_wizard
+from . import order_close_wizard
+from . import system_work_order_wizard
diff --git a/jikimo_system_order/wizard/order_close_wizard.py b/jikimo_system_order/wizard/order_close_wizard.py
new file mode 100644
index 00000000..ca10ae7c
--- /dev/null
+++ b/jikimo_system_order/wizard/order_close_wizard.py
@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+
+from odoo import models, fields, api
+from odoo.addons.jikimo_system_order.models.constant import STATE_SELECTION
+from odoo.exceptions import ValidationError
+import datetime, logging
+
+
+class OrderCloseWizard(models.TransientModel):
+ _name = 'order.close.wizard'
+
+
+ def get_context(self):
+ if self._context.get('active_id'):
+ obj = self.env['system.work.order'].browse(self._context.get('active_id'))
+ if obj.initiator_id.id != self.env.user.id:
+ raise ValidationError(u'非本人无法操作')
+ return obj
+
+ order_id = fields.Many2one('system.work.order', string=u'工单ID',
+ default=lambda self: self.get_context().id)
+ # 关闭原因
+ close_cause = fields.Text(string=u'关闭问题原因', default=lambda self: self.get_context().close_cause)
+ # 关闭时间
+ close_time = fields.Datetime(string=u'关闭问题时间', default=fields.datetime.now())
+ # 状态
+ state = fields.Selection(STATE_SELECTION, default='closed', string=u'状态')
+ # 关闭人
+ close_user_id = fields.Many2one('res.users', string=u'关闭人', default=lambda self: self.env.user)
+
+
+ def sure(self):
+ self.order_id.close_cause = self.close_cause
+ self.order_id.close_time = self.close_time
+ if self.order_id.state == 'unconfirmed':
+ state_remark = u'待确认 --> 已关闭'
+ if self.order_id.state == 'pending':
+ state_remark = u'待处理 --> 已关闭'
+ if self.order_id.state == 'processed':
+ state_remark = u'已处理待评分 --> 已关闭'
+ # self.order_id.message_post(u'操作人:%s,操作时间:%s,状态变更过程:%s' % (
+ # self.env.user.name,
+ # (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
+ self.order_id.state = self.state
+ self.order_id.close_user_id = self.close_user_id
+ we_employee_ids = []
+ if self.order_id.initiator_id.we_employee_id:
+ we_employee_ids.append(self.order_id.initiator_id.we_employee_id)
+ lost_agent_id = self.env['ir.config_parameter'].sudo().get_param('lost_agent_id')
+ wechat = self.env['we.config'].sudo().get_wechat(agent_id=lost_agent_id)
+ # agent_id, user_ids, content
+ content = """您提交的工单-**工单标题:{0}**-**已关闭**
+ >提交时间:{1}
+ >处理时间:{2}
+ >处理人:{3}
+ 如有问题,请联系系统管理员!
+ """.format(self.order_id.title,
+ (self.order_id.date + datetime.timedelta(hours=8)).strftime(
+ '%Y-%m-%d %H:%M'), (datetime.datetime.now() + datetime.timedelta(
+ hours=8)).strftime('%Y-%m-%d %H:%M'), self.env.user.name or '')
+ # wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_ids, content=content)
+ for we_employee_id in we_employee_ids:
+ try:
+ wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_id, content=content)
+ except Exception as e:
+ logging.error('工单关闭发送消息异常%s' % str(e))
+ return {}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jikimo_system_order/wizard/order_other_wizard.py b/jikimo_system_order/wizard/order_other_wizard.py
new file mode 100644
index 00000000..3f6c8710
--- /dev/null
+++ b/jikimo_system_order/wizard/order_other_wizard.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+
+from odoo import models, fields, api
+from odoo.exceptions import ValidationError
+from odoo.addons.jikimo_system_order.models.constant import STATE_SELECTION, GRADE
+import datetime
+
+
+class OrderOtherWizard(models.TransientModel):
+ _name = 'order.other.wizard'
+
+
+ def get_context(self):
+ if self._context.get('active_id'):
+ obj = self.env['system.work.order'].browse(self._context.get('active_id'))
+ if obj.initiator_id.id != self.env.user.id:
+ raise ValidationError(u'非本人无法操作')
+ return obj
+
+ order_id = fields.Many2one('system.work.order', string=u'工单ID',
+ default=lambda self: self.get_context().id)
+ # 关闭时间
+ close_time = fields.Datetime(string=u'关闭时间', default=fields.datetime.now())
+ # 状态
+ state = fields.Selection(STATE_SELECTION, default='completed', string=u'状态')
+ # 打分
+ grade = fields.Selection(GRADE, string=u'评分')
+ # 关闭人
+ close_user_id = fields.Many2one('res.users', string=u'关闭人', default=lambda self: self.env.user)
+
+
+ def sure(self):
+ self.order_id.close_time = self.close_time
+ self.order_id.grade = self.grade
+ if self.order_id.state == 'processed':
+ state_remark = u'已处理待评分 --> 已完成'
+ # self.order_id.message_post(u'操作人:%s,操作时间:%s,状态变更过程:%s' % (
+ # self.env.user.name,
+ # (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
+ self.order_id.state = self.state
+ self.order_id.close_user_id = self.close_user_id
+ return {}
diff --git a/jikimo_system_order/wizard/order_technician_wizard.py b/jikimo_system_order/wizard/order_technician_wizard.py
new file mode 100644
index 00000000..78458038
--- /dev/null
+++ b/jikimo_system_order/wizard/order_technician_wizard.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+
+from odoo import models, fields, api
+from odoo.addons.jikimo_system_order.models.constant import STATE_SELECTION
+import datetime
+import logging
+
+
+class OrderTechnicianWizard(models.TransientModel):
+ _name = 'order.technician.wizard'
+
+ order_id = fields.Many2one('system.work.order', string=u'工单ID',
+ default=lambda self: self.env.context.get('active_id'))
+ # 解决人
+ solve_people_id = fields.Many2one('res.users', string=u'解决人', default=lambda self: self.env.user)
+ # 用户实际问题
+ users_problem = fields.Text(string=u'用户实际问题')
+ # 最终解决方案
+ solution = fields.Text(string=u'最终解决方案')
+ # 状态
+ state = fields.Selection(STATE_SELECTION, default='processed', string=u'状态')
+
+ def sure(self):
+ self.order_id.solve_people_id = self.solve_people_id
+ self.order_id.users_problem = self.users_problem
+ self.order_id.solution = self.solution
+ if self.order_id.state == 'pending':
+ state_remark = u'待处理 --> 已处理待评分'
+ # self.order_id.message_post(u'操作人:%s,操作时间:%s,状态变更过程:%s' % (
+ # self.env.user.name,
+ # (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
+ self.order_id.state = self.state
+ # 获取通知人
+ # objs = self.env['system.order.notice'].search([])
+ # user_ids = objs.notice_user_ids.filtered(lambda item: item.we_employee_id not in ['', False])
+ # we_employee_ids = user_ids.mapped('we_employee_id')
+ we_employee_ids = []
+ if self.order_id.initiator_id.we_employee_id:
+ we_employee_ids.append(self.order_id.initiator_id.we_employee_id)
+ print(we_employee_ids)
+ lost_agent_id = self.env['ir.config_parameter'].sudo().get_param('lost_agent_id')
+ wechat = self.env['we.config'].sudo().get_wechat(agent_id=lost_agent_id)
+ # agent_id, user_ids, content
+ content = """您提交的工单-**工单标题:{0}**-**已处理**
+ >提交时间:{1}
+ >处理反馈:{4}
+ >处理时间:{2}
+ >处理人:{3}
+ 如有问题,请联系系统管理员!
+ """.format(self.order_id.title,
+ (self.order_id.date + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M'), (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M'), self.env.user.name or '', self.solution or '')
+ # wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_ids, content=content)
+ for we_employee_id in we_employee_ids:
+ try:
+ wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_id, content=content)
+ except Exception as e:
+ logging.error('工单处理发送消息异常%s' % str(e))
+
+ return {}
diff --git a/jikimo_system_order/wizard/order_wizard.xml b/jikimo_system_order/wizard/order_wizard.xml
new file mode 100644
index 00000000..64c13ccd
--- /dev/null
+++ b/jikimo_system_order/wizard/order_wizard.xml
@@ -0,0 +1,122 @@
+
+
+
+
+
+ 技术员向导
+ order.technician.wizard
+
+
+
+
+
+
+ 技术员编辑
+ ir.actions.act_window
+ order.technician.wizard
+ form
+
+ {'display_default_code':False}
+ new
+
+
+
+
+
+
+ 其它向导
+ order.other.wizard
+
+
+
+
+
+
+ 其它编辑
+ ir.actions.act_window
+ order.other.wizard
+ form
+
+ {'display_default_code':False}
+ new
+
+
+
+
+ 关闭向导
+ order.close.wizard
+
+
+
+
+
+
+ 关闭工单
+ ir.actions.act_window
+ order.close.wizard
+ form
+
+ {'display_default_code':False}
+ new
+
+
+
+ system_work_order_wizard_view
+ system.work.order.wizard
+
+
+
+
+
+
+ 二次确认
+ ir.actions.act_window
+ system.work.order.wizard
+ form
+ new
+
+
+
diff --git a/jikimo_system_order/wizard/system_work_order_wizard.py b/jikimo_system_order/wizard/system_work_order_wizard.py
new file mode 100644
index 00000000..8c8f95dc
--- /dev/null
+++ b/jikimo_system_order/wizard/system_work_order_wizard.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# @Time : 2017/12/12 9:46
+# @Author : GuoXiang
+# @Site :
+# @File : system_work_order_wizard.py
+# @Software: PyCharm
+# @Desc :
+# @license : Copyright©2018 www.dasmaster.com All Rights Reserved.
+# @Contact : xg1230205321@163.com
+from odoo import models, api, fields
+from odoo.exceptions import ValidationError
+
+
+class SystemWorkOrderWizard(models.TransientModel):
+ _name = "system.work.order.wizard"
+ _description = u"追回确认"
+
+
+ def _get_explain(self):
+ if self._context.get('object_id'):
+ obj = self.env['system.work.order'].browse(self._context.get('object_id'))
+ if obj.initiator_id.id != self.env.user.id:
+ raise ValidationError(u'非本人无法操作')
+ if self._context.get('explain'):
+ return self._context["explain"]
+
+ explain = fields.Char(default=_get_explain)
+
+
+ def sure(self):
+ """
+ 确认
+ :return:
+ """
+ if self._context.get('object_id') and self._context.get('object_name') and self._context.get(
+ 'explain') and self._context.get('function_name'):
+ work_sheet_obj = self.env[self._context["object_name"]].search([('id', '=', int(self._context["object_id"]))])
+ class_name = self._context.get('object_name') # 获得对象类名
+ method_name = self._context.get('function_name') # 获得对象的方法
+ obj_function = getattr(self.env[class_name], method_name)
+ obj_function(work_sheet_obj)
diff --git a/sf_base/models/common.py b/sf_base/models/common.py
index 6fd7d814..65dfe13d 100644
--- a/sf_base/models/common.py
+++ b/sf_base/models/common.py
@@ -61,12 +61,10 @@ class MrsMaterialModel(models.Model):
supplier_ids = fields.One2many('sf.supplier.sort', 'materials_model_id', string='供应商')
active = fields.Boolean('有效', default=True)
- @api.onchange('gain_way')
- def _check_gain_way(self):
- if not self.gain_way:
- raise UserError("请选择获取方式")
- if self.gain_way in ['外协', '采购']:
- if not self.supplier_ids:
+ @api.constrains("gain_way")
+ def _check_supplier_ids(self):
+ for item in self:
+ if item.gain_way in ('外协', '采购') and not item.supplier_ids:
raise UserError("请添加供应商")
@@ -94,8 +92,10 @@ class MrsProductionProcess(models.Model):
partner_process_ids = fields.Many2many('res.partner', 'process_ids', '加工工厂')
active = fields.Boolean('有效', default=True)
parameter_ids = fields.One2many('sf.production.process.parameter', 'process_id', string='可选参数')
- category_id = fields.Many2one('sf.production.process.category')
+ category_id = fields.Many2one('sf.production.process.category', string='表面工艺类别')
# workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_process', required=True)
+ processing_day = fields.Float('加工天数/d')
+ travel_day = fields.Float('路途天数/d')
# class MrsProcessingTechnology(models.Model):
@@ -143,7 +143,10 @@ class MrsProductionProcessParameter(models.Model):
is_check = fields.Boolean(default=False)
# price = fields.Float('单价')
process_id = fields.Many2one('sf.production.process', string='表面工艺')
+ process_description = fields.Char(string='工艺描述')
materials_model_ids = fields.Many2many('sf.materials.model', 'applicable_material', string='适用材料')
+ processing_day = fields.Float('加工天数/d')
+ travel_day = fields.Float('路途天数/d')
active = fields.Boolean('有效', default=True)
def name_get(self):
diff --git a/sf_base/views/common_view.xml b/sf_base/views/common_view.xml
index 74e916a8..533a3e04 100644
--- a/sf_base/views/common_view.xml
+++ b/sf_base/views/common_view.xml
@@ -27,9 +27,13 @@
+
-
+
+
+
+
@@ -179,41 +183,46 @@
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -251,7 +260,7 @@
-
+
@@ -270,9 +279,9 @@
-
+
-
+
diff --git a/sf_bf_connect/models/jd_eclp.py b/sf_bf_connect/models/jd_eclp.py
index 7879938e..ec14f7e1 100644
--- a/sf_bf_connect/models/jd_eclp.py
+++ b/sf_bf_connect/models/jd_eclp.py
@@ -161,6 +161,9 @@ class JdEclp(models.Model):
url2 = config['bfm_url'] + '/api/get/jd/no'
response = requests.post(url2, json=json2, data=None)
# _logger.info('调用成功2', response.json()['result']['wbNo'])
+ tem_ret = response.json().get('result')
+ if not tem_ret:
+ raise ValidationError('京东物流返回异常,请联系管理员')
self.carrier_tracking_ref = response.json()['result'].get('wbNo')
if not self.carrier_tracking_ref:
raise ValidationError('物流下单未成功,请联系管理员')
diff --git a/sf_dlm_management/data/stock_data.xml b/sf_dlm_management/data/stock_data.xml
index 3d2c4527..be2a36e0 100644
--- a/sf_dlm_management/data/stock_data.xml
+++ b/sf_dlm_management/data/stock_data.xml
@@ -22,6 +22,16 @@
+
+ 拆解
+
+ internal
+ DJCJ
+ true
+ true
+
+
+
刀具组装入库
diff --git a/sf_machine_connect/__init__.py b/sf_machine_connect/__init__.py
index 9b429614..6bac603e 100644
--- a/sf_machine_connect/__init__.py
+++ b/sf_machine_connect/__init__.py
@@ -1,2 +1,3 @@
from . import models
from . import wizard
+from . import controllers
diff --git a/sf_machine_connect/controllers/__init__.py b/sf_machine_connect/controllers/__init__.py
new file mode 100644
index 00000000..e046e49f
--- /dev/null
+++ b/sf_machine_connect/controllers/__init__.py
@@ -0,0 +1 @@
+from . import controllers
diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py
new file mode 100644
index 00000000..ba8139d0
--- /dev/null
+++ b/sf_machine_connect/controllers/controllers.py
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+import ast
+import json
+import logging
+from odoo import http
+from odoo.http import request
+
+
+class Sf_Dashboard_Connect(http.Controller):
+
+ @http.route('/api/get_machine_datas/list', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
+ cors="*")
+ def get_machine_datas_list(self, **kw):
+ """
+ 拿到机床数据返回给大屏展示
+ :param kw:
+ :return:
+ """
+ res = {'status': 1, 'message': '成功', 'data': []}
+ logging.info('前端请求机床数据的参数为:%s' % kw)
+ # tem_list = [
+ # "XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-1", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-3",
+ # "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-4", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-5",
+ # "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-6", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-7",
+ # "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-8", "XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-2",
+ # "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-9", "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-10",
+ # "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-11", "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-12",
+ # "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-13", "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-14"
+ # ]
+ try:
+ equipment_obj = request.env['maintenance.equipment'].sudo()
+ # 获取请求的机床数据
+ machine_list = ast.literal_eval(kw['machine_list'])
+ for item in machine_list:
+ machine_data = equipment_obj.search([('code', '=', item)])
+ if machine_data:
+ res['data'].append({
+ 'id': machine_data.id,
+ 'name': machine_data.name,
+ 'code': machine_data.code,
+ 'status': machine_data.status,
+ 'run_status': machine_data.run_status,
+ 'run_time': machine_data.run_time,
+ 'system_date': machine_data.system_date,
+ 'system_time': machine_data.system_time,
+ 'cut_time': machine_data.cut_time,
+ 'cut_status': machine_data.cut_status,
+ 'program': machine_data.program,
+ 'program_name': machine_data.program_name,
+ 'program_status': machine_data.program_status,
+ 'tool_num': machine_data.tool_num,
+ 'machine_power_on_time': machine_data.machine_power_on_time,
+ 'product_counts': machine_data.product_counts,
+ 'mode': machine_data.mode,
+ 'start_time': machine_data.start_time,
+ 'end_time': machine_data.end_time,
+ 'program_start_time': machine_data.program_start_time,
+ 'program_end_time': machine_data.program_end_time,
+ 'standby_start_time': machine_data.standby_start_time,
+ 'standby_end_time': machine_data.standby_end_time,
+ 'offline_start_time': machine_data.offline_start_time,
+ 'offline_end_time': machine_data.offline_end_time,
+ 'emg_status': machine_data.emg_status,
+ 'current_program': machine_data.current_program,
+ 'current_program_seq': machine_data.current_program_seq,
+ 'x_abs_pos': machine_data.x_abs_pos,
+ 'y_abs_pos': machine_data.y_abs_pos,
+ 'z_abs_pos': machine_data.z_abs_pos,
+ 'feed_speed_set': machine_data.feed_speed_set,
+ 'act_feed_speed': machine_data.act_feed_speed,
+ 'spindle_speed_set': machine_data.spindle_speed_set,
+ 'act_spindle_speed': machine_data.act_spindle_speed,
+ 'spindle_load': machine_data.spindle_load,
+ 'x_axis_load': machine_data.x_axis_load,
+ 'y_axis_load': machine_data.y_axis_load,
+ 'z_axis_load': machine_data.z_axis_load,
+ 'rapid_feed': machine_data.rapid_feed,
+ 'feed_rate': machine_data.feed_rate,
+ 'x_mach_coord': machine_data.x_mach_coord,
+ 'y_mach_coord': machine_data.y_mach_coord,
+ 'z_mach_coord': machine_data.z_mach_coord,
+ 'x_rel_coord': machine_data.x_rel_coord,
+ 'y_rel_coord': machine_data.y_rel_coord,
+ 'z_rel_coord': machine_data.z_rel_coord,
+ 'x_dis_coord': machine_data.x_dis_coord,
+ 'y_dis_coord': machine_data.y_dis_coord,
+ 'z_dis_coord': machine_data.z_dis_coord,
+ 'alarm_time': machine_data.alarm_time,
+ 'alarm_msg': machine_data.alarm_msg,
+ 'clear_time': machine_data.clear_time,
+ })
+
+ return json.JSONEncoder().encode(res)
+ except Exception as e:
+ logging.info('前端请求机床数据失败,原因:%s' % e)
+ res['status'] = -1
+ res['message'] = '前端请求机床数据失败,原因:%s' % e
+ return json.JSONEncoder().encode(res)
diff --git a/sf_machine_connect/models/ftp_client.py b/sf_machine_connect/models/ftp_client.py
index 2cc1ee5d..1e09f665 100644
--- a/sf_machine_connect/models/ftp_client.py
+++ b/sf_machine_connect/models/ftp_client.py
@@ -123,128 +123,159 @@ class Machine_ftp(models.Model):
# workorder_ids = fields.One2many('mrp.workorder', 'machine_tool_id', string='工单')
- # 机床配置项目
- # ftp相关
- ftp_num = fields.Char('ftp账号')
- ftp_pwd = fields.Char('ftp密码')
- ftp_host = fields.Char('ftp地址')
- ftp_port = fields.Integer('ftp端口')
- ftp_remote_path = fields.Char('机床ftp路径')
- # 补偿值写入相关
- x_compensation_node = fields.Char('x补偿值节点')
- y_compensation_node = fields.Char('y补偿值节点')
- # 数采配置相关
- machine_ip = fields.Char('机床IP')
- machine_signed = fields.Char('机床刷新间隔')
- machine_status = fields.Char('机床在线状态')
- machine_cnc_type = fields.Char('机床CNC型号')
- machine_axis_count = fields.Char('机床轴总数')
- machine_run_status = fields.Char('机床运行状态')
- machine_emg_status = fields.Char('机床急停状态')
- machine_cut_status = fields.Char('机床当前切削状态')
- machine_mode = fields.Char('机床当前操作模式')
- machine_spindle_load = fields.Char('机床主轴负载')
- machine_x_mach = fields.Char('机床X轴机械坐标')
- machine_x_abs_mach = fields.Char('机床X轴当前位置')
- machine_x_rel_mach = fields.Char('机床X轴相对工件坐标')
- machine_x_dis_mach = fields.Char('机床X轴目标距离')
- machine_x_axis_load = fields.Char('机床X轴伺服轴负载')
- machine_y_mach = fields.Char('机床Y轴机械坐标')
- machine_y_abs_mach = fields.Char('机床Y轴当前位置')
- machine_y_rel_mach = fields.Char('机床Y轴相对工件坐标')
- machine_y_dis_mach = fields.Char('机床Y轴目标距离')
- machine_y_axis_load = fields.Char('机床Y轴伺服轴负载')
- machine_z_mach = fields.Char('机床Z轴机械坐标')
- machine_z_abs_mach = fields.Char('机床Z轴当前位置')
- machine_z_rel_mach = fields.Char('机床Z轴相对工件坐标')
- machine_z_dis_mach = fields.Char('机床Z轴目标距离')
- machine_z_axis_load = fields.Char('机床Z轴伺服轴负载')
- machine_tool_num = fields.Char('机床当前刀位号')
- machine_program = fields.Char('机床主程序名称')
- machine_current_prg = fields.Char('机床当前执行指令')
- machine_prg_seq = fields.Char('机床当前执行语句号')
- machine_spindle_speed_set = fields.Char('机床设定主轴速度')
- machine_act_spindle_speed = fields.Char('机床实际主轴转速')
- machine_feed_speed_set = fields.Char('机床设定进给速度')
- machine_act_feed_speed = fields.Char('机床实际进给速度')
- machine_spindle_feed = fields.Char('机床主轴倍率')
- machine_feed_rate = fields.Char('机床进给倍率')
- machine_rapid_feed = fields.Char('机床快速移动倍率')
- machine_run_time = fields.Char('机床运行时间')
- machine_cut_time = fields.Char('机床切削时间')
- machine_keep_alive_time = fields.Char('机床上电时间')
- machine_circle_time = fields.Char('机床循环时间')
- machine_product_counts = fields.Char('机床加工件数')
- machine_system_date = fields.Char('机床系统日期')
- machine_system_time = fields.Char('机床系统时间')
- machine_alarm_msg = fields.Char('机床系统报警')
+ # # 机床配置项目
+ # # ftp相关
+ # ftp_num = fields.Char('ftp账号')
+ # ftp_pwd = fields.Char('ftp密码')
+ # ftp_host = fields.Char('ftp地址')
+ # ftp_port = fields.Integer('ftp端口')
+ # ftp_remote_path = fields.Char('机床ftp路径')
+ # # 补偿值写入相关
+ # x_compensation_node = fields.Char('x补偿值节点')
+ # y_compensation_node = fields.Char('y补偿值节点')
+ # # 数采配置相关
+ # machine_ip = fields.Char('机床IP')
+ # machine_signed = fields.Char('机床刷新间隔')
+ # machine_status = fields.Char('机床在线状态')
+ # machine_cnc_type = fields.Char('机床CNC型号')
+ # machine_axis_count = fields.Char('机床轴总数')
+ # machine_run_status = fields.Char('机床运行状态')
+ # machine_emg_status = fields.Char('机床急停状态')
+ # machine_cut_status = fields.Char('机床当前切削状态')
+ # machine_mode = fields.Char('机床当前操作模式')
+ # machine_spindle_load = fields.Char('机床主轴负载')
+ # machine_x_mach = fields.Char('机床X轴机械坐标')
+ # machine_x_abs_mach = fields.Char('机床X轴当前位置')
+ # machine_x_rel_mach = fields.Char('机床X轴相对工件坐标')
+ # machine_x_dis_mach = fields.Char('机床X轴目标距离')
+ # machine_x_axis_load = fields.Char('机床X轴伺服轴负载')
+ # machine_y_mach = fields.Char('机床Y轴机械坐标')
+ # machine_y_abs_mach = fields.Char('机床Y轴当前位置')
+ # machine_y_rel_mach = fields.Char('机床Y轴相对工件坐标')
+ # machine_y_dis_mach = fields.Char('机床Y轴目标距离')
+ # machine_y_axis_load = fields.Char('机床Y轴伺服轴负载')
+ # machine_z_mach = fields.Char('机床Z轴机械坐标')
+ # machine_z_abs_mach = fields.Char('机床Z轴当前位置')
+ # machine_z_rel_mach = fields.Char('机床Z轴相对工件坐标')
+ # machine_z_dis_mach = fields.Char('机床Z轴目标距离')
+ # machine_z_axis_load = fields.Char('机床Z轴伺服轴负载')
+ # machine_tool_num = fields.Char('机床当前刀位号')
+ # machine_program = fields.Char('机床主程序名称')
+ # machine_current_prg = fields.Char('机床当前执行指令')
+ # machine_prg_seq = fields.Char('机床当前执行语句号')
+ # machine_spindle_speed_set = fields.Char('机床设定主轴速度')
+ # machine_act_spindle_speed = fields.Char('机床实际主轴转速')
+ # machine_feed_speed_set = fields.Char('机床设定进给速度')
+ # machine_act_feed_speed = fields.Char('机床实际进给速度')
+ # machine_spindle_feed = fields.Char('机床主轴倍率')
+ # machine_feed_rate = fields.Char('机床进给倍率')
+ # machine_rapid_feed = fields.Char('机床快速移动倍率')
+ # machine_run_time = fields.Char('机床运行时间')
+ # machine_cut_time = fields.Char('机床切削时间')
+ # machine_keep_alive_time = fields.Char('机床上电时间')
+ # machine_circle_time = fields.Char('机床循环时间')
+ # machine_product_counts = fields.Char('机床加工件数')
+ # machine_system_date = fields.Char('机床系统日期')
+ # machine_system_time = fields.Char('机床系统时间')
+ # machine_alarm_msg = fields.Char('机床系统报警')
- # 刀位配置
- tool_num1 = fields.Char('刀位1')
- tool_num2 = fields.Char('刀位2')
- tool_num3 = fields.Char('刀位3')
- tool_num4 = fields.Char('刀位4')
- tool_num5 = fields.Char('刀位5')
- tool_num6 = fields.Char('刀位6')
- tool_num7 = fields.Char('刀位7')
- tool_num8 = fields.Char('刀位8')
- tool_num9 = fields.Char('刀位9')
- tool_num10 = fields.Char('刀位10')
- tool_num11 = fields.Char('刀位11')
- tool_num12 = fields.Char('刀位12')
- tool_num13 = fields.Char('刀位13')
- tool_num14 = fields.Char('刀位14')
- tool_num15 = fields.Char('刀位15')
- tool_num16 = fields.Char('刀位16')
- tool_num17 = fields.Char('刀位17')
- tool_num18 = fields.Char('刀位18')
- tool_num19 = fields.Char('刀位19')
- tool_num20 = fields.Char('刀位20')
- tool_num21 = fields.Char('刀位21')
- tool_num22 = fields.Char('刀位22')
- tool_num23 = fields.Char('刀位23')
- tool_num24 = fields.Char('刀位24')
+ # # 刀位配置
+ # tool_num1 = fields.Char('刀位1')
+ # tool_num2 = fields.Char('刀位2')
+ # tool_num3 = fields.Char('刀位3')
+ # tool_num4 = fields.Char('刀位4')
+ # tool_num5 = fields.Char('刀位5')
+ # tool_num6 = fields.Char('刀位6')
+ # tool_num7 = fields.Char('刀位7')
+ # tool_num8 = fields.Char('刀位8')
+ # tool_num9 = fields.Char('刀位9')
+ # tool_num10 = fields.Char('刀位10')
+ # tool_num11 = fields.Char('刀位11')
+ # tool_num12 = fields.Char('刀位12')
+ # tool_num13 = fields.Char('刀位13')
+ # tool_num14 = fields.Char('刀位14')
+ # tool_num15 = fields.Char('刀位15')
+ # tool_num16 = fields.Char('刀位16')
+ # tool_num17 = fields.Char('刀位17')
+ # tool_num18 = fields.Char('刀位18')
+ # tool_num19 = fields.Char('刀位19')
+ # tool_num20 = fields.Char('刀位20')
+ # tool_num21 = fields.Char('刀位21')
+ # tool_num22 = fields.Char('刀位22')
+ # tool_num23 = fields.Char('刀位23')
+ # tool_num24 = fields.Char('刀位24')
# 机床采集项目
timestamp = fields.Datetime('时间戳', readonly=True)
- signed = fields.Integer('刷新间隔', readonly=True)
- status = fields.Boolean('在线状态', readonly=True)
- time_on = fields.Char('总在线时长', readonly=True)
- time_on_now = fields.Char('本次在线时长', readonly=True)
- tool_num = fields.Integer('当前刀具', readonly=True)
- program = fields.Char('当前程序', readonly=True)
- run_status = fields.Selection([('0', '空闲中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')], string='运行状态',
- readonly=True, default='0')
- run_time = fields.Char('总运行时长', readonly=True)
- cut_time = fields.Char('总切削时长', readonly=True)
- cut_status = fields.Selection([('0', '未切削'), ('1', '切削中'), ('2', '切削中'), ('3', '切削中')], string='切削状态',
- readonly=True, default='0')
- spindle_speed = fields.Char('主轴转速', readonly=True)
+ status = fields.Boolean('机床在线状态', readonly=True)
+ # run_status = fields.Selection([('0', '空闲中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')], string='机床运行状态',
+ # readonly=True, default='0')
+ run_status = fields.Char('机床运行状态', readonly=True)
+ run_time = fields.Char('机床累计运行时长', readonly=True)
+ # 机床系统日期
+ system_date = fields.Char('机床系统日期', readonly=True)
+ # 机床系统时间
+ system_time = fields.Char('机床系统时间', readonly=True)
+ cut_time = fields.Char('机床累计切削时间', readonly=True)
+ # cut_status = fields.Selection([('0', '未切削'), ('1', '切削中'), ('2', '切削中'), ('3', '切削中')], string='机床当前切削状态',
+ # readonly=True, default='0')
+ cut_status = fields.Char('机床当前切削状态', readonly=True)
+ # 当前程序名
+ program = fields.Char('机床当前程序', readonly=True)
+ # 当前刀具号
+ tool_num = fields.Integer('机床当前刀具号', readonly=True)
+ # 机床通电开机时间, 机床加工件数, 机床当前操作模式, 开始加工时间, 结束加工时间, 加工程序开始时间, 加工程序结束时间, 待机开始时间,
+ # 待机结束时间, 机床离线开始时间, 机床离线结束时间, 机床急停状态, 机床主程序名称, 程序运行的状态, 机床当前执行指令, 机床当前执行语句号
+ # 机床X轴当前位置, 机床Y轴当前位置, 机床Z轴当前位置
+ machine_power_on_time = fields.Char('机床通电开机时间', readonly=True)
+ product_counts = fields.Char('机床加工件数', readonly=True)
+ mode = fields.Char('机床当前操作模式', readonly=True)
+ start_time = fields.Char('开始加工时间', readonly=True)
+ end_time = fields.Char('结束加工时间', readonly=True)
+ program_start_time = fields.Char('加工程序开始时间', readonly=True)
+ program_end_time = fields.Char('加工程序结束时间', readonly=True)
+ standby_start_time = fields.Char('待机开始时间', readonly=True)
+ standby_end_time = fields.Char('待机结束时间', readonly=True)
+ offline_start_time = fields.Char('机床离线开始时间', readonly=True)
+ offline_end_time = fields.Char('机床离线结束时间', readonly=True)
+ emg_status = fields.Char('机床急停状态', readonly=True)
+ program_name = fields.Char('机床主程序名称', readonly=True)
+ program_status = fields.Char('程序运行状态', readonly=True)
+ current_program = fields.Char('机床当前执行指令', readonly=True)
+ current_program_seq = fields.Char('机床当前执行语句号', readonly=True)
+ x_abs_pos = fields.Char('机床X轴当前位置(mm)', readonly=True)
+ y_abs_pos = fields.Char('机床Y轴当前位置(mm)', readonly=True)
+ z_abs_pos = fields.Char('机床Z轴当前位置(mm)', readonly=True)
- tool_num_process_time1 = fields.Char('刀位1', readonly=True, default='0')
- tool_num_process_time2 = fields.Char('刀位2', readonly=True, default='0')
- tool_num_process_time3 = fields.Char('刀位3', readonly=True, default='0')
- tool_num_process_time4 = fields.Char('刀位4', readonly=True, default='0')
- tool_num_process_time5 = fields.Char('刀位5', readonly=True, default='0')
- tool_num_process_time6 = fields.Char('刀位6', readonly=True, default='0')
- tool_num_process_time7 = fields.Char('刀位7', readonly=True, default='0')
- tool_num_process_time8 = fields.Char('刀位8', readonly=True, default='0')
- tool_num_process_time9 = fields.Char('刀位9', readonly=True, default='0')
- tool_num_process_time10 = fields.Char('刀位10', readonly=True, default='0')
- tool_num_process_time11 = fields.Char('刀位11', readonly=True, default='0')
- tool_num_process_time12 = fields.Char('刀位12', readonly=True, default='0')
- tool_num_process_time13 = fields.Char('刀位13', readonly=True, default='0')
- tool_num_process_time14 = fields.Char('刀位14', readonly=True, default='0')
- tool_num_process_time15 = fields.Char('刀位15', readonly=True, default='0')
- tool_num_process_time16 = fields.Char('刀位16', readonly=True, default='0')
- tool_num_process_time17 = fields.Char('刀位17', readonly=True, default='0')
- tool_num_process_time18 = fields.Char('刀位18', readonly=True, default='0')
- tool_num_process_time19 = fields.Char('刀位19', readonly=True, default='0')
- tool_num_process_time20 = fields.Char('刀位20', readonly=True, default='0')
- tool_num_process_time21 = fields.Char('刀位21', readonly=True, default='0')
- tool_num_process_time22 = fields.Char('刀位22', readonly=True, default='0')
- tool_num_process_time23 = fields.Char('刀位23', readonly=True, default='0')
- tool_num_process_time24 = fields.Char('刀位24', readonly=True, default='0')
+ # 机床设定进给速度, 机床实际进给速度, 机床设定主轴转速, 机床实际主轴转速, 机床主轴负载, 机床X轴伺服轴负载, 机床Y轴伺服轴负载
+ # 机床Z轴伺服轴负载, 机床快速移动倍率, 机床进给倍率, 机床X轴机械坐标, 机床Y轴机械坐标, 机床Z轴机械坐标, 机床X轴相对工件坐标
+ # 机床Y轴相对工件坐标, 机床Z轴相对工件坐标, 机床X轴目标距离, 机床Y轴目标距离, 机床Z轴目标距离
+ feed_speed_set = fields.Char('机床设定进给速度(mm/min)', readonly=True)
+ act_feed_speed = fields.Char('机床实际进给速度(mm/min)', readonly=True)
+ spindle_speed_set = fields.Char('机床设定主轴转速(r/min)', readonly=True)
+ act_spindle_speed = fields.Char('机床实际主轴转速(r/min)', readonly=True)
+ spindle_load = fields.Char('机床主轴负载(%)', readonly=True)
+ x_axis_load = fields.Char('机床X轴伺服轴负载(%)', readonly=True)
+ y_axis_load = fields.Char('机床Y轴伺服轴负载(%)', readonly=True)
+ z_axis_load = fields.Char('机床Z轴伺服轴负载(%)', readonly=True)
+ rapid_feed = fields.Char('机床快速移动倍率(%)', readonly=True)
+ feed_rate = fields.Char('机床进给倍率(%)', readonly=True)
+ x_mach_coord = fields.Char('机床X轴机械坐标(mm)', readonly=True)
+ y_mach_coord = fields.Char('机床Y轴机械坐标(mm)', readonly=True)
+ z_mach_coord = fields.Char('机床Z轴机械坐标(mm)', readonly=True)
+ x_rel_coord = fields.Char('机床X轴相对工件坐标(mm)', readonly=True)
+ y_rel_coord = fields.Char('机床Y轴相对工件坐标(mm)', readonly=True)
+ z_rel_coord = fields.Char('机床Z轴相对工件坐标(mm)', readonly=True)
+ x_dis_coord = fields.Char('机床X轴目标距离(mm)', readonly=True)
+ y_dis_coord = fields.Char('机床Y轴目标距离(mm)', readonly=True)
+ z_dis_coord = fields.Char('机床Z轴目标距离(mm)', readonly=True)
+
+ # 故障报警时间, 故障报警信息, 故障消除时间(复原时间)
+ alarm_time = fields.Char('故障报警时间', readonly=True)
+ alarm_msg = fields.Char('故障报警信息', readonly=True)
+ clear_time = fields.Char('故障消除时间(复原时间)', readonly=True)
+
+ # 当前程序名, 机床累计运行时间, 机床系统日期, 机床系统时间, 当前刀具号, 机床循环时间
class WorkCenterBarcode(models.Model):
@@ -259,39 +290,59 @@ class WorkCenterBarcode(models.Model):
button_compensation_state = fields.Boolean(string='是否已经补偿', readonly=True)
button_up_all_state = fields.Boolean(string='是否已经全部下发', readonly=True)
machine_tool_id = fields.Many2one('sf.machine_tool.type', string='机床')
- machine_tool_name = fields.Char(string='机床名称', default='未知机床', compute='_run_info', readonly=True)
- machine_tool_type_id = fields.Char(string='机床型号', default='未知型号', compute='_run_info', readonly=True)
- machine_tool_status = fields.Boolean(string='在线状态', compute='_run_info', readonly=True)
+ # machine_tool_name = fields.Char(string='机床名称', default='未知机床', compute='_run_info', readonly=True)
+ # machine_tool_type_id = fields.Char(string='机床型号', default='未知型号', compute='_run_info', readonly=True)
+ # machine_tool_status = fields.Boolean(string='在线状态', compute='_run_info', readonly=True)
+ # machine_tool_run_status = fields.Selection([('0', '关机中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')],
+ # string='运行状态', compute='_run_info', readonly=True, default='0')
+ # machine_tool_timestamp = fields.Datetime('时间戳', compute='_run_info', readonly=True)
+ # machine_tool_time_on = fields.Char('总在线时长', compute='_run_info', readonly=True)
+ # machine_tool_time_on_now = fields.Char('本次在线时长', compute='_run_info', readonly=True)
+ # machine_tool_tool_num = fields.Integer('当前刀具', compute='_run_info', readonly=True)
+ # machine_tool_program = fields.Char('当前程序', compute='_run_info', readonly=True)
+ # machine_tool_machine_ip = fields.Char('机床IP', compute='_run_info', readonly=True)
+ # machine_tool_cut_status = fields.Selection([('0', '未切削'), ('1', '切削中'), ('2', '切削中'), ('3', '切削中')],
+ # string='切削状态', compute='_run_info', readonly=True, default='0')
+ # machine_tool_compensation_value_x = fields.Char('x补偿值', compute='_run_info', readonly=True)
+ # machine_tool_compensation_value_y = fields.Char('y补偿值', compute='_run_info', readonly=True)
+
+ machine_tool_name = fields.Char(string='机床名称', default='未知机床', readonly=True)
+ machine_tool_type_id = fields.Char(string='机床型号', default='未知型号', readonly=True)
+ machine_tool_status = fields.Boolean(string='在线状态', readonly=True)
machine_tool_run_status = fields.Selection([('0', '关机中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')],
- string='运行状态', compute='_run_info', readonly=True, default='0')
- machine_tool_timestamp = fields.Datetime('时间戳', compute='_run_info', readonly=True)
- machine_tool_time_on = fields.Char('总在线时长', compute='_run_info', readonly=True)
- machine_tool_time_on_now = fields.Char('本次在线时长', compute='_run_info', readonly=True)
- machine_tool_tool_num = fields.Integer('当前刀具', compute='_run_info', readonly=True)
- machine_tool_program = fields.Char('当前程序', compute='_run_info', readonly=True)
- machine_tool_machine_ip = fields.Char('机床IP', compute='_run_info', readonly=True)
+ string='运行状态', readonly=True, default='0')
+ machine_tool_timestamp = fields.Datetime('时间戳', readonly=True)
+ machine_tool_time_on = fields.Char('总在线时长', readonly=True)
+ machine_tool_time_on_now = fields.Char('本次在线时长', readonly=True)
+ machine_tool_tool_num = fields.Integer('当前刀具', readonly=True)
+ machine_tool_program = fields.Char('当前程序', readonly=True)
+ machine_tool_machine_ip = fields.Char('机床IP', readonly=True)
machine_tool_cut_status = fields.Selection([('0', '未切削'), ('1', '切削中'), ('2', '切削中'), ('3', '切削中')],
- string='切削状态', compute='_run_info', readonly=True, default='0')
- machine_tool_compensation_value_x = fields.Char('x补偿值', compute='_run_info', readonly=True)
- machine_tool_compensation_value_y = fields.Char('y补偿值', compute='_run_info', readonly=True)
+ string='切削状态', readonly=True, default='0')
+ machine_tool_compensation_value_x = fields.Char('x补偿值', readonly=True)
+ machine_tool_compensation_value_y = fields.Char('y补偿值', readonly=True)
+
+ # 工单状态
+
delivery_records = fields.One2many('delivery.record', 'workorder_id', string="下发记录")
@api.depends('equipment_id.timestamp')
def _run_info(self):
# self.machine_tool_name = '1号机床'
- self.machine_tool_name = self.equipment_id.name
- self.machine_tool_type_id = self.equipment_id.type_id.name
- self.machine_tool_status = self.equipment_id.status
- self.machine_tool_run_status = self.equipment_id.run_status
- self.machine_tool_timestamp = self.equipment_id.timestamp
- self.machine_tool_time_on = self.equipment_id.time_on
- self.machine_tool_time_on_now = self.equipment_id.time_on_now
- self.machine_tool_tool_num = self.equipment_id.tool_num
- self.machine_tool_program = self.equipment_id.program
- self.machine_tool_machine_ip = self.equipment_id.machine_ip
- self.machine_tool_cut_status = self.equipment_id.cut_status
- self.machine_tool_compensation_value_x = self.compensation_value_x
- self.machine_tool_compensation_value_y = self.compensation_value_y
+ # self.machine_tool_name = self.equipment_id.name
+ # self.machine_tool_type_id = self.equipment_id.type_id.name
+ # self.machine_tool_status = self.equipment_id.status
+ # self.machine_tool_run_status = self.equipment_id.run_status
+ # self.machine_tool_timestamp = self.equipment_id.timestamp
+ # self.machine_tool_time_on = self.equipment_id.time_on
+ # self.machine_tool_time_on_now = self.equipment_id.time_on_now
+ # self.machine_tool_tool_num = self.equipment_id.tool_num
+ # self.machine_tool_program = self.equipment_id.program
+ # self.machine_tool_machine_ip = self.equipment_id.machine_ip
+ # self.machine_tool_cut_status = self.equipment_id.cut_status
+ # self.machine_tool_compensation_value_x = self.compensation_value_x
+ # self.machine_tool_compensation_value_y = self.compensation_value_y
+ pass
def compensation(self):
diff --git a/sf_machine_connect/views/default_delivery.xml b/sf_machine_connect/views/default_delivery.xml
index 0238c736..c9f3c07c 100644
--- a/sf_machine_connect/views/default_delivery.xml
+++ b/sf_machine_connect/views/default_delivery.xml
@@ -11,6 +11,7 @@
+
diff --git a/sf_machine_connect/views/machine_monitor.xml b/sf_machine_connect/views/machine_monitor.xml
index d52bead8..c6964659 100644
--- a/sf_machine_connect/views/machine_monitor.xml
+++ b/sf_machine_connect/views/machine_monitor.xml
@@ -13,294 +13,138 @@
-
-
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -->
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/sf_maintenance/models/sf_maintenance_logs.py b/sf_maintenance/models/sf_maintenance_logs.py
index 2dc26f7e..7f80e163 100644
--- a/sf_maintenance/models/sf_maintenance_logs.py
+++ b/sf_maintenance/models/sf_maintenance_logs.py
@@ -10,21 +10,21 @@ class SfMaintenanceLogs(models.Model):
name = fields.Char(string='名称')
type = fields.Selection([('type1', '类型1'), ('type2', '类型2')], string='类型')
brand = fields.Many2one('sf.machine.brand', related='maintenance_equipment_id.brand_id', string='品牌')
- maintenance_equipment_id = fields.Many2one('maintenance.equipment', string='设备')
+ maintenance_equipment_id = fields.Many2one('maintenance.equipment', string='机台号')
maintenance_equipment_oee_id = fields.Many2one('maintenance.equipment.oee', string='设备oee')
code_location = fields.Char(string='编码位置')
fault_type = fields.Selection(
[('电气类', '电气类'), ('机械类', '机械类'), ('程序类', '程序类'), ('系统类', '系统类')], string='故障类型')
fault_code = fields.Char(string='故障代码')
- fault_alarm_info = fields.Char(string='故障报警信息')
+ fault_alarm_info = fields.Text(string='故障报警信息')
alarm_level = fields.Selection([('一级', '一级(严重)'), ('二级', '二级(中等)'), ('三级', '三级(轻微)')],
string='报警级别')
- alarm_time = fields.Datetime(string='报警时间')
+ alarm_time = fields.Datetime(string='故障报警时间')
alarm_way = fields.Selection([('文本提示报警', '文本提示报警'), ('声光报警', '声光报警'), ('图文报警', '图文报警')],
string='报警方式')
fault_process = fields.Text(string='故障处理方法')
operator = fields.Many2one('res.users', string='处理人')
- recovery_time = fields.Datetime(string='复原时间')
+ recovery_time = fields.Datetime(string='故障消除时间')
fault_duration = fields.Float(string='故障时长')
note = fields.Text(string='备注')
active = fields.Boolean('Active', default=True)
diff --git a/sf_maintenance/models/sf_maintenance_oee.py b/sf_maintenance/models/sf_maintenance_oee.py
index 6d76ffbe..41301410 100644
--- a/sf_maintenance/models/sf_maintenance_oee.py
+++ b/sf_maintenance/models/sf_maintenance_oee.py
@@ -7,20 +7,23 @@ class SfMaintenanceEquipmentOEE(models.Model):
_description = '设备OEE'
name = fields.Char('设备oee')
- equipment_id = fields.Many2one('maintenance.equipment', '设备',
+ equipment_id = fields.Many2one('maintenance.equipment', '机台号',
domain="[('category_id.equipment_type', '=', '机床'),('state_zc', '=', '已注册')]")
type_id = fields.Many2one('sf.machine_tool.type', '型号', related='equipment_id.type_id')
- machine_tool_picture = fields.Binary('设备图片', related='equipment_id.machine_tool_picture')
+ machine_tool_picture = fields.Binary('设备图片', related='equipment_id.machine_tool_picture')
state = fields.Selection(
[("正常", "正常"), ("故障停机", "故障停机"), ("计划维保", "计划维保"), ("空闲", "空闲"),
("封存(报废)", "封存(报废)")],
default='正常', string="机床状态", related='equipment_id.state')
- run_time = fields.Float('正常运行总时长(h)')
- equipment_time = fields.Float('总时长(h)')
- done_nums = fields.Integer('累计加工总件数')
- utilization_rate = fields.Char('开动率')
- fault_time = fields.Float('故障停机总时长(h)')
+ run_time = fields.Float('加工时长(h)')
+ equipment_time = fields.Float('开机时长(h)')
+ done_nums = fields.Integer('加工件数')
+ utilization_rate = fields.Char('可用率')
+ fault_time = fields.Float('故障时长')
fault_nums = fields.Integer('故障次数')
+ # 故障率
+ fault_rate = fields.Char('故障率')
+ # 设备故障日志
sf_maintenance_logs_ids = fields.One2many('sf.maintenance.logs', 'maintenance_equipment_oee_id', '设备故障日志',
related='equipment_id.sf_maintenance_logs_ids')
oee_logs = fields.One2many('maintenance.equipment.oee.logs', 'equipment_oee_id', string='运行日志')
@@ -38,12 +41,52 @@ class SfMaintenanceEquipmentOEELog(models.Model):
_name = 'maintenance.equipment.oee.logs'
_description = '设备运行日志'
- name = fields.Char('运行日志')
- run_time = fields.Datetime('时间')
- state = fields.Selection([("开机", "开机"), ("关机", "关机"), ("等待", "等待"), ("开始加工", "开始加工"),
- ("结束加工", "结束加工"), ("故障", "故障"),
- ("检修", "检修"), ("保养", "保养")], default="", string="事件/状态")
- workorder_id = fields.Char('加工订单')
- time = fields.Char('持续时长')
- color = fields.Char('颜色', default=1)
+ equipment_id = fields.Many2one('maintenance.equipment', '机台号')
+ equipment_code = fields.Char('设备编码')
+ name = fields.Char('设备名称', readonly='True')
+ machine_tool_picture = fields.Binary('设备图片')
+ type_id = fields.Many2one('sf.machine_tool.type', '品牌型号')
+ state = fields.Selection([("加工", "加工"), ("关机", "关机"), ("待机", "待机"), ("故障", "故障"),
+ ("检修", "检修"), ("保养", "保养")], default="", string="实时状态")
+ online_time = fields.Char('开机时长')
+
+ offline_time = fields.Char('关机时长')
+ offline_nums = fields.Integer('关机次数')
+ # 待机时长
+
+ idle_time = fields.Char('待机时长')
+
+ # 待机率
+ idle_rate = fields.Char('待机率')
+
+ work_time = fields.Char('加工时长')
+ work_rate = fields.Char('可用率')
+ fault_time = fields.Char('故障时长')
+ fault_rate = fields.Char('故障率')
+ fault_nums = fields.Integer('故障次数')
+
+ detail_ids = fields.One2many('maintenance.equipment.oee.log.detail', 'log_id', string='日志详情')
+
+ # maintenance_time = fields.Char('维保时长')
+ # work_nums = fields.Integer('加工件数')
equipment_oee_id = fields.Many2one('maintenance.equipment.oee', '设备OEE')
+
+ @api.onchange('equipment_id')
+ def get_name(self):
+ self.name = self.equipment_id.name
+ self.equipment_code = self.equipment_id.code
+
+
+# 设备运行日志详情
+class SfMaintenanceEquipmentOEELogDetail(models.Model):
+ _name = 'maintenance.equipment.oee.log.detail'
+ _description = '设备运行日志详情'
+
+ sequence = fields.Integer('序号')
+ time = fields.Datetime('时间')
+ state = fields.Selection([("加工", "加工"), ("关机", "关机"), ("待机", "待机"), ("故障", "故障"),
+ ("检修", "检修"), ("保养", "保养")], default="", string="事件/状态")
+ production_id = fields.Many2one('mrp.production', '加工工单')
+
+ log_id = fields.Many2one('maintenance.equipment.oee.logs', '日志')
+
diff --git a/sf_maintenance/security/ir.model.access.csv b/sf_maintenance/security/ir.model.access.csv
index abbd4878..ae18b36f 100644
--- a/sf_maintenance/security/ir.model.access.csv
+++ b/sf_maintenance/security/ir.model.access.csv
@@ -67,3 +67,6 @@ access_sf_cutting_tool_type_admin_sf_group_equipment_user,sf_cutting_tool_type_a
access_sf_cutting_tool_type_group_purchase_director_sf_group_equipment_user,sf_cutting_tool_type_group_purchase_director,sf_base.model_sf_cutting_tool_type,sf_maintenance.sf_group_equipment_user,1,0,0,0
access_sf_cutting_tool_type_group_sale_director_sf_group_equipment_user,sf_cutting_tool_type_group_sale_director,sf_base.model_sf_cutting_tool_type,sf_maintenance.sf_group_equipment_user,1,0,0,0
access_sf_cutting_tool_type_group_plan_director_sf_group_equipment_user,sf_cutting_tool_type_group_plan_director,sf_base.model_sf_cutting_tool_type,sf_maintenance.sf_group_equipment_user,1,0,0,0
+
+access_maintenance_equipment_oee_logs,maintenance_equipment_oee_logs,model_maintenance_equipment_oee_logs,sf_maintenance.sf_group_equipment_manager,1,1,1,1
+access_maintenance_equipment_oee_log_detail,maintenance_equipment_oee_log_detail,model_maintenance_equipment_oee_log_detail,sf_maintenance.sf_group_equipment_manager,1,1,1,1
\ No newline at end of file
diff --git a/sf_maintenance/views/maintenance_equipment_oee_views.xml b/sf_maintenance/views/maintenance_equipment_oee_views.xml
index 091e3c47..9370fbbc 100644
--- a/sf_maintenance/views/maintenance_equipment_oee_views.xml
+++ b/sf_maintenance/views/maintenance_equipment_oee_views.xml
@@ -14,6 +14,7 @@
+
@@ -49,19 +50,19 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sf_maintenance/views/maintenance_logs_views.xml b/sf_maintenance/views/maintenance_logs_views.xml
index 087f6ffb..0d172285 100644
--- a/sf_maintenance/views/maintenance_logs_views.xml
+++ b/sf_maintenance/views/maintenance_logs_views.xml
@@ -7,22 +7,20 @@
sf.maintenance.logs
-
-
+
-
-
-
+
+
-
+
@@ -45,25 +43,32 @@
-
-
-
+
+
-
-
-
-
-
-
-
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -100,6 +105,246 @@
+
+
+ maintenance.logs.run.tree
+ maintenance.equipment.oee.logs
+
+
+
+
+
+
+
+
+ maintenance.logs.run.form
+ maintenance.equipment.oee.logs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ maintenance.logs.run.detail.tree
+ maintenance.equipment.oee.log.detail
+
+
+
+
+
+
+
+
+
+
+
+ maintenance.logs.run.detail.form
+ maintenance.equipment.oee.log.detail
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 设备运行日志
+ ir.actions.act_window
+ maintenance.equipment.oee.logs
+
+ tree,form
+
+
+
+
+ 设备运行日志
+
+
+
+
+
+
+
diff --git a/sf_manufacturing/__manifest__.py b/sf_manufacturing/__manifest__.py
index 54538a7d..39da4482 100644
--- a/sf_manufacturing/__manifest__.py
+++ b/sf_manufacturing/__manifest__.py
@@ -14,9 +14,12 @@
'data': [
'data/stock_data.xml',
'data/empty_racks_data.xml',
+ 'data/panel_data.xml',
'security/group_security.xml',
'security/ir.model.access.csv',
'wizard/workpiece_delivery_views.xml',
+ 'wizard/rework_wizard_views.xml',
+ 'wizard/production_wizard_views.xml',
'views/mrp_views_menus.xml',
'views/stock_lot_views.xml',
'views/mrp_production_addional_change.xml',
diff --git a/sf_manufacturing/controllers/controllers.py b/sf_manufacturing/controllers/controllers.py
index 9f76f065..f5a55db2 100644
--- a/sf_manufacturing/controllers/controllers.py
+++ b/sf_manufacturing/controllers/controllers.py
@@ -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):
"""
@@ -25,7 +25,8 @@ class Manufacturing_Connect(http.Controller):
{'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'])])
+ workorder = request.env['mrp.workorder'].sudo().search(
+ [('rfid_code', '=', ret['RfidCode']), ('state', '!=', 'rework')])
if workorder:
for item in workorder:
res['Datas'].append({
@@ -102,7 +103,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('get_ShiftPlan error:%s' % e)
return json.JSONEncoder().encode(res)
- @http.route('/AutoDeviceApi/QcCheck', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
+ @http.route('/AutoDeviceApi/QcCheck', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def get_qcCheck(self, **kw):
"""
@@ -122,7 +123,8 @@ class Manufacturing_Connect(http.Controller):
logging.info('RfidCode:%s' % ret['RfidCode'])
if 'RfidCode' in ret:
workorder = request.env['mrp.workorder'].sudo().search(
- [('routing_type', '=', '装夹预调'), ('rfid_code', '=', ret['RfidCode'])], limit=1, order='id asc')
+ [('routing_type', '=', '装夹预调'), ('rfid_code', '=', ret['RfidCode']), ('state', '!=', 'rework')],
+ limit=1, order='id asc')
if workorder:
for item in workorder:
if item.material_center_point:
@@ -143,7 +145,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='sf_token', methods=['GET', 'POST'], csrf=False,
+ @http.route('/AutoDeviceApi/FeedBackStart', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def button_Work_START(self, **kw):
"""
@@ -163,7 +165,7 @@ class Manufacturing_Connect(http.Controller):
equipment_id = ret["DeviceId"]
workorder = request.env['mrp.workorder'].sudo().search(
[('production_id', '=', production_id), ('routing_type', '=', routing_type),
- ('rfid_code', '!=', False)], limit=1)
+ ('rfid_code', '!=', False), ('state', '!=', 'rework')], limit=1)
if not workorder:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '该工单不存在'}
return json.JSONEncoder().encode(res)
@@ -191,7 +193,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='sf_token', methods=['GET', 'POST'], csrf=False,
+ @http.route('/AutoDeviceApi/FeedBackEnd', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def button_Work_End(self, **kw):
"""
@@ -211,7 +213,7 @@ class Manufacturing_Connect(http.Controller):
routing_type = ret['CraftId']
workorder = request.env['mrp.workorder'].sudo().search(
[('production_id', '=', production_id), ('routing_type', '=', routing_type),
- ('rfid_code', '!=', False)], limit=1)
+ ('rfid_code', '!=', False), ('state', '!=', 'rework')], limit=1)
if not workorder:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '该工单不存在'}
return json.JSONEncoder().encode(res)
@@ -220,28 +222,29 @@ class Manufacturing_Connect(http.Controller):
return json.JSONEncoder().encode(res)
# workorder.write({'date_finished': datetime.now()})
if ret['IsComplete'] is True:
- workorder.button_finish()
- # workorder.process_state = '待解除装夹'
- # workorder.sudo().production_id.process_state = '待解除装夹'
+ workorder.write({'date_finished': datetime.now()})
- # 根据工单的实际结束时间修改排程单的结束时间、状态,同时修改销售订单的状态
- # if workorder.date_finished:
- # request.env['sf.production.plan'].sudo().search([('production_id', '=', production_id)]).write(
- # {'actual_end_time': workorder.date_finished,
- # 'state': 'finished'})
- # production_obj = request.env['mrp.production'].sudo().search([('name', '=', production_id)])
- # if production_obj:
- # production_obj.sudo().work_order_state = '已完成'
- # production_obj.write({'state': 'done'})
- # request.env['sale.order'].sudo().search(
- # [('name', '=', production_obj.origin)]).write({'schedule_status': 'to deliver'})
+ # workorder.process_state = '待解除装夹'
+ # workorder.sudo().production_id.process_state = '待解除装夹'
+
+ # 根据工单的实际结束时间修改排程单的结束时间、状态,同时修改销售订单的状态
+ # if workorder.date_finished:
+ # request.env['sf.production.plan'].sudo().search([('production_id', '=', production_id)]).write(
+ # {'actual_end_time': workorder.date_finished,
+ # 'state': 'finished'})
+ # production_obj = request.env['mrp.production'].sudo().search([('name', '=', production_id)])
+ # if production_obj:
+ # production_obj.sudo().work_order_state = '已完成'
+ # production_obj.write({'state': 'done'})
+ # 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='sf_token', methods=['GET', 'POST'], csrf=False,
+ @http.route('/AutoDeviceApi/PartQualityInspect', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def PartQualityInspect(self, **kw):
"""
@@ -259,7 +262,8 @@ class Manufacturing_Connect(http.Controller):
production_id = ret['BillId']
routing_type = ret['CraftId']
workorder = request.env['mrp.workorder'].sudo().search(
- [('production_id', '=', production_id), ('routing_type', '=', routing_type)], limit=1)
+ [('production_id', '=', production_id), ('routing_type', '=', routing_type), ('state', '!=', 'rework')],
+ limit=1)
if workorder:
# workorder.test_results = ret['Quality']
logging.info('制造订单:%s' % workorder.production_id.name)
@@ -299,7 +303,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('PartQualityInspect error:%s' % e)
return json.JSONEncoder().encode(res)
- @http.route('/AutoDeviceApi/CMMProgDolod', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
+ @http.route('/AutoDeviceApi/CMMProgDolod', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def CMMProgDolod(self, **kw):
"""
@@ -317,7 +321,7 @@ class Manufacturing_Connect(http.Controller):
if 'RfidCode' in ret:
logging.info('RfidCode:%s' % ret['RfidCode'])
workorder = request.env['mrp.workorder'].sudo().search(
- [('rfid_code', '=', ret['RfidCode']), ('routing_type', '=', 'CNC加工')])
+ [('rfid_code', '=', ret['RfidCode']), ('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')])
if workorder:
for item in workorder.cmm_ids:
if item.program_create_date is not False:
@@ -339,7 +343,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('CMMProgDolod error:%s' % e)
return json.JSONEncoder().encode(res)
- @http.route('/AutoDeviceApi/NCProgDolod', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
+ @http.route('/AutoDeviceApi/NCProgDolod', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def NCProgDolod(self, **kw):
"""
@@ -357,7 +361,7 @@ class Manufacturing_Connect(http.Controller):
if 'RfidCode' in ret:
logging.info('RfidCode:%s' % ret['RfidCode'])
workorder = request.env['mrp.workorder'].sudo().search(
- [('rfid_code', '=', ret['RfidCode']), ('routing_type', '=', 'CNC加工')])
+ [('rfid_code', '=', ret['RfidCode']), ('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')])
if workorder:
for item in workorder.cnc_ids:
res['Datas'].append({
@@ -380,7 +384,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('NCProgDolod error:%s' % e)
return json.JSONEncoder().encode(res)
- @http.route('/AutoDeviceApi/LocationChange', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
+ @http.route('/AutoDeviceApi/LocationChange', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def LocationChange(self, **kw):
"""
@@ -438,7 +442,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('LocationChange error:%s' % e)
return json.JSONEncoder().encode(res)
- @http.route('/AutoDeviceApi/AGVToProduct', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
+ @http.route('/AutoDeviceApi/AGVToProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def AGVToProduct(self, **kw):
"""
@@ -466,7 +470,7 @@ class Manufacturing_Connect(http.Controller):
if rfid_code is not None:
domain = [
('rfid_code', '=', rfid_code),
- ('routing_type', '=', 'CNC加工')
+ ('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')
]
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
if workorder:
@@ -475,14 +479,16 @@ class Manufacturing_Connect(http.Controller):
logging.info(
'工单产线状态:%s' % order.production_line_state)
panel_workorder = request.env['mrp.workorder'].sudo().search(
- [('rfid_code', '=', rfid_code),
+ [('rfid_code', '=', rfid_code), ('state', '!=', 'rework'),
('processing_panel', '=', order.processing_panel)])
if panel_workorder:
panel_workorder.write({'production_line_state': '已上产线'})
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
[
('rfid_code', '=', rfid_code), ('type', '=', '上产线'),
- ('production_id', '=', order.production_id.id)])
+ ('production_id', '=', order.production_id.id),
+ ('workorder_id', '=', order.id),
+ ('workorder_state', '=', 'done')])
if workpiece_delivery.status == '待下发':
workpiece_delivery.write({'is_manual_work': True})
else:
@@ -497,7 +503,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('AGVToProduct error:%s' % e)
return json.JSONEncoder().encode(res)
- @http.route('/AutoDeviceApi/AGVDownProduct', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
+ @http.route('/AutoDeviceApi/AGVDownProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def AGVDownProduct(self, **kw):
"""
@@ -527,7 +533,7 @@ class Manufacturing_Connect(http.Controller):
if rfid_code is not None:
domain = [
('rfid_code', '=', rfid_code),
- ('routing_type', '=', 'CNC加工')
+ ('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')
]
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
if workorder:
@@ -536,15 +542,19 @@ class Manufacturing_Connect(http.Controller):
logging.info(
'工单产线状态:%s' % order.production_line_state)
panel_workorder = request.env['mrp.workorder'].sudo().search(
- [('rfid_code', '=', rfid_code),
+ [('rfid_code', '=', rfid_code), ('state', '!=', 'rework'),
('processing_panel', '=', order.processing_panel)])
if panel_workorder:
panel_workorder.write({'production_line_state': '已下产线'})
+ workorder.write({'state': 'to be detected'})
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
[
('rfid_code', '=', rfid_code), ('type', '=', '下产线'),
- ('production_id', '=', order.production_id.id)])
- delivery_Arr.append(workpiece_delivery.id)
+ ('production_id', '=', order.production_id.id),
+ ('workorder_id', '=', order.id),
+ ('workorder_state', '=', 'done')])
+ if workpiece_delivery:
+ delivery_Arr.append(workpiece_delivery.id)
else:
res = {'Succeed': False, 'ErrorCode': 204,
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}
diff --git a/sf_manufacturing/data/panel_data.xml b/sf_manufacturing/data/panel_data.xml
new file mode 100644
index 00000000..0bdf4aa3
--- /dev/null
+++ b/sf_manufacturing/data/panel_data.xml
@@ -0,0 +1,24 @@
+
+
+
+
+ ZM
+
+
+
+ FM
+
+
+ YC
+
+
+ ZC
+
+
+ QC
+
+
+ HC
+
+
+
\ No newline at end of file
diff --git a/sf_manufacturing/data/stock_data.xml b/sf_manufacturing/data/stock_data.xml
index 07d59708..9220d827 100644
--- a/sf_manufacturing/data/stock_data.xml
+++ b/sf_manufacturing/data/stock_data.xml
@@ -1,6 +1,16 @@
+
+ 返工且编程中的制造订单定时获取Cloud编程单状态
+
+ code
+ model._cron_get_programming_state()
+ 3
+ minutes
+ -1
+
+
工序编码规则
mrp.routing.workcenter
diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py
index 73782a9c..e6668ecf 100644
--- a/sf_manufacturing/models/mrp_production.py
+++ b/sf_manufacturing/models/mrp_production.py
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
import base64
import logging
+import json
+import os
import re
import requests
from itertools import groupby
@@ -24,6 +26,46 @@ class MrpProduction(models.Model):
work_order_state = fields.Selection([('未排', '未排'), ('已排', '已排'), ('已完成', '已完成')],
string='工单状态', default='未排')
+ detection_result_ids = fields.One2many('sf.detection.result', 'production_id', '检测报告')
+ tool_state = fields.Selection([('0', '正常'), ('1', '缺刀'), ('2', '无效刀')], string='功能刀具状态', default='0',
+ store=True, compute='_compute_tool_state')
+ tool_state_remark = fields.Text(string='功能刀具状态备注(缺刀)', compute='_compute_tool_state_remark', store=True)
+ tool_state_remark2 = fields.Text(string='功能刀具状态备注(无效刀)', readonly=True)
+
+ @api.depends('workorder_ids.tool_state_remark')
+ def _compute_tool_state_remark(self):
+ for item in self:
+ if item.workorder_ids:
+ workorder_ids = item.workorder_ids.filtered(lambda a: a.state not in ('rework', '返工'))
+ if workorder_ids.filtered(lambda a: a.tool_state_remark):
+ work_ids = workorder_ids.filtered(lambda a: a.tool_state == '1' and a.state not in ['rework'])
+ tool_state_remark = ''
+ for work_id in work_ids:
+ if tool_state_remark == '':
+ tool_state_remark = f"{work_id.tool_state_remark}"
+ else:
+ tool_state_remark = f"{tool_state_remark}\n{work_id.tool_state_remark}"
+ item.tool_state_remark = tool_state_remark
+ else:
+ item.tool_state_remark = False
+
+ @api.depends('workorder_ids.tool_state')
+ def _compute_tool_state(self):
+ for item in self:
+ if item.workorder_ids:
+ tool_state = item.tool_state
+ workorder_ids = item.workorder_ids.filtered(lambda a: a.state not in ('rework', '返工'))
+ if workorder_ids.filtered(lambda a: a.tool_state == '2'):
+ item.tool_state = '2'
+ elif workorder_ids.filtered(lambda a: a.tool_state == '1'):
+ item.tool_state = '1'
+ else:
+ item.tool_state = '0'
+ if tool_state == '2' and item.tool_state != '2':
+ item.detection_result_ids.filtered(
+ lambda a: a.detailed_reason == '无效功能刀具' and a.handle_result == '待处理').write(
+ {'handle_result': '已处理'})
+
# state = fields.Selection(selection_add=[
# ('pending_scheduling', '待排程'),
# ('pending_processing', '待加工'),
@@ -34,9 +76,10 @@ class MrpProduction(models.Model):
('confirmed', '待排程'),
('pending_cam', '待加工'),
('progress', '加工中'),
+ ('rework', '返工'),
('to_close', 'To Close'),
('done', 'Done'),
- ('cancel', 'Cancelled')], string='State',
+ ('cancel', '报废')], string='State',
compute='_compute_state', copy=False, index=True, readonly=True,
store=True, tracking=True,
help=" * Draft: The MO is not confirmed yet.\n"
@@ -51,10 +94,13 @@ class MrpProduction(models.Model):
programming_no = fields.Char('编程单号')
work_state = fields.Char('业务状态')
programming_state = fields.Selection(
- [('编程中', '编程中'), ('已编程', '已编程')], string='编程状态', tracking=True)
+ [('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'), ('已下发', '已下发')],
+ string='编程状态',
+ tracking=True)
glb_file = fields.Binary("glb模型文件")
production_line_id = fields.Many2one('sf.production.line', string='生产线', tracking=True)
plan_start_processing_time = fields.Datetime('计划开始加工时间')
+ is_rework = fields.Boolean(string='是否返工', default=False)
# production_line_state = fields.Selection(
# [('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')],
# string='上/下产线', default='待上产线', tracking=True)
@@ -75,10 +121,10 @@ class MrpProduction(models.Model):
part_drawing = fields.Binary('零件图纸')
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
- rework_production = fields.Many2one('mrp.production', string='返工的制造订单')
+ is_scrap = fields.Boolean('是否报废', default=False)
@api.depends(
- 'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state',
+ 'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state', 'tool_state',
'workorder_ids.state', 'product_qty', 'qty_producing', 'schedule_state')
def _compute_state(self):
for production in self:
@@ -114,12 +160,33 @@ class MrpProduction(models.Model):
if (
production.state == 'to_close' or production.state == 'progress') and production.schedule_state == '未排':
production.state = 'confirmed'
+ elif production.state == 'pending_cam' and production.schedule_state == '未排':
+ production.state = 'confirmed'
elif production.state == 'to_close' and production.schedule_state == '已排':
production.state = 'pending_cam'
if production.state == 'progress':
- if all(wo_state not in ('progress', 'done') for wo_state in production.workorder_ids.mapped('state')):
+ if all(wo_state not in ('progress', 'done', 'rework') for wo_state in
+ production.workorder_ids.mapped('state')):
production.state = 'pending_cam'
+ if production.is_rework is True:
+ production.state = 'rework'
+ # if production.state == 'pending_cam':
+ # if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')):
+ # production.state = 'done'
+ if any(
+ (
+ wo.test_results == '返工' and wo.state == 'done' and production.programming_state in [
+ '已编程']) or (
+ wo.state == 'rework' and production.programming_state == '编程中') or (
+ wo.is_rework is True and wo.state == 'done' and production.programming_state in ['编程中',
+ '已编程'])
+ for wo in
+ production.workorder_ids):
+ production.state = 'rework'
+ # 如果制造订单的功能刀具为【无效刀】则制造订单状态改为返工
+ if production.tool_state == '2':
+ production.state = 'rework'
def action_check(self):
"""
@@ -150,27 +217,66 @@ class MrpProduction(models.Model):
for production in self:
production.maintenance_count = len(production.request_ids)
- # 制造订单报废:编程单更新
- def updateCNC(self):
+ # 获取cloud编程单的状态
+ def _cron_get_programming_state(self):
try:
- res = {'production_no': self.name, 'programming_no': self.programming_no,
- 'order_no': self.origin}
+ if not self:
+ reproduction = self.env['mrp.production'].search(
+ [('state', '=', 'rework'), ('programming_state', '=', '编程中'), ('is_rework', '=', True)])
+ else:
+ reproduction = self
+ if reproduction:
+ programming_no_set = set([str(item.programming_no) for item in reproduction])
+ programming_no = list(programming_no_set)
+ programming_no_str = ','.join(programming_no)
+ res = {'programming_no': programming_no_str}
+ logging.info('res=%s:' % res)
+ configsettings = self.env['res.config.settings'].get_values()
+ config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
+ url = '/api/intelligent_programming/get_state'
+ config_url = configsettings['sf_url'] + url
+ ret = requests.post(config_url, json=res, data=None, headers=config_header)
+ ret = ret.json()
+ result = json.loads(ret['result'])
+ logging.info('cron_get_programming_state-ret:%s' % result)
+ if result['status'] == 1:
+ for item in result['programming_list']:
+ if not self:
+ for rp in reproduction:
+ if rp.programming_no == item['programming_no']:
+ rp.write({'programming_state': '已编程未下发' if item[
+ 'programming_state'] == '已编程' else '编程中'})
+ logging.info('rp:%s' % rp.name)
+
+ else:
+ return item
+
+ else:
+ raise UserError(ret['message'])
+ except Exception as e:
+ logging.info('cron_get_programming_state error:%s' % e)
+
+ # 编程单更新
+ def update_programming_state(self):
+ try:
+ res = {'programming_no': self.programming_no,
+ 'manufacturing_type': 'rework' if self.is_scrap is False else 'scrap'}
logging.info('res=%s:' % res)
configsettings = self.env['res.config.settings'].get_values()
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
- url = '/api/intelligent_programming/update_intelligent_programmings'
+ url = '/api/intelligent_programming/reset_state_again'
config_url = configsettings['sf_url'] + url
- res['token'] = configsettings['token']
- ret = requests.post(config_url, json={}, data=res, headers=config_header)
+ ret = requests.post(config_url, json=res, data=None, headers=config_header)
ret = ret.json()
- logging.info('updateCNC-ret:%s' % ret)
- if ret['status'] == 1:
- self.write({'work_state': '已编程'})
+ result = json.loads(ret['result'])
+ logging.info('update_programming_state-ret:%s' % result)
+ if result['status'] == 1:
+ self.write({'is_rework': True})
else:
raise UserError(ret['message'])
except Exception as e:
- logging.info('updateCNC error:%s' % e)
- raise UserError("更新程单失败,请联系管理员")
+ logging.info('update_programming_state error:%s' % e)
+ raise UserError("更新编程单状态失败,请联系管理员")
# cnc程序获取
def fetchCNC(self, production_names):
@@ -196,7 +302,7 @@ class MrpProduction(models.Model):
'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,
+ '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,
@@ -429,28 +535,6 @@ class MrpProduction(models.Model):
for workorder in production.workorder_ids:
workorder.duration_expected = workorder._get_duration_expected()
- # 在之前的销售单上重新生成制造订单
- def create_production1_values(self, production):
- production_values_str = {'origin': production.origin,
- 'product_id': production.product_id.id,
- 'product_description_variants': production.product_description_variants,
- 'product_qty': production.product_qty,
- 'product_uom_id': production.product_uom_id.id,
- 'location_src_id': production.location_src_id.id,
- 'location_dest_id': production.location_dest_id.id,
- 'bom_id': production.bom_id.id,
- 'date_deadline': production.date_deadline,
- 'date_planned_start': production.date_planned_start,
- 'date_planned_finished': production.date_planned_finished,
- 'procurement_group_id': False,
- 'propagate_cancel': production.propagate_cancel,
- 'orderpoint_id': production.orderpoint_id.id,
- 'picking_type_id': production.picking_type_id.id,
- 'company_id': production.company_id.id,
- 'move_dest_ids': production.move_dest_ids.ids,
- 'user_id': production.user_id.id}
- return production_values_str
-
# 工单排序
def _reset_work_order_sequence1(self, k):
for rec in self:
@@ -519,70 +603,90 @@ class MrpProduction(models.Model):
def _reset_work_order_sequence(self):
for rec in self:
- sequence_list = {}
+ workorder_ids = rec.workorder_ids.filtered(lambda item: item.state in ('返工', 'rework'))
# 产品模型类型
model_type_id = rec.product_id.product_model_type_id
# 产品加工面板
model_processing_panel = rec.product_id.model_processing_panel
- if model_type_id:
- if model_processing_panel:
- tmpl_num = 1
- panel_list = model_processing_panel.split(',')
- for panel in panel_list:
- panel_sequence_list = {}
- # 成品工序
- product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids
- if product_routing_tmpl_ids:
- for tmpl_id in product_routing_tmpl_ids:
- panel_sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
- tmpl_num += 1
- sequence_list.update({panel: panel_sequence_list})
- # 表面工艺工序
- # 模型类型的表面工艺工序模版
- 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})
+ if not workorder_ids:
+ sequence_list = {}
+ if model_type_id:
+ if model_processing_panel:
+ tmpl_num = 1
+ panel_list = model_processing_panel.split(',')
+ for panel in panel_list:
+ panel_sequence_list = {}
+ # 成品工序
+ product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids
+ if product_routing_tmpl_ids:
+ for tmpl_id in product_routing_tmpl_ids:
+ panel_sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
+ tmpl_num += 1
+ sequence_list.update({panel: panel_sequence_list})
+ # 表面工艺工序
+ # 模型类型的表面工艺工序模版
+ 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('该产品【加工面板】为空!')
else:
- raise ValidationError('该产品【加工面板】为空!')
+ raise ValidationError('该产品没有选择【模版类型】!')
- else:
- raise ValidationError('该产品没有选择【模版类型】!')
-
- for work in rec.workorder_ids:
- if sequence_list.get(work.name):
- work.sequence = sequence_list[work.name]
- elif sequence_list.get(work.processing_panel):
- processing_panel = sequence_list.get(work.processing_panel)
- if processing_panel.get(work.name):
- work.sequence = processing_panel[work.name]
+ for work in rec.workorder_ids:
+ if sequence_list.get(work.name):
+ work.sequence = sequence_list[work.name]
+ elif sequence_list.get(work.processing_panel):
+ processing_panel = sequence_list.get(work.processing_panel)
+ if processing_panel.get(work.name):
+ work.sequence = processing_panel[work.name]
+ else:
+ raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
else:
raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
- else:
- raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
- # if work.name == '获取CNC加工程序':
- # work.button_start()
- # #work.fetchCNC()
- # work.button_finish()
+ # 当单个面触发返工时,将新生成的工单插入到返工工单下方,并且后面的所以工单工序重排
+ elif rec.workorder_ids.filtered(lambda item: item.sequence == 0):
+ # 获取新增的返工工单
+ work_ids = rec.workorder_ids.filtered(lambda item: item.sequence == 0)
+ # 获取当前返工面最后一个工单工序
+ sequence_max = sorted(
+ rec.workorder_ids.filtered(lambda item: item.processing_panel == work_ids[0].processing_panel),
+ key=lambda item: item.sequence, reverse=True)[0].sequence
+ # 对当前返工工单之后的工单工序进行重排
+ work_order_ids = rec.workorder_ids.filtered(lambda item: item.sequence > sequence_max)
+ for work_id in work_order_ids:
+ work_id.sequence = work_id.sequence + 3
+ # 生成新增的返工工单的工序
+ # 成品工序
+ panel_sequence_list = {}
+ 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_max += 1
+ panel_sequence_list.update({tmpl_id.route_workcenter_id.name: sequence_max})
+ for work_id in work_ids:
+ if panel_sequence_list.get(work_id.name):
+ work_id.sequence = panel_sequence_list[work_id.name]
# 创建工单并进行排序
def _create_workorder(self, item):
@@ -631,9 +735,11 @@ class MrpProduction(models.Model):
for production in self:
logging.info('qty_produced:%s' % production.qty_produced)
+ if production.qty_produced == 0.0:
+ production.qty_produced = 1.0
production.write({
'date_finished': fields.Datetime.now(),
- 'product_qty': production.product_qty if production.qty_produced < 1.0 else production.qty_produced,
+ 'product_qty': production.qty_produced,
'priority': '0',
'is_locked': True,
'state': 'done',
@@ -683,3 +789,317 @@ class MrpProduction(models.Model):
'view_mode': 'tree,form',
})
return action
+
+ # 返工
+ def button_rework(self):
+ cloud_programming = None
+ if self.programming_state in ['已编程']:
+ cloud_programming = self._cron_get_programming_state()
+ logging.info('cloud_programming_state:%s' % cloud_programming['programming_state'])
+ logging.info('programming_state:%s' % self.programming_state)
+ return {
+ 'name': _('返工'),
+ 'type': 'ir.actions.act_window',
+ 'view_mode': 'form',
+ 'res_model': 'sf.rework.wizard',
+ 'target': 'new',
+ 'context': {
+ 'default_production_id': self.id,
+ 'default_reprogramming_num': cloud_programming['reprogramming_num'],
+ 'default_programming_state': cloud_programming['programming_state'],
+ 'default_is_reprogramming': True if cloud_programming['programming_state'] in ['已下发'] else False
+ }
+ }
+
+ # 更新程序
+ def do_update_program(self):
+ program_production = self
+ if len(program_production) >= 1:
+ same_product_id = None
+ is_not_same_product = 0
+ for item in program_production:
+ if same_product_id is None:
+ same_product_id = item.product_id
+ if item.product_id != same_product_id:
+ is_not_same_product += 1
+ if item.state != "rework" and item.programming_state != "已编程未下发":
+ raise UserError("请选择状态为返工且已编程未下发的制造订单")
+ if is_not_same_product >= 1:
+ raise UserError("您选择的记录中含有其他产品的制造订单,请选择同一产品的制造订单")
+ grouped_program_ids = {k: list(g) for k, g in groupby(program_production, key=lambda x: x.programming_no)}
+ program_to_production_names = {}
+ for programming_no, program_production in grouped_program_ids.items():
+ program_to_production_names[programming_no] = [production.name for production in program_production]
+ for production in self:
+ if production.programming_no in program_to_production_names:
+ productions_not_delivered = self.env['mrp.production'].search(
+ [('programming_no', '=', production.programming_no), ('programming_state', '=', '已编程未下发')])
+ rework_workorder = production.workorder_ids.filtered(lambda m: m.state == 'rework')
+ if rework_workorder:
+ for rework_item in rework_workorder:
+ pending_workorder = production.workorder_ids.filtered(
+ lambda m1: m1.state in [
+ 'pending'] and m1.processing_panel == rework_item.processing_panel and m1.routing_type == 'CNC加工')
+ if not pending_workorder.cnc_ids:
+ production.get_new_program(rework_item.processing_panel)
+ # production.write({'state': 'progress', 'programming_state': '已编程', 'is_rework': False})
+ productions_not_delivered.write(
+ {'state': 'progress', 'programming_state': '已编程', 'is_rework': False})
+
+ # 从cloud获取重新编程过的最新程序
+ def get_new_program(self, processing_panel):
+ try:
+ res = {'programming_no': self.programming_no, 'processing_panel': processing_panel}
+ configsettings = self.env['res.config.settings'].get_values()
+ config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
+ url = '/api/intelligent_programming/get_new_program'
+ config_url = configsettings['sf_url'] + url
+ r = requests.post(config_url, json=res, data=None, headers=config_header)
+ r = r.json()
+ result = json.loads(r['result'])
+ if result['status'] == 1:
+ program_path_tmp_panel = os.path.join('/tmp', result['folder_name'], 'return', processing_panel)
+ if os.path.exists(program_path_tmp_panel):
+ files_r = os.listdir(program_path_tmp_panel)
+ if files_r:
+ for file_name in files_r:
+ file_path = os.path.join(program_path_tmp_panel, file_name)
+ os.remove(file_path)
+ download_state = self.env['sf.cnc.processing'].download_file_tmp(result['folder_name'],
+ processing_panel)
+ if download_state is False:
+ raise UserError('编程单号为%s的CNC程序文件从FTP拉取失败' % (self.programming_no))
+ productions = self.env['mrp.production'].search(
+ [('programming_no', '=', self.programming_no), ('state', 'not in', ('cancel', 'done'))])
+ if productions:
+ for production in productions:
+ panel_workorder = production.workorder_ids.filtered(lambda
+ pw: pw.processing_panel == processing_panel and pw.routing_type == 'CNC加工' and pw.state not in (
+ 'rework', 'done'))
+ if panel_workorder:
+ if panel_workorder.cmm_ids:
+ panel_workorder.cmm_ids.sudo().unlink()
+ if panel_workorder.cnc_ids:
+ panel_workorder.cnc_ids.sudo().unlink()
+ self.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan(
+ production)
+ # program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test',
+ # processing_panel)
+ logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel)
+ files_panel = os.listdir(program_path_tmp_panel)
+ if files_panel:
+ for file in files_panel:
+ file_extension = os.path.splitext(file)[1]
+ if file_extension.lower() == '.pdf':
+ panel_file_path = os.path.join(program_path_tmp_panel, file)
+ logging.info('panel_file_path:%s' % panel_file_path)
+ panel_workorder.write(
+ {'cnc_ids': panel_workorder.cnc_ids.sudo()._json_cnc_processing(processing_panel,
+ result),
+ 'cmm_ids': panel_workorder.cmm_ids.sudo()._json_cmm_program(processing_panel, result),
+ 'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
+ logging.info('len(cnc_worksheet):%s' % len(panel_workorder.cnc_worksheet))
+ pre_workorder = production.workorder_ids.filtered(lambda
+ ap: ap.routing_type == '装夹预调' and ap.processing_panel == processing_panel and ap.state not in (
+ 'rework', 'done'))
+ if pre_workorder:
+ pre_workorder.write(
+ {'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
+ # if production.state == 'rework' and production.programming_state == '已编程未下发':
+ # production.write(
+ # {'state': 'progress', 'programming_state': '已编程', 'is_rework': False})
+ # logging.info('返工含有已编程未下发的程序更新完成:%s' % production.name)
+ logging.info('更新程序完成:%s' % production.name)
+
+ else:
+ raise UserError(result['message'])
+ except Exception as e:
+ logging.info('get_new_program error:%s' % e)
+ raise UserError("从云平台获取最新程序失败,请联系管理员")
+
+ def recreateManufacturing(self):
+ """
+ 重新生成制造订单
+ """
+ if self.is_scrap is True:
+ sale_order = self.env['sale.order'].sudo().search([('name', '=', productions.origin)])
+ values = self.env['mrp.production'].create_production1_values(self.production_id)
+ productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(
+ self.production_id.company_id).create(
+ values)
+ # self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
+ self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
+ productions._create_workorder()
+ productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \
+ (
+ p.move_dest_ids.procure_method != 'make_to_order' and
+ not p.move_raw_ids and not p.workorder_ids)).action_confirm()
+ for production_item in productions:
+ process_parameter_workorder = self.env['mrp.workorder'].search(
+ [('surface_technics_parameters_id', '!=', False), ('production_id', '=', production_item.id),
+ ('is_subcontract', '=', True)])
+ if process_parameter_workorder:
+ is_pick = False
+ consecutive_workorders = []
+ m = 0
+ sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.id)
+ for i in range(len(sorted_workorders) - 1):
+ if m == 0:
+ is_pick = False
+ if sorted_workorders[i].supplier_id.id == sorted_workorders[i + 1].supplier_id.id and \
+ sorted_workorders[i].is_subcontract == sorted_workorders[i + 1].is_subcontract and \
+ sorted_workorders[i].id == sorted_workorders[i + 1].id - 1:
+ if sorted_workorders[i] not in consecutive_workorders:
+ consecutive_workorders.append(sorted_workorders[i])
+ consecutive_workorders.append(sorted_workorders[i + 1])
+ m += 1
+ continue
+ else:
+ if m == len(consecutive_workorders) - 1 and m != 0:
+ self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
+ production_item)
+ if sorted_workorders[i] in consecutive_workorders:
+ is_pick = True
+ consecutive_workorders = []
+ m = 0
+ # 当前面的连续工序生成对应的外协出入库单再生成当前工序的外协出入库单
+ if is_pick is False:
+ self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i],
+ production_item)
+ if m == len(consecutive_workorders) - 1 and m != 0:
+ self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
+ production_item)
+ if sorted_workorders[i] in consecutive_workorders:
+ is_pick = True
+ consecutive_workorders = []
+ m = 0
+ if m == len(consecutive_workorders) - 1 and m != 0:
+ self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production_item)
+ if is_pick is False and m == 0:
+ if len(sorted_workorders) == 1:
+ self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production_item)
+ else:
+ self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production_item)
+
+ for production in productions:
+ origin_production = production.move_dest_ids and production.move_dest_ids[
+ 0].raw_material_production_id or False
+ orderpoint = production.orderpoint_id
+ if orderpoint and orderpoint.create_uid.id == SUPERUSER_ID and orderpoint.trigger == 'manual':
+ production.message_post(
+ body=_('This production order has been created from Replenishment Report.'),
+ message_type='comment',
+ subtype_xmlid='mail.mt_note')
+ elif orderpoint:
+ production.message_post_with_view(
+ 'mail.message_origin_link',
+ values={'self': production, 'origin': orderpoint},
+ subtype_id=self.env.ref('mail.mt_note').id)
+ elif origin_production:
+ production.message_post_with_view(
+ 'mail.message_origin_link',
+ values={'self': production, 'origin': origin_production},
+ subtype_id=self.env.ref('mail.mt_note').id)
+
+ '''
+ 创建生产计划
+ '''
+ # 工单耗时
+ workorder_duration = 0
+ for workorder in productions.workorder_ids:
+ workorder_duration += workorder.duration_expected
+
+ if sale_order:
+ sale_order.mrp_production_ids |= productions
+ # sale_order.write({'schedule_status': 'to schedule'})
+ self.env['sf.production.plan'].sudo().with_company(self.production_id.company_id).create({
+ 'name': productions.name,
+ 'order_deadline': sale_order.deadline_of_delivery,
+ 'production_id': productions.id,
+ 'date_planned_start': productions.date_planned_start,
+ 'origin': productions.origin,
+ 'product_qty': productions.product_qty,
+ 'product_id': productions.product_id.id,
+ 'state': 'draft',
+ })
+
+ # 在之前的销售单上重新生成制造订单
+ def create_production1_values(self, production, sale_order):
+ production_values_str = {'origin': production.origin,
+ 'product_id': production.product_id.id,
+ 'product_description_variants': production.product_description_variants,
+ 'product_qty': production.product_qty,
+ 'product_uom_id': production.product_uom_id.id,
+ 'location_src_id': production.location_src_id.id,
+ 'location_dest_id': production.location_dest_id.id,
+ 'bom_id': production.bom_id.id,
+ 'date_deadline': production.date_deadline,
+ 'date_planned_start': production.date_planned_start,
+ 'date_planned_finished': production.date_planned_finished,
+ 'procurement_group_id': sale_order.id,
+ 'propagate_cancel': production.propagate_cancel,
+ 'orderpoint_id': production.orderpoint_id.id,
+ 'picking_type_id': production.picking_type_id.id,
+ 'company_id': production.company_id.id,
+ 'move_dest_ids': production.move_dest_ids.ids,
+ 'user_id': production.user_id.id}
+ return production_values_str
+
+
+class sf_detection_result(models.Model):
+ _name = 'sf.detection.result'
+ _description = "检测结果"
+
+ production_id = fields.Many2one('mrp.production')
+ processing_panel = fields.Char('加工面')
+ routing_type = fields.Selection([
+ ('装夹预调', '装夹预调'),
+ ('CNC加工', 'CNC加工')], string="工序类型")
+
+ rework_reason = fields.Selection(
+ [("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"),
+ ("operate computer", "操机"),
+ ("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因", tracking=True)
+ detailed_reason = fields.Text('详细原因')
+ test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")],
+ string="检测结果", tracking=True)
+ test_report = fields.Binary('检测报告', readonly=True)
+ handle_result = fields.Selection([("待处理", "待处理"), ("已处理", "已处理")], default='', string="处理结果",
+ tracking=True)
+
+ # 查看检测报告
+ def button_look_test_report(self):
+ return {
+ 'res_model': 'sf.detection.result',
+ 'type': 'ir.actions.act_window',
+ 'res_id': self.id,
+ 'views': [(self.env.ref('sf_manufacturing.sf_test_report_form').id, 'form')],
+ # 'view_mode': 'form',
+ # 'context': {
+ # 'default_id': self.id
+ # },
+ 'target': 'new'
+ }
+
+
+class sf_processing_panel(models.Model):
+ _name = 'sf.processing.panel'
+ _description = "加工面"
+
+ name = fields.Char('加工面')
+ active = fields.Boolean('有效', default=True)
+
+ # @api.model
+ # def name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
+ # if self.env.user.has_group('sf_base.group_sf_order_user'):
+ # if self._context.get('product_id'):
+ # product = self.env['product.product'].search([('id', '=', self._context.get('product_id'))])
+ # if product:
+ # panel = self.env['sf.processing.panel'].search([('name', 'ilike', 'ZM')])
+ # if panel:
+ # ids = [t.id for t in panel]
+ # domain = [('id', 'in', ids)]
+ # else:
+ # domain = [('id', '=', False)]
+ # return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
+ # return super()._name_search(name, args, operator, limit, name_get_uid)
diff --git a/sf_manufacturing/models/mrp_workcenter.py b/sf_manufacturing/models/mrp_workcenter.py
index 7d70ae5e..64cf2d8d 100644
--- a/sf_manufacturing/models/mrp_workcenter.py
+++ b/sf_manufacturing/models/mrp_workcenter.py
@@ -5,24 +5,49 @@ from odoo.addons.resource.models.resource import Intervals
class ResWorkcenter(models.Model):
- _inherit = "mrp.workcenter"
+ _name = "mrp.workcenter"
+ _inherit = ['mrp.workcenter', 'mail.thread']
# 生产线显示
production_line_show = fields.Char(string='生产线名称')
- equipment_id = fields.Many2one(
- 'maintenance.equipment', string="设备",
- )
+ equipment_id = fields.Many2one('maintenance.equipment', string="设备", tracking=True)
production_line_id = fields.Many2one('sf.production.line', string='生产线',
related='equipment_id.production_line_id', store=True)
-
is_process_outsourcing = fields.Boolean('工艺外协')
- users_ids = fields.Many2many("res.users", 'users_workcenter')
+ users_ids = fields.Many2many("res.users", 'users_workcenter', tracking=True)
+ def write(self, vals):
+ if 'users_ids' in vals:
+ old_users = self.users_ids
+ res = super(ResWorkcenter, self).write(vals)
+ new_users = self.users_ids
+ added_users = new_users - old_users
+ removed_users = old_users - new_users
+ if added_users or removed_users:
+ message = "增加 → %s ; 移除 → %s (可操作用户)" % (
+ # ','.join(added_users.mapped('name')), ','.join(removed_users.mapped('name')))
+ added_users.mapped('name'), removed_users.mapped('name'))
+ self.message_post(body=message)
+ return res
+ return super(ResWorkcenter, self).write(vals)
+ name = fields.Char('Work Center', related='resource_id.name', store=True, readonly=False, tracking=True)
+ time_efficiency = fields.Float('Time Efficiency', related='resource_id.time_efficiency', default=100, store=True,
+ readonly=False, tracking=True)
+ default_capacity = fields.Float(
+ 'Capacity', default=1.0,
+ help="Default number of pieces (in product UoM) that can be produced in parallel (at the same time) at this work center. For example: the capacity is 5 and you need to produce 10 units, then the operation time listed on the BOM will be multiplied by two. However, note that both time before and after production will only be counted once.",
+ tracking=True)
+ oee_target = fields.Float(
+ string='OEE Target', help="Overall Effective Efficiency Target in percentage", default=90, tracking=True)
+
+ time_start = fields.Float('Setup Time', tracking=True)
+ time_stop = fields.Float('Cleanup Time', tracking=True)
+ costs_hour = fields.Float(string='Cost per hour', help='Hourly processing cost.', default=0.0, tracking=True)
equipment_status = fields.Selection(
- [("正常", "正常"), ("故障停机", "故障停机"), ("计划维保", "计划维保"),("空闲", "空闲"),("封存(报废)", "封存(报废)")],
+ [("正常", "正常"), ("故障停机", "故障停机"), ("计划维保", "计划维保"), ("空闲", "空闲"), ("封存(报废)", "封存(报废)")],
string="设备状态", related='equipment_id.state')
# @api.depends('equipment_id')
diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py
index 46bcde47..44c019eb 100644
--- a/sf_manufacturing/models/mrp_workorder.py
+++ b/sf_manufacturing/models/mrp_workorder.py
@@ -13,13 +13,13 @@ from dateutil.relativedelta import relativedelta
# import subprocess
from odoo import api, fields, models, SUPERUSER_ID, _
from odoo.addons.sf_base.commons.common import Common
-from odoo.exceptions import UserError
+from odoo.exceptions import UserError, ValidationError
from odoo.addons.sf_mrs_connect.models.ftp_operate import FtpController
class ResMrpWorkOrder(models.Model):
_inherit = 'mrp.workorder'
- _order = 'id'
+ _order = 'sequence asc'
product_tmpl_name = fields.Char('坯料产品名称', related='production_bom_id.bom_line_ids.product_id.name')
@@ -47,9 +47,28 @@ class ResMrpWorkOrder(models.Model):
('切割', '切割'), ('表面工艺', '表面工艺')
], string="工序类型")
results = fields.Char('结果')
+ state = fields.Selection([
+ ('pending', '等待其他工单'),
+ ('waiting', '等待组件'),
+ ('ready', '就绪'),
+ ('progress', '进行中'),
+ ('to be detected', "待检测"),
+ ('done', '已完工'),
+ ('rework', '返工'),
+ ('cancel', '取消')], string='Status',
+ compute='_compute_state', store=True,
+ default='pending', copy=False, readonly=True, recursive=True, index=True, tracking=True)
+ # state = fields.Selection(selection_add=[('to be detected', "待检测"), ('rework', '返工')], tracking=True)
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
+ def _compute_working_users(self):
+ super()._compute_working_users()
+ for item in self:
+ if item.state == 'to be detected':
+ if self.env.user.has_group('sf_base.group_sf_equipment_user'):
+ item.is_user_working = True
+
@api.onchange('users_ids')
def get_user_permissions(self):
uid = self.env.uid
@@ -158,13 +177,46 @@ class ResMrpWorkOrder(models.Model):
# 加工图纸
processing_drawing = fields.Binary(string='加工图纸')
+ # 功能刀具状态
+ tool_state = fields.Selection([('0', '正常'), ('1', '缺刀'), ('2', '无效刀')], string='功能刀具状态', default='0',
+ store=True, compute='_compute_tool_state')
+ tool_state_remark = fields.Text(string='功能刀具状态备注(缺刀)', compute='_compute_tool_state_remark', store=True)
+
+ @api.depends('cnc_ids.tool_state')
+ def _compute_tool_state_remark(self):
+ for item in self:
+ if item.cnc_ids:
+ if item.cnc_ids.filtered(lambda a: a.tool_state == '2'):
+ item.tool_state_remark = None
+ elif item.cnc_ids.filtered(lambda a: a.tool_state == '1'):
+ tool_state_remark = []
+ cnc_ids = item.cnc_ids.filtered(lambda a: a.tool_state == '1')
+ for cnc_id in cnc_ids:
+ if cnc_id.cutting_tool_name not in tool_state_remark:
+ tool_state_remark.append(cnc_id.cutting_tool_name)
+ item.tool_state_remark = f"{item.processing_panel}缺刀:{tool_state_remark}]"
+ else:
+ item.tool_state_remark = None
+
+ @api.depends('cnc_ids.tool_state')
+ def _compute_tool_state(self):
+ for item in self:
+ if item.cnc_ids:
+ if item.cnc_ids.filtered(lambda a: a.tool_state == '2'):
+ item.tool_state = '2'
+ elif item.cnc_ids.filtered(lambda a: a.tool_state == '1'):
+ item.tool_state = '1'
+ else:
+ item.tool_state = '0'
+
@api.depends('production_id')
def _compute_save_name(self):
"""
保存名称
"""
for record in self:
- record.save_name = record.production_id.name.replace('/', '_')
+ tem_name = record.production_id.name.replace('/', '_')
+ record.save_name = tem_name + '_' + record.processing_panel
schedule_state = fields.Selection(related='production_id.schedule_state', store=True)
# 工件装夹信息
@@ -196,15 +248,75 @@ class ResMrpWorkOrder(models.Model):
production_line_state = fields.Selection(
[('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')],
string='上/下产线', default='待上产线', tracking=True)
- detection_report = fields.Binary('检测报告', readonly=True)
+ detection_report = fields.Binary('检测报告', readonly=False)
is_remanufacture = fields.Boolean(string='重新生成制造订单', default=False)
is_fetchcnc = fields.Boolean(string='重新获取NC程序', default=False)
reason = fields.Selection(
- [("programming", "编程"), ("clamping", "返工"), ("cutter", "刀具"), ("operate computer", "操机"),
- ("technology", "工艺"), ("customer redrawing", "客户改图"), ("other", "其他"), ], string="原因", tracking=True)
+ [("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"), ("operate computer", "操机"),
+ ("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因", tracking=True)
detailed_reason = fields.Text('详细原因')
+ is_rework = fields.Boolean(string='是否返工', default=False)
- # is_send_program_again = fields.Boolean(string='是否重新下发NC程序', default=False)
+ @api.constrains('blocked_by_workorder_ids')
+ def _check_no_cyclic_dependencies(self):
+ if self.production_id.state not in ['rework'] and self.state not in ['rework']:
+ if not self._check_m2m_recursion('blocked_by_workorder_ids'):
+ raise ValidationError(_("您不能创建周期性的依赖关系."))
+
+ def _plan_workorder(self, replan=False):
+ self.ensure_one()
+ # Plan workorder after its predecessors
+ start_date = max(self.production_id.date_planned_start, datetime.now())
+ for workorder in self.blocked_by_workorder_ids:
+ if workorder.state in ['done', 'cancel', 'rework']:
+ continue
+ workorder._plan_workorder(replan)
+ start_date = max(start_date, workorder.date_planned_finished)
+ # Plan only suitable workorders
+ if self.state not in ['pending', 'waiting', 'ready']:
+ return
+ if self.leave_id:
+ if replan:
+ self.leave_id.unlink()
+ else:
+ return
+ # Consider workcenter and alternatives
+ workcenters = self.workcenter_id | self.workcenter_id.alternative_workcenter_ids
+ best_finished_date = datetime.max
+ vals = {}
+ for workcenter in workcenters:
+ # Compute theoretical duration
+ if self.workcenter_id == workcenter:
+ duration_expected = self.duration_expected
+ else:
+ duration_expected = self._get_duration_expected(alternative_workcenter=workcenter)
+ from_date, to_date = workcenter._get_first_available_slot(start_date, duration_expected)
+ # If the workcenter is unavailable, try planning on the next one
+ if not from_date:
+ continue
+ # Check if this workcenter is better than the previous ones
+ if to_date and to_date < best_finished_date:
+ best_start_date = from_date
+ best_finished_date = to_date
+ best_workcenter = workcenter
+ vals = {
+ 'workcenter_id': workcenter.id,
+ 'duration_expected': duration_expected,
+ }
+ # If none of the workcenter are available, raise
+ if best_finished_date == datetime.max:
+ raise UserError(_('Impossible to plan the workorder. Please check the workcenter availabilities.'))
+ # Create leave on chosen workcenter calendar
+ leave = self.env['resource.calendar.leaves'].create({
+ 'name': self.display_name,
+ 'calendar_id': best_workcenter.resource_calendar_id.id,
+ 'date_from': best_start_date,
+ 'date_to': best_finished_date,
+ 'resource_id': best_workcenter.resource_id.id,
+ 'time_type': 'other'
+ })
+ vals['leave_id'] = leave.id
+ self.write(vals)
@api.onchange('rfid_code')
def _onchange(self):
@@ -219,7 +331,7 @@ class ResMrpWorkOrder(models.Model):
sql = """
SELECT *
FROM mrp_workorder
- WHERE
+ WHERE state!='rework'
to_char(date_planned_start::timestamp + '8 hour','YYYY-MM-DD HH:mm:SS')>= %s
AND to_char(date_planned_finished::timestamp + '8 hour','YYYY-MM-DD HH:mm:SS')<= %s
"""
@@ -430,6 +542,7 @@ class ResMrpWorkOrder(models.Model):
work.compensation_value_y = eval(self.material_center_point)[1]
# work.process_state = '待加工'
# self.sudo().production_id.process_state = '待加工'
+ # self.sudo().production_id.process_state = '待加工'
self.date_finished = datetime.now()
workorder.button_finish()
@@ -460,6 +573,20 @@ class ResMrpWorkOrder(models.Model):
else:
raise UserError(_("该工单暂未完成,无法进行工件配送"))
+ def button_rework_pre(self):
+ return {
+ 'name': _('返工'),
+ 'type': 'ir.actions.act_window',
+ 'view_mode': 'form',
+ 'res_model': 'sf.rework.wizard',
+ 'target': 'new',
+ 'context': {
+ 'default_workorder_id': self.id,
+ 'default_production_id': self.production_id.id,
+ # 'default_programming_state': self.production_id.programming_state,
+ 'default_routing_type': self.routing_type
+ }}
+
# 拼接工单对象属性值
def json_workorder_str(self, k, production, route, item):
# 计算预计时长duration_expected
@@ -516,12 +643,16 @@ class ResMrpWorkOrder(models.Model):
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}]]
+ [0, '',
+ {'production_id': production.id, 'production_line_id': production.production_line_id.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, 'production_line_id': production.production_line_id.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):
@@ -667,119 +798,6 @@ class ResMrpWorkOrder(models.Model):
# 'target':'new'
# }
- def recreateManufacturingOrWorkerOrder(self):
- """
- 重新生成制造订单或者重新生成工单
- """
- if self.test_results in ['返工', '报废']:
- values = self.env['mrp.production'].create_production1_values(self.production_id)
- productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(
- self.production_id.company_id).create(
- values)
- # self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
- self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
- productions._create_workorder()
- productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \
- (
- p.move_dest_ids.procure_method != 'make_to_order' and
- not p.move_raw_ids and not p.workorder_ids)).action_confirm()
- for production_item in productions:
- process_parameter_workorder = self.env['mrp.workorder'].search(
- [('surface_technics_parameters_id', '!=', False), ('production_id', '=', production_item.id),
- ('is_subcontract', '=', True)])
- if process_parameter_workorder:
- is_pick = False
- consecutive_workorders = []
- m = 0
- sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.id)
- for i in range(len(sorted_workorders) - 1):
- if m == 0:
- is_pick = False
- if sorted_workorders[i].supplier_id.id == sorted_workorders[i + 1].supplier_id.id and \
- sorted_workorders[i].is_subcontract == sorted_workorders[i + 1].is_subcontract and \
- sorted_workorders[i].id == sorted_workorders[i + 1].id - 1:
- if sorted_workorders[i] not in consecutive_workorders:
- consecutive_workorders.append(sorted_workorders[i])
- consecutive_workorders.append(sorted_workorders[i + 1])
- m += 1
- continue
- else:
- if m == len(consecutive_workorders) - 1 and m != 0:
- self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
- production_item)
- if sorted_workorders[i] in consecutive_workorders:
- is_pick = True
- consecutive_workorders = []
- m = 0
- # 当前面的连续工序生成对应的外协出入库单再生成当前工序的外协出入库单
- if is_pick is False:
- self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i],
- production_item)
- if m == len(consecutive_workorders) - 1 and m != 0:
- self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
- production_item)
- if sorted_workorders[i] in consecutive_workorders:
- is_pick = True
- consecutive_workorders = []
- m = 0
- if m == len(consecutive_workorders) - 1 and m != 0:
- self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production_item)
- if is_pick is False and m == 0:
- if len(sorted_workorders) == 1:
- self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production_item)
- else:
- self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production_item)
-
- for production in productions:
- origin_production = production.move_dest_ids and production.move_dest_ids[
- 0].raw_material_production_id or False
- orderpoint = production.orderpoint_id
- if orderpoint and orderpoint.create_uid.id == SUPERUSER_ID and orderpoint.trigger == 'manual':
- production.message_post(
- body=_('This production order has been created from Replenishment Report.'),
- message_type='comment',
- subtype_xmlid='mail.mt_note')
- elif orderpoint:
- production.message_post_with_view(
- 'mail.message_origin_link',
- values={'self': production, 'origin': orderpoint},
- subtype_id=self.env.ref('mail.mt_note').id)
- elif origin_production:
- production.message_post_with_view(
- 'mail.message_origin_link',
- values={'self': production, 'origin': origin_production},
- subtype_id=self.env.ref('mail.mt_note').id)
-
- '''
- 创建生产计划
- '''
- # 工单耗时
- workorder_duration = 0
- for workorder in productions.workorder_ids:
- workorder_duration += workorder.duration_expected
-
- sale_order = self.env['sale.order'].sudo().search([('name', '=', productions.origin)])
- if sale_order:
- sale_order.mrp_production_ids |= productions
- # sale_order.write({'schedule_status': 'to schedule'})
- self.env['sf.production.plan'].sudo().with_company(self.production_id.company_id).create({
- 'name': productions.name,
- 'order_deadline': sale_order.deadline_of_delivery,
- 'production_id': productions.id,
- 'date_planned_start': productions.date_planned_start,
- 'origin': productions.origin,
- 'product_qty': productions.product_qty,
- 'product_id': productions.product_id.id,
- 'state': 'draft',
- })
- # if self.test_results == '返工':
- # productions = self.production_id
- # # self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
- # # self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
- # productions._create_workorder2(self.processing_panel)
- # else:
- # self.results = '合格'
-
def json_workorder_str1(self, k, production, route):
workorders_values_str = [0, '', {
'product_uom_id': production.product_uom_id.id,
@@ -803,28 +821,162 @@ class ResMrpWorkOrder(models.Model):
}]
return workorders_values_str
- # @api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state')
- # def _compute_state(self):
- # super(ResMrpWorkOrder, self)._compute_state()
- # for item in self:
- # print(item.name)
- # print(item.state)
- # print(item.is_remanufacture)
- # scrap_workorder = self.env['mrp.workorder'].search(
- # [('production_id', '=', item.production_id.id), ('routing_type', '=', 'CNC加工'),
- # ('state', '=', 'done'), ('test_results', 'in', ['返工', '报废'])])
- # print(scrap_workorder)
- # # if item.routing_type == 'CNC加工' and item.state in ['done'] and item.test_results in ['返工', '报废']:
- # if item.routing_type == '解除装夹':
- # if scrap_workorder and item.state not in ['cancel']:
- # item.state = 'cancel'
- # elif item.routing_type == '表面工艺':
- # if scrap_workorder:
- # stock_move = self.env['stock.move'].search(
- # [('origin', '=', item.production_id.name)])
- # stock_move.write({'state': 'cancel'})
- # item.picking_ids.write({'state': 'cancel'})
- # item.state = 'cancel'
+ @api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state',
+ 'production_id.tool_state')
+ def _compute_state(self):
+ super()._compute_state()
+ for workorder in self:
+ re_work = self.env['mrp.workorder'].search([('production_id', '=', workorder.production_id.id),
+ ('processing_panel', '=', workorder.processing_panel),
+ ('is_rework', '=', True), ('state', 'in', ['done', 'rework'])])
+ cnc_workorder = self.env['mrp.workorder'].search(
+ [('production_id', '=', workorder.production_id.id),
+ ('processing_panel', '=', workorder.processing_panel),
+ ('routing_type', '=', 'CNC加工'), ('state', 'in', ['done', 'rework']),
+ ('test_results', '=', '返工')])
+ cnc_workorder_pending = self.env['mrp.workorder'].search(
+ [('production_id', '=', workorder.production_id.id),
+ ('processing_panel', '=', workorder.processing_panel),
+ ('routing_type', '=', 'CNC加工'), ('state', 'in', ['pending'])])
+ unclamp_workorder = self.env['mrp.workorder'].search(
+ [('production_id', '=', workorder.production_id.id),
+ ('sequence', '=', workorder.sequence - 1),
+ ('state', 'in', ['done'])])
+ if workorder.state not in ['cancel', 'progress', 'rework']:
+ if workorder.production_id.state == 'rework':
+ logging.info('len(re_work):%s' % len(re_work))
+ logging.info('len(cnc_workorder):%s' % len(cnc_workorder))
+ logging.info('len(cnc_workorder_pending):%s' % len(cnc_workorder_pending))
+ logging.info('工序:%s' % workorder.routing_type)
+ logging.info('状态:%s' % workorder.state)
+ logging.info('is_rework:%s' % workorder.is_rework)
+ logging.info('production_id.is_rework:%s' % workorder.production_id.is_rework)
+ logging.info('面:%s' % workorder.processing_panel)
+ logging.info('编程状态:%s' % workorder.production_id.programming_state)
+ logging.info('制造状态:%s' % workorder.production_id.state)
+ if workorder.routing_type == '装夹预调' and workorder.state not in ['done', 'rework',
+ 'cancel']:
+ # # 有返工工单
+ # if re_work:
+ # 新工单
+ if workorder.is_rework is False:
+ if workorder.production_id.programming_state == '已编程' and workorder.production_id.is_rework is False:
+ if re_work or cnc_workorder:
+ workorder.state = 'ready'
+ else:
+ if workorder.production_id.is_rework is True:
+ if re_work or cnc_workorder:
+ workorder.state = 'waiting'
+
+ elif workorder.routing_type == 'CNC加工' and workorder.state not in ['done', 'rework', 'cancel']:
+ pre_workorder = self.env['mrp.workorder'].search(
+ [('production_id', '=', workorder.production_id.id),
+ ('processing_panel', '=', workorder.processing_panel),
+ ('routing_type', '=', '装夹预调'), ('state', '=', 'done')])
+ if pre_workorder:
+ if re_work:
+ workorder.state = 'waiting'
+ elif workorder.routing_type == '解除装夹' and workorder.state not in ['done', 'rework', 'cancel']:
+ if cnc_workorder:
+ if not cnc_workorder_pending:
+ workorder.state = 'waiting'
+ # else:
+ # if workorder.production_id.is_rework is True:
+ # workorder.state = 'waiting'
+ elif workorder.production_id.state == 'progress':
+ logging.info('len(re_work):%s' % len(re_work))
+ logging.info('len(cnc_workorder):%s' % len(cnc_workorder))
+ logging.info('len(cnc_workorder_pending):%s' % len(cnc_workorder_pending))
+ logging.info('工序:%s' % workorder.routing_type)
+ logging.info('状态:%s' % workorder.state)
+ logging.info('is_rework:%s' % workorder.is_rework)
+ logging.info('production_id.is_rework:%s' % workorder.production_id.is_rework)
+ logging.info('面:%s' % workorder.processing_panel)
+ logging.info('编程状态:%s' % workorder.production_id.programming_state)
+ logging.info('制造状态:%s' % workorder.production_id.state)
+ if workorder.routing_type == '装夹预调' and workorder.production_id.programming_state == '已编程' and \
+ workorder.is_rework is False and workorder.state not in [
+ 'done', 'rework',
+ 'cancel']:
+ if workorder.production_id.is_rework is False:
+ if re_work or cnc_workorder or unclamp_workorder:
+ workorder.state = 'ready'
+ # if (re_work or cnc_workorder) and workorder.production_id.is_rework is False:
+ # workorder.state = 'ready'
+ if workorder.routing_type == '表面工艺' and workorder.state not in ['done', 'progress']:
+ if unclamp_workorder:
+ workorder.state = 'ready'
+ # else:
+ # if workorder.state not in ['cancel', 'rework']:
+ # workorder.state = 'rework'
+ if workorder.routing_type == '装夹预调' and workorder.state in ['waiting', 'ready', 'pending']:
+ # 当工单对应制造订单的功能刀具状态为 【无效刀】时,先对的第一个装夹预调工单状态设置为 【等待组件】
+ if workorder.production_id.tool_state in ['1', '2']:
+ if workorder.state in ['ready']:
+ workorder.state = 'waiting'
+ continue
+ elif workorder.state in ['waiting']:
+ continue
+ elif workorder.state == 'pending' and workorder == self.search(
+ [('production_id', '=', workorder.production_id.id),
+ ('routing_type', '=', '装夹预调'),
+ ('state', 'not in', ['rework', 'done', 'cancel'])],
+ limit=1,
+ order="sequence"):
+ workorder.state = 'waiting'
+ continue
+ elif workorder.production_id.tool_state in ['0']:
+ if workorder.production_id.workorder_ids.filtered(lambda a: a.state == 'rework'):
+ if not workorder.production_id.workorder_ids.filtered(
+ lambda a: (a.routing_type not in ['装夹预调'] and
+ a.state not in ['pending', 'done', 'rework', 'cancel'])):
+ # 查询工序最小的非完工、非返工的装夹预调工单
+ work_id = self.search(
+ [('production_id', '=', workorder.production_id.id),
+ ('routing_type', '=', '装夹预调'),
+ ('state', 'not in', ['rework', 'done', 'cancel'])],
+ limit=1,
+ order="sequence")
+ if workorder == work_id:
+ if workorder.production_id.reservation_state == 'assigned':
+ workorder.state = 'ready'
+ elif workorder.production_id.reservation_state != 'assigned':
+ workorder.state = 'waiting'
+ continue
+
+ logging.info('工序:%s' % workorder.sequence)
+ logging.info('工单最终状态:%s' % workorder.state)
+
+ # elif workorder.routing_type == 'CNC加工' and workorder.state not in ['done', 'cancel', 'progress',
+ # 'rework']:
+ # per_work = self.env['mrp.workorder'].search(
+ # [('routing_type', '=', '装夹预调'), ('production_id', '=', workorder.production_id.id),
+ # ('processing_panel', '=', workorder.processing_panel), ('is_rework', '=', True)])
+ # if per_work:
+ # workorder.state = 'waiting'
+ # if workorder.routing_type == 'CNC加工' and workorder.state == 'progress':
+ # workorder.state = 'to be detected'
+
+ # for workorder in self:
+ # if workorder.is_rework is True and workorder.state == 'done':
+ # cnc_work = self.env['mrp.workorder'].search([('routing_type','=','CNC加工'),('production_id','=',workorder.production_id.id)])
+ # if cnc_work:
+ # cnc_work.state = 'waiting'
+ # if workorder.state == 'pending':
+ # if all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]):
+ # workorder.state = 'ready' if workorder.production_id.reservation_state == 'assigned' else 'waiting'
+ # continue
+ # if workorder.state not in ('waiting', 'ready'):
+ # continue
+ # if not all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]):
+ # workorder.state = 'pending'
+ # continue
+ # if workorder.production_id.reservation_state not in ('waiting', 'confirmed', 'assigned'):
+ # continue
+ # if workorder.production_id.reservation_state == 'assigned' and workorder.state == 'waiting':
+ # workorder.state = 'ready'
+ # elif workorder.production_id.reservation_state != 'assigned' and workorder.state == 'ready':
+ # workorder.state = 'waiting'
# 重写工单开始按钮方法
def button_start(self):
@@ -843,12 +995,14 @@ class ResMrpWorkOrder(models.Model):
limit=1, order='id asc')
if not cnc_workorder.cnc_ids:
raise UserError(_('该制造订单还未下发CNC程序,请稍后再试'))
- # else:
- # for item in cnc_workorder.cnc_ids:
- # functional_cutting_tool = self.env['sf.functional.cutting.tool.entity'].search(
- # [('tool_name_id.name', '=', item.cutting_tool_name)])
- # if not functional_cutting_tool:
- # raise UserError(_('该制造订单的CNC程序为%s没有对应的功能刀具' % item.cutting_tool_name))
+ else:
+ if self.production_id.tool_state in ['1', '2']:
+ if self.production_id.tool_state == '1':
+ state = '缺刀'
+ else:
+ state = '无效刀'
+ raise UserError(
+ f'制造订单【{self.production_id.name}】功能刀具状态为【{state}】!')
if self.routing_type == '解除装夹':
'''
记录开始时间
@@ -932,7 +1086,7 @@ class ResMrpWorkOrder(models.Model):
if record.routing_type == '装夹预调':
if not record.material_center_point and record.X_deviation_angle > 0:
raise UserError("请对前置三元检测定位参数进行计算定位")
- if not record.rfid_code:
+ if not record.rfid_code and record.is_rework is False:
raise UserError("请扫RFID码进行绑定")
record.process_state = '待加工'
# record.write({'process_state': '待加工'})
@@ -941,6 +1095,15 @@ class ResMrpWorkOrder(models.Model):
record.process_state = '待解除装夹'
# record.write({'process_state': '待加工'})
record.production_id.process_state = '待解除装夹'
+ record.production_id.write({'detection_result_ids': [(0, 0, {
+ 'rework_reason': record.reason,
+ 'detailed_reason': record.detailed_reason,
+ 'processing_panel': record.processing_panel,
+ 'routing_type': record.routing_type,
+ 'handle_result': '待处理' if record.test_results == '返工' or record.is_rework is True else '',
+ 'test_results': record.test_results,
+ 'test_report': record.detection_report})],
+ 'is_scrap': True if record.test_results == '报废' else False})
if record.routing_type == '解除装夹':
'''
记录结束时间
@@ -989,34 +1152,38 @@ class ResMrpWorkOrder(models.Model):
record.write({
'date_planned_finished': tem_date_planned_finished # 保持原值
})
- # if record.routing_type == 'CNC加工':
- # record.write({
- # 'date_finished': tem_date_finished # 保持原值
- # })
+
# if record.routing_type == 'CNC加工' and record.test_results in ['返工', '报废']:
# record.production_id.action_cancel()
# record.production_id.workorder_ids.write({'rfid_code': False, 'rfid_code_old': record.rfid_code})
# if record.is_remanufacture is True:
# record.recreateManufacturingOrWorkerOrder()
- is_production_id = True
- for workorder in record.production_id.workorder_ids:
- if workorder.state != 'done':
- is_production_id = False
- if record.routing_type == '解除装夹':
+ is_production_id = False
+ rework_workorder = record.production_id.workorder_ids.filtered(lambda p: p.state == 'rework')
+ done_workorder = record.production_id.workorder_ids.filtered(lambda p1: p1.state == 'done')
+ if (len(rework_workorder) + len(done_workorder) == len(record.production_id.workorder_ids)) or (
+ len(done_workorder) == len(record.production_id.workorder_ids)):
+ is_production_id = True
+ if record.routing_type in ['解除装夹'] or (
+ record.is_rework is True and record.routing_type in ['装夹预调']) or (
+ record.test_results in ['返工', '报废'] and record.routing_type in ['CNC加工']):
for workorder in record.production_id.workorder_ids:
if workorder.processing_panel == record.processing_panel:
rfid_code = workorder.rfid_code
workorder.write({'rfid_code_old': rfid_code,
'rfid_code': False})
- workorder.rfid_code_old = rfid_code
- workorder.rfid_code = False
+ if workorder.rfid_code:
+ raise ValidationError(f'【{workorder.name}】工单解绑失败,请重新点击完成按钮!!!')
+ # workorder.rfid_code_old = rfid_code
+ # workorder.rfid_code = False
+ logging.info('workorder.rfid_code:%s' % workorder.rfid_code)
if is_production_id is True and record.routing_type in ['解除装夹', '表面工艺']:
logging.info('product_qty:%s' % record.production_id.product_qty)
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 ['解除装夹', '表面工艺']:
+ if record.routing_type in ['表面工艺']:
raw_move = self.env['stock.move'].sudo().search(
[('origin', '=', record.production_id.name),
('procure_method', 'in', ['make_to_order', 'make_to_stock']),
@@ -1066,31 +1233,15 @@ class ResMrpWorkOrder(models.Model):
workorder.detection_report = base64.b64encode(open(report_file_path, 'rb').read())
return True
- # 重新下发nc程序
- # def button_send_program_again(self):
- # try:
- # res = {'programming_no': self.production_id.programming_no}
- # configsettings = self.env['res.config.settings'].get_values()
- # config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
- # url = '/api/intelligent_programming/reset_state_again'
- # config_url = configsettings['sf_url'] + url
- # r = requests.post(config_url, json=res, data=None, headers=config_header)
- # r = r.json()
- # result = json.loads(r['result'])
- # if result['status'] == 1:
- # productions = self.env['mrp.production'].search(
- # [('programming_no', '=', self.production_id.programming_no), ('programming_state', '=', '已编程')])
- # if productions:
- # workorder = productions.workorder_ids.filtered(
- # lambda ap: ap.routing_type in ['装夹预调', 'CNC加工'] and ap.state not in ['done', 'cancel',
- # 'progress'])
- # if workorder:
- # productions.write({'work_state': '编程中', 'programming_state': '编程中'})
- # else:
- # raise UserError(result['message'])
- # except Exception as e:
- # logging.info('button_send_program_again error:%s' % e)
- # raise UserError("重新下发nc程序失败,请联系管理员")
+ def print_method(self):
+ """
+ 解除装夹处调用关联制造订单的关联序列号的打印方法
+ """
+ if self.production_id:
+ if self.production_id.lot_producing_id:
+ self.production_id.lot_producing_id.print_single_method()
+ else:
+ raise UserError("无关联制造订单或关联序列号,无法打印。请检查!")
class CNCprocessing(models.Model):
@@ -1119,6 +1270,8 @@ class CNCprocessing(models.Model):
program_path = fields.Char('程序文件路径')
program_create_date = fields.Datetime('程序创建日期')
+ tool_state = fields.Selection([('0', '正常'), ('1', '缺刀'), ('2', '无效刀')], string='刀具状态', default='0')
+
# mrs下发编程单创建CNC加工
def cnc_processing_create(self, cnc_workorder, ret, program_path, program_path_tmp):
cnc_processing = None
@@ -1152,24 +1305,25 @@ class CNCprocessing(models.Model):
def _json_cnc_processing(self, panel, ret):
cnc_processing = []
- for item in ret['programming_list']:
- if item['processing_panel'] == panel and item['ftp_path'].find('.dmi') == -1:
- cnc_processing.append((0, 0, {
- 'sequence_number': item['sequence_number'],
- 'program_name': item['program_name'],
- 'cutting_tool_name': item['cutting_tool_name'],
- 'cutting_tool_no': item['cutting_tool_no'],
- 'processing_type': item['processing_type'],
- 'margin_x_y': item['margin_x_y'],
- 'margin_z': item['margin_z'],
- 'depth_of_processing_z': item['depth_of_processing_z'],
- 'cutting_tool_extension_length': item['cutting_tool_extension_length'],
- 'cutting_tool_handle_type': item['cutting_tool_handle_type'],
- 'estimated_processing_time': item['estimated_processing_time'],
- 'program_path': item['ftp_path'],
- 'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'),
- 'remark': item['remark']
- }))
+ if ret is not False:
+ for item in ret['programming_list']:
+ if item['processing_panel'] == panel and item['ftp_path'].find('.dmi') == -1:
+ cnc_processing.append((0, 0, {
+ 'sequence_number': item['sequence_number'],
+ 'program_name': item['program_name'],
+ 'cutting_tool_name': item['cutting_tool_name'],
+ 'cutting_tool_no': item['cutting_tool_no'],
+ 'processing_type': item['processing_type'],
+ 'margin_x_y': item['margin_x_y'],
+ 'margin_z': item['margin_z'],
+ 'depth_of_processing_z': item['depth_of_processing_z'],
+ 'cutting_tool_extension_length': item['cutting_tool_extension_length'],
+ 'cutting_tool_handle_type': item['cutting_tool_handle_type'],
+ 'estimated_processing_time': item['estimated_processing_time'],
+ 'program_path': item['ftp_path'],
+ 'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'),
+ 'remark': item['remark']
+ }))
return cnc_processing
# 根据程序名和加工面匹配到ftp里对应的Nc程序名,可优化为根据cnc_processing.program_path进行匹配
@@ -1372,7 +1526,8 @@ class WorkPieceDelivery(models.Model):
[('上产线', '上产线'), ('下产线', '下产线'), ('运送空料架', '运送空料架')], string='类型')
delivery_duration = fields.Float('配送时长', compute='_compute_delivery_duration')
status = fields.Selection(
- [('待下发', '待下发'), ('待配送', '待配送'), ('已配送', '已配送')], string='状态', default='待下发',
+ [('待下发', '待下发'), ('待配送', '待配送'), ('已配送', '已配送'), ('已取消', '已取消')], string='状态',
+ default='待下发',
tracking=True)
is_cnc_program_down = fields.Boolean('程序是否下发', default=False, tracking=True)
is_manual_work = fields.Boolean('人工操作', default=False)
@@ -1625,12 +1780,13 @@ class CMMprogram(models.Model):
def _json_cmm_program(self, panel, ret):
cmm_program = []
- for item in ret['programming_list']:
- if item['processing_panel'] == panel and item['ftp_path'].find('.dmi') != -1:
- cmm_program.append((0, 0, {
- 'sequence_number': 1,
- 'program_name': item['program_name'],
- 'program_path': item['ftp_path'],
- 'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'),
- }))
+ if ret is not False:
+ for item in ret['programming_list']:
+ if item['processing_panel'] == panel and item['ftp_path'].find('.dmi') != -1:
+ cmm_program.append((0, 0, {
+ 'sequence_number': 1,
+ 'program_name': item['program_name'],
+ 'program_path': item['ftp_path'],
+ 'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'),
+ }))
return cmm_program
diff --git a/sf_manufacturing/models/stock.py b/sf_manufacturing/models/stock.py
index 9234060f..4bf7cc72 100644
--- a/sf_manufacturing/models/stock.py
+++ b/sf_manufacturing/models/stock.py
@@ -204,7 +204,8 @@ 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())
'''
创建工单
@@ -404,6 +405,7 @@ class ProductionLot(models.Model):
def print_single_method(self):
+ print('self.name========== %s' % self.name)
self.ensure_one()
qr_code_data = self.qr_code_image
if not qr_code_data:
diff --git a/sf_manufacturing/security/ir.model.access.csv b/sf_manufacturing/security/ir.model.access.csv
index ec2d5a76..9b5b5e28 100644
--- a/sf_manufacturing/security/ir.model.access.csv
+++ b/sf_manufacturing/security/ir.model.access.csv
@@ -29,7 +29,7 @@ access_mrp_workorder_group_sf_mrp_user,mrp_workorder,model_mrp_workorder,sf_base
access_mrp_workorder_manager,mrp_workorder,model_mrp_workorder,sf_base.group_sf_mrp_manager,1,1,1,0
access_mrp_workcenter_group_sf_mrp_user,mrp_workcenter,model_mrp_workcenter,sf_base.group_sf_mrp_user,1,0,0,0
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_group_sf_equipment_user,mrp_workcenter_productivity,model_mrp_workcenter_productivity,sf_base.group_sf_equipment_user,1,1,1,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,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
@@ -141,3 +141,14 @@ access_sf_model_type_group_sf_stock_manager,sf_model_type_group_sf_mrp_manager,m
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
+access_sf_rework_wizard_group_sf_order_user,sf_rework_wizard_group_sf_order_user,model_sf_rework_wizard,sf_base.group_sf_order_user,1,1,1,0
+access_sf_rework_wizard_group_plan_dispatch,sf_rework_wizard_group_plan_dispatch,model_sf_rework_wizard,sf_base.group_plan_dispatch,1,1,1,0
+access_sf_detection_result_group_sf_order_user,sf_detection_result_group_sf_order_user,model_sf_detection_result,sf_base.group_sf_order_user,1,1,1,0
+access_sf_detection_result_group_plan_dispatch,sf_detection_result_group_plan_dispatch,model_sf_detection_result,sf_base.group_plan_dispatch,1,1,1,0
+access_sf_detection_result_group_sf_equipment_user,sf_detection_result_group_sf_equipment_user,model_sf_detection_result,sf_base.group_sf_equipment_user,1,1,1,0
+access_sf_processing_panel_group_sf_order_user,sf_processing_panel_group_sf_order_user,model_sf_processing_panel,sf_base.group_sf_order_user,1,1,1,0
+access_sf_production_wizard_group_sf_order_user,sf_production_wizard_group_sf_order_user,model_sf_production_wizard,sf_base.group_sf_order_user,1,1,1,0
+access_sf_processing_panel_group_plan_dispatch,sf_processing_panel_group_plan_dispatch,model_sf_processing_panel,sf_base.group_plan_dispatch,1,1,1,0
+
+
+
diff --git a/sf_manufacturing/views/mrp_production_addional_change.xml b/sf_manufacturing/views/mrp_production_addional_change.xml
index b5e348b9..fdbec99f 100644
--- a/sf_manufacturing/views/mrp_production_addional_change.xml
+++ b/sf_manufacturing/views/mrp_production_addional_change.xml
@@ -1,11 +1,16 @@
+
custom.mrp.production.tree
mrp.production
+
+
+
@@ -70,26 +75,33 @@
-
+
-
+
+
+
-
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
@@ -259,6 +282,32 @@
[])]}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 投料
+
+
+ 投料状态
+
@@ -280,6 +329,16 @@
mrp.workorder
+
+ sequence
+
+
+
+
@@ -365,7 +424,9 @@
-
+
+
+
@@ -397,6 +458,22 @@
mrp.production
+
+
+
+
+
+
+
+
+
+
+
+
@@ -482,5 +559,72 @@
+
+
+ sf.detection.result
+ sf.detection.result
+
+
+
+
+
+
+
+ 检测报告
+ sf.detection.result
+ form
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sf_manufacturing/views/mrp_workcenter_views.xml b/sf_manufacturing/views/mrp_workcenter_views.xml
index 9aad2a99..ad433bf6 100644
--- a/sf_manufacturing/views/mrp_workcenter_views.xml
+++ b/sf_manufacturing/views/mrp_workcenter_views.xml
@@ -24,6 +24,20 @@
+
+ custom.model.form.view.inherit
+ mrp.workcenter
+
+
+
+
+
+
+
+
+
+
+
mrp.workcenter.view.kanban.inherit.mrp.workorder
mrp.workcenter
diff --git a/sf_manufacturing/views/mrp_workorder_view.xml b/sf_manufacturing/views/mrp_workorder_view.xml
index dc5b4a56..805dcb1f 100644
--- a/sf_manufacturing/views/mrp_workorder_view.xml
+++ b/sf_manufacturing/views/mrp_workorder_view.xml
@@ -7,11 +7,10 @@
-
-
+
@@ -19,6 +18,7 @@
+
@@ -43,7 +43,7 @@
{'invisible': ['|', '|', '|','|','|', ('production_state','in', ('draft',
'done',
'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),
- ('is_user_working', '!=', False),("user_permissions","=",False),("name","=","获取CNC加工程序")]}
+ ('is_user_working', '!=', False),("user_permissions","=",False),("name","=","CNC加工")]}
@@ -113,6 +113,10 @@
mrp.workorder
+
+
+
+
+
@@ -137,7 +143,7 @@
+ attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>
-
-
-
-
-
+ attrs="{'invisible': ['|','|','|','|',('routing_type','!=','装夹预调'),('is_delivery','=',True),('state','!=','done'),('is_rework','=',True),'&',('rfid_code','in',['',False]),('state','=','done')]}"/>
+
+
@@ -218,11 +224,11 @@
attrs="{'invisible': [('routing_type', '!=', 'CNC加工')]}"/>
-
-
@@ -469,7 +475,8 @@
+ decoration-danger="status == '待配送'"
+ decoration-info="status== '已取消'"/>
@@ -481,16 +488,18 @@
-
+
-
-
-
-
+
+
+
+ widget="pdf_viewer" readonly="1"/>
@@ -507,6 +516,7 @@
+
@@ -587,6 +597,9 @@
+
+
+
@@ -623,7 +636,9 @@
+ decoration-danger="status == '待配送'"
+ decoration-info="status == '已取消'"
+ />
@@ -719,7 +734,7 @@
tree,form
- [('type','in',['上产线','下产线']),('workorder_state','=','done'),('is_manual_work','=',false)]
+ [('type','in',['上产线','下产线']),('workorder_state','in',['done','rework']),('is_manual_work','=',false)]
diff --git a/sf_manufacturing/views/production_line_view.xml b/sf_manufacturing/views/production_line_view.xml
index c43885b4..18b628cd 100644
--- a/sf_manufacturing/views/production_line_view.xml
+++ b/sf_manufacturing/views/production_line_view.xml
@@ -100,7 +100,7 @@
sf.maintenance.logs
-
+
diff --git a/sf_manufacturing/wizard/__init__.py b/sf_manufacturing/wizard/__init__.py
index 5cfab982..ae06323f 100644
--- a/sf_manufacturing/wizard/__init__.py
+++ b/sf_manufacturing/wizard/__init__.py
@@ -1 +1,3 @@
from . import workpiece_delivery_wizard
+from . import rework_wizard
+from . import production_wizard
diff --git a/sf_manufacturing/wizard/production_wizard.py b/sf_manufacturing/wizard/production_wizard.py
new file mode 100644
index 00000000..c00ed37d
--- /dev/null
+++ b/sf_manufacturing/wizard/production_wizard.py
@@ -0,0 +1,21 @@
+# -*- 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, _
+
+
+class ProductionWizard(models.TransientModel):
+ _name = 'sf.production.wizard'
+ _description = '制造订单向导'
+
+ production_id = fields.Many2one('mrp.production', string='制造订单号')
+ is_reprogramming = fields.Boolean(string='申请重新编程', default=True)
+ is_remanufacture = fields.Boolean(string='重新生成制造订单', default=True)
+
+ def confirm(self):
+ if self.is_reprogramming is True:
+ self.production_id.update_programming_state()
+ self.production_id.action_cancel()
+
diff --git a/sf_manufacturing/wizard/production_wizard_views.xml b/sf_manufacturing/wizard/production_wizard_views.xml
new file mode 100644
index 00000000..57b547dc
--- /dev/null
+++ b/sf_manufacturing/wizard/production_wizard_views.xml
@@ -0,0 +1,34 @@
+
+
+
+ sf.production.wizard.form.view
+ sf.production.wizard
+
+
+
+
+
+
+ 报废
+ sf.production.wizard
+ form
+ new
+
+
+
\ No newline at end of file
diff --git a/sf_manufacturing/wizard/rework_wizard.py b/sf_manufacturing/wizard/rework_wizard.py
new file mode 100644
index 00000000..91b5b970
--- /dev/null
+++ b/sf_manufacturing/wizard/rework_wizard.py
@@ -0,0 +1,200 @@
+# -*- 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, _
+
+
+class ReworkWizard(models.TransientModel):
+ _name = 'sf.rework.wizard'
+ _description = '返工向导'
+
+ workorder_id = fields.Many2one('mrp.workorder', string='工单')
+ product_id = fields.Many2one('product.product')
+ production_id = fields.Many2one('mrp.production', string='制造订单号')
+ rework_reason = fields.Selection(
+ [("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"),
+ ("operate computer", "操机"),
+ ("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因")
+ detailed_reason = fields.Text('详细原因')
+ routing_type = fields.Selection([
+ ('装夹预调', '装夹预调'),
+ ('CNC加工', 'CNC加工')], string="工序类型")
+ # 根据工单的加工面来显示
+ processing_panel_id = fields.Many2many('sf.processing.panel', string="加工面")
+ is_reprogramming = fields.Boolean(string='申请重新编程', default=False)
+ is_reprogramming_readonly = fields.Boolean(string='申请重新编程(只读)', default=False)
+ reprogramming_num = fields.Integer('重新编程次数', default=0)
+ programming_state = fields.Selection(
+ [('待编程', '待编程'), ('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'),
+ ('已下发', '已下发')],
+ string='编程状态')
+
+ tool_state = fields.Selection(string='功能刀具状态', related='production_id.tool_state')
+
+ def confirm(self):
+ if self.routing_type in ['装夹预调', 'CNC加工']:
+ self.workorder_id.is_rework = True
+ self.production_id.write({'detection_result_ids': [(0, 0, {
+ 'rework_reason': self.rework_reason,
+ 'detailed_reason': self.detailed_reason,
+ 'processing_panel': self.workorder_id.processing_panel,
+ 'routing_type': self.workorder_id.routing_type,
+ 'handle_result': '待处理' if self.workorder_id.test_results == '返工' or self.workorder_id.is_rework is True else '',
+ 'test_results': '返工' if not self.routing_type == 'CNC加工' else self.workorder_id.test_results,
+ 'test_report': self.workorder_id.detection_report})]})
+ self.workorder_id.button_finish()
+ else:
+ if self.production_id.workorder_ids:
+ handle_result = self.production_id.detection_result_ids.filtered(
+ lambda dr: dr.handle_result == '待处理')
+ if handle_result:
+ processing_panels_to_handle = set(handle_item.processing_panel for handle_item in handle_result)
+ processing_panels_choice = set(dr_panel.name for dr_panel in self.processing_panel_id)
+ # 使用集合的差集操作找出那些待处理结果中有但实际可用加工面中没有的加工面
+ processing_panels_missing = processing_panels_to_handle - processing_panels_choice
+ # 存在不一致的加工面
+ if processing_panels_missing:
+ processing_panels_str = ','.join(processing_panels_missing)
+ raise UserError('您还有待处理的检测结果中为%s的加工面未选择' % processing_panels_str)
+ # processing_panels = set()
+ # for handle_item in handle_result:
+ # for dr_panel in self.processing_panel_id:
+ # if dr_panel.name == handle_item.processing_panel:
+ # processing_panels.add(dr_panel.name)
+ # if len(processing_panels) != len(handle_result):
+ # processing_panels_str = ','.join(processing_panels)
+ # return UserError(f'您还有待处理的检测结果中为{processing_panels_str}的加工面未选择')
+ for panel in self.processing_panel_id:
+ panel_workorder = self.production_id.workorder_ids.filtered(
+ lambda ap: ap.processing_panel == panel.name and ap.state != 'rework')
+ if panel_workorder:
+ panel_workorder.write({'state': 'rework'})
+ panel_workorder.filtered(
+ lambda wo: wo.routing_type == '装夹预调').workpiece_delivery_ids.filtered(
+ lambda wd: wd.status == '待下发').write({'status': '已取消'})
+ # workpiece = self.env['sf.workpiece.delivery'].search([('status', '=', '待下发'), (
+ # 'workorder_id', '=',
+ # panel_workorder.filtered(lambda wd: wd.routing_type == '装夹预调').id)])
+ product_routing_workcenter = self.env['sf.product.model.type.routing.sort'].search(
+ [('product_model_type_id', '=', self.production_id.product_id.product_model_type_id.id)],
+ order='sequence asc'
+ )
+ workorders_values = []
+ for route in product_routing_workcenter:
+ if route.is_repeat is True:
+ workorders_values.append(
+ self.env['mrp.workorder'].json_workorder_str(panel.name,
+ self.production_id, route, False))
+ if workorders_values:
+ self.production_id.write({'workorder_ids': workorders_values, 'is_rework': True})
+ self.production_id._reset_work_order_sequence()
+ self.production_id.detection_result_ids.filtered(
+ lambda ap1: ap1.processing_panel == panel.name and ap1.handle_result == '待处理').write(
+ {'handle_result': '已处理'})
+ if self.is_reprogramming is False:
+ if self.programming_state in ['已编程', '已下发']:
+ if self.reprogramming_num >= 1 and self.programming_state == '已编程':
+ self.production_id.get_new_program(panel.name)
+ if self.reprogramming_num >= 0 and self.programming_state == '已下发':
+ ret = {'programming_list': []}
+ cnc_rework = max(
+ self.production_id.workorder_ids.filtered(
+ lambda
+ crw: crw.processing_panel == panel.name and crw.state == 'rework' and crw.routing_type == 'CNC加工'),
+ key=lambda w: w.create_date
+ )
+ if cnc_rework.cnc_ids:
+ for item_line in cnc_rework.cnc_ids:
+ vals = {
+ 'sequence_number': item_line.sequence_number,
+ 'program_name': item_line.program_name,
+ 'cutting_tool_name': item_line.cutting_tool_name,
+ 'cutting_tool_no': item_line.cutting_tool_no,
+ 'processing_type': item_line.processing_type,
+ 'margin_x_y': item_line.margin_x_y,
+ 'margin_z': item_line.margin_z,
+ 'depth_of_processing_z': item_line.depth_of_processing_z,
+ 'cutting_tool_extension_length': item_line.cutting_tool_extension_length,
+ 'estimated_processing_time': item_line.estimated_processing_time,
+ 'cutting_tool_handle_type': item_line.cutting_tool_handle_type,
+ 'program_path': item_line.program_path,
+ 'ftp_path': item_line.program_path,
+ 'processing_panel': panel.name,
+ 'program_create_date': datetime.strftime(item_line.program_create_date,
+ '%Y-%m-%d %H:%M:%S'),
+ 'remark': item_line.remark
+ }
+ ret['programming_list'].append(vals)
+ for cmm_line in cnc_rework.cmm_ids:
+ vals = {
+ 'sequence_number': cmm_line.sequence_number,
+ 'program_name': cmm_line.program_name,
+ 'program_path': cmm_line.program_path,
+ 'ftp_path': item_line.program_path,
+ 'processing_panel': panel.name,
+ 'program_create_date': datetime.strftime(
+ cmm_line.program_create_date,
+ '%Y-%m-%d %H:%M:%S')
+ }
+ ret['programming_list'].append(vals)
+ new_cnc_workorder = self.production_id.workorder_ids.filtered(
+ lambda ap1: ap1.processing_panel == panel.name and ap1.state not in (
+ 'rework', 'done') and ap1.routing_type == 'CNC加工')
+ if not new_cnc_workorder.cnc_ids:
+ new_cnc_workorder.write({
+ 'cnc_ids': new_cnc_workorder.cnc_ids.sudo()._json_cnc_processing(panel.name,
+ ret),
+ 'cmm_ids': new_cnc_workorder.cmm_ids.sudo()._json_cmm_program(panel.name,
+ ret),
+ 'cnc_worksheet': cnc_rework.cnc_worksheet})
+ new_pre_workorder = self.production_id.workorder_ids.filtered(lambda
+ p: p.routing_type == '装夹预调' and p.processing_panel == panel.name and p.state not in (
+ 'rework', 'done'))
+ if new_pre_workorder:
+ pre_rework = max(self.production_id.workorder_ids.filtered(
+ lambda pr: pr.processing_panel == panel.name and pr.state in (
+ 'rework') and pr.routing_type == '装夹预调'),
+ key=lambda w1: w1.create_date)
+ new_pre_workorder.write(
+ {'processing_drawing': pre_rework.processing_drawing})
+ self.production_id.write({'state': 'progress', 'is_rework': False})
+ elif self.programming_state in ['待编程', '编程中']:
+ self.production_id.write(
+ {'programming_state': '编程中', 'work_state': '编程中', 'is_rework': True})
+ if self.is_reprogramming is True:
+ self.production_id.update_programming_state()
+ self.production_id.write(
+ {'programming_state': '编程中', 'work_state': '编程中'})
+ if self.production_id.state == 'progress':
+ self.production_id.write({'programming_state': '已编程', 'work_state': '已编程'})
+ if self.reprogramming_num >= 1 and self.programming_state == '已编程':
+ productions_not_delivered = self.env['mrp.production'].search(
+ [('programming_no', '=', self.production_id.programming_no),
+ ('programming_state', '=', '已编程未下发')])
+ if productions_not_delivered:
+ productions_not_delivered.write(
+ {'state': 'progress', 'programming_state': '已编程', 'work_state': '已编程',
+ 'is_rework': False})
+
+ @api.onchange('production_id')
+ def onchange_processing_panel_id(self):
+ for item in self:
+ domain = [('id', '=', False)]
+ production_id = item.production_id
+ if production_id:
+ if self.env.user.has_group('sf_base.group_sf_order_user'):
+ panel_ids = []
+ panel_arr = production_id.product_id.model_processing_panel
+ for p in production_id.detection_result_ids.filtered(
+ lambda ap1: ap1.handle_result == '待处理'):
+ if p.processing_panel not in panel_arr:
+ panel_arr += ','.join(p.processing_panel)
+ for item in panel_arr.split(','):
+ panel = self.env['sf.processing.panel'].search(
+ [('name', 'ilike', item)])
+ if panel:
+ panel_ids.append(panel.id)
+ domain = {'processing_panel_id': [('id', 'in', panel_ids)]}
+ return {'domain': domain}
diff --git a/sf_manufacturing/wizard/rework_wizard_views.xml b/sf_manufacturing/wizard/rework_wizard_views.xml
new file mode 100644
index 00000000..08e5f8ef
--- /dev/null
+++ b/sf_manufacturing/wizard/rework_wizard_views.xml
@@ -0,0 +1,67 @@
+
+
+
+ sf.rework.wizard.form.view
+ sf.rework.wizard
+
+
+
+
+
+
+ 返工
+ sf.rework.wizard
+ form
+ new
+
+
+
\ No newline at end of file
diff --git a/sf_mrs_connect/controllers/controllers.py b/sf_mrs_connect/controllers/controllers.py
index f32dedea..8267fdac 100644
--- a/sf_mrs_connect/controllers/controllers.py
+++ b/sf_mrs_connect/controllers/controllers.py
@@ -112,7 +112,8 @@ class Sf_Mrs_Connect(http.Controller):
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})
+ workpiece_delivery.write(
+ {'is_cnc_program_down': True, 'production_line_id': productions.production_line_id.id})
return json.JSONEncoder().encode(res)
else:
res = {'status': 0, 'message': '该制造订单暂未开始'}
diff --git a/sf_mrs_connect/models/sync_common.py b/sf_mrs_connect/models/sync_common.py
index e3ca710b..e37d460a 100644
--- a/sf_mrs_connect/models/sync_common.py
+++ b/sf_mrs_connect/models/sync_common.py
@@ -326,6 +326,8 @@ class sfProductionProcess(models.Model):
production_process.name = item['name']
production_process.category_id = category.id
production_process.remark = item['remark']
+ production_process.processing_day = item['processing_day']
+ production_process.travel_day = item['travel_day']
production_process.active = item['active']
else:
self.create({
@@ -333,6 +335,8 @@ class sfProductionProcess(models.Model):
"category_id": category.id,
"code": item['code'],
"remark": item['remark'],
+ "processing_day": item['processing_day'],
+ "travel_day": item['travel_day'],
"active": item['active'],
})
else:
@@ -358,12 +362,16 @@ class sfProductionProcess(models.Model):
"category_id": category.id,
"code": item['code'],
"remark": item['remark'],
+ "processing_day": item['processing_day'],
+ "travel_day": item['travel_day'],
"active": item['active'],
})
else:
production_process.name = item['name']
production_process.category_id = category.id
production_process.remark = item['remark']
+ production_process.processing_day = item['processing_day']
+ production_process.travel_day = item['travel_day']
production_process.active = item['active']
else:
raise ValidationError("表面工艺认证未通过")
@@ -1073,6 +1081,9 @@ class sfProductionProcessParameter(models.Model):
[('code', '=', item['process_id_code'])])
if production_process_parameter:
production_process_parameter.name = item['name']
+ production_process_parameter.process_description = item['process_description']
+ production_process_parameter.processing_day = item['processing_day']
+ production_process_parameter.travel_day = item['travel_day']
production_process_parameter.active = item['active']
production_process_parameter.process_id = process.id
production_process_parameter.materials_model_ids = self.env['sf.materials.model'].search(
@@ -1080,6 +1091,9 @@ class sfProductionProcessParameter(models.Model):
else:
self.create({
"name": item['name'],
+ "process_description": item['process_description'],
+ "processing_day": item['processing_day'],
+ "travel_day": item['travel_day'],
"code": item['code'],
"active": item['active'],
"process_id": process.id,
@@ -1107,6 +1121,9 @@ class sfProductionProcessParameter(models.Model):
if not production_process_parameter:
self.create({
"name": item['name'],
+ "process_description": item['process_description'],
+ "processing_day": item['processing_day'],
+ "travel_day": item['travel_day'],
"code": item['code'],
"active": item['active'],
"process_id": process.id,
@@ -1115,6 +1132,9 @@ class sfProductionProcessParameter(models.Model):
})
else:
production_process_parameter.name = item['name']
+ production_process_parameter.process_description = item['process_description']
+ production_process_parameter.processing_day = item['processing_day']
+ production_process_parameter.travel_day = item['travel_day']
production_process_parameter.process_id = process.id
production_process_parameter.materials_model_ids = self.env['sf.materials.model'].search(
[('materials_no', 'in', item['materials_model_ids_codes'])])
diff --git a/sf_sale/models/sale_order.py b/sf_sale/models/sale_order.py
index f2d4073e..31a89c00 100644
--- a/sf_sale/models/sale_order.py
+++ b/sf_sale/models/sale_order.py
@@ -44,7 +44,6 @@ class ReSaleOrder(models.Model):
store=True, readonly=False, precompute=True, check_company=True, tracking=True,
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
remark = fields.Text('备注')
-
validity_date = fields.Date(
string="Expiration",
compute='_compute_validity_date',
diff --git a/sf_sale/views/sale_order_view.xml b/sf_sale/views/sale_order_view.xml
index 647cf8ef..7bf352eb 100644
--- a/sf_sale/views/sale_order_view.xml
+++ b/sf_sale/views/sale_order_view.xml
@@ -69,6 +69,9 @@
+
+
+
{'no_create': True}
{'is_sale_order_line': True }
diff --git a/sf_tool_management/models/base.py b/sf_tool_management/models/base.py
index 0fb31704..83fa3b36 100644
--- a/sf_tool_management/models/base.py
+++ b/sf_tool_management/models/base.py
@@ -313,42 +313,27 @@ class CAMWorkOrderProgramKnifePlan(models.Model):
'applicant': None,
'sf_functional_tool_assembly_id': None})
- def create_cam_work_plan(self, cnc_processing_ids):
+ def create_cam_work_plan(self, cnc_processing):
"""
根据传入的工单信息,查询是否有需要的功能刀具,如果没有则生成CAM工单程序用刀计划
"""
- for cnc_processing in cnc_processing_ids:
- status = False
- if cnc_processing.cutting_tool_name:
- functional_tools = self.env['sf.real.time.distribution.of.functional.tools'].sudo().search(
- [('name', '=', cnc_processing.cutting_tool_name)])
- if functional_tools:
- for functional_tool in functional_tools:
- if functional_tool.on_tool_stock_num == 0:
- if functional_tool.tool_stock_num == 0 and functional_tool.side_shelf_num == 0:
- status = True
- else:
- status = True
- if status:
- knife_plan = self.env['sf.cam.work.order.program.knife.plan'].sudo().create({
- 'name': cnc_processing.workorder_id.production_id.name,
- 'cam_procedure_code': cnc_processing.program_name,
- 'filename': cnc_processing.cnc_id.name,
- 'functional_tool_name': cnc_processing.cutting_tool_name,
- 'cam_cutter_spacing_code': cnc_processing.cutting_tool_no,
- 'process_type': cnc_processing.processing_type,
- 'margin_x_y': float(cnc_processing.margin_x_y),
- 'margin_z': float(cnc_processing.margin_z),
- 'finish_depth': float(cnc_processing.depth_of_processing_z),
- 'extension_length': float(cnc_processing.cutting_tool_extension_length),
- 'shank_model': cnc_processing.cutting_tool_handle_type,
- 'estimated_processing_time': cnc_processing.estimated_processing_time,
- })
- logging.info('CAM工单程序用刀计划创建成功!!!')
- # 创建装刀请求
- knife_plan.apply_for_tooling()
- else:
- logging.info('功能刀具【%s】满足CNC用刀需求!!!' % cnc_processing.cutting_tool_name)
+ knife_plan = self.env['sf.cam.work.order.program.knife.plan'].sudo().create({
+ 'name': cnc_processing.workorder_id.production_id.name,
+ 'cam_procedure_code': cnc_processing.program_name,
+ 'filename': cnc_processing.cnc_id.name,
+ 'functional_tool_name': cnc_processing.cutting_tool_name,
+ 'cam_cutter_spacing_code': cnc_processing.cutting_tool_no,
+ 'process_type': cnc_processing.processing_type,
+ 'margin_x_y': float(cnc_processing.margin_x_y),
+ 'margin_z': float(cnc_processing.margin_z),
+ 'finish_depth': float(cnc_processing.depth_of_processing_z),
+ 'extension_length': float(cnc_processing.cutting_tool_extension_length),
+ 'shank_model': cnc_processing.cutting_tool_handle_type,
+ 'estimated_processing_time': cnc_processing.estimated_processing_time,
+ })
+ logging.info('CAM工单程序用刀计划创建成功!!!')
+ # 创建装刀请求
+ knife_plan.apply_for_tooling()
def unlink_cam_plan(self, production):
for item in production:
@@ -669,6 +654,20 @@ class FunctionalToolAssembly(models.Model):
:return:
"""
+ picking_num = fields.Integer('调拨单数量', compute='compute_picking_num', store=True)
+
+ @api.depends('assemble_status')
+ def compute_picking_num(self):
+ for item in self:
+ picking_ids = self.env['stock.picking'].sudo().search([('origin', '=', item.assembly_order_code)])
+ item.picking_num = len(picking_ids)
+
+ def open_tool_stock_picking(self):
+ action = self.env.ref('stock.action_picking_tree_all')
+ result = action.read()[0]
+ result['domain'] = [('origin', '=', self.assembly_order_code)]
+ return result
+
@api.model_create_multi
def create(self, vals):
obj = super(FunctionalToolAssembly, self).create(vals)
@@ -758,7 +757,7 @@ class FunctionalToolDismantle(models.Model):
num = "%03d" % m
return 'GNDJ-CJD-%s-%s' % (datetime, num)
- functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', required=True,
+ functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', required=True, tracking=True,
domain=[('functional_tool_status', '!=', '已拆除'),
('current_location', '=', '刀具房')])
tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', store=True,
@@ -776,8 +775,18 @@ class FunctionalToolDismantle(models.Model):
dismantle_person = fields.Char('拆解人', readonly=True)
image = fields.Binary('图片', readonly=True)
- scrap_id = fields.Char('报废单号', readonly=True)
+ scrap_ids = fields.One2many('stock.scrap', 'functional_tool_dismantle_id', string='报废单号', readonly=True)
grinding_id = fields.Char('磨削单号', readonly=True)
+ picking_id = fields.Many2one('stock.picking', string='刀具物料调拨单')
+ picking_num = fields.Integer('调拨单数量', default=0, compute='compute_picking_num', store=True)
+
+ @api.depends('picking_id')
+ def compute_picking_num(self):
+ for item in self:
+ if item.picking_id:
+ item.picking_num = 1
+ else:
+ item.picking_num = 0
state = fields.Selection([('待拆解', '待拆解'), ('已拆解', '已拆解')], default='待拆解', tracking=True)
active = fields.Boolean('有效', default=True)
@@ -791,7 +800,14 @@ class FunctionalToolDismantle(models.Model):
handle_rfid = fields.Char(string='刀柄Rfid', compute='_compute_functional_tool_num', store=True)
handle_lot_id = fields.Many2one('stock.lot', string='刀柄序列号', compute='_compute_functional_tool_num',
store=True)
- scrap_boolean = fields.Boolean(string='刀柄是否报废', default=False, tracking=True)
+ scrap_boolean = fields.Boolean(string='刀柄是否报废', default=False, tracking=True, compute='compute_scrap_boolean',
+ store=True)
+
+ @api.depends('dismantle_cause')
+ def compute_scrap_boolean(self):
+ for item in self:
+ if item.dismantle_cause not in ['寿命到期报废', '崩刀报废']:
+ item.scrap_boolean = False
# 整体式
integral_product_id = fields.Many2one('product.product', string='整体式刀具',
@@ -933,75 +949,50 @@ class FunctionalToolDismantle(models.Model):
self.rfid, self.functional_tool_id.current_location))
# 目标重复校验
self.location_duplicate_check()
- location = self.env['stock.location'].search([('name', '=', '刀具组装位置')])
- location_dest = self.env['stock.location'].search([('name', '=', '刀具房')])
+ datas = {'scrap': [], 'picking': []}
# =================刀柄是否[报废]拆解=======
- location_dest_scrap_ids = self.env['stock.location'].search([('name', 'in', ('Scrap', '报废'))])
if self.handle_rfid:
lot = self.env['stock.lot'].sudo().search([('rfid', '=', self.handle_rfid)])
if not lot:
- raise ValidationError('Rfid为【%s】的功能刀具序列号不存在!' % self.handle_rfid)
- functional_tool_assembly = self.functional_tool_id.functional_tool_name_id
+ raise ValidationError('Rfid为【%s】的刀柄序列号不存在!' % self.handle_rfid)
if self.scrap_boolean:
# 刀柄报废 入库到Scrap
- lot.create_stock_quant(location, location_dest_scrap_ids[-1], False, code, False, False)
+ datas['scrap'].append({'lot_id': lot})
lot.tool_material_status = '报废'
else:
# 刀柄不报废 入库到刀具房
- lot.create_stock_quant(location, location_dest, False, code, False, False)
+ datas['picking'].append({'lot_id': lot, 'destination': self.env['sf.shelf.location']})
lot.tool_material_status = '可用'
-
# ==============功能刀具[报废]拆解================
if self.dismantle_cause in ['寿命到期报废', '崩刀报废']:
# 除刀柄外物料报废 入库到Scrap
if self.integral_product_id:
- self.integral_product_id.dismantle_stock_moves(False, self.integral_lot_id, location,
- location_dest_scrap_ids[-1], code)
+ datas['scrap'].append({'lot_id': self.integral_lot_id})
elif self.blade_product_id:
- self.blade_product_id.dismantle_stock_moves(False, self.blade_lot_id, location,
- location_dest_scrap_ids[-1], code)
+ datas['scrap'].append({'lot_id': self.blade_lot_id})
if self.bar_product_id:
- self.bar_product_id.dismantle_stock_moves(False, self.bar_lot_id, location,
- location_dest_scrap_ids[-1], code)
+ datas['scrap'].append({'lot_id': self.bar_lot_id})
elif self.pad_product_id:
- self.pad_product_id.dismantle_stock_moves(False, self.pad_lot_id, location,
- location_dest_scrap_ids[-1], code)
+ datas['scrap'].append({'lot_id': self.pad_lot_id})
if self.chuck_product_id:
- self.chuck_product_id.dismantle_stock_moves(False, self.chuck_lot_id, location,
- location_dest_scrap_ids[-1], code)
- # ===========功能刀具[磨削]拆解==============
- # elif self.dismantle_cause in ['刀具需磨削']:
- # location_dest = self.env['stock.location'].search([('name', '=', '磨削房')])
- # # 除刀柄外物料拆解 入库到具体库位
- # if self.integral_product_id:
- # self.integral_product_id.dismantle_stock_moves(False, location, location_dest)
- # elif self.blade_product_id:
- # self.blade_product_id.dismantle_stock_moves(False, location, location_dest)
- # if self.bar_product_id:
- # self.bar_product_id.dismantle_stock_moves(False, location, location_dest)
- # elif self.pad_product_id:
- # self.pad_product_id.dismantle_stock_moves(False, location, location_dest)
- # if self.chuck_product_id:
- # self.chuck_product_id.dismantle_stock_moves(False, location, location_dest)
+ datas['scrap'].append({'lot_id': self.chuck_lot_id})
# ==============功能刀具[更换,磨削]拆解==============
elif self.dismantle_cause in ['更换为其他刀具', '刀具需磨削']:
# 除刀柄外物料拆解 入库到具体货位
if self.integral_freight_id:
- self.integral_product_id.dismantle_stock_moves(self.integral_freight_id, self.integral_lot_id, location,
- location_dest, code)
+ datas['picking'].append({'lot_id': self.integral_lot_id, 'destination': self.integral_freight_id})
elif self.blade_freight_id:
- self.blade_product_id.dismantle_stock_moves(self.blade_freight_id, self.blade_lot_id, location,
- location_dest, code)
+ datas['picking'].append({'lot_id': self.blade_lot_id, 'destination': self.blade_freight_id})
if self.bar_freight_id:
- self.bar_product_id.dismantle_stock_moves(self.bar_freight_id, self.bar_lot_id, location,
- location_dest, code)
+ datas['picking'].append({'lot_id': self.bar_lot_id, 'destination': self.bar_freight_id})
elif self.pad_freight_id:
- self.pad_product_id.dismantle_stock_moves(self.pad_freight_id, self.pad_lot_id, location,
- location_dest, code)
+ datas['picking'].append({'lot_id': self.pad_lot_id, 'destination': self.pad_freight_id})
if self.chuck_freight_id:
- self.chuck_product_id.dismantle_stock_moves(self.chuck_freight_id, self.chuck_lot_id, location,
- location_dest, code)
- # ===============删除功能刀具的Rfid字段的值, 赋值给Rfid(已拆解)字段=====
+ datas['picking'].append({'lot_id': self.chuck_lot_id, 'destination': self.chuck_freight_id})
+ self.create_tool_picking_scrap(datas)
+ # ===============创建功能刀具拆解移动记录=====
+ self.env['stock.move'].create_functional_tool_stock_move(self)
+ # 修改功能刀具数据
self.functional_tool_id.write({
'rfid_dismantle': self.functional_tool_id.rfid,
'rfid': '',
@@ -1009,37 +1000,173 @@ class FunctionalToolDismantle(models.Model):
})
# 修改拆解单的值
self.write({
- 'rfid_dismantle': self.rfid,
'dismantle_data': fields.Datetime.now(),
'dismantle_person': self.env.user.name,
- 'rfid': '',
+ 'rfid': '%s(已拆解)' % self.rfid,
'state': '已拆解'
})
logging.info('【%s】刀具拆解成功!' % self.name)
+ def create_tool_picking_scrap(self, datas):
+ scrap_data = datas['scrap']
+ picking_data = datas['picking']
+ if scrap_data:
+ for data in scrap_data:
+ if data:
+ self.env['stock.scrap'].create_tool_dismantle_stock_scrap(data['lot_id'], self)
+ if picking_data:
+ picking_id = self.env['stock.picking'].create_tool_dismantle_picking(self)
+ self.picking_id = picking_id.id
+ self.env['stock.move'].create_tool_stock_move({'data': picking_data, 'picking_id': picking_id})
+ # 将刀具物料出库库单的状态更改为就绪
+ picking_id.action_confirm()
+ # 修改刀具物料出库移动历史记录
+ self.env['stock.move'].write_tool_stock_move_line({'data': picking_data, 'picking_id': picking_id})
+ # 设置数量,并验证完成
+ picking_id.action_set_quantities_to_reservation()
+ picking_id.button_validate()
-class ProductProduct(models.Model):
- _inherit = 'product.product'
+ def action_open_reference1(self):
+ self.ensure_one()
+ return {
+ 'res_model': self._name,
+ 'type': 'ir.actions.act_window',
+ 'views': [[False, "form"]],
+ 'res_id': self.id,
+ }
- def dismantle_stock_moves(self, shelf_location_id, lot_id, location_id, location_dest_id, code):
- # 创建功能刀具拆解单产品库存移动记录
- stock_move_id = self.env['stock.move'].sudo().create({
- 'name': code,
- 'product_id': self.id,
+ def open_function_tool_stock_move_line(self):
+ action = self.env.ref('sf_tool_management.sf_inbound_and_outbound_records_of_functional_tools_view_act')
+ result = action.read()[0]
+ result['domain'] = [('functional_tool_dismantle_id', '=', self.id), ('qty_done', '>', 0)]
+ return result
+
+ def open_tool_stock_picking(self):
+ action = self.env.ref('stock.action_picking_tree_all')
+ result = action.read()[0]
+ result['domain'] = [('origin', '=', self.code)]
+ return result
+
+
+class StockPicking(models.Model):
+ _inherit = 'stock.picking'
+
+ def create_tool_dismantle_picking(self, obj):
+ """
+ 创建刀具物料入库单
+ """
+ # 获取名称为内部调拨的作业类型
+ picking_type_id = self.env['stock.picking.type'].sudo().search([('name', '=', '内部调拨')])
+ location_id = self.env['stock.location'].search([('name', '=', '刀具组装位置')])
+ location_dest_id = self.env['stock.location'].search([('name', '=', '刀具房')])
+ if not location_id:
+ raise ValidationError('缺少名称为【刀具组装位置】的仓库管理地点')
+ if not location_dest_id:
+ raise ValidationError('缺少名称为【刀具房】的仓库管理地点')
+ # 创建刀具物料出库单
+ picking_id = self.env['stock.picking'].create({
+ 'name': self._get_name_stock1(picking_type_id),
+ 'picking_type_id': picking_type_id.id,
'location_id': location_id.id,
'location_dest_id': location_dest_id.id,
+ 'origin': obj.code
+ })
+
+ return picking_id
+
+
+class StockMove(models.Model):
+ _inherit = 'stock.move'
+
+ def create_tool_stock_move(self, datas):
+ picking_id = datas['picking_id']
+ data = datas['data']
+ stock_move_ids = []
+ for res in data:
+ if res:
+ # 创建库存移动记录
+ stock_move_id = self.env['stock.move'].sudo().create({
+ 'name': picking_id.name,
+ 'picking_id': picking_id.id,
+ 'product_id': res['lot_id'].product_id.id,
+ 'location_id': picking_id.location_id.id,
+ 'location_dest_id': picking_id.location_dest_id.id,
+ 'product_uom_qty': 1.00,
+ 'reserved_availability': 1.00
+ })
+ stock_move_ids.append(stock_move_id)
+ return stock_move_ids
+
+ def write_tool_stock_move_line(self, datas):
+ picking_id = datas['picking_id']
+ data = datas['data']
+ move_line_ids = picking_id.move_line_ids
+ for move_line_id in move_line_ids:
+ for res in data:
+ if move_line_id.lot_id.product_id == res['lot_id'].product_id:
+ move_line_id.write({
+ 'destination_location_id': res.get('destination').id,
+ 'lot_id': res.get('lot_id').id
+ })
+ return True
+
+ def create_functional_tool_stock_move(self, dismantle_id):
+ """
+ 对功能刀具拆解过程的功能刀具进行库存移动,以及创建移动历史
+ """
+ location_dismantle_id = self.env['stock.location'].search([('name', '=', '拆解')])
+ if not location_dismantle_id:
+ raise ValidationError('缺少名称为【拆解】的仓库管理地点')
+ tool_id = dismantle_id.functional_tool_id
+ # 创建库存移动记录
+ stock_move_id = self.env['stock.move'].sudo().create({
+ 'name': dismantle_id.code,
+ 'product_id': tool_id.barcode_id.product_id.id,
+ 'location_id': tool_id.current_location_id.id,
+ 'location_dest_id': location_dismantle_id.id,
'product_uom_qty': 1.00,
'state': 'done'
})
+
# 创建移动历史记录
stock_move_line_id = self.env['stock.move.line'].sudo().create({
- 'product_id': self.id,
- 'lot_id': lot_id.id,
+ 'product_id': tool_id.barcode_id.product_id.id,
+ 'functional_tool_dismantle_id': dismantle_id.id,
+ 'lot_id': tool_id.barcode_id.id,
'move_id': stock_move_id.id,
- 'destination_location_id': shelf_location_id.id if shelf_location_id else False,
- 'install_tool_time': fields.Datetime.now(),
'qty_done': 1.0,
'state': 'done',
+ 'functional_tool_type_id': tool_id.sf_cutting_tool_type_id.id,
+ 'diameter': tool_id.functional_tool_diameter,
+ 'knife_tip_r_angle': tool_id.knife_tip_r_angle,
+ 'code': tool_id.code,
+ 'rfid': tool_id.rfid,
+ 'functional_tool_name': tool_id.name,
+ 'tool_groups_id': tool_id.tool_groups_id.id
})
-
return stock_move_id, stock_move_line_id
+
+
+class CustomStockScrap(models.Model):
+ _inherit = 'stock.scrap'
+
+ functional_tool_dismantle_id = fields.Many2one('sf.functional.tool.dismantle', string="功能刀具拆解单")
+
+ def create_tool_dismantle_stock_scrap(self, lot, dismantle_id):
+ location_id = self.env['stock.location'].search([('name', '=', '刀具组装位置')])
+ scrap_location_id = self.env['stock.location'].search([('name', 'in', ('Scrap', '报废'))])
+ if not location_id:
+ raise ValidationError('缺少名称为【刀具组装位置】的仓库管理地点')
+ if not scrap_location_id:
+ raise ValidationError('缺少名称为【Scrap】或【Scrap】的仓库管理地点')
+ stock_scrap_id = self.create({
+ 'product_id': lot.product_id.id,
+ 'lot_id': lot.id,
+ 'location_id': location_id.id,
+ 'scrap_location_id': scrap_location_id.id,
+ 'functional_tool_dismantle_id': dismantle_id.id,
+ 'origin': dismantle_id.code
+ })
+ # 完成报废单
+ stock_scrap_id.action_validate()
+ return stock_scrap_id
diff --git a/sf_tool_management/models/functional_tool.py b/sf_tool_management/models/functional_tool.py
index 18f0a466..dd539fc2 100644
--- a/sf_tool_management/models/functional_tool.py
+++ b/sf_tool_management/models/functional_tool.py
@@ -53,7 +53,8 @@ class FunctionalCuttingToolEntity(models.Model):
safe_inventory_id = fields.Many2one('sf.real.time.distribution.of.functional.tools',
string='功能刀具安全库存', readonly=True)
- @api.depends('barcode_id.quant_ids', 'functional_tool_status', 'current_shelf_location_id')
+ @api.depends('barcode_id.quant_ids', 'barcode_id.quant_ids.location_id', 'functional_tool_status',
+ 'current_shelf_location_id')
def _compute_current_location_id(self):
for record in self:
if record.functional_tool_status == '已拆除':
@@ -252,11 +253,19 @@ class FunctionalCuttingToolEntity(models.Model):
def open_safety_stock(self):
action = self.env.ref('sf_tool_management.sf_real_time_distribution_of_functional_tools_view_act')
result = action.read()[0]
- result['domain'] = [('name', '=', self.name), ('diameter', '=', self.functional_tool_diameter),
- ('knife_tip_r_angle', '=', self.knife_tip_r_angle),
- ('coarse_middle_thin', '=', self.coarse_middle_thin)]
+ result['domain'] = [('id', '=', self.safe_inventory_id.id)]
return result
+ def cnc_function_tool_use_verify(self):
+ """
+ cnc程序用刀可用校验(校验是否是制造订单所缺刀)
+ """
+ if self.tool_name_id.name:
+ cnc_processing_ids = self.env['sf.cnc.processing'].search(
+ [('tool_state', '=', '1'), ('cutting_tool_name', '=', self.tool_name_id.name)])
+ if cnc_processing_ids:
+ cnc_processing_ids.sudo().write({'tool_state': '0'})
+
def tool_inventory_displacement_out(self):
"""
机床当前刀库实时信息接口,功能刀具出库
@@ -267,6 +276,7 @@ class FunctionalCuttingToolEntity(models.Model):
self.create_stock_move(stock_location_id, False)
self.current_location_id = stock_location_id.id
self.current_shelf_location_id = False
+
# self.barcode_id.create_stock_quant(location_inventory_id, stock_location_id,
# self.functional_tool_name_id.id, '机床装刀', self.functional_tool_name_id,
# self.functional_tool_name_id.tool_groups_id)
@@ -372,6 +382,7 @@ class StockMoveLine(models.Model):
_order = 'date desc'
functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具组装单')
+ functional_tool_dismantle_id = fields.Many2one('sf.functional.tool.dismantle', string='功能刀具拆解单')
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('刀具名称')
@@ -392,6 +403,9 @@ class StockMoveLine(models.Model):
if self.functional_tool_name_id:
action = self.functional_tool_name_id.action_open_reference1()
return action
+ if self.functional_tool_dismantle_id:
+ action = self.functional_tool_dismantle_id.action_open_reference1()
+ return action
elif self.move_id:
action = self.move_id.action_open_reference()
if action['res_model'] != 'stock.move':
@@ -409,13 +423,14 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
_inherit = ['mail.thread']
_description = '功能刀具安全库存'
- name = fields.Char('名称', readonly=True, compute='_compute_name', store=True)
+ name = fields.Char('名称', compute='_compute_num', store=True)
functional_name_id = fields.Many2one('sf.tool.inventory', string='功能刀具名称', required=True)
- 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.Float(string='刀具直径(mm)', readonly=False)
- knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', readonly=False)
+ tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', compute='_compute_num', store=True)
+ sf_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型',
+ compute='_compute_num', store=True,
+ group_expand='_read_mrs_cutting_tool_type_ids')
+ diameter = fields.Float(string='刀具直径(mm)', compute='_compute_num', store=True)
+ knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', compute='_compute_num', store=True)
tool_stock_num = fields.Integer(string='刀具房数量', compute='_compute_stock_num', store=True)
side_shelf_num = fields.Integer(string='线边刀库数量', compute='_compute_stock_num', store=True)
on_tool_stock_num = fields.Integer(string='机内刀库数量', compute='_compute_stock_num', store=True)
@@ -460,22 +475,18 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
active = fields.Boolean(string='已归档', default=True)
- @api.onchange('functional_name_id')
- def _onchange_num(self):
+ @api.depends('functional_name_id', 'functional_name_id.diameter', 'functional_name_id.angle',
+ 'functional_name_id.functional_cutting_tool_model_id')
+ def _compute_num(self):
for item in self:
if item.functional_name_id:
item.tool_groups_id = item.functional_name_id.tool_groups_id.id
item.sf_cutting_tool_type_id = item.functional_name_id.functional_cutting_tool_model_id.id
item.diameter = item.functional_name_id.diameter
item.knife_tip_r_angle = item.functional_name_id.angle
-
- @api.depends('functional_name_id')
- def _compute_name(self):
- for obj in self:
- if obj.tool_groups_id:
- obj.name = obj.functional_name_id.name
+ item.name = item.functional_name_id.name
else:
- obj.sudo().name = ''
+ item.sudo().name = ''
@api.constrains('min_stock_num', 'max_stock_num')
def _check_stock_num(self):
@@ -583,4 +594,10 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
for vals in vals_list:
vals['status_create'] = False
records = super(RealTimeDistributionOfFunctionalTools, self).create(vals_list)
+ for item in records:
+ if item:
+ record = self.search([('functional_name_id', '=', item.functional_name_id.id)])
+ if len(record) > 1:
+ raise ValidationError(
+ '功能刀具名称为【%s】的安全库存已经存在,请勿重复创建!!!' % item.functional_name_id.name)
return records
diff --git a/sf_tool_management/models/functional_tool_enroll.py b/sf_tool_management/models/functional_tool_enroll.py
index 9c5b1417..ba3553e1 100644
--- a/sf_tool_management/models/functional_tool_enroll.py
+++ b/sf_tool_management/models/functional_tool_enroll.py
@@ -37,14 +37,10 @@ class ToolDatasync(models.Model):
def _cron_tool_datasync_all(self):
try:
- self.env['stock.lot'].sudo().sync_enroll_tool_material_stock_all()
-
- self.env['stock.lot'].sudo().sync_enroll_fixture_material_stock_all()
-
self.env['sf.tool.material.search'].sudo().sync_enroll_tool_material_all()
-
+ self.env['stock.lot'].sudo().sync_enroll_tool_material_stock_all()
self.env['sf.fixture.material.search'].sudo().sync_enroll_fixture_material_all()
-
+ self.env['stock.lot'].sudo().sync_enroll_fixture_material_stock_all()
self.env['sf.functional.cutting.tool.entity'].sudo().esync_enroll_functional_tool_entity_all()
logging.info("已全部同步完成!!!")
# self.env['sf.functional.tool.warning'].sudo().sync_enroll_functional_tool_warning_all()
@@ -106,7 +102,7 @@ class StockLot(models.Model):
logging.info("没有刀具物料序列号信息")
except Exception as e:
logging.info("刀具物料序列号同步失败:%s" % e)
-
+
class ToolMaterial(models.Model):
_inherit = 'sf.tool.material.search'
@@ -198,7 +194,7 @@ class FunctionalCuttingToolEntity(models.Model):
for item in objs_all:
val = {
'id': item.id,
- 'code': item.code,
+ 'code': False if not item.code else item.code.split('-', 1)[1],
'name': item.name,
'rfid': item.rfid,
'tool_groups_name': item.tool_groups_id.name,
diff --git a/sf_tool_management/models/mrp_workorder.py b/sf_tool_management/models/mrp_workorder.py
index e21941ca..965fa03c 100644
--- a/sf_tool_management/models/mrp_workorder.py
+++ b/sf_tool_management/models/mrp_workorder.py
@@ -29,13 +29,106 @@ class CNCprocessing(models.Model):
# else:
# raise ValidationError("MES装刀指令发送失败")
+ def cnc_tool_checkout(self, cnc_processing_ids):
+ """
+ 根据传入的工单信息,查询是否有需要的功能刀具,如果没有则生成CAM工单程序用刀计划
+ """
+ logging.info('开始进行工单cnc程序用刀校验!!!')
+ logging.info(f'cnc_processing_ids:{cnc_processing_ids}')
+ if not cnc_processing_ids:
+ return False
+ cam_id = self.env['sf.cam.work.order.program.knife.plan']
+ production_ids = [] # 制造订单集
+ datas = {'缺刀': {}, '无效刀': {}} # 缺刀/无效刀集
+ for cnc_processing in cnc_processing_ids:
+ # ======创建字典: {'缺刀': {'制造订单1': {'加工面1': [], ...}, ...}, '无效刀': {'制造订单1': {'加工面1': [], ...}, ...}}======
+ production_name = cnc_processing.workorder_id.production_id.name # 制造订单
+ processing_panel = cnc_processing.workorder_id.processing_panel # 加工面
+ if production_name not in list(datas['缺刀'].keys()):
+ datas['缺刀'].update({production_name: {processing_panel: []}})
+ datas['无效刀'].update({production_name: {processing_panel: []}})
+ production_ids.append(cnc_processing.workorder_id.production_id)
+ else:
+ if processing_panel not in list(datas['缺刀'].get(production_name).keys()):
+ datas['缺刀'].get(production_name).update({processing_panel: []})
+ datas['无效刀'].get(production_name).update({processing_panel: []})
+ # ======================================
+ if cnc_processing.cutting_tool_name:
+ tool_name = cnc_processing.cutting_tool_name
+ # 检验CNC用刀是否是功能刀具清单中的刀具
+ tool_inventory_id = self.env['sf.tool.inventory'].sudo().search([('name', '=', tool_name)])
+ if not tool_inventory_id:
+ if cnc_processing.cutting_tool_name not in datas['无效刀'][production_name][processing_panel]:
+ datas['无效刀'][production_name][processing_panel].append(cnc_processing.cutting_tool_name)
+ cnc_processing.tool_state = '2'
+ logging.info(f'"无效刀":[{production_name}、{processing_panel}、{cnc_processing.cutting_tool_name}]')
+ # 跳过本次循环
+ continue
+ # 校验CNC用刀在系统是否存在
+ functional_tools = self.env['sf.functional.cutting.tool.entity'].sudo().search(
+ [('tool_name_id', '=', tool_inventory_id.id), ('functional_tool_status', '=', '正常')])
+ # 判断线边、机内是否有满足条件的刀
+ if not functional_tools.filtered(lambda p: p.current_location in ('线边刀库', '机内刀库')):
+ if cnc_processing.cutting_tool_name not in datas['缺刀'][production_name][processing_panel]:
+ datas['缺刀'][production_name][processing_panel].append(cnc_processing.cutting_tool_name)
+ cnc_processing.tool_state = '1'
+ logging.info(f'"缺刀":[{production_name}、{processing_panel}、{cnc_processing.cutting_tool_name}]')
+ # 判断是否有满足条件的刀
+ if not functional_tools:
+ # 创建CAM申请装刀记录
+ cam_id.create_cam_work_plan(cnc_processing)
+ logging.info('成功调用CAM工单程序用刀计划创建方法!!!')
+ logging.info(f'datas:{datas}')
+ for production_id in production_ids:
+ logging.info(f'production_id: {production_id}')
+ if production_id:
+ data1 = datas['无效刀'].get(production_id.name) # data1: {'加工面1': [], ...}
+ data2 = datas['缺刀'].get(production_id.name) # data2: {'加工面1': [], ...}
+ # tool_state_remark1 = ''
+ tool_state_remark2 = ''
+ # 对无效刀信息进行处理
+ for key in data1:
+ if data1.get(key):
+ # if tool_state_remark1 != '':
+ # tool_state_remark1 = f'{tool_state_remark1}\n{key}无效刀:{data1.get(key)}'
+ # else:
+ # tool_state_remark1 = f'{key}无效刀:{data1.get(key)}'
+ # 无效刀处理逻辑
+ # 1、创建制造订单无效刀检测结果记录
+ logging.info('创建制造订单无效刀检测结果记录!')
+ production_id.detection_result_ids.create({
+ 'production_id': production_id.id,
+ 'processing_panel': key,
+ 'routing_type': 'CNC加工',
+ 'rework_reason': 'programming', # 原因:编程(programming)
+ 'detailed_reason': '无效功能刀具',
+ 'test_results': '返工',
+ 'handle_result': '待处理'
+ })
+ # 修改当前面装夹预调工单的 is_rework 为 True
+ # work_ids = production_id.workorder_ids.filtered(
+ # lambda a: a.routing_type == '装夹预调' and a.processing_panel == key and not a.is_rework)
+ # work_ids.write({'is_rework': True})
+ # 对缺刀信息进行处理
+ for key in data2:
+ if data2.get(key):
+ if tool_state_remark2 != '':
+ tool_state_remark2 = f'{tool_state_remark2}\n{key}缺刀:{data2.get(key)}'
+ else:
+ tool_state_remark2 = f'{key}缺刀:{data2.get(key)}'
+ # 将备注信息存入制造订单功能刀具状态的备注字段
+ logging.info('修改制造订单功能刀具状态的备注字段')
+ production_id.write({
+ 'tool_state_remark': tool_state_remark2,
+ # 'tool_state_remark2': tool_state_remark1
+ })
+ logging.info('工单cnc程序用刀校验已完成!')
+
@api.model_create_multi
def create(self, vals):
obj = super(CNCprocessing, self).create(vals)
- for item in obj:
- # 调用CAM工单程序用刀计划创建方法
- self.env['sf.cam.work.order.program.knife.plan'].create_cam_work_plan(item)
- logging.info('成功调用CAM工单程序用刀计划创建方法!!!')
+ # 调用CAM工单程序用刀计划创建方法
+ self.cnc_tool_checkout(obj)
return obj
diff --git a/sf_tool_management/models/stock.py b/sf_tool_management/models/stock.py
index be0f2e45..a179c7d7 100644
--- a/sf_tool_management/models/stock.py
+++ b/sf_tool_management/models/stock.py
@@ -21,3 +21,40 @@ class ShelfLocation(models.Model):
continue
item.tool_rfid = ''
item.tool_name_id = False
+
+
+class StockMoveLine(models.Model):
+ _inherit = 'stock.move.line'
+
+ @api.model_create_multi
+ def create(self, vals_list):
+ records = super(StockMoveLine, self).create(vals_list)
+ move_lines = records.filtered(lambda a: a.product_id.categ_id.name == '功能刀具' and a.state == 'done')
+ if move_lines: # 校验是否为功能刀具移动历史
+ self.button_function_tool_use_verify(move_lines)
+ return records
+
+ def button_function_tool_use_verify(self, move_lines):
+ """
+ 对所有从【刀具房】到【制造前】的功能刀具进行校验(校验是否为制造订单所缺的刀)
+ """
+ location_id = self.env['stock.location'].search([('name', '=', '刀具房')])
+ location_dest_id = self.env['stock.location'].search([('name', '=', '制造前')])
+ line_ids = move_lines.filtered(
+ lambda a: a.location_id == location_id and a.location_dest_id == location_dest_id)
+ for line_id in line_ids:
+ if line_id.lot_id:
+ self.env['sf.functional.cutting.tool.entity'].sudo().search(
+ [('barcode_id', '=', line_id.lot_id.id),
+ ('functional_tool_status', '=', '正常')]).cnc_function_tool_use_verify()
+
+
+class StockPicking(models.Model):
+ _inherit = 'stock.picking'
+
+ def button_validate(self):
+ res = super().button_validate()
+ move_lines = self.move_line_ids.filtered(lambda a: a.product_id.categ_id.name == '功能刀具')
+ if move_lines:
+ self.env['stock.move.line'].sudo().button_function_tool_use_verify(move_lines)
+ return res
diff --git a/sf_tool_management/views/functional_tool_views.xml b/sf_tool_management/views/functional_tool_views.xml
index 6fde82a0..103583c2 100644
--- a/sf_tool_management/views/functional_tool_views.xml
+++ b/sf_tool_management/views/functional_tool_views.xml
@@ -29,7 +29,6 @@
-
@@ -61,13 +60,9 @@
-
-
- 出库入库记录
-
-
+
+ 出库入库记录
-
+
@@ -482,18 +477,19 @@
-
-
-
+
+
+
+
diff --git a/sf_tool_management/views/tool_base_views.xml b/sf_tool_management/views/tool_base_views.xml
index 31fe53c9..c180c917 100644
--- a/sf_tool_management/views/tool_base_views.xml
+++ b/sf_tool_management/views/tool_base_views.xml
@@ -407,6 +407,7 @@
+
@@ -476,6 +477,17 @@
+
@@ -711,10 +723,14 @@
-
-
-
-
+
+
+
+
@@ -759,10 +775,25 @@