feat: маршрут отпуска и отпуска без содержания учитывает организацию сотрудника

Кадровый сотрудник и исполнитель в этих маршрутах зависят от компании
сотрудника (Ольга Кудрящова и Айнур — Тансу/КПС/CT, Мольдир Хамит —
остальные), но формы не передавали организацию в расчёт маршрута, и
условные шаги в админке не срабатывали. Теперь названия компаний из поля
«Организация» (Employee/ByEmail) уходят как Organization через пробел — и в
согласование, и в исполнителей; маршрут пересчитывается, когда организация
подгрузится. Общий хелпер — shared/dictionary/organizationRouteFields.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Тимур Городецкий
2026-10-15 12:44:29 +05:00
parent 91c66bbf81
commit e811186cec
4 changed files with 80 additions and 8 deletions
@@ -47,6 +47,7 @@ import {
dataListTitleForListId
} from '../../shared/listTitles';
import { FORM_ID, IFormData, EMPTY_DATA, SetField } from './model';
import { organizationRouteFieldValues } from '../../shared/dictionary/organizationRouteFields';
import { formIssues } from './validation';
import { saveUnpaidLeaveRequest, unpaidLeaveApprovalService } from './submit';
import { loadUnpaidLeaveRequest } from './load';
@@ -162,16 +163,22 @@ const UnpaidLeaveForm: React.FC<IRequestFormProps> = ({
// Планируемый маршрут резолвится по e-mail ВЫБРАННОГО сотрудника (не текущего
// пользователя) — так работает подача за другого. У заявки на редактировании
// маршрут читается из уже записанных строк, чтобы не затереть правки инициатора.
//
// Кадровый сотрудник и исполнитель зависят от организации сотрудника (условия
// «Organization содержит …» в админке) — см. shared/dictionary/organizationRouteFields.ts.
// Организация приходит позже выбора сотрудника, поэтому она в ключе перезагрузки.
const routeFieldValues = useMemo(() => organizationRouteFieldValues(data.Organization), [data.Organization]);
const routeOrgKey = routeFieldValues.map((f) => f.value).join('|');
const loadMatrix = useCallback(
() =>
loadRoutePhase(approval, editId, requestStatus, 'Approval', () =>
getPlannedMatrix(context, requestTypeId as number, data.Employee, approval, 'Approval')
getPlannedMatrix(context, requestTypeId as number, data.Employee, approval, 'Approval', null, routeFieldValues)
),
[context, approval, editId, requestStatus, data.Employee, requestTypeId]
[context, approval, editId, requestStatus, data.Employee, requestTypeId, routeFieldValues]
);
const matrix = useMatrixEditor(
loadMatrix,
`${data.Employee}-${editId ?? 'new'}`,
`${data.Employee}-${editId ?? 'new'}-${routeOrgKey}`,
!readOnly,
isOpenedRouteReady(editId, requestStatus, requestTypeId) && (!!editId || (!!data.Employee && !!requestTypeId)),
(login) => approval.getJobTitle(login)
@@ -185,7 +192,8 @@ const UnpaidLeaveForm: React.FC<IRequestFormProps> = ({
requestTypeId,
userEmail: data.Employee,
editId,
requestStatus
requestStatus,
fieldValues: routeFieldValues
});
// Проблемы полей (см. validation.ts) подсвечиваются под полями через FieldIssuesProvider.
@@ -46,6 +46,7 @@ import {
dataListTitleForListId
} from '../../shared/listTitles';
import { FORM_ID, IFormData, EMPTY_DATA, SetField, MIN_NOTICE_DAYS, MAX_DAYS } from './model';
import { organizationRouteFieldValues } from '../../shared/dictionary/organizationRouteFields';
import { saveVacationRequest, vacationApprovalService } from './submit';
import { loadVacationRequest } from './load';
import { RequestSection } from './sections/RequestSection';
@@ -170,16 +171,24 @@ const VacationForm: React.FC<IRequestFormProps> = ({
// Планируемый маршрут резолвится по e-mail ВЫБРАННОГО сотрудника (не текущего
// пользователя) — так работает подача за другого. У заявки на редактировании
// маршрут читается из уже записанных строк, чтобы не затереть правки инициатора.
//
// Кадровый сотрудник и исполнитель в маршруте зависят от организации
// сотрудника (условия «Organization содержит …» в админке) — см.
// shared/dictionary/organizationRouteFields.ts. Организация приходит позже
// выбора сотрудника (Employee/ByEmail), поэтому она в ключе перезагрузки:
// маршрут пересчитается, когда придёт, а не останется рассчитанным без условий.
const routeFieldValues = useMemo(() => organizationRouteFieldValues(data.Organization), [data.Organization]);
const routeOrgKey = routeFieldValues.map((f) => f.value).join('|');
const loadMatrix = useCallback(
() =>
loadRoutePhase(approval, editId, requestStatus, 'Approval', () =>
getPlannedMatrix(context, requestTypeId as number, data.Employee, approval, 'Approval')
getPlannedMatrix(context, requestTypeId as number, data.Employee, approval, 'Approval', null, routeFieldValues)
),
[context, approval, editId, requestStatus, data.Employee, requestTypeId]
[context, approval, editId, requestStatus, data.Employee, requestTypeId, routeFieldValues]
);
const matrix = useMatrixEditor(
loadMatrix,
`${data.Employee}-${editId ?? 'new'}`,
`${data.Employee}-${editId ?? 'new'}-${routeOrgKey}`,
!readOnly,
isOpenedRouteReady(editId, requestStatus, requestTypeId) && (!!editId || (!!data.Employee && !!requestTypeId)),
(login) => approval.getJobTitle(login)
@@ -194,7 +203,8 @@ const VacationForm: React.FC<IRequestFormProps> = ({
requestTypeId,
userEmail: data.Employee,
editId,
requestStatus
requestStatus,
fieldValues: routeFieldValues
});
const daysNum = Number(data.DaysCount);