feat: заявление на отпуск уходит на подпись в ЭДО после финального согласования

Когда последний согласующий согласовывает заявку на отпуск, заявление собирается в браузере
как DOCX и отправляется в ЭДО (POST /api/v1/integration/signing-requests). Подписывает сотрудник
из заявки, его же почта передаётся как initiator_email. ИИН берётся из Employee/ByEmail,
БИН компании — из Dictionary/Companies. Неудача отправки не отменяет согласование и
показывается сообщением с кнопкой повтора.

Свойство веб-части «С каким ЭДО работать»: стейдж (https://10.0.1.225:8443) или локальный
бэкенд (https://localhost:7016). jszip объявлен зависимостью явно (раньше был транзитивным).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
gorbenko.pavel
2026-10-01 16:46:43 +05:00
parent 4f292dde3d
commit bed190835e
20 changed files with 777 additions and 10 deletions
@@ -0,0 +1,53 @@
import { ICompany } from '../dictionary/models/ICompany';
import { IEmployeeOrganization } from '../dictionary/models/IEmployeeOrganization';
import { IFioParts, splitFio } from './fio';
/** Кому и от имени какой компании отправляется документ на подпись. */
export interface ISigningTarget {
iin: string;
name: IFioParts;
/** БИН компании, от имени которой документ идёт в Sigex. */
companyBin: string;
companyName: string;
}
/**
* Определяет подписанта и компанию по данным справочников api.tnsu.kz:
* ИИН и ФИО — из Employee/ByEmail, БИН — из Dictionary/Companies по CompanyId сотрудника.
*
* Сотрудник может состоять в нескольких организациях (на один e-mail — несколько записей).
* Берём первую запись, у которой компания известна справочнику и в нём есть БИН: заявление
* от имени компании без БИН в ЭДО не принять. Каждая невозможность — отдельное сообщение,
* чтобы по тексту было ясно, что поправить: карточку сотрудника или справочник компаний.
*/
export function resolveSigningTarget(records: IEmployeeOrganization[], companies: ICompany[]): ISigningTarget {
if (records.length === 0) {
throw new Error('сотрудника нет в кадровом справочнике — ИИН для подписания взять неоткуда');
}
const withCompany = records.filter((r) => r.companyId);
if (withCompany.length === 0) {
throw new Error('в карточке сотрудника не указана компания');
}
const byId = new Map(companies.map((c) => [c.Id.toLowerCase(), c]));
const match = withCompany
.map((record) => ({ record, company: byId.get(record.companyId.toLowerCase()) }))
.find((m) => m.company && m.company.Bin && m.company.Bin.trim().length > 0);
if (!match || !match.company) {
throw new Error(`в справочнике компаний нет БИН для «${withCompany[0].company}»`);
}
const iin = (match.record.iin || '').trim();
if (!iin) {
throw new Error('в кадровом справочнике у сотрудника не указан ИИН');
}
const name = splitFio(match.record.fio);
if (!name) {
throw new Error(`не удалось разобрать Ф.И.О. «${match.record.fio}» на фамилию и имя`);
}
return { iin, name, companyBin: (match.company.Bin as string).trim(), companyName: match.company.Name };
}