Let each store owner set their own primary currency - #837
Closed
KrzysztofPajak wants to merge 2 commits into
Closed
KrzysztofPajak wants to merge 2 commits into
KrzysztofPajak wants to merge 2 commits into
Conversation
A store whose published currencies are all limited to other stores made WorkingCurrency throw "No currency could be loaded" from ContextMiddleware, which took down every request for that store. A store manager could reach that state alone: unset the default currency, then unassign each remaining store-limited currency. Fall back to the primary store currency instead of throwing, and refuse the edits that would empty a store in the first place - in the store panel when unassigning the last available currency, and in Main Admin when saving store mappings that would leave any store without one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrGY1NnCzdkn7wUxbyHzsX
Product prices are stored in the primary store currency, which was a single global setting. A store owner running their own catalog had no way to hold prices in their own currency. The store now carries its own Store.PrimaryCurrencyId and falls back to the global CurrencySettings.PrimaryStoreCurrencyId when it is empty, so existing installations keep behaving exactly as before. The override deliberately does not live in a store-scoped CurrencySettings row: LoadSetting falls back per object, not per field, and three of that class's four fields (exchange rate currency, rate provider, auto update) are genuinely system-wide. A store-scoped row would freeze them for that store and silently break its rates the next time an administrator changed the global value. Prices are not recalculated when the currency changes - the stored amounts are simply reinterpreted. Because a product shared with another store would silently change meaning, the store owner has to confirm the change when CountSharedProducts finds any. Deleting, unpublishing or unmapping a currency now checks every store's primary currency, not just the global one, so a store cannot be left with a currency it no longer has access to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrGY1NnCzdkn7wUxbyHzsX
KrzysztofPajak
changed the base branch from
fix/store-without-available-currency
to
develop
September 13, 2026 09:00
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.
Type: feature
Issue
Product prices are stored in the primary store currency, and that currency was a single global setting (
CurrencySettings.PrimaryStoreCurrencyId). In a multi-store installation where each store owner runs their own catalog, every store was forced to hold its prices in the same base currency.Store.DefaultCurrencyIdonly selects which currency a visitor sees, not the one prices are stored in, so there was no way to express "this store's prices are in PLN".Solution
Store.PrimaryCurrencyId— a per-store override sitting next to the existingDefaultCurrencyId.CurrencyService.GetPrimaryStoreCurrency()prefers it and falls back to the global setting when it is empty or points at a currency that no longer exists. Every price conversion in the system already routes through that one method, so pricing, checkout and the storefront needed no changes.The override deliberately does not live in a store-scoped
CurrencySettingsrow.SettingService.LoadSettingfalls back per object rather than per field, and three of that class's four fields (PrimaryExchangeRateCurrencyId,ActiveExchangeRateProviderSystemName,AutoUpdateEnabled) are genuinely system-wide. A store-scoped row would freeze them for that store, so a later change to the global exchange rate currency would never reach it — a silent rate failure.Store owners set the currency from Store manager → Configuration → Currencies. Prices are not recalculated; the stored amounts are reinterpreted in the new currency. Because a product shared with another store would silently change meaning,
IProductService.CountSharedProductscounts products visible both here and elsewhere, and the owner has to confirm when there are any.Deleting, unpublishing or unmapping a currency now inspects every store's primary currency rather than only the global one — previously a store's primary currency could be deleted or unmapped from another panel.
Breaking changes
Two public contracts changed. Both are source-breaking for anyone who constructs these types directly; neither changes behaviour for existing installations.
ICurrencyServiceimplementationCurrencyServicetakes an additionalIContextAccessorconstructor parameter.IProductServicegainsTask<int> CountSharedProducts(string storeId). Any external implementation of the interface must add it.No data migration is required: the new
Store.PrimaryCurrencyIddeserializes to empty on existing documents and the fallback covers it. New localization resources ship inUpgrade/en_240.xml, picked up by the existing 2.4 resource migration.Testing
StaffStoreIdis one of them.Limited to storesunchecked, or mapped to more than one store), a confirmation dialog reports how many. Cancel it — nothing changes.Automated
Full solution builds. All 22 test projects pass (~3250 tests, 0 failures), including 21 new tests covering the fallback chain, shared-product counting, the confirmation flow, and the extended currency validations.
🤖 Generated with Claude Code