This is an automated email from the ASF dual-hosted git repository.

msyavuz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 02cb1f1a28a fix(table): resolve auto-detected currency on time 
comparison columns (#44649)
02cb1f1a28a is described below

commit 02cb1f1a28a55827256c520a0c99fe6a6b276bef
Author: Mehmet Salih Yavuz <[email protected]>
AuthorDate: Fri Sep 25 15:29:52 2026 +0300

    fix(table): resolve auto-detected currency on time comparison columns 
(#44649)
---
 .../superset-ui-core/src/currency-format/utils.ts  | 22 +++++++++++
 .../test/currency-format/utils.test.ts             | 33 ++++++++++++++++
 .../src/transformProps.ts                          | 44 ++++++++++++---------
 .../test/transformProps.test.ts                    | 45 ++++++++++++++++++++++
 .../plugin-chart-table/src/transformProps.ts       | 44 ++++++++++++---------
 .../plugin-chart-table/test/TableChart.test.tsx    | 32 +++++++++++++++
 6 files changed, 184 insertions(+), 36 deletions(-)

diff --git 
a/superset-frontend/packages/superset-ui-core/src/currency-format/utils.ts 
b/superset-frontend/packages/superset-ui-core/src/currency-format/utils.ts
index 53a704ab97f..0036bd9e8dd 100644
--- a/superset-frontend/packages/superset-ui-core/src/currency-format/utils.ts
+++ b/superset-frontend/packages/superset-ui-core/src/currency-format/utils.ts
@@ -75,6 +75,28 @@ export const resolveAutoCurrency = (
   return null; // Mixed currencies
 };
 
+/**
+ * Resolves an AUTO currency to the backend-detected one when the currency
+ * code column isn't part of the query results, since CurrencyFormatter can
+ * only detect AUTO per row from that column.
+ */
+export const resolveDetectedCurrency = (
+  currency: Currency | undefined,
+  detectedCurrency: string | null | undefined,
+  currencyCodeColumn: string | undefined,
+  colnames: string[] | undefined,
+): Currency | undefined => {
+  if (
+    currency?.symbol !== AUTO_CURRENCY_SYMBOL ||
+    !detectedCurrency ||
+    (currencyCodeColumn && colnames?.includes(currencyCodeColumn))
+  ) {
+    return currency;
+  }
+  const symbol = normalizeCurrency(detectedCurrency);
+  return symbol ? { ...currency, symbol } : currency;
+};
+
 const getEffectiveCurrencyFormat = (
   resolvedCurrencyFormat: Currency | undefined | null,
   savedFormat: Currency | undefined,
diff --git 
a/superset-frontend/packages/superset-ui-core/test/currency-format/utils.test.ts
 
b/superset-frontend/packages/superset-ui-core/test/currency-format/utils.test.ts
index facf7183806..0f428f3ab23 100644
--- 
a/superset-frontend/packages/superset-ui-core/test/currency-format/utils.test.ts
+++ 
b/superset-frontend/packages/superset-ui-core/test/currency-format/utils.test.ts
@@ -30,6 +30,7 @@ import {
 import {
   analyzeCurrencyInData,
   resolveAutoCurrency,
+  resolveDetectedCurrency,
 } from '../../src/currency-format/utils';
 
 test('buildCustomFormatters without saved metrics returns empty object', () => 
{
@@ -412,3 +413,35 @@ test('getValueFormatter returns NumberFormatter via line 
205 when AUTO resolves
   );
   expect(formatter).toBeInstanceOf(NumberFormatter);
 });
+
+test('resolveDetectedCurrency uses the detected currency when the currency 
column is not in the results', () => {
+  expect(
+    resolveDetectedCurrency(
+      { symbol: 'AUTO', symbolPosition: 'prefix' },
+      'gbp',
+      'currency_code',
+      ['metric'],
+    ),
+  ).toEqual({ symbol: 'GBP', symbolPosition: 'prefix' });
+});
+
+test('resolveDetectedCurrency keeps AUTO when the currency column is in the 
results', () => {
+  const currency: Currency = { symbol: 'AUTO', symbolPosition: 'prefix' };
+  expect(
+    resolveDetectedCurrency(currency, 'GBP', 'currency_code', [
+      'metric',
+      'currency_code',
+    ]),
+  ).toBe(currency);
+});
+
+test('resolveDetectedCurrency leaves non-AUTO and unresolvable currencies 
untouched', () => {
+  const usd: Currency = { symbol: 'USD', symbolPosition: 'prefix' };
+  const auto: Currency = { symbol: 'AUTO', symbolPosition: 'prefix' };
+  expect(resolveDetectedCurrency(usd, 'GBP', 'currency_code', [])).toBe(usd);
+  expect(resolveDetectedCurrency(auto, null, 'currency_code', [])).toBe(auto);
+  expect(resolveDetectedCurrency(auto, 'nope', 'currency_code', 
[])).toBe(auto);
+  expect(resolveDetectedCurrency(undefined, 'GBP', undefined, [])).toBe(
+    undefined,
+  );
+});
diff --git 
a/superset-frontend/plugins/plugin-chart-ag-grid-table/src/transformProps.ts 
b/superset-frontend/plugins/plugin-chart-ag-grid-table/src/transformProps.ts
index fde606dae12..231c5f73464 100644
--- a/superset-frontend/plugins/plugin-chart-ag-grid-table/src/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-ag-grid-table/src/transformProps.ts
@@ -29,7 +29,7 @@ import {
   getNumberFormatter,
   getTimeFormatter,
   getTimeFormatterForGranularity,
-  normalizeCurrency,
+  resolveDetectedCurrency,
   NumberFormats,
   QueryMode,
   SMART_DATE_ID,
@@ -147,6 +147,7 @@ const getComparisonColFormatter = (
   columnConfig: Record<string, TableColumnConfig>,
   savedFormat: string | undefined,
   savedCurrency: Currency | undefined,
+  resolveCurrency: (currency: Currency | undefined) => Currency | undefined,
 ) => {
   const currentColConfig = getComparisonColConfig(
     label,
@@ -161,7 +162,9 @@ const getComparisonColFormatter = (
   if (label === '%') {
     formatter = getNumberFormatter(currentColNumberFormat || PERCENT_3_POINT);
   } else if (currentColNumberFormat || hasCurrency) {
-    const currency = currentColConfig.currencyFormat || savedCurrency;
+    const currency = resolveCurrency(
+      currentColConfig.currencyFormat || savedCurrency,
+    );
     const numberFormat = currentColNumberFormat || savedFormat;
     formatter = currency
       ? new CurrencyFormatter({
@@ -254,9 +257,19 @@ const processComparisonColumns = (
 ) =>
   columns.flatMap(col => {
     const {
-      datasource: { columnFormats, currencyFormats },
+      datasource: { columnFormats, currencyFormats, currencyCodeColumn },
       rawFormData: { column_config: columnConfig = {} },
+      queriesData,
     } = props;
+    const { detected_currency: detectedCurrency, colnames } =
+      queriesData[0] || {};
+    const resolveCurrency = (currency: Currency | undefined) =>
+      resolveDetectedCurrency(
+        currency,
+        detectedCurrency,
+        currencyCodeColumn,
+        colnames,
+      );
     const savedFormat = columnFormats?.[col.key];
     const savedCurrency = currencyFormats?.[col.key];
     const originalLabel = col.label;
@@ -279,6 +292,7 @@ const processComparisonColumns = (
             columnConfig,
             savedFormat,
             savedCurrency,
+            resolveCurrency,
           ),
         },
         {
@@ -294,6 +308,7 @@ const processComparisonColumns = (
             columnConfig,
             savedFormat,
             savedCurrency,
+            resolveCurrency,
           ),
         },
         {
@@ -309,6 +324,7 @@ const processComparisonColumns = (
             columnConfig,
             savedFormat,
             savedCurrency,
+            resolveCurrency,
           ),
         },
         {
@@ -324,6 +340,7 @@ const processComparisonColumns = (
             columnConfig,
             savedFormat,
             savedCurrency,
+            resolveCurrency,
           ),
         },
       ];
@@ -477,21 +494,12 @@ const processColumns = memoizePerChart(function 
processColumns(
         // percent metrics have a default format
         formatter = getNumberFormatter(numberFormat || PERCENT_3_POINT);
       } else if (isMetric || (isNumber && (numberFormat || currency))) {
-        // Resolve AUTO currency when currency column isn't in query results
-        let resolvedCurrency = currency;
-        if (
-          currency?.symbol === 'AUTO' &&
-          detectedCurrency &&
-          (!currencyCodeColumn || !colnames?.includes(currencyCodeColumn))
-        ) {
-          const normalizedCurrency = normalizeCurrency(detectedCurrency);
-          if (normalizedCurrency) {
-            resolvedCurrency = {
-              ...currency,
-              symbol: normalizedCurrency,
-            };
-          }
-        }
+        const resolvedCurrency = resolveDetectedCurrency(
+          currency,
+          detectedCurrency,
+          currencyCodeColumn,
+          colnames,
+        );
         formatter = resolvedCurrency?.symbol
           ? new CurrencyFormatter({
               d3Format: numberFormat,
diff --git 
a/superset-frontend/plugins/plugin-chart-ag-grid-table/test/transformProps.test.ts
 
b/superset-frontend/plugins/plugin-chart-ag-grid-table/test/transformProps.test.ts
index 61c12b3bc06..bcfefaa1861 100644
--- 
a/superset-frontend/plugins/plugin-chart-ag-grid-table/test/transformProps.test.ts
+++ 
b/superset-frontend/plugins/plugin-chart-ag-grid-table/test/transformProps.test.ts
@@ -329,6 +329,51 @@ test('does not mistake the all_records percent-metric 
query for the totals query
   expect(result.totals).toEqual({ sum__num: 42 });
 });
 
+test('resolves AUTO currency on comparison columns from detected_currency', () 
=> {
+  const autoCurrency = { symbol: 'AUTO', symbolPosition: 'prefix' };
+  const comparisonKeys = ['Main metric_1', '# metric_1', '△ metric_1'];
+  const props = createMockChartProps({
+    rawFormData: {
+      viz_type: 'table',
+      datasource: '1__table',
+      slice_id: 1,
+      query_mode: QueryMode.Aggregate,
+      metrics: ['metric_1'],
+      percent_metrics: [],
+      column_config: Object.fromEntries(
+        comparisonKeys.map(key => [key, { currencyFormat: autoCurrency }]),
+      ),
+      table_timestamp_format: '',
+      time_compare: ['1 year ago'],
+      comparison_type: 'values',
+    },
+    datasource: {
+      columns: [],
+      metrics: [],
+      columnFormats: {},
+      currencyFormats: {},
+      verboseMap: {},
+      currencyCodeColumn: 'currency_code',
+    },
+    queriesData: [
+      {
+        data: [{ metric_1: 100, 'metric_1__1 year ago': 80 }],
+        colnames: ['metric_1', 'metric_1__1 year ago'],
+        coltypes: [GenericDataType.Numeric, GenericDataType.Numeric],
+        rowcount: 1,
+        detected_currency: 'GBP',
+      },
+      { data: [{ rowcount: 1 }] },
+    ] as unknown as TableChartProps['queriesData'],
+  } as unknown as Partial<TableChartProps>);
+
+  const { columns } = transformProps(props);
+  comparisonKeys.forEach(key => {
+    const formatted = columns.find(col => col.key === key)?.formatter?.(100);
+    expect(formatted).toContain('£');
+  });
+});
+
 test('excludes Green/Red color-scheme rules from columnColorFormatters', () => 
{
   // Green/Red rules are rendered via the increase/decrease path, so they must
   // not reach getColorFormatters, which would treat the scheme name as a hex
diff --git a/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts 
b/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts
index 21442b9f0fa..b3e3e62bf99 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts
@@ -30,7 +30,7 @@ import {
   getTimeFormatter,
   getTimeFormatterForGranularity,
   isAdhocColumn,
-  normalizeCurrency,
+  resolveDetectedCurrency,
   NumberFormats,
   QueryMode,
   SMART_DATE_ID,
@@ -299,21 +299,12 @@ const processColumns = memoizeOne(function processColumns(
         // percent metrics have a default format
         formatter = getNumberFormatter(numberFormat || PERCENT_3_POINT);
       } else if (isMetric || (isNumber && (numberFormat || currency))) {
-        // Resolve AUTO currency when currency column isn't in query results
-        let resolvedCurrency = currency;
-        if (
-          currency?.symbol === 'AUTO' &&
-          detectedCurrency &&
-          (!currencyCodeColumn || !colnames?.includes(currencyCodeColumn))
-        ) {
-          const normalizedCurrency = normalizeCurrency(detectedCurrency);
-          if (normalizedCurrency) {
-            resolvedCurrency = {
-              ...currency,
-              symbol: normalizedCurrency,
-            };
-          }
-        }
+        const resolvedCurrency = resolveDetectedCurrency(
+          currency,
+          detectedCurrency,
+          currencyCodeColumn,
+          colnames,
+        );
         formatter = resolvedCurrency?.symbol
           ? new CurrencyFormatter({
               d3Format: numberFormat,
@@ -358,6 +349,7 @@ const getComparisonColFormatter = (
   columnConfig: Record<string, TableColumnConfig>,
   savedFormat: string | undefined,
   savedCurrency: Currency | undefined,
+  resolveCurrency: (currency: Currency | undefined) => Currency | undefined,
 ) => {
   const currentColConfig = getComparisonColConfig(
     label,
@@ -372,7 +364,9 @@ const getComparisonColFormatter = (
   if (label === '%') {
     formatter = getNumberFormatter(currentColNumberFormat || PERCENT_3_POINT);
   } else if (currentColNumberFormat || hasCurrency) {
-    const currency = currentColConfig.currencyFormat || savedCurrency;
+    const currency = resolveCurrency(
+      currentColConfig.currencyFormat || savedCurrency,
+    );
     const numberFormat = currentColNumberFormat || savedFormat;
     formatter = currency
       ? new CurrencyFormatter({
@@ -391,9 +385,19 @@ const processComparisonColumns = (
 ) =>
   columns.flatMap(col => {
     const {
-      datasource: { columnFormats, currencyFormats },
+      datasource: { columnFormats, currencyFormats, currencyCodeColumn },
       rawFormData: { column_config: columnConfig = {} },
+      queriesData,
     } = props;
+    const { detected_currency: detectedCurrency, colnames } =
+      queriesData[0] || {};
+    const resolveCurrency = (currency: Currency | undefined) =>
+      resolveDetectedCurrency(
+        currency,
+        detectedCurrency,
+        currencyCodeColumn,
+        colnames,
+      );
     const savedFormat = columnFormats?.[col.key];
     const savedCurrency = currencyFormats?.[col.key];
     const originalLabel = col.label;
@@ -415,6 +419,7 @@ const processComparisonColumns = (
             columnConfig,
             savedFormat,
             savedCurrency,
+            resolveCurrency,
           ),
         },
         {
@@ -429,6 +434,7 @@ const processComparisonColumns = (
             columnConfig,
             savedFormat,
             savedCurrency,
+            resolveCurrency,
           ),
         },
         {
@@ -443,6 +449,7 @@ const processComparisonColumns = (
             columnConfig,
             savedFormat,
             savedCurrency,
+            resolveCurrency,
           ),
         },
         {
@@ -457,6 +464,7 @@ const processComparisonColumns = (
             columnConfig,
             savedFormat,
             savedCurrency,
+            resolveCurrency,
           ),
         },
       ];
diff --git 
a/superset-frontend/plugins/plugin-chart-table/test/TableChart.test.tsx 
b/superset-frontend/plugins/plugin-chart-table/test/TableChart.test.tsx
index 9e67f24fd80..01ab2b7651f 100644
--- a/superset-frontend/plugins/plugin-chart-table/test/TableChart.test.tsx
+++ b/superset-frontend/plugins/plugin-chart-table/test/TableChart.test.tsx
@@ -335,6 +335,38 @@ describe('plugin-chart-table', () => {
       expect(formattedPercentMetric).toBe('0.123');
     });
 
+    test('resolves AUTO currency on comparison columns from 
detected_currency', () => {
+      const autoCurrency = { symbol: 'AUTO', symbolPosition: 'prefix' };
+      const comparisonKeys = ['Main metric_1', '# metric_1', '△ metric_1'];
+      const transformedProps = transformProps({
+        ...testData.comparisonWithConfig,
+        rawFormData: {
+          ...testData.comparisonWithConfig.rawFormData,
+          column_config: Object.fromEntries(
+            comparisonKeys.map(key => [key, { currencyFormat: autoCurrency }]),
+          ),
+        },
+        datasource: {
+          ...testData.comparisonWithConfig.datasource,
+          currencyCodeColumn: 'currency_code',
+        },
+        queriesData: [
+          {
+            ...testData.comparisonWithConfig.queriesData[0],
+            detected_currency: 'GBP',
+          },
+          testData.comparisonWithConfig.queriesData[1],
+        ],
+      });
+
+      comparisonKeys.forEach(key => {
+        const formatted = transformedProps.columns
+          .find(col => col.key === key)
+          ?.formatter?.(100);
+        expect(formatted).toContain('£');
+      });
+    });
+
     test('should set originalLabel for comparison columns when time_compare 
and comparison_type are set', () => {
       const transformedProps = transformProps(testData.comparison);
 

Reply via email to