From ebab33f7a90e2afc21497cec9626b5d5b24d852b Mon Sep 17 00:00:00 2001 From: "admin.suherdy" Date: Wed, 3 Dec 2025 08:48:28 +0700 Subject: [PATCH] feat: Display a customer's top 3 purchased products in the POS partner editor. --- .../__pycache__/res_partner.cpython-312.pyc | Bin 1668 -> 2582 bytes models/res_partner.py | 19 ++++++++++++++++++ static/src/js/partner_editor.js | 14 ++++++++----- static/src/xml/partner_editor.xml | 18 +++++++++++++++++ 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/models/__pycache__/res_partner.cpython-312.pyc b/models/__pycache__/res_partner.cpython-312.pyc index d55d443e14d636593514c2ea99eb633ceb675509..e4559c25787800d36753ab27137925d18b1ca37f 100644 GIT binary patch delta 1055 zcmb7D&rcIU6rSDgw#$!FNr@P#vL2u=Vu>*vG{8Yv1yU$!A&QsHrX94+(k`<@0)it@b~tk89Zv1yL# zh6a_3c|RVTk%}8f4ltzPNzkfP5HT#DInH*!a5phK6lspP$B%Nx=m;pys z^H}pLn`T{Gckigpf2i}$h!iiIhEBm7we@DCE~fx!&K~sdJswV~ag{kUbwaZ+p3UpC zTs9xXV+(Wgy~X+|yTDjJKB2}L+BiOWLQRajNYSa8sd@Q!qo)Yu5?pqLPm+ocui@k> z#g+C{I!Tu>O+M8PL-CLvU=WGX#FbS{)-Cp-!l{v~tk?AfQ;l6qp5{!gHmmilH;%12 zXO>8v(M-4kT5ANt7OHyNJKJB2)yt^GUz7?`v8~W{EcF~oJs;HF>D|$9(yfy~u((!O zD{d4vjsxMNK=^AwIh2%h(4$#Jsz{oaE$@kUTSy;tJ|WUyJ{7y~3`4PvP6DB=$?u}{ zYVpP58Q&%Lo_mltP-R(l5P4gi{snQs?w|3HmAkzI3BSTQa f4}mOpc2y6@ai=KsL%6{Wyt%j&DusR`NNweBVGsQA delta 155 zcmbOx(!$GknwOW00SM}s>1Eb1PvmQ3G?_Tnl%n(UJWIqhXuGJFOp zE0O~dY%qcu$S9VbT*xWS`jJV5RethHPAz9ukgOVr04e^(VUwGmQks)$SELE#G6He2 g1d#Z^%*e=ipF!peiv*+m1hMHd6J@?K0GVK&0D1Eu>;M1& diff --git a/models/res_partner.py b/models/res_partner.py index 331ebae..748ae32 100644 --- a/models/res_partner.py +++ b/models/res_partner.py @@ -25,3 +25,22 @@ class ResPartner(models.Model): 'price_subtotal_incl': line.price_subtotal_incl, } for line in order.lines], } for order in orders] + + @api.model + def get_pos_top_products(self, partner_id): + # SQL query to get top 3 products by quantity for a specific partner + query = """ + SELECT + pt.name ->> 'en_US' as product_name, + SUM(pol.qty) as total_qty + FROM pos_order_line pol + JOIN pos_order po ON pol.order_id = po.id + JOIN product_product pp ON pol.product_id = pp.id + JOIN product_template pt ON pp.product_tmpl_id = pt.id + WHERE po.partner_id = %s + GROUP BY pt.name + ORDER BY total_qty DESC + LIMIT 3 + """ + self.env.cr.execute(query, (partner_id,)) + return self.env.cr.dictfetchall() diff --git a/static/src/js/partner_editor.js b/static/src/js/partner_editor.js index 4b8fffb..0d602ca 100644 --- a/static/src/js/partner_editor.js +++ b/static/src/js/partner_editor.js @@ -9,21 +9,25 @@ patch(PartnerDetailsEdit.prototype, { setup() { super.setup(); this.orm = useService("orm"); - this.state = useState({ ...this.state, lastOrders: [] }); + this.state = useState({ ...this.state, lastOrders: [], topProducts: [] }); onWillStart(async () => { - await this.fetchLastOrders(); + await this.fetchData(); }); }, - async fetchLastOrders() { + async fetchData() { const partnerId = this.props.partner.id; if (partnerId) { try { - const orders = await this.orm.call("res.partner", "get_pos_last_orders", [partnerId]); + const [orders, topProducts] = await Promise.all([ + this.orm.call("res.partner", "get_pos_last_orders", [partnerId]), + this.orm.call("res.partner", "get_pos_top_products", [partnerId]) + ]); this.state.lastOrders = orders; + this.state.topProducts = topProducts; } catch (error) { - console.error("Error fetching orders:", error); + console.error("Error fetching data:", error); } } } diff --git a/static/src/xml/partner_editor.xml b/static/src/xml/partner_editor.xml index ffb8527..8ab53e6 100644 --- a/static/src/xml/partner_editor.xml +++ b/static/src/xml/partner_editor.xml @@ -5,6 +5,24 @@
+

Top 3 Products

+ +
+ +
+ + + + Units + +
+
+
+
+ +
No top products found.
+
+

Last Orders