diff --git a/jikimo_purchase_request/__manifest__.py b/jikimo_purchase_request/__manifest__.py index 3221a54e..2c8bb641 100644 --- a/jikimo_purchase_request/__manifest__.py +++ b/jikimo_purchase_request/__manifest__.py @@ -13,6 +13,7 @@ 'views/purchase_request_view.xml', 'wizard/purchase_request_line_make_purchase_order_view.xml', 'views/purchase_request_line_view.xml', + 'views/stock_picking_views.xml', ], 'assets': { 'web.assets_backend': [ diff --git a/jikimo_purchase_request/models/__init__.py b/jikimo_purchase_request/models/__init__.py index 433b063d..4d5c92da 100644 --- a/jikimo_purchase_request/models/__init__.py +++ b/jikimo_purchase_request/models/__init__.py @@ -5,3 +5,4 @@ from . import sale_order from . import mrp_production from . import purchase_order from . import stock_rule +from . import stock_picking diff --git a/jikimo_purchase_request/models/mrp_production.py b/jikimo_purchase_request/models/mrp_production.py index dd9d4aa9..58a94eba 100644 --- a/jikimo_purchase_request/models/mrp_production.py +++ b/jikimo_purchase_request/models/mrp_production.py @@ -9,12 +9,20 @@ class MrpProduction(models.Model): @api.depends('state') def _compute_pr_mp_count(self): for item in self: - pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name)]) + # if item.product_id.product_tmpl_id.single_manufacturing == True and not item.is_remanufacture: + # first_order = self.env['mrp.production'].search( + # [('origin', '=', item.origin), ('product_id', '=', item.product_id.id)], limit=1, order='id asc') + # pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', first_order.name)]) + # item.pr_mp_count = len(pr_ids) + # else: + # pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name)]) + # item.pr_mp_count = len(pr_ids) + # 由于采购申请合并了所有销售订单行的采购,所以不区分产品 + first_mp = self.env['mrp.production'].search( + [('origin', '=', item.origin)], limit=1, order='id asc') + pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', first_mp.name)]) + item.pr_mp_count = len(pr_ids) # pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name), ('is_subcontract', '!=', 'True')]) - if pr_ids: - item.pr_mp_count = len(pr_ids) - else: - item.pr_mp_count = 0 def action_view_pr_mp(self): """ @@ -22,7 +30,18 @@ class MrpProduction(models.Model): """ self.ensure_one() # pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name),('is_subcontract', '!=', True)]) - pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name)]) + # if self.product_id.product_tmpl_id.single_manufacturing == True and not self.is_remanufacture: + # first_order = self.env['mrp.production'].search( + # [('origin', '=', self.origin), ('product_id', '=', self.product_id.id)], limit=1, order='id asc') + # pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', first_order.name)]) + # else: + # pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name)]) + # 由于采购申请合并了所有销售订单行的采购,所以不区分产品 + first_mp = self.env['mrp.production'].search( + [('origin', '=', self.origin)], limit=1, order='id asc') + pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', first_mp.name)]) + + action = { 'res_model': 'purchase.request', 'type': 'ir.actions.act_window', diff --git a/jikimo_purchase_request/models/stock_picking.py b/jikimo_purchase_request/models/stock_picking.py new file mode 100644 index 00000000..d8f15f6b --- /dev/null +++ b/jikimo_purchase_request/models/stock_picking.py @@ -0,0 +1,35 @@ +from odoo import fields, api, models, _ + + +class StockPicking(models.Model): + _inherit = "stock.picking" + + purchase_request_count = fields.Integer('采购订单数量', compute='_compute_purchase_request') + + @api.depends('name') + def _compute_purchase_request(self): + for record in self: + purchase_request_ids = self.env['purchase.request'].search([('origin', '=', record.name)]) + record.purchase_request_count = len(purchase_request_ids) + + def action_view_purchase_request(self): + self.ensure_one() + + purchase_request_ids = self.env['purchase.request'].search([('origin', '=', self.name)]) + + action = { + 'res_model': 'purchase.request', + 'type': 'ir.actions.act_window', + } + if len(purchase_request_ids) == 1: + action.update({ + 'view_mode': 'form', + 'res_id': purchase_request_ids[0].id, + }) + else: + action.update({ + 'name': _("从 %s生成采购请求单", self.name), + 'domain': [('id', 'in', purchase_request_ids.ids)], + 'view_mode': 'tree,form', + }) + return action diff --git a/jikimo_purchase_request/models/stock_rule.py b/jikimo_purchase_request/models/stock_rule.py index 08f32b2d..02dd3626 100644 --- a/jikimo_purchase_request/models/stock_rule.py +++ b/jikimo_purchase_request/models/stock_rule.py @@ -1,4 +1,5 @@ from odoo import api, fields, models +from collections import defaultdict class StockRule(models.Model): @@ -44,7 +45,40 @@ class StockRule(models.Model): purchase_request_line_model.create(request_line_data) def _run_buy(self, procurements): - res = super(StockRule, self)._run_buy(procurements) + # 如果补货组相同,并且产品相同,则合并 + procurements_dict = defaultdict() + for procurement, rule in procurements: + if (procurement.product_id.id, procurement.values['group_id'], rule.id) not in procurements_dict: + procurements_dict[(procurement.product_id.id, procurement.values['group_id'], rule.id)] = { + 'product_id': procurement.product_id, + 'product_qty': procurement.product_qty, + 'product_uom': procurement.product_uom, + 'location_id': procurement.location_id, + 'name': procurement.name, + 'origin': procurement.origin, + 'company_id': procurement.company_id, + 'values': procurement.values, + 'rule': rule + } + else: + procurements_dict[(procurement.product_id.id, procurement.values['group_id'], rule.id)]['product_qty'] += procurement.product_qty + procurements_dict[(procurement.product_id.id, procurement.values['group_id'], rule.id)]['values']['move_dest_ids'] |= procurement.values['move_dest_ids'] + new_procurements = [] + for k, p in procurements_dict.items(): + new_procurements.append(( + self.env['procurement.group'].Procurement( + product_id=p['product_id'], + product_qty=p['product_qty'], + product_uom=p['product_uom'], + location_id=p['location_id'], + name=p['name'], + origin=p['origin'], + company_id=p['company_id'], + values=p['values'] + ), p['rule']) + ) + + res = super(StockRule, self)._run_buy(new_procurements) # 判断是否根据规则生成新的采购申请单据,如果生成则修改状态为 approved origins = list(set([procurement[0].origin for procurement in procurements])) for origin in origins: diff --git a/jikimo_purchase_request/views/stock_picking_views.xml b/jikimo_purchase_request/views/stock_picking_views.xml new file mode 100644 index 00000000..b96aea0c --- /dev/null +++ b/jikimo_purchase_request/views/stock_picking_views.xml @@ -0,0 +1,21 @@ + + + + stock.pikcing.inherited.form.jikimo.purchase.request + stock.picking + + + + + + + + \ No newline at end of file diff --git a/quality_control/models/quality.py b/quality_control/models/quality.py index f3a4315f..dbf4f0b4 100644 --- a/quality_control/models/quality.py +++ b/quality_control/models/quality.py @@ -141,7 +141,7 @@ class QualityCheck(models.Model): # # 出厂检验报告编号 # report_number = fields.Char('出厂检验报告编号', compute='_compute_report_number', readonly=True) # 总数量,值为调拨单_产品明细_数量 - total_qty = fields.Char('总数量', compute='_compute_total_qty') + total_qty = fields.Char('总数量', compute='_compute_total_qty', store=True) column_nums = fields.Integer('测量值列数', default=1) @@ -153,9 +153,9 @@ class QualityCheck(models.Model): for move in record.picking_id.move_ids_without_package: if move.product_id == record.product_id: total_qty = int(move.product_uom_qty) - record.total_qty = total_qty if total_qty > 0 else '' + record.total_qty = total_qty if total_qty > 0 else 0 else: - record.total_qty = '' + record.total_qty = 0 # 检验数 check_qty = fields.Integer('检验数', default=lambda self: self._get_default_check_qty()) @@ -735,8 +735,9 @@ class QualityCheck(models.Model): def _compute_qty_to_test(self): for qc in self: if qc.is_lot_tested_fractionally: + rounding = qc.product_id.uom_id.rounding if qc.product_id.uom_id else 0.01 qc.qty_to_test = float_round(qc.qty_line * qc.testing_percentage_within_lot / 100, - precision_rounding=self.product_id.uom_id.rounding, rounding_method="UP") + precision_rounding=rounding, rounding_method="UP") else: qc.qty_to_test = qc.qty_line diff --git a/quality_control/wizard/quality_check_wizard.py b/quality_control/wizard/quality_check_wizard.py index f4297f47..4da51f4b 100644 --- a/quality_control/wizard/quality_check_wizard.py +++ b/quality_control/wizard/quality_check_wizard.py @@ -23,8 +23,8 @@ class QualityCheckWizard(models.TransientModel): lot_name = fields.Char(related='current_check_id.lot_name') lot_line_id = fields.Many2one(related='current_check_id.lot_line_id') qty_line = fields.Float(related='current_check_id.qty_line') - qty_to_test = fields.Float(related='current_check_id.qty_to_test') - qty_tested = fields.Float(related='current_check_id.qty_tested', readonly=False) + qty_to_test = fields.Float(related='current_check_id.qty_to_test', string='待检') + qty_tested = fields.Float(related='current_check_id.qty_tested', string='已检', readonly=False) measure = fields.Float(related='current_check_id.measure', readonly=False) measure_on = fields.Selection(related='current_check_id.measure_on') quality_state = fields.Selection(related='current_check_id.quality_state') diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index 070ff404..2c32ed9c 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -1357,11 +1357,10 @@ class ResMrpWorkOrder(models.Model): # 判断是否有坯料的序列号信息 boolean = False if self.production_id.move_raw_ids: - if self.production_id.move_raw_ids[0].product_id.categ_type == '坯料': + if self.production_id.move_raw_ids[0].product_id.categ_type == '坯料' and self.production_id.move_raw_ids[0].product_id.tracking == 'serial': if self.production_id.move_raw_ids[0].move_line_ids: - if self.production_id.move_raw_ids[0].move_line_ids: - if self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name: - boolean = True + if self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name: + boolean = True else: boolean = True if not boolean: diff --git a/sf_manufacturing/models/product_template.py b/sf_manufacturing/models/product_template.py index 41637123..775df50e 100644 --- a/sf_manufacturing/models/product_template.py +++ b/sf_manufacturing/models/product_template.py @@ -1030,6 +1030,7 @@ class ResProductMo(models.Model): 'single_manufacturing': product_id.single_manufacturing, 'is_bfm': True, 'active': True, + 'tracking': finish_product.tracking, # 坯料的跟踪方式跟随成品 } # 外协和采购生成的坯料需要根据材料型号绑定供应商 if route_type == 'subcontract' or route_type == 'purchase': diff --git a/sf_manufacturing/models/purchase_order.py b/sf_manufacturing/models/purchase_order.py index 9b682d83..f4a7be81 100644 --- a/sf_manufacturing/models/purchase_order.py +++ b/sf_manufacturing/models/purchase_order.py @@ -96,8 +96,12 @@ class PurchaseOrder(models.Model): # 'origin': record.name + ',' + replenish.origin, # }) # replenish.action_replenish() - - return super(PurchaseOrder, self).button_confirm() + res = super(PurchaseOrder, self).button_confirm() + for line in self.order_line: + # 将产品不追踪序列号的行项目设置qty_done + if line.move_ids[0].product_id.tracking == 'none': + line.move_ids[0].quantity_done = line.move_ids[0].product_qty + return res origin_sale_id = fields.Many2one('sale.order', string='销售订单号', store=True, compute='_compute_origin_sale_id') diff --git a/sf_manufacturing/models/sale_order.py b/sf_manufacturing/models/sale_order.py index c8cc129d..4ce8750c 100644 --- a/sf_manufacturing/models/sale_order.py +++ b/sf_manufacturing/models/sale_order.py @@ -59,6 +59,7 @@ class SaleOrder(models.Model): line.product_id.product_tmpl_id.copy_template(product_template_id) # 将模板上的single_manufacturing属性复制到成品上 line.product_id.single_manufacturing = product_template_id.single_manufacturing + line.product_id.tracking = product_template_id.tracking order_id = self product = line.product_id diff --git a/sf_manufacturing/models/stock.py b/sf_manufacturing/models/stock.py index 3a1b3cd9..249c3d9b 100644 --- a/sf_manufacturing/models/stock.py +++ b/sf_manufacturing/models/stock.py @@ -675,6 +675,7 @@ class StockPicking(models.Model): # 创建 外协出库入单 def create_outcontract_picking(self, workorders, item, sorted_workorders): + production = workorders[0].production_id for workorder in workorders: if workorder.move_subcontract_workorder_ids: workorder.move_subcontract_workorder_ids.write({'state': 'cancel'}) @@ -706,7 +707,7 @@ class StockPicking(models.Model): }) moves_in = self.env['stock.move'].sudo().with_context(context).create( self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_in, - procurement_group_id.id, move_dest_id)) + procurement_group_id.id, move_dest_id, production.product_uom_qty)) picking_in = self.create( moves_in._get_new_picking_values_Res(item, workorder, 'WH/OCIN/')) # pick_ids.append(picking_in.id) @@ -716,7 +717,7 @@ class StockPicking(models.Model): # self.env.context.get('default_production_id') moves_out = self.env['stock.move'].sudo().with_context(context).create( self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_out, - procurement_group_id.id, moves_in.id)) + procurement_group_id.id, moves_in.id, production.product_uom_qty)) workorder.write({'move_subcontract_workorder_ids': [(6, 0, [moves_in.id, moves_out.id])]}) picking_out = self.create( moves_out._get_new_picking_values_Res(item, workorder, 'WH/OCOUT/')) @@ -848,7 +849,7 @@ class ReStockMove(models.Model): traceback_error = traceback.format_exc() logging.error("零件图号 零件名称获取失败:%s" % traceback_error) - def _get_stock_move_values_Res(self, item, picking_type_id, group_id, move_dest_ids=False): + def _get_stock_move_values_Res(self, item, picking_type_id, group_id, move_dest_ids=False, product_uom_qty=1.0): route_id = self.env.ref('sf_manufacturing.route_surface_technology_outsourcing').id stock_rule = self.env['stock.rule'].sudo().search( [('route_id', '=', route_id), ('picking_type_id', '=', picking_type_id)]) @@ -857,7 +858,7 @@ class ReStockMove(models.Model): 'company_id': item.company_id.id, 'product_id': item.bom_id.bom_line_ids.product_id.id, 'product_uom': item.bom_id.bom_line_ids.product_uom_id.id, - 'product_uom_qty': 1.0, + 'product_uom_qty': product_uom_qty, 'location_id': stock_rule.location_src_id.id, 'location_dest_id': stock_rule.location_dest_id.id, 'origin': item.name, diff --git a/sf_manufacturing/wizard/rework_wizard.py b/sf_manufacturing/wizard/rework_wizard.py index b6b657c1..cc3f5fb2 100644 --- a/sf_manufacturing/wizard/rework_wizard.py +++ b/sf_manufacturing/wizard/rework_wizard.py @@ -268,6 +268,8 @@ class ReworkWizard(models.TransientModel): 'cmm_ids': new_cnc_workorder.cmm_ids.sudo()._json_cmm_program( cnc_work.processing_panel, ret), 'cnc_worksheet': old_cnc_rework.cnc_worksheet}) + # 复制装夹图纸 + new_cnc_workorder.processing_drawing = old_cnc_rework.processing_drawing # ========== 处理装夹预调 【装夹图纸】 数据 ================ for new_pre_work in new_pre_workorder_ids: pre_rework = max(self.production_id.workorder_ids.filtered( diff --git a/sf_quality/models/quality.py b/sf_quality/models/quality.py index 9d762e4e..19c845b3 100644 --- a/sf_quality/models/quality.py +++ b/sf_quality/models/quality.py @@ -5,6 +5,7 @@ from odoo import fields, models, api from odoo.exceptions import ValidationError from datetime import datetime from odoo.addons.sf_base.commons.common import Common +from odoo.tools import float_round class QualityCheck(models.Model): @@ -132,3 +133,71 @@ class QualityCheck(models.Model): return "零件特采发送成功" else: raise ValidationError("零件特采发送失败") + + + + @api.model_create_multi + def create(self, vals_list): + for val in vals_list: + if 'point_id' in val and 'measure_on' not in val: + # 如果没有控制方式字段,则从检查点读取质量方式 + point_id = self.env['quality.point'].browse(val['point_id']) + val.update({'measure_on': point_id.measure_on}) + return super(QualityCheck, self).create(vals_list) + + + @api.depends('total_qty','testing_percentage_within_lot', 'is_lot_tested_fractionally') + def _compute_workorder_qty_to_test(self): + for qc in self: + if qc.is_lot_tested_fractionally: + rounding = qc.product_id.uom_id.rounding if qc.product_id.uom_id else 0.01 + qc.workorder_qty_to_test = float_round(float(qc.total_qty) * qc.testing_percentage_within_lot / 100, + precision_rounding=rounding, rounding_method="UP") + else: + qc.workorder_qty_to_test = qc.total_qty + + @api.depends('picking_id', 'workorder_id') + def _compute_total_qty(self): + super(QualityCheck, self)._compute_total_qty() + for qc in self: + if not qc.picking_id and qc.workorder_id: + qc.total_qty = qc.workorder_id.production_id.product_qty + + @api.depends('workorder_qty_to_test') + def _compute_workorder_qty_tested(self): + for qc in self: + qc.workorder_qty_tested = qc.workorder_qty_to_test + + + workorder_qty_to_test = fields.Float('应检', compute='_compute_workorder_qty_to_test', store=True) + workorder_qty_tested = fields.Float('已检', compute='_compute_workorder_qty_tested', store=True) + workorder_qty_test_failed = fields.Float('不合格数') + + + @api.onchange('total_qty', 'workorder_qty_test_failed', 'workorder_qty_to_test', 'workorder_qty_tested') + def _onchage_qty(self): + for record in self: + if record.total_qty and record.workorder_qty_to_test and record.workorder_qty_to_test > float(record.total_qty): + record.workorder_qty_to_test = float(record.total_qty) + return { + 'warning': { + 'title': '警告', + 'message': '待检数量不能超过总数量' + } + } + if record.workorder_qty_to_test and record.workorder_qty_tested and record.workorder_qty_tested > record.workorder_qty_to_test: + record.workorder_qty_tested = record.workorder_qty_to_test + return { + 'warning': { + 'title': '警告', + 'message': '已检数量不能超过待检数量' + } + } + if record.workorder_qty_tested and record.workorder_qty_test_failed and record.workorder_qty_test_failed > record.workorder_qty_tested: + record.workorder_qty_test_failed = record.workorder_qty_tested + return { + 'warning': { + 'title': '警告', + 'message': '不合格数量不能超过已检数量' + } + } \ 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 cadedc8e..69e1875f 100644 --- a/sf_quality/views/quality_check_view.xml +++ b/sf_quality/views/quality_check_view.xml @@ -88,6 +88,42 @@