Always fetch FX rate by expense date (remove UTC “today” path and latest fallback)#4
Merged
Merged
Conversation
Agent-Logs-Url: https://github.com/ClementLSW/osps/sessions/7f23da57-fd59-4ff9-8e7d-b17be954e069 Co-authored-by: ClementLSW <24468433+ClementLSW@users.noreply.github.com>
Copilot created this pull request from a session on behalf of
ClementLSW
May 26, 2026 16:56
View session
✅ Deploy Preview for omoneypmoney ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates foreign-exchange rate retrieval to always fetch a rate for the explicit expense date (removing the previous UTC “today” branching), and adjusts the AddExpense FX effect/loading behavior accordingly.
Changes:
- Updated the AddExpense FX
useEffectto always passexpenseDateintofetchExchangeRateand simplified the FX error logging payload. - Replaced the exchange-rate utility implementation with a single explicit-date fetch against
https://api.frankfurter.dev/v1/{date}?from=&to=. - Adjusted FX loading-state handling to clear loading on early-guard returns and in
finally.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/pages/AddExpense.jsx | Always fetches FX by expenseDate, updates loading-state behavior, and simplifies FX error logging payload. |
| src/lib/exchangeRate.js | Replaces prior multi-path FX fetching with a single explicit-date Frankfurter fetch and same-currency fast path. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (!cancelled) toast.error('Could not fetch exchange rate') | ||
| } finally { | ||
| if (!cancelled) setRateLoading(false) | ||
| setRateLoading(false) |
| useEffect(() => { | ||
| const groupCurrency = group?.currency || 'SGD' | ||
| if (!expenseCurrency || expenseCurrency === groupCurrency || manualRate) return | ||
| if (!expenseCurrency || expenseCurrency === groupCurrency || manualRate) { |
Comment on lines
14
to
20
| export async function fetchExchangeRate(from, to, date) { | ||
| if (from === to) { | ||
| return { | ||
| from, | ||
| to, | ||
| rate: 1, | ||
| date: date || new Date().toISOString().slice(0, 10), | ||
| fetchedAt: new Date().toISOString(), | ||
| } | ||
| return { from, to, rate: 1, date, fetchedAt: new Date().toISOString() } | ||
| } | ||
|
|
||
| // First attempt | ||
| try { | ||
| return await _fetchOnce(from, to, date) | ||
| } catch (err) { | ||
| // Only retry /latest — historical dates don't have publish window issues | ||
| if (date) throw err | ||
|
|
||
| const yesterday = new Date() | ||
| yesterday.setUTCDate(yesterday.getUTCDate() - 1) | ||
| const fallbackDate = yesterday.toISOString().slice(0, 10) | ||
|
|
||
| const result = await _fetchOnce(from, to, fallbackDate) | ||
| return { ...result, usedFallbackDate: true } | ||
| } | ||
| } | ||
|
|
||
| async function _fetchOnce(from, to, date) { | ||
| const params = new URLSearchParams({ from, to }) | ||
| if (date) params.set('date', date) | ||
|
|
||
| const res = await fetch(`/api/exchange-rate?${params}`) | ||
| const res = await fetch(`${BASE_URL}/${date}?from=${from}&to=${to}`) | ||
|
|
Comment on lines
+19
to
23
| const res = await fetch(`${BASE_URL}/${date}?from=${from}&to=${to}`) | ||
|
|
||
| if (!res.ok) { | ||
| const body = await res.json().catch(() => ({})) | ||
| const err = new Error(body.error || `Exchange rate fetch failed: ${res.status}`) | ||
| const err = new Error(`Exchange rate fetch failed: ${res.status}`) | ||
| err.status = res.status |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Late-night SGT entries could be treated as “today” via a UTC date check, causing live-rate fetches instead of the expense-date rate. This updates FX retrieval to always use an explicit expense date, removing time-zone-dependent branching.
Exchange rate utility: date-only fetch path
src/lib/exchangeRate.jswith a single explicit-date implementation against Frankfurter (/v1/{date}?from=&to=)./latestbehavior, retry/fallback logic, and related helper paths.date.AddExpense FX effect: remove UTC “isToday” logic
fetchExchangeRate(...)now always receivesexpenseDate.is_historicalfromfx.errorlogging payload (no longer meaningful after date-only behavior).Loading-state correctness
finallynow always clears loading, independent of cancellation flag.