203 lines
8.8 KiB
Python
203 lines
8.8 KiB
Python
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 = {}
|
|
product_rounding = {}
|
|
if lines:
|
|
for line in lines:
|
|
for line_item in line.order_line:
|
|
if line_item.state == 'purchase':
|
|
product_id = line_item.product_id.id
|
|
qty = line_item.product_qty
|
|
product_rounding[product_id] = line_item.product_id.uom_id.rounding
|
|
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_rounding=product_rounding[product_id]) < 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)
|
|
else:
|
|
product_name = record.product_id.name
|
|
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
|