From 7c3605508df5bef7099bd4462ac215e5424a136e Mon Sep 17 00:00:00 2001 From: mtpc4s9 Date: Sat, 6 Dec 2025 12:09:51 +0700 Subject: [PATCH] =?UTF-8?q?Th=C3=AAm=2003=20tr=C6=B0=E1=BB=9Dng=20nh?= =?UTF-8?q?=E1=BA=ADp=20li=E1=BB=87u=20Vendors,=20chu=E1=BA=A9n=20b?= =?UTF-8?q?=E1=BB=8B=20cho=20vi=E1=BB=87c=20g=E1=BB=99p=20nhi=E1=BB=81u=20?= =?UTF-8?q?PR=20v=C3=A0o=20RFQs=20sau=20n=C3=A0y?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tôi ưu UX Thêm group Rejection Điều chỉnh lại login nhận dạng Line Manager --- .../epr_purchase_request.cpython-312.pyc | Bin 13420 -> 16045 bytes addons/epr/models/epr_purchase_request.py | 150 +++++++++++------- addons/epr/security/epr_record_rules.xml | 5 +- .../epr/views/epr_purchase_request_views.xml | 145 +++++++++++++++-- 4 files changed, 231 insertions(+), 69 deletions(-) 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 d35b642b724e053518242f315f795661e9266629..c596ccf7d25bb10af9913b591118a8bcf894e079 100644 GIT binary patch delta 4671 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 Rjqba6_`>1u1pdO0`Y%hK0`C9- delta 2492 zcmaJ>Z%kX)6@S;p&&D>`V4K+Z59XhZF|omb+t4JymOy9~k~Pbk(rizT?+Y~r+q;G| zZs=egZE2e-NsqcFYZcmlSsAXP$y2vQs;2JCCQaKynFe~w)a}dGNz>*LHfhp6?4DUbI$Lcd+xdC!{`6@bnUNdYK#IO-s$dC`{=dW&rJqg@^LS#1w#HneDG~z zt9DR5)yw)RAN06w(4w&|6dKm5%9ay#8iA|Y%Bu%)QlR_ZC)7z@R9|SU*xUY(y_?&6 zs27i;hJtTR+&#mt`gB&<3hhd6dE3m3`6{!>C|xOhTW(!e-It1tztGlDf(n)CObDP!<^~es!_gE%$naK z;&CoL=Yk&^ZQ!pJ$;%L|eYW_9>NY1?INfGo2D$yiLSjsGgFvUU9@F-lKFKbFd@f)7!2^oXC z-d>Z+s``8r8hwuw3(WYwV#EzBz{}d0_%q_^<$Nrnbe_X_$W%U6)!k1M zX0sfG4gUbUrr2K|N2Y z`l@_!mMtO+mXu;Rk(SG9d}qaQJd>7Z6D-f3L=*L8(kNvR&ck~_ZvyK~(Nj4qr)iEI zK}7;#8i66qBfNmH#6hW%q~vrWmrKgi(~=Y->?GgK@x%#3%!*dn3RS>2&5grw$M=X8 zb~;kkJ6=nTxOlV_gH-4aTqROxtIPh2vq9&baO2>wh03&>n6j zvEo>`gTTG8-QlL5f_LwuPFVQ|7N=V91K0Q{HZqYm7h2YI@aL}Px>m|tRUuGm3wHQ> zm&+Zb9o(voh61X$sS>J$}0Xfkxm|ZDMHSj;!H*`JmAUEE@COp&l?t zKICtBJF+4k;0A0Iq^O4+g!!mMM&WMsS)(G-%uFJc=BkNqHyMHp-A{1UZ@TAUBle8i z0^pVV+LcEW$0XT7UttF%}D}AeWNx<@=&86)LkSwKjD%Sy4`j- zpto7)1qpw+I_8BbBc?)6p6TG!Zm8AoYclU?@6X&vx3_{idxIwDK~wm=#Q~qijSG1p zFR%i);NSEXpOosk%#4)H@D9eta4q6GMHK_i>BNkz-b<;?%_h<`!KjpFsidr!X)2eU zPUOobwcJxJNxxc>ie1X2lTRnolQK6~Z_Fv53m?sbHDX_ge3AA~*a|Ljo<-r07TcAD zR~NR-^(CX=@~&8JST>zK>&~7{XMEim|I~T(0LA zBd?E@w1U;e>UjX;Avb%G&+H|HmpLeUNursgB(Y`OdIbSrhFwAU5(2gZHKFV>N<{=U zpH;_ZXQma~UK*&JgUgQ?&MBq~ya8mGnmjn5;cK$D5kBCc81@p& zKH|$&L5;ZF2X3Mf!VISJ1u!4{t*chB`K|`u3SSFvSv*@c=XP{jo4EK`$q4a7ueh&v zZRnc*sj1iX-_r`BUzN&)H{UpXOjHR!hVjGBqn~hE{DQe&4kaxi', 0), + '|', ('company_id', '=', False), + ('company_id', '=', self.env.company.id) + ], + help="Chọn nhà cung cấp có sẵn trong hệ thống." + ) + + # User nhập text tự do (Dùng khi không tìm thấy hoặc đề xuất mới) + suggested_vendor_name = fields.Char( + string='Suggested Vendor Name', help="Tên nhà cung cấp được đề xuất bởi người yêu cầu (tham khảo)." ) + # === 2. PURCHASING ONLY FIELDS === + # Purchasing chốt Vendor cuối cùng để làm RFQ + final_vendor_id = fields.Many2one( + comodel_name='res.partner', + string='Final Vendor', + domain="[('supplier_rank', '>', 0)]", + help="Nhà cung cấp chính thức được bộ phận Mua hàng chốt." + ) + quantity = fields.Float( string='Quantity', default=1.0, - digits='Product Unit of Measure', # Sử dụng độ chính xác cấu hình trong hệ thống + digits='Product Unit of Measure', # Sử dụng độ chính xác cấu hình trong hệ thống required=True, help="Số lượng cần mua." ) @@ -475,26 +507,30 @@ class EprPurchaseRequestLine(models.Model): price = line.estimated_price or 0.0 line.subtotal_estimated = qty * price - # === logic Onchange === - # Chỉ chạy khi Purchasing Staff chọn product_id (lúc xử lý phiếu) + # ========================================================================== + # ONCHANGE FIELDS + # ========================================================================== + @api.onchange('user_vendor_id') + def _onchange_user_vendor_id(self): + """ + UX Logic: + 1. Nếu User chọn Vendor ID -> Tự động điền text & set Final Vendor. + 2. Nếu User bỏ chọn Vendor ID -> Xóa Final Vendor để Purchasing xử lý lại. + """ + if self.user_vendor_id: + # User đã chọn vendor từ danh bạ + self.suggested_vendor_name = self.user_vendor_id.name + self.final_vendor_id = self.user_vendor_id + else: + # User bỏ chọn vendor + self.final_vendor_id = False + # suggested_vendor_name giữ nguyên để User có thể nhập thủ công - @api.onchange('product_id') - def _onchange_product_id(self): - if not self.product_id: - return - - # Đơn vị tính (UoM): NÊN ghi đè theo chuẩn hệ thống - # Lý do: Staff có thể nhập "Cái", nhưng hệ thống kho quản lý là "Unit(s)". - # Để tạo PO chính xác sau này, ta cần lấy UoM chuẩn của sản phẩm. - self.uom_name = self.product_id.uom_po_id.name or self.product_id.uom_id.name - - # Giá dự kiến: Chỉ điền nếu Staff để bằng 0 - if self.estimated_price == 0.0: - self.estimated_price = self.product_id.standard_price - - # Tên sản phẩm: KHÔNG ghi đè (Giữ nguyên mô tả của Staff) - # Vì Staff mô tả nhu cầu thực tế (VD: "Máy tính Dell cho kế toán"), - # còn tên Product hệ thống có thể chung chung (VD: "Laptop Dell Latitude"). - # Ta chỉ điền nếu dòng này do Purchasing tạo mới hoàn toàn (name đang rỗng). - if not self.name: - self.name = self.product_id.display_name + @api.constrains('user_vendor_id', 'suggested_vendor_name') + def _check_vendor_presence(self): + """ + Data Integrity: Bắt buộc phải có ít nhất 1 thông tin về nhà cung cấp. + """ + for line in self: + if not line.user_vendor_id and not line.suggested_vendor_name: + raise ValidationError(_("Dòng sản phẩm '%s': Vui lòng chọn Nhà cung cấp hoặc nhập tên đề xuất.", line.name)) diff --git a/addons/epr/security/epr_record_rules.xml b/addons/epr/security/epr_record_rules.xml index 4dbe231..1684470 100644 --- a/addons/epr/security/epr_record_rules.xml +++ b/addons/epr/security/epr_record_rules.xml @@ -23,10 +23,11 @@ ePR: Manager sees department requests - ['|', '|', + ['|', '|', '|', ('employee_id.user_id','=',user.id), ('approver_ids', 'in', user.id), - ('department_id.manager_id.user_id', '=', user.id) + ('department_id.manager_id.user_id', '=', user.id), + ('employee_id.parent_id.user_id', '=', user.id) ] diff --git a/addons/epr/views/epr_purchase_request_views.xml b/addons/epr/views/epr_purchase_request_views.xml index 25058f2..67f3a4a 100644 --- a/addons/epr/views/epr_purchase_request_views.xml +++ b/addons/epr/views/epr_purchase_request_views.xml @@ -36,6 +36,101 @@ + + + epr.purchase.request.kanban + epr.purchase.request + + + + + + + + + + + + + + + + + + + + + + +

+ + +
+
+ + + + + + +
+ + +
+ + +
+
+ + + + +
+ +
+ +
+
+ + +
+
+ + +
+
+ +
+
+
+ + + + + + + + +