PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.16
Yatra – Travel Booking & Tour Operator Software v3.0.16
3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 All 84 releases
← All changes | resources/js/lib/currency-display.ts +59 -23 3.0.3 → 3.0.16 View file →
@@ -7,13 +7,9 @@
7 7
8 8 import { __ } from "./i18n";
9 9 import { getCurrency, getCurrencySymbol } from "../data/currencies";
10 10
11 -export type CurrencyPlacement =
12 - | "left"
13 - | "right"
14 - | "left_space"
15 - | "right_space";
11 +export type CurrencyPlacement = "left" | "right" | "left_space" | "right_space";
16 12
17 13 /**
18 14 * Normalize option / legacy values to one of the four placement modes.
19 15 */
@@ -26,9 +22,14 @@
26 22 }
27 23 if (p === "after") {
28 24 return "right_space";
29 25 }
30 - if (p === "right" || p === "right_space" || p === "left" || p === "left_space") {
26 + if (
27 + p === "right" ||
28 + p === "right_space" ||
29 + p === "left" ||
30 + p === "left_space"
31 + ) {
31 32 return p as CurrencyPlacement;
32 33 }
33 34 return "left_space";
34 35 }
@@ -93,31 +94,62 @@
93 94 "before"
94 95 );
95 96 }
96 97
97 -function readAdminNumberFormatConfig(): {
98 +function readYatraNumberFormatConfig(): {
98 99 decimalPlaces: number;
99 100 thousandSeparator: string;
100 101 decimalSeparator: string;
101 102 } {
103 + const fallback = {
104 + decimalPlaces: 2,
105 + thousandSeparator: ",",
106 + decimalSeparator: ".",
107 + };
102 108 if (typeof window === "undefined") {
103 - return { decimalPlaces: 2, thousandSeparator: ",", decimalSeparator: "." };
109 + return fallback;
104 110 }
105 - const a = ((window as AdminWindow).yatraAdmin || {}) as Record<
106 - string,
107 - unknown
108 - >;
109 - const decimals = Number(a.decimalPlaces ?? a.currency_decimals ?? 2) || 2;
111 + // Read from the SAME set of globals as the currency position, not just
112 + // yatraAdmin. On public pages (customer account, checkout, single trip)
113 + // window.yatraAdmin is absent, so reading separators only from it made the
114 + // decimal/thousand separators silently fall back to defaults ("." / ",")
115 + // while the position — read from these public globals — was correct. That is
116 + // the "decimal separator ignored in payment amounts" bug on the storefront.
117 + const w = window as AdminWindow;
118 + const sources: Array<Record<string, unknown> | undefined> = [
119 + w.yatraAdmin as Record<string, unknown> | undefined,
120 + w.yatraBookingData as Record<string, unknown> | undefined,
121 + w.yatraTripData as Record<string, unknown> | undefined,
122 + w.yatraAccountPage as Record<string, unknown> | undefined,
123 + ];
124 + const pickString = (field: string): string | undefined => {
125 + for (const o of sources) {
126 + const v = o?.[field];
127 + if (typeof v === "string" && v !== "") {
128 + return v;
129 + }
130 + }
131 + return undefined;
132 + };
133 + const pickNumber = (...fields: string[]): number | undefined => {
134 + for (const o of sources) {
135 + if (!o) {
136 + continue;
137 + }
138 + for (const field of fields) {
139 + const v = o[field];
140 + if (v !== undefined && v !== null && v !== "" && !Number.isNaN(Number(v))) {
141 + return Number(v);
142 + }
143 + }
144 + }
145 + return undefined;
146 + };
147 + const decimals = pickNumber("decimalPlaces", "currency_decimals") ?? 2;
110 148 return {
111 149 decimalPlaces: Math.max(0, Math.min(4, decimals)),
112 - thousandSeparator:
113 - typeof a.thousandSeparator === "string" && a.thousandSeparator !== ""
114 - ? (a.thousandSeparator as string)
115 - : ",",
116 - decimalSeparator:
117 - typeof a.decimalSeparator === "string" && a.decimalSeparator !== ""
118 - ? (a.decimalSeparator as string)
119 - : ".",
150 + thousandSeparator: pickString("thousandSeparator") ?? ",",
151 + decimalSeparator: pickString("decimalSeparator") ?? ".",
120 152 };
121 153 }
122 154
123 155 /**
@@ -136,9 +168,9 @@
136 168 return __("Contact for pricing", "yatra");
137 169 }
138 170
139 171 const { decimalPlaces, thousandSeparator, decimalSeparator } =
140 - readAdminNumberFormatConfig();
172 + readYatraNumberFormatConfig();
141 173 const currencyData = getCurrency(currencyCode);
142 174 const decimals =
143 175 currencyData?.decimalDigits !== undefined
144 176 ? Math.max(0, Math.min(4, currencyData.decimalDigits))
@@ -143,9 +175,13 @@
143 175 currencyData?.decimalDigits !== undefined
144 176 ? Math.max(0, Math.min(4, currencyData.decimalDigits))
145 177 : decimalPlaces;
146 178
147 - const formattedCore = new Intl.NumberFormat(undefined, {
179 + // Format with a FIXED "en-US" base (grouping ",", decimal ".") so the
180 + // replace-with-configured-separators step below is deterministic. Using the
181 + // viewer's locale here (undefined) would already yield locale separators and
182 + // the naive comma/dot replacement would swap them for e.g. German browsers.
183 + const formattedCore = new Intl.NumberFormat("en-US", {
148 184 minimumFractionDigits: decimals,
149 185 maximumFractionDigits: decimals,
150 186 })
151 187 .format(num)