import re import ast from odoo import models, fields, api, _ from itertools import groupby from odoo.tools import float_compare class PurchaseRequest(models.Model): _inherit = 'purchase.request' _description = '采购申请' # 为state添加取消状态 state = fields.Selection( selection_add=[('cancel', '已取消')], ondelete={'cancel': 'set default'} # 添加 ondelete 策略 ) rule_new_add = fields.Boolean('采购请求为规则创建', default=False, compute='_compute_state', store=True) @api.depends('state') def _compute_state(self): for pr in self: if pr.state != 'draft' and pr.rule_new_add: pr.rule_new_add = False def action_view_purchase_order(self): action = super(PurchaseRequest, self).action_view_purchase_order() origin_context = ast.literal_eval(action['context']) if 'search_default_draft' in origin_context: origin_context.pop('search_default_draft') action['context'] = origin_context return action def button_done(self): product_qty_map = {key: sum(line.product_qty for line in group) for key, group in groupby(self.line_ids, key=lambda x: x.product_id.id)} lines = self.mapped("line_ids.purchase_lines.order_id") # 采购单产品和数量 product_summary = {} if lines: for line in lines: for line_item in line.order_line: product_id = line_item.product_id.id qty = line_item.product_qty if product_id in product_summary: product_summary[product_id] += qty else: product_summary[product_id] = qty # 校验产品数量 discrepancies = [] for product_id, qty in product_qty_map.items(): if product_id in product_summary: if float_compare(product_summary[product_id], qty, precision_digits=2) < 0: discrepancies.append((product_id, qty, product_summary[product_id])) else: discrepancies.append((product_id, qty, 0)) if discrepancies: # 弹出提示框 message = "产品数量不一致:\n" for product_id, required_qty, order_qty in discrepancies: product_name = self.env['product.product'].browse(product_id).display_name # 获取产品名称 message += f"产品 {product_name},需求数量 {required_qty},关联采购订单数量 {order_qty}(含询价状态)\n" # 添加确认框 message += "确认关闭?" return { 'name': _('采购申请'), 'type': 'ir.actions.act_window', 'views': [(self.env.ref( 'jikimo_purchase_request.purchase_request_wizard_wizard_form_view').id, 'form')], 'res_model': 'purchase.request.wizard', 'target': 'new', 'context': { 'default_purchase_request_id': self.id, 'default_message': message, }} return super(PurchaseRequest, self).button_done() class PurchaseRequestLine(models.Model): _inherit = 'purchase.request.line' _description = '采购申请明细' origin = fields.Char(string="Source Document") part_number = fields.Char('零件图号', store=True, compute='_compute_part_number') part_name = fields.Char('零件名称', store=True, compute='_compute_part_number') related_product = fields.Many2one('product.product', string='关联产品', help='经此产品工艺加工成的成品') supply_method = fields.Selection([ ('automation', "自动化产线加工"), ('manual', "人工线下加工"), ('purchase', "外购"), ('outsourcing', "委外加工"), ], string='供货方式', compute='_compute_supply_method', store=True) purchase_request_count = fields.Integer(string='采购申请数量', compute='_compute_purchase_request_count', readonly=True) purchase_count = fields.Integer(string="采购订单数量", compute="_compute_purchase_count", readonly=True) @api.depends("purchase_lines") def _compute_purchase_count(self): for rec in self: rec.purchase_count = len(rec.mapped("purchase_lines.order_id")) @api.depends('request_id') def _compute_purchase_request_count(self): for order in self: order.purchase_request_count = len(order.request_id) @api.depends('origin') def _compute_supply_method(self): for prl in self: order_ids = [] if not prl.origin: continue origin = [origin.replace(' ', '') for origin in prl.origin.split(',')] if 'S' in prl.origin: # 原单据是销售订单 order_ids = self.env['sale.order'].sudo().search([('name', 'in', origin)]).ids elif 'MO' in prl.origin: # 原单据是制造订单 mp_ids = self.env['mrp.production'].sudo().search([('name', 'in', origin)]) order_ids = [mp_id.sale_order_id.id for mp_id in mp_ids] if mp_ids else [] elif 'WH' in prl.origin: # 原单据是调拨单 sp_ids = self.env['stock.picking'].sudo().search([('name', 'in', origin)]) order_ids = [sp_id.sale_order_id.id for sp_id in sp_ids] if sp_ids else [] order_line = self.env['sale.order.line'].sudo().search( [('product_id', '=', prl.product_id.id), ('order_id', 'in', order_ids)]) if order_line: prl.supply_method = order_line[0].supply_method else: prl.supply_method = None @api.depends('product_id') def _compute_part_number(self): for record in self: if record.part_number and record.part_name: continue if record.product_id.categ_id.name == '坯料': product_name = '' match = re.search(r'(S\d{5}-\d+)', record.product_id.name) # 如果匹配成功,提取结果 if match: product_name = match.group(0) sale_order_name = '' match_sale = re.search(r'S(\d+)', record.product_id.name) if match_sale: sale_order_name = match_sale.group(0) sale_order = self.env['sale.order'].sudo().search( [('name', '=', sale_order_name)]) if sale_order: filtered_order_line = sale_order.order_line.filtered( lambda order_line: re.search(f'{product_name}$', order_line.product_id.name) ) record.part_number = filtered_order_line.product_id.part_number record.part_name = filtered_order_line.product_id.part_name else: record.part_number = record.product_id.part_number record.part_name = record.product_id.part_name def _compute_qty_to_buy(self): for pr in self: qty_to_buy = sum(pr.mapped("product_qty")) if pr.purchase_count > 0: qty_to_buy -= sum(pr.mapped("purchase_lines").filtered(lambda po: po.state != 'cancel').mapped( "product_qty")) pr.qty_to_buy = qty_to_buy > 0.0 pr.pending_qty_to_receive = qty_to_buy def action_view_purchase_request(self): action = self.env["ir.actions.actions"]._for_xml_id("purchase_request.purchase_request_form_action") action.update({ 'res_id': self.request_id.id, 'views': [[False, 'form']], }) return action def action_view_purchase_order(self): action = self.env["ir.actions.actions"]._for_xml_id("purchase.purchase_rfq") lines = self.mapped("purchase_lines.order_id") if len(lines) > 1: action["domain"] = [("id", "in", lines.ids)] elif lines: action["views"] = [ (self.env.ref("purchase.purchase_order_form").id, "form") ] action["res_id"] = lines.id origin_context = ast.literal_eval(action['context']) if 'search_default_draft' in origin_context: origin_context.pop('search_default_draft') action['context'] = origin_context return action