Skip to content

feat: Email Accounts per Store#680

Open
Copilot wants to merge 8 commits intodevelopfrom
copilot/add-email-functionality-per-store
Open

feat: Email Accounts per Store#680
Copilot wants to merge 8 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
  • Extend GetAllEmailAccounts(string storeId = "") with optional store filter, replacing the separate GetEmailAccountsByStore method
  • 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 in Grand.Web.Store/Areas/Store/Views/EmailAccount/
  • Add new resource strings to DefaultLanguage.xml
  • Fix dirty data: single GetAllEmailAccounts cache is the only source of truth
  • Remove PrepareAvailableStores/PrepareEmailAccountModel calls from Store controller — Store area uses a hidden StoreId field locked to CurrentStoreId
  • Simplify GetEmailAccountOfMessageTemplate — remove storeId parameter; resolution order is template account ID → default account from settings → first available account

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>
Comment thread src/Business/Grand.Business.Messages/Services/EmailAccountService.cs Outdated
Comment thread src/Business/Grand.Business.Messages/Services/MessageProviderService.cs Outdated
…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>
@KrzysztofPajak
Copy link
Copy Markdown
Member

@copilot Grand.Web.Store should not see all available store

…lableStores/PrepareEmailAccountModel calls

Agent-Logs-Url: https://github.com/grandnode/grandnode2/sessions/d1fe358f-d64b-47c9-af25-cea72fae9de0

Co-authored-by: KrzysztofPajak <16772986+KrzysztofPajak@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 27, 2026

@copilot Grand.Web.Store should not see all available store

