Fix 404 when a store owner opens a shipping provider's configuration - #838
Merged
KrzysztofPajak merged 3 commits intoSep 13, 2026
Merged
Conversation
The store-owner panel needs to list only the rates belonging to its own store, and doing that in the controller would page over every store's records first. Filtering in the service keeps paging correct and matches how TaxRateService.GetAllTaxRates already scopes its query. The store id joins the cache key so a per-store read cannot serve a previously cached all-stores page. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bf2UUL1avH6UD5Mxxzu7UK
A provider's ConfigurationUrl is relative, so the same value resolves
under /Admin/ for the main admin and under /Store/ for the store-owner
panel. Both shipping plugins only shipped an [Area("Admin")] controller,
so following the link from Store/Shipping/Providers hit a route that does
not exist and returned 404.
Both plugins store their data per store already, so the screens are
genuinely usable by a store owner - they just had nowhere to live. The
Store controllers mirror Tax.CountryStateZip's: the store id always comes
from StaffStoreId rather than the posted model, and every read, edit and
delete is confined to that store, so a store owner can neither see nor
change another store's rates. The by-weight plugin settings are written
as a per-store override so one owner cannot change the others' behaviour.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bf2UUL1avH6UD5Mxxzu7UK
Not every shipping provider can be configured per store - the fixed rate
plugin keeps its rates in system-wide setting keys, so it has no Store
area controller and never will. Linking to its relative ConfigurationUrl
from the store-owner panel produced the same 404 as the plugins that were
just fixed.
Rather than hardcoding which providers are multi-store, ask the plugin
assembly whether it exposes an [Area("Store")] controller at all; a
third-party provider then gets the right answer without changes here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bf2UUL1avH6UD5Mxxzu7UK
KrzysztofPajak
deleted the
feature/store-area-shipping-plugin-config
branch
September 13, 2026 09:23
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: bugfix
Issue
A store owner who opens Store → Shipping → Providers and clicks a provider (e.g. "Shipping by weight") gets a 404 Page not found.
A provider's
ConfigurationUrlis a relative url —Shipping.ByWeightreturns"../ShippingByWeight/Configure". From/Admin/Shipping/Providersthat resolves to/Admin/ShippingByWeight/Configure, which exists; from/Store/Shipping/Providersit resolves to/Store/ShippingByWeight/Configure, and the plugin's only controller is[Area("Admin")]. The Store grid (Areas/Store/Views/Shipping/Providers.cshtml) was copied from the Admin view and renders the anchor unconditionally, so every shipping provider was a dead link.The Tax plugins already had
Areas/Storecontrollers; the Shipping plugins were missed.Reproduce: sign in as a store owner → Store → Shipping → Providers → click "Shipping by weight" → 404.
Solution
Per-plugin, driven by whether the plugin actually supports multi-store:
Shipping.ByWeightShippingByWeightRecord.StoreIdAreas/Storecontroller + viewsShipping.ShippingPointShippingPoints.StoreIdAreas/Storecontroller + viewsShipping.FixedRateShippingIShippingByWeightService.GetAllgained astoreIdfilter (applied in the query, included in the cache key), mirroringTaxRateService.GetAllTaxRates. Filtering in the service keeps paging correct.Tax.CountryStateZip/Areas/Storeexactly: the store id always comes fromStaffStoreId, never from the posted model; reads are filtered to that store and edit/delete/open are guarded with an ownership check, so a store owner can neither see nor modify another store's records. No store selector is offered. Warehouses are limited to the store's own plus the globally shared ones; shipping methods are filtered by store.ByWeightShippingSettingsis loaded and saved as a per-store override in the Store area, so one owner cannot change behaviour for the others. Main admin keeps writing the system-wide value.Grand.Web.Store/Extensions/StoreAreaConfiguration.Exists(provider)reflects over the plugin assembly (cached per assembly) for any[Area("Store")]controller.ShippingController.ProvidersblanksConfigurationUrlwhen there is none, and the grid renders plain text instead of a dead link. A third-party provider gets the right answer with no change here.No new localization resources, settings or permissions — every string and key reused already exists.
Breaking changes
One, source-level only:
IShippingByWeightService.GetAll(int pageIndex, int pageSize)→GetAll(string storeId = "", int pageIndex = 0, int pageSize = int.MaxValue). The new parameter is leading, so an existing positional callGetAll(0, 10)no longer compiles. The default""preserves the previous all-stores behaviour, and the in-repo caller was updated. This interface is internal to theShipping.ByWeightplugin; only a fork that reimplements or calls it directly is affected.No database, schema or settings-key changes. Existing records and settings are read exactly as before.
Testing
StaffStoreId) with the Manage Shipping Settings permission.*). Back in store A's panel, neither appears in the grid.POST /Store/ShippingByWeight/RateDeletewith store B's record id, andGET /Store/ShippingByWeight/EditPopup?id=<store B's id>. The delete is a no-op and the edit redirects to Configure; the record is untouched in the main admin.Configuration → Settings → All settings, confirm ashippingbyweightsettings.displayorderentry now exists scoped to store A and that the system-wide value is unchanged. Store B's storefront shipping rates are unaffected.🤖 Generated with Claude Code
https://claude.ai/code/session_01Bf2UUL1avH6UD5Mxxzu7UK