24d2cd7a6a
GET api/reports/vacancies?from=&to= отдаёт лист «Список вакансий (все)» по образцу ручного отчёта HR: те же шестнадцать колонок, разделители по объектам, двойная нумерация, итоги формулами. Данные — из двух форм портала: «Заявка на подбор» даёт вакансию, «Приём на работу» — её закрытие (связь по SelectionRequestId + SelectionRequestListId). Рекрутёр и дата закрытия берутся из фазы «Исполнение» парного списка «— согласование»: в самой заявке их нет. Период отбирает заявки по «Дате подачи заявки» с откатом на Created — поле заполняется руками и часто пустое. Заявки на приём периодом не ограничены: приём по вакансии из начала интервала мог оформляться уже после его конца. Колонка «Комментарии» остаётся пустой — в ручном отчёте это пометки рекрутёра, которым в заявках пока негде храниться. Генерация — ClosedXML (MIT): нужны стили, формулы и autofilter, а EPPlus с v5 под Polyform Noncommercial. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
57 lines
2.9 KiB
C#
57 lines
2.9 KiB
C#
using System.Text.Json;
|
|
|
|
namespace Tnsu.ApprovalRoute.Application.Reports;
|
|
|
|
/// <summary>
|
|
/// Строка списка «Заявка на подбор — данные». Имена колонок — как в request-portal
|
|
/// (provisioning.json формы RecruitingSelection).
|
|
/// </summary>
|
|
internal sealed record SelectionRow(
|
|
int Id,
|
|
string ListId,
|
|
string Status,
|
|
string? Position,
|
|
string? DepartmentName,
|
|
string? ObjectProject,
|
|
int VacanciesCount,
|
|
DateOnly? SubmitDate,
|
|
string? Initiator,
|
|
string? OpenReason,
|
|
string? OpenReasonDetails)
|
|
{
|
|
/// <summary>
|
|
/// Поля, которые нужны отчёту, — перечислены явно: список данных широкий
|
|
/// (полсотни колонок, часть — Note с многострочным текстом), и тянуть его
|
|
/// целиком по всем партициям только ради шестнадцати колонок незачем.
|
|
/// </summary>
|
|
public const string Select =
|
|
"$select=Id,RequestStatus,Position,DepartmentName,ObjectProject,OpenVacanciesCount," +
|
|
"SubmitDate,Created,OpenReason,OpenReasonDetails,Responsible/Title&$expand=Responsible&$top=5000";
|
|
|
|
public static SelectionRow? TryParse(JsonElement item, string listId)
|
|
{
|
|
var id = SharePointJson.Int(item, "Id");
|
|
if (id is null) return null;
|
|
|
|
return new SelectionRow(
|
|
id.Value,
|
|
listId,
|
|
SharePointJson.String(item, "RequestStatus") ?? string.Empty,
|
|
SharePointJson.String(item, "Position"),
|
|
SharePointJson.String(item, "DepartmentName"),
|
|
SharePointJson.String(item, "ObjectProject"),
|
|
// Пустое «Количество открытых вакансий» — это одна вакансия: заявку
|
|
// подают на конкретную должность, и ноль вакансий в отчёте сломал бы
|
|
// и итог по колонке E, и признак «закрыта» (0 из 0).
|
|
SharePointJson.Int(item, "OpenVacanciesCount") is { } count and > 0 ? count : 1,
|
|
// Дата подачи — поле формы, заполняется руками и может быть пустым;
|
|
// тогда берём дату создания элемента. Тем же правилом заявки
|
|
// отбираются по интервалу (см. VacancyReportService.PeriodFilter) —
|
|
// иначе заявка попадала бы в отчёт, но с пустой колонкой H.
|
|
SharePointJson.Date(item, "SubmitDate") ?? SharePointJson.Date(item, "Created"),
|
|
SharePointJson.PersonTitle(item, "Responsible"),
|
|
SharePointJson.String(item, "OpenReason"),
|
|
SharePointJson.String(item, "OpenReasonDetails"));
|
|
}
|
|
}
|