import { AadHttpClient, HttpClientResponse } from '@microsoft/sp-http'; import { WebPartContext } from '@microsoft/sp-webpart-base'; import { APPROVAL_API_CLIENT_ID, DICTIONARY_API_BASE_URL } from '../config'; /** * Название должности директора по компании — ЗЕРКАЛО того же справочника, что * использует approval-route на бэкенде для маршрута HeadType.CompanyDirectors * (см. tnsu-approval-route TemplateRouteService.DirectorPositionByCompany). * Держим копию здесь, а не тянем с бэкенда: нужен только на фронте, чтобы найти * СТРОКУ МАТРИЦЫ, соответствующую конкретной компании (см. * forms/Dismissal/sections/OrganizationsTable.tsx) — сама матрица уже пришла * готовой от approval-route, эта функция лишь опознаёт в ней директора нужной * компании тем же способом, каким его туда положил бэкенд. * * Если бэкендный справочник изменится — эту копию нужно поправить вручную, * отдельного источника правды на два репозитория нет. */ const DIRECTOR_POSITION_BY_COMPANY: Record = { 'ТОО KazPromService': 'Главный механик', 'ТОО СТ Global': 'Генеральный директор' }; const DEFAULT_DIRECTOR_POSITION = 'Директор'; function normalizeCompany(company: string): string { return company.trim().toLowerCase(); } const DIRECTOR_POSITION_BY_COMPANY_NORMALIZED: Record = Object.keys( DIRECTOR_POSITION_BY_COMPANY ).reduce>((acc, key) => { acc[normalizeCompany(key)] = DIRECTOR_POSITION_BY_COMPANY[key]; return acc; }, {}); /** Название должности директора для компании (регистронезависимо). */ export function directorPositionFor(company: string): string { return DIRECTOR_POSITION_BY_COMPANY_NORMALIZED[normalizeCompany(company)] || DEFAULT_DIRECTOR_POSITION; } async function getClient(context: WebPartContext): Promise { return context.aadHttpClientFactory.getClient(APPROVAL_API_CLIENT_ID); } /** * GET /Employee/ByPosition — сотрудник на должности в компании (напр. директор). * 404/«не найден» — не ошибка формы, просто вернуть '' (см. вызывающий код: * отсутствие директора не должно ронять форму, только не даст найти строку матрицы). */ export async function getEmployeeEmailByPosition( context: WebPartContext, position: string, company: string ): Promise { const client = await getClient(context); const response: HttpClientResponse = await client.get( `${DICTIONARY_API_BASE_URL}/Employee/ByPosition?position=${encodeURIComponent( position )}&company=${encodeURIComponent(company)}`, AadHttpClient.configurations.v1 ); if (response.status === 404) return ''; if (!response.ok) { throw new Error(`Employee/ByPosition: ${response.status} ${response.statusText}`); } const body = (await response.json()) as { Email?: string; error?: string }; return body.Email || ''; } /** Директор (или должностной эквивалент, см. directorPositionFor) компании. */ export function getCompanyDirectorEmail(context: WebPartContext, company: string): Promise { return getEmployeeEmailByPosition(context, directorPositionFor(company), company); } /** * GET /Employee/Manager — руководитель сотрудника по типу связи. 'chief' — * непосредственный руководитель (тот же managerType, что использует * approval-route для HeadType.Chief — см. TemplateRouteService.ResolveStepAsync). * Не найден/сотрудник без руководителя — '' (не ошибка). */ export async function getManagerEmail( context: WebPartContext, email: string, managerType: 'chief' | 'directionHead' = 'chief' ): Promise { if (!email) return ''; const client = await getClient(context); const response: HttpClientResponse = await client.get( `${DICTIONARY_API_BASE_URL}/Employee/Manager?email=${encodeURIComponent(email)}&managerType=${encodeURIComponent( managerType )}`, AadHttpClient.configurations.v1 ); if (response.status === 404) return ''; if (!response.ok) { throw new Error(`Employee/Manager: ${response.status} ${response.statusText}`); } const body = (await response.json()) as { managerEmail?: string }; return body.managerEmail || ''; }