From 3dfcb3d5613a69cd34076f51da382b5991cee076 Mon Sep 17 00:00:00 2001
From: adam-james <21064735+adam-james-v@users.noreply.github.com>
Date: Fri, 27 Sep 2024 16:38:53 +0200
Subject: [PATCH] Pivot Options are Properly Calculated for Questions based on
 Models with Joins (#47830)

Fixes 46575

Creating a Pivot Table Question that is based off of a model that has at least one column derived from a join failed
to display row totals.

This is because the pivot-options map was being mis-calculated; not all column indices were correctly found/passed in
to the :pivot-rows or :pivot-cols keys, causing the pivot query not to compute all necessary data.

Here, I just modify the :lib/source key of the columns whose source is a card (as determined by the existence of
:lib/card-id). The columns being checked will all have :source/breakout, which caused, in the issue's repro example,
the "NAME" column to be missed. If it instead has :lib/source :source/card, the logic inside
`lib/find-matching-column` works.
---
 src/metabase/query_processor/pivot.clj |  7 +++-
 test/metabase/api/card_test.clj        | 46 ++++++++++++++++++++++++++
 2 files changed, 52 insertions(+), 1 deletion(-)

diff --git a/src/metabase/query_processor/pivot.clj b/src/metabase/query_processor/pivot.clj
index fcc51208709..3e0d6bbafd8 100644
--- a/src/metabase/query_processor/pivot.clj
+++ b/src/metabase/query_processor/pivot.clj
@@ -359,7 +359,12 @@
                                      mlv2-query        (lib/query metadata-provider query)
                                      breakouts         (into []
                                                              (map-indexed (fn [i col]
-                                                                            (assoc col ::i i)))
+                                                                            (cond-> col
+                                                                              true                         (assoc ::i i)
+                                                                              ;; if the col has a card-id, we swap the :lib/source to say source/card
+                                                                              ;; this allows `lib/find-matching-column` to properly match a column that has a join-alias
+                                                                              ;; but whose source is a model
+                                                                              (contains? col :lib/card-id) (assoc :lib/source :source/card))))
                                                              (lib/breakouts-metadata mlv2-query))]
                                  (fn [legacy-ref]
                                    (try
diff --git a/test/metabase/api/card_test.clj b/test/metabase/api/card_test.clj
index 91e6c1bd0d6..21d043a8292 100644
--- a/test/metabase/api/card_test.clj
+++ b/test/metabase/api/card_test.clj
@@ -3847,3 +3847,49 @@
                                        false
                                        (apply original-can-read? args)))]
           (is (map? (mt/user-http-request :crowberto :get 200 (format "card/%d/query_metadata" (:id card))))))))))
+
+(deftest pivot-tables-with-model-sources-show-row-totals
+  (testing "Pivot Tables with a model source will return row totals (#46575)"
+    (mt/with-temp [:model/Card {model-id :id} {:type :model
+                                               :dataset_query
+                                               {:database (mt/id)
+                                                :type     :query
+                                                :query
+                                                {:source-table (mt/id :orders)
+                                                 :joins
+                                                 [{:fields       :all
+                                                   :strategy     :left-join
+                                                   :alias        "People - User"
+                                                   :condition
+                                                   [:=
+                                                    [:field (mt/id :orders :user_id) {:base-type :type/Integer}]
+                                                    [:field (mt/id :people :id) {:base-type :type/BigInteger :join-alias "People - User"}]]
+                                                   :source-table (mt/id :people)}]}}}
+                   :model/Card {pivot-id :id} {:display :pivot
+                                               :dataset_query
+                                               {:database (mt/id)
+                                                :type     :query
+                                                :query
+                                                {:aggregation  [[:sum [:field "TOTAL" {:base-type :type/Float}]]]
+                                                 :breakout
+                                                 [[:field "CREATED_AT" {:base-type :type/DateTime, :temporal-unit :month}]
+                                                  [:field "NAME" {:base-type :type/Text}]
+                                                  [:field (mt/id :products :category) {:base-type    :type/Text
+                                                                                       :source-field (mt/id :orders :product_id)}]]
+                                                 :source-table (format "card__%s" model-id)}}
+                                               :visualization_settings
+                                               {:pivot_table.column_split
+                                                {:rows
+                                                 [[:field "NAME" {:base-type :type/Text}]
+                                                  [:field "CREATED_AT" {:base-type :type/DateTime, :temporal-unit :month}]]
+                                                 :columns [[:field (mt/id :products :category) {:base-type    :type/Text
+                                                                                                :source-field (mt/id :orders :product_id)}]]
+                                                 :values  [[:aggregation 0]]}}}]
+      ;; pivot row totals have a pivot-grouping of 1 (the second-last column in these results)
+      ;; before fixing issue #46575, these rows would not be returned given the model + card setup
+      (is (= [nil "Abbey Satterfield" "Doohickey" 1 347.91]
+             (let [result (mt/user-http-request :rasta :post 202 (format "card/pivot/%d/query" pivot-id))
+                   totals (filter (fn [row]
+                                    (< 0 (second (reverse row))))
+                                  (get-in result [:data :rows]))]
+               (first totals)))))))
-- 
GitLab