Skip to content

Feature/store portal discount management - #1

Open
DrShoker wants to merge 14 commits into
developfrom
feature/store-portal-discount-management
Open

Feature/store portal discount management#1
DrShoker wants to merge 14 commits into
developfrom
feature/store-portal-discount-management

Conversation

@DrShoker

Copy link
Copy Markdown
Owner

Title:
feat: Discount Management in Store Portal

Body:

Summary

Adds full Discount Management to the Store portal (Grand.Web.Store), following the same pattern as MessageTemplate and Page management (PRs grandnode#693, grandnode#694).

Store staff can now create and manage store-scoped discounts without needing Admin access.

Changes

New: Store Portal Discount Management

  • List view — two-tab Kendo grid: Store Discounts (owned exclusively by this store, editable) and Global Discounts (read-only)
  • Create / Edit / Delete — full CRUD with store ownership enforcement; global discounts open in read-only mode
  • Coupon Codes tab — inline-edit grid to add and delete coupon codes per discount
  • Products tab — assign/remove products with a store-scoped search popup
  • Categories tab — assign/remove categories with a search popup

Architecture

  • New DiscountController in Grand.Web.Store — inherits BaseStoreController, reuses the existing IDiscountViewModelService from Grand.Web.AdminShared (no new service class, no DI changes)
  • Store isolation enforced via CurrentStoreId = contextAccessor.WorkContext.CurrentCustomer.StaffStoreId
  • model.Stores = [CurrentStoreId] before every insert/update; AutoMapper sets LimitedToStores = true automatically
  • Per-product and per-category store ownership checked in assignment popups

Documentation

  • CLAUDE.md — project guide for build, test, architecture, and key directories
  • docs/guides/ — seven per-layer guides (core, business, web, modules, plugins, testing, Aspire)

Files Changed

File Change
Grand.Web.Store/Controllers/DiscountController.cs New
Grand.Web.Store/Areas/Store/Views/Discount/List.cshtml New
Grand.Web.Store/Areas/Store/Views/Discount/Create.cshtml New
Grand.Web.Store/Areas/Store/Views/Discount/Edit.cshtml New
Grand.Web.Store/Areas/Store/Views/Discount/Partials/_TabInfo.cshtml New
Grand.Web.Store/Areas/Store/Views/Discount/Partials/_TabCouponCodes.cshtml New

Files Changed

| Grand.Web.Store/Areas/Store/Views/Discount/Partials/_TabProducts.cshtml | New |
| Grand.Web.Store/Areas/Store/Views/Discount/Partials/_TabCategories.cshtml | New |
| Grand.Web.Store/Areas/Store/Views/Discount/ProductAddPopup.cshtml | New |
| Grand.Web.Store/Areas/Store/Views/Discount/CategoryAddPopup.cshtml | New |
| Grand.Web.Store/Areas/Store/Views/_ViewImports.cshtml | Updated |
| CLAUDE.md | New |
| docs/guides/*.md | New (7 files) |

Test Plan

  • Log in as a store staff user (StaffStoreId set)
  • Navigate to Store portal → Marketing → Discounts
  • Verify List loads with two tabs (Store / Global)
  • Create a discount → confirm it appears in the Store tab
  • Edit → save, save-continue, delete all work
  • Enable Requires Coupon Code → Coupon Codes tab appears; add and delete coupons
  • Set type to Assigned to SKUs → Products tab appears; add popup filters to current store
  • Set type to Assigned to Categories → Categories tab appears; add/remove categories
  • Global discount → opens read-only (no Save/Delete buttons)
  • Attempt to access a discount from another store directly via URL → redirected to List

Dmytro Shokov and others added 14 commits May 25, 2026 15:46
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…untController

- Fix 1: CategoryAddPopup POST now checks per-category store ownership
  before applying discounts, mirroring the ProductAddPopup pattern
- Fix 2: CouponCodeList, ProductList, CategoryList, CouponCodeInsert, and
  CouponCodeDelete now require LimitedToStores=true to prevent store users
  from enumerating sub-resources of global or multi-store discounts
- Fix 3: Add [HttpPost] to CouponCodeInsert and CouponCodeDelete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Create.cshtml and Edit.cshtml wrapper views for the Discount controller with:
- Proper model binding to DiscountModel
- IsReadOnly support for global discounts (hides Save/Delete buttons)
- Tabstrip structure with four tabs (Info, CouponCodes, Products, Categories)
- Delete confirmation tag helper for Edit view
- Added Grand.Web.AdminShared.Models.Discounts to _ViewImports.cshtml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add !discount.LimitedToStores guard to ProductAddPopup POST, ProductDelete, and CategoryDelete (Fix 1)
- Add coupon.DiscountId != discountId ownership check in CouponCodeDelete (Fix 2)
- Add storeId: CurrentStoreId to CategoryAddPopupList to prevent cross-store category leakage (Fix 3)
- Remove unused [FromServices] IProductService parameter from ProductAddPopup GET (Fix 4)
- Inject IDateTimeService and use discount.ToModel(dateTimeService) in Edit GET for correct date conversion (Fix 5)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add CLAUDE.md with architecture overview, build/test instructions, key
  directories, and a guide table pointing to per-layer docs
- Add docs/guides/ with seven guides covering core layer, business layer,
  web layer, modules, plugins, testing patterns, and Aspire orchestration
- Add docs/superpowers/plans/ with Store portal Discount Management
  implementation plan

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

1 participant