diff --git a/jikimo_workorder_exception/security/ir.model.access.csv b/jikimo_workorder_exception/security/ir.model.access.csv index bbc066e4..660adb9c 100644 --- a/jikimo_workorder_exception/security/ir.model.access.csv +++ b/jikimo_workorder_exception/security/ir.model.access.csv @@ -1,2 +1,5 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" "access_jikimo_workorder_exception","access.jikimo.workorder.exception","model_jikimo_workorder_exception","mrp.group_mrp_user",1,1,1,0 +"access_jikimo_workorder_exception_group_quality","access.jikimo.workorder.exception.group_quality","model_jikimo_workorder_exception","sf_base.group_quality",1,1,1,0 +"access_jikimo_workorder_exception_group_quality_director","access.jikimo.workorder.exception.group_quality_director","model_jikimo_workorder_exception","sf_base.group_quality_director",1,1,1,0 + diff --git a/quality_control/models/product_category.py b/quality_control/models/product_category.py index 9b5535d4..17f26006 100644 --- a/quality_control/models/product_category.py +++ b/quality_control/models/product_category.py @@ -21,4 +21,12 @@ class ProductCategory(models.Model): args += [('name', 'not in', ['Saleable', 'Expenses', 'Deliveries'])] # 调用父类的 name_search 方法 - return super(ProductCategory, self).name_search(name, args=args, operator=operator, limit=limit) \ No newline at end of file + return super(ProductCategory, self).name_search(name, args=args, operator=operator, limit=limit) + + @api.model + def search(self, args, limit=100, offset=0, order=None, count=False): + # 添加过滤条件,确保只返回名称不在指定列表中的记录 + args += [('name', 'not in', ['Saleable', 'Expenses', 'Deliveries'])] + + # 调用父类的 search 方法 + return super(ProductCategory, self).search(args, limit=limit, offset=offset, order=order, count=count) \ No newline at end of file diff --git a/sf_base/static/js/customTable.js b/sf_base/static/js/customTable.js new file mode 100644 index 00000000..a3da6f4f --- /dev/null +++ b/sf_base/static/js/customTable.js @@ -0,0 +1,125 @@ +// 获取表格数据 +function getDomData() { + const dom = $('div[name=cutting_speed_ids]') + if (!dom.length) return + const table = dom.find('.o_list_table') + const thead = table.children('thead') + const tbody = table.children('tbody') + const tbody_child = tbody.children() + const hideTheadDom = thead.find('[data-name=process_capability]') + hideTheadDom.hide().next().hide() + hideTheadDom.before('精加工粗加工') + tbody_child.each(function () { + const dom = $(this).children('[name=process_capability]') + if(!dom.length) return + dom.css('cssText', 'display: none!important').next().css('cssText', 'display: none!important') + const isCu = dom.text() == '粗加工' // 是否粗加工 + const v = dom.next().text() // 切削速度 + dom.after(`${!isCu ? v : ''}${isCu ? v : ''}`) + setListenClick() + }) +return; + handleTbody(tbody, newTableData, ΦList, table) +} + +// 监听点击 +function setListenClick() { + $(document).click(function (e) { + if ($(e.target).attr('customSpeed')) { + const orginV = $('[customInput=1]').children('input').val() + $('[customInput=1]').parent().html(orginV) + const v = $(e.target).attr('val') + const is = $(e.target).attr('is') + $(e.target).html('') + const input = $('
') + input.children('input').val(v) + $(e.target).append(input) + input.children('input').focus() + input.children('input').select() + } else if ($(e.target).attr('customInput')) { + + } else { + const orginV = $('[customInput=1]').children('input').val() + $('[customInput=1]').parent().html(orginV) + const v = $(e.target).attr('val') + } + }) + $(document).off('change') // 防止重复绑定 + $(document).on('change', '[customInput] input', async function () { + $(this).parents('td').attr('val', $(this).val()) + $(this).parents('td').siblings('[customspeed]').attr('val', $(this).val()) + var eve1 = new Event('change') + var eve2 = new Event('input') + var eve3 = new Event('click') + let patchSpeedDom = $(this).parents('td').siblings('[name=cutting_speed]') + let patchProcessDom = $(this).parents('td').siblings('[name=process_capability]') + $(this).parents('td').siblings('[customspeed]').text('') // 清空其他加工类型的数据 + await timeOut(500) + patchProcessDom[0].dispatchEvent(eve3) + await timeOut(200) + const processVal = $(this).parent().attr('is') + patchProcessDom.find('select').val(`"${processVal}"`) // 设置源select的val为“加工类型 is”、 + patchProcessDom.attr("data-tooltip", `${processVal}`) + patchProcessDom.find('select')[0].dispatchEvent(eve1) + + patchSpeedDom[0].dispatchEvent(eve3) + await timeOut(200) + patchSpeedDom.find('input').val($(this).val()) + await timeOut(50) + patchSpeedDom.find('input')[0].dispatchEvent(eve2) + patchSpeedDom.find('input')[0].dispatchEvent(eve1) + }) + $(document).off('blur') // 防止重复绑定 + $(document).on('blur', '[customInput] input', async function () { + if(!$(this).length) return + + $(this).parents('td').siblings('[customspeed]').text('') // 清空其他加工类型的数据 + let patchProcessDom = $(this).parents('td').siblings('[name=process_capability]') + try { + patchProcessDom[0].dispatchEvent(new Event('click')) + const processVal = $(this).parent().attr('is') + patchProcessDom.find('select').val(`"${processVal}"`) // 设置源select的val为“加工类型 is”、 + patchProcessDom.attr("data-tooltip", `${processVal}`) + patchProcessDom.find('select')[0].dispatchEvent(new Event('change')) + } catch { + + } + + }) +} +function timeOut(time) { + return new Promise(resolve => { + setTimeout(() => { + resolve() + }, time) + }) +} + +function listenAdd() { + $('td.o_field_x2many_list_row_add a').click(async function () { + await timeOut(500) + const tr = $('.o_list_table').children('tbody').children('tr').eq(-2) + if(tr.children('td').eq(2).text() == '') { + const dom = tr.children('[name=process_capability]') + if(!dom.length) return + dom.css('cssText', 'display: none!important').next().css('cssText', 'display: none!important') + const isCu = dom.text() == '粗加工' // 是否粗加工 + const v = dom.next().text() // 切削速度 + dom.after(`${!isCu ? v : ''}${isCu ? v : ''}`) + } + }) +} + +function listenSave() { + $('.o_form_button_save').click( async function () { + await timeOut(1000) + if($(this).parent().next().length) return + $('th[customTh],td[cusomSpeed]').remove() + getDomData() + + }) +} + +listenAdd() +listenSave() +getDomData() diff --git a/sf_base/static/js/setTableWidth.js b/sf_base/static/js/setTableWidth.js new file mode 100644 index 00000000..571d46cf --- /dev/null +++ b/sf_base/static/js/setTableWidth.js @@ -0,0 +1,47 @@ +// 因为表格可以拖动设置宽度,所以需要用js设置初始宽度 +function setBasicParamTableWidth() { + // const _100px = 'th[data-name="cutting_blade_length"],th[data-name="cutting_blade_length"],th[data-name="name"],th[data-name="tip_handling_size"],th[data-name="cutting_depth_max"],th[data-name="diameter_inner_circle"],th[data-name="diameter_mounting_hole" ],th[data-name="radius_tip_re" ],th[data-name="is_chip_breaker"],th[data-name="chip_breaker_type_code"],th[data-name="blade_profile"]' + // const _65px = 'th[data-name="edge_angle"],th[data-name="relief_angle"],[data-name="total_length"],th[data-name="length"],th[data-name="thickness"],th[data-name="blade_number"]' + // const _80px = 'th[data-name="arbor_diameter"],th[data-name="head_height"],th[data-name="head_width"],th[data-name="head_length"],th[data-name="blade_diameter"],th[data-name="blade_length"] ,th[data-name="neck_length"] ,th[data-name="neck_diameter"] ,th[data-name="shank_diameter"],th[data-name="shank_length"],th[data-name="tip_diameter"],th[data-name="knife_tip_taper"],th[data-name="blade_helix_angle"] ,th[data-name="blade_width"],th[data-name="blade_depth"]' + // const _50px = 'th[data-name="pitch"],th[data-name="width"],th[data-name="height"]' + + const basicParamDom = $('.fixTableCss') + // const basicParamDom_100px = basicParamDom.find(_100px) // 四字以上 + // const basicParamDom_65px = basicParamDom.find(_65px) // 大概三个字加单位 + // const basicParamDom_80px = basicParamDom.find(_80px) // 大概四个字加单位 + // const basicParamDom_50px= basicParamDom.find(_50px) // 大概两个字加单位 + // + // basicParamDom_100px.css({'width': '100px', 'max-width': 'auto', ',min-width': 'auto'}) + // basicParamDom_65px.css({'width': '65px', 'max-width': 'auto', ',min-width': 'auto'}) + // basicParamDom_80px.css({'width': '80px', 'max-width': 'auto', ',min-width': 'auto'}) + // basicParamDom_50px.css({'width': '50px', 'max-width': 'auto', ',min-width': 'auto'}) + let dom = [] + try { + dom = basicParamDom.find('table').find('thead').children().children() + + } catch { + dom = [] + } + if (!dom) return + dom.each(function () { + if ($(this).hasClass('row_no') >= 0) { // 序号列 + // 不设置 通过css设置 + } + const text = $(this).text().split('(') + if ($(this).attr('data-name') == 'name' || text[0].length > 4) { + $(this).width('100px') + } else if(text[0].length == 4){ + $(this).width('80px') + } else if(text[0].length == 3){ + $(this).width('65px') + } else if(text[0].length == 2){ + $(this).width('50px') + } + + }) +} + +setBasicParamTableWidth() +$('.o_field_many2one_selection').on('click', $('#cutting_tool_material_id + ul'), function () { + setTimeout(setBasicParamTableWidth, 500) +}) diff --git a/sf_base/static/js/updateTable.js b/sf_base/static/js/updateTable.js new file mode 100644 index 00000000..beec2c94 --- /dev/null +++ b/sf_base/static/js/updateTable.js @@ -0,0 +1,159 @@ +// 获取表格数据 +function getDomData() { + const dom = $('#updateTable').prev() + if (!dom.length) return + const table = $('#updateTable').prev().find('.o_list_table') + const customTable = table.clone() + customTable.addClass('customTable') + table.parent().append(customTable) + table.hide() + const thead = customTable.children('thead') + const tbody = customTable.children('tbody') + const tableData = [] + const tbody_child = tbody.children() + + const tbody_child_len = tbody_child.length + + for (let v = 0; v < tbody_child_len; v++) { // 将数据取出来到tableData里面 + const data = tbody_child[v].innerText.split('\t') + // console.log('dom data',data) + const [index, deep, name, Φ, value] = data + tableData.push({index, deep, name, Φ, value}) + } + const ΦList = [...new Set(tableData.map(_ => _.name))] // ΦList去重 + const newTableData = {} + tableData.forEach(_ => { + const key = _.deep + '|' + _.Φ + !newTableData[key] ? newTableData[key] = {i: _.index} : ''; + if (_.Φ) { // 去除没有Φ的脏数据 + newTableData[key]['Φ' + _.Φ] = _.value + newTableData[key]['Φ' + _.Φ + 'i'] = _.index + } + }) + // console.log('qwdh',tableData, ΦList, newTableData); + + if (ΦList.filter(_ => _).length == 0) return; + handleThead(thead, ΦList) + + handleTbody(tbody, newTableData, ΦList, table) +} + +// 重新设置表头、 +function handleThead(thead, ΦList) { + const dom = thead.children().eq(0).children() + const len = dom.length + dom.eq(0).attr('rowspan', 2) + dom.eq(1).attr('rowspan', 2) + len == 5 ? dom.eq(2).attr('rowspan', 2) : '' + dom.eq(-2).attr('colspan', ΦList.length) + dom.eq(-1).remove() + + const tr = document.createElement('tr') + for (let v = 0; v < ΦList.length; v++) { + const th = document.createElement('th') + th.innerText = 'Φ' + ΦList[v] + tr.append(th) + } + thead.append(tr) +} + +// 重新设置表格 +function handleTbody(tbody, newTableData, ΦList, table) { + console.log(newTableData) + tbody.html('') + let i = 0 + const data = Object.keys(newTableData) + // data.sort((a, b) => { + // a = a.split('=')[1].split('%')[0] + // b = b.split('=')[1].split('%')[0] + // return a - b + // }) + // console.log('wqoqw ',ΦList) + data.forEach(_ => { + i++ + const tr = $('') + const td0 = $('') + td0.text(i) + tr.append(td0) + const lit = _.split('|') + // + const td1 = $('') + const td2 = $('') + td1.text(lit[0]) + td2.text(lit[1] || '') + tr.append(td1) + tr.append(td2) + ΦList.forEach(Φ => { + const td = $('') + td.text(newTableData[_]['Φ' + Φ]) + td.attr('col', newTableData[_]['Φ' + Φ + 'i']) + td.attr('val', newTableData[_]['Φ' + Φ]) + td.attr('coustomTd', 1) + tr.append(td) + }) + // // for (let j = 0; j < ΦList.length; j++) { + // // const td = document.createElement('td') + // // td.innerText = newTableData[data[v]][_] + // // th.append(td) + // // } + tbody.append(tr) + }) + // $(document).click(function (e) { + // if ($(e.target).attr('coustomTd')) { + // const orginV = $('[coustomInput=1]').children('input').val() + // $('[coustomInput=1]').parent().html(orginV) + // const v = $(e.target).attr('val') + // console.log($(e.target)); + // $(e.target).html('') + // const input = $('
') + // input.children('input').val(v) + // $(e.target).append(input) + // input.children('input').focus() + // input.children('input').select() + // } else if ($(e.target).attr('coustomInput')) { + // + // } else { + // const orginV = $('[coustomInput=1]').children('input').val() + // $('[coustomInput=1]').parent().html(orginV) + // const v = $(e.target).attr('val') + // } + // }) + // $(document).off('change') // 防止重复绑定 +// $(document).on('change', '[coustomInput] input', function () { +// $(this).parents('td').attr('val', $(this).val()); +// var eve1 = new Event('change'); +// var eve2 = new Event('input'); +// var eve3 = new Event('click'); +// const i = $(this).parents('td').attr('col'); +// let patchDom = table.find('tbody').children('tr').eq(i - 1); +// +// if (patchDom.length === 0) { +// console.error('No such row found'); +// return; +// } +// +// patchDom = patchDom.children().eq(-1); +// +// setTimeout(() => { +// if (patchDom.length === 0) { +// console.error('No such cell found'); +// return; +// } +// patchDom[0].dispatchEvent(eve3); // Simulate click event +// +// setTimeout(() => { +// patchDom = patchDom.find('input'); +// if (patchDom.length === 0) { +// console.error('No input found in the target cell'); +// return; +// } +// patchDom.val($(this).val()); +// patchDom[0].dispatchEvent(eve2); +// patchDom[0].dispatchEvent(eve1); +// }, 200); +// }, 500); +// }); + +} + +getDomData() \ No newline at end of file diff --git a/sf_base/static/src/scss/change.scss b/sf_base/static/src/scss/change.scss new file mode 100644 index 00000000..6102b815 --- /dev/null +++ b/sf_base/static/src/scss/change.scss @@ -0,0 +1,52 @@ +.o_list_renderer .o_list_table tbody > tr > td:not(.o_list_record_selector):not(.o_handle_cell):not(.o_list_button):not(.o_list_record_remove) { + white-space: nowrap !important; +} + +.text-truncate { + overflow: unset !important; + text-overflow: unset !important; + white-space: unset !important; +} + + +// 设置表格不超出页面宽度 +.o_form_view .o_field_widget .o_list_renderer { + width: calc(100% - 64px) !important; + margin:0 auto; + overflow: auto; +} + +// 表格针对处理 +.fixTableCss { + text-align: center; + .o_list_number_th,.o_list_number { + text-align: center!important; + } + .ui-sortable { + tr > td:first-child { + padding: 0!important; + } + } + .row_no { + padding: 0!important;; + width: 35px!important; + } + + th[data-name="total_length"],th[data-name="length"],th[data-name="thickness"] { + .flex-row-reverse { + span { + text-align: center; + } + } + } +} + +// 其他不能用js处理的表格 +.otherTableFix { + th[data-name="cutting_tool_material_id"] { + width: 100px!important; + } + th[data-name="ramping_angle_max"],th[data-name="ramping_angle_min"] { + width: 200px!important; + } +} \ No newline at end of file diff --git a/sf_base/views/tool_views.xml b/sf_base/views/tool_views.xml index 6b97f0ec..b2d53392 100644 --- a/sf_base/views/tool_views.xml +++ b/sf_base/views/tool_views.xml @@ -360,6 +360,7 @@ + @@ -380,6 +381,9 @@ + + + @@ -392,6 +396,8 @@ +
+ diff --git a/sf_bf_connect/models/process_status.py b/sf_bf_connect/models/process_status.py index 1bfdd356..09fecfae 100644 --- a/sf_bf_connect/models/process_status.py +++ b/sf_bf_connect/models/process_status.py @@ -36,6 +36,7 @@ class StatusChange(models.Model): # 使用super()来调用原始方法(在本例中为'sale.order'模型的'action_confirm'方法) try: res = super(StatusChange, self).action_confirm() + logging.info('原生方法返回结果:%s' % res) # 原有方法执行后,进行额外的操作(如调用外部API) process_start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') config = self.env['res.config.settings'].get_values() @@ -61,6 +62,7 @@ class StatusChange(models.Model): traceback_error = traceback.format_exc() logging.error("工厂加工同步订单状态失败:%s " % traceback_error) raise UserError(e) + logging.info('最终返回值:%s' % res) return res def action_cancel(self): diff --git a/sf_dlm_management/__manifest__.py b/sf_dlm_management/__manifest__.py index a5c894d4..9a94082a 100644 --- a/sf_dlm_management/__manifest__.py +++ b/sf_dlm_management/__manifest__.py @@ -9,7 +9,7 @@ """, 'category': 'sf', 'website': 'https://www.sf.jikimo.com', - 'depends': ['sf_sale', 'sf_dlm', 'sf_manufacturing'], + 'depends': ['sf_sale', 'sf_dlm', 'sf_manufacturing','jikimo_attachment_viewer'], 'data': [ 'data/stock_data.xml', 'views/product_template_management_view.xml', diff --git a/sf_hr/models/hr_employee.py b/sf_hr/models/hr_employee.py index e9826f29..065288de 100644 --- a/sf_hr/models/hr_employee.py +++ b/sf_hr/models/hr_employee.py @@ -11,6 +11,38 @@ class JkmPracticeEmployee(models.Model): we_id = fields.Char(string='企微ID', index=True) + @api.model_create_multi + def create(self, vals_list): + for val in vals_list: + if 'work_email' in val: + val["we_id"] = self._get_we_id(val.get('work_email')) + if val.get('user_id'): + user = self.env['res.users'].browse(val['user_id']) + user.write({'we_employee_id': val["we_id"]}) + return super(JkmPracticeEmployee, self).create(vals_list) + + def write(self, vals): + if 'work_email' in vals: + vals["we_id"] = self._get_we_id(vals.get('work_email')) + if self.user_id: + self.user_id.write({'we_employee_id': vals["we_id"]}) + return super(JkmPracticeEmployee, self).write(vals) + + def _get_we_id(self, work_email): + json1 = { + 'params': { + 'work_email': work_email + } + } + url = '/api/get/we_id/info' + config = self.env['res.config.settings'].get_values() + ret = requests.post((config['ims_url'] + url), json=json1, data={}) + result = ret.json()['result'] + if result['code'] == 200: + if result['we_id']: + return result['we_id'] + return None + def _employee_info_sync(self): url = '/api/get/organization' config = self.env['res.config.settings'].get_values() diff --git a/sf_maintenance/security/ir.model.access.csv b/sf_maintenance/security/ir.model.access.csv index 9a20f67f..8c1b0741 100644 --- a/sf_maintenance/security/ir.model.access.csv +++ b/sf_maintenance/security/ir.model.access.csv @@ -22,6 +22,10 @@ access_maintenance_equipment_agv_log,maintenance_equipment_agv_log,model_mainten access_maintenance_system_user,equipment.request system user,maintenance.model_maintenance_request,base.group_user,1,1,1,0 access_maintenance_wizard_system_user,maintenance.request.wizard system user,model_maintenance_request_wizard,base.group_user,1,1,1,0 +access_maintenance_sf_group_equipment_user,equipment.request_sf_group_equipment_user,maintenance.model_maintenance_request,sf_group_equipment_user,1,1,1,0 +access_maintenance_wizard_sf_group_equipment_user,maintenance_wizard_sf_group_equipment_user,model_maintenance_request_wizard,sf_group_equipment_user,1,1,1,0 +access_maintenance_sf_group_equipment_manager,equipment.request_sf_group_equipment_manager,maintenance.model_maintenance_request,sf_group_equipment_manager,1,1,1,0 +access_maintenance_wizard_sf_group_equipment_manager,maintenance_wizard_sf_group_equipment_manager,model_maintenance_request_wizard,sf_group_equipment_manager,1,1,1,0 access_maintenance_equipment_group_plan_dispatch,maintenance.equipment,maintenance.model_maintenance_equipment,sf_base.group_plan_dispatch,1,0,0,0 access_maintenance_equipment_oee_group_plan_dispatch,maintenance_equipment_oee,model_maintenance_equipment_oee,sf_base.group_plan_dispatch,1,0,0,0 diff --git a/sf_maintenance/views/maintenance_request_views.xml b/sf_maintenance/views/maintenance_request_views.xml index ad09c096..9353e80e 100644 --- a/sf_maintenance/views/maintenance_request_views.xml +++ b/sf_maintenance/views/maintenance_request_views.xml @@ -76,33 +76,61 @@ - - - - - - + + + + + + + + + 计划维保日期 + - + - - - + + + - - + + + + maintenance.request.view.tree.sf + maintenance.request + + + + + + + + + + + + maintenance.request.view.search.sf + maintenance.request + + + + + + @@ -110,7 +138,7 @@ 维保计划 maintenance.request - kanban,tree,form,pivot,graph,calendar + tree,kanban,form,pivot,graph,calendar {'default_user_id': uid} diff --git a/sf_manufacturing/__manifest__.py b/sf_manufacturing/__manifest__.py index 71f4027a..fd15858a 100644 --- a/sf_manufacturing/__manifest__.py +++ b/sf_manufacturing/__manifest__.py @@ -10,7 +10,7 @@ """, 'category': 'sf', 'website': 'https://www.sf.jikimo.com', - 'depends': ['sf_base', 'sf_maintenance', 'web_widget_model_viewer', 'sf_warehouse'], + 'depends': ['sf_base', 'sf_maintenance', 'web_widget_model_viewer', 'sf_warehouse','jikimo_attachment_viewer'], 'data': [ 'data/stock_data.xml', 'data/empty_racks_data.xml', diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index 573bb73f..c602fd8b 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -141,12 +141,12 @@ class MrpProduction(models.Model): ], string='工序状态', default='待装夹') # 零件图号 - part_number = fields.Char('零件图号', readonly=True) + part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True) # 上传零件图纸 - part_drawing = fields.Binary('零件图纸', readonly=True) + part_drawing = fields.Binary('零件图纸', related='product_id.machining_drawings', readonly=True) - quality_standard = fields.Binary('质检标准', readonly=True) + quality_standard = fields.Binary('质检标准', related='product_id.quality_standard', readonly=True) @api.depends('product_id.manual_quotation') def _compute_manual_quotation(self): diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index 9c6b2a5c..37d42b42 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -59,7 +59,8 @@ class ResMrpWorkOrder(models.Model): compute='_compute_state', store=True, default='pending', copy=False, readonly=True, recursive=True, index=True, tracking=True) - delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效') + delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效', + tracking=True) @api.depends('production_id.manual_quotation') def _compute_manual_quotation(self): @@ -1846,7 +1847,7 @@ class WorkPieceDelivery(models.Model): return is_free else: raise UserError("接驳站暂未反馈站点实时状态,请稍后再试") - + def delivery_avg(self): is_agv_task_dispatch = self.env['ir.config_parameter'].sudo().get_param('is_agv_task_dispatch') if is_agv_task_dispatch: diff --git a/sf_manufacturing/models/product_template.py b/sf_manufacturing/models/product_template.py index 3b8ed0a6..b7aa1adb 100644 --- a/sf_manufacturing/models/product_template.py +++ b/sf_manufacturing/models/product_template.py @@ -16,6 +16,12 @@ from OCC.Extend.DataExchange import write_stl_file class ResProductMo(models.Model): _inherit = 'product.template' + def _get_machining_precision(self): + machinings = self.env['sf.machining.accuracy'].sudo().search([]) + + list = [(m.sync_id, m.name) for m in machinings] + return list + model_file = fields.Binary('模型文件') categ_type = fields.Selection(string='产品的类别', related='categ_id.type', store=True) model_name = fields.Char('模型名称') @@ -23,12 +29,7 @@ class ResProductMo(models.Model): model_width = fields.Float('模型宽(mm)', digits=(16, 3)) model_height = fields.Float('模型高(mm)', digits=(16, 3)) model_volume = fields.Float('模型体积(m³)') - model_machining_precision = fields.Selection([ - ('0.10', '±0.10mm'), - ('0.05', '±0.05mm'), - ('0.03', '±0.03mm'), - ('0.02', '±0.02mm'), - ('0.01', '±0.01mm')], string='加工精度') + model_machining_precision = fields.Selection(selection=_get_machining_precision, string='加工精度') model_processing_panel = fields.Char('模型加工面板') model_remark = fields.Char('模型备注说明') length = fields.Float('长(mm)', digits=(16, 3)) @@ -776,8 +777,6 @@ class ResProductMo(models.Model): part_number = fields.Char(string='零件图号', readonly=True) machining_drawings = fields.Binary('2D加工图纸', readonly=True) quality_standard = fields.Binary('质检标准', readonly=True) - machining_drawings_name = fields.Char('2D加工图纸名', readonly=True) - quality_standard_name = fields.Char('质检标准名', readonly=True) @api.constrains('tool_length') def _check_tool_length_size(self): @@ -839,10 +838,10 @@ class ResProductMo(models.Model): else: return self.env.ref('sf_dlm.product_uom_cubic_millimeter') - def attachment_update(self, name, res_id, res_field): + def attachment_update(self, name, res_id, res_field, mimetype): attachment_info = self.env['ir.attachment'].sudo().search( [('res_id', '=', res_id), ('res_field', '=', res_field)], limit=1) - attachment_info.write({'name': name}) + attachment_info.write({'name': name, 'mimetype': mimetype}) # 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品 def product_create(self, product_id, item, order_id, order_number, i): @@ -882,8 +881,6 @@ class ResProductMo(models.Model): 'manual_quotation': item['manual_quotation'] or False, 'part_number': item.get('part_number') or '', 'active': True, - 'machining_drawings_name': item['machining_drawings_name'], - 'quality_standard_name': item['quality_standard_name'], 'machining_drawings': '' if not item['machining_drawings'] else base64.b64decode( item['machining_drawings']), 'quality_standard': '' if not item['quality_standard'] else base64.b64decode(item['quality_standard']), @@ -894,12 +891,12 @@ class ResProductMo(models.Model): vals.update({'taxes_id': [(6, 0, [int(tax_id)])]}) copy_product_id.sudo().write(vals) product_id.product_tmpl_id.active = False - if item['machining_drawings'] and item['machining_drawings_name']: + if item['machining_drawings'] and item['machining_drawings_name'] and item['machining_drawings_mimetype']: self.attachment_update(item['machining_drawings_name'], copy_product_id.product_tmpl_id.id, - 'machining_drawings') - if item['quality_standard'] and item['quality_standard_name']: + 'machining_drawings', item['machining_drawings_mimetype']) + if item['quality_standard'] and item['quality_standard_name'] and item['quality_standard_mimetype']: self.attachment_update(item['quality_standard_name'], copy_product_id.product_tmpl_id.id, - 'quality_standard') + 'quality_standard', item['quality_standard_mimetype']) return copy_product_id def _get_ids(self, param): diff --git a/sf_manufacturing/models/stock.py b/sf_manufacturing/models/stock.py index 02161b4a..40d159d5 100644 --- a/sf_manufacturing/models/stock.py +++ b/sf_manufacturing/models/stock.py @@ -272,21 +272,6 @@ class StockRule(models.Model): workorder_duration += workorder.duration_expected sale_order = self.env['sale.order'].sudo().search([('name', '=', production.origin)]) - # 根据销售订单号查询快速订单 - quick_easy_order = self.env['quick.easy.order'].sudo().search([('sale_order_id', '=', sale_order.id)]) - if quick_easy_order: - production.write({'part_number': quick_easy_order.part_drawing_number, - 'part_drawing': quick_easy_order.machining_drawings}) - else: - production.write({'part_number': production.product_id.part_number, - 'part_drawing': production.product_id.machining_drawings, - 'quality_standard': production.product_id.quality_standard}) - if production.product_id.machining_drawings and production.product_id.machining_drawings_name: - self.attachment_update(production.product_id.machining_drawings_name, production.id, - 'part_drawing') - if production.product_id.quality_standard and production.product_id.quality_standard_name: - self.attachment_update(production.product_id.quality_standard_name, production.id, - 'quality_standard') if sale_order: # sale_order.write({'schedule_status': 'to schedule'}) self.env['sf.production.plan'].sudo().with_company(company_id).create({ diff --git a/sf_manufacturing/views/mrp_workorder_view.xml b/sf_manufacturing/views/mrp_workorder_view.xml index 143ac4ab..8d3c6c4f 100644 --- a/sf_manufacturing/views/mrp_workorder_view.xml +++ b/sf_manufacturing/views/mrp_workorder_view.xml @@ -102,7 +102,7 @@ current [('state', '!=', 'cancel'),('schedule_state', '=', '已排')] {'search_default_product': 1, 'search_default_workcenter_id': - active_id,'search_default_filter_order_warning':1,'search_default_filter_order_overdue':1} + active_id,'search_default_filter_order_warning':1,'search_default_filter_order_overdue':1,'search_default_filter_order_normal':1}

@@ -125,9 +125,9 @@ mrp.workorder - - - + + + @@ -480,6 +480,15 @@ + + + + + + + + + @@ -533,6 +542,13 @@ + + + + + + + @@ -603,18 +619,6 @@ mrp.group_mrp_manager,sf_base.group_sf_mrp_manager,sf_base.group_sf_equipment_user,sf_base.group_sf_order_user - - - - - - - - - - @@ -655,6 +659,7 @@ + diff --git a/sf_message/data/cron_data.xml b/sf_message/data/cron_data.xml index 736e3e91..9cddd04c 100644 --- a/sf_message/data/cron_data.xml +++ b/sf_message/data/cron_data.xml @@ -13,6 +13,19 @@ + + 检查销售订单是否完成并恢复正常时效 + + code + model._recover_sale_time_warning_func() + 10 + minutes + -1 + + + + + 检查工单是否已逾期预警和逾期 diff --git a/sf_message/data/template_data.xml b/sf_message/data/template_data.xml index 057dc009..beb37771 100644 --- a/sf_message/data/template_data.xml +++ b/sf_message/data/template_data.xml @@ -36,7 +36,7 @@ markdown normal ### 销售订单逾期预警 -事项:共有[{{warning_num}}]({{url}})个销售订单有逾期风险 +事项:[共有{{warning_num}}个销售订单有逾期风险]({{url}}) @@ -48,7 +48,7 @@ markdown urgent ### 销售订单已逾期提醒 -事项:共有[{{overdue_num}}]({{url}})个销售订单已逾期 +事项:[共有{{overdue_num}}个销售订单已逾期]({{url}}) @@ -97,7 +97,7 @@ timing normal ### 工单逾期预警 -事项:共有[{{warning_num}}]({{url}})工单有逾期风险 +事项:[共有{{warning_num}}个工单有逾期风险]({{url}}) @@ -109,7 +109,7 @@ timing normal ### 工单已逾期提醒 -事项:共有[{{overdue_num}}]({{url}})工单已逾期 +事项:[共有{{overdue_num}}个工单已逾期]({{url}}) @@ -121,7 +121,7 @@ timing normal ### 工单逾期预警 -事项:共有[{{warning_num}}]({{url}})工单有逾期风险 +事项:[共有{{warning_num}}个工单有逾期风险]({{url}}) @@ -133,7 +133,7 @@ timing normal ### 工单已逾期提醒 -事项:共有[{{overdue_num}}]({{url}})工单已逾期 +事项:[共有{{overdue_num}}个工单已逾期]({{url}}) @@ -145,7 +145,7 @@ timing normal ### 工单逾期预警 -事项:共有[{{warning_num}}]({{url}})工单有逾期风险 +事项:[共有{{warning_num}}个工单有逾期风险]({{url}}) @@ -157,7 +157,7 @@ timing normal ### 工单已逾期提醒 -事项:共有[{{overdue_num}}]({{url}})工单已逾期 +事项:[共有{{overdue_num}}个工单已逾期]({{url}}) @@ -169,7 +169,7 @@ timing normal ### 工单逾期预警 -事项:共有[{{warning_num}}]({{url}})工单有逾期风险 +事项:[共有{{warning_num}}个工单有逾期风险]({{url}}) @@ -181,7 +181,7 @@ timing normal ### 工单已逾期提醒 -事项:共有[{{overdue_num}}]({{url}})工单已逾期 +事项:[共有{{overdue_num}}个工单已逾期]({{url}}) @@ -262,7 +262,7 @@ markdown normal ### 待质量判定提醒 -事项:共有[{{judge_num}}]({{url}})个工单需判定质量结果 +事项:[共有{{judge_num}}个工单需判定质量结果]({{url}}) 设备故障 diff --git a/sf_message/models/sf_message_sale.py b/sf_message/models/sf_message_sale.py index 3fd57f8a..58d1a021 100644 --- a/sf_message/models/sf_message_sale.py +++ b/sf_message/models/sf_message_sale.py @@ -34,18 +34,19 @@ class SFMessageSale(models.Model): picking_id.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id).ids purchase_order_id.extend(purchase_order_ids) if purchase_order_id: - purchase_order_list = self.env['purchase.order'].search([('id', 'in', purchase_order_id)]) + purchase_order_list = self.env['purchase.order'].sudo().search([('id', 'in', purchase_order_id)]) for purchase_order_info in purchase_order_list: purchase_order_info.add_queue('坯料采购提醒') except Exception as e: logging.info('add_queue error:%s' % e) + logging.info('action_confirm res:%s' % res) return res # 继承并重写jikimo.message.dispatch的_get_message() def _get_message(self, message_queue_ids): contents = [] bussiness_node = None - url = self.env['ir.config_parameter'].get_param('web.base.url') + url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') current_time_strf = datetime.now().strftime("%Y-%m-%d %H:%M:%S") current_time = self.env['sf.sync.common'].sudo().get_add_time(current_time_strf) current_time_datetime = datetime.strptime(current_time, '%Y-%m-%d %H:%M:%S') @@ -60,7 +61,7 @@ class SFMessageSale(models.Model): contents.append(content) elif item.message_template_id.bussiness_node_id.name == '确认接单': content = super(SFMessageSale, self)._get_message(item) - sale_order_line = self.env['sale.order.line'].search([('order_id', '=', int(item.res_id))]) + sale_order_line = self.env['sale.order.line'].sudo().search([('order_id', '=', int(item.res_id))]) product = sale_order_line[0].product_id.name if len(sale_order_line) == 1 else '%s...' % \ sale_order_line[ 0].product_id.name @@ -100,42 +101,49 @@ class SFMessageSale(models.Model): # # 销售订单逾期预警和已逾期 def _overdue_or_warning_func(self): - today = datetime.today().date() - deadline_check = today + timedelta(days=1) - logging.info(f"today: {today}, deadline_check: {deadline_check}") + current_time_strf = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + current_time = self.env['sf.sync.common'].sudo().get_add_time(current_time_strf) + today_str = datetime.strptime(current_time, '%Y-%m-%d %H:%M:%S') + today = today_str.strftime("%Y-%m-%d") + # 计算下一天的日期 + deadline_check_str = today_str + timedelta(days=1) + deadline_check = deadline_check_str.strftime("%Y-%m-%d") + logging.info( + f"today: {today}, deadline_check: {deadline_check},current_time_strf: {current_time_strf}, current_time: {current_time}'") sale_order = self.sudo().search( [('state', 'in', ['sale']), ('deadline_of_delivery', '!=', False), ('delivery_status', '!=', 'full')]) for item in sale_order: - production = self.env['mrp.production'].search([('origin', '=', item.name)]) + production = self.env['mrp.production'].sudo().search([('origin', '=', item.name)]) production_not_done = production.filtered(lambda p: p.state not in ['done', 'scrap', 'cancel']) production_done_count = len(production.filtered(lambda p: p.state in ['done', 'scrap', 'cancel'])) + deadline_of_delivery = item.deadline_of_delivery.strftime("%Y-%m-%d") if (len(production_not_done) >= 1 and len(production_not_done) != item.mrp_production_count) or len( production_not_done) != production_done_count: - logging.info("-----不等于----") - logging.info(f"name: {item.name}") - logging.info( - f"production_not_done: {len(production_not_done)}, production_done_count: {production_done_count}") - logging.info(f"deadline_of_delivery: {item.deadline_of_delivery}") - if deadline_check == item.deadline_of_delivery and item.delivery_warning not in ['warning']: + # logging.info("-----不等于----") + # logging.info(f"name: {item.name}") + # logging.info( + # f"production_not_done: {len(production_not_done)}, production_done_count: {production_done_count}") + # logging.info(f"deadline_of_delivery: {item.deadline_of_delivery}") + if deadline_check == deadline_of_delivery and item.delivery_warning not in ['warning']: item.delivery_warning = 'warning' - elif today >= item.deadline_of_delivery and item.delivery_warning not in ['overdue']: + elif today >= deadline_of_delivery and item.delivery_warning not in ['overdue']: item.delivery_warning = 'overdue' elif production_done_count == item.mrp_production_count: - logging.info("-----等于----") - logging.info(f"name: {item.name}") - logging.info( - f"production_not_done: {len(production_not_done)}, production_done_count: {production_done_count}") - logging.info(f"deadline_of_delivery: {item.deadline_of_delivery}") + # logging.info("-----等于----") + # logging.info(f"name: {item.name}") + # logging.info( + # f"production_not_done: {len(production_not_done)}, production_done_count: {production_done_count}") + # logging.info(f"deadline_of_delivery: {item.deadline_of_delivery}") if item.delivery_status in ['pending', 'partial']: - if deadline_check == item.deadline_of_delivery and item.delivery_warning not in ['warning']: + if deadline_check == deadline_of_delivery and item.delivery_warning not in ['warning']: item.delivery_warning = 'warning' - elif today >= item.deadline_of_delivery and item.delivery_warning not in ['overdue']: + elif today >= deadline_of_delivery and item.delivery_warning not in ['overdue']: item.delivery_warning = 'overdue' else: - logging.info("-----1111111----") - logging.info(f"name: {item.name}") - logging.info( - f"production_not_done: {len(production_not_done)}, production_done_count: {production_done_count}") + # logging.info("-----1111111----") + # logging.info(f"name: {item.name}") + # logging.info( + # f"production_not_done: {len(production_not_done)}, production_done_count: {production_done_count}") continue # 获取业务节点 business_node_ids = { @@ -146,11 +154,11 @@ class SFMessageSale(models.Model): for wo in overdue_orders: business_node_id = business_node_ids.get(wo.delivery_warning) if business_node_id: - message_template = self.env["jikimo.message.template"].search([ + message_template = self.env["jikimo.message.template"].sudo().search([ ("model", "=", self._name), ("bussiness_node_id", "=", business_node_id) ], limit=1) - sale_order_has = self.env['jikimo.message.queue'].search([ + sale_order_has = self.env['jikimo.message.queue'].sudo().search([ ('res_id', '=', wo.id), ('message_status', '=', 'pending'), ('message_template_id', '=', message_template.id) @@ -158,3 +166,32 @@ class SFMessageSale(models.Model): if not sale_order_has: message_name = '销售订单逾期预警' if wo.delivery_warning == 'warning' else '销售订单已逾期' wo.add_queue(message_name) + if wo.delivery_warning == 'overdue': + # 把消息队列中销售订单预警的状态改为取消发送 + business_node_id_warning = business_node_ids['warning'] + if business_node_id_warning: + message_template_warning = self.env["jikimo.message.template"].search([ + ("model", "=", self._name), + ("bussiness_node_id", "=", business_node_id_warning) + ], limit=1) + if message_template_warning: + sale_order_warning = self.env['jikimo.message.queue'].search([ + ('res_id', '=', wo.id), + ('message_status', '=', 'pending'), + ('message_template_id', '=', message_template_warning.id) + ]) + if sale_order_warning: + logging.info('取消发送:%s' % sale_order_warning) + sale_order_warning.write({'message_status': 'cancel'}) + + def _recover_sale_time_warning_func(self): + sale_order_done = self.sudo().search([('state', 'in', ['sale']), ('delivery_status', '=', 'full')]) + sale_order_overdue = sale_order_done.filtered(lambda x: x.delivery_warning in ['overdue', 'warning']) + if sale_order_overdue: + sale_order_overdue.write({'delivery_warning': 'normal'}) + message_queue_ids = self.env["jikimo.message.queue"].sudo().search([ + ("message_status", "=", "pending"), + ("res_id", "in", [item.id for item in sale_order_overdue]) + ]) + if message_queue_ids: + message_queue_ids.write({'message_status': 'cancel'}) diff --git a/sf_message/models/sf_message_stock_picking.py b/sf_message/models/sf_message_stock_picking.py index 9d7c7f0e..6e80c670 100644 --- a/sf_message/models/sf_message_stock_picking.py +++ b/sf_message/models/sf_message_stock_picking.py @@ -29,14 +29,14 @@ class SFMessageStockPicking(models.Model): [('origin', '=', record.origin), ('state', '!=', 'done'), ('picking_type_id.sequence_code', '=', 'SFP')]) if not stock_picking_sfp: - stock_picking_send = self.env["jikimo.message.queue"].search([('res_id', '=', record.id)]) + stock_picking_send = self.env["jikimo.message.queue"].sudo().search([('res_id', '=', record.id)]) if not stock_picking_send: record.add_queue('订单发货提醒') def deal_stock_picking_sfp(self, message_queue_id): # 处理订单发货提醒 content = None - stock_picking = self.env['stock.picking'].search([('id', '=', int(message_queue_id.res_id))]) - stock_picking_out = self.env['stock.picking'].search( + stock_picking = self.env['stock.picking'].sudo().search([('id', '=', int(message_queue_id.res_id))]) + stock_picking_out = self.env['stock.picking'].sudo().search( [('origin', '=', stock_picking.origin), ('state', '=', 'assigned'), ('picking_type_id.sequence_code', '=', 'OUT')]) if stock_picking_out and len(stock_picking_out) > 0: @@ -54,10 +54,10 @@ class SFMessageStockPicking(models.Model): i = 0 if message_queue_id.message_template_id.name == '坯料发料提醒': content = message_queue_id.message_template_id.content - stock_picking_line = self.env['stock.picking'].search([('id', '=', int(message_queue_id.res_id))]) - mrp_production_info = self.env['mrp.production'].search( + stock_picking_line = self.env['stock.picking'].sudo().search([('id', '=', int(message_queue_id.res_id))]) + mrp_production_info = self.env['mrp.production'].sudo().search( [('name', '=', stock_picking_line.origin)]) - mrp_production_list = self.env['mrp.production'].search( + mrp_production_list = self.env['mrp.production'].sudo().search( [('product_id', '=', mrp_production_info.product_id.id)]) for mrp_production_line in mrp_production_list: picking_ids = mrp_production_line.picking_ids @@ -87,9 +87,9 @@ class SFMessageStockPicking(models.Model): return super(SFMessageStockPicking, self).get_special_url(id, tmplate_name, special_name, model_id) def request_url(self): - url = self.env['ir.config_parameter'].get_param('web.base.url') + url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') action_id = self.env.ref('stock.stock_picking_type_action').id - menu_id = self.env['ir.model.data'].search([('name', '=', 'module_theme_treehouse')]).id + menu_id = self.env['ir.model.data'].sudo().search([('name', '=', 'module_theme_treehouse')]).id # 查询参数 params = {'menu_id': menu_id, 'action': action_id, 'model': 'stock.picking', 'view_type': 'kanban'} @@ -100,9 +100,9 @@ class SFMessageStockPicking(models.Model): return full_url def request_url1(self, id): - url = self.env['ir.config_parameter'].get_param('web.base.url') + url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') action_id = self.env.ref('stock.action_picking_tree_all').id - menu_id = self.env['ir.model.data'].search([('name', '=', 'module_theme_treehouse')]).id + menu_id = self.env['ir.model.data'].sudo().search([('name', '=', 'module_theme_treehouse')]).id # 查询参数 params = {'id': id, 'menu_id': menu_id, 'action': action_id, 'model': 'stock.picking', 'view_type': 'form'} diff --git a/sf_message/models/sf_message_workorder.py b/sf_message/models/sf_message_workorder.py index c531e013..d5186797 100644 --- a/sf_message/models/sf_message_workorder.py +++ b/sf_message/models/sf_message_workorder.py @@ -26,7 +26,7 @@ class SFMessageWork(models.Model): contents = [] product_id = [] bussiness_node = None - url = self.env['ir.config_parameter'].get_param('web.base.url') + url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') current_time_strf = datetime.now().strftime("%Y-%m-%d %H:%M:%S") current_time = self.env['sf.sync.common'].sudo().get_add_time(current_time_strf) current_time_datetime = datetime.strptime(current_time, '%Y-%m-%d %H:%M:%S') @@ -39,8 +39,8 @@ class SFMessageWork(models.Model): for message_queue_id in message_queue_ids: if message_queue_id.message_template_id.name == '工单已下发通知': content = message_queue_id.message_template_id.content - mrp_workorder_line = self.env['mrp.workorder'].search([('id', '=', int(message_queue_id.res_id))]) - mrp_workorder_list = self.env['mrp.workorder'].search( + mrp_workorder_line = self.env['mrp.workorder'].sudo().search([('id', '=', int(message_queue_id.res_id))]) + mrp_workorder_list = self.env['mrp.workorder'].sudo().search( [('product_id', '=', mrp_workorder_line.product_id.id), ('state', '=', 'ready'), ('routing_type', '=', '装夹预调')]) if len(mrp_workorder_list) > 0 and mrp_workorder_line.product_id.id not in product_id: @@ -61,20 +61,32 @@ class SFMessageWork(models.Model): second=0, microsecond=0 ) - logging.info(current_time) - logging.info(target_time) - logging.info(target_time - time_range) - logging.info(target_time + time_range) + # logging.info(current_time) + # logging.info(target_time) + # logging.info(target_time - time_range) + # logging.info(target_time + time_range) if target_time - time_range <= current_time_datetime <= target_time + time_range: search_condition = [ ('delivery_warning', '=', 'warning')] if bussiness_node in template_names['预警'] else [ ('delivery_warning', '=', 'overdue')] - record = self.sudo().search(search_condition + [('id', '=', int(item.res_id))]) + record = self.sudo().search(search_condition + [('id', '=', int(message_queue_id.res_id))]) if record: + bussiness_node = item.bussiness_node_id.name + # 分割业务节点名称,提取出业务节点关键字 + business_node_key = bussiness_node.split('工单')[0].strip() + workcenter_mapping = { + '装夹预调': '工件装夹', + 'CNC加工': '自动生产', + '解除装夹': '工件拆卸', + '表面工艺': '表面工艺外协', + } + workcenter_name = workcenter_mapping.get(business_node_key) + active_id = self.env['mrp.workcenter'].search([('name', 'ilike', workcenter_name)], + limit=1).id i += 1 if i >= 1: action_id = self.env.ref('sf_message.mrp_workorder_action_notify').id - url_with_id = f"{url}/web#view_type=list&action={action_id}" + url_with_id = f"{url}/web#view_type=list&action={action_id}&active_id={active_id}" content_template = content.replace('{{url}}', url_with_id) if bussiness_node in template_names['预警']: content = content_template.replace('{{warning_num}}', str(i)) @@ -84,12 +96,13 @@ class SFMessageWork(models.Model): return contents def request_url(self): - url = self.env['ir.config_parameter'].get_param('web.base.url') - action_id = self.env.ref('sf_message.mrp_workorder_action_notify').id - menu_id = self.env['ir.model.data'].search([('name', '=', 'module_stock_dropshipping')]).id + url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') + action_id = self.env.ref('sf_message.mrp_workorder_issued_action').id + menu_id = self.env['ir.model.data'].sudo().search([('name', '=', 'module_stock_dropshipping')]).id + active_id = self.env['mrp.workcenter'].sudo().search([('name', '=', '工件装夹中心')]).id # 查询参数 params = {'menu_id': menu_id, 'action': action_id, 'model': 'mrp.workorder', - 'view_type': 'list', 'active_id': 1} + 'view_type': 'list', 'active_id': active_id} # 拼接查询参数 query_string = urlencode(params) # 拼接URL @@ -97,7 +110,8 @@ class SFMessageWork(models.Model): return full_url def _overdue_or_warning_func(self): - workorders = self.env['mrp.workorder'].search([("state", "in", ["ready", "progress", "to be detected"])]) + workorders = self.env['mrp.workorder'].search( + [("state", "in", ["ready", "progress", "to be detected"]), ('schedule_state', '=', '已排')]) grouped_workorders = {} for workorder in workorders: routing_type = workorder.routing_type @@ -116,21 +130,25 @@ class SFMessageWork(models.Model): date_planned_finished = datetime.strptime(date_planned_finished_str, '%Y-%m-%d %H:%M:%S') twelve_hours_ago = current_time_datetime - timedelta(hours=12) if current_time_datetime >= date_planned_finished and item.delivery_warning not in ['overdue']: - logging.info("------overdue-------") - logging.info(f"Workorder: {item.production_id.name}, Current Time: {current_time_datetime}, " - f"Planned Finish: {date_planned_finished}") + # logging.info("------overdue-------") + # logging.info(f"Workorder: {item.production_id.name}, Current Time: {current_time_datetime}, " + # f"Planned Finish: {date_planned_finished}") item.delivery_warning = 'overdue' elif twelve_hours_ago <= current_time_datetime <= date_planned_finished and item.delivery_warning not in [ 'warning']: - logging.info("------warning-------") - logging.info(f"Workorder: {item.production_id.name}, Current Time: {current_time_datetime}, " - f"Planned Finish: {date_planned_finished}") + # logging.info("------warning-------") + # logging.info(f"Workorder: {item.production_id.name}, Current Time: {current_time_datetime}, " + # f"Planned Finish: {date_planned_finished}") item.delivery_warning = 'warning' business_node_ids = { - '装夹预调': self.env.ref('sf_message.bussiness_mrp_workorder_pre_overdue_warning').id, - 'CNC加工': self.env.ref('sf_message.bussiness_mrp_workorder_cnc_overdue_warning').id, - '解除装夹': self.env.ref('sf_message.bussiness_mrp_workorder_unclamp_overdue_warning').id, - '表面工艺': self.env.ref('sf_message.bussiness_mrp_workorder_surface_overdue_warning').id, + '装夹预调_overdue': self.env.ref('sf_message.bussiness_mrp_workorder_pre_overdue').id, + '装夹预调_warning': self.env.ref('sf_message.bussiness_mrp_workorder_pre_overdue_warning').id, + 'CNC加工_overdue': self.env.ref('sf_message.bussiness_mrp_workorder_cnc_overdue').id, + 'CNC加工_warning': self.env.ref('sf_message.bussiness_mrp_workorder_cnc_overdue_warning').id, + '解除装夹_overdue': self.env.ref('sf_message.bussiness_mrp_workorder_unclamp_overdue').id, + '解除装夹_warning': self.env.ref('sf_message.bussiness_mrp_workorder_unclamp_overdue_warning').id, + '表面工艺_overdue': self.env.ref('sf_message.bussiness_mrp_workorder_surface_overdue').id, + '表面工艺_warning': self.env.ref('sf_message.bussiness_mrp_workorder_surface_overdue_warning').id, } message_templates = {key: self.env["jikimo.message.template"].sudo().search([ ("model", "=", self._name), @@ -138,13 +156,17 @@ class SFMessageWork(models.Model): ]) for key in business_node_ids} for item in orders: if item.delivery_warning in ['overdue', 'warning']: - bussiness_node_id = business_node_ids.get(item.routing_type) - if bussiness_node_id and message_templates[item.routing_type]: - message_queue_ids = self.env["jikimo.message.queue"].sudo().search([ - ("message_template_id", "=", message_templates[item.routing_type].id), - ("message_status", "=", "pending"), - ("res_id", "=", item.id) - ]) + warning_type = 'warning' if item.delivery_warning == 'warning' else 'overdue' + key = f"{item.routing_type}_{warning_type}" + bussiness_node_id = business_node_ids.get(key, None) + if bussiness_node_id: + message_template = message_templates.get(key) + if message_template and message_template.id: + message_queue_ids = self.env["jikimo.message.queue"].sudo().search([ + ("message_template_id", "=", message_template.id), + ("message_status", "=", "pending"), + ("res_id", "=", item.id) + ], limit=1) if not message_queue_ids: overdue_message = '工单已逾期' if item.delivery_warning == 'overdue' else '工单逾期预警' queue_method_name = f'add_queue' @@ -152,8 +174,33 @@ class SFMessageWork(models.Model): args = [f'{item.routing_type}{overdue_message}'] # 获取add_queue方法并调用它,传入参数列表 getattr(item, queue_method_name)(*args) + if item.delivery_warning == 'overdue': + # 把消息队列中销售订单预警的状态改为取消发送 + key = f"{item.routing_type}_{'warning'}" + business_node_id_warning = business_node_ids.get(key, None) + if business_node_id_warning: + message_template_warning = self.env["jikimo.message.template"].search([ + ("model", "=", self._name), + ("bussiness_node_id", "=", business_node_id_warning) + ], limit=1) + if message_template_warning: + work_order_warning = self.env['jikimo.message.queue'].search([ + ('res_id', '=', item.id), + ('message_status', '=', 'pending'), + ('message_template_id', '=', message_template_warning.id) + ]) + if work_order_warning: + logging.info('取消发送:%s' % work_order_warning) + work_order_warning.write({'message_status': 'cancel'}) def _recover_time_warning_func(self): workorder_done = self.env['mrp.workorder'].search([("state", "in", ["done", "rework", "cancel"])]) workorder_overdue = workorder_done.filtered(lambda x: x.delivery_warning in ['overdue', 'warning']) - workorder_overdue.write({'delivery_warning': 'normal'}) + if workorder_overdue: + workorder_overdue.write({'delivery_warning': 'normal'}) + message_queue_ids = self.env["jikimo.message.queue"].sudo().search([ + ("message_status", "=", "pending"), + ("res_id", "in", [item.id for item in workorder_overdue]) + ]) + if message_queue_ids: + message_queue_ids.write({'message_status': 'cancel'}) diff --git a/sf_message/security/ir.model.access.csv b/sf_message/security/ir.model.access.csv index 05a7366c..4aa153e6 100644 --- a/sf_message/security/ir.model.access.csv +++ b/sf_message/security/ir.model.access.csv @@ -5,24 +5,28 @@ access_jikimo_message_template_group_purchase,jikimo_message_template,jikimo_mes access_jikimo_message_template_group_sf_stock_user,jikimo_message_template,jikimo_message_notify.model_jikimo_message_template,sf_base.group_sf_stock_user,1,1,1,0 access_jikimo_message_template_group_sf_order_user,jikimo_message_template,jikimo_message_notify.model_jikimo_message_template,sf_base.group_sf_order_user,1,1,1,0 access_jikimo_message_template_group_sf_tool_user,jikimo_message_template,jikimo_message_notify.model_jikimo_message_template,sf_base.group_sf_tool_user,1,1,1,0 +access_jikimo_message_template_group_purchase_director,jikimo_message_template,jikimo_message_notify.model_jikimo_message_template,sf_base.group_purchase_director,1,1,1,0 access_jikimo_message_bussiness_node_group_sale_salemanager,jikimo_message_bussiness_node,jikimo_message_notify.model_jikimo_message_bussiness_node,sf_base.group_sale_salemanager,1,1,1,0 access_jikimo_message_bussiness_node_group_purchase,jikimo_message_bussiness_node,jikimo_message_notify.model_jikimo_message_bussiness_node,sf_base.group_purchase,1,1,1,0 access_jikimo_message_bussiness_node_group_sf_stock_user,jikimo_message_bussiness_node,jikimo_message_notify.model_jikimo_message_bussiness_node,sf_base.group_sf_stock_user,1,1,1,0 access_jikimo_message_bussiness_node_group_sf_order_user,jikimo_message_bussiness_node,jikimo_message_notify.model_jikimo_message_bussiness_node,sf_base.group_sf_order_user,1,1,1,0 access_jikimo_message_bussiness_node_group_sf_tool_user,jikimo_message_bussiness_node,jikimo_message_notify.model_jikimo_message_bussiness_node,sf_base.group_sf_tool_user,1,1,1,0 +access_jikimo_message_bussiness_node_group_purchase_director,jikimo_message_bussiness_node,jikimo_message_notify.model_jikimo_message_bussiness_node,sf_base.group_purchase_director,1,1,1,0 access_jikimo_message_queue_group_sale_salemanager,jikimo_message_queue,jikimo_message_notify.model_jikimo_message_queue,sf_base.group_sale_salemanager,1,1,1,0 access_jikimo_message_queue_group_purchase,jikimo_message_queue,jikimo_message_notify.model_jikimo_message_queue,sf_base.group_purchase,1,1,1,0 access_jikimo_message_queue_group_sf_stock_user,jikimo_message_queue,jikimo_message_notify.model_jikimo_message_queue,sf_base.group_sf_stock_user,1,1,1,0 access_jikimo_message_queue_group_sf_order_user,jikimo_message_queue,jikimo_message_notify.model_jikimo_message_queue,sf_base.group_sf_order_user,1,1,1,0 access_jikimo_message_queue_group_sf_tool_user,jikimo_message_queue,jikimo_message_notify.model_jikimo_message_queue,sf_base.group_sf_tool_user,1,1,1,0 +access_jikimo_message_queue_group_purchase_director,jikimo_message_queue,jikimo_message_notify.model_jikimo_message_queue,sf_base.group_purchase_director,1,1,1,0 access_jikimo_message_reminder_time_group_sale_salemanager,jikimo_message_reminder_time,jikimo_message_notify.model_jikimo_message_reminder_time,sf_base.group_sale_salemanager,1,1,1,0 access_jikimo_message_reminder_time_group_purchase,jikimo_message_reminder_time,jikimo_message_notify.model_jikimo_message_reminder_time,sf_base.group_purchase,1,1,1,0 access_jikimo_message_reminder_time_group_sf_stock_user,jikimo_message_reminder_time,jikimo_message_notify.model_jikimo_message_reminder_time,sf_base.group_sf_stock_user,1,1,1,0 access_jikimo_message_reminder_time_group_sf_order_user,jikimo_message_reminder_time,jikimo_message_notify.model_jikimo_message_reminder_time,sf_base.group_sf_order_user,1,1,1,0 access_jikimo_message_reminder_time_group_sf_tool_user,jikimo_message_reminder_time,jikimo_message_notify.model_jikimo_message_reminder_time,sf_base.group_sf_tool_user,1,1,1,0 +access_jikimo_message_reminder_time_group_purchase_director,jikimo_message_reminder_time,jikimo_message_notify.model_jikimo_message_reminder_time,sf_base.group_purchase_director,1,1,1,0 diff --git a/sf_message/views/mrp_workorder_views.xml b/sf_message/views/mrp_workorder_views.xml index 1e31fc6d..ab0a76a9 100644 --- a/sf_message/views/mrp_workorder_views.xml +++ b/sf_message/views/mrp_workorder_views.xml @@ -30,5 +30,36 @@

+ + + + 工单 + ir.actions.act_window + mrp.workorder + tree,form + + + + current + [('state', '!=', 'cancel'),('schedule_state', '=', '已排')] + {'search_default_product': 1, 'search_default_workcenter_id': + active_id,'search_default_ready': 1, 'search_default_progress': 1} + + +

+ 没有工单要做! +

+

+ 工作订单是作为制造订单的一部分执行的操作。 + 工序在物料清单中定义或直接添加到制造订单中。 +

+

+ 使用工作台工作中心控制面板直接登记车间中的操作. + 平板电脑为您的工人提供工作表,并允许他们报废产品,跟踪时间, + 发起维护请求,执行质量测试等. +

+
+
\ No newline at end of file diff --git a/sf_mrs_connect/models/sync_common.py b/sf_mrs_connect/models/sync_common.py index ae1930c6..29d32e5f 100644 --- a/sf_mrs_connect/models/sync_common.py +++ b/sf_mrs_connect/models/sync_common.py @@ -3166,5 +3166,5 @@ class MachiningAccuracySync(models.Model): self.env['sf.machining.accuracy'].sudo().create({ "sync_id": time['id'], "name": time['name'], - "discount": time['discount'], + "standard_tolerance": time['standard_tolerance'], }) diff --git a/sf_plan/models/custom_plan.py b/sf_plan/models/custom_plan.py index 492ef525..bdf916ce 100644 --- a/sf_plan/models/custom_plan.py +++ b/sf_plan/models/custom_plan.py @@ -246,26 +246,27 @@ class sf_production_plan(models.Model): record.date_planned_finished = item.date_planned_finished # 计算下一个cnc工单的开始时间 last_cnc_start = workorder_list[-1].date_planned_finished + timedelta(minutes=pre_duration) - record.state = 'done' - # record.production_id.schedule_state = '已排' - record.sudo().production_id.schedule_state = '已排' - record.sudo().production_id.process_state = '待装夹' - # self.env['sale.order'].browse(record.production_id.origin).schedule_status = 'to process' - # sale_obj = self.env['sale.order'].search([('name', '=', record.origin)]) - # if 'S' in sale_obj.name: - # sale_obj.schedule_status = 'to process' - mrp_production_ids = record.production_id._get_children().ids - print('mrp_production_ids', mrp_production_ids) - for i in mrp_production_ids: - record.env['mrp.production'].sudo().browse(i).schedule_state = '已排' - # record.production_id.date_planned_start = record.date_planned_start - # record.production_id.date_planned_finished = record.date_planned_finished - record.sudo().production_id.production_line_id = record.production_line_id.id - if record.production_id.workorder_ids: - record.sudo().production_id.workorder_ids.filtered( - lambda b: b.routing_type == "装夹预调").workpiece_delivery_ids.write( - {'production_line_id': record.production_line_id.id, - 'plan_start_processing_time': record.date_planned_start}) + # 没有工单也能排程 + record.state = 'done' + # record.production_id.schedule_state = '已排' + record.sudo().production_id.schedule_state = '已排' + record.sudo().production_id.process_state = '待装夹' + # self.env['sale.order'].browse(record.production_id.origin).schedule_status = 'to process' + # sale_obj = self.env['sale.order'].search([('name', '=', record.origin)]) + # if 'S' in sale_obj.name: + # sale_obj.schedule_status = 'to process' + mrp_production_ids = record.production_id._get_children().ids + print('mrp_production_ids', mrp_production_ids) + for i in mrp_production_ids: + record.env['mrp.production'].sudo().browse(i).schedule_state = '已排' + # record.production_id.date_planned_start = record.date_planned_start + # record.production_id.date_planned_finished = record.date_planned_finished + record.sudo().production_id.production_line_id = record.production_line_id.id + if record.production_id.workorder_ids: + record.sudo().production_id.workorder_ids.filtered( + lambda b: b.routing_type == "装夹预调").workpiece_delivery_ids.write( + {'production_line_id': record.production_line_id.id, + 'plan_start_processing_time': record.date_planned_start}) # record.date_planned_finished = record.date_planned_start + timedelta(days=3) # record.state = 'done' diff --git a/sf_plan/wizard/action_plan_some.py b/sf_plan/wizard/action_plan_some.py index 1c08ff0f..86b3f1ac 100644 --- a/sf_plan/wizard/action_plan_some.py +++ b/sf_plan/wizard/action_plan_some.py @@ -37,52 +37,4 @@ class Action_Plan_All_Wizard(models.TransientModel): # 判断能否排成 self.plan_ids.deal_processing_schedule(self.date_planned_start) self.plan_ids.do_production_schedule() - # for plan in temp_plan_ids: - # # 处理每个计划 - # # 比如更新计划状态、分配资源等 - # # 示例:plan.state = 'scheduled' - # print('处理计划:', plan.id) - # # 拿到计划对象 - # plan_obj = self.env['sf.production.plan'].browse(plan.id) - # plan_obj.production_line_id = self.production_line_id.id - # plan.date_planned_start = self.date_planned_start - # plan_obj.do_production_schedule() - # plan_obj.state = 'done' _logger.info('处理计划: %s 完成', self.plan_ids.ids) - - # # 获取当前生产线 - # production_line_id = self.production_line_id - # # 获取当前生产线的所有生产订单 - # production_order_ids = self.env['mrp.production'].search([('production_line_id', '=', production_line_id.id)]) - # # 获取当前生产线的所有生产订单的id - # production_order_id_list = [] - # for production_order_id in production_order_ids: - # production_order_id_list.append(production_order_id.id) - # # 获取当前生产线的所有生产订单的排程状态 - # production_order_plan_state_list = [] - # for production_order_id in production_order_ids: - # production_order_plan_state_list.append(production_order_id.plan_state) - # # 如果当前生产线的所有生产订单的排程状态都是已排程,则报错 - # if all(production_order_plan_state == '已排程' for production_order_plan_state in production_order_plan_state_list): - # raise UserError('当前生产线的所有生产订单都已排程,请勿重复排程!') - # # 如果当前生产线的所有生产订单的排程状态都是未排程,则报错 - # if all(production_order_plan_state == '未排程' for production_order_plan_state in production_order_plan_state_list): - # raise UserError('当前生产线的所有生产订单都未排程,请先排程!') - # # 如果当前生产线的所有生产订单的排程状态都是已完成,则报错 - # if all(production_order_plan_state == '已完成' for production_order_plan_state in production_order_plan_state_list): - # raise UserError('当前生产线的所有生产订单都已完成,请勿重复排程!') - # # 如果当前生产线的所有生产订单的排程状态都是已取消,则报错 - # if all(production_order_plan_state == '已取消' for production_order_plan_state in production_order_plan_state_list): - # raise UserError('当前生产线的所有生产订单都已取消,请勿重复排程!') - # # 如果当前生产线的所有生产订单的排程状态都是已暂停,则报错 - # if all(production_order_plan_state == '已暂停' for production_order_plan_state in production_order_plan_state_list): - # raise UserError('当前生产线的所有生产订单都已暂停,请勿重复排程!') - # # 如果当前生产线的所有生产订单的排程状态都是已完成,则报错 - # if all(production_order_plan_state == '已完成' for production_order_plan_state in production_order_plan_state_list): - # raise UserError('当前生产线的所有生产订单都已完成,请勿重复排程!') - # # 如果当前生产线的所有生产订单的排程状态都是已取消,则报错 - # if all(production_order_plan_state == '已取消' for production_order_plan_state in production_order_plan_state_list): - # raise UserError('当前生产线的所有生产订单都已取消,请勿重复排程!') - # # 如果当前生产线的所有生产订单的排程状态都是已暂停,则报错 - # if all(production_order_plan_state == '已暂停' for production_order_plan_state in production_order_plan_state_list): - # raise UserError('当前生产线的所有生产订单都已暂停,请勿重复排程!') diff --git a/sf_quality/__manifest__.py b/sf_quality/__manifest__.py index b1151b6d..582e306a 100644 --- a/sf_quality/__manifest__.py +++ b/sf_quality/__manifest__.py @@ -13,7 +13,7 @@ 'author': 'jikimo', 'website': 'https://sf.cs.jikimo.com', # 此处依赖sf_manufacturing是因为我要重写其中的一个字段operation_id的string,故需要sf_manufacturing先安装 - 'depends': ['quality_control', 'web_widget_model_viewer', 'sf_manufacturing'], + 'depends': ['quality_control', 'web_widget_model_viewer', 'sf_manufacturing','jikimo_attachment_viewer'], 'data': [ 'security/ir.model.access.csv', 'views/view.xml', diff --git a/sf_sale/models/quick_easy_order_old.py b/sf_sale/models/quick_easy_order_old.py index 4756a2c5..e974c5d4 100644 --- a/sf_sale/models/quick_easy_order_old.py +++ b/sf_sale/models/quick_easy_order_old.py @@ -20,6 +20,12 @@ class QuickEasyOrder(models.Model): _description = '简易下单' _order = 'id desc' + def _get_machining_precision(self): + machinings = self.env['sf.machining.accuracy'].sudo().search([]) + + list = [(m.sync_id, m.name) for m in machinings] + return list + name = fields.Char('订单编号', default=lambda self: self.env['ir.sequence'].next_by_code('quick.easy.order')) model_length = fields.Float('长(mm)', digits=(16, 3)) model_width = fields.Float('宽(mm)', digits=(16, 3)) @@ -27,12 +33,7 @@ class QuickEasyOrder(models.Model): model_volume = fields.Float('体积(mm³)', digits=(16, 3)) model_processing_side = fields.Char('加工面', default='A') model_feature = fields.Char('特征') - machining_precision = fields.Selection([ - ('0.10', '±0.10mm'), - ('0.05', '±0.05mm'), - ('0.03', '±0.03mm'), - ('0.02', '±0.02mm'), - ('0.01', '±0.01mm')], string='加工精度', default='0.10') + machining_precision = fields.Selection(selection=_get_machining_precision, string='加工精度') material_id = fields.Many2one('sf.production.materials', '材料', tracking=True) material_model_id = fields.Many2one('sf.materials.model', '型号', domain="[('materials_id', '=', material_id)]", tracking=True) @@ -219,7 +220,14 @@ class QuickEasyOrder(models.Model): 'total_amount': item.price, 'remark': '', 'manual_quotation': True, - 'barcode': barcode + 'barcode': barcode, + 'part_number': item.part_drawing_number, + 'machining_drawings_name': '', + 'quality_standard_name': '', + 'machining_drawings_mimetype': '', + 'quality_standard_mimetype': '', + 'machining_drawings': item.machining_drawings, + 'quality_standard': '', }) # res['bfm_process_order_list'] = json.dumps(res['bfm_process_order_list']) product_id = self.env.ref('sf_dlm.product_template_sf').sudo() diff --git a/sf_sale/models/sale_order.py b/sf_sale/models/sale_order.py index d3ec1aa0..53f73aef 100644 --- a/sf_sale/models/sale_order.py +++ b/sf_sale/models/sale_order.py @@ -55,7 +55,8 @@ class ReSaleOrder(models.Model): store=True, readonly=False, copy=False, precompute=True, states=READONLY_FIELD_STATES, default=fields.Datetime.now) - delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效') + delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效', + tracking=True) # 业务平台分配工厂后在智能工厂先创建销售订单 def sale_order_create(self, company_id, delivery_name, delivery_telephone, delivery_address, @@ -117,12 +118,16 @@ class ReSaleOrder(models.Model): # 业务平台分配工厂时在创建完产品后再创建销售明细信息 def sale_order_create_line(self, product, item): + machining_accuracy_name = '' + if product.model_machining_precision: + machining_accuracy_name = self.env['sf.machining.accuracy'].sudo().search( + [('sync_id', '=', product.model_machining_precision)]).name vals = { 'order_id': self.id, 'product_id': product.id, - 'name': '%s/%s/%s/%s/±%s/%s' % ( + 'name': '%s/%s/%s/%s/%s/%s' % ( product.model_long, product.model_width, product.model_height, product.model_volume, - product.model_machining_precision, + machining_accuracy_name, product.materials_id.name), 'price_unit': product.list_price, 'product_uom_qty': item['number'], diff --git a/sf_sale/views/purchase_order_view.xml b/sf_sale/views/purchase_order_view.xml index 7a9836c5..f721d515 100644 --- a/sf_sale/views/purchase_order_view.xml +++ b/sf_sale/views/purchase_order_view.xml @@ -6,14 +6,20 @@ purchase.order - + + 1 +