diff --git a/backend/Tnsu.ApprovalRoute.Application/RequestTypeAccesses/RequestTypeAccessDto.cs b/backend/Tnsu.ApprovalRoute.Application/RequestTypeAccesses/RequestTypeAccessDto.cs
index 3cb7950..30c432c 100644
--- a/backend/Tnsu.ApprovalRoute.Application/RequestTypeAccesses/RequestTypeAccessDto.cs
+++ b/backend/Tnsu.ApprovalRoute.Application/RequestTypeAccesses/RequestTypeAccessDto.cs
@@ -1,5 +1,18 @@
+using Tnsu.ApprovalRoute.Application.ListPartitioning;
+
namespace Tnsu.ApprovalRoute.Application.RequestTypeAccesses;
-public sealed record RequestTypeAccessDto(int Id, int RequestTypeId, string Email);
+///
+/// Вся серия SharePoint-списков данных (архив + текущая партиция) типа заявки —
+/// именно на них нужно выдать email'у право чтения, иначе поиск по порталу
+/// продолжит отдавать только его собственные заявки: доступ здесь настраивает
+/// видимость в UI approval-route, но не права SharePoint, которые остаются
+/// отдельным ручным шагом.
+///
+public sealed record RequestTypeAccessDto(
+ int Id,
+ int RequestTypeId,
+ string Email,
+ IReadOnlyList SharePointLists);
public sealed record CreateRequestTypeAccessRequest(int RequestTypeId, string Email);
diff --git a/backend/Tnsu.ApprovalRoute.Application/RequestTypeAccesses/RequestTypeAccessService.cs b/backend/Tnsu.ApprovalRoute.Application/RequestTypeAccesses/RequestTypeAccessService.cs
index 6de3477..029ce54 100644
--- a/backend/Tnsu.ApprovalRoute.Application/RequestTypeAccesses/RequestTypeAccessService.cs
+++ b/backend/Tnsu.ApprovalRoute.Application/RequestTypeAccesses/RequestTypeAccessService.cs
@@ -3,26 +3,53 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Tnsu.ApprovalRoute.Application.Common.Errors;
using Tnsu.ApprovalRoute.Application.Common.Interfaces;
+using Tnsu.ApprovalRoute.Application.ListPartitioning;
using Tnsu.ApprovalRoute.Domain.Entities;
using Tnsu.ApprovalRoute.Infrastructure.Persistence;
namespace Tnsu.ApprovalRoute.Application.RequestTypeAccesses;
public sealed class RequestTypeAccessService(
- ApprovalRouteDbContext db, ICurrentUser currentUser, ILogger logger)
+ ApprovalRouteDbContext db,
+ ListPartitionService partitions,
+ ICurrentUser currentUser,
+ ILogger logger)
{
public async Task>> ListAsync(
int? requestTypeId, CancellationToken cancellationToken)
{
- var items = await db.RequestTypeAccesses.AsNoTracking()
+ var entities = await db.RequestTypeAccesses.AsNoTracking()
.Where(x => requestTypeId == null || x.RequestTypeId == requestTypeId)
.OrderBy(x => x.RequestTypeId).ThenBy(x => x.Email)
- .Select(x => new RequestTypeAccessDto(x.Id, x.RequestTypeId, x.Email))
.ToListAsync(cancellationToken);
+ // Партиции запрашиваются РАЗ на тип заявки, а не на строку — иначе список
+ // из N email по одному типу заново обходил бы SharePoint N раз подряд.
+ var listsByType = new Dictionary>();
+ var items = new List(entities.Count);
+ foreach (var e in entities)
+ {
+ var lists = await GetListsCachedAsync(listsByType, e.RequestTypeId, cancellationToken);
+ items.Add(new RequestTypeAccessDto(e.Id, e.RequestTypeId, e.Email, lists));
+ }
+
return Result.Ok>(items);
}
+ private async Task> GetListsCachedAsync(
+ Dictionary> cache,
+ int requestTypeId,
+ CancellationToken cancellationToken)
+ {
+ if (cache.TryGetValue(requestTypeId, out var cached))
+ return cached;
+
+ var result = await partitions.ListAsync(requestTypeId, cancellationToken);
+ var lists = result.IsSuccess ? result.Value : Array.Empty();
+ cache[requestTypeId] = lists;
+ return lists;
+ }
+
/// Список email'ов с доступом к типу заявок — дёргает Power Automate.
public async Task>> ListEmailsAsync(int requestTypeId, CancellationToken cancellationToken)
{
@@ -58,7 +85,9 @@ public sealed class RequestTypeAccessService(
"Доступ к типу заявки {RequestTypeId} выдан {Email} пользователем {Author}",
entity.RequestTypeId, entity.Email, currentUser.Email);
- return Result.Ok(new RequestTypeAccessDto(entity.Id, entity.RequestTypeId, entity.Email));
+ var listsResult = await partitions.ListAsync(entity.RequestTypeId, cancellationToken);
+ var lists = listsResult.IsSuccess ? listsResult.Value : Array.Empty();
+ return Result.Ok(new RequestTypeAccessDto(entity.Id, entity.RequestTypeId, entity.Email, lists));
}
public async Task DeleteAsync(int id, CancellationToken cancellationToken)
diff --git a/frontend/src/webparts/approvalAdmin/components/screens/RequestTypeAccessScreen.tsx b/frontend/src/webparts/approvalAdmin/components/screens/RequestTypeAccessScreen.tsx
index 64f628d..ab74268 100644
--- a/frontend/src/webparts/approvalAdmin/components/screens/RequestTypeAccessScreen.tsx
+++ b/frontend/src/webparts/approvalAdmin/components/screens/RequestTypeAccessScreen.tsx
@@ -45,6 +45,14 @@ export const RequestTypeAccessScreen: React.FC =
const requestTypeName = (id: number): string => requestTypes.items.find(rt => rt.id === id)?.name ?? `#${id}`;
+ // Список SharePoint нужно вручную открыть на чтение этому email — иначе доступ
+ // здесь настраивает только видимость в UI approval-route, но не в самом
+ // SharePoint (поиск по порталу отфильтрует чужие заявки правами списка).
+ const formatSharePointLists = (item: RequestTypeAccess): string =>
+ item.sharePointLists.length === 0
+ ? texts.common.empty
+ : item.sharePointLists.map(l => l.title ?? l.listId).join(', ');
+
const openCreate = (): void => {
setRequestTypeId('');
setEmail('');
@@ -86,6 +94,7 @@ export const RequestTypeAccessScreen: React.FC =
columns={[
{ key: 'requestType', header: texts.requestTypeAccess.columnRequestType, render: r => {requestTypeName(r.requestTypeId)} },
{ key: 'email', header: texts.requestTypeAccess.columnEmail, render: r => r.email },
+ { key: 'sharePointLists', header: texts.requestTypeAccess.columnSharePointLists, render: formatSharePointLists },
{
key: 'actions',
header: '',
diff --git a/frontend/src/webparts/approvalAdmin/models/requestTypeAccess.ts b/frontend/src/webparts/approvalAdmin/models/requestTypeAccess.ts
index 8178b6e..dfacc02 100644
--- a/frontend/src/webparts/approvalAdmin/models/requestTypeAccess.ts
+++ b/frontend/src/webparts/approvalAdmin/models/requestTypeAccess.ts
@@ -1,9 +1,17 @@
// DTO эндпоинта api/request-type-access (Application/RequestTypeAccesses/*.cs).
+import { RequestListPartition } from './requestTypeLists';
export interface RequestTypeAccess {
id: number;
requestTypeId: number;
email: string;
+ /**
+ * Вся серия SharePoint-списков данных типа заявки (архив + текущая партиция) —
+ * именно на них нужно вручную выдать email'у право чтения, иначе поиск по
+ * порталу продолжит отдавать только его собственные заявки: этот реестр
+ * настраивает видимость в UI approval-route, а не права SharePoint.
+ */
+ sharePointLists: RequestListPartition[];
}
export interface CreateRequestTypeAccessInput {
diff --git a/frontend/src/webparts/approvalAdmin/texts.ts b/frontend/src/webparts/approvalAdmin/texts.ts
index b638be3..7479a73 100644
--- a/frontend/src/webparts/approvalAdmin/texts.ts
+++ b/frontend/src/webparts/approvalAdmin/texts.ts
@@ -187,6 +187,7 @@ export const texts = {
emptyList: 'Пока никому не выдан доступ ни к одному типу заявок.',
columnRequestType: 'Тип заявки',
columnEmail: 'Сотрудник',
+ columnSharePointLists: 'Списки SharePoint (выдать доступ вручную)',
createTitle: 'Выдать доступ',
requestTypeLabel: 'Тип заявки',
requestTypePlaceholder: 'Выберите тип заявки',