ARCH-001 Phase 24: shrink Admin CurrencyController/LanguageController - #825
Merged
KrzysztofPajak merged 1 commit intoSep 11, 2026
Merged
Conversation
Not a cross-host consolidation (Store's Currency/Language controllers implement a different feature - store assignment/default picker, not CRUD - so there is no real Admin/Store duplication to remove here). Moves business-rule validation and setting+cache-clear orchestration out of the two controllers and into the already-injected ICurrencyViewModelService/ILanguageViewModelService (already shared via Grand.Web.AdminShared): - MarkAsPrimaryExchangeRateCurrency/MarkAsPrimaryStoreCurrency: setting save + cache clear now live in the service; removes the controller's private ClearCache() helper and its now-unused ICacheBase dependency. - Edit(POST)'s 'last published currency/language' guard and Delete's 'can't delete primary/last published' guards are extracted as separate service methods (ValidateCurrencyUnpublish/ValidateCurrencyDelete, ValidateLanguageUnpublish/ValidateLanguageDelete) matching each original condition 1:1 - not merged into one shared method, since Edit's guard also checks !model.Published while Delete's doesn't. No behavior change for callers: same messages, same redirects. One disclosed side effect: Delete's primary/exchange-currency rejection no longer round-trips through a thrown GrandException, so it no longer triggers BaseController.Error(Exception)'s LogException call for this expected, validation-only outcome (aligns with .ai/standards/csharp-style.md's 'prefer result objects over exceptions for expected business failures'). New unit tests for the four extracted service methods in Grand.Web.Admin.Tests/Services (CurrencyViewModelServiceTests, LanguageViewModelServiceTests) - no test coverage existed for either controller/service before this change. Verified: dotnet build GrandNode.sln (0 errors); Grand.Web.Admin.Tests 1347/1347 green. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DjfBG3opo33qNHQYVbgH9Q
KrzysztofPajak
deleted the
arch001/phase24-currency-language-cleanup
branch
September 11, 2026 00:59
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: refactor
Issue
ARCH-001 (see repo memory) previously surveyed Currency/Language as a "config-shaped, lower-priority" candidate distinct from every other completed phase: Store's
CurrencyController/LanguageControllerimplement a genuinely different feature (per-store currency/language assignment + default picker) than Admin's full CRUD editor, so there is no real cross-host duplication to remove with the usualIAdminDataScope<TEntity>pattern.Fresh read of both Admin controllers found a smaller, real opportunity instead: business-rule validation and setting+cache-clear orchestration was living directly in the controllers even though both already inject a shared
ICurrencyViewModelService/ILanguageViewModelService(Grand.Web.AdminShared) that was the natural home for it.Solution
Moved into the already-injected view-model services, no new abstractions:
MarkAsPrimaryExchangeRateCurrency/MarkAsPrimaryStoreCurrency: setting-save + cache-clear now live inCurrencyViewModelService. RemovesCurrencyController's privateClearCache()helper and its now-unusedICacheBasedependency.Edit(POST)'s "last published currency/language" guard andDelete's "can't delete primary / last published" guards are extracted as separate service methods per entity (ValidateCurrencyUnpublish/ValidateCurrencyDelete,ValidateLanguageUnpublish/ValidateLanguageDelete) — deliberately not merged into one shared method, sinceEdit's guard is also conditioned on!model.PublishedwhileDelete's is not; merging would have re-derived a similar-looking but wrong condition (a mistake this initiative has hit before on other phases).No behavior change for callers — same messages, same redirects.
One disclosed side effect:
Delete's primary/exchange-currency rejection no longer round-trips through a thrownGrandException, so it no longer triggersBaseController.Error(Exception)'sLogExceptioncall for this expected, validation-only outcome. This aligns with.ai/standards/csharp-style.md's "prefer result objects over exceptions for expected business failures" and removes ERROR-level log noise for a routine user action, but is a real (intentional) behavior delta worth a second pair of eyes.Store's
CurrencyController/LanguageControllerare untouched — out of scope, as explained above.Breaking changes
None. Public HTTP surface, messages, and redirects are unchanged.
ICurrencyViewModelService/ILanguageViewModelServicegained new methods (additive); no existing interface members changed signature.Testing
dotnet build GrandNode.sln— 0 errors, 0 new warnings.src/Tests/Grand.Web.Admin.Tests/Services/CurrencyViewModelServiceTests.cs,LanguageViewModelServiceTests.cs— happy path + every extracted guard condition (primary currency, exchange-rate currency, last-published, still-published short-circuit).dotnet test src/Tests/Grand.Web.Admin.Tests/Grand.Web.Admin.Tests.csproj— 1347/1347 green (includes the 14 new tests).MarkAsPrimaryExchangeRateCurrency/MarkAsPrimaryStoreCurrency: set Euro as primary (both flags), grid reflected it immediately (setting + cache-clear both took effect), reverted to US Dollar.ValidateCurrencyDelete: blocked deleting the primary store currency ("The primary store currency can't be deleted.") and the primary exchange-rate currency ("The primary exchange rate currency can't be deleted.") — exact resource strings, entities confirmed untouched. Success path also verified: created and cleanly deleted a throwaway currency.ValidateCurrencyUnpublish: unpublishing a non-last currency succeeded; unpublishing the resulting last-published currency was correctly blocked ("At least one published currency is required."), with the entity'sPublishedstate confirmed unchanged (rejected, not partially applied).ValidateLanguageDelete/ValidateLanguageUnpublish: this dev DB has exactly one language (English) — verified the most important edge case directly, both Delete and Edit(POST) unpublish blocked on the sole published language, same resource string as Currency's. Success path verified via a throwaway second language (create → unpublish English while throwaway stays published → succeeds → delete throwaway → succeeds).DisplayLocalechanged from""tonullafter a test round-trip — a pre-existing, unrelated Mongo/mapping quirk (not touched by this change), functionally identical in the UI (both render as "Select locale" unselected).🤖 Generated with Claude Code