400 lines
17 KiB
Python
400 lines
17 KiB
Python
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:
|
|
docs_has_movement = any(doc.cancel_reason for doc in wizard.related_docs)
|
|
order_canceled = wizard.order_id.state == 'cancel'
|
|
wizard.has_movement = docs_has_movement or order_canceled
|
|
|
|
@api.depends('has_movement', 'related_docs', 'related_docs.doc_state')
|
|
def _compute_display_message(self):
|
|
for wizard in self:
|
|
# 如果没有相关记录,显示为空
|
|
if not wizard.related_docs:
|
|
wizard.display_message = '无下游单据'
|
|
continue
|
|
|
|
# 检查是否所有记录都是已取消状态
|
|
all_canceled = all(doc.doc_state == '已取消' for doc in wizard.related_docs)
|
|
if all_canceled:
|
|
wizard.display_message = '取消的下游单据如下:'
|
|
else:
|
|
wizard.display_message = '部分或全部下游单据存在异动,无法取消,详情如下:' if wizard.has_movement else '确认所有下游单据全部取消?'
|
|
|
|
def action_confirm_cancel(self):
|
|
self.ensure_one()
|
|
|
|
# 取消销售订单关联的采购单
|
|
purchase_orders = self.env['purchase.order'].search([
|
|
('origin', '=', self.order_id.name)
|
|
])
|
|
if purchase_orders:
|
|
purchase_orders.write({'state': 'cancel'})
|
|
|
|
# 取消销售订单
|
|
result = self.order_id.action_cancel()
|
|
|
|
# 取消关联的制造订单及其采购单
|
|
manufacturing_orders = self.env['mrp.production'].search([
|
|
('origin', '=', self.order_id.name)
|
|
])
|
|
for mo in manufacturing_orders:
|
|
# 取消制造订单关联的采购单,但保持关联关系
|
|
mo_purchase_orders = self.env['purchase.order'].search([
|
|
('origin', '=', mo.name)
|
|
])
|
|
if mo_purchase_orders:
|
|
mo_purchase_orders.write({'state': 'cancel'})
|
|
|
|
# 取消制造订单的质检单
|
|
mo_quality_checks = self.env['quality.check'].search([
|
|
('production_id', '=', mo.id)
|
|
])
|
|
if mo_quality_checks:
|
|
mo_quality_checks.write({'quality_state': 'cancel'})
|
|
|
|
# 取消制造订单
|
|
mo.action_cancel()
|
|
|
|
# 取消制造订单关联的编程单
|
|
mo._change_programming_state()
|
|
|
|
# 取消组件的制造单关联的采购单
|
|
for comp_mo in self.env['mrp.production'].search([
|
|
('origin', '=', mo.name)
|
|
]):
|
|
comp_purchase_orders = self.env['purchase.order'].search([
|
|
('origin', '=', comp_mo.name)
|
|
])
|
|
if comp_purchase_orders:
|
|
comp_purchase_orders.button_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 = []
|
|
map_dict = {
|
|
'waiting': '等待其他作业',
|
|
'to approve': '待批准',
|
|
'technology_to_confirmed': '待工艺确认',
|
|
'confirmed': '已确认',
|
|
'pending': '等待其他工单',
|
|
'none': '待处理',
|
|
'draft': '询价',
|
|
'cancel': '已取消',
|
|
'pass': '通过的',
|
|
'fail': '失败的',
|
|
'done': '已完成',
|
|
'rework': '返工',
|
|
'purchase': '采购订单',
|
|
'ready': '就绪',
|
|
'approved': '已批准',
|
|
'pending_cam': '待加工',
|
|
'progress': '加工中',
|
|
'assigned': '就绪'
|
|
}
|
|
|
|
# 检查销售订单
|
|
if order.invoice_ids:
|
|
a = 0
|
|
for invoice in order.invoice_ids:
|
|
a += 1
|
|
vals = {
|
|
'wizard_id': wizard_id,
|
|
'sequence': sequence,
|
|
'category': '销售',
|
|
'doc_name': '销售订单',
|
|
'operation_type': '',
|
|
'doc_number': invoice.name,
|
|
'line_number': a,
|
|
'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:
|
|
b = 0
|
|
for picking in order.picking_ids:
|
|
for move in picking.move_ids:
|
|
b += 1
|
|
vals = {
|
|
'wizard_id': wizard_id,
|
|
'sequence': sequence,
|
|
'category': '库存',
|
|
'doc_name': '交货单',
|
|
'operation_type': '调拨',
|
|
'doc_number': picking.name,
|
|
'line_number': b,
|
|
'product_name': f'[{move.product_id.default_code}] {move.product_id.name}' if move else '',
|
|
# 'quantity': picking.product_qty if hasattr(picking, 'product_qty') else 0,
|
|
'quantity': move.product_uom_qty,
|
|
'doc_state': map_dict.get(picking.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:
|
|
c = 0
|
|
for po in purchase_orders:
|
|
c += 1
|
|
vals = {
|
|
'wizard_id': wizard_id,
|
|
'sequence': sequence,
|
|
'category': '采购',
|
|
'doc_name': '询价单',
|
|
'operation_type': '',
|
|
'doc_number': po.name,
|
|
'line_number': c,
|
|
'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': map_dict.get(po.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)
|
|
])
|
|
d = 0
|
|
for mo in manufacturing_orders:
|
|
# 添加制造订单本身
|
|
d += 1
|
|
vals = {
|
|
'wizard_id': wizard_id,
|
|
'sequence': sequence,
|
|
'category': '制造',
|
|
'doc_name': '制造订单',
|
|
'doc_number': mo.name,
|
|
'operation_type': '',
|
|
'line_number': d,
|
|
'product_name': f'[{mo.product_id.default_code}] {mo.product_id.name}',
|
|
'quantity': mo.product_qty,
|
|
'doc_state': map_dict.get(mo.state, mo.state),
|
|
'cancel_reason': '已有异动' if mo.state not in ['technology_to_confirmed', 'cancel'] else ''
|
|
}
|
|
lines.append(self.create(vals))
|
|
sequence += 1
|
|
|
|
# 检查制造订单关联的采购单
|
|
purchase_orders = self.env['purchase.order'].search([
|
|
('origin', '=', mo.name)
|
|
])
|
|
if purchase_orders:
|
|
e = 0
|
|
for po in purchase_orders:
|
|
e += 1
|
|
vals = {
|
|
'wizard_id': wizard_id,
|
|
'sequence': sequence,
|
|
'category': '制造',
|
|
'doc_name': '询价单',
|
|
'doc_number': po.name,
|
|
'line_number': e,
|
|
'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': map_dict.get(po.state, po.state),
|
|
'cancel_reason': '已有异动' if po.state not in ['draft', 'cancel'] else ''
|
|
}
|
|
lines.append(self.create(vals))
|
|
sequence += 1
|
|
|
|
# 检查制造订单的领料单
|
|
if mo.picking_ids:
|
|
f = 0
|
|
for picking in mo.picking_ids:
|
|
|
|
for move in picking.move_ids:
|
|
f += 1
|
|
vals = {
|
|
'wizard_id': wizard_id,
|
|
'sequence': sequence,
|
|
'category': '制造',
|
|
'doc_name': '库存移动',
|
|
'doc_number': picking.name,
|
|
'line_number': f,
|
|
'operation_type': picking.picking_type_id.name,
|
|
'product_name': move.product_id.name if move.product_id else '',
|
|
'quantity': move.product_uom_qty,
|
|
'doc_state': map_dict.get(picking.state, picking.state),
|
|
'cancel_reason': '已有异动' if picking.state not in ['draft', 'cancel', 'waiting'] else ''
|
|
}
|
|
lines.append(self.create(vals))
|
|
sequence += 1
|
|
break
|
|
|
|
# 检查制造订单的工单
|
|
if mo.workorder_ids:
|
|
g = 0
|
|
for workorder in mo.workorder_ids:
|
|
g += 1
|
|
vals = {
|
|
'wizard_id': wizard_id,
|
|
'sequence': sequence,
|
|
'category': '制造',
|
|
'doc_name': '工单',
|
|
'doc_number': workorder.name,
|
|
'line_number': g,
|
|
'operation_type': '',
|
|
'product_name': f'[{mo.product_id.default_code}] {mo.product_id.name}',
|
|
'quantity': workorder.qty_production,
|
|
'doc_state': map_dict.get(workorder.state, workorder.state),
|
|
'cancel_reason': '已有异动' if workorder.state not in ['draft', 'cancel', 'pending',
|
|
'waiting'] 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)
|
|
])
|
|
h = 0
|
|
for comp_mo in component_mos:
|
|
h += 1
|
|
vals = {
|
|
'wizard_id': wizard_id,
|
|
'sequence': sequence,
|
|
'category': '制造',
|
|
'doc_name': '组件制造单',
|
|
'operation_type': '',
|
|
'doc_number': comp_mo.name,
|
|
'line_number': h,
|
|
'product_name': move.product_id.name,
|
|
'quantity': comp_mo.product_qty,
|
|
'doc_state': map_dict.get(comp_mo.state, comp_mo.state),
|
|
'cancel_reason': '已有异动' if comp_mo.state not in ['technology_to_confirmed', 'cancel'] else ''
|
|
}
|
|
lines.append(self.create(vals))
|
|
sequence += 1
|
|
|
|
# 检查制造订单的质检单
|
|
quality_checks = self.env['quality.check'].search([
|
|
('production_id', '=', mo.id)
|
|
])
|
|
if quality_checks:
|
|
i = 0
|
|
for check in quality_checks:
|
|
i += 1
|
|
vals = {
|
|
'wizard_id': wizard_id,
|
|
'sequence': sequence,
|
|
'category': '制造',
|
|
'doc_name': '质检单',
|
|
'operation_type': '',
|
|
'doc_number': check.name,
|
|
'line_number': i,
|
|
'product_name': f'[{check.product_id.default_code}] {check.product_id.name}',
|
|
'quantity': 1,
|
|
'doc_state': map_dict.get(check.quality_state, check.quality_state),
|
|
'cancel_reason': '已有异动' if check.quality_state not in ['none', 'cancel', 'waiting'] else ''
|
|
}
|
|
lines.append(self.create(vals))
|
|
sequence += 1
|
|
|
|
# 检查制造订单的编程单
|
|
cloud_programming = mo._cron_get_programming_state()
|
|
if cloud_programming:
|
|
vals = {
|
|
'wizard_id': wizard_id,
|
|
'sequence': sequence,
|
|
'category': '编程',
|
|
'doc_name': '编程单',
|
|
'operation_type': '',
|
|
'doc_number': cloud_programming['programming_no'],
|
|
'line_number': 1,
|
|
'product_name': '',
|
|
'quantity': '',
|
|
'doc_state': cloud_programming['programming_state'],
|
|
'cancel_reason': ''
|
|
}
|
|
lines.append(self.create(vals))
|
|
sequence += 1
|
|
return lines
|
|
|
|
# unique_lines = {}
|
|
# for line in lines:
|
|
# doc_number = line.doc_number
|
|
# if doc_number not in unique_lines:
|
|
# unique_lines[doc_number] = line
|
|
#
|
|
# # 返回去重后的记录列表
|
|
# return list(unique_lines.values())
|