from odoo import models, fields, api class SFSaleOrderCancelWizard(models.TransientModel): _name = 'sf.sale.order.cancel.wizard' _description = '销售订单取消向导' order_id = fields.Many2one('sale.order', string='销售订单') related_docs = fields.One2many('sf.sale.order.cancel.line', 'wizard_id', string='相关单据') has_movement = fields.Boolean(compute='_compute_has_movement', string='是否有异动') display_message = fields.Char(compute='_compute_display_message', string='显示消息') @api.model def default_get(self, fields_list): defaults = super().default_get(fields_list) if self._context.get('active_id'): order = self.env['sale.order'].browse(self._context.get('active_id')) defaults['order_id'] = order.id # 创建向导时自动创建关联单据行 wizard = self.create(defaults) self.env['sf.sale.order.cancel.line'].create_from_order(wizard.id, order) defaults['related_docs'] = wizard.related_docs.ids return defaults @api.depends('related_docs.cancel_reason') def _compute_has_movement(self): for wizard in self: wizard.has_movement = any(doc.cancel_reason for doc in wizard.related_docs) @api.depends('has_movement') def _compute_display_message(self): for wizard in self: wizard.display_message = '部分或全部下游单据存在异动,无法取消,详情如下:' if wizard.has_movement else '确认所有下游单据全部取消?' def action_confirm_cancel(self): self.ensure_one() # 取消销售订单 result = self.order_id.action_cancel() # 取消关联的制造订单 manufacturing_orders = self.env['mrp.production'].search([ ('origin', '=', self.order_id.name) ]) if manufacturing_orders: manufacturing_orders.action_cancel() return result class SFSaleOrderCancelLine(models.TransientModel): _name = 'sf.sale.order.cancel.line' _description = '销售订单取消行' wizard_id = fields.Many2one('sf.sale.order.cancel.wizard') sequence = fields.Integer('序号') category = fields.Char('大类') doc_name = fields.Char('单据名称') operation_type = fields.Char('作业类型') doc_number = fields.Char('单据编号') line_number = fields.Char('行号') product_name = fields.Char('产品名称') quantity = fields.Float('数量') doc_state = fields.Char('单据状态') cancel_reason = fields.Char('禁止取消原因') @api.model def create_from_order(self, wizard_id, order): sequence = 1 lines = [] # 检查销售订单 if order.invoice_ids: for invoice in order.invoice_ids: vals = { 'wizard_id': wizard_id, 'sequence': sequence, 'category': '销售', 'doc_name': '销售订单', 'operation_type': '销售', 'doc_number': invoice.name, 'product_name': invoice.product_id.name, 'quantity': invoice.quantity, 'doc_state': invoice.state, 'cancel_reason': '已有异动' if invoice.state != 'draft' else '' } lines.append(self.create(vals)) sequence += 1 # 检查交货单 if order.picking_ids: for picking in order.picking_ids: vals = { 'wizard_id': wizard_id, 'sequence': sequence, 'category': '库存', 'doc_name': '交货单', 'operation_type': '调拨', 'doc_number': picking.name, 'product_name': picking.product_id.name if picking.product_id else '', # 'quantity': picking.product_qty if hasattr(picking, 'product_qty') else 0, 'quantity': sum(picking.move_ids.mapped('product_uom_qty') or [0]), 'doc_state': picking.state, 'cancel_reason': '已有异动' if picking.state not in ['draft', 'cancel', 'waiting'] else '' } lines.append(self.create(vals)) sequence += 1 # 检查销售订单直接关联的采购单 purchase_orders = self.env['purchase.order'].search([ ('origin', '=', order.name) ]) if purchase_orders: for po in purchase_orders: vals = { 'wizard_id': wizard_id, 'sequence': sequence, 'category': '采购', 'doc_name': '采购单', 'operation_type': '销售采购', 'doc_number': po.name, 'product_name': po.order_line[0].product_id.name if po.order_line else '', 'quantity': po.order_line[0].product_qty if po.order_line else 0, 'doc_state': po.state, 'cancel_reason': '已有异动' if po.state not in ['draft', 'cancel'] else '' } lines.append(self.create(vals)) sequence += 1 # 检查制造订单 manufacturing_orders = self.env['mrp.production'].search([ ('origin', '=', order.name) ]) for mo in manufacturing_orders: # 添加制造订单本身 vals = { 'wizard_id': wizard_id, 'sequence': sequence, 'category': '制造', 'doc_name': '制造订单', 'doc_number': mo.name, 'operation_type': '制造', 'product_name': mo.product_id.name, 'quantity': mo.product_qty, 'doc_state': mo.state, 'cancel_reason': '已有异动' if mo.state not in ['technology_to_confirmed'] else '' } lines.append(self.create(vals)) sequence += 1 # 检查制造订单关联的采购单 purchase_orders = self.env['purchase.order'].search([ ('origin', '=', mo.name) ]) if purchase_orders: for po in purchase_orders: vals = { 'wizard_id': wizard_id, 'sequence': sequence, 'category': '制造', 'doc_name': '采购单', 'doc_number': po.name, 'operation_type': '制造采购', 'product_name': po.order_line[0].product_id.name if po.order_line else '', 'quantity': po.order_line[0].product_qty if po.order_line else 0, 'doc_state': po.state, 'cancel_reason': '已有异动' if po.state not in ['draft', 'cancel'] else '' } lines.append(self.create(vals)) sequence += 1 # 检查制造订单的领料单 if mo.picking_ids: for picking in mo.picking_ids: vals = { 'wizard_id': wizard_id, 'sequence': sequence, 'category': '制造', 'doc_name': '领料单', 'doc_number': picking.name, 'operation_type': picking.picking_type_id.name, 'product_name': picking.product_id.name if picking.product_id else '', 'quantity': sum(picking.move_ids.mapped('product_uom_qty') or [0]), 'doc_state': picking.state, 'cancel_reason': '已有异动' if picking.state not in ['draft', 'cancel', 'waiting'] else '' } lines.append(self.create(vals)) sequence += 1 # 检查制造订单的工单 if mo.workorder_ids: for workorder in mo.workorder_ids: vals = { 'wizard_id': wizard_id, 'sequence': sequence, 'category': '制造', 'doc_name': '工单', 'doc_number': workorder.name, 'operation_type': workorder.workcenter_id.name, 'product_name': mo.product_id.name, 'quantity': workorder.qty_production, 'doc_state': workorder.state, 'cancel_reason': '已有异动' if workorder.state not in ['draft', 'cancel', 'pending'] else '' } lines.append(self.create(vals)) sequence += 1 # 检查制造订单组件的采购单和制造单 for move in mo.move_raw_ids: # 检查组件的采购单 component_pos = self.env['purchase.order'].search([ ('origin', '=', mo.name), ('order_line.product_id', '=', move.product_id.id) ]) for po in component_pos: vals = { 'wizard_id': wizard_id, 'sequence': sequence, 'category': '制造', 'doc_name': '组件采购单', 'operation_type': '组件采购', 'doc_number': po.name, 'product_name': move.product_id.name, 'quantity': po.order_line[0].product_qty if po.order_line else 0, 'doc_state': po.state, 'cancel_reason': '已有异动' if po.state not in ['draft', 'cancel'] else '' } lines.append(self.create(vals)) sequence += 1 # 检查组件的制造单 component_mos = self.env['mrp.production'].search([ ('origin', '=', mo.name), ('product_id', '=', move.product_id.id) ]) for comp_mo in component_mos: vals = { 'wizard_id': wizard_id, 'sequence': sequence, 'category': '制造', 'doc_name': '组件制造单', 'operation_type': '组件制造', 'doc_number': comp_mo.name, 'product_name': move.product_id.name, 'quantity': comp_mo.product_qty, 'doc_state': comp_mo.state, 'cancel_reason': '已有异动' if comp_mo.state not in ['technology_to_confirmed'] else '' } lines.append(self.create(vals)) sequence += 1 # 检查制造订单的质检单 quality_checks = self.env['quality.check'].search([ ('production_id', '=', mo.id) ]) if quality_checks: for check in quality_checks: vals = { 'wizard_id': wizard_id, 'sequence': sequence, 'category': '制造', 'doc_name': '质检单', 'doc_number': check.name, 'product_name': check.product_id.name, 'doc_state': check.state, 'cancel_reason': '已有异动' if check.state not in ['draft', 'cancel'] else '' } lines.append(self.create(vals)) sequence += 1 return lines