From 4274b9fe99e5dd433661f942c605cd00d46f9f23 Mon Sep 17 00:00:00 2001 From: yuxianghui <3437689193@qq.com> Date: Fri, 20 Jun 2025 17:53:04 +0800 Subject: [PATCH 01/33] =?UTF-8?q?=E5=87=BA=E5=8E=82=E6=A3=80=E9=AA=8C?= =?UTF-8?q?=E6=8A=A5=E5=91=8A-=E9=A1=B5=E8=84=9A=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=9B=9E=E9=80=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_quality/data/insepection_report_template.xml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/sf_quality/data/insepection_report_template.xml b/sf_quality/data/insepection_report_template.xml index dbc28574..263afc9d 100644 --- a/sf_quality/data/insepection_report_template.xml +++ b/sf_quality/data/insepection_report_template.xml @@ -203,13 +203,8 @@ - + - -
- -
-
@@ -335,13 +330,8 @@ - + - -
- -
-
From b177bd95ff5c0bce29f7dcf1c7de868249255652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?= Date: Mon, 23 Jun 2025 11:04:44 +0800 Subject: [PATCH 02/33] =?UTF-8?q?=E5=B0=86fetchCNC=E7=A7=BB=E5=8A=A8?= =?UTF-8?q?=E5=88=B0sf=5Fmrs=5Fconnect=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/models/mrp_production.py | 80 -------------------- sf_mrs_connect/models/mrp_production.py | 91 ++++++++++++++++++++++- 2 files changed, 88 insertions(+), 83 deletions(-) diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index 2d0545af..eac183d7 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -740,86 +740,6 @@ class MrpProduction(models.Model): logging.info('change_programming_state error:%s' % e) raise UserError("修改编程单状态失败,请联系管理员") - # cnc程序获取 - def fetchCNC(self, production_names): - cnc = self.env['mrp.production'].search([('id', '=', self.id)]) - quick_order = False - if cnc.product_id.default_code: - quick_order = self.env['quick.easy.order'].search( - [('name', '=', cnc.product_id.default_code.rsplit('-', 1)[0])]) - programme_way = False - if cnc.manual_quotation is True: - programme_way = 'manual operation' - else: - programme_way = 'auto' - if cnc.production_type == '人工线下加工': - programme_way = 'manual operation' - if quick_order: - programme_way = 'manual operation' - try: - res = { - 'production_no': production_names, - 'machine_tool_code': '', - 'product_name': cnc.product_id.name, - 'remanufacture_type': '', - 'model_code': cnc.product_id.model_code, - 'material_code': self.env['sf.production.materials'].search( - [('id', '=', cnc.product_id.materials_id.id)]).materials_no, - 'material_type_code': self.env['sf.materials.model'].search( - [('id', '=', cnc.product_id.materials_type_id.id)]).materials_no, - 'machining_processing_panel': cnc.product_id.model_processing_panel, - 'machining_precision': '', - 'embryo_long': cnc.product_id.bom_ids[0].bom_line_ids.product_id.length, - 'embryo_height': cnc.product_id.bom_ids[0].bom_line_ids.product_id.height, - 'embryo_width': cnc.product_id.bom_ids[0].bom_line_ids.product_id.width, - 'order_no': cnc.origin, - 'model_order_no': cnc.product_id.default_code, - 'user': cnc.env.user.name, - 'programme_way': programme_way, - # 'model_file': '' if not cnc.product_id.model_file else base64.b64encode( - # cnc.product_id.model_file).decode('utf-8'), - # 'glb_url': cnc.product_id.glb_url, - 'part_name': cnc.product_id.part_name, - 'part_number': cnc.product_id.part_number, - 'machining_drawings': base64.b64encode(cnc.product_id.machining_drawings).decode( - 'utf-8') if cnc.product_id.machining_drawings else '', - 'machining_drawings_name': cnc.product_id.machining_drawings_name, - 'machining_drawings_mimetype': cnc.product_id.machining_drawings_mimetype, - # 'model_id': cnc.product_id.model_id, - } - # 打印出除了 model_file 之外的所有键值对 - for key, value in res.items(): - if key != 'model_file': - logging.info('%s: %s' % (key, value)) - configsettings = self.env['res.config.settings'].get_values() - config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key']) - url = '/api/intelligent_programming/create' - config_url = configsettings['sf_url'] + url - res['token'] = configsettings['token'] - # res_str = json.dumps(res) - ret = requests.post(config_url, json={}, data=res, headers=config_header) - ret = ret.json() - logging.info('fetchCNC-ret:%s' % ret) - if ret['status'] == 1: - self.write( - {'programming_no': ret['programming_no'], 'programming_state': '编程中', 'work_state': '编程中'}) - # 生成编程记录 - self.programming_record_ids.create({ - 'number': len(self.programming_record_ids) + 1, - 'production_id': self.id, - 'reason': '首次下发', - 'programming_method': False, - 'current_programming_count': False, - 'target_production_id': False, - 'apply_time': fields.Datetime.now(), - 'send_time': False, - }) - else: - raise UserError(ret['message']) - except Exception as e: - logging.info('fetchCNC error:%s' % e) - raise UserError("cnc程序获取编程单失败,请联系管理员") - # 维修模块按钮 def button_maintenance_req(self): self.ensure_one() diff --git a/sf_mrs_connect/models/mrp_production.py b/sf_mrs_connect/models/mrp_production.py index ac3f6640..ec740abd 100644 --- a/sf_mrs_connect/models/mrp_production.py +++ b/sf_mrs_connect/models/mrp_production.py @@ -1,6 +1,11 @@ +import base64 +import requests +import logging + from itertools import groupby -from odoo import models, api -from odoo.tools.misc import OrderedSet +from odoo import models, api, fields +from odoo.exceptions import UserError +from odoo.addons.sf_base.commons.common import Common class MrpProduction(models.Model): @@ -41,4 +46,84 @@ class MrpProduction(models.Model): 'programming_no': grouped_product_programming_no[production.product_id.id], 'programming_state': '编程中' }) - return productions \ No newline at end of file + return productions + + # cnc程序获取 + def fetchCNC(self, production_names): + cnc = self.env['mrp.production'].search([('id', '=', self.id)]) + quick_order = False + if cnc.product_id.default_code: + quick_order = self.env['quick.easy.order'].search( + [('name', '=', cnc.product_id.default_code.rsplit('-', 1)[0])]) + programme_way = False + if cnc.manual_quotation is True: + programme_way = 'manual operation' + else: + programme_way = 'auto' + if cnc.production_type == '人工线下加工': + programme_way = 'manual operation' + if quick_order: + programme_way = 'manual operation' + try: + res = { + 'production_no': production_names, + 'machine_tool_code': '', + 'product_name': cnc.product_id.name, + 'remanufacture_type': '', + 'model_code': cnc.product_id.model_code, + 'material_code': self.env['sf.production.materials'].search( + [('id', '=', cnc.product_id.materials_id.id)]).materials_no, + 'material_type_code': self.env['sf.materials.model'].search( + [('id', '=', cnc.product_id.materials_type_id.id)]).materials_no, + 'machining_processing_panel': cnc.product_id.model_processing_panel, + 'machining_precision': '', + 'embryo_long': cnc.product_id.bom_ids[0].bom_line_ids.product_id.length, + 'embryo_height': cnc.product_id.bom_ids[0].bom_line_ids.product_id.height, + 'embryo_width': cnc.product_id.bom_ids[0].bom_line_ids.product_id.width, + 'order_no': cnc.origin, + 'model_order_no': cnc.product_id.default_code, + 'user': cnc.env.user.name, + 'programme_way': programme_way, + # 'model_file': '' if not cnc.product_id.model_file else base64.b64encode( + # cnc.product_id.model_file).decode('utf-8'), + # 'glb_url': cnc.product_id.glb_url, + 'part_name': cnc.product_id.part_name, + 'part_number': cnc.product_id.part_number, + 'machining_drawings': base64.b64encode(cnc.product_id.machining_drawings).decode( + 'utf-8') if cnc.product_id.machining_drawings else '', + 'machining_drawings_name': cnc.product_id.machining_drawings_name, + 'machining_drawings_mimetype': cnc.product_id.machining_drawings_mimetype, + # 'model_id': cnc.product_id.model_id, + } + # 打印出除了 model_file 之外的所有键值对 + for key, value in res.items(): + if key != 'model_file': + logging.info('%s: %s' % (key, value)) + configsettings = self.env['res.config.settings'].get_values() + config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key']) + url = '/api/intelligent_programming/create' + config_url = configsettings['sf_url'] + url + res['token'] = configsettings['token'] + # res_str = json.dumps(res) + ret = requests.post(config_url, json={}, data=res, headers=config_header) + ret = ret.json() + logging.info('fetchCNC-ret:%s' % ret) + if ret['status'] == 1: + self.write( + {'programming_no': ret['programming_no'], 'programming_state': '编程中', 'work_state': '编程中'}) + # 生成编程记录 + self.programming_record_ids.create({ + 'number': len(self.programming_record_ids) + 1, + 'production_id': self.id, + 'reason': '首次下发', + 'programming_method': False, + 'current_programming_count': False, + 'target_production_id': False, + 'apply_time': fields.Datetime.now(), + 'send_time': False, + }) + else: + raise UserError(ret['message']) + except Exception as e: + logging.info('fetchCNC error:%s' % e) + raise UserError("cnc程序获取编程单失败,请联系管理员") \ No newline at end of file From fe3492ceb5eb8b33fd8f261e743510427ca25094 Mon Sep 17 00:00:00 2001 From: guanhuan Date: Tue, 24 Jun 2025 11:32:41 +0800 Subject: [PATCH 03/33] =?UTF-8?q?=E5=9D=AF=E6=96=99=E5=B0=BA=E5=AF=B8?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../models/sf_production_demand_plan.py | 52 ++++++++++--------- sf_demand_plan/views/demand_plan.xml | 2 +- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/sf_demand_plan/models/sf_production_demand_plan.py b/sf_demand_plan/models/sf_production_demand_plan.py index 79fcb99b..1e1459d3 100644 --- a/sf_demand_plan/models/sf_production_demand_plan.py +++ b/sf_demand_plan/models/sf_production_demand_plan.py @@ -196,11 +196,14 @@ class SfProductionDemandPlan(models.Model): else: line.model_long = None - @api.depends('product_id.model_long', 'product_id.model_width', 'product_id.model_height') + @api.depends('product_id.model_long', 'product_id.model_width', 'product_id.model_height', 'product_id.blank_type') def _compute_embryo_long(self): for line in self: if line.product_id: - line.embryo_long = f"{round(line.product_id.model_long, 3)}*{round(line.product_id.model_width, 3)}*{round(line.product_id.model_height, 3)}" + if line.product_id.blank_type == '圆料': + line.embryo_long = f"Ø{round(line.product_id.model_width, 3)}*{round(line.product_id.model_long, 3)}" + else: + line.embryo_long = f"{round(line.product_id.model_long, 3)}*{round(line.product_id.model_width, 3)}*{round(line.product_id.model_height, 3)}" else: line.embryo_long = None @@ -399,29 +402,30 @@ class SfProductionDemandPlan(models.Model): outsourcing_purchase_request.extend(pr_ids.ids) if record.supply_method == 'outsourcing' and not record.sale_order_line_id.is_incoming_material: bom_line_ids = record.product_id.bom_ids.bom_line_ids - # BOM_数量 - total_product_qty = sum(line.product_qty for line in bom_line_ids) - bom_product_ids = bom_line_ids.mapped('product_id') - product_purchase_orders = self.env['purchase.order'].sudo().search([ - ('state', 'in', ('purchase', 'done')), - ('order_line.product_id', 'in', bom_product_ids.ids) - ]) - # 购订单_数量 - total_outsourcing_purchase_quantity = sum( - sum( - order.order_line.filtered( - lambda line: line.product_id in bom_product_ids - ).mapped('product_qty') + if bom_line_ids: + # BOM_数量 + total_product_qty = sum(line.product_qty for line in bom_line_ids) + bom_product_ids = bom_line_ids.mapped('product_id') + product_purchase_orders = self.env['purchase.order'].sudo().search([ + ('state', 'in', ('purchase', 'done')), + ('order_line.product_id', 'in', bom_product_ids.ids) + ]) + # 购订单_数量 + total_outsourcing_purchase_quantity = sum( + sum( + order.order_line.filtered( + lambda line: line.product_id in bom_product_ids + ).mapped('product_qty') + ) + for order in product_purchase_orders ) - for order in product_purchase_orders - ) - quantity = total_outsourcing_purchase_quantity / total_product_qty - if float_compare(quantity, record.product_uom_qty, - precision_rounding=record.product_id.uom_id.rounding) == -1: - purchase_request = self.env['purchase.request'].sudo().search( - [('line_ids.product_id', 'in', bom_product_ids.ids), - ('line_ids.purchase_state', 'not in', ('purchase', 'done')), ('state', '!=', 'done')]) - outsourcing_purchase_request.extend(purchase_request.ids) + quantity = total_outsourcing_purchase_quantity / total_product_qty + if float_compare(quantity, record.product_uom_qty, + precision_rounding=record.product_id.uom_id.rounding) == -1: + purchase_request = self.env['purchase.request'].sudo().search( + [('line_ids.product_id', 'in', bom_product_ids.ids), + ('line_ids.purchase_state', 'not in', ('purchase', 'done')), ('state', '!=', 'done')]) + outsourcing_purchase_request.extend(purchase_request.ids) record.outsourcing_purchase_request = json.dumps(outsourcing_purchase_request) if outsourcing_purchase_request: record.hide_action_purchase_orders = True diff --git a/sf_demand_plan/views/demand_plan.xml b/sf_demand_plan/views/demand_plan.xml index 24a2707e..5b9c425a 100644 --- a/sf_demand_plan/views/demand_plan.xml +++ b/sf_demand_plan/views/demand_plan.xml @@ -28,7 +28,7 @@ - + From 7d1f7b11ebdd2156709f8d387cd4dd43f55031d6 Mon Sep 17 00:00:00 2001 From: guanhuan Date: Tue, 24 Jun 2025 11:39:47 +0800 Subject: [PATCH 04/33] =?UTF-8?q?=E5=9D=AF=E6=96=99=E5=B0=BA=E5=AF=B8?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_demand_plan/models/sf_production_demand_plan.py | 7 +++++-- sf_demand_plan/views/demand_plan.xml | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/sf_demand_plan/models/sf_production_demand_plan.py b/sf_demand_plan/models/sf_production_demand_plan.py index d07e4936..49cbe926 100644 --- a/sf_demand_plan/models/sf_production_demand_plan.py +++ b/sf_demand_plan/models/sf_production_demand_plan.py @@ -196,11 +196,14 @@ class SfProductionDemandPlan(models.Model): else: line.model_long = None - @api.depends('product_id.model_long', 'product_id.model_width', 'product_id.model_height') + @api.depends('product_id.model_long', 'product_id.model_width', 'product_id.model_height', 'product_id.blank_type') def _compute_embryo_long(self): for line in self: if line.product_id: - line.embryo_long = f"{round(line.product_id.model_long, 3)}*{round(line.product_id.model_width, 3)}*{round(line.product_id.model_height, 3)}" + if line.product_id.blank_type == '圆料': + line.embryo_long = f"Ø{round(line.product_id.model_width, 3)}*{round(line.product_id.model_long, 3)}" + else: + line.embryo_long = f"{round(line.product_id.model_long, 3)}*{round(line.product_id.model_width, 3)}*{round(line.product_id.model_height, 3)}" else: line.embryo_long = None diff --git a/sf_demand_plan/views/demand_plan.xml b/sf_demand_plan/views/demand_plan.xml index 24a2707e..5b9c425a 100644 --- a/sf_demand_plan/views/demand_plan.xml +++ b/sf_demand_plan/views/demand_plan.xml @@ -28,7 +28,7 @@ - + From 33647fa3e09b255121e8fabb2a849654eee4e70b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?= Date: Tue, 24 Jun 2025 14:18:27 +0800 Subject: [PATCH 05/33] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=98=8E=E7=BB=86?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_machine_connect/controllers/controllers.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py index 565ba613..18b4346c 100644 --- a/sf_machine_connect/controllers/controllers.py +++ b/sf_machine_connect/controllers/controllers.py @@ -870,11 +870,7 @@ class Sf_Dashboard_Connect(http.Controller): begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S') end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S') # print('line_list: %s' % line_list) - not_done_data = [] - done_data = [] final_data = {} - not_done_index = 1 - done_index = 1 # 获取当前时间,并计算24小时前的时间 current_time = datetime.now() @@ -889,6 +885,10 @@ class Sf_Dashboard_Connect(http.Controller): ]) for line in line_list: + not_done_data = [] + done_data = [] + not_done_index = 1 + done_index = 1 if line == '业绩总览': work_order_domain = [('routing_type', 'in', ['人工线下加工', 'CNC加工'])] @@ -922,11 +922,11 @@ class Sf_Dashboard_Connect(http.Controller): # time_24_hours_ago = current_time - timedelta(hours=24) finish_orders = work_order_obj.search(work_order_domain + [ - ('state', 'in', ['finished']), + ('state', 'in', ['done']), ('production_id.state', 'not in', ['cancel']), ('date_finished', '>=', time_48_hours_ago) ], order='id asc') - # print(finish_orders) + # logging.info('完成订单: %s' % finish_orders) # 获取所有未完成订单的ID列表 order_ids = [order.id for order in not_done_orders] @@ -985,8 +985,6 @@ class Sf_Dashboard_Connect(http.Controller): not_done_index += 1 for finish_order in finish_orders: - if not finish_order.actual_end_time: - continue blank_name = '' try: blank_name = finish_order.production_id.move_raw_ids[0].product_id.name @@ -1007,8 +1005,8 @@ class Sf_Dashboard_Connect(http.Controller): 'material': material, 'dimensions': dimensions, 'order_qty': order.qty_produced, - 'finish_time': finish_order.actual_end_time.strftime( - '%Y-%m-%d %H:%M:%S') if finish_order.actual_end_time else ' ' + 'finish_time': finish_order.date_finished.strftime( + '%Y-%m-%d %H:%M:%S') if finish_order.date_finished else ' ' } done_data.append(line_dict) From 912847ae33135bc76e64312f524905e6a26f36b9 Mon Sep 17 00:00:00 2001 From: yuxianghui <3437689193@qq.com> Date: Tue, 24 Jun 2025 14:32:21 +0800 Subject: [PATCH 06/33] =?UTF-8?q?=E9=9C=80=E6=B1=82=20=20=20=E8=B0=83?= =?UTF-8?q?=E6=8B=A8=E5=8D=95=E5=85=B3=E8=81=94=E6=A0=A1=E9=AA=8C=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E5=AE=8C=E6=88=90=E8=B4=A8=E9=87=8F=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E5=8F=AF=E5=85=88=E6=8E=92=E9=99=A4=E6=AC=A0?= =?UTF-8?q?=E5=8D=95=E9=87=8F=E5=90=8E=E5=86=8D=E6=A0=A1=E9=AA=8C=20=20?= =?UTF-8?q?=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_quality/models/stock.py | 181 ++++++++++++++++++++++++++++++------- 1 file changed, 146 insertions(+), 35 deletions(-) diff --git a/sf_quality/models/stock.py b/sf_quality/models/stock.py index add80f8a..3a68ea30 100644 --- a/sf_quality/models/stock.py +++ b/sf_quality/models/stock.py @@ -1,11 +1,41 @@ +import logging + from odoo import api, models +from odoo.exceptions import ValidationError, UserError class StockPicking(models.Model): _inherit = 'stock.picking' + def _compute_check(self): + super()._compute_check() + for picking in self: + picking_to_quality = picking.get_picking_to_quality() + if not picking_to_quality: + picking.quality_check_todo = False + break + else: + need_quality_line = picking.get_need_quality_line(picking_to_quality) + if not need_quality_line or all(not line.get('need_done_check_ids') for line in need_quality_line): + picking.quality_check_todo = False + + def check_quality(self): + self.ensure_one() + # checkable_products = self.mapped('move_line_ids').mapped('product_id') + # checks = self.check_ids.filtered(lambda check: check.quality_state == 'none' and ( + # check.product_id in checkable_products or check.measure_on == 'operation')) + checks = self.env['quality.check'] + picking_to_quality = self._get_picking_to_quality() + need_quality_line = self.get_need_quality_line(picking_to_quality) + if need_quality_line and any(line.get('need_done_check_ids') for line in need_quality_line): + for item in need_quality_line: + checks += item.get('need_done_check_ids') + if checks: + return checks.action_open_quality_check_wizard() + return False + def button_validate(self): - """ + """= 出厂检验报告上传 """ @@ -17,43 +47,124 @@ class StockPicking(models.Model): if not out_quality_check.is_factory_report_uploaded: if out_quality_check and self.state == 'assigned': out_quality_check.upload_factory_report() + quality_action = self.pinking_checkout_quality() + if quality_action: + return quality_action + res = super(StockPicking, self).button_validate() + return res + def pinking_checkout_quality(self): """ 调拨单若关联了质量检查单,验证调拨单时,应校验是否有不合格品,若存在,应弹窗提示: “警告:存在不合格产品XXXX n 件、YYYYY m件,继续调拨请点“确认”,否则请取消?” """ - context = self.env.context - if not context.get('again_validate') and self.quality_check_ids.filtered(lambda qc: qc.quality_state == 'fail'): - # 回滚事务,为二次确认/取消做准备 - self.env.cr.rollback() - quality_check_ids = self.quality_check_ids.filtered(lambda qc: qc.quality_state == 'fail') - product_list = list(set([quality_check_id.product_id for quality_check_id in quality_check_ids])) - fail_check_text = '' - for product_id in product_list: - check_ids = quality_check_ids.filtered(lambda qc: qc.product_id == product_id) - if all(check_id.measure_on == 'move_line' for check_id in check_ids): - number = sum(check_ids.mapped('qty_line')) - else: - number = sum(self.move_ids_without_package.filtered( - lambda ml: ml.product_id == product_id).mapped('quantity_done')) - if number == 0: - number = sum(self.move_ids_without_package.filtered( - lambda ml: ml.product_id == product_id).mapped('reserved_availability')) - if number == 0: - number = sum(self.move_ids_without_package.filtered( - lambda ml: ml.product_id == product_id).mapped('product_uom_qty')) - fail_check_text = (f'{fail_check_text}、{product_id.display_name} {number}件' - if fail_check_text != '' else f'{product_id.display_name} {number}件') - return { - 'type': 'ir.actions.act_window', - 'res_model': 'picking.validate.check.wizard', - 'name': '质检不合格提示', - 'view_mode': 'form', - 'target': 'new', - 'context': { - 'default_picking_id': self.id, - 'default_fail_check_text': f'警告:存在不合格产品{fail_check_text},继续调拨请点“确认”,否则请取消?', - 'again_validate': True} - } - res = super(StockPicking, self).button_validate() + try: + context = self.env.context + picking_to_quality = self._get_picking_to_quality() + if not picking_to_quality: return False + need_quality_val = self.get_need_quality_line(picking_to_quality) + if (not context.get('pinking_checkout_quality') and + any(line.get('fail_check_ids') for line in need_quality_val)): + # 回滚事务,为二次确认/取消做准备 + self.env.cr.rollback() + # 获取存在失败的 质检单 调拨单明细行 + check_list = [item for item in need_quality_val if item.get('fail_check_ids')] + fail_check_text = '' + for item in check_list: + move_id, pre_done_qty = item.get('move_id'), item.get('pre_done_qty') + fail_check_text = (f'{fail_check_text}、{move_id.product_id.display_name} {pre_done_qty}件' + if fail_check_text != '' else f'{move_id.product_id.display_name} {pre_done_qty}件') + return { + 'type': 'ir.actions.act_window', + 'res_model': 'picking.validate.check.wizard', + 'name': '质检不合格提示', + 'view_mode': 'form', + 'target': 'new', + 'context': { + 'default_picking_id': self.id, + 'default_fail_check_text': f'警告:存在不合格产品{fail_check_text},继续调拨请点“确认”,否则请取消?', + 'pinking_checkout_quality': True} + } + else: + return False + except Exception as e: + logging.info('pinking_checkout_quality()方法报错:%s' % e) + raise ValidationError('调拨单验证质检单是否合格时报错,请联系管理员处理!!') + + def get_need_quality_line(self, picking_to_quality): + """ + # 对需要进行质检,还没有质检完成的明细行进行统计 + # 1、当【质量标准_控制方式】=“产品、作业”,仅校验“预完成数量”>0的产品行对应的质检单必须处理。 + 2、当【质量标准_控制方式】=“数量”时,仅校验“预完成数量”>0的产品行对应的质检单必须处理: + 1)每一类的“总单数”=【调拨单_需求】时,则已处理的质检单“单数”≥“预完成数量”时,可执行调拨单验证; + 2)每一类的“总单数”<【调拨单_需求】时,则已处理的质检单“单数”≥0时,可执行调拨单验证; + """ + res = [] + for item in picking_to_quality: + need_done_check_ids = self.env['quality.check'] + fail_check_ids = self.env['quality.check'] + move_id, pre_done_qty, check_ids = item.values() + check_ids_1 = check_ids.filtered(lambda qc: qc.measure_on in ('operation', 'product')) + if check_ids_1: + check_ids_1_done = check_ids_1.filtered(lambda qc: qc.quality_state in ('pass', 'fail')) + check_ids_1_fail = check_ids_1.filtered(lambda qc: qc.quality_state == 'fail') + check_ids_1_none = check_ids_1.filtered(lambda qc: qc.quality_state == 'none') + if check_ids_1 and not check_ids_1_done: + need_done_check_ids += check_ids_1_none + if check_ids_1_fail: + fail_check_ids += check_ids_1_fail + + check_ids_2 = check_ids.filtered(lambda qc: qc.measure_on == 'move_line') + if check_ids_2: + check_ids_2_done = check_ids_2.filtered(lambda qc: qc.quality_state in ('pass', 'fail')) + check_ids_2_fail = check_ids_2.filtered(lambda qc: qc.quality_state == 'fail') + check_ids_2_none = check_ids_2.filtered(lambda qc: qc.quality_state == 'none') + if len(check_ids_2) >= pre_done_qty > len(check_ids_2_done): + need_done_check_ids += check_ids_2_none + elif len(check_ids_2) < pre_done_qty and len(check_ids_2_done) == 0: + need_done_check_ids += check_ids_2_none + if check_ids_2_fail: + fail_check_ids += check_ids_2_fail + + if need_done_check_ids or fail_check_ids: + res.append({'move_id': move_id, + 'pre_done_qty': pre_done_qty, + 'check_ids': check_ids, + 'fail_check_ids': fail_check_ids, + 'need_done_check_ids': need_done_check_ids}) return res + + def get_picking_to_quality(self): + self.ensure_one() + return self._get_picking_to_quality() + + def _get_picking_to_quality(self): + """ + 对需要质检的明细行进行统计(针对“预完成数量”>0的行) + """ + quality_piking_line_list = [] + pre_done_qty_lines = self._get_pinking_pre_done_qty() + for line in pre_done_qty_lines: + move_id, pre_done_qty = line.values() + if pre_done_qty == 0: + continue + product_id = move_id.product_id + check_ids = self.check_ids.filtered(lambda c: c.product_id == product_id) + quality_piking_line_list.append({'move_id': move_id, 'pre_done_qty': pre_done_qty, 'check_ids': check_ids}) + return quality_piking_line_list + + def _get_pinking_pre_done_qty(self): + """ + return: 明细行 及 预完成数量 + 1、若调拨单所有明细行的【完成】=0,且任意行的【预留】<【需求】,则验证时将会话是否需创建欠单。 + ---->此时“预完成数量”=【预留】 + 2、若调拨单任意行的0<【完成】<【需求】,且则验证时将会话是否需创建欠单 + ---->此时“预完成数量”=【完成】 + """ + if all(move_id.quantity_done == 0 for move_id in self.move_ids_without_package): + pre_done_qty = [{'move_id': move_id, 'pre_done_qty': move_id.reserved_availability} for move_id in + self.move_ids_without_package] + else: + pre_done_qty = [{'move_id': move_id, 'pre_done_qty': move_id.quantity_done} for move_id in + self.move_ids_without_package] + return pre_done_qty From 98f8430c45f3b464c8398f76beb07970d8c38924 Mon Sep 17 00:00:00 2001 From: yuxianghui <3437689193@qq.com> Date: Tue, 24 Jun 2025 14:32:40 +0800 Subject: [PATCH 07/33] =?UTF-8?q?Revert=20"=E9=9C=80=E6=B1=82=20=20=20?= =?UTF-8?q?=E8=B0=83=E6=8B=A8=E5=8D=95=E5=85=B3=E8=81=94=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E5=AE=8C=E6=88=90=E8=B4=A8=E9=87=8F=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=E6=97=B6=EF=BC=8C=E5=8F=AF=E5=85=88=E6=8E=92=E9=99=A4?= =?UTF-8?q?=E6=AC=A0=E5=8D=95=E9=87=8F=E5=90=8E=E5=86=8D=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=20=20=E5=BC=80=E5=8F=91"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 912847ae33135bc76e64312f524905e6a26f36b9. --- sf_quality/models/stock.py | 181 +++++++------------------------------ 1 file changed, 35 insertions(+), 146 deletions(-) diff --git a/sf_quality/models/stock.py b/sf_quality/models/stock.py index 3a68ea30..add80f8a 100644 --- a/sf_quality/models/stock.py +++ b/sf_quality/models/stock.py @@ -1,41 +1,11 @@ -import logging - from odoo import api, models -from odoo.exceptions import ValidationError, UserError class StockPicking(models.Model): _inherit = 'stock.picking' - def _compute_check(self): - super()._compute_check() - for picking in self: - picking_to_quality = picking.get_picking_to_quality() - if not picking_to_quality: - picking.quality_check_todo = False - break - else: - need_quality_line = picking.get_need_quality_line(picking_to_quality) - if not need_quality_line or all(not line.get('need_done_check_ids') for line in need_quality_line): - picking.quality_check_todo = False - - def check_quality(self): - self.ensure_one() - # checkable_products = self.mapped('move_line_ids').mapped('product_id') - # checks = self.check_ids.filtered(lambda check: check.quality_state == 'none' and ( - # check.product_id in checkable_products or check.measure_on == 'operation')) - checks = self.env['quality.check'] - picking_to_quality = self._get_picking_to_quality() - need_quality_line = self.get_need_quality_line(picking_to_quality) - if need_quality_line and any(line.get('need_done_check_ids') for line in need_quality_line): - for item in need_quality_line: - checks += item.get('need_done_check_ids') - if checks: - return checks.action_open_quality_check_wizard() - return False - def button_validate(self): - """= + """ 出厂检验报告上传 """ @@ -47,124 +17,43 @@ class StockPicking(models.Model): if not out_quality_check.is_factory_report_uploaded: if out_quality_check and self.state == 'assigned': out_quality_check.upload_factory_report() - quality_action = self.pinking_checkout_quality() - if quality_action: - return quality_action - res = super(StockPicking, self).button_validate() - return res - def pinking_checkout_quality(self): """ 调拨单若关联了质量检查单,验证调拨单时,应校验是否有不合格品,若存在,应弹窗提示: “警告:存在不合格产品XXXX n 件、YYYYY m件,继续调拨请点“确认”,否则请取消?” """ - try: - context = self.env.context - picking_to_quality = self._get_picking_to_quality() - if not picking_to_quality: return False - need_quality_val = self.get_need_quality_line(picking_to_quality) - if (not context.get('pinking_checkout_quality') and - any(line.get('fail_check_ids') for line in need_quality_val)): - # 回滚事务,为二次确认/取消做准备 - self.env.cr.rollback() - # 获取存在失败的 质检单 调拨单明细行 - check_list = [item for item in need_quality_val if item.get('fail_check_ids')] - fail_check_text = '' - for item in check_list: - move_id, pre_done_qty = item.get('move_id'), item.get('pre_done_qty') - fail_check_text = (f'{fail_check_text}、{move_id.product_id.display_name} {pre_done_qty}件' - if fail_check_text != '' else f'{move_id.product_id.display_name} {pre_done_qty}件') - return { - 'type': 'ir.actions.act_window', - 'res_model': 'picking.validate.check.wizard', - 'name': '质检不合格提示', - 'view_mode': 'form', - 'target': 'new', - 'context': { - 'default_picking_id': self.id, - 'default_fail_check_text': f'警告:存在不合格产品{fail_check_text},继续调拨请点“确认”,否则请取消?', - 'pinking_checkout_quality': True} - } - else: - return False - except Exception as e: - logging.info('pinking_checkout_quality()方法报错:%s' % e) - raise ValidationError('调拨单验证质检单是否合格时报错,请联系管理员处理!!') - - def get_need_quality_line(self, picking_to_quality): - """ - # 对需要进行质检,还没有质检完成的明细行进行统计 - # 1、当【质量标准_控制方式】=“产品、作业”,仅校验“预完成数量”>0的产品行对应的质检单必须处理。 - 2、当【质量标准_控制方式】=“数量”时,仅校验“预完成数量”>0的产品行对应的质检单必须处理: - 1)每一类的“总单数”=【调拨单_需求】时,则已处理的质检单“单数”≥“预完成数量”时,可执行调拨单验证; - 2)每一类的“总单数”<【调拨单_需求】时,则已处理的质检单“单数”≥0时,可执行调拨单验证; - """ - res = [] - for item in picking_to_quality: - need_done_check_ids = self.env['quality.check'] - fail_check_ids = self.env['quality.check'] - move_id, pre_done_qty, check_ids = item.values() - check_ids_1 = check_ids.filtered(lambda qc: qc.measure_on in ('operation', 'product')) - if check_ids_1: - check_ids_1_done = check_ids_1.filtered(lambda qc: qc.quality_state in ('pass', 'fail')) - check_ids_1_fail = check_ids_1.filtered(lambda qc: qc.quality_state == 'fail') - check_ids_1_none = check_ids_1.filtered(lambda qc: qc.quality_state == 'none') - if check_ids_1 and not check_ids_1_done: - need_done_check_ids += check_ids_1_none - if check_ids_1_fail: - fail_check_ids += check_ids_1_fail - - check_ids_2 = check_ids.filtered(lambda qc: qc.measure_on == 'move_line') - if check_ids_2: - check_ids_2_done = check_ids_2.filtered(lambda qc: qc.quality_state in ('pass', 'fail')) - check_ids_2_fail = check_ids_2.filtered(lambda qc: qc.quality_state == 'fail') - check_ids_2_none = check_ids_2.filtered(lambda qc: qc.quality_state == 'none') - if len(check_ids_2) >= pre_done_qty > len(check_ids_2_done): - need_done_check_ids += check_ids_2_none - elif len(check_ids_2) < pre_done_qty and len(check_ids_2_done) == 0: - need_done_check_ids += check_ids_2_none - if check_ids_2_fail: - fail_check_ids += check_ids_2_fail - - if need_done_check_ids or fail_check_ids: - res.append({'move_id': move_id, - 'pre_done_qty': pre_done_qty, - 'check_ids': check_ids, - 'fail_check_ids': fail_check_ids, - 'need_done_check_ids': need_done_check_ids}) + context = self.env.context + if not context.get('again_validate') and self.quality_check_ids.filtered(lambda qc: qc.quality_state == 'fail'): + # 回滚事务,为二次确认/取消做准备 + self.env.cr.rollback() + quality_check_ids = self.quality_check_ids.filtered(lambda qc: qc.quality_state == 'fail') + product_list = list(set([quality_check_id.product_id for quality_check_id in quality_check_ids])) + fail_check_text = '' + for product_id in product_list: + check_ids = quality_check_ids.filtered(lambda qc: qc.product_id == product_id) + if all(check_id.measure_on == 'move_line' for check_id in check_ids): + number = sum(check_ids.mapped('qty_line')) + else: + number = sum(self.move_ids_without_package.filtered( + lambda ml: ml.product_id == product_id).mapped('quantity_done')) + if number == 0: + number = sum(self.move_ids_without_package.filtered( + lambda ml: ml.product_id == product_id).mapped('reserved_availability')) + if number == 0: + number = sum(self.move_ids_without_package.filtered( + lambda ml: ml.product_id == product_id).mapped('product_uom_qty')) + fail_check_text = (f'{fail_check_text}、{product_id.display_name} {number}件' + if fail_check_text != '' else f'{product_id.display_name} {number}件') + return { + 'type': 'ir.actions.act_window', + 'res_model': 'picking.validate.check.wizard', + 'name': '质检不合格提示', + 'view_mode': 'form', + 'target': 'new', + 'context': { + 'default_picking_id': self.id, + 'default_fail_check_text': f'警告:存在不合格产品{fail_check_text},继续调拨请点“确认”,否则请取消?', + 'again_validate': True} + } + res = super(StockPicking, self).button_validate() return res - - def get_picking_to_quality(self): - self.ensure_one() - return self._get_picking_to_quality() - - def _get_picking_to_quality(self): - """ - 对需要质检的明细行进行统计(针对“预完成数量”>0的行) - """ - quality_piking_line_list = [] - pre_done_qty_lines = self._get_pinking_pre_done_qty() - for line in pre_done_qty_lines: - move_id, pre_done_qty = line.values() - if pre_done_qty == 0: - continue - product_id = move_id.product_id - check_ids = self.check_ids.filtered(lambda c: c.product_id == product_id) - quality_piking_line_list.append({'move_id': move_id, 'pre_done_qty': pre_done_qty, 'check_ids': check_ids}) - return quality_piking_line_list - - def _get_pinking_pre_done_qty(self): - """ - return: 明细行 及 预完成数量 - 1、若调拨单所有明细行的【完成】=0,且任意行的【预留】<【需求】,则验证时将会话是否需创建欠单。 - ---->此时“预完成数量”=【预留】 - 2、若调拨单任意行的0<【完成】<【需求】,且则验证时将会话是否需创建欠单 - ---->此时“预完成数量”=【完成】 - """ - if all(move_id.quantity_done == 0 for move_id in self.move_ids_without_package): - pre_done_qty = [{'move_id': move_id, 'pre_done_qty': move_id.reserved_availability} for move_id in - self.move_ids_without_package] - else: - pre_done_qty = [{'move_id': move_id, 'pre_done_qty': move_id.quantity_done} for move_id in - self.move_ids_without_package] - return pre_done_qty From 579b7138e763512055eff01b897dea5ee761804f Mon Sep 17 00:00:00 2001 From: yuxianghui <3437689193@qq.com> Date: Tue, 24 Jun 2025 14:41:49 +0800 Subject: [PATCH 08/33] =?UTF-8?q?=E9=9C=80=E6=B1=82=20=20=E8=B0=83?= =?UTF-8?q?=E6=8B=A8=E5=8D=95=E5=85=B3=E8=81=94=E6=A0=A1=E9=AA=8C=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E5=AE=8C=E6=88=90=E8=B4=A8=E9=87=8F=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E5=8F=AF=E5=85=88=E6=8E=92=E9=99=A4=E6=AC=A0?= =?UTF-8?q?=E5=8D=95=E9=87=8F=E5=90=8E=E5=86=8D=E6=A0=A1=E9=AA=8C=20?= =?UTF-8?q?=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_quality/models/stock.py | 181 ++++++++++++++++++++++++++++++------- 1 file changed, 146 insertions(+), 35 deletions(-) diff --git a/sf_quality/models/stock.py b/sf_quality/models/stock.py index add80f8a..3a68ea30 100644 --- a/sf_quality/models/stock.py +++ b/sf_quality/models/stock.py @@ -1,11 +1,41 @@ +import logging + from odoo import api, models +from odoo.exceptions import ValidationError, UserError class StockPicking(models.Model): _inherit = 'stock.picking' + def _compute_check(self): + super()._compute_check() + for picking in self: + picking_to_quality = picking.get_picking_to_quality() + if not picking_to_quality: + picking.quality_check_todo = False + break + else: + need_quality_line = picking.get_need_quality_line(picking_to_quality) + if not need_quality_line or all(not line.get('need_done_check_ids') for line in need_quality_line): + picking.quality_check_todo = False + + def check_quality(self): + self.ensure_one() + # checkable_products = self.mapped('move_line_ids').mapped('product_id') + # checks = self.check_ids.filtered(lambda check: check.quality_state == 'none' and ( + # check.product_id in checkable_products or check.measure_on == 'operation')) + checks = self.env['quality.check'] + picking_to_quality = self._get_picking_to_quality() + need_quality_line = self.get_need_quality_line(picking_to_quality) + if need_quality_line and any(line.get('need_done_check_ids') for line in need_quality_line): + for item in need_quality_line: + checks += item.get('need_done_check_ids') + if checks: + return checks.action_open_quality_check_wizard() + return False + def button_validate(self): - """ + """= 出厂检验报告上传 """ @@ -17,43 +47,124 @@ class StockPicking(models.Model): if not out_quality_check.is_factory_report_uploaded: if out_quality_check and self.state == 'assigned': out_quality_check.upload_factory_report() + quality_action = self.pinking_checkout_quality() + if quality_action: + return quality_action + res = super(StockPicking, self).button_validate() + return res + def pinking_checkout_quality(self): """ 调拨单若关联了质量检查单,验证调拨单时,应校验是否有不合格品,若存在,应弹窗提示: “警告:存在不合格产品XXXX n 件、YYYYY m件,继续调拨请点“确认”,否则请取消?” """ - context = self.env.context - if not context.get('again_validate') and self.quality_check_ids.filtered(lambda qc: qc.quality_state == 'fail'): - # 回滚事务,为二次确认/取消做准备 - self.env.cr.rollback() - quality_check_ids = self.quality_check_ids.filtered(lambda qc: qc.quality_state == 'fail') - product_list = list(set([quality_check_id.product_id for quality_check_id in quality_check_ids])) - fail_check_text = '' - for product_id in product_list: - check_ids = quality_check_ids.filtered(lambda qc: qc.product_id == product_id) - if all(check_id.measure_on == 'move_line' for check_id in check_ids): - number = sum(check_ids.mapped('qty_line')) - else: - number = sum(self.move_ids_without_package.filtered( - lambda ml: ml.product_id == product_id).mapped('quantity_done')) - if number == 0: - number = sum(self.move_ids_without_package.filtered( - lambda ml: ml.product_id == product_id).mapped('reserved_availability')) - if number == 0: - number = sum(self.move_ids_without_package.filtered( - lambda ml: ml.product_id == product_id).mapped('product_uom_qty')) - fail_check_text = (f'{fail_check_text}、{product_id.display_name} {number}件' - if fail_check_text != '' else f'{product_id.display_name} {number}件') - return { - 'type': 'ir.actions.act_window', - 'res_model': 'picking.validate.check.wizard', - 'name': '质检不合格提示', - 'view_mode': 'form', - 'target': 'new', - 'context': { - 'default_picking_id': self.id, - 'default_fail_check_text': f'警告:存在不合格产品{fail_check_text},继续调拨请点“确认”,否则请取消?', - 'again_validate': True} - } - res = super(StockPicking, self).button_validate() + try: + context = self.env.context + picking_to_quality = self._get_picking_to_quality() + if not picking_to_quality: return False + need_quality_val = self.get_need_quality_line(picking_to_quality) + if (not context.get('pinking_checkout_quality') and + any(line.get('fail_check_ids') for line in need_quality_val)): + # 回滚事务,为二次确认/取消做准备 + self.env.cr.rollback() + # 获取存在失败的 质检单 调拨单明细行 + check_list = [item for item in need_quality_val if item.get('fail_check_ids')] + fail_check_text = '' + for item in check_list: + move_id, pre_done_qty = item.get('move_id'), item.get('pre_done_qty') + fail_check_text = (f'{fail_check_text}、{move_id.product_id.display_name} {pre_done_qty}件' + if fail_check_text != '' else f'{move_id.product_id.display_name} {pre_done_qty}件') + return { + 'type': 'ir.actions.act_window', + 'res_model': 'picking.validate.check.wizard', + 'name': '质检不合格提示', + 'view_mode': 'form', + 'target': 'new', + 'context': { + 'default_picking_id': self.id, + 'default_fail_check_text': f'警告:存在不合格产品{fail_check_text},继续调拨请点“确认”,否则请取消?', + 'pinking_checkout_quality': True} + } + else: + return False + except Exception as e: + logging.info('pinking_checkout_quality()方法报错:%s' % e) + raise ValidationError('调拨单验证质检单是否合格时报错,请联系管理员处理!!') + + def get_need_quality_line(self, picking_to_quality): + """ + # 对需要进行质检,还没有质检完成的明细行进行统计 + # 1、当【质量标准_控制方式】=“产品、作业”,仅校验“预完成数量”>0的产品行对应的质检单必须处理。 + 2、当【质量标准_控制方式】=“数量”时,仅校验“预完成数量”>0的产品行对应的质检单必须处理: + 1)每一类的“总单数”=【调拨单_需求】时,则已处理的质检单“单数”≥“预完成数量”时,可执行调拨单验证; + 2)每一类的“总单数”<【调拨单_需求】时,则已处理的质检单“单数”≥0时,可执行调拨单验证; + """ + res = [] + for item in picking_to_quality: + need_done_check_ids = self.env['quality.check'] + fail_check_ids = self.env['quality.check'] + move_id, pre_done_qty, check_ids = item.values() + check_ids_1 = check_ids.filtered(lambda qc: qc.measure_on in ('operation', 'product')) + if check_ids_1: + check_ids_1_done = check_ids_1.filtered(lambda qc: qc.quality_state in ('pass', 'fail')) + check_ids_1_fail = check_ids_1.filtered(lambda qc: qc.quality_state == 'fail') + check_ids_1_none = check_ids_1.filtered(lambda qc: qc.quality_state == 'none') + if check_ids_1 and not check_ids_1_done: + need_done_check_ids += check_ids_1_none + if check_ids_1_fail: + fail_check_ids += check_ids_1_fail + + check_ids_2 = check_ids.filtered(lambda qc: qc.measure_on == 'move_line') + if check_ids_2: + check_ids_2_done = check_ids_2.filtered(lambda qc: qc.quality_state in ('pass', 'fail')) + check_ids_2_fail = check_ids_2.filtered(lambda qc: qc.quality_state == 'fail') + check_ids_2_none = check_ids_2.filtered(lambda qc: qc.quality_state == 'none') + if len(check_ids_2) >= pre_done_qty > len(check_ids_2_done): + need_done_check_ids += check_ids_2_none + elif len(check_ids_2) < pre_done_qty and len(check_ids_2_done) == 0: + need_done_check_ids += check_ids_2_none + if check_ids_2_fail: + fail_check_ids += check_ids_2_fail + + if need_done_check_ids or fail_check_ids: + res.append({'move_id': move_id, + 'pre_done_qty': pre_done_qty, + 'check_ids': check_ids, + 'fail_check_ids': fail_check_ids, + 'need_done_check_ids': need_done_check_ids}) return res + + def get_picking_to_quality(self): + self.ensure_one() + return self._get_picking_to_quality() + + def _get_picking_to_quality(self): + """ + 对需要质检的明细行进行统计(针对“预完成数量”>0的行) + """ + quality_piking_line_list = [] + pre_done_qty_lines = self._get_pinking_pre_done_qty() + for line in pre_done_qty_lines: + move_id, pre_done_qty = line.values() + if pre_done_qty == 0: + continue + product_id = move_id.product_id + check_ids = self.check_ids.filtered(lambda c: c.product_id == product_id) + quality_piking_line_list.append({'move_id': move_id, 'pre_done_qty': pre_done_qty, 'check_ids': check_ids}) + return quality_piking_line_list + + def _get_pinking_pre_done_qty(self): + """ + return: 明细行 及 预完成数量 + 1、若调拨单所有明细行的【完成】=0,且任意行的【预留】<【需求】,则验证时将会话是否需创建欠单。 + ---->此时“预完成数量”=【预留】 + 2、若调拨单任意行的0<【完成】<【需求】,且则验证时将会话是否需创建欠单 + ---->此时“预完成数量”=【完成】 + """ + if all(move_id.quantity_done == 0 for move_id in self.move_ids_without_package): + pre_done_qty = [{'move_id': move_id, 'pre_done_qty': move_id.reserved_availability} for move_id in + self.move_ids_without_package] + else: + pre_done_qty = [{'move_id': move_id, 'pre_done_qty': move_id.quantity_done} for move_id in + self.move_ids_without_package] + return pre_done_qty From 2a330a4bd862da1bec3ef87b13ae88d8aa7f7ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?= Date: Tue, 24 Jun 2025 14:52:03 +0800 Subject: [PATCH 09/33] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=A4=A7=E5=B1=8F?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E8=AE=A2=E5=8D=95=E8=AF=A6=E6=83=85=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_machine_connect/controllers/controllers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py index 18b4346c..de4a8a33 100644 --- a/sf_machine_connect/controllers/controllers.py +++ b/sf_machine_connect/controllers/controllers.py @@ -862,6 +862,8 @@ class Sf_Dashboard_Connect(http.Controller): """ # res = {'status': 1, 'message': '成功', 'not_done_data': [], 'done_data': []} res = {'status': 1, 'message': '成功', 'data': {}} + # 解决产品名称取到英文的问题 + request.update_context(lang='zh_CN') plan_obj = request.env['sf.production.plan'].sudo() work_order_obj = request.env['mrp.workorder'].sudo() line_list = ast.literal_eval(kw['line_list']) From 00922e3674466ba76f1e6e4ecb2df7730a0d9ed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?= Date: Tue, 24 Jun 2025 15:35:19 +0800 Subject: [PATCH 10/33] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=B7=A5=E5=8D=95?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E5=AF=B9=E5=BA=94=E5=90=8D=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_machine_connect/controllers/controllers.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py index de4a8a33..8316411e 100644 --- a/sf_machine_connect/controllers/controllers.py +++ b/sf_machine_connect/controllers/controllers.py @@ -866,6 +866,8 @@ class Sf_Dashboard_Connect(http.Controller): request.update_context(lang='zh_CN') plan_obj = request.env['sf.production.plan'].sudo() work_order_obj = request.env['mrp.workorder'].sudo() + # 获取mrp.workorder的state字段的selection内容 + state_dict = dict(request.env['mrp.workorder'].sudo()._fields['state'].selection) line_list = ast.literal_eval(kw['line_list']) begin_time_str = kw['begin_time'].strip('"') end_time_str = kw['end_time'].strip('"') @@ -964,14 +966,6 @@ class Sf_Dashboard_Connect(http.Controller): material_match = re.search(material_pattern, blank_name) material = material_match.group(1) if material_match else 'No match found' - state_dict = { - 'draft': '待排程', - 'done': '已排程', - 'processing': '生产中', - 'finished': '已完成', - 'ready': '待加工', - 'progress': '生产中', - } line_dict = { 'sequence': not_done_index, From 35b1d648c326977ddb0d54d9ba27c7c041208899 Mon Sep 17 00:00:00 2001 From: yuxianghui <3437689193@qq.com> Date: Tue, 24 Jun 2025 15:36:23 +0800 Subject: [PATCH 11/33] =?UTF-8?q?=E4=BA=A7=E5=93=81=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=8D=95=E4=BB=B6=E7=94=A8=E9=87=8F=20unit=5Fnumber=20?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=EF=BC=8C=E5=80=BC=E7=94=B1bfm=E4=B8=8B?= =?UTF-8?q?=E5=8D=95=E5=90=8C=E6=AD=A5=E8=8E=B7=E5=BE=97=EF=BC=9B=E4=BA=A7?= =?UTF-8?q?=E5=93=81=E7=94=9F=E6=88=90=E7=9A=84BOM=E4=B8=AD=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E6=95=B0=E9=87=8F=E7=9A=84=E5=80=BC=E7=94=B1unit=5Fnu?= =?UTF-8?q?mber=20=E5=AD=97=E6=AE=B5=E6=8F=90=E4=BE=9B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_dlm/models/product_supplierinfo.py | 7 +++++-- .../views/product_template_management_view.xml | 3 ++- sf_manufacturing/models/product_template.py | 4 +++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/sf_dlm/models/product_supplierinfo.py b/sf_dlm/models/product_supplierinfo.py index f4278e03..313b51ed 100644 --- a/sf_dlm/models/product_supplierinfo.py +++ b/sf_dlm/models/product_supplierinfo.py @@ -47,11 +47,14 @@ class ResMrpBomMo(models.Model): item.subcontractor_name = '' def bom_create_line_has(self, embryo): + product = self.product_tmpl_id + if product.unit_number in (0, None, False): + raise ValueError(f'单件用量的值不能为{product.unit_number}') vals = { 'bom_id': self.id, 'product_id': embryo.id, 'product_tmpl_id': embryo.product_tmpl_id.id, - 'product_qty': 1, + 'product_qty': product.unit_number, 'product_uom_id': 1 } return self.env['mrp.bom.line'].sudo().create(vals) @@ -122,7 +125,7 @@ class ResMrpBomMo(models.Model): # 查bom的原材料 def get_raw_bom(self, product): raw_bom = self.env['product.product'].search( - [('categ_id.type', '=', '原材料'), ('materials_type_id', '=', product.materials_type_id.id)],limit=1) + [('categ_id.type', '=', '原材料'), ('materials_type_id', '=', product.materials_type_id.id)], limit=1) return raw_bom diff --git a/sf_dlm_management/views/product_template_management_view.xml b/sf_dlm_management/views/product_template_management_view.xml index bb9904a7..b0a7b87e 100644 --- a/sf_dlm_management/views/product_template_management_view.xml +++ b/sf_dlm_management/views/product_template_management_view.xml @@ -95,6 +95,7 @@ +