Files
request-portal/src/webparts/requestPortal/shared/dictionary/employeeFullBase.service.ts
T
Тимур Городецкий 4c018154d3 feat: выбор организации сотрудника в изменении условий ТД
Условия меняются в одном трудовом договоре, а у совместителя их несколько.
Как в командировке: пикер FullBase теперь выбирает человека (записи
склеены по ИИН), а под ним — список его мест работы «Компания — должность —
подразделение»; единственное место выбирается само. Маршрут строится по
человеку и не ждёт выбора организации. Строка под полем и вложенная сетка,
сжимавшая поля вида, убраны.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-28 14:44:16 +05:00

95 lines
4.4 KiB
TypeScript

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';
import { IEmployeeFullBaseRecord } from './models/IEmployeeFullBaseRecord';
import { IEmployeeFullBasePerson } from './models/IEmployeeFullBasePerson';
// null из API → '' (см. IEmployeeFullBaseRecord).
function textOrEmpty(value: unknown): string {
return typeof value === 'string' ? value.trim() : '';
}
function toRecord(r: Record<string, unknown>): IEmployeeFullBaseRecord {
return {
id: textOrEmpty(r.id),
fio: textOrEmpty(r.fio),
iin: textOrEmpty(r.iin),
code: textOrEmpty(r.code),
company: textOrEmpty(r.company),
// Должности в базе бывают с хвостовым пробелом («Монтажник ») — textOrEmpty его срезает.
position: textOrEmpty(r.position),
department: textOrEmpty(r.department),
// В API опечатка «admision» — переименовываем на входе, дальше по коду правильное имя.
admissionDate: textOrEmpty(r.admisionDate),
dismissalDate: textOrEmpty(r.dismissalDate),
email: textOrEmpty(r.email)
};
}
/**
* GET /Employee/FullBase?Fio=... — работающие сотрудники по части Ф.И.О.
*
* Без фильтра эндпоинт отдаёт всю базу (тысячи записей, с уволенными), поэтому
* ищем на сервере по мере ввода, как PositionPicker — и ничего не кешируем.
*
* Уволенных отсекаем по dismissalDate на клиенте. Параметр Status у эндпоинта
* есть, но его значения не задокументированы — ДОПУЩЕНИЕ, проверить и, если он
* фильтрует работающих, перейти на него.
*/
export async function searchActiveEmployees(
context: WebPartContext,
fio: string,
maxResults: number
): Promise<IEmployeeFullBaseRecord[]> {
const query = fio.trim();
if (!query) return [];
const client = await context.aadHttpClientFactory.getClient(APPROVAL_API_CLIENT_ID);
const response: HttpClientResponse = await client.get(
`${DICTIONARY_API_BASE_URL}/Employee/FullBase?Fio=${encodeURIComponent(query)}`,
AadHttpClient.configurations.v1
);
if (response.status === 404) return [];
if (!response.ok) {
throw new Error(`Employee/FullBase: ${response.status} ${response.statusText}`);
}
const body = (await response.json()) as Array<Record<string, unknown>>;
return body
.map(toRecord)
.filter((r) => !r.dismissalDate)
.slice(0, maxResults);
}
/** Чей это человек: ИИН, а без него — id записи (тогда запись считается отдельным человеком). */
export function personKey(record: IEmployeeFullBaseRecord): string {
return record.iin || record.id;
}
/**
* Уникальный ключ места работы — для value в выпадающем списке. Не только id:
* у совместителя на каждое место работы может прийти один и тот же id человека.
*/
export function workplaceKey(record: IEmployeeFullBaseRecord): string {
return `${record.id}|${record.company}|${record.position}`;
}
/** Человек из одной записи — когда остальные его места работы неизвестны (напр. просмотр заявки). */
export function personFromWorkplace(record: IEmployeeFullBaseRecord): IEmployeeFullBasePerson {
return { key: personKey(record), fio: record.fio, iin: record.iin, email: record.email, workplaces: [record] };
}
/** Записи FullBase → люди (по ИИН), порядок — по первому появлению человека в ответе. */
export function groupEmployeesByPerson(records: IEmployeeFullBaseRecord[]): IEmployeeFullBasePerson[] {
const people: IEmployeeFullBasePerson[] = [];
records.forEach((record) => {
const person = people.find((p) => p.key === personKey(record));
if (!person) {
people.push(personFromWorkplace(record));
return;
}
person.workplaces.push(record);
if (!person.email) person.email = record.email;
});
return people;
}