Перейти к содержимому

Табель учёта рабочего времени (форма Т-13)

Назначение

Раздел, позволяющий корпорации выгрузить табель учёта рабочего времени по унифицированной форме Т-13 за выбранный период (обычно — календарный месяц). Без этого экспорта корпорация не может закрыть отчётность ФНС/бухгалтерии и не рассматривается к покупке как замена iiko. См. LOCALIOFFICE-998.

Экран — кнопка «Экспорт Т-13» в журнале явок, открывающая модалку с выбором периода и опциональным фильтром по подразделению/юрлицу. По нажатию «Скачать» — браузер получает xlsx-файл.

Customer Journey Map

[Журнал явок]
└─► «Экспорт Т-13»
[Модалка «Экспорт табеля Т-13»]
• Период (dateFrom..dateTo)
• Подразделение (опц.)
• Юрлицо (опц.)
└─► «Скачать»
POST /staffing/attendances/timesheet-t13/export
├─► 200 → xlsx-файл (Content-Disposition: attachment)
├─► 400 → toast: «Некорректный период»
└─► 404 → toast: «Подразделение/юрлицо не найдено»

Эндпоинты раздела

МетодМаршрутНазначениеКонтроллер
POST/staffing/attendances/timesheet-t13/exportЭкспорт табеля Т-13 в xlsxattendance.controller.ts

Селекторы для модалки используют существующие эндпоинты:

СелекторОткуда братьФильтрация
ПодразделениеGET /organization/subdivisionsisDeleted: false
ЮрлицоGET /organization/legal-entitiesпо текущей корпорации

DTO запроса (T13ExportDto)

t13-export.dto.ts

ПолеТипОбяз.Описание
dateFromstring (YYYY-MM-DD)Нижняя граница периода
dateTostring (YYYY-MM-DD)Верхняя граница периода (включительно). Диапазон ≤ 366 дней
subdivisionIdstring (UUID)Если задан — выгрузка только по одному подразделению; юрлицо в шапке файла берётся из subdivision.legalEntity
legalEntityIdstring (UUID)Если задан без subdivisionId — выгрузка по всем подразделениям юрлица; в шапке имя юрлица

Если заданы оба фильтра — subdivisionId имеет приоритет, legalEntityId игнорируется.

Если не задан ни один фильтр — выгрузка по всей корпорации, в шапке имя корпорации.

Формат ответа

  • Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
  • Content-Disposition: attachment; filename="timesheet-t13.xlsx"
  • Body — бинарный xlsx.

Фронту скачивание организовать через fetchBlobURL.createObjectURL + <a download> (стандартный паттерн для xlsx-экспортов).

Семантика данных в xlsx

Шапка файла:

  • Заголовок «Табель учёта рабочего времени (форма Т-13)»
  • Организация (юрлицо при фильтре по подразделению/юрлицу; имя корпорации иначе)
  • Подразделение (только при фильтре по конкретному подразделению)
  • Период dateFrom – dateTo (часовой пояс корпорации)

Таблица:

  • Одна строка = тройка (сотрудник × должность × подразделение). Если сотрудник за период работал на двух должностях — две строки.
  • Фиксированные колонки: № п/п, табельный номер, ФИО (Фамилия И. О.), должность, подразделение.
  • Дневные колонки за каждый день периода. В ячейке:
    • Для «явки» (AttendanceType.earningRule = WORK): код типа явки + часы за день.
    • Для «неявки» (SICK_LEAVE, VACATION, ABSENCE, BUSINESS_TRIP, COMPENSATORY, CUSTOM): только код, часы пустые.
  • Итоговые блоки: «За I половину месяца» (day ≤ 15), «За II половину месяца» (day ≥ 16), «Итого за период» — пара «дни / часы».
  • Подвал «Итого» — сумма по всем строкам.

Расчёт часов в ячейке

Position.workScheduleTypeИсточник часов
HOURLYAttendance.durationInMinutes для CLOSED-явок с earningRule = WORK
SCHEDULED / SALARYПересечение интервала явки с плановой сменой (ScheduleEntry) того же (employeeId, positionId) за тот же учётный день

Приоритет кода в дне с несколькими явками

Если за один учётный день у сотрудника несколько явок с разными типами, в ячейку идёт код приоритетного типа: WORK > BUSINESS_TRIP > SICK_LEAVE > VACATION > COMPENSATORY > ABSENCE > CUSTOM. Часы при этом — суммарные WORK-часы дня (неявки часы не дают).

Семантика статусов и enum’ов

  • AttendanceType.shortName (например, «Я», «ОТ», «Б») идёт в ячейку как буквенный код типа.
  • Учётный день для группировки — Attendance.accountingDate, считается из confirmedArrivalTime ?? registeredArrivalTime с учётом Subdivision.timezone и Corporation.accountingDayStartTime. Ночные смены попадают в свой учётный день.

Известные расхождения и план доработок

  • ⚠️ Прямой связи Attendance ↔ ScheduleEntry пока нет (LOCALIOFFICE-993, open). Пересечение SCHEDULED/SALARY часов считается через любую плановую смену того же дня по (employeeId, positionId). Корректно для типового случая (одна плановая смена в день). После LOCALIOFFICE-993 расчёт перейдёт на прямую связь.
  • ⚠️ Коэффициенты к ставке у типов явок (LOCALIOFFICE-988, in progress) и среднее число рабочих часов в месяце (LOCALIOFFICE-990, open) на Т-13 пока не влияют — экспорт показывает фактические часы из явок, без коэффициентов.
  • ✅ Календарное деление полумесяцев 1..15 / 16..end работает и для произвольного периода (для периодов вне одного месяца итоги частичные).