diff --git a/addons/epr/__manifest__.py b/addons/epr/__manifest__.py index ea7d2bd..58be4a8 100644 --- a/addons/epr/__manifest__.py +++ b/addons/epr/__manifest__.py @@ -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': { diff --git a/addons/epr/data/import_partners.csv b/addons/epr/data/import_partners.csv new file mode 100644 index 0000000..b240352 --- /dev/null +++ b/addons/epr/data/import_partners.csv @@ -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 diff --git a/addons/epr/data/import_purchase_request_lines.csv b/addons/epr/data/import_purchase_request_lines.csv new file mode 100644 index 0000000..79c6b45 --- /dev/null +++ b/addons/epr/data/import_purchase_request_lines.csv @@ -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 diff --git a/addons/epr/data/import_purchase_requests.csv b/addons/epr/data/import_purchase_requests.csv new file mode 100644 index 0000000..4f52d9a --- /dev/null +++ b/addons/epr/data/import_purchase_requests.csv @@ -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 diff --git a/addons/epr/models/__pycache__/epr_purchase_request.cpython-312.pyc b/addons/epr/models/__pycache__/epr_purchase_request.cpython-312.pyc index a3d9732..156411f 100644 Binary files a/addons/epr/models/__pycache__/epr_purchase_request.cpython-312.pyc and b/addons/epr/models/__pycache__/epr_purchase_request.cpython-312.pyc differ diff --git a/addons/epr/models/__pycache__/epr_rfq.cpython-312.pyc b/addons/epr/models/__pycache__/epr_rfq.cpython-312.pyc index 8c16d87..543598a 100644 Binary files a/addons/epr/models/__pycache__/epr_rfq.cpython-312.pyc and b/addons/epr/models/__pycache__/epr_rfq.cpython-312.pyc differ diff --git a/addons/epr/models/epr_purchase_request.py b/addons/epr/models/epr_purchase_request.py index 3bcca60..40490d2 100644 --- a/addons/epr/models/epr_purchase_request.py +++ b/addons/epr/models/epr_purchase_request.py @@ -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." ) diff --git a/addons/epr/models/epr_rfq.py b/addons/epr/models/epr_rfq.py index ddf3436..7ab7abd 100644 --- a/addons/epr/models/epr_rfq.py +++ b/addons/epr/models/epr_rfq.py @@ -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( diff --git a/addons/epr/security/ir.model.access.csv b/addons/epr/security/ir.model.access.csv index ee07cf3..d2023f0 100644 --- a/addons/epr/security/ir.model.access.csv +++ b/addons/epr/security/ir.model.access.csv @@ -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 diff --git a/addons/epr/views/epr_rfq_views.xml b/addons/epr/views/epr_rfq_views.xml index b82f9ba..0cc2713 100644 --- a/addons/epr/views/epr_rfq_views.xml +++ b/addons/epr/views/epr_rfq_views.xml @@ -82,15 +82,25 @@ - +
-
--> + + + +
@@ -105,7 +115,7 @@ - + @@ -160,6 +170,17 @@ context="{'default_rfq_id': id}"/> + + + + + + + + + + + diff --git a/addons/epr/wizards/__init__.py b/addons/epr/wizards/__init__.py index 0f3b969..cc0fe26 100644 --- a/addons/epr/wizards/__init__.py +++ b/addons/epr/wizards/__init__.py @@ -1,2 +1,3 @@ from . import epr_reject_wizard +from . import epr_create_rfq from . import epr_create_po diff --git a/addons/epr/wizards/__pycache__/__init__.cpython-312.pyc b/addons/epr/wizards/__pycache__/__init__.cpython-312.pyc index e198c2d..f72fdf0 100644 Binary files a/addons/epr/wizards/__pycache__/__init__.cpython-312.pyc and b/addons/epr/wizards/__pycache__/__init__.cpython-312.pyc differ diff --git a/addons/epr/wizards/__pycache__/epr_create_po.cpython-312.pyc b/addons/epr/wizards/__pycache__/epr_create_po.cpython-312.pyc index 1f6f8fc..7b7c078 100644 Binary files a/addons/epr/wizards/__pycache__/epr_create_po.cpython-312.pyc and b/addons/epr/wizards/__pycache__/epr_create_po.cpython-312.pyc differ diff --git a/addons/epr/wizards/__pycache__/epr_create_rfq.cpython-312.pyc b/addons/epr/wizards/__pycache__/epr_create_rfq.cpython-312.pyc new file mode 100644 index 0000000..fc34cdc Binary files /dev/null and b/addons/epr/wizards/__pycache__/epr_create_rfq.cpython-312.pyc differ diff --git a/addons/epr/wizards/epr_create_rfq.py b/addons/epr/wizards/epr_create_rfq.py new file mode 100644 index 0000000..c628d71 --- /dev/null +++ b/addons/epr/wizards/epr_create_rfq.py @@ -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 + ) diff --git a/addons/epr/wizards/epr_create_rfq_views.xml b/addons/epr/wizards/epr_create_rfq_views.xml new file mode 100644 index 0000000..32dc1aa --- /dev/null +++ b/addons/epr/wizards/epr_create_rfq_views.xml @@ -0,0 +1,91 @@ + + + + + + + epr.create.rfq.wizard.form + epr.create.rfq.wizard + +
+ +
+

Batch Create RFQs

+

+ Review lines below. Assign a Final Vendor to group them. + Lines with the same Final Vendor will be merged into one RFQ. +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ + + + + + Create RFQ + epr.create.rfq.wizard + form + new + + + list + + + + +
diff --git a/docker-compose.yml b/docker-compose.yml index c9f2097..0af8e3f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 - \ No newline at end of file +