Feature/routes export #29
@@ -24,7 +24,6 @@ public sealed class RouteExportService(
|
|||||||
{
|
{
|
||||||
private const string Empty = "—";
|
private const string Empty = "—";
|
||||||
private const string Always = "Всегда";
|
private const string Always = "Всегда";
|
||||||
private const string AnyOrganization = "Любая";
|
|
||||||
private const string AnyCompany = "любая компания";
|
private const string AnyCompany = "любая компания";
|
||||||
private const string AnyDepartment = "любой отдел";
|
private const string AnyDepartment = "любой отдел";
|
||||||
private const string ByRequest = "Определяется по заявке";
|
private const string ByRequest = "Определяется по заявке";
|
||||||
@@ -112,12 +111,9 @@ public sealed class RouteExportService(
|
|||||||
var sections = routes
|
var sections = routes
|
||||||
.Select(route =>
|
.Select(route =>
|
||||||
{
|
{
|
||||||
var organization = route.CompanyId is { } companyId
|
|
||||||
? directory.CompanyName(companyId)
|
|
||||||
: AnyOrganization;
|
|
||||||
return new RouteExportSection(
|
return new RouteExportSection(
|
||||||
routes.Count < 2 ? null : RouteTitle(route, directory),
|
routes.Count < 2 ? null : RouteTitle(route, directory),
|
||||||
BuildGroups(route.Steps, organization, directory));
|
BuildGroups(route.Steps, directory));
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
@@ -131,7 +127,7 @@ public sealed class RouteExportService(
|
|||||||
return $"Маршрут: {company} · {department}{(route.ByProject ? " · по проекту" : string.Empty)}";
|
return $"Маршрут: {company} · {department}{(route.ByProject ? " · по проекту" : string.Empty)}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<RouteExportGroup> BuildGroups(IEnumerable<TemplateRouteStep> steps, string organization, PortalDirectory directory)
|
private List<RouteExportGroup> BuildGroups(IEnumerable<TemplateRouteStep> steps, PortalDirectory directory)
|
||||||
{
|
{
|
||||||
var ordered = steps.OrderBy(s => s.OrderNo).ToList();
|
var ordered = steps.OrderBy(s => s.OrderNo).ToList();
|
||||||
var approvers = ordered.Where(s => s.Status != RouteStepStatus.Execution).ToList();
|
var approvers = ordered.Where(s => s.Status != RouteStepStatus.Execution).ToList();
|
||||||
@@ -139,22 +135,22 @@ public sealed class RouteExportService(
|
|||||||
|
|
||||||
return new[]
|
return new[]
|
||||||
{
|
{
|
||||||
new RouteExportGroup("Согласующие", BuildRows(approvers, organization, directory)),
|
new RouteExportGroup("Согласующие", BuildRows(approvers, directory)),
|
||||||
new RouteExportGroup("Исполнители", BuildRows(executors, organization, directory))
|
new RouteExportGroup("Исполнители", BuildRows(executors, directory))
|
||||||
}
|
}
|
||||||
.Where(g => g.Rows.Count > 0)
|
.Where(g => g.Rows.Count > 0)
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Параллельные шаги (равный OrderNo) получают один и тот же номер.
|
// Параллельные шаги (равный OrderNo) получают один и тот же номер.
|
||||||
private List<RouteExportRow> BuildRows(IReadOnlyList<TemplateRouteStep> steps, string organization, PortalDirectory directory)
|
private List<RouteExportRow> BuildRows(IReadOnlyList<TemplateRouteStep> steps, PortalDirectory directory)
|
||||||
{
|
{
|
||||||
var orderNos = steps.Select(s => s.OrderNo).Distinct().OrderBy(n => n).ToList();
|
var orderNos = steps.Select(s => s.OrderNo).Distinct().OrderBy(n => n).ToList();
|
||||||
|
|
||||||
return steps
|
return steps
|
||||||
.Select(step =>
|
.Select(step =>
|
||||||
{
|
{
|
||||||
var (role, executor) = DescribeRoleAndExecutor(step, directory);
|
var (role, executor, organization) = DescribeRoleAndExecutor(step, directory);
|
||||||
return new RouteExportRow(
|
return new RouteExportRow(
|
||||||
orderNos.IndexOf(step.OrderNo) + 1,
|
orderNos.IndexOf(step.OrderNo) + 1,
|
||||||
role,
|
role,
|
||||||
@@ -169,19 +165,22 @@ public sealed class RouteExportService(
|
|||||||
// «Роль» — должность или тип руководителя, «исполнитель» — конкретный человек,
|
// «Роль» — должность или тип руководителя, «исполнитель» — конкретный человек,
|
||||||
// если он известен уже при настройке маршрута. Директор Tansu и Управляющий
|
// если он известен уже при настройке маршрута. Директор Tansu и Управляющий
|
||||||
// директор Tansu — тоже конкретные люди (их email лежит в настройках).
|
// директор Tansu — тоже конкретные люди (их email лежит в настройках).
|
||||||
private (string Role, string Executor) DescribeRoleAndExecutor(TemplateRouteStep step, PortalDirectory directory)
|
private (string Role, string Executor, string Organization) DescribeRoleAndExecutor(TemplateRouteStep step, PortalDirectory directory)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(step.ExecutorEmail))
|
if (!string.IsNullOrWhiteSpace(step.ExecutorEmail))
|
||||||
{
|
{
|
||||||
var employee = directory.FindByEmail(step.ExecutorEmail);
|
var employee = directory.FindByEmail(step.ExecutorEmail);
|
||||||
return (EmptyIfBlank(employee?.Position), employee?.Fio ?? step.ExecutorEmail);
|
return (EmptyIfBlank(employee?.Position), employee?.Fio ?? step.ExecutorEmail, directory.CompaniesOf(step.ExecutorEmail));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(step.Position))
|
if (!string.IsNullOrWhiteSpace(step.Position))
|
||||||
return (step.Position, directory.FindByPosition(step.Position)?.Fio ?? Empty);
|
{
|
||||||
|
var holder = directory.FindByPosition(step.Position);
|
||||||
|
return (step.Position, holder?.Fio ?? Empty, directory.CompaniesOf(holder?.Email));
|
||||||
|
}
|
||||||
|
|
||||||
if (step.ProjectRole is { } projectRole && projectRole != ProjectRole.None)
|
if (step.ProjectRole is { } projectRole && projectRole != ProjectRole.None)
|
||||||
return (ProjectRoleLabels[projectRole], ByRequest);
|
return (ProjectRoleLabels[projectRole], ByRequest, Empty);
|
||||||
|
|
||||||
if (step.HeadType is { } headType && headType != HeadType.None)
|
if (step.HeadType is { } headType && headType != HeadType.None)
|
||||||
{
|
{
|
||||||
@@ -193,12 +192,12 @@ public sealed class RouteExportService(
|
|||||||
};
|
};
|
||||||
if (fixedEmail is not null)
|
if (fixedEmail is not null)
|
||||||
// Нет в справочнике — лучше показать email, чем пустую ячейку.
|
// Нет в справочнике — лучше показать email, чем пустую ячейку.
|
||||||
return (HeadTypeLabels[headType], directory.FindByEmail(fixedEmail)?.Fio ?? fixedEmail);
|
return (HeadTypeLabels[headType], directory.FindByEmail(fixedEmail)?.Fio ?? fixedEmail, directory.CompaniesOf(fixedEmail));
|
||||||
|
|
||||||
return (HeadTypeLabels[headType], ByRequest);
|
return (HeadTypeLabels[headType], ByRequest, Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (Empty, Empty);
|
return (Empty, Empty, Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string DescribeCondition(TemplateRouteStep step)
|
private static string DescribeCondition(TemplateRouteStep step)
|
||||||
@@ -239,6 +238,22 @@ public sealed class RouteExportService(
|
|||||||
public EmployeeContactInfo? FindByPosition(string position) =>
|
public EmployeeContactInfo? FindByPosition(string position) =>
|
||||||
employees.FirstOrDefault(e => string.Equals(e.Position?.Trim(), position.Trim(), StringComparison.OrdinalIgnoreCase));
|
employees.FirstOrDefault(e => string.Equals(e.Position?.Trim(), position.Trim(), StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
// Организация — компания СОТРУДНИКА, а не маршрута. Справочник даёт строку на
|
||||||
|
// каждую компанию, где он числится (совместители), поэтому выводим все.
|
||||||
|
public string CompaniesOf(string? email)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(email))
|
||||||
|
return Empty;
|
||||||
|
|
||||||
|
var names = employees
|
||||||
|
.Where(e => string.Equals(e.Email, email, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& !string.IsNullOrWhiteSpace(e.Company))
|
||||||
|
.Select(e => e.Company)
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
return names.Count == 0 ? Empty : string.Join(", ", names);
|
||||||
|
}
|
||||||
|
|
||||||
// Пока справочник не отдал имя (или запись удалена) — усечённый Guid, как в админке.
|
// Пока справочник не отдал имя (или запись удалена) — усечённый Guid, как в админке.
|
||||||
public string CompanyName(Guid id) => companies.GetValueOrDefault(id) ?? ShortGuid(id);
|
public string CompanyName(Guid id) => companies.GetValueOrDefault(id) ?? ShortGuid(id);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user