feat: отчёт по вакансиям в xlsx за период
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>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
namespace Tnsu.ApprovalRoute.Application.Reports;
|
||||
|
||||
/// <summary>
|
||||
/// Отчёт по вакансиям за период — ровно то, что ложится на лист «Список вакансий
|
||||
/// (все)» в xlsx: интервал плюс вакансии, разбитые по объектам/проектам.
|
||||
/// </summary>
|
||||
public sealed record VacancyReport(DateOnly From, DateOnly To, IReadOnlyList<VacancyReportGroup> Groups);
|
||||
|
||||
/// <summary>
|
||||
/// Объект/проект и его вакансии. Имя группы становится строкой-разделителем на
|
||||
/// листе (как «КЭС КУРЧАТОВ» в образце отчёта); пустое имя — вакансии, у которых
|
||||
/// объект в заявке не указан, они идут первым блоком без разделителя.
|
||||
/// </summary>
|
||||
public sealed record VacancyReportGroup(string Name, IReadOnlyList<VacancyReportRow> Rows);
|
||||
|
||||
/// <summary>
|
||||
/// Одна вакансия — строка листа. Колонки A/B (нумерация), G (в работе), J (срок
|
||||
/// отработки) и итоги здесь не хранятся: их считает сам xlsx формулами, чтобы
|
||||
/// цифры пересчитывались, если строку в файле поправят руками.
|
||||
/// </summary>
|
||||
public sealed record VacancyReportRow(
|
||||
// C — «Вакансия»: должность из заявки на подбор.
|
||||
string Position,
|
||||
// D — «Подразделение» ответственного за подачу заявки.
|
||||
string? Department,
|
||||
// E — «Кол-во вакансий».
|
||||
int VacanciesCount,
|
||||
// F — «количество закрытых вакансий»: сколько заявок на приём завели по этой заявке на подбор.
|
||||
int ClosedCount,
|
||||
// H — «Дата подачи заявки».
|
||||
DateOnly? SubmitDate,
|
||||
// I — «Дата закрытия вакансии»: дата решения по последнему шагу исполнения.
|
||||
DateOnly? ClosingDate,
|
||||
// K — «Дата выхода»: самая поздняя дата приёма среди принятых по этой вакансии.
|
||||
DateOnly? StartDate,
|
||||
// L — «Инициатор заявки».
|
||||
string? Initiator,
|
||||
// M — «Вместо сотрудника Ф.И.О.».
|
||||
string? ReplacedEmployee,
|
||||
// O — «Закрыта вакансия»: да / нет / частично.
|
||||
string ClosedFlag,
|
||||
// P — «Рекрутер»: исполнитель фазы исполнения заявки на подбор.
|
||||
string? Recruiter);
|
||||
|
||||
/// <summary>Значения колонки O. «Пауза» из образца отчёта не выводится — такого статуса в заявке нет.</summary>
|
||||
public static class VacancyClosedFlags
|
||||
{
|
||||
public const string Yes = "да";
|
||||
public const string No = "нет";
|
||||
public const string Partial = "частично";
|
||||
}
|
||||
Reference in New Issue
Block a user