Skip to content

feat: Email Accounts per Store#680

Draft
Copilot wants to merge 5 commits intodevelopfrom
copilot/add-email-functionality-per-store
Draft

feat: Email Accounts per Store#680
Copilot wants to merge 5 commits intodevelopfrom
copilot/add-email-functionality-per-store

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 26, 2026

  • Add StoreId property to EmailAccount domain entity
  • Add GetEmailAccountsByStore(storeId) to IEmailAccountService and implementation
  • Add EMAILACCOUNT_BY_STORE_KEY cache key (removed — filter from GetAllEmailAccounts cache instead to prevent dirty data)
  • Update MessageProviderService.GetEmailAccountOfMessageTemplate to prefer store-specific account as fallback
  • Add StoreId to EmailAccountModel in AdminShared
  • Update EmailAccountProfile mapper to include StoreId
  • Update EmailAccountViewModelService to be async and populate available stores
  • Update Admin EmailAccountController for async PrepareEmailAccountModel
  • Add EmailAccountController to Grand.Web.Store with CRUD actions (store-isolated)
  • Add views (List, Create, Edit, Partials/CreateOrUpdate) in Grand.Web.Store/Areas/Store/Views/EmailAccount/
  • Add Grand.Web.AdminShared.Models.Messages to Store area _ViewImports.cshtml
  • Add new resource strings to DefaultLanguage.xml
  • Replace hardcoded strings with localized resource strings
  • Fix dirty data: GetEmailAccountsByStore now filters the GetAllEmailAccounts cache in-memory

Copilot AI and others added 2 commits April 26, 2026 18:47
- Add StoreId property to EmailAccount domain entity
- Add GetEmailAccountsByStore(storeId) method to IEmailAccountService and EmailAccountService
- Add EMAILACCOUNT_BY_STORE_KEY cache key
- Update MessageProviderService to use store-specific email account as fallback
- Update EmailAccountModel with StoreId and AvailableStores
- Update EmailAccountProfile mapper to include StoreId
- Update EmailAccountViewModelService to be async and populate available stores
- Add EmailAccountController to Grand.Web.Store with CRUD + store isolation
- Add EmailAccount views (List, Create, Edit, Partials/CreateOrUpdate) in Store area
- Add Messages namespace to Store _ViewImports.cshtml
- Update Admin EmailAccountController for async PrepareEmailAccountModel

Agent-Logs-Url: https://github.com/grandnode/grandnode2/sessions/094d04cc-7d75-4a00-af14-f5bb77f05736

Co-authored-by: KrzysztofPajak <16772986+KrzysztofPajak@users.noreply.github.com>
Agent-Logs-Url: https://github.com/grandnode/grandnode2/sessions/094d04cc-7d75-4a00-af14-f5bb77f05736

Co-authored-by: KrzysztofPajak <16772986+KrzysztofPajak@users.noreply.github.com>
Comment thread src/Web/Grand.Web.Store/Controllers/EmailAccountController.cs Fixed
Comment thread src/Web/Grand.Web.Store/Controllers/EmailAccountController.cs Fixed
KrzysztofPajak and others added 2 commits April 27, 2026 05:51
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
if (string.IsNullOrEmpty(storeId))
return await GetAllEmailAccounts();

var key = string.Format(CacheKey.EMAILACCOUNT_BY_STORE_KEY, storeId);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@copilot when we update email account the data is dirty

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in d7be617. The separate EMAILACCOUNT_BY_STORE_KEY cache was the root cause — it held its own object instances that could become stale/mutated independently when an account's StoreId changed during an update. GetEmailAccountsByStore now filters in-memory from the already correctly-invalidated GetAllEmailAccounts cache, so there's only a single source of truth. The unused EMAILACCOUNT_BY_STORE_KEY constant has also been removed.


protected virtual async Task<EmailAccount> GetEmailAccountOfMessageTemplate(MessageTemplate messageTemplate,
string languageId)
string languageId, string storeId = "")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@copilot Does MessageTemplate contain storeId?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

MessageTemplate does not have a single StoreId — it uses LimitedToStores (bool) + Stores (list of store IDs), which is the standard GrandNode store-scoping pattern for multi-store visibility.

The storeId in GetEmailAccountOfMessageTemplate comes from the call site (e.g. the order-placed notification passes the order's StoreId). It's already threaded correctly through GetMessageTemplate(messageTemplateName, storeId) for the template lookup, and is now reused for the store-specific email account fallback.

…vent dirty data

Instead of caching per-store results separately (which could hold stale/mutated
objects when an account's StoreId changes), filter directly from the already
correctly-invalidated GetAllEmailAccounts cache. Also removes the now-unused
EMAILACCOUNT_BY_STORE_KEY constant.

Agent-Logs-Url: https://github.com/grandnode/grandnode2/sessions/5697bb0d-b979-44be-8f1a-2f8c98ad8b5e

Co-authored-by: KrzysztofPajak <16772986+KrzysztofPajak@users.noreply.github.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.

2 participants