7f5f59fc81
Сотрудник заполняет заявку один раз, а в ЭДО уходит по документу на каждую компанию, где он работает: своя шапка (компания, должность) и свой БИН в массиве file_company_bins. Подписант один — сам сотрудник, от имени физлица. Запрос один на все файлы: принимается целиком или отклоняется целиком, повтор безопасен. Компания без БИН в справочнике пропускается и показывается в сообщении. Удалять согласующих при составлении может редактор маршрута (во всех формах, не только в трёх). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
88 lines
4.0 KiB
TypeScript
88 lines
4.0 KiB
TypeScript
import { ICompany } from '../dictionary/models/ICompany';
|
|
import { IEmployeeOrganization } from '../dictionary/models/IEmployeeOrganization';
|
|
import { IFioParts, splitFio } from './fio';
|
|
|
|
/** Одна компания, от имени которой подписывается документ. */
|
|
export interface ISigningCompany {
|
|
/** БИН компании: от её имени документ идёт в Sigex. */
|
|
bin: string;
|
|
name: string;
|
|
/** Должность сотрудника именно в этой компании. */
|
|
position: string;
|
|
}
|
|
|
|
/** Компания сотрудника, по которой документ отправить нельзя, и почему. */
|
|
export interface ISkippedCompany {
|
|
name: string;
|
|
reason: string;
|
|
}
|
|
|
|
/** Кто подписывает и по каким компаниям. */
|
|
export interface ISigningTargets {
|
|
iin: string;
|
|
name: IFioParts;
|
|
/** По документу на каждую компанию, где у сотрудника есть БИН в справочнике. */
|
|
companies: ISigningCompany[];
|
|
/** Компании, по которым документ не отправить (нет БИН в справочнике и т. п.). */
|
|
skipped: ISkippedCompany[];
|
|
}
|
|
|
|
/**
|
|
* Определяет подписанта и компании по справочникам api.tnsu.kz: ИИН и ФИО — из Employee/ByEmail,
|
|
* БИН — из Dictionary/Companies по CompanyId.
|
|
*
|
|
* Сотрудник может работать в нескольких компаниях (на один e-mail — несколько записей), и заявление
|
|
* подписывается по каждой из них отдельно: у каждой компании свой документ со своей должностью.
|
|
* Одна и та же компания дважды не попадает. Компания без БИН не роняет остальные — она уходит в
|
|
* skipped с причиной. Ошибка бросается, только если подписать нельзя вовсе: нет человека, ИИН, ФИО
|
|
* или ни одной компании.
|
|
*/
|
|
export function resolveSigningTargets(records: IEmployeeOrganization[], companies: ICompany[]): ISigningTargets {
|
|
if (records.length === 0) {
|
|
throw new Error('сотрудника нет в кадровом справочнике — ИИН для подписания взять неоткуда');
|
|
}
|
|
|
|
// Человек один на все записи: ИИН и ФИО берём из первой, где они есть.
|
|
const iin = (records.find((r) => (r.iin || '').trim())?.iin || '').trim();
|
|
if (!iin) {
|
|
throw new Error('в кадровом справочнике у сотрудника не указан ИИН');
|
|
}
|
|
|
|
const fio = records.find((r) => (r.fio || '').trim())?.fio || '';
|
|
const name = splitFio(fio);
|
|
if (!name) {
|
|
throw new Error(`не удалось разобрать Ф.И.О. «${fio}» на фамилию и имя`);
|
|
}
|
|
|
|
const byId = new Map(companies.map((c) => [c.Id.toLowerCase(), c]));
|
|
const result: ISigningCompany[] = [];
|
|
const skipped: ISkippedCompany[] = [];
|
|
const seen = new Set<string>();
|
|
|
|
for (const record of records) {
|
|
if (!record.companyId) continue;
|
|
|
|
const company = byId.get(record.companyId.toLowerCase());
|
|
const bin = (company?.Bin || '').trim();
|
|
const companyName = company?.Name || record.company;
|
|
|
|
if (!bin) {
|
|
if (!seen.has(`name:${companyName}`)) {
|
|
seen.add(`name:${companyName}`);
|
|
skipped.push({ name: companyName, reason: 'в справочнике компаний нет БИН' });
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (seen.has(bin)) continue;
|
|
seen.add(bin);
|
|
result.push({ bin, name: companyName, position: record.position });
|
|
}
|
|
|
|
if (result.length === 0 && skipped.length === 0) {
|
|
throw new Error('в карточке сотрудника не указана компания');
|
|
}
|
|
|
|
return { iin, name, companies: result, skipped };
|
|
}
|