Chưa thành công trong việc gộp nhiều PRs để tạo thành RFQs
This commit is contained in:
parent
0570680d58
commit
7555d2ca65
@ -34,6 +34,7 @@
|
||||
'views/epr_po_views.xml',
|
||||
'views/epr_menus.xml',
|
||||
'wizards/epr_reject_wizard_views.xml',
|
||||
'wizards/epr_create_rfq_views.xml',
|
||||
'wizards/epr_create_po_views.xml',
|
||||
],
|
||||
'assets': {
|
||||
|
||||
11
addons/epr/data/import_partners.csv
Normal file
11
addons/epr/data/import_partners.csv
Normal file
@ -0,0 +1,11 @@
|
||||
id,name,email,phone,is_company,supplier_rank
|
||||
partner_vendor_1,CÔNG TY TNHH THIẾT BỊ VĂN PHÒNG ABC,contact@abc-office.vn,028 1234 5678,True,1
|
||||
partner_vendor_2,CÔNG TY CP CÔNG NGHỆ DELTA,sales@deltatech.vn,024 9876 5432,True,1
|
||||
partner_vendor_3,NHÀ PHÂN PHỐI PHẦN MỀM SIGMA,info@sigmasoft.vn,028 5555 6666,True,1
|
||||
partner_vendor_4,CÔNG TY VẬT TƯ XÂY DỰNG OMEGA,order@omega-building.vn,028 7777 8888,True,1
|
||||
partner_vendor_5,ĐẠI LÝ VĂN PHÒNG PHẨM BETA,sales@beta-stationery.vn,028 2222 3333,True,1
|
||||
partner_vendor_6,CÔNG TY TNHH ĐIỆN TỬ GAMMA,contact@gamma-electronics.vn,024 4444 5555,True,1
|
||||
partner_vendor_7,NHÀ CUNG CẤP NỘI THẤT EPSILON,info@epsilon-furniture.vn,028 6666 7777,True,1
|
||||
partner_vendor_8,CÔNG TY DỊCH VỤ IT ZETA,support@zeta-it.vn,1900 1234,True,1
|
||||
partner_vendor_9,ĐẠI LÝ PHÂN PHỐI LAMBDA,order@lambda-dist.vn,028 8888 9999,True,1
|
||||
partner_vendor_10,CÔNG TY THƯƠNG MẠI THETA,sales@theta-trading.vn,024 1111 2222,True,1
|
||||
|
21
addons/epr/data/import_purchase_request_lines.csv
Normal file
21
addons/epr/data/import_purchase_request_lines.csv
Normal file
@ -0,0 +1,21 @@
|
||||
id,request_id/id,name,product_description,quantity,estimated_price,uom_name,suggested_vendor_name
|
||||
pr_line_1,pr_demo_1,Máy in HP LaserJet Pro M404dn,Máy in laser đen trắng - in 2 mặt tự động - kết nối mạng LAN,2,8500000,Cái,CÔNG TY TNHH THIẾT BỊ VĂN PHÒNG ABC
|
||||
pr_line_2,pr_demo_2,Laptop Dell Latitude 5540,Laptop văn phòng - Intel Core i5 - RAM 16GB - SSD 512GB,5,25000000,Bộ,CÔNG TY CP CÔNG NGHỆ DELTA
|
||||
pr_line_3,pr_demo_3,Bàn làm việc 1m4,Bàn làm việc chữ L - kích thước 1m4 x 0.6m - màu gỗ sồi,10,3500000,Cái,NHÀ CUNG CẤP NỘI THẤT EPSILON
|
||||
pr_line_4,pr_demo_4,Ghế công thái học Ergohuman,Ghế văn phòng cao cấp - hỗ trợ lưng - tay vịn điều chỉnh,5,12000000,Cái,NHÀ CUNG CẤP NỘI THẤT EPSILON
|
||||
pr_line_5,pr_demo_5,Máy chiếu Epson EB-X51,Máy chiếu 3800 lumens - độ phân giải XGA - kết nối HDMI,2,15000000,Cái,CÔNG TY TNHH ĐIỆN TỬ GAMMA
|
||||
pr_line_6,pr_demo_6,Microsoft 365 Business Premium (1 năm),Bản quyền phần mềm Office 365 - bao gồm Teams - OneDrive 1TB,50,3000000,License,NHÀ PHÂN PHỐI PHẦN MỀM SIGMA
|
||||
pr_line_7,pr_demo_7,Ổ cứng SSD Samsung 870 EVO 1TB,Ổ cứng SSD SATA 2.5 inch - tốc độ đọc 560MB/s,10,2500000,Cái,CÔNG TY CP CÔNG NGHỆ DELTA
|
||||
pr_line_8,pr_demo_8,Dịch vụ bảo trì Server (6 tháng),Gói bảo trì hệ thống server - bao gồm backup và monitoring 24/7,1,50000000,Gói,CÔNG TY DỊCH VỤ IT ZETA
|
||||
pr_line_9,pr_demo_9,Router Wifi TP-Link Archer AX73,Router Wifi 6 - băng tần kép - tốc độ 5400Mbps,5,3200000,Cái,CÔNG TY TNHH ĐIỆN TỬ GAMMA
|
||||
pr_line_10,pr_demo_10,Webcam Logitech C920 HD Pro,Webcam Full HD 1080p - tự động lấy nét - micro kép,10,2800000,Cái,CÔNG TY TNHH ĐIỆN TỬ GAMMA
|
||||
pr_line_11,pr_demo_11,Giấy A4 Double A 80gsm,Giấy in A4 chất lượng cao - độ trắng 96%,100,85000,Ream,ĐẠI LÝ VĂN PHÒNG PHẨM BETA
|
||||
pr_line_12,pr_demo_12,Bút bi Thiên Long TL-027,Bút bi mực xanh - nét thanh đẹp,200,5000,Cây,ĐẠI LÝ VĂN PHÒNG PHẨM BETA
|
||||
pr_line_13,pr_demo_13,Kẹp giấy loại lớn,Kẹp giấy kim loại 51mm - hộp 12 cái,50,25000,Hộp,ĐẠI LÝ VĂN PHÒNG PHẨM BETA
|
||||
pr_line_14,pr_demo_14,Sổ tay bìa cứng A5,Sổ tay bìa da cao cấp - 200 trang giấy kẻ ngang,30,65000,Quyển,ĐẠI LÝ VĂN PHÒNG PHẨM BETA
|
||||
pr_line_15,pr_demo_15,Mực in HP 76A Black,Hộp mực chính hãng cho máy in HP LaserJet Pro M404,10,2200000,Hộp,CÔNG TY TNHH THIẾT BỊ VĂN PHÒNG ABC
|
||||
pr_line_16,pr_demo_16,Điều hòa Daikin Inverter 1HP,Điều hòa tiết kiệm điện - công suất 9000BTU,3,12000000,Bộ,CÔNG TY TNHH ĐIỆN TỬ GAMMA
|
||||
pr_line_17,pr_demo_17,Bình nước nóng Ariston 30L,Bình nước nóng điện - dung tích 30 lít - có chống giật,2,4500000,Cái,CÔNG TY VẬT TƯ XÂY DỰNG OMEGA
|
||||
pr_line_18,pr_demo_18,Camera IP Hikvision DS-2CD1047G2,Camera IP 4MP - hồng ngoại 30m - chuẩn IP67,8,1800000,Cái,CÔNG TY TNHH ĐIỆN TỬ GAMMA
|
||||
pr_line_19,pr_demo_19,Tủ hồ sơ sắt 3 ngăn,Tủ hồ sơ kim loại - có khóa - kích thước 1.2m x 0.5m x 0.4m,5,2800000,Cái,NHÀ CUNG CẤP NỘI THẤT EPSILON
|
||||
pr_line_20,pr_demo_20,Nước uống đóng chai Aquafina 500ml,Nước tinh khiết chai 500ml - thùng 24 chai,50,95000,Thùng,CÔNG TY THƯƠNG MẠI THETA
|
||||
|
21
addons/epr/data/import_purchase_requests.csv
Normal file
21
addons/epr/data/import_purchase_requests.csv
Normal file
@ -0,0 +1,21 @@
|
||||
id,date_required,priority,state
|
||||
pr_demo_1,2024-12-23,2,draft
|
||||
pr_demo_2,2024-12-21,3,draft
|
||||
pr_demo_3,2024-12-26,1,draft
|
||||
pr_demo_4,2024-12-19,4,draft
|
||||
pr_demo_5,2024-12-30,2,draft
|
||||
pr_demo_6,2025-01-15,2,draft
|
||||
pr_demo_7,2024-12-23,3,draft
|
||||
pr_demo_8,2024-12-21,4,draft
|
||||
pr_demo_9,2025-01-06,1,draft
|
||||
pr_demo_10,2024-12-26,2,draft
|
||||
pr_demo_11,2024-12-19,1,draft
|
||||
pr_demo_12,2024-12-21,1,draft
|
||||
pr_demo_13,2024-12-23,2,draft
|
||||
pr_demo_14,2024-12-26,1,draft
|
||||
pr_demo_15,2024-12-21,2,draft
|
||||
pr_demo_16,2024-12-30,3,draft
|
||||
pr_demo_17,2025-01-06,2,draft
|
||||
pr_demo_18,2024-12-23,4,draft
|
||||
pr_demo_19,2024-12-26,2,draft
|
||||
pr_demo_20,2025-01-15,1,draft
|
||||
|
Binary file not shown.
Binary file not shown.
@ -88,15 +88,15 @@ class EprPurchaseRequest(models.Model):
|
||||
)
|
||||
|
||||
currency_id = fields.Many2one(
|
||||
'res.currency',
|
||||
comodel_name='res.currency',
|
||||
string='Currency',
|
||||
default=lambda self: self.env.company.currency_id,
|
||||
required=True
|
||||
)
|
||||
|
||||
line_ids = fields.One2many(
|
||||
'epr.purchase.request.line',
|
||||
'request_id',
|
||||
comodel_name='epr.purchase.request.line',
|
||||
inverse_name='request_id',
|
||||
string='Products'
|
||||
)
|
||||
|
||||
@ -397,10 +397,10 @@ class EprPurchaseRequest(models.Model):
|
||||
'name': _('Request for Quotations'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'epr.rfq',
|
||||
'view_mode': 'list,form', # Odoo 18 ưu tiên dùng 'list' thay vì 'tree'
|
||||
'view_mode': 'list,form',
|
||||
'domain': [('id', 'in', self.rfq_ids.ids)],
|
||||
'context': {
|
||||
'default_request_ids': [(6, 0, [self.id])], # Tự động link ngược lại PR này nếu tạo mới RFQ
|
||||
'default_request_ids': [(6, 0, [self.id])], # Tự động link ngược lại PR này nếu tạo mới RFQ
|
||||
'create': True,
|
||||
},
|
||||
}
|
||||
@ -507,7 +507,7 @@ class EprPurchaseRequestLine(models.Model):
|
||||
final_vendor_id = fields.Many2one(
|
||||
comodel_name='res.partner',
|
||||
string='Final Vendor',
|
||||
domain="[('supplier_rank', '>', 0)]",
|
||||
# domain="[('supplier_rank', '>', 0)]",
|
||||
help="Nhà cung cấp chính thức được bộ phận Mua hàng chốt."
|
||||
)
|
||||
|
||||
|
||||
@ -59,17 +59,15 @@ class EprRfq(models.Model):
|
||||
column2='request_id',
|
||||
string='Source Requests',
|
||||
# Chỉ lấy các PR đã duyệt để tạo RFQ
|
||||
domain="[('state', '=', 'approved')]",
|
||||
readonly=True
|
||||
domain="[('state', '=', 'approved')]"
|
||||
)
|
||||
|
||||
partner_id = fields.Many2one(
|
||||
comodel_name='res.partner',
|
||||
string='Vendor',
|
||||
required=True,
|
||||
tracking=True,
|
||||
tracking=True
|
||||
# domain="[('supplier_rank', '>', 0)]", # Chỉ chọn đã từng được chọn qua ít nhất 01 lần
|
||||
readonly=True
|
||||
)
|
||||
|
||||
company_id = fields.Many2one(
|
||||
@ -105,8 +103,7 @@ class EprRfq(models.Model):
|
||||
comodel_name='epr.rfq.line',
|
||||
inverse_name='rfq_id',
|
||||
string='Products',
|
||||
copy=True,
|
||||
readonly=True
|
||||
copy=True
|
||||
)
|
||||
|
||||
# Link sang Purchase Order gốc của Odoo
|
||||
@ -121,12 +118,23 @@ class EprRfq(models.Model):
|
||||
string='PO Count'
|
||||
)
|
||||
|
||||
request_count = fields.Integer(
|
||||
compute='_compute_request_count',
|
||||
string='PR Count'
|
||||
)
|
||||
|
||||
# === 5. COMPUTE METHODS ===
|
||||
@api.depends('purchase_ids')
|
||||
def _compute_purchase_count(self):
|
||||
for rfq in self:
|
||||
rfq.purchase_count = len(rfq.purchase_ids)
|
||||
|
||||
# Link ngược về PR gốc
|
||||
@api.depends('request_ids')
|
||||
def _compute_request_count(self):
|
||||
for rfq in self:
|
||||
rfq.request_count = len(rfq.request_ids)
|
||||
|
||||
# === 6. CRUD OVERRIDES ===
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
@ -225,9 +233,37 @@ class EprRfq(models.Model):
|
||||
raise UserError(_("Chỉ có thể reset khi ở trạng thái Sent, To Approve hoặc Cancel."))
|
||||
rfq.write({'state': 'draft'})
|
||||
|
||||
# Mở danh sách các PO được tạo từ RFQ này
|
||||
def action_view_purchase_orders(self):
|
||||
"""Mở danh sách các Purchase Orders (PO) được tạo từ RFQ này"""
|
||||
self.ensure_one()
|
||||
return {
|
||||
'name': _('Purchase Orders'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'purchase.order',
|
||||
'view_mode': 'list,form',
|
||||
# Filter các PO có field epr_rfq_id khớp với ID hiện tại
|
||||
'domain': [('epr_rfq_id', '=', self.id)],
|
||||
'context': {'default_epr_rfq_id': self.id},
|
||||
'target': 'current',
|
||||
}
|
||||
|
||||
# Mở danh sách các PR gốc của RFQ này
|
||||
def action_view_source_requests(self):
|
||||
"""Mở danh sách các PR gốc của RFQ này"""
|
||||
self.ensure_one()
|
||||
return {
|
||||
'name': _('Source Purchase Requests'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'epr.purchase.request',
|
||||
'view_mode': 'list,form',
|
||||
'domain': [('id', 'in', self.request_ids.ids)],
|
||||
'target': 'current',
|
||||
}
|
||||
# -------------------------------------------------------------------------
|
||||
# RFQ APPROVAL PROCESS
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def action_submit_approval(self):
|
||||
"""Nút bấm Submit for Approval"""
|
||||
self.ensure_one()
|
||||
@ -333,8 +369,7 @@ class EprRfqLine(models.Model):
|
||||
# Sản phẩm (Odoo Product)
|
||||
product_id = fields.Many2one(
|
||||
comodel_name='product.product',
|
||||
string='Product',
|
||||
required=True
|
||||
string='Product'
|
||||
)
|
||||
|
||||
description = fields.Text(
|
||||
|
||||
@ -25,3 +25,7 @@ access_epr_create_po_wizard_officer,ePR Create PO Wizard Officer,model_epr_creat
|
||||
access_epr_create_po_wizard_admin,ePR Create PO Wizard Admin,model_epr_create_po_wizard,group_epr_admin,1,1,1,1
|
||||
access_epr_create_po_line_wizard_officer,ePR Create PO Line Wizard Officer,model_epr_create_po_line_wizard,group_epr_purchasing_officer,1,1,1,1
|
||||
access_epr_create_po_line_wizard_admin,ePR Create PO Line Wizard Admin,model_epr_create_po_line_wizard,group_epr_admin,1,1,1,1
|
||||
access_epr_create_rfq_wizard_officer,ePR Create RFQ Wizard Officer,model_epr_create_rfq_wizard,group_epr_purchasing_officer,1,1,1,1
|
||||
access_epr_create_rfq_wizard_admin,ePR Create RFQ Wizard Admin,model_epr_create_rfq_wizard,group_epr_admin,1,1,1,1
|
||||
access_epr_create_rfq_line_officer,ePR Create RFQ Line Officer,model_epr_create_rfq_line,group_epr_purchasing_officer,1,1,1,1
|
||||
access_epr_create_rfq_line_admin,ePR Create RFQ Line Admin,model_epr_create_rfq_line,group_epr_admin,1,1,1,1
|
||||
|
||||
|
@ -82,15 +82,25 @@
|
||||
|
||||
<sheet>
|
||||
<!-- SMART BUTTON AREA -->
|
||||
<!-- <div class="oe_button_box" name="button_box">
|
||||
<!-- Button POs: Hiển thị số lượng POs liên quan -->
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="action_view_purchase_orders"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-shopping-cart"
|
||||
invisible="purchase_count == 0">
|
||||
<field name="purchase_count" widget="statinfo" string="Purchase Orders"/>
|
||||
<field name="purchase_count" widget="statinfo" string="POs"/>
|
||||
</button>
|
||||
</div> -->
|
||||
|
||||
<!-- Button PRs: Hiển thị số lượng PRs liên quan -->
|
||||
<button name="action_view_source_requests"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-file-text-o"
|
||||
invisible="request_count == 0">
|
||||
<field name="request_count" widget="statinfo" string="PRs"/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- TITLE: Số phiếu RFQ -->
|
||||
<div class="oe_title">
|
||||
@ -105,7 +115,7 @@
|
||||
<group>
|
||||
<!-- Readonly logic: Chỉ cho sửa khi nháp -->
|
||||
<field name="partner_id" widget="res_partner_many2one" context="{'show_address': 1}" readonly="state != 'draft'"/>
|
||||
<field name="request_ids" widget="many2many_tags" options="{'color_field': 'color'}" readonly="state != 'draft'"/>
|
||||
<field name="request_ids" widget="many2many_tags" readonly="state != 'draft'"/>
|
||||
<field name="currency_id" groups="base.group_multi_currency" readonly="state != 'draft'"/>
|
||||
</group>
|
||||
<group>
|
||||
@ -160,6 +170,17 @@
|
||||
context="{'default_rfq_id': id}"/>
|
||||
</page>
|
||||
|
||||
<page string="Source Requests" name="source_requests">
|
||||
<field name="request_ids" readonly="1">
|
||||
<list>
|
||||
<field name="name"/>
|
||||
<field name="employee_id"/>
|
||||
<field name="date_required"/>
|
||||
<field name="state" widget="badge"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
|
||||
</notebook>
|
||||
</sheet>
|
||||
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
from . import epr_reject_wizard
|
||||
from . import epr_create_rfq
|
||||
from . import epr_create_po
|
||||
|
||||
Binary file not shown.
Binary file not shown.
BIN
addons/epr/wizards/__pycache__/epr_create_rfq.cpython-312.pyc
Normal file
BIN
addons/epr/wizards/__pycache__/epr_create_rfq.cpython-312.pyc
Normal file
Binary file not shown.
209
addons/epr/wizards/epr_create_rfq.py
Normal file
209
addons/epr/wizards/epr_create_rfq.py
Normal file
@ -0,0 +1,209 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models, fields, api, Command, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class EprCreateRfqWizard(models.TransientModel):
|
||||
_name = 'epr.create.rfq.wizard'
|
||||
_description = 'Wizard: Merge PRs to RFQ'
|
||||
|
||||
# Danh sách các dòng PR đang được xử lý trong Wizard
|
||||
line_ids = fields.One2many(
|
||||
comodel_name='epr.create.rfq.line',
|
||||
inverse_name='wizard_id',
|
||||
string='PR Lines to Process'
|
||||
)
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields_list):
|
||||
"""
|
||||
Khi mở Wizard, tự động load các PR đã chọn từ màn hình danh sách.
|
||||
"""
|
||||
res = super().default_get(fields_list)
|
||||
active_ids = self.env.context.get('active_ids', [])
|
||||
|
||||
if not active_ids:
|
||||
return res
|
||||
|
||||
# Lấy danh sách PR gốc
|
||||
requests = self.env['epr.purchase.request'].browse(active_ids)
|
||||
|
||||
# Prepare dữ liệu cho dòng Wizard
|
||||
lines_vals = []
|
||||
for pr in requests:
|
||||
# Chỉ xử lý các PR đã được duyệt (Ví dụ trạng thái 'approved')
|
||||
# Bạn có thể bỏ comment dòng dưới nếu có field state
|
||||
if pr.state != 'approved':
|
||||
raise UserError(_("PR %s chưa được duyệt.", pr.name))
|
||||
|
||||
for line in pr.line_ids:
|
||||
lines_vals.append(Command.create({
|
||||
'pr_line_id': line.id, # Link tới PR Line
|
||||
'request_id': pr.id,
|
||||
'final_vendor_id': line.final_vendor_id.id if line.final_vendor_id else False,
|
||||
'suggested_vendor_name': line.suggested_vendor_name,
|
||||
}))
|
||||
|
||||
res['line_ids'] = lines_vals
|
||||
return res
|
||||
|
||||
def action_create_rfqs(self):
|
||||
"""
|
||||
Logic hardened with .sudo():
|
||||
1. Access PR data using sudo to bypass security rules.
|
||||
2. Group by Vendor.
|
||||
3. Create RFQ Header and Lines using sudo() to ensure data persistence.
|
||||
4. Redirect to the newly created RFQ(s).
|
||||
"""
|
||||
self.ensure_one()
|
||||
|
||||
# 1. Validation
|
||||
missing_vendor_lines = self.line_ids.filtered(lambda l: not l.final_vendor_id)
|
||||
if missing_vendor_lines:
|
||||
raise UserError(_("Please select a Final Vendor for all lines."))
|
||||
|
||||
missing_product_lines = self.line_ids.filtered(lambda l: not l.final_product_id)
|
||||
if missing_product_lines:
|
||||
raise UserError(_("Please select a Final Product for all lines."))
|
||||
|
||||
# 2. Grouping
|
||||
grouped_lines = {}
|
||||
for wiz_line in self.line_ids:
|
||||
# Sync back to PR line (Sudo to ensure write permission if needed)
|
||||
if wiz_line.pr_line_id.final_vendor_id != wiz_line.final_vendor_id:
|
||||
wiz_line.pr_line_id.sudo().write({'final_vendor_id': wiz_line.final_vendor_id.id})
|
||||
|
||||
vendor = wiz_line.final_vendor_id
|
||||
if vendor not in grouped_lines:
|
||||
grouped_lines[vendor] = self.env['epr.create.rfq.line']
|
||||
grouped_lines[vendor] |= wiz_line
|
||||
|
||||
created_rfqs = self.env['epr.rfq'].sudo()
|
||||
|
||||
# 3. RFQ Creation
|
||||
for vendor, wiz_lines in grouped_lines.items():
|
||||
rfq_line_commands = []
|
||||
source_pr_ids = []
|
||||
|
||||
for wiz_line in wiz_lines:
|
||||
# DÙNG SUDO: Để chắc chắn lấy được dữ liệu PR line
|
||||
pr_line_sudo = wiz_line.pr_line_id.sudo()
|
||||
request_sudo = wiz_line.request_id.sudo()
|
||||
|
||||
# Collect unique source PR ids
|
||||
if request_sudo and request_sudo.id not in source_pr_ids:
|
||||
source_pr_ids.append(request_sudo.id)
|
||||
|
||||
# Determine UoM (Directly from Product or PR Line)
|
||||
uom_id = False
|
||||
if wiz_line.final_product_id:
|
||||
uom_id = wiz_line.final_product_id.uom_po_id.id or wiz_line.final_product_id.uom_id.id
|
||||
|
||||
if not uom_id and pr_line_sudo.product_id:
|
||||
uom_id = pr_line_sudo.product_id.uom_po_id.id or pr_line_sudo.product_id.uom_id.id
|
||||
|
||||
# Create line command - DÙNG GIÁ TRỊ TỪ SUDO RECORD
|
||||
rfq_line_commands.append(Command.create({
|
||||
'product_id': wiz_line.final_product_id.id,
|
||||
'description': pr_line_sudo.name or '',
|
||||
'quantity': pr_line_sudo.quantity or 1.0,
|
||||
'uom_id': uom_id,
|
||||
'price_unit': 0.0,
|
||||
}))
|
||||
|
||||
# Create RFQ header - SỬ DỤNG SUDO ĐỂ TẠO
|
||||
rfq = self.env['epr.rfq'].sudo().create({
|
||||
'partner_id': vendor.id,
|
||||
'state': 'draft',
|
||||
'date_order': fields.Datetime.now(),
|
||||
'request_ids': [Command.set(source_pr_ids)],
|
||||
'line_ids': rfq_line_commands,
|
||||
})
|
||||
created_rfqs |= rfq
|
||||
|
||||
# 4. Result Action
|
||||
if len(created_rfqs) == 1:
|
||||
return {
|
||||
'name': _('Request for Quotation'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'epr.rfq',
|
||||
'view_mode': 'form',
|
||||
'res_id': created_rfqs.id,
|
||||
'target': 'current',
|
||||
}
|
||||
else:
|
||||
return {
|
||||
'name': _('Requests for Quotation'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'epr.rfq',
|
||||
'view_mode': 'list,form',
|
||||
'domain': [('id', 'in', created_rfqs.ids)],
|
||||
'target': 'current',
|
||||
}
|
||||
|
||||
|
||||
class EprCreateRfqLine(models.TransientModel):
|
||||
_name = 'epr.create.rfq.line'
|
||||
_description = 'Wizard Line: PR Details'
|
||||
|
||||
wizard_id = fields.Many2one('epr.create.rfq.wizard', string='Wizard')
|
||||
|
||||
# Link tới PR gốc (Readonly)
|
||||
request_id = fields.Many2one(
|
||||
comodel_name='epr.purchase.request',
|
||||
string='Purchase Request',
|
||||
readonly=True
|
||||
)
|
||||
|
||||
# Link tới PR Line gốc
|
||||
pr_line_id = fields.Many2one(
|
||||
comodel_name='epr.purchase.request.line',
|
||||
string='PR Line',
|
||||
readonly=True
|
||||
)
|
||||
|
||||
# Cột hiển thị text gợi ý (Readonly) -> Giúp Officer tham chiếu
|
||||
suggested_vendor_name = fields.Char(
|
||||
string='Suggested Vendor (Text)',
|
||||
readonly=True,
|
||||
help="Tên nhà cung cấp do người yêu cầu nhập tay (tham khảo)."
|
||||
)
|
||||
|
||||
# Cột Final Vendor (Editable) -> Đây là nơi Officer thao tác chính
|
||||
final_vendor_id = fields.Many2one(
|
||||
comodel_name='res.partner',
|
||||
string='Final Vendor',
|
||||
required=True,
|
||||
# domain="[('supplier_rank', '>', 0)]", # Chỉ lấy nhà cung cấp
|
||||
help="Chọn nhà cung cấp chính thức trong hệ thống để tạo RFQ."
|
||||
)
|
||||
|
||||
# Lấy thông tin sản phẩm cần mua
|
||||
product_description = fields.Char(
|
||||
related='pr_line_id.name',
|
||||
string='Product / Description',
|
||||
readonly=True
|
||||
)
|
||||
|
||||
# Purchasing Officer chọn sản phẩm tương ứng với mô tả của User
|
||||
final_product_id = fields.Many2one(
|
||||
comodel_name='product.product',
|
||||
string='Final Product',
|
||||
required=True,
|
||||
# domain="[('purchase_ok', '=', True)]",
|
||||
help="Chọn sản phẩm tương ứng với mô tả của người yêu cầu."
|
||||
)
|
||||
|
||||
# Lấy số lượng
|
||||
quantity = fields.Float(
|
||||
related='pr_line_id.quantity',
|
||||
string='Qty',
|
||||
readonly=True
|
||||
)
|
||||
|
||||
# Lấy đơn vị tính
|
||||
uom_name = fields.Char(
|
||||
related='pr_line_id.uom_name',
|
||||
string='UoM',
|
||||
readonly=True
|
||||
)
|
||||
91
addons/epr/wizards/epr_create_rfq_views.xml
Normal file
91
addons/epr/wizards/epr_create_rfq_views.xml
Normal file
@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- ============================================================ -->
|
||||
<!-- WIZARD FORM VIEW: Create RFQ from PRs -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="view_epr_create_rfq_wizard_form" model="ir.ui.view">
|
||||
<field name="name">epr.create.rfq.wizard.form</field>
|
||||
<field name="model">epr.create.rfq.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Create RFQ from Purchase Requests">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<h2>Batch Create RFQs</h2>
|
||||
<p class="text-muted">
|
||||
Review lines below. Assign a <b>Final Vendor</b> to group them.
|
||||
Lines with the same Final Vendor will be merged into one RFQ.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<field name="line_ids" nolabel="1">
|
||||
<list editable="bottom" create="0" delete="0"
|
||||
decoration-danger="not final_vendor_id">
|
||||
<!-- PR Reference (readonly) -->
|
||||
<field name="request_id" string="PR Ref"/>
|
||||
|
||||
<!-- Product Description -->
|
||||
<field name="product_description" readonly="1"/>
|
||||
|
||||
<!-- Final Product (editable - main action field) -->
|
||||
<field name="final_product_id"
|
||||
string="Final Product"
|
||||
options="{'no_create': True}"
|
||||
placeholder="Select Product to Group..."
|
||||
decoration-bf="final_product_id"/>
|
||||
|
||||
<!-- Quantity -->
|
||||
<field name="quantity"/>
|
||||
|
||||
<!-- UOM -->
|
||||
<field name="uom_name" optional="show"/>
|
||||
|
||||
<!-- Suggested Vendor Text (readonly - for reference) -->
|
||||
<field name="suggested_vendor_name"
|
||||
string="Suggested Vendor"
|
||||
decoration-muted="1"
|
||||
optional="show"/>
|
||||
|
||||
<!-- Final Vendor (editable - main action field) -->
|
||||
<field name="final_vendor_id"
|
||||
string="Final Vendor"
|
||||
options="{'no_create': True}"
|
||||
placeholder="Select Vendor to Group..."
|
||||
decoration-bf="final_vendor_id"/>
|
||||
|
||||
<!-- Hidden fields -->
|
||||
<field name="pr_line_id" column_invisible="True"/>
|
||||
</list>
|
||||
</field>
|
||||
</sheet>
|
||||
|
||||
<footer>
|
||||
<button name="action_create_rfqs"
|
||||
string="Create RFQ(s)"
|
||||
type="object"
|
||||
class="btn-primary"
|
||||
data-hotkey="q"/>
|
||||
<button string="Cancel"
|
||||
class="btn-secondary"
|
||||
special="cancel"
|
||||
data-hotkey="z"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- ACTION: Binding to PR List View -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="action_epr_pr_create_rfq" model="ir.actions.act_window">
|
||||
<field name="name">Create RFQ</field>
|
||||
<field name="res_model">epr.create.rfq.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
<!-- Binding to epr.purchase.request list view -->
|
||||
<field name="binding_model_id" ref="model_epr_purchase_request"/>
|
||||
<field name="binding_view_types">list</field>
|
||||
<!-- Only Purchasing Officer and Admin can see this action -->
|
||||
<field name="groups_id" eval="[(4, ref('epr.group_epr_purchasing_officer')), (4, ref('epr.group_epr_admin'))]"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@ -1,4 +1,4 @@
|
||||
version: '3.1'
|
||||
# version: '3.1'
|
||||
services:
|
||||
db:
|
||||
image: postgres:17
|
||||
@ -32,4 +32,4 @@ services:
|
||||
- ./addons:/mnt/extra-addons
|
||||
- ./etc:/etc/odoo
|
||||
restart: always # run as a service
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user