Skip to content

Fix 404 when a store owner opens a shipping provider's configuration - #838

Merged
KrzysztofPajak merged 3 commits into
developfrom
feature/store-area-shipping-plugin-config
Sep 13, 2026
Merged

KrzysztofPajak merged 3 commits into
developfrom
feature/store-area-shipping-plugin-config

Conversation

@KrzysztofPajak

Copy link
Copy Markdown
Member

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 ConfigurationUrl is a relative url — Shipping.ByWeight returns "../ShippingByWeight/Configure". From /Admin/Shipping/Providers that resolves to /Admin/ShippingByWeight/Configure, which exists; from /Store/Shipping/Providers it 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/Store controllers; 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:

Plugin Per-store data Change
Shipping.ByWeight ShippingByWeightRecord.StoreId new Areas/Store controller + views
Shipping.ShippingPoint ShippingPoints.StoreId new Areas/Store controller + views
Shipping.FixedRateShipping none — rates live in system-wide setting keys link suppressed instead
  • IShippingByWeightService.GetAll gained a storeId filter (applied in the query, included in the cache key), mirroring TaxRateService.GetAllTaxRates. Filtering in the service keeps paging correct.
  • New Store-area controllers follow Tax.CountryStateZip/Areas/Store exactly: the store id always comes from StaffStoreId, 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.
  • ByWeightShippingSettings is 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.
  • Non-multi-store providers: rather than hardcoding a list, Grand.Web.Store/Extensions/StoreAreaConfiguration.Exists(provider) reflects over the plugin assembly (cached per assembly) for any [Area("Store")] controller. ShippingController.Providers blanks ConfigurationUrl when 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 call GetAll(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 the Shipping.ByWeight plugin; 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

  1. Have at least two stores configured, and a staff customer bound to store A (StaffStoreId) with the Manage Shipping Settings permission.
  2. The reported bug — sign in as that store owner, go to Store → Shipping → Providers and click Shipping by weight. It now opens the configuration screen instead of returning 404. Do the same for Shipping Point.
  3. Fixed rate is listed in the same grid as plain text, with no link — it is not configurable per store.
  4. On the by-weight screen, add a record. Confirm there is no store drop-down, then sign in to the main admin and open Configuration → Shipping → Providers → Shipping by weight: the new record shows store A in the Store column.
  5. In the main admin add a second record for store B (and one with store *). Back in store A's panel, neither appears in the grid.
  6. Still as store A's owner, try to reach store B's record directly: POST /Store/ShippingByWeight/RateDelete with store B's record id, and GET /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.
  7. On the by-weight screen change Display order and save. In the main admin, Configuration → Settings → All settings, confirm a shippingbyweightsettings.displayorder entry now exists scoped to store A and that the system-wide value is unchanged. Store B's storefront shipping rates are unaffected.
  8. Repeat steps 4–6 for Shipping Point (Store → Shipping → Providers → Shipping Point): create, confirm the grid shows only store A's points, and confirm editing/deleting another store's point is refused.
  9. Confirm the main admin screens for both plugins still behave as before, listing every store's records.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Bf2UUL1avH6UD5Mxxzu7UK

KrzysztofPajak and others added 3 commits September 13, 2026 10:52
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
Copilot AI lite review requested due to automatic review settings September 13, 2026 08:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

//zip
m.Zip = !string.IsNullOrEmpty(x.Zip) ? x.Zip : "*";

var htmlSb = new StringBuilder("<div>");
@KrzysztofPajak
KrzysztofPajak merged commit 4f8e654 into develop Sep 13, 2026
6 checks passed
@KrzysztofPajak
KrzysztofPajak deleted the feature/store-area-shipping-plugin-config branch September 13, 2026 09:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants