ARCH-001 Phase 22: TaxCategory controller/service consolidation (Admin/Store) - #822
Merged
KrzysztofPajak merged 5 commits intoSep 9, 2026
Merged
Conversation
…ation Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DjfBG3opo33qNHQYVbgH9Q
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DjfBG3opo33qNHQYVbgH9Q
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DjfBG3opo33qNHQYVbgH9Q
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DjfBG3opo33qNHQYVbgH9Q
…mments, dead using, StoreName test coverage) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DjfBG3opo33qNHQYVbgH9Q
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.
Resolves N/A (ARCH-001 initiative, no tracking issue)
Type: refactor
Issue
[[project_arch001_triple_admin_duplication]] (ARCH-001) —
Grand.Web.Admin/Grand.Web.Storelargely duplicate rather than share controller/service code.TaxControllerbundles three unrelated regions (Providers,Settings,Tax Categories); onlyTaxCategoryis a real duplicated CRUD entity, with the same action set implemented independently in both hosts.Solution
Consolidated only the
Tax Categoriesregion into a sharedBaseTaxCategoryController(Grand.Web.AdminShared), following the sameIAdminDataScope<TEntity>pattern used by 21 prior phases.TaxCategory : BaseEntityhas a flatStoreIdstring (empty = global), notIStoreLinkEntity, so this reuses the bespoke-scope pattern already established forOrder/EmailAccount(StoreTaxCategoryDataScope, mirroringStoreOrderDataScope) rather than the genericStoreAdminDataScope<TEntity>. Admin reuses the existing genericGlobalAdminDataScope<TaxCategory>. NewRoutedTaxCategoryDataScope(2-branch, Admin/Store, fails closed — no Vendor screen exists for Tax).Deliberately out of scope, untouched, still duplicated:
Providers/MarkAsPrimaryProvider/Settings(GET+POST) — not entity-shaped, nothing forIAdminDataScope<TEntity>to scope. Both hosts'Categories()GET action andCategories.cshtmlviews also stay separate — the real duplication here was in controller/scope logic, not markup (Admin's view has a store-picker/column Store's doesn't).Disclosed behavior changes
CategoryDeletenot-found response unified. Admin's original code threwArgumentExceptionon a not-found id; Store's returned a silent empty JSON. Both hosts now return the silent empty JSON — deliberate, since the action is grid-driven (id always comes from a row the grid just rendered) and the simpler behavior removes a stack-trace/500 surface for what was already functionally a no-op.CategoryUpdategained a real null-safety fix. The original code calledmodel.ToEntity(taxCategory)on a possibly-nulltaxCategorywith no not-found guard at all; the shared base now checkstaxCategory == nullfirst. Strict improvement, not a behavior regression.[PermissionAuthorizeAction]attributes (List/Edit/Create/Delete) — Admin's original methods had none (only Store did).PermissionActionrows are deny-only records, so this is a no-op for a normally-configured admin and only takes effect for a customer group with an explicit configured deny onTaxSettings.{Create,Edit,Delete}— same direction as every other consolidated controller and as Store already had. Flagged here per this initiative's practice of disclosing every authorization-surface delta, however narrow.Known, deliberately deferred (not this phase)
GetActiveStore()is hand-duplicated onto Admin'sTaxController(previously inherited transitively viaBaseAdminController, which this class no longer extends). The repo already has a registeredIAdminStoreService.GetActiveStore()equivalent, but it is not behavior-equivalent toBaseAdminController's version (missing an empty-storeId short-circuit, NREs on zero stores) — using the hand-duplicated copy was the correct behavior-preserving choice for a pure refactor phase. Follow-up filed: migrateTaxController/PaymentController/SettingController/MaintenanceControllerontoIAdminStoreServicetogether in a future phase, reconciling those two behavior deltas first.Breaking changes
None for normal operation. See "Disclosed behavior changes" above for the two narrow, deliberate exceptions (both net-neutral-or-safer).
Testing
dotnet build GrandNode.sln— 0 errors (70 pre-existing NU1902 advisory warnings, unrelated).dotnet test src/Tests/Grand.Web.Admin.Tests— 1333/1333 passed.dotnet test src/Tests/Grand.Web.Store.Tests— 120/120 passed.dotnet test src/Tests/Grand.Mapping.Tests— 234/234 passed.Grand.Web) + an existing dev MongoDB (grand20260825), via crafted antiforgery-tokened HTTP requests (browser automation was unavailable this session):Store/Tax/CategoryAdd) — persisted with the correctStoreId.CategoryUpdate/CategoryDeleteon store1's category — both denied (empty-JSON response, HTTP 200), confirmed via re-read that neither the name nor existence changed.CategoryDeleteon a nonexistent id returned HTTP 200 (confirms disclosed behavior change Label at local plugins #1 — no moreArgumentException).Providers/SettingsGET on both hosts returned 200 (regression check on the untouched, still-duplicated regions).Final whole-branch review (opus): Ready to merge: With fixes — 0 Critical, 2 Important (one fixed: missing
#nullable enable; one disclosure-only: item 3 above), 5 Minor (4 fixed: DI cleanup, cutover comments, dead using, addedStoreName-resolution test coverage; 1 ruled as correct-as-shipped: theGetActiveStore()deferral above). Fix wave scoped-re-reviewed clean, no new breakage.