Fixed in 84fdff0. The Store controller was calling PrepareEmailAccountModel() and PrepareAvailableStores() which both invoke GetAllStores() internally. Since the Store view already renders StoreId as a hidden field (not a dropdown) and the value is always locked to CurrentStoreId, all those calls have been removed. The Create GET action now builds the model directly (just sets Port = 25 and StoreId = CurrentStoreId) without touching the store service at all.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • ccsca2021.crl.certum.pl
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj -v q (dns block)
  • ccsca2021.ocsp-certum.com
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj -v q (dns block)
  • crl.certum.pl
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj -v q (dns block)
  • subca.ocsp-certum.com
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj -v q (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@KrzysztofPajak
Copy link
Copy Markdown
Member

@copilot check my comments

…tEmailAccountsByStore, pass store?.Id to GetEmailAccountOfMessageTemplate callers

Agent-Logs-Url: https://github.com/grandnode/grandnode2/sessions/dba495a6-918d-4de2-8bc5-ae0409bfb8ff

Co-authored-by: KrzysztofPajak <16772986+KrzysztofPajak@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 29, 2026

@copilot check my comments

Both comments addressed in 5994a60:

  1. GetAllEmailAccounts extended with optional storeIdGetEmailAccountsByStore has been removed. IEmailAccountService.GetAllEmailAccounts(string storeId = "") now handles both cases: no argument returns all accounts, a non-empty storeId filters in-memory from the same cache entry.

  2. storeId = "" never used — all 34 call sites of GetEmailAccountOfMessageTemplate now pass store?.Id as the third argument, so the store-specific email account fallback is actually reachable.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • admin\@admin.com
    • Triggering command: /usr/share/dotnet/dotnet /usr/share/dotnet/dotnet exec --runtimeconfig /home/REDACTED/work/grandnode2/grandnode2/src/Tests/Grand.Business.Messages.Tests/bin/Debug/net10.0/Grand.Business.Messages.Tests.runtimeconfig.json --depsfile /home/REDACTED/work/grandnode2/grandnode2/src/Tests/Grand.Business.Messages.Tests/bin/Debug/net10.0/Grand.Business.Messages.Tests.deps.json /home/REDACTED/work/grandnode2/grandnode2/src/Tests/Grand.Business.Messages.Tests/bin/Debug/net10.0/testhost.dll --port 38021 --endpoint 127.0.0.1:038021 --role client --parentprocessid 5147 --telemetryoptedin false (dns block)
    • Triggering command: /usr/share/dotnet/dotnet /usr/share/dotnet/dotnet exec --runtimeconfig /home/REDACTED/work/grandnode2/grandnode2/src/Tests/Grand.Business.Messages.Tests/bin/Debug/net10.0/Grand.Business.Messages.Tests.runtimeconfig.json --depsfile /home/REDACTED/work/grandnode2/grandnode2/src/Tests/Grand.Business.Messages.Tests/bin/Debug/net10.0/Grand.Business.Messages.Tests.deps.json /home/REDACTED/work/grandnode2/grandnode2/src/Tests/Grand.Business.Messages.Tests/bin/Debug/net10.0/testhost.dll --port 37459 --endpoint 127.0.0.1:037459 --role client --parentprocessid 5397 --telemetryoptedin false (dns block)
  • ccsca2021.crl.certum.pl
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj -v q (dns block)
  • ccsca2021.ocsp-certum.com
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj -v q (dns block)
  • crl.certum.pl
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj -v q (dns block)
  • subca.ocsp-certum.com
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj -v q (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@KrzysztofPajak
Copy link
Copy Markdown
Member

@copilot GetEmailAccountOfMessageTemplate remove param storeId

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 30, 2026

@copilot GetEmailAccountOfMessageTemplate remove param storeId

Done in 50dfcbc. GetEmailAccountOfMessageTemplate now takes only (MessageTemplate, languageId) — the storeId parameter and the store-specific fallback branch have been removed. The resolution order is: template's email account ID → default account from settings → first available account.

@KrzysztofPajak KrzysztofPajak marked this pull request as ready for review May 2, 2026 17:43
Copilot AI review requested due to automatic review settings May 2, 2026 17:43
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds store-scoped email accounts across domain/service layers and exposes CRUD management in the Store area UI, while updating Admin UI to support selecting a store scope for each email account.

Changes:

  • Introduces StoreId on EmailAccount and extends GetAllEmailAccounts with an optional store filter.
  • Updates AdminShared model/mapping and view-model service to support store selection (available stores list).
  • Adds Store-area EmailAccountController plus corresponding views for store-isolated management.

Reviewed changes

Copilot reviewed 17 out of 18 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/Web/Grand.Web.Store/Controllers/EmailAccountController.cs New Store-area CRUD controller for email accounts filtered to current staff store
src/Web/Grand.Web.Store/Areas/Store/Views/_ViewImports.cshtml Adds messages model namespace for Store-area views
src/Web/Grand.Web.Store/Areas/Store/Views/EmailAccount/Partials/CreateOrUpdate.cshtml New create/edit partial for Store area (hidden StoreId)
src/Web/Grand.Web.Store/Areas/Store/Views/EmailAccount/List.cshtml New Kendo grid list view for Store area
src/Web/Grand.Web.Store/Areas/Store/Views/EmailAccount/Edit.cshtml New Store-area edit view
src/Web/Grand.Web.Store/Areas/Store/Views/EmailAccount/Create.cshtml New Store-area create view
src/Web/Grand.Web.AdminShared/Services/EmailAccountViewModelService.cs Makes model preparation async and populates available stores for selection
src/Web/Grand.Web.AdminShared/Models/Messages/EmailAccountModel.cs Adds StoreId + AvailableStores to the model
src/Web/Grand.Web.AdminShared/Mapper/EmailAccountProfile.cs Updates mapping to ignore AvailableStores
src/Web/Grand.Web.AdminShared/Interfaces/IEmailAccountViewModelService.cs Makes PrepareEmailAccountModel async; adds PrepareAvailableStores
src/Web/Grand.Web.Admin/Controllers/EmailAccountController.cs Uses async model preparation and populates stores on Edit GET
src/Web/Grand.Web.Admin/Areas/Admin/Views/EmailAccount/Partials/CreateOrUpdate.cshtml Adds Store selector dropdown to Admin email account editor
src/Tests/Grand.Business.Messages.Tests/Services/MessageProviderServiceTest.cs Updates mock setup for the new GetAllEmailAccounts signature
src/Core/Grand.Domain/Messages/EmailAccount.cs Adds StoreId to the domain entity
src/Business/Grand.Business.Messages/Services/MessageProviderService.cs Simplifies email account resolution for message templates
src/Business/Grand.Business.Messages/Services/EmailAccountService.cs Adds optional store filter to GetAllEmailAccounts
src/Business/Grand.Business.Core/Interfaces/Messages/IEmailAccountService.cs Updates interface signature/documentation for store-filtered retrieval

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +150 to 154
if (string.IsNullOrEmpty(storeId))
return accounts;

return accounts.Where(ea => ea.StoreId == storeId).ToList();
}
Comment on lines +70 to +80
private async Task PopulateAvailableStores(EmailAccountModel model)
{
model.AvailableStores.Add(new SelectListItem {
Value = "",
Text = _translationService.GetResource("Admin.Settings.StoreScope.AllStores")
});
foreach (var store in await _storeService.GetAllStores())
model.AvailableStores.Add(new SelectListItem {
Value = store.Id,
Text = store.Name
});
Comment on lines +39 to +41
var gridModel = new DataSourceResult {
Data = emailAccountModels,
Total = emailAccountModels.Count
/// Gets or sets the store identifier this email account belongs to.
/// An empty string means this is a global/shared email account.
/// </summary>
public string StoreId { get; set; }
Comment thread src/Web/Grand.Web.Store/Areas/Store/Views/EmailAccount/List.cshtml
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.

3 participants