Sq2a01{fs4
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
-
+
@@ -328,6 +328,11 @@
+
diff --git a/quality_control/wizard/__init__.py b/quality_control/wizard/__init__.py
index 2b9d69c4..49415fd2 100644
--- a/quality_control/wizard/__init__.py
+++ b/quality_control/wizard/__init__.py
@@ -2,3 +2,4 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import quality_check_wizard
+from . import import_complex_model
diff --git a/quality_control/wizard/import_complex_model.py b/quality_control/wizard/import_complex_model.py
new file mode 100644
index 00000000..e53e7bcd
--- /dev/null
+++ b/quality_control/wizard/import_complex_model.py
@@ -0,0 +1,418 @@
+# -*- coding: utf-8 -*-
+import base64
+import logging
+import os
+import traceback
+from datetime import datetime
+from io import BytesIO
+from openpyxl import load_workbook
+import pandas as pd
+import xlrd
+
+from odoo import fields, models, api, Command, _
+# from odoo.exceptions import ValidationError
+from odoo.exceptions import UserError
+from datetime import datetime, timedelta
+
+from odoo.http import request
+
+_logger = logging.getLogger(__name__)
+
+
+class ImportComplexModelWizard(models.TransientModel):
+ _name = 'quality.check.import.complex.model.wizard'
+ file_data = fields.Binary("数据文件")
+ model_name = fields.Char(string='Model Name')
+ field_basis = fields.Char(string='Field Basis')
+
+ def get_model_column_name_labels(self, model):
+ fields_info = model.fields_get()
+ # 提取字段名称和展示名称
+ field_labels = {field_info.get('string'): field_info for field_name, field_info in fields_info.items()}
+
+ return field_labels
+
+ def field_name_mapping(selfcolumn, column, field_data):
+ return {}
+
+ @api.model
+ def page_to_field(self, field_data):
+ return {}
+
+ def count_continuous_none(self, df):
+ # 用于存储间断的连续空值的区间
+ none_intervals = []
+ # if pd.isna(val):
+ # 遍历数据并查找连续的 None
+ start = 0
+ num = 0
+ for index, row in df.iterrows():
+ if pd.isna(row[0]):
+ continue
+ else:
+ # 记录区间
+ if num == 0:
+ start = index
+ num = 1
+ else:
+ end = index
+ none_intervals.append({'start': start, 'end': index})
+ start = end
+ # start = None # 重置
+
+ # 检查最后一个区间
+ if len(df) - start >= 1:
+ none_intervals.append({'start': start, 'end': len(df)})
+
+ return none_intervals
+
+ def find_repeated_ranges(self, data):
+ # 判断内联列表范围,有column列的为内联列表字段
+ if not data:
+ return []
+
+ repeats = []
+ start_index = 0
+ current_value = data[0]
+
+ for i in range(1, len(data)):
+ if data[i] == current_value:
+ continue
+ else:
+ if i - start_index > 1: # 有重复
+ repeats.append({'start': start_index, 'end': i, 'column': data[i - 1]})
+ # repeats.append((current_value, start_index, i - 1))
+ current_value = data[i]
+ start_index = i
+
+ # 检查最后一段
+ if len(data) - start_index > 1:
+ repeats.append({'start': start_index, 'end': i, 'column': data[i - 1]})
+ # repeats.append((current_value, start_index, len(data) - 1))
+
+ return repeats
+
+ def import_data(self):
+ """导入Excel数据"""
+ if not self.file_data:
+ raise UserError(_('请先上传Excel文件'))
+
+ # 解码文件数据
+ file_content = base64.b64decode(self.file_data)
+
+ try:
+ # 使用xlrd读取Excel文件
+ workbook = xlrd.open_workbook(file_contents=file_content)
+ sheet = workbook.sheet_by_index(0)
+
+ # 检查表头是否符合预期(忽略星号)
+ expected_headers = ['产品名称', '图号', '检测项目', '测量值1', '测量值2', '测量值3', '测量值4', '测量值5', '判定', '备注']
+ actual_headers = sheet.row_values(0)
+
+ # 处理合并单元格的情况
+ # 获取所有合并单元格
+ merged_cells = []
+ for crange in sheet.merged_cells:
+ rlo, rhi, clo, chi = crange
+ if rlo == 0: # 只关注第一行(表头)的合并单元格
+ merged_cells.append((clo, chi))
+
+ # 清理表头(移除星号和处理空值)
+ actual_headers_clean = []
+ for i, header in enumerate(actual_headers):
+ # 检查当前列是否在合并单元格范围内但不是合并单元格的起始列
+ is_merged_not_first = any(clo < i < chi for clo, chi in merged_cells)
+
+ if is_merged_not_first:
+ # 如果是合并单元格的非起始列,跳过
+ continue
+
+ # 处理表头文本
+ if isinstance(header, str):
+ header = header.replace('*', '').strip()
+
+ if header: # 只添加非空表头
+ actual_headers_clean.append(header)
+
+ # 检查表头数量
+ if len(actual_headers_clean) < len(expected_headers):
+ raise UserError(_('表头列数不足,请使用正确的模板文件'))
+
+ # 检查表头顺序(忽略星号)
+ for i, header in enumerate(expected_headers):
+ if i >= len(actual_headers_clean) or header != actual_headers_clean[i]:
+ actual_header = actual_headers_clean[i] if i < len(actual_headers_clean) else "缺失"
+ raise UserError(_('表头顺序不正确,第%s列应为"%s",但实际为"%s"') %
+ (i + 1, header, actual_header))
+
+ # 获取当前活动的quality.check记录
+ active_id = self.env.context.get('active_id')
+ quality_check = self.env['quality.check'].browse(active_id)
+
+ if not quality_check:
+ raise UserError(_('未找到相关的质检记录'))
+
+ # 记录是否有有效数据被导入
+ valid_data_imported = False
+
+ # 从第二行开始读取数据(跳过表头)
+ for row_index in range(1, sheet.nrows):
+ row = sheet.row_values(row_index)
+
+ # 检查行是否有数据
+ if not any(row):
+ continue
+
+ # 创建quality.check.measure.line记录
+ measure_line_vals = {
+ 'check_id': quality_check.id,
+ 'sequence': len(quality_check.measure_line_ids) + 1,
+ 'product_name': str(row[0]) if row[0] else '', # 产品名称列
+ 'drawing_no': str(row[1]) if row[1] else '', # 图号列
+ 'measure_item': row[2] or '', # 检测项目列
+ 'measure_value1': str(row[4]) if row[4] else '', # 测量值1
+ 'measure_value2': str(row[5]) if row[5] else '', # 测量值2
+ 'measure_value3': str(row[6]) if len(row) > 6 and row[6] else '', # 测量值3
+ 'measure_value4': str(row[7]) if len(row) > 7 and row[7] else '', # 测量值4
+ 'measure_value5': str(row[8]) if len(row) > 8 and row[8] else '', # 测量值5
+ 'measure_result': 'NG' if row[9] == 'NG' else 'OK', # 判定列
+ 'remark': row[10] if len(row) > 10 and row[10] else '', # 备注列
+ }
+
+ self.env['quality.check.measure.line'].create(measure_line_vals)
+ valid_data_imported = True
+
+ # 检查是否有有效数据被导入
+ if not valid_data_imported:
+ raise UserError(_('表格中没有有效数据行可导入,请检查表格内容'))
+
+ # 返回关闭弹窗的动作
+ return {
+ 'type': 'ir.actions.act_window_close'
+ }
+
+ except Exception as e:
+ _logger.error("导入Excel数据时出错: %s", str(e))
+ _logger.error(traceback.format_exc())
+ raise UserError(_('导入失败: %s') % str(e))
+
+ def process_first_line(self, df_columns, column_labels, field_data):
+ columns = []
+ last_column_name = None
+ for index, column in enumerate(df_columns):
+ if not column_labels.get(column):
+ if 'Unnamed' in column:
+ columns.append(last_column_name)
+ else:
+ field_name_map = self.page_to_field(field_data)
+ field_name = field_name_map.get(column)
+ if field_name:
+ columns.append(field_name)
+ last_column_name = field_name
+ else:
+ columns.append(column)
+ last_column_name = column
+ else:
+ columns.append(column)
+ last_column_name = column
+ return columns
+
+ def process_inline_list_column(self, columns, first_row, repeat_list):
+ for index, repeat_map in enumerate(repeat_list):
+ start = repeat_map.get('start')
+ end = int(repeat_map.get('end'))
+ if len(repeat_list) - 1 == index:
+ end = end + 1
+ for i in range(start, end):
+ field_name = columns[i]
+ embedded_fields = first_row[i]
+ if pd.isna(embedded_fields):
+ columns[i] = field_name
+ else:
+ columns[i] = field_name + '?' + embedded_fields
+
+ def process_data_list(self, model, data_list, column_labels, sheet):
+ try:
+ for index, data in enumerate(data_list):
+ # 转换每行数据到模型data = {dict: 11} {'刀具物料': '刀片', '刀尖特征': '刀尖倒角', '刀片形状': '五边形', '刀片物料参数': [{'刀片物料参数?内接圆直径IC/D(mm)': 23, '刀片物料参数?刀尖圆弧半径RE(mm)': 2, '刀片物料参数?刀片牙型': '石油管螺纹刀片', '刀片物料参数?切削刃长(mm)': 3, '刀片物料参数?厚度(mm)': 12, '刀片物料参数?后角(mm)': 4, '刀片物料参数?安装孔直径D1(mm)': 2, '刀片物料参数?有无断屑槽': '无', '刀片物料参数?物…视图
+ self.import_model_record(model, data, column_labels, sheet)
+ except Exception as e:
+ traceback_error = traceback.format_exc()
+ logging.error('批量导入失败sheet %s' % sheet)
+ logging.error('批量导入失败 : %s' % traceback_error)
+ raise UserError(e)
+
+ @api.model
+ def process_model_record_data(self, model, data, column_labels, sheet):
+ pass
+
+ def import_model_record(self, model, data, column_labels, sheet):
+ self.process_model_record_data(model, data, column_labels, sheet)
+ model_data = self.convert_column_name(data, column_labels)
+ logging.info('批量导入模型{} 数据: {}'.format(model, model_data))
+ new_model = model.create(model_data)
+ self.model_subsequent_processing(new_model, data)
+
+ @api.model
+ def model_subsequent_processing(self, model, data):
+ pass
+
+ def convert_column_name(self, data, column_labels):
+ tmp_map = {}
+ for key, value in data.items():
+ if not column_labels.get(key):
+ continue
+ if key == "执行标准":
+ print('fqwioiqwfo ', value, column_labels)
+ field_info = column_labels.get(key)
+ tmp_map[field_info.get('name')] = self.process_field_data(value, field_info)
+ return tmp_map
+
+ def process_field_data(self, value, field_info):
+ relation_field_types = ['many2one', 'one2many', 'many2many']
+ field_type = field_info.get('type')
+ if field_type not in relation_field_types:
+ return value
+ if field_type == 'Boolean':
+ if value == '是' or value == '有':
+ return True
+ else:
+ return False
+ if field_type == 'many2one':
+ relation_info = self.env[field_info.get('relation')].sudo().search([('name', '=', value)], limit=1)
+ if relation_info:
+ return int(relation_info)
+
+ if isinstance(value, list):
+ return self.process_basic_data_list(value, field_info)
+ else:
+ relation_info = self.env[field_info.get('relation')].sudo().search([('name', '=', value)], limit=1)
+ if relation_info:
+ return [Command.link(int(relation_info))]
+
+ def process_basic_data_list(self, value, field_info):
+ if self.is_basic_data_list(value):
+ return [
+ Command.link(
+ int(self.env[field_info.get('relation')].sudo().search([('name', '=', element)], limit=1)))
+ for element in value
+ ]
+ else:
+ association_column_labels = self.get_model_column_name_labels(
+ self.env[field_info.get('relation')].sudo())
+ data_list = [
+ {column_name.split('?')[1]: column_value
+ for column_name, column_value in association_column_data.items()
+ if
+ len(column_name.split('?')) == 2 and association_column_labels.get(column_name.split('?')[1])}
+ for association_column_data in value
+ ]
+ data_list = [self.convert_column_name(element, association_column_labels) for element in data_list]
+ return [
+ Command.create(
+ column_map
+ ) for column_map in data_list
+ ]
+
+ def get_remaining_ranges(self, ranges, full_list_length):
+ # 创建一个集合用于存储被覆盖的索引
+ covered_indices = set()
+
+ # 遍历范围集合,标记覆盖的索引
+ for r in ranges:
+ start = r['start']
+ end = r['end']
+ # 将该范围内的索引加入集合
+ covered_indices.update(range(start, end + 1))
+
+ # 计算未覆盖的范围
+ remaining_ranges = []
+ start = None
+
+ for i in range(full_list_length):
+ if i not in covered_indices:
+ if start is None:
+ start = i # 开始新的未覆盖范围
+ else:
+ if start is not None:
+ # 记录当前未覆盖范围
+ remaining_ranges.append({'start': start, 'end': i - 1})
+ start = None # 重置
+
+ # 处理最后一个范围
+ if start is not None:
+ remaining_ranges.append({'start': start, 'end': full_list_length - 1})
+
+ return remaining_ranges
+
+ def is_basic_data_list(self, lst):
+ basic_types = (int, float, str, bool)
+ lst_len = len(lst)
+ if lst_len < 1:
+ return False
+ if isinstance(lst[0], basic_types):
+ return True
+ return False
+
+ def index_retrieve_data(self, df, range_map, i, data_map):
+ for remaining_map in range_map:
+ relation_column_data_map = {}
+ for j in range(remaining_map.get('start'), int(remaining_map.get('end')) + 1):
+ value = df.iat[i, j]
+ if pd.isna(value):
+ continue
+ if remaining_map.get('column'):
+ relation_column_data_map.update({df.columns[j]: value})
+ else:
+ if not data_map.get(df.columns[j]):
+ data_map.update({df.columns[j]: value})
+ elif isinstance(data_map.get(df.columns[j]), list):
+ data_map.get(df.columns[j]).append(value)
+ else:
+ lst = [data_map.get(df.columns[j]), value]
+ data_map.update({df.columns[j]: lst})
+ if relation_column_data_map and len(relation_column_data_map) > 0:
+ data_map.setdefault(remaining_map.get('column'), []).append(relation_column_data_map)
+
+ def parse_excel_data_matrix(self, df, repeat_list):
+ row_interval_list = self.count_continuous_none(df)
+ remaining_ranges = self.get_remaining_ranges(repeat_list, len(df.columns))
+ data_list = []
+ for row_interval_map in row_interval_list:
+ data_map = {}
+ for index in range(row_interval_map.get('start'), int(row_interval_map.get('end'))):
+ if index == 0:
+ self.index_retrieve_data(df, remaining_ranges, index, data_map)
+ else:
+ self.index_retrieve_data(df, remaining_ranges, index, data_map)
+ self.index_retrieve_data(df, repeat_list, index, data_map)
+ if len(data_map) > 0:
+ data_list.append(data_map)
+ return data_list
+
+ def saadqw(self):
+
+ excel_template = self.env['excel.template'].sudo().search([('model_id.model', '=', self.model_name)], limit=1)
+ file_content = base64.b64decode(excel_template.file_data)
+ return {
+ 'type': 'ir.actions.act_url',
+ 'url': 'data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,{file_content}',
+ 'target': 'self',
+ 'download': excel_template.file_name,
+ }
+ # return request.make_response(
+ # file_content,
+ # headers=[
+ # ('Content-Disposition', f'attachment; filename="{excel_template.file_name}"'),
+ # ('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'),
+ # ]
+ # )
+
+ def download_excel_template(self):
+ excel_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') + '/quality_control/static/src/binary/出厂检验报告上传模版.xlsx'
+ value = dict(
+ type='ir.actions.act_url',
+ target='self',
+ url=excel_url,
+ )
+ return value
diff --git a/quality_control/wizard/import_complex_model.xml b/quality_control/wizard/import_complex_model.xml
new file mode 100644
index 00000000..db7ea5d9
--- /dev/null
+++ b/quality_control/wizard/import_complex_model.xml
@@ -0,0 +1,33 @@
+
+
+
+ 请导入数据文件
+ quality.check.import.complex.model.wizard
+
+
+
+
+
+ 导入模型数据
+ ir.actions.act_window
+
+ quality.check.import.complex.model.wizard
+ form
+
+ new
+
+
+
+
+
+
+
\ No newline at end of file
From de1bdbe18b11e3816d882deadea4201457b66b38 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Wed, 12 Mar 2025 09:08:41 +0800
Subject: [PATCH 05/46] =?UTF-8?q?=E5=87=86=E5=A4=87=E5=89=8D=E7=AB=AF?=
=?UTF-8?q?=E5=BC=80=E5=8F=91=E7=94=A8=E7=BB=93=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
quality_control/models/quality.py | 32 +++++++++++--------
.../views/quality.check.measures.line.xml | 2 +-
quality_control/views/quality_views.xml | 1 +
3 files changed, 21 insertions(+), 14 deletions(-)
diff --git a/quality_control/models/quality.py b/quality_control/models/quality.py
index b1efad6c..1af23aff 100644
--- a/quality_control/models/quality.py
+++ b/quality_control/models/quality.py
@@ -147,14 +147,22 @@ class QualityCheck(models.Model):
measure_line_ids = fields.One2many('quality.check.measure.line', 'check_id', string='测量明细')
def add_measure_line(self):
- '''
+ """
新增测量值,如果测量值有5列了,则提示“最多只能有5列测量值”
- '''
- self.ensure_one()
- self.env['quality.check.measure.line'].create({
- 'check_id': self.id,
- 'sequence': len(self.measure_line_ids) + 1,
- })
+ """
+ pass
+ # self.ensure_one()
+ # self.env['quality.check.measure.line'].create({
+ # 'check_id': self.id,
+ # 'sequence': len(self.measure_line_ids) + 1,
+ # })
+
+ def remove_measure_line(self):
+ """
+ 删除测量值
+ """
+ pass
+ # self.ensure_one()
@depends('product_id')
def _compute_part_name_number(self):
@@ -579,12 +587,12 @@ class QualityCheckMeasureLine(models.Model):
sequence = fields.Integer('序号')
check_id = fields.Many2one('quality.check', string='质检单', required=True, ondelete='cascade')
-
+
# 基本信息
product_name = fields.Char('产品名称', related='check_id.product_id.name', readonly=True)
drawing_no = fields.Char('图号')
measure_item = fields.Char('检测项目')
-
+
# 测量值
measure_value1 = fields.Char('测量值1')
measure_value2 = fields.Char('测量值2')
@@ -594,17 +602,15 @@ class QualityCheckMeasureLine(models.Model):
# 展示列数
show_colomn_number = fields.Integer('展示列数', default=1)
-
+
# 判定结果
measure_result = fields.Selection([
('OK', 'OK'),
('NG', 'NG')
], string='判定', default='OK')
-
+
remark = fields.Char('备注')
def del_measure_value(self):
self.ensure_one()
self.sudo().unlink()
-
-
\ No newline at end of file
diff --git a/quality_control/views/quality.check.measures.line.xml b/quality_control/views/quality.check.measures.line.xml
index b0c603df..c0e3be04 100644
--- a/quality_control/views/quality.check.measures.line.xml
+++ b/quality_control/views/quality.check.measures.line.xml
@@ -14,7 +14,7 @@
-
+
diff --git a/quality_control/views/quality_views.xml b/quality_control/views/quality_views.xml
index e1ea819b..fe198f4a 100644
--- a/quality_control/views/quality_views.xml
+++ b/quality_control/views/quality_views.xml
@@ -328,6 +328,7 @@
+
-
+
diff --git a/sf_quality/__manifest__.py b/sf_quality/__manifest__.py
index 9ca7128a..560963b7 100644
--- a/sf_quality/__manifest__.py
+++ b/sf_quality/__manifest__.py
@@ -17,10 +17,14 @@
'data': [
'security/ir.model.access.csv',
'data/check_standards.xml',
+ 'data/documents_data.xml',
+ 'data/insepection_report_template.xml',
+ 'data/report_actions.xml',
'views/view.xml',
'views/quality_cnc_test_view.xml',
'views/mrp_workorder.xml',
- 'views/quality_check_view.xml'
+ 'views/quality_check_view.xml',
+ 'views/quality_company.xml'
],
'assets': {
diff --git a/sf_quality/data/documents_data.xml b/sf_quality/data/documents_data.xml
new file mode 100644
index 00000000..69fdbe8c
--- /dev/null
+++ b/sf_quality/data/documents_data.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+ 出厂检验报告
+ 存放出厂检验报告相关文件
+ 11
+
+
+
\ No newline at end of file
diff --git a/sf_quality/data/insepection_report_template.xml b/sf_quality/data/insepection_report_template.xml
new file mode 100644
index 00000000..d8df704a
--- /dev/null
+++ b/sf_quality/data/insepection_report_template.xml
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
出厂检验报告
+
+
+
+
+
+
+ | 产品名称: |
+ |
+ 材料: |
+ |
+
+
+ | 图号: |
+ |
+ 日期: |
+ |
+
+
+ | 总数量: |
+ |
+ 检验数量: |
+ |
+
+
+
+
检验结果
+
+
+
+ | 检测项目 |
+ 测量值 |
+ 判定 |
+ 备注 |
+
+
+ |
+ 1 |
+ 2 |
+ 3 |
+ 4 |
+ 5 |
+ |
+ |
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
检验结论:
+ ☑ 合格
+ □ 合格
+ ☑ 不合格
+ □ 不合格
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sf_quality/data/report_actions.xml b/sf_quality/data/report_actions.xml
new file mode 100644
index 00000000..2d875d71
--- /dev/null
+++ b/sf_quality/data/report_actions.xml
@@ -0,0 +1,22 @@
+
+
+
+ 出厂检验报告
+ quality.check
+ qweb-pdf
+ sf_quality.report_quality_inspection
+ sf_quality.report_quality_inspection
+ 'QC-' + object.name + '.pdf'
+
+ report
+
+
+
+
+ 预览检验报告
+ quality.inspection
+ qweb-html
+ sf_quality.report_quality_inspection
+ report
+
+
\ No newline at end of file
diff --git a/sf_quality/models/__init__.py b/sf_quality/models/__init__.py
index 4fcdb16f..b547faa9 100644
--- a/sf_quality/models/__init__.py
+++ b/sf_quality/models/__init__.py
@@ -6,3 +6,4 @@ from . import quality
from . import quality_cnc_test
from . import mrp_workorder
# from . import stock
+from . import quality_company
diff --git a/sf_quality/models/quality_company.py b/sf_quality/models/quality_company.py
new file mode 100644
index 00000000..de80ad50
--- /dev/null
+++ b/sf_quality/models/quality_company.py
@@ -0,0 +1,8 @@
+from odoo import models, fields
+
+
+# 为公司增加字段
+class Company(models.Model):
+ _inherit = 'res.company'
+
+ factory_name = fields.Char('加工工厂')
diff --git a/sf_quality/views/quality_check_view.xml b/sf_quality/views/quality_check_view.xml
index eaee2a84..d288324e 100644
--- a/sf_quality/views/quality_check_view.xml
+++ b/sf_quality/views/quality_check_view.xml
@@ -70,6 +70,14 @@
+
+
+
diff --git a/sf_quality/views/quality_company.xml b/sf_quality/views/quality_company.xml
new file mode 100644
index 00000000..a200b920
--- /dev/null
+++ b/sf_quality/views/quality_company.xml
@@ -0,0 +1,13 @@
+
+
+
+ sf.quality.company.view
+ res.company
+
+
+
+
+
+
+
+
From 082e25feae64a5a391074566866d2fea3385ba2c Mon Sep 17 00:00:00 2001
From: hyyy <123@qq.com>
Date: Thu, 13 Mar 2025 16:10:43 +0800
Subject: [PATCH 10/46] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B7=BB=E5=8A=A0?=
=?UTF-8?q?=E5=88=A0=E9=99=A4=E5=88=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
quality_control/models/quality.py | 2 +
.../src/js/quality.check.measures.line.js | 80 +++++++++++++++++++
.../views/quality.check.measures.line.xml | 3 +-
quality_control/views/quality_views.xml | 2 +-
4 files changed, 85 insertions(+), 2 deletions(-)
create mode 100644 quality_control/static/src/js/quality.check.measures.line.js
diff --git a/quality_control/models/quality.py b/quality_control/models/quality.py
index f67741a0..3e83aec2 100644
--- a/quality_control/models/quality.py
+++ b/quality_control/models/quality.py
@@ -193,6 +193,7 @@ class QualityCheck(models.Model):
def add_measure_line(self):
+ self.total_qty = self.total_qty + 1
"""
新增测量值,如果测量值有5列了,则提示“最多只能有5列测量值”
"""
@@ -204,6 +205,7 @@ class QualityCheck(models.Model):
# })
def remove_measure_line(self):
+ self.total_qty = self.total_qty - 1
"""
删除测量值
"""
diff --git a/quality_control/static/src/js/quality.check.measures.line.js b/quality_control/static/src/js/quality.check.measures.line.js
new file mode 100644
index 00000000..233edda7
--- /dev/null
+++ b/quality_control/static/src/js/quality.check.measures.line.js
@@ -0,0 +1,80 @@
+/** @odoo-module **/
+
+import { patch } from "@web/core/utils/patch";
+import { ListRenderer } from "@web/views/list/list_renderer";
+import { X2ManyField } from '@web/views/fields/x2many/x2many_field';
+import { registry } from '@web/core/registry';
+
+
+export class QualityCheckMeasureLineTree extends X2ManyField {
+ setup() {
+ super.setup();
+ }
+
+ get rendererProps() {
+ const props = super.rendererProps;
+ return props;
+ }
+}
+
+patch(ListRenderer.prototype, 'custom_measure_line_tree_patch', {
+ setup() {
+ this._super.apply(this, arguments);
+ console.log('custom_measure_line_tree_patch');
+ console.log(owl);
+ owl.onMounted(() => {
+ console.log($(this.__owl__.bdom.el).find('tfoot'));
+ $(this.__owl__.bdom.el).find('tfoot').hide()
+ })
+
+ owl.onRendered(() => {
+ setTimeout(() => {
+ const $dom = $(this.__owl__.bdom.el)
+ const $thead = $dom.find('thead').find('th');
+ const $total_qty = this.props.list.records[0].data.total_qty;
+ const hideKey = []
+ const width = $dom.find('[data-name=measure_value1]').width();
+ $thead.each(function () {
+ const key = $(this).attr('data-name');
+ if (key && key.indexOf('measure_value') >= 0) {
+ const keyNum = Number(key.replace('measure_value', ''));
+ if (keyNum > $total_qty) {
+ hideKey.push(key);
+ }
+ }
+ });
+ $dom.find(':hidden').show().css('width', width);
+ const hideDom = hideKey.map(_ => {
+ return `[data-name="${_}"],[name="${_}"]`
+ })
+ $dom.find(hideDom.join(',')).hide()
+ const $cloNum = $dom.find('.o_data_row').children('td:visible').length;
+ $dom.find('.o_data_row').siblings('tr').children('td').prop('colspan', $cloNum);
+ $dom.find('tfoot').hide()
+ }, 50);
+ })
+
+ },
+
+
+ // get hasSelectors() {
+ // return this.withSelectorColumn || (this.props.allowSelectors && !this.env.isSmall);
+ // },
+
+
+ _renderRow(list) {
+ console.log('list', list);
+ // if(!this.__owl__.bdom || !this.__owl__.bdom.el) return;
+ // const $row = $(this.__owl__.bdom.el).find('tbody').children('tr');
+ return '';
+ },
+
+ getContext() {
+ return this.context;
+ },
+ getContext() {
+ return this.context;
+ },
+})
+
+registry.category('fields').add('custom_measure_line_tree', QualityCheckMeasureLineTree);
\ No newline at end of file
diff --git a/quality_control/views/quality.check.measures.line.xml b/quality_control/views/quality.check.measures.line.xml
index c0e3be04..879a175a 100644
--- a/quality_control/views/quality.check.measures.line.xml
+++ b/quality_control/views/quality.check.measures.line.xml
@@ -4,8 +4,9 @@
quality.check.measure.line.tree
quality.check.measure.line
-
+
+
diff --git a/quality_control/views/quality_views.xml b/quality_control/views/quality_views.xml
index c55a6f53..425716c0 100644
--- a/quality_control/views/quality_views.xml
+++ b/quality_control/views/quality_views.xml
@@ -339,7 +339,7 @@
-
+
From f780d475621b7dd30cf4f842bf6fecb8adf67081 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Thu, 13 Mar 2025 16:12:48 +0800
Subject: [PATCH 11/46] =?UTF-8?q?=E5=8F=91=E5=B8=83=E7=9B=B8=E5=85=B3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
quality_control/models/quality.py | 133 ++++++++++++++----
quality_control/security/ir.model.access.csv | 1 +
.../views/quality.check.measures.line.xml | 4 +-
quality_control/views/quality_views.xml | 12 +-
sf_quality/data/documents_data.xml | 15 +-
sf_quality/views/quality_check_view.xml | 7 +-
6 files changed, 140 insertions(+), 32 deletions(-)
diff --git a/quality_control/models/quality.py b/quality_control/models/quality.py
index f67741a0..17acf218 100644
--- a/quality_control/models/quality.py
+++ b/quality_control/models/quality.py
@@ -128,7 +128,7 @@ class QualityPoint(models.Model):
class QualityCheck(models.Model):
_inherit = "quality.check"
part_name = fields.Char('零件名称', related='product_id.part_name')
- part_number = fields.Char('零件图号', related='product_id.part_number')
+ part_number = fields.Char('零件图号', related='product_id.part_number', readonly=False, store=True)
material_name = fields.Char('材料名称', compute='_compute_material_name')
# # 总数量,值为调拨单_产品明细_数量
@@ -187,10 +187,21 @@ class QualityCheck(models.Model):
('OK', 'OK'),
('NG', 'NG')
], string='出厂检验报告结果', default='OK')
- measure_operator = fields.Char('测量员', readonly=True)
- quality_manager = fields.Char('质量管理人员', readonly=True)
+ measure_operator = fields.Many2one('res.users', string='操机员')
+ quality_manager = fields.Many2one('res.users', string='质检员')
+ # 流水号(从1开始,最大99)
+ serial_number = fields.Integer('流水号', default=1, readonly=True)
+ # 发布历史
+ report_history_ids = fields.One2many('quality.check.report.history', 'check_id', string='发布历史')
+
+ # 发布状态
+ publish_status = fields.Selection([
+ ('draft', '草稿'),
+ ('published', '已发布'),
+ ('canceled', '已撤销')
+ ], string='发布状态', default='draft')
def add_measure_line(self):
"""
@@ -229,46 +240,91 @@ class QualityCheck(models.Model):
res_ids=self.ids
)
- # 获取默认的文档文件夹
- workspace = self.env['documents.folder'].search([('name', '=', '出厂检验报告')], limit=1)
-
+ attachment = self.env['ir.attachment'].create({
+ 'name': self.name,
+ 'type': 'binary',
+ 'datas': pdf_content,
+ 'res_model': self._name,
+ 'res_id': self.id,
+ 'mimetype': 'application/pdf',
+ })
+
+ # 获取已发布的文档文件夹
+ workspace = self.env['documents.folder'].search([('parent_folder_id', '=', self.env.ref('sf_quality.documents_purchase_contracts_folder').id), ('name', '=', '已发布')], limit=1)
+
+ if self.serial_number > 99:
+ raise UserError(_('流水号不能大于99'))
+
+ str_serial_number = '0' + str(self.serial_number) if self.serial_number < 10 else str(self.serial_number)
+ str_part_number = self.part_number if self.part_number else ''
# 3. 创建文档记录
doc_vals = {
- 'name': f'FQC-{self.name}',
- 'raw': pdf_content,
- 'mimetype': 'application/pdf',
+ 'name': f'FQC{str_part_number}{str_serial_number}',
+ # 'raw': pdf_content,
+ 'attachment_id': attachment.id,
+ # 'mimetype': 'application/pdf',
'res_id': self.id,
'folder_id': workspace.id,
'res_model': self._name,
}
- # 如果已经有报告,则更新
- if self.report_number_id:
- self.report_number_id.write(doc_vals)
- else:
- # 创建新的document记录
- doc = self.env['documents.document'].create(doc_vals)
- # 关联到当前质检记录
- self.write({
- 'report_number_id': doc.id,
- 'measure_operator': self.env.user.name, # 记录操作人
- 'quality_manager': self.env.user.name, # 记录质检人
- })
+ doc = self.env['documents.document'].create(doc_vals)
+ # 关联到当前质检记录
+ self.write({
+ 'report_number_id': doc.id,
+ 'publish_status': 'published'
+ })
+ # 记录发布历史
+ self.env['quality.check.report.history'].create({
+ 'check_id': self.id,
+ 'report_number_id': doc.id,
+ 'action': 'publish',
+ 'operator': self.env.user.name,
+ 'operation_time': datetime.now(),
+ 'document_status': 'published',
+ 'sequence': len(self.report_history_ids) + 1
+ })
+
+ # 更新流水号
+ self.serial_number += 1
+
+ # 返回成功消息
return True
def do_cancel_publish(self):
"""
- 取消发布出厂检验报告
+ 取消发布出厂检验报告(将当前质检单关联的出厂检验报告文档位置移动到废弃文件夹), 并记录发布历史
"""
- pass
+ self.ensure_one()
+ # 1. 获取已发布的文档文件夹
+ workspace = self.env['documents.folder'].search([('parent_folder_id', '=', self.env.ref('sf_quality.documents_purchase_contracts_folder').id), ('name', '=', '已发布')], limit=1)
+ # 2. 将当前质检单关联的出厂检验报告文档位置移动到废弃文件夹
+ self.report_number_id.write({
+ 'folder_id': self.env.ref('sf_quality.documents_purchase_contracts_folder_canceled').id,
+ })
+ # 3. 更新发布状态
+ self.publish_status = 'canceled'
+ # 3. 记录发布历史
+ self.env['quality.check.report.history'].create({
+ 'check_id': self.id,
+ 'report_number_id': self.report_number_id.id,
+ 'action': 'cancel_publish',
+ 'operator': self.env.user.name,
+ 'operation_time': datetime.now(),
+ 'document_status': 'canceled',
+ 'sequence': len(self.report_history_ids) + 1
+ })
+ return True
+
def do_re_publish(self):
"""
- 重新发布出厂检验报告
+ 重新发布出厂检验报告,参考发布规则
"""
- pass
+ self.do_publish()
+
def generate_qr_code(self):
"""生成二维码URL"""
@@ -725,3 +781,30 @@ class QualityCheckMeasureLine(models.Model):
def del_measure_value(self):
self.ensure_one()
self.sudo().unlink()
+
+
+# 增加出厂检验报告发布历史
+class QualityCheckReportHistory(models.Model):
+ _name = 'quality.check.report.history'
+ _description = '出厂检验报告发布历史'
+
+ check_id = fields.Many2one('quality.check', string='质检单', required=True, ondelete='cascade')
+ report_number_id = fields.Many2one('documents.document', string='报告编号', readonly=True)
+
+ sequence = fields.Integer('序号')
+ # 操作(发布、撤销发布、重新发布)
+ action = fields.Selection([
+ ('publish', '发布'),
+ ('cancel_publish', '撤销发布'),
+ ('re_publish', '重新发布')
+ ], string='操作')
+ # 操作人
+ operator = fields.Char('操作人')
+ # 操作时间
+ operation_time = fields.Datetime('操作时间')
+ # 文档状态(已发布、废弃)
+ document_status = fields.Selection([
+ ('published', '已发布'),
+ ('canceled', '废弃')
+ ], string='操作后文档状态')
+
diff --git a/quality_control/security/ir.model.access.csv b/quality_control/security/ir.model.access.csv
index c0c409a6..d06537b6 100644
--- a/quality_control/security/ir.model.access.csv
+++ b/quality_control/security/ir.model.access.csv
@@ -2,3 +2,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_quality_check_wizard,access.quality_check_wizard,model_quality_check_wizard,quality.group_quality_user,1,1,1,0
access_quality_check_measure_line,quality.check.measure.line,model_quality_check_measure_line,base.group_user,1,1,1,0
access_quality_check_import_complex_model_wizard,quality.check.import.complex.model.wizard,model_quality_check_import_complex_model_wizard,quality.group_quality_user,1,1,1,0
+access_quality_check_report_history,quality.check.report.history,model_quality_check_report_history,quality.group_quality_user,1,1,1,0
\ No newline at end of file
diff --git a/quality_control/views/quality.check.measures.line.xml b/quality_control/views/quality.check.measures.line.xml
index c0e3be04..9000fbd3 100644
--- a/quality_control/views/quality.check.measures.line.xml
+++ b/quality_control/views/quality.check.measures.line.xml
@@ -4,7 +4,7 @@
quality.check.measure.line.tree
quality.check.measure.line
-
+
@@ -14,7 +14,7 @@
-
+
diff --git a/quality_control/views/quality_views.xml b/quality_control/views/quality_views.xml
index c55a6f53..1f7cbbd2 100644
--- a/quality_control/views/quality_views.xml
+++ b/quality_control/views/quality_views.xml
@@ -318,6 +318,7 @@
+
@@ -356,7 +357,16 @@
-
+
+
+
+
+
+
+
+
+
+
diff --git a/sf_quality/data/documents_data.xml b/sf_quality/data/documents_data.xml
index 69fdbe8c..3e18668a 100644
--- a/sf_quality/data/documents_data.xml
+++ b/sf_quality/data/documents_data.xml
@@ -1,11 +1,24 @@
-
+
出厂检验报告
存放出厂检验报告相关文件
11
+
+
+ 已发布
+
+ 1
+
+
+
+ 废弃
+
+ 2
+
+
\ No newline at end of file
diff --git a/sf_quality/views/quality_check_view.xml b/sf_quality/views/quality_check_view.xml
index d288324e..e8c024a6 100644
--- a/sf_quality/views/quality_check_view.xml
+++ b/sf_quality/views/quality_check_view.xml
@@ -70,6 +70,7 @@
+
-
-
-
+
+
+
From 839b3c981da8f5f676280bffb3d463e982789560 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Thu, 13 Mar 2025 16:39:10 +0800
Subject: [PATCH 12/46] =?UTF-8?q?=E9=85=8D=E5=90=88=E5=89=8D=E7=AB=AF?=
=?UTF-8?q?=E5=88=97=E6=95=B0=E4=BF=AE=E6=94=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
quality_control/models/quality.py | 8 +++++---
.../static/src/js/quality.check.measures.line.js | 2 +-
quality_control/views/quality.check.measures.line.xml | 2 +-
3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/quality_control/models/quality.py b/quality_control/models/quality.py
index 16f47a0e..1d32bcb0 100644
--- a/quality_control/models/quality.py
+++ b/quality_control/models/quality.py
@@ -140,6 +140,8 @@ class QualityCheck(models.Model):
# 总数量,值为调拨单_产品明细_数量
total_qty = fields.Char('总数量', compute='_compute_total_qty')
+ column_nums = fields.Integer('测量值列数', default=1)
+
@api.depends('picking_id')
def _compute_total_qty(self):
for record in self:
@@ -204,7 +206,7 @@ class QualityCheck(models.Model):
], string='发布状态', default='draft')
def add_measure_line(self):
- self.total_qty = self.total_qty + 1
+ self.column_nums = self.column_nums + 1
"""
新增测量值,如果测量值有5列了,则提示“最多只能有5列测量值”
"""
@@ -216,7 +218,7 @@ class QualityCheck(models.Model):
# })
def remove_measure_line(self):
- self.total_qty = self.total_qty - 1
+ self.column_nums = self.column_nums - 1
"""
删除测量值
"""
@@ -770,7 +772,7 @@ class QualityCheckMeasureLine(models.Model):
measure_value5 = fields.Char('测量值5')
# 展示列数
- show_colomn_number = fields.Integer('展示列数', default=1)
+ column_nums = fields.Integer('列数', related='check_id.column_nums')
# 判定结果
measure_result = fields.Selection([
diff --git a/quality_control/static/src/js/quality.check.measures.line.js b/quality_control/static/src/js/quality.check.measures.line.js
index 233edda7..1db76151 100644
--- a/quality_control/static/src/js/quality.check.measures.line.js
+++ b/quality_control/static/src/js/quality.check.measures.line.js
@@ -31,7 +31,7 @@ patch(ListRenderer.prototype, 'custom_measure_line_tree_patch', {
setTimeout(() => {
const $dom = $(this.__owl__.bdom.el)
const $thead = $dom.find('thead').find('th');
- const $total_qty = this.props.list.records[0].data.total_qty;
+ const $total_qty = this.props.list.records[0].data.column_nums;
const hideKey = []
const width = $dom.find('[data-name=measure_value1]').width();
$thead.each(function () {
diff --git a/quality_control/views/quality.check.measures.line.xml b/quality_control/views/quality.check.measures.line.xml
index ef77d373..a69c8b98 100644
--- a/quality_control/views/quality.check.measures.line.xml
+++ b/quality_control/views/quality.check.measures.line.xml
@@ -6,7 +6,7 @@
-
+
From 20efa0bdab76eeca58dc14811b927c1957eac2a5 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Thu, 13 Mar 2025 16:44:03 +0800
Subject: [PATCH 13/46] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E9=87=8D=E5=A4=8D?=
=?UTF-8?q?=E5=A3=B0=E6=98=8E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
quality_control/static/src/js/quality.check.measures.line.js | 3 ---
1 file changed, 3 deletions(-)
diff --git a/quality_control/static/src/js/quality.check.measures.line.js b/quality_control/static/src/js/quality.check.measures.line.js
index 1db76151..3a19db67 100644
--- a/quality_control/static/src/js/quality.check.measures.line.js
+++ b/quality_control/static/src/js/quality.check.measures.line.js
@@ -72,9 +72,6 @@ patch(ListRenderer.prototype, 'custom_measure_line_tree_patch', {
getContext() {
return this.context;
},
- getContext() {
- return this.context;
- },
})
registry.category('fields').add('custom_measure_line_tree', QualityCheckMeasureLineTree);
\ No newline at end of file
From c632926348d31a23fbd4d14a0e698e535b59f4b7 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Thu, 13 Mar 2025 17:07:07 +0800
Subject: [PATCH 14/46] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=8A=A5=E8=A1=A8?=
=?UTF-8?q?=E5=B1=95=E7=A4=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
quality_control/views/quality_views.xml | 1 +
.../data/insepection_report_template.xml | 34 +++++++++----------
2 files changed, 18 insertions(+), 17 deletions(-)
diff --git a/quality_control/views/quality_views.xml b/quality_control/views/quality_views.xml
index cf804f1c..18eb945b 100644
--- a/quality_control/views/quality_views.xml
+++ b/quality_control/views/quality_views.xml
@@ -274,6 +274,7 @@
+
diff --git a/sf_quality/data/insepection_report_template.xml b/sf_quality/data/insepection_report_template.xml
index d8df704a..c99e16dc 100644
--- a/sf_quality/data/insepection_report_template.xml
+++ b/sf_quality/data/insepection_report_template.xml
@@ -80,32 +80,32 @@
- | 检测项目 |
- 测量值 |
- 判定 |
- 备注 |
+ 检测项目 |
+ 测量值 |
+ 判定 |
+ 备注 |
|
- 1 |
- 2 |
- 3 |
- 4 |
- 5 |
+ 1 |
+ 2 |
+ 3 |
+ 4 |
+ 5 |
|
|
- |
- |
- |
- |
- |
- |
- |
- |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
From 73ce21ef997640ab2095ad2d7aebc57e86e6c7ed Mon Sep 17 00:00:00 2001
From: hyyy <123@qq.com>
Date: Thu, 13 Mar 2025 17:07:19 +0800
Subject: [PATCH 15/46] =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=8C=E4=BF=AE?=
=?UTF-8?q?=E5=A4=8Dbug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
quality_control/static/src/js/quality.check.measures.line.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/quality_control/static/src/js/quality.check.measures.line.js b/quality_control/static/src/js/quality.check.measures.line.js
index 3a19db67..5e8d2f14 100644
--- a/quality_control/static/src/js/quality.check.measures.line.js
+++ b/quality_control/static/src/js/quality.check.measures.line.js
@@ -31,6 +31,7 @@ patch(ListRenderer.prototype, 'custom_measure_line_tree_patch', {
setTimeout(() => {
const $dom = $(this.__owl__.bdom.el)
const $thead = $dom.find('thead').find('th');
+ if(!this.props.list.records || this.props.list.records.length == 0) return
const $total_qty = this.props.list.records[0].data.column_nums;
const hideKey = []
const width = $dom.find('[data-name=measure_value1]').width();
@@ -48,8 +49,8 @@ patch(ListRenderer.prototype, 'custom_measure_line_tree_patch', {
return `[data-name="${_}"],[name="${_}"]`
})
$dom.find(hideDom.join(',')).hide()
- const $cloNum = $dom.find('.o_data_row').children('td:visible').length;
- $dom.find('.o_data_row').siblings('tr').children('td').prop('colspan', $cloNum);
+ const $cloNum = $dom.find('.o_data_row').eq(0).children('td:visible').length;
+ $dom.find('.o_data_row').siblings('tr:not(.o_data_row)').children('td').prop('colspan', $cloNum);
$dom.find('tfoot').hide()
}, 50);
})
From 16dbfc3867122bfd8f8c0dcb91a9bdbea914c41b Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Thu, 13 Mar 2025 17:13:51 +0800
Subject: [PATCH 16/46] =?UTF-8?q?=E9=99=90=E5=88=B6=E6=B5=8B=E9=87=8F?=
=?UTF-8?q?=E5=88=97=E6=95=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
quality_control/models/quality.py | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/quality_control/models/quality.py b/quality_control/models/quality.py
index 1d32bcb0..396d2a55 100644
--- a/quality_control/models/quality.py
+++ b/quality_control/models/quality.py
@@ -206,24 +206,22 @@ class QualityCheck(models.Model):
], string='发布状态', default='draft')
def add_measure_line(self):
- self.column_nums = self.column_nums + 1
"""
新增测量值,如果测量值有5列了,则提示“最多只能有5列测量值”
"""
- pass
- # self.ensure_one()
- # self.env['quality.check.measure.line'].create({
- # 'check_id': self.id,
- # 'sequence': len(self.measure_line_ids) + 1,
- # })
+ if self.column_nums >= 5:
+ raise UserError(_('最多只能有5列测量值'))
+ else:
+ self.column_nums = self.column_nums + 1
def remove_measure_line(self):
- self.column_nums = self.column_nums - 1
"""
删除测量值
"""
- pass
- # self.ensure_one()
+ if self.column_nums <= 1:
+ raise UserError(_('最少要有1列测量值'))
+ else:
+ self.column_nums = self.column_nums - 1
def do_preview(self):
"""
From 9d84b685257682e86f126b90b87117bdf49527cd Mon Sep 17 00:00:00 2001
From: hyyy <123@qq.com>
Date: Thu, 13 Mar 2025 17:21:22 +0800
Subject: [PATCH 17/46] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=8A=A5=E9=94=99?=
=?UTF-8?q?=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../static/src/js/quality.check.measures.line.js | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/quality_control/static/src/js/quality.check.measures.line.js b/quality_control/static/src/js/quality.check.measures.line.js
index 5e8d2f14..03ae4eea 100644
--- a/quality_control/static/src/js/quality.check.measures.line.js
+++ b/quality_control/static/src/js/quality.check.measures.line.js
@@ -20,16 +20,14 @@ export class QualityCheckMeasureLineTree extends X2ManyField {
patch(ListRenderer.prototype, 'custom_measure_line_tree_patch', {
setup() {
this._super.apply(this, arguments);
- console.log('custom_measure_line_tree_patch');
- console.log(owl);
owl.onMounted(() => {
- console.log($(this.__owl__.bdom.el).find('tfoot'));
$(this.__owl__.bdom.el).find('tfoot').hide()
})
owl.onRendered(() => {
setTimeout(() => {
- const $dom = $(this.__owl__.bdom.el)
+ const $dom = $(this.__owl__?.bdom?.el)
+ if(!$dom || $dom.length) return
const $thead = $dom.find('thead').find('th');
if(!this.props.list.records || this.props.list.records.length == 0) return
const $total_qty = this.props.list.records[0].data.column_nums;
From 2d4926f8b7419dabc012c02c8a06c03d0e7e808e Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Thu, 13 Mar 2025 17:23:26 +0800
Subject: [PATCH 18/46] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=8A=E4=BC=A0?=
=?UTF-8?q?=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
quality_control/wizard/import_complex_model.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/quality_control/wizard/import_complex_model.py b/quality_control/wizard/import_complex_model.py
index e53e7bcd..fef2e4ba 100644
--- a/quality_control/wizard/import_complex_model.py
+++ b/quality_control/wizard/import_complex_model.py
@@ -163,6 +163,9 @@ class ImportComplexModelWizard(models.TransientModel):
if not any(row):
continue
+ if row[2] == '':
+ continue
+
# 创建quality.check.measure.line记录
measure_line_vals = {
'check_id': quality_check.id,
From bd2ba3bb49c17f230f4f4a8be38dc23bc49a2ee5 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Thu, 13 Mar 2025 17:55:13 +0800
Subject: [PATCH 19/46] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=90=88=E6=A0=BC?=
=?UTF-8?q?=E5=9B=BE=E7=AB=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
quality_control/models/quality.py | 4 ++--
.../src/js/quality.check.measures.line.js | 2 +-
sf_quality/data/insepection_report_template.xml | 9 +++++++--
sf_quality/static/img/pass.png | Bin 0 -> 25514 bytes
4 files changed, 10 insertions(+), 5 deletions(-)
create mode 100644 sf_quality/static/img/pass.png
diff --git a/quality_control/models/quality.py b/quality_control/models/quality.py
index 396d2a55..7992021f 100644
--- a/quality_control/models/quality.py
+++ b/quality_control/models/quality.py
@@ -774,8 +774,8 @@ class QualityCheckMeasureLine(models.Model):
# 判定结果
measure_result = fields.Selection([
- ('OK', 'OK'),
- ('NG', 'NG')
+ ('OK', '合格'),
+ ('NG', '不合格')
], string='判定', default='OK')
remark = fields.Char('备注')
diff --git a/quality_control/static/src/js/quality.check.measures.line.js b/quality_control/static/src/js/quality.check.measures.line.js
index 03ae4eea..b64f3feb 100644
--- a/quality_control/static/src/js/quality.check.measures.line.js
+++ b/quality_control/static/src/js/quality.check.measures.line.js
@@ -27,7 +27,7 @@ patch(ListRenderer.prototype, 'custom_measure_line_tree_patch', {
owl.onRendered(() => {
setTimeout(() => {
const $dom = $(this.__owl__?.bdom?.el)
- if(!$dom || $dom.length) return
+ if(!$dom || !$dom.length) return
const $thead = $dom.find('thead').find('th');
if(!this.props.list.records || this.props.list.records.length == 0) return
const $total_qty = this.props.list.records[0].data.column_nums;
diff --git a/sf_quality/data/insepection_report_template.xml b/sf_quality/data/insepection_report_template.xml
index c99e16dc..7392838f 100644
--- a/sf_quality/data/insepection_report_template.xml
+++ b/sf_quality/data/insepection_report_template.xml
@@ -77,6 +77,7 @@
检验结果
+
-
+

+
+
检验结论:
@@ -129,7 +132,9 @@
质检员:
-
+
+
+
(以下空白)
diff --git a/sf_quality/static/img/pass.png b/sf_quality/static/img/pass.png
new file mode 100644
index 0000000000000000000000000000000000000000..2c5fef0c167ad295db13bbb2e597fde2ae2bd5e8
GIT binary patch
literal 25514
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
literal 0
HcmV?d00001
From 424a496046b58895e3ac270a8eb30e03bd5b6677 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Thu, 13 Mar 2025 18:33:26 +0800
Subject: [PATCH 20/46] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=A2=84=E8=A7=88?=
=?UTF-8?q?=E6=8A=A5=E8=A1=A8model=E5=90=8D=E5=AD=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_quality/data/report_actions.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sf_quality/data/report_actions.xml b/sf_quality/data/report_actions.xml
index 2d875d71..215021a9 100644
--- a/sf_quality/data/report_actions.xml
+++ b/sf_quality/data/report_actions.xml
@@ -14,7 +14,7 @@
预览检验报告
- quality.inspection
+ quality.check
qweb-html
sf_quality.report_quality_inspection
report
From af3a2880e8518047b7afca6e271cb757345d683c Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Thu, 13 Mar 2025 18:55:59 +0800
Subject: [PATCH 21/46] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8F=91=E5=B8=83?=
=?UTF-8?q?=E5=89=8D=E6=A0=A1=E9=AA=8C=E6=95=B0=E6=8D=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
quality_control/models/quality.py | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/quality_control/models/quality.py b/quality_control/models/quality.py
index 7992021f..2c25cd34 100644
--- a/quality_control/models/quality.py
+++ b/quality_control/models/quality.py
@@ -232,6 +232,8 @@ class QualityCheck(models.Model):
def do_publish(self):
"""发布出厂检验报告"""
self.ensure_one()
+ self._check_measure_line()
+ self._check_check_qty_and_total_qty()
# 1. 获取报告动作
report_action = self.env.ref('sf_quality.action_report_quality_inspection')
@@ -294,6 +296,26 @@ class QualityCheck(models.Model):
# 返回成功消息
return True
+ # 发布前校验明细行列均非空
+ def _check_measure_line(self):
+ for record in self:
+ if not record.measure_line_ids:
+ raise UserError(_('请先添加测量明细'))
+ for line in record.measure_line_ids:
+ if not line.measure_item:
+ raise UserError(_('有检测项目值为空'))
+ for i in range(1, record.column_nums + 1):
+ if not getattr(line, f'measure_value{i}'):
+ raise UserError(_('有测量值为空'))
+
+
+ # 发布前校验检验数与总数量、检验数与测量件数(即测量列数)
+ def _check_check_qty_and_total_qty(self):
+ for record in self:
+ if int(record.check_qty) <= int(record.total_qty):
+ raise UserError(_('检验数不可超过总数量'))
+ if int(record.column_nums) >= int(record.check_qty):
+ raise UserError(_('测量件数不可超过检验数'))
def do_cancel_publish(self):
"""
From 954ff6b848bc4bbbc5ae57809b48050c7263e822 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Thu, 13 Mar 2025 19:13:57 +0800
Subject: [PATCH 22/46] =?UTF-8?q?=E5=AF=B9=E5=8F=91=E5=B8=83=E6=8C=89?=
=?UTF-8?q?=E9=92=AE=E8=BF=9B=E8=A1=8C=E4=BA=8C=E6=AC=A1=E7=A1=AE=E8=AE=A4?=
=?UTF-8?q?=E6=8E=A7=E5=88=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
quality_control/__manifest__.py | 1 +
quality_control/models/quality.py | 56 ++++++++++++++-----
quality_control/security/ir.model.access.csv | 3 +-
quality_control/wizard/__init__.py | 1 +
quality_control/wizard/quality_wizard.py | 18 ++++++
.../wizard/quality_wizard_view.xml | 19 +++++++
6 files changed, 82 insertions(+), 16 deletions(-)
create mode 100644 quality_control/wizard/quality_wizard.py
create mode 100644 quality_control/wizard/quality_wizard_view.xml
diff --git a/quality_control/__manifest__.py b/quality_control/__manifest__.py
index b1eecb9a..ff899d4a 100644
--- a/quality_control/__manifest__.py
+++ b/quality_control/__manifest__.py
@@ -21,6 +21,7 @@ Quality Control
'data': [
'data/quality_control_data.xml',
'wizard/import_complex_model.xml',
+ 'wizard/quality_wizard_view.xml',
'report/worksheet_custom_reports.xml',
'report/worksheet_custom_report_templates.xml',
'views/quality_views.xml',
diff --git a/quality_control/models/quality.py b/quality_control/models/quality.py
index 2c25cd34..cff74112 100644
--- a/quality_control/models/quality.py
+++ b/quality_control/models/quality.py
@@ -152,6 +152,7 @@ class QualityCheck(models.Model):
record.total_qty = total_qty if total_qty > 0 else ''
else:
record.total_qty = ''
+
# 检验数
check_qty = fields.Char('检验数', default=lambda self: self._get_default_check_qty())
@@ -232,12 +233,33 @@ class QualityCheck(models.Model):
def do_publish(self):
"""发布出厂检验报告"""
self.ensure_one()
- self._check_measure_line()
- self._check_check_qty_and_total_qty()
-
+ # self._check_measure_line()
+ # self._check_check_qty_and_total_qty()
+
+ # 打开确认向导而不是直接发布
+ return {
+ 'name': _('发布确认'),
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'quality.check.publish.wizard',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'context': {
+ 'default_check_id': self.id,
+ 'default_product_name': self.product_id.name,
+ 'default_total_qty': self.total_qty,
+ 'default_check_qty': self.check_qty,
+ 'default_measure_count': self.column_nums,
+ 'default_item_count': len(self.measure_line_ids),
+ }
+ }
+
+ def _do_publish_implementation(self):
+ """实际执行发布操作的方法"""
+ self.ensure_one()
+
# 1. 获取报告动作
report_action = self.env.ref('sf_quality.action_report_quality_inspection')
-
+
# 2. 生成PDF报告 - 修改这里的调用方式
pdf_content, _ = report_action._render_qweb_pdf(
report_ref=report_action.report_name, # 添加report_ref参数
@@ -254,12 +276,14 @@ class QualityCheck(models.Model):
})
# 获取已发布的文档文件夹
- workspace = self.env['documents.folder'].search([('parent_folder_id', '=', self.env.ref('sf_quality.documents_purchase_contracts_folder').id), ('name', '=', '已发布')], limit=1)
+ workspace = self.env['documents.folder'].search(
+ [('parent_folder_id', '=', self.env.ref('sf_quality.documents_purchase_contracts_folder').id),
+ ('name', '=', '已发布')], limit=1)
if self.serial_number > 99:
raise UserError(_('流水号不能大于99'))
- str_serial_number = '0' + str(self.serial_number) if self.serial_number < 10 else str(self.serial_number)
+ str_serial_number = '0' + str(self.serial_number) if self.serial_number < 10 else str(self.serial_number)
str_part_number = self.part_number if self.part_number else ''
# 3. 创建文档记录
doc_vals = {
@@ -271,14 +295,14 @@ class QualityCheck(models.Model):
'folder_id': workspace.id,
'res_model': self._name,
}
-
+
doc = self.env['documents.document'].create(doc_vals)
# 关联到当前质检记录
self.write({
'report_number_id': doc.id,
'publish_status': 'published'
})
-
+
# 记录发布历史
self.env['quality.check.report.history'].create({
'check_id': self.id,
@@ -295,7 +319,7 @@ class QualityCheck(models.Model):
# 返回成功消息
return True
-
+
# 发布前校验明细行列均非空
def _check_measure_line(self):
for record in self:
@@ -307,11 +331,14 @@ class QualityCheck(models.Model):
for i in range(1, record.column_nums + 1):
if not getattr(line, f'measure_value{i}'):
raise UserError(_('有测量值为空'))
-
-
+
# 发布前校验检验数与总数量、检验数与测量件数(即测量列数)
def _check_check_qty_and_total_qty(self):
for record in self:
+ if not record.check_qty:
+ raise UserError(_('请先输入检验数'))
+ if not record.total_qty:
+ raise UserError(_('总数量不能为空'))
if int(record.check_qty) <= int(record.total_qty):
raise UserError(_('检验数不可超过总数量'))
if int(record.column_nums) >= int(record.check_qty):
@@ -323,7 +350,9 @@ class QualityCheck(models.Model):
"""
self.ensure_one()
# 1. 获取已发布的文档文件夹
- workspace = self.env['documents.folder'].search([('parent_folder_id', '=', self.env.ref('sf_quality.documents_purchase_contracts_folder').id), ('name', '=', '已发布')], limit=1)
+ workspace = self.env['documents.folder'].search(
+ [('parent_folder_id', '=', self.env.ref('sf_quality.documents_purchase_contracts_folder').id),
+ ('name', '=', '已发布')], limit=1)
# 2. 将当前质检单关联的出厂检验报告文档位置移动到废弃文件夹
self.report_number_id.write({
'folder_id': self.env.ref('sf_quality.documents_purchase_contracts_folder_canceled').id,
@@ -342,13 +371,11 @@ class QualityCheck(models.Model):
})
return True
-
def do_re_publish(self):
"""
重新发布出厂检验报告,参考发布规则
"""
self.do_publish()
-
def generate_qr_code(self):
"""生成二维码URL"""
@@ -831,4 +858,3 @@ class QualityCheckReportHistory(models.Model):
('published', '已发布'),
('canceled', '废弃')
], string='操作后文档状态')
-
diff --git a/quality_control/security/ir.model.access.csv b/quality_control/security/ir.model.access.csv
index d06537b6..e6911403 100644
--- a/quality_control/security/ir.model.access.csv
+++ b/quality_control/security/ir.model.access.csv
@@ -2,4 +2,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_quality_check_wizard,access.quality_check_wizard,model_quality_check_wizard,quality.group_quality_user,1,1,1,0
access_quality_check_measure_line,quality.check.measure.line,model_quality_check_measure_line,base.group_user,1,1,1,0
access_quality_check_import_complex_model_wizard,quality.check.import.complex.model.wizard,model_quality_check_import_complex_model_wizard,quality.group_quality_user,1,1,1,0
-access_quality_check_report_history,quality.check.report.history,model_quality_check_report_history,quality.group_quality_user,1,1,1,0
\ No newline at end of file
+access_quality_check_report_history,quality.check.report.history,model_quality_check_report_history,quality.group_quality_user,1,1,1,0
+access_quality_check_publish_wizard,quality.check.publish.wizard,model_quality_check_publish_wizard,quality.group_quality_user,1,1,1,0
\ No newline at end of file
diff --git a/quality_control/wizard/__init__.py b/quality_control/wizard/__init__.py
index 49415fd2..5aa64e16 100644
--- a/quality_control/wizard/__init__.py
+++ b/quality_control/wizard/__init__.py
@@ -3,3 +3,4 @@
from . import quality_check_wizard
from . import import_complex_model
+from . import quality_wizard
diff --git a/quality_control/wizard/quality_wizard.py b/quality_control/wizard/quality_wizard.py
new file mode 100644
index 00000000..3768792a
--- /dev/null
+++ b/quality_control/wizard/quality_wizard.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+from odoo import api, fields, models, _
+
+class QualityCheckPublishWizard(models.TransientModel):
+ _name = 'quality.check.publish.wizard'
+ _description = '质检报告发布确认'
+
+ check_id = fields.Many2one('quality.check', string='质检单', required=True)
+ product_name = fields.Char('产品名称', readonly=True)
+ total_qty = fields.Char('总数量', readonly=True)
+ check_qty = fields.Char('检验数', readonly=True)
+ measure_count = fields.Integer('测量件数', readonly=True)
+ item_count = fields.Integer('检验项目数', readonly=True)
+
+ def action_confirm_publish(self):
+ """确认发布"""
+ self.ensure_one()
+ return self.check_id._do_publish_implementation()
\ No newline at end of file
diff --git a/quality_control/wizard/quality_wizard_view.xml b/quality_control/wizard/quality_wizard_view.xml
new file mode 100644
index 00000000..90bc3c05
--- /dev/null
+++ b/quality_control/wizard/quality_wizard_view.xml
@@ -0,0 +1,19 @@
+
+
+
+ quality.check.publish.wizard.form
+ quality.check.publish.wizard
+
+
+
+
+
\ No newline at end of file
From 7b6dda3d75cdaee7be39d4a9c12a3e20f1fe8763 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Thu, 13 Mar 2025 19:17:49 +0800
Subject: [PATCH 23/46] =?UTF-8?q?=E9=87=8D=E6=96=B0=E5=8F=91=E5=B8=83?=
=?UTF-8?q?=E6=8C=89=E9=92=AE=E4=B9=9F=E5=A2=9E=E5=8A=A0=E4=BA=8C=E6=AC=A1?=
=?UTF-8?q?=E7=A1=AE=E8=AE=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
quality_control/models/quality.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/quality_control/models/quality.py b/quality_control/models/quality.py
index cff74112..aaf8c26c 100644
--- a/quality_control/models/quality.py
+++ b/quality_control/models/quality.py
@@ -375,7 +375,7 @@ class QualityCheck(models.Model):
"""
重新发布出厂检验报告,参考发布规则
"""
- self.do_publish()
+ return self.do_publish()
def generate_qr_code(self):
"""生成二维码URL"""
From 35bcfa6fa14526238d89476fdc89768fda277846 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Fri, 14 Mar 2025 09:50:13 +0800
Subject: [PATCH 24/46] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=8C=89=E9=92=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sf_quality/views/quality_check_view.xml | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/sf_quality/views/quality_check_view.xml b/sf_quality/views/quality_check_view.xml
index e8c024a6..886056d4 100644
--- a/sf_quality/views/quality_check_view.xml
+++ b/sf_quality/views/quality_check_view.xml
@@ -72,14 +72,14 @@
-
-
-
+ class="oe_highlight" attrs="{'invisible': [('is_out_check', '=', False)]}"/>
+
+
+
+
+
From f9525beb65ca1fa76bc200e93bc8c8cab871cbc5 Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Fri, 14 Mar 2025 10:44:54 +0800
Subject: [PATCH 25/46] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=8F=91=E5=B8=83?=
=?UTF-8?q?=E4=B8=9A=E5=8A=A1=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
quality_control/models/quality.py | 39 ++++++++++++++++++++----------
sf_quality/data/report_actions.xml | 1 +
2 files changed, 27 insertions(+), 13 deletions(-)
diff --git a/quality_control/models/quality.py b/quality_control/models/quality.py
index aaf8c26c..a94ed623 100644
--- a/quality_control/models/quality.py
+++ b/quality_control/models/quality.py
@@ -233,6 +233,7 @@ class QualityCheck(models.Model):
def do_publish(self):
"""发布出厂检验报告"""
self.ensure_one()
+ self._check_part_number()
# self._check_measure_line()
# self._check_check_qty_and_total_qty()
@@ -266,14 +267,14 @@ class QualityCheck(models.Model):
res_ids=self.ids
)
- attachment = self.env['ir.attachment'].create({
- 'name': self.name,
- 'type': 'binary',
- 'datas': pdf_content,
- 'res_model': self._name,
- 'res_id': self.id,
- 'mimetype': 'application/pdf',
- })
+ # attachment = self.env['ir.attachment'].create({
+ # 'name': f'{self.name}.pdf',
+ # 'type': 'binary',
+ # 'datas': b64encode(pdf_content),
+ # 'res_model': self._name,
+ # 'res_id': self.id,
+ # 'mimetype': 'application/pdf',
+ # })
# 获取已发布的文档文件夹
workspace = self.env['documents.folder'].search(
@@ -288,9 +289,9 @@ class QualityCheck(models.Model):
# 3. 创建文档记录
doc_vals = {
'name': f'FQC{str_part_number}{str_serial_number}',
- # 'raw': pdf_content,
- 'attachment_id': attachment.id,
- # 'mimetype': 'application/pdf',
+ 'raw': pdf_content,
+ # 'attachment_id': attachment.id,
+ 'mimetype': 'application/pdf',
'res_id': self.id,
'folder_id': workspace.id,
'res_model': self._name,
@@ -320,6 +321,13 @@ class QualityCheck(models.Model):
# 返回成功消息
return True
+ # 发布前检验零件图号、操机员、质检员
+ def _check_part_number(self):
+ if not self.part_number:
+ raise UserError(_('零件图号不能为空'))
+ if not self.measure_operator:
+ raise UserError(_('操机员不能为空'))
+
# 发布前校验明细行列均非空
def _check_measure_line(self):
for record in self:
@@ -357,8 +365,7 @@ class QualityCheck(models.Model):
self.report_number_id.write({
'folder_id': self.env.ref('sf_quality.documents_purchase_contracts_folder_canceled').id,
})
- # 3. 更新发布状态
- self.publish_status = 'canceled'
+
# 3. 记录发布历史
self.env['quality.check.report.history'].create({
'check_id': self.id,
@@ -369,6 +376,12 @@ class QualityCheck(models.Model):
'document_status': 'canceled',
'sequence': len(self.report_history_ids) + 1
})
+
+ # 3. 更新发布状态
+ self.write({
+ 'publish_status': 'canceled',
+ 'report_number_id': False
+ })
return True
def do_re_publish(self):
diff --git a/sf_quality/data/report_actions.xml b/sf_quality/data/report_actions.xml
index 215021a9..b65a4b55 100644
--- a/sf_quality/data/report_actions.xml
+++ b/sf_quality/data/report_actions.xml
@@ -9,6 +9,7 @@
'QC-' + object.name + '.pdf'
report
+ False
From f66ff6339dd45124b0deff795c1761f618797f9c Mon Sep 17 00:00:00 2001
From: hyyy <123@qq.com>
Date: Fri, 14 Mar 2025 10:52:50 +0800
Subject: [PATCH 26/46] =?UTF-8?q?=E4=BF=AE=E6=94=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/js/quality.check.measures.line.js | 76 -------------------
.../views/quality.check.measures.line.xml | 10 +--
quality_control/views/quality_views.xml | 2 +-
3 files changed, 6 insertions(+), 82 deletions(-)
delete mode 100644 quality_control/static/src/js/quality.check.measures.line.js
diff --git a/quality_control/static/src/js/quality.check.measures.line.js b/quality_control/static/src/js/quality.check.measures.line.js
deleted file mode 100644
index b64f3feb..00000000
--- a/quality_control/static/src/js/quality.check.measures.line.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/** @odoo-module **/
-
-import { patch } from "@web/core/utils/patch";
-import { ListRenderer } from "@web/views/list/list_renderer";
-import { X2ManyField } from '@web/views/fields/x2many/x2many_field';
-import { registry } from '@web/core/registry';
-
-
-export class QualityCheckMeasureLineTree extends X2ManyField {
- setup() {
- super.setup();
- }
-
- get rendererProps() {
- const props = super.rendererProps;
- return props;
- }
-}
-
-patch(ListRenderer.prototype, 'custom_measure_line_tree_patch', {
- setup() {
- this._super.apply(this, arguments);
- owl.onMounted(() => {
- $(this.__owl__.bdom.el).find('tfoot').hide()
- })
-
- owl.onRendered(() => {
- setTimeout(() => {
- const $dom = $(this.__owl__?.bdom?.el)
- if(!$dom || !$dom.length) return
- const $thead = $dom.find('thead').find('th');
- if(!this.props.list.records || this.props.list.records.length == 0) return
- const $total_qty = this.props.list.records[0].data.column_nums;
- const hideKey = []
- const width = $dom.find('[data-name=measure_value1]').width();
- $thead.each(function () {
- const key = $(this).attr('data-name');
- if (key && key.indexOf('measure_value') >= 0) {
- const keyNum = Number(key.replace('measure_value', ''));
- if (keyNum > $total_qty) {
- hideKey.push(key);
- }
- }
- });
- $dom.find(':hidden').show().css('width', width);
- const hideDom = hideKey.map(_ => {
- return `[data-name="${_}"],[name="${_}"]`
- })
- $dom.find(hideDom.join(',')).hide()
- const $cloNum = $dom.find('.o_data_row').eq(0).children('td:visible').length;
- $dom.find('.o_data_row').siblings('tr:not(.o_data_row)').children('td').prop('colspan', $cloNum);
- $dom.find('tfoot').hide()
- }, 50);
- })
-
- },
-
-
- // get hasSelectors() {
- // return this.withSelectorColumn || (this.props.allowSelectors && !this.env.isSmall);
- // },
-
-
- _renderRow(list) {
- console.log('list', list);
- // if(!this.__owl__.bdom || !this.__owl__.bdom.el) return;
- // const $row = $(this.__owl__.bdom.el).find('tbody').children('tr');
- return '';
- },
-
- getContext() {
- return this.context;
- },
-})
-
-registry.category('fields').add('custom_measure_line_tree', QualityCheckMeasureLineTree);
\ No newline at end of file
diff --git a/quality_control/views/quality.check.measures.line.xml b/quality_control/views/quality.check.measures.line.xml
index a69c8b98..185591a0 100644
--- a/quality_control/views/quality.check.measures.line.xml
+++ b/quality_control/views/quality.check.measures.line.xml
@@ -8,11 +8,11 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/quality_control/views/quality_views.xml b/quality_control/views/quality_views.xml
index 18eb945b..ae0e5452 100644
--- a/quality_control/views/quality_views.xml
+++ b/quality_control/views/quality_views.xml
@@ -341,7 +341,7 @@
-
+
From c23aa4de7522df2585cb6ca0d2db1ca42071e31c Mon Sep 17 00:00:00 2001
From: mgw <1392924357@qq.com>
Date: Fri, 14 Mar 2025 11:13:50 +0800
Subject: [PATCH 27/46] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=8A=A5=E5=91=8A?=
=?UTF-8?q?=E6=A0=B7=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
quality_control/models/quality.py | 1 +
.../data/insepection_report_template.xml | 23 ++++++++++---------
2 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/quality_control/models/quality.py b/quality_control/models/quality.py
index a94ed623..73a0c534 100644
--- a/quality_control/models/quality.py
+++ b/quality_control/models/quality.py
@@ -317,6 +317,7 @@ class QualityCheck(models.Model):
# 更新流水号
self.serial_number += 1
+ self.quality_manager = self.env.user.id
# 返回成功消息
return True
diff --git a/sf_quality/data/insepection_report_template.xml b/sf_quality/data/insepection_report_template.xml
index 7392838f..3edbe087 100644
--- a/sf_quality/data/insepection_report_template.xml
+++ b/sf_quality/data/insepection_report_template.xml
@@ -4,8 +4,12 @@