Skip to content

Зарплата (payroll) ​

Расчёт с сотрудниками: условия оплаты, настройки выплат и выплата по табелям через реестр платежей; прежние месячные ведомости — только для чтения. Четвёртый блок АРМ бухгалтера и юриста.

Реализовано: модуль internal/modules/payroll, миграции 0113 (схема) и 0114 (RBAC).


Зачем ​

Раньше salary существовала только как категория затрат в бюджете. Полноценного расчёта с сотрудниками не было. Этот блок добавляет ведомости: начисления из окладов и табеля, удержание НДФЛ, выплата через кассу/платежи.


Сущности ​

Условия оплаты (CompensationProfile) ​

Условия оплаты сотрудника с историей: каждое изменение — новая запись со своим effective_from, прежняя закрывается днём раньше (effective_to). С миграции 0170 условия состоят из независимых частей, которые складываются (ТЗ «Табель и зарплата», п.4):

ПолеОписание
user_idСотрудник
salary_amount, salary_period, salary_dayОклад за месяц (month, день — число 1–31, 31 — последний день месяца) или за неделю (week, день недели 1–7). Начисляется целиком в свой день, сколько бы дней ни было отработано
hourly_rateСтавка за каждый подтверждённый час, поверх оклада
shift_rateСтавка за смену — день с подтверждёнными часами
effective_from / effective_toПериод действия
created_by, created_by_nameКто задал условия
kind, base_amountПрежняя модель (оклад либо ставка) для месячной ведомости: сервис заполняет их сам. Оклад за неделю в ней — в пересчёте на месяц (×52/12)

Правила:

  • Нужна хотя бы одна часть (400 payroll_pay_conditions_empty); нули значат «не задано». Оклад требует период и день (payroll_salary_period_invalid, payroll_salary_day_invalid).
  • Новые условия действуют только с даты позже начала последних (409 payroll_profile_not_after_latest называет дату). Задним числом история не переписывается.
  • Ошибку ввода исправляют удалением последних условий (DELETE /compensation-profiles/:id, иначе 409 payroll_profile_not_latest): предыдущие снова действуют без даты окончания.
  • После изменения трудозатраты проектов с часами сотрудника с этой даты пересчитываются: ставка за час из условий — одна из ставок трудозатрат, а оклад без ставки идёт как оклад / норму часов (APP_LABOR_MONTHLY_NORM_HOURS).
  • Прежний формат запроса (kind + base_amount) ещё принимается: оклад переводится в оклад за месяц с днём 31, ставка — в ставку за час. Перенос существующих записей — в миграции 0170.

Фиксированная сумма за проект хранится у участника команды (project_team_members.fixed_fee, поле fixed_fee в API команды): видна и меняется по тем же правам, что ставка в час (объект costs). В табель она войдёт на этапе 2.

Настройки выплат (payroll_settings) ​

Одна строка на компанию: pay_due_workdays — «Выплатить до» по умолчанию, рабочих дней от формирования табеля (0–60, по умолчанию 3); reminder_days — порог напоминания начальнику о часах без табеля (1–365, по умолчанию 14). Читает любой вошедший, меняет payroll.manage.

Выплата ​

С этапа 3 ТЗ «Табель, зарплата и передача выплат в финансы» зарплата выплачивается по табелям: переданный табель ставит запланированную выплату в платежи на «Счёт для зарплаты», проведение закрывает табель. НДФЛ не удерживается: к выплате равно начисленному.

Прежние месячные ведомости (PayrollRun) ​

Ведомости (payroll_runs, payroll_items) больше не создаются, существующие доступны только для чтения: period, status (draft → approved → paid), итоги total_accrued / total_withheld / total_net, строки с source_breakdown и payment_id. Корректировки табеля, вошедшие в ведомость деньгами, привязаны к ней через timesheet_corrections.payroll_run_id.


API ​

МетодПутьПравоОписание
GET/api/v1/compensation-profilespayroll.viewСписок условий оплаты (?user_id=), новые первыми
GET/api/v1/compensation-profiles/myлюбойСвои условия оплаты (профиль)
POST/api/v1/compensation-profilespayroll.manageНовые условия с даты, прежние закрываются
DELETE/api/v1/compensation-profiles/:idpayroll.manageУдалить последние условия
GET/api/v1/payroll/settingsлюбойНастройки выплат
PUT/api/v1/payroll/settingspayroll.manageИзменить срок выплаты и порог напоминания
GET/api/v1/payroll/runspayroll.viewПрежние ведомости (только чтение)
GET/api/v1/payroll/runs/:idpayroll.viewПрежняя ведомость со строками

Пример ​

json
POST /api/v1/compensation-profiles
{ "user_id": 2, "salary_amount": 60000.0, "salary_period": "month", "salary_day": 15,
  "hourly_rate": 500.0, "shift_rate": 2000.0, "effective_from": "2026-10-01" }

PUT /api/v1/payroll/settings
{ "pay_due_workdays": 3, "reminder_days": 14 }

Доступ (RBAC) ​

Права payroll.view / payroll.manage (seeded в migrations/0114), закреплены за ролью accountant (и admin). director видит через system.global_read. Подробнее — rbac_matrix.md.


Что дальше ​

  • Попадание начислений в finance как затрат категории salary для сводного P&L — запланировано (требует привязки ФОТ к проекту/организации; модуль finance сейчас project-scoped).
  • Расширенные удержания (авансы, займы) и отчёт по НДФЛ войдут в фазу сводной отчётности (см. дорожную карту).

Загружаем документацию…