Fix spend rule currency deploy blockers#94207
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
🦜 Polyglot Parrot! 🦜Squawk! Looks like you added some shiny new English strings. Allow me to parrot them back to you in other tongues: View the translation diffdiff --git a/src/languages/de.ts b/src/languages/de.ts
index 297a6a126ca..f0c4b620340 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -8297,7 +8297,8 @@ Fügen Sie weitere Ausgabelimits hinzu, um den Cashflow Ihres Unternehmens zu sc
bodyCurrency: ({adjective, value}: {adjective: string; value: string}) => (adjective !== '' ? `${adjective} Währung „${value}“` : `Währung „${value}“`),
bodyCurrencyValueOnly: ({value}: {value: string}) => `'${value}'`,
bodyCurrencyChange: ({adjective, oldValue, newValue}: {adjective: string; oldValue: string; newValue: string}) =>
- adjective !== '' ? `${adjective} Währung von „${oldValue}“ zu „${newValue}“` : `Währung von „${oldValue}“ in „${newValue}“`,
+ adjective !== '' ? `${adjective} Währung von „${oldValue}“ zu „${newValue}“` : `Währung von „${oldValue}“ zu „${newValue}“`,
+ bodyCurrencyRestriction: 'die Währungsbeschränkung',
},
allowedCurrencyFilters: ({currencies}: {currencies: string}) => `Währungen ${currencies}`,
blockedCurrencyFilters: ({currencies}: {currencies: string}) => `Währungen, die nicht in ${currencies} sind`,
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 020f29f758d..c0da855d173 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -7765,13 +7765,14 @@ ${amount} para ${merchant} - ${date}`,
bodyRemovedFromCards: ({cards}: {cards: string}) => `regla de gasto de ${cards}`,
composeOnCards: ({content, cards}: {content: string; cards: string}) => `${content} en ${cards}`,
composeFromCards: ({content, cards}: {content: string; cards: string}) => `${content} de ${cards}`,
- bodyCurrency: ({adjective, value}: {adjective: string; value: string}) => (adjective !== '' ? `moneda ${adjective} «${value}»` : `moneda «${value}»`),
+ bodyCurrency: ({adjective, value}: {adjective: string; value: string}) => (adjective !== '' ? `Moneda ${adjective} «${value}»` : `moneda «${value}»`),
bodyCurrencyValueOnly: ({value}: {value: string}) => `'${value}'`,
bodyCurrencyChange: ({adjective, oldValue, newValue}: {adjective: string; oldValue: string; newValue: string}) =>
- adjective !== '' ? `Moneda ${adjective} de «${oldValue}» a «${newValue}»` : `moneda de '${oldValue}' a '${newValue}'`,
+ adjective !== '' ? `${adjective} divisa de «${oldValue}» a «${newValue}»` : `moneda de '${oldValue}' a '${newValue}'`,
+ bodyCurrencyRestriction: 'la restricción de divisa',
},
allowedCurrencyFilters: ({currencies}: {currencies: string}) => `monedas ${currencies}`,
- blockedCurrencyFilters: ({currencies}: {currencies: string}) => `monedas que no estén en ${currencies}`,
+ blockedCurrencyFilters: ({currencies}: {currencies: string}) => `monedas que no están en ${currencies}`,
},
preventSelfApproval: (oldValue, newValue) =>
`actualizó "Evitar la autoaprobación" a "${newValue === 'true' ? 'Habilitada' : 'Deshabilitada'}" (previamente "${oldValue === 'true' ? 'Habilitada' : 'Deshabilitada'}")`,
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index e3b626cde28..85b128be1bf 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -8329,9 +8329,10 @@ Ajoutez davantage de règles de dépenses pour protéger la trésorerie de l’e
composeOnCards: ({content, cards}: {content: string; cards: string}) => `${content} sur ${cards}`,
composeFromCards: ({content, cards}: {content: string; cards: string}) => `${content} de ${cards}`,
bodyCurrencyValueOnly: ({value}: {value: string}) => `'${value}'`,
- bodyCurrency: ({adjective, value}: {adjective: string; value: string}) => (adjective !== '' ? `Devise ${adjective} « ${value} »` : `devise « ${value} »`),
+ bodyCurrency: ({adjective, value}: {adjective: string; value: string}) => (adjective !== '' ? `Devise ${adjective} « ${value} »` : `devise « ${value} »`),
bodyCurrencyChange: ({adjective, oldValue, newValue}: {adjective: string; oldValue: string; newValue: string}) =>
- adjective !== '' ? `Devise ${adjective} de « ${oldValue} » à « ${newValue} »` : `devise de « ${oldValue} » à « ${newValue} »`,
+ adjective !== '' ? `Devise ${adjective} de « ${oldValue} » à « ${newValue} »` : `devise de « ${oldValue} » à « ${newValue} »`,
+ bodyCurrencyRestriction: 'la restriction de devise',
},
allowedCurrencyFilters: ({currencies}: {currencies: string}) => `devises ${currencies}`,
blockedCurrencyFilters: ({currencies}: {currencies: string}) => `devises ne figurant pas dans ${currencies}`,
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 4b66ca8a1d4..7ca95eb5444 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -8284,10 +8284,11 @@ Aggiungi altre regole di spesa per proteggere il flusso di cassa aziendale.`,
bodyRemovedFromCards: ({cards}: {cards: string}) => `regola di spesa da ${cards}`,
composeOnCards: ({content, cards}: {content: string; cards: string}) => `${content} su ${cards}`,
composeFromCards: ({content, cards}: {content: string; cards: string}) => `${content} da ${cards}`,
- bodyCurrency: ({adjective, value}: {adjective: string; value: string}) => (adjective !== '' ? `Valuta ${adjective} "${value}"` : `valuta '${value}'`),
+ bodyCurrency: ({adjective, value}: {adjective: string; value: string}) => (adjective !== '' ? `Valuta ${adjective} '${value}'` : `valuta '${value}'`),
bodyCurrencyValueOnly: ({value}: {value: string}) => `'${value}'`,
bodyCurrencyChange: ({adjective, oldValue, newValue}: {adjective: string; oldValue: string; newValue: string}) =>
adjective !== '' ? `valuta ${adjective} da '${oldValue}' a '${newValue}'` : `valuta da '${oldValue}' a '${newValue}'`,
+ bodyCurrencyRestriction: 'la restrizione sulla valuta',
},
allowedCurrencyFilters: ({currencies}: {currencies: string}) => `valute ${currencies}`,
blockedCurrencyFilters: ({currencies}: {currencies: string}) => `valute non in ${currencies}`,
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index ac1fc29cbb6..ed0ae9424cf 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -8187,9 +8187,10 @@ ${reportName}`,
bodyCurrencyValueOnly: ({value}: {value: string}) => `'${value}'`,
bodyCurrencyChange: ({adjective, oldValue, newValue}: {adjective: string; oldValue: string; newValue: string}) =>
adjective !== '' ? `${adjective}通貨を「${oldValue}」から「${newValue}」に変更しました` : `通貨を「${oldValue}」から「${newValue}」に変更しました`,
+ bodyCurrencyRestriction: '通貨制限',
},
allowedCurrencyFilters: ({currencies}: {currencies: string}) => `通貨 ${currencies}`,
- blockedCurrencyFilters: ({currencies}: {currencies: string}) => `${currencies} に含まれない通貨`,
+ blockedCurrencyFilters: ({currencies}: {currencies: string}) => `${currencies} に含まれていない通貨`,
},
updatedCategoryTaxRate: ({categoryName, oldTax, newTax}: {categoryName: string; oldTax: string; newTax: string}) =>
`「${categoryName}」カテゴリのデフォルト税率を「${newTax}」に変更しました(以前は「${oldTax}」)`,
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 1815bf6e824..daa8827a400 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -8254,10 +8254,11 @@ er bestedingsregels toe om de kasstroom van het bedrijf te beschermen.`,
bodyCurrency: ({adjective, value}: {adjective: string; value: string}) => (adjective !== '' ? `${adjective} valuta '${value}'` : `valuta '${value}'`),
bodyCurrencyValueOnly: ({value}: {value: string}) => `'${value}'`,
bodyCurrencyChange: ({adjective, oldValue, newValue}: {adjective: string; oldValue: string; newValue: string}) =>
- adjective !== '' ? `${adjective} valuta gewijzigd van '${oldValue}' naar '${newValue}'` : `valuta van '${oldValue}' naar '${newValue}'`,
+ adjective !== '' ? `${adjective} valuta van '${oldValue}' naar '${newValue}'` : `valuta van '${oldValue}' naar '${newValue}'`,
+ bodyCurrencyRestriction: 'de valutabeperking',
},
- allowedCurrencyFilters: ({currencies}: {currencies: string}) => `valuta’s ${currencies}`,
- blockedCurrencyFilters: ({currencies}: {currencies: string}) => `valuta die niet in ${currencies} staan`,
+ allowedCurrencyFilters: ({currencies}: {currencies: string}) => `valuta's ${currencies}`,
+ blockedCurrencyFilters: ({currencies}: {currencies: string}) => `valuta’s niet in ${currencies}`,
},
updatedCategoryTaxRate: ({categoryName, oldTax, newTax}: {categoryName: string; oldTax: string; newTax: string}) =>
`heeft het standaardbelastingtarief van de categorie "${categoryName}" gewijzigd naar "${newTax}" (voorheen "${oldTax}")`,
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 00e432fd1cf..6851fe2cf58 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -8246,10 +8246,11 @@ Dodaj więcej zasad wydatków, żeby chronić płynność finansową firmy.`,
bodyCurrency: ({adjective, value}: {adjective: string; value: string}) => (adjective !== '' ? `${adjective} waluta „${value}”` : `waluta „${value}”`),
bodyCurrencyValueOnly: ({value}: {value: string}) => `'${value}'`,
bodyCurrencyChange: ({adjective, oldValue, newValue}: {adjective: string; oldValue: string; newValue: string}) =>
- adjective !== '' ? `zmieniono walutę ${adjective} z „${oldValue}” na „${newValue}”` : `walutę z „${oldValue}” na „${newValue}”`,
+ adjective !== '' ? `${adjective} walutę z „${oldValue}” na „${newValue}”` : `walutę z „${oldValue}” na „${newValue}”`,
+ bodyCurrencyRestriction: 'ograniczenie waluty',
},
allowedCurrencyFilters: ({currencies}: {currencies: string}) => `waluty ${currencies}`,
- blockedCurrencyFilters: ({currencies}: {currencies: string}) => `waluty inne niż ${currencies}`,
+ blockedCurrencyFilters: ({currencies}: {currencies: string}) => `waluty spoza ${currencies}`,
},
updatedCategoryTaxRate: ({categoryName, oldTax, newTax}: {categoryName: string; oldTax: string; newTax: string}) =>
`zmienił(a) domyślną stawkę podatku dla kategorii „${categoryName}” na „${newTax}” (wcześniej „${oldTax}”)`,
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index d195ad3bfd9..442a64252f8 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -8245,6 +8245,7 @@ Adicione mais regras de gasto para proteger o fluxo de caixa da empresa.`,
bodyCurrencyValueOnly: ({value}: {value: string}) => `'${value}'`,
bodyCurrencyChange: ({adjective, oldValue, newValue}: {adjective: string; oldValue: string; newValue: string}) =>
adjective !== '' ? `${adjective} moeda de '${oldValue}' para '${newValue}'` : `moeda de '${oldValue}' para '${newValue}'`,
+ bodyCurrencyRestriction: 'a restrição de moeda',
},
allowedCurrencyFilters: ({currencies}: {currencies: string}) => `moedas ${currencies}`,
blockedCurrencyFilters: ({currencies}: {currencies: string}) => `moedas que não estão em ${currencies}`,
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index aa54e9074bb..8427267f944 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -8025,7 +8025,8 @@ ${reportName}`,
bodyCurrency: ({adjective, value}: {adjective: string; value: string}) => (adjective !== '' ? `${adjective} 货币“${value}”` : `货币“${value}”`),
bodyCurrencyValueOnly: ({value}: {value: string}) => `'${value}'`,
bodyCurrencyChange: ({adjective, oldValue, newValue}: {adjective: string; oldValue: string; newValue: string}) =>
- adjective !== '' ? `将 ${adjective} 货币从“${oldValue}”更改为“${newValue}”` : `货币从“${oldValue}”更改为“${newValue}”`,
+ adjective !== '' ? `将 ${adjective} 货币从“${oldValue}”更改为“${newValue}”` : `货币从“${oldValue}”变更为“${newValue}”`,
+ bodyCurrencyRestriction: '货币限制',
},
allowedCurrencyFilters: ({currencies}: {currencies: string}) => `货币 ${currencies}`,
blockedCurrencyFilters: ({currencies}: {currencies: string}) => `不在 ${currencies} 中的货币`,
Note You can apply these changes to your branch by copying the patch to your clipboard, then running |
|
@marufsharifi updated the message for updating rules |
Screen.Recording.2026-06-26.at.12.38.26.PM.movThe popover issue on android seems to be a problem with the existing production modal component, so lets ignore that one here |
|
@JS00001, When navigating to the Permitted Currencies page through the UI, the USD currency is locked and cannot be selected or deselected. However, when accessing the same page directly via its URL, USD is no longer locked and can be both selected and deselected. Could you please confirm whether this difference in behavior is expected or if it should be considered a regression? RF-12.mov |
|
Yeah I think thats fine @marufsharifi . The same issue would apply with setting a max amount: the user could technically navigate straight to that page (bypassing the 'invalid cards' modal) and set a max amount for two cards not on the same currency. I dont think this is a likely case |
|
@marufsharifi can you please re-review? |
|
@JS00001, could you please merge the main? thanks. |
|
Done |
|
LGTM |
|
@luacmartins over to you |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚧 luacmartins has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/luacmartins in version: 9.4.26-0 🚀
|
|
🤖 Help site review — changes required This PR ships the currency restriction for Expensify Card Spend Rules (fixing its deploy blockers). The help article Expensify Card Spend Rules documented only Merchant, Spend category, and Max amount — it never mentioned the Permitted currencies field ("Choose to allow all or specific currencies") that the spend-rule UI exposes. So the docs were out of date relative to the shipped behavior. Draft docs PR: #95087 (labeled What I changed in the article
UI label verified against source: @JS00001, please review the linked help site PR and confirm it reflects the current behavior. Then mark the linked help site PR |
|
🚀 Deployed to production by https://github.com/grgia in version: 9.4.27-2 🚀
Bundle Size Analysis (Sentry): |
Explanation of Change
Fixes deploy blockers for currencies in spend rules
Fixed Issues
$ #94116
$ #94078
$ #94091
Offline tests
Validate the linked issues
QA Steps
Validate the linked issues
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectioncanBeMissingparam foruseOnyxtoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps./** comment above it */thisproperly so there are no scoping issues (i.e. foronClick={this.submit}the methodthis.submitshould be bound tothisin the constructor)thisare necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);ifthis.submitis never passed to a component event handler likeonClick)Screenshots/Videos
Screen.Recording.2026-06-23.at.1.14.29.PM.mov