Files
request-portal/src/webparts/requestPortal/shared/config.ts
T
gorbenko.pavel e976b4e8d4
Tests / frontend-test (pull_request) Successful in 2m12s
feat: форма «Заявка на оплату» (СТ/потребность, счёт через OCR и ИИ)
Новая форма acc-payment-request в категории «Бухгалтерия»: основание —
согласованная СТ (сайт СТ) или потребность; из основания подставляются
получатель, раздел, вид работ, условия, общая сумма и позиции. Счёт (PDF)
сохраняется в библиотеку, уходит на OCR, ИИ достаёт реквизиты, суммы и позиции
и сверяет их с СТ. Подсвечиваются чужой получатель в счёте и превышение
общей суммы СТ всеми оплатами. Справочники 1С — общий DictionaryPicker.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 15:01:28 +05:00

250 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Сайт SharePoint, где живут списки данных портала.
* Портал (web part) может размещаться на другом сайте (например, на главной),
* поэтому клиент PnPjs всегда нацеливаем на этот адрес явно.
*
* TODO: при необходимости вынести в свойство web part (property pane),
* чтобы не хардкодить и переключать между dev/prod-сайтами.
*/
export const DATA_SITE_URL = 'https://tnsukz.sharepoint.com/sites/requests';
/**
* Инструкция пользователя по порталу (docx на сайте базы знаний) — ссылка в
* боковом меню (см. components/RequestPortal.tsx), открывается в новой вкладке.
*/
export const USER_GUIDE_URL =
'https://tnsukz.sharepoint.com/:w:/r/sites/knowledge/DocLib8/%D0%98%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%86%D0%B8%D0%B8%20%D0%BA%20IT%20%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0%D0%BC/%D0%98%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%86%D0%B8%D1%8F%20-%20%D0%9F%D0%BE%D1%80%D1%82%D0%B0%D0%BB%20%D0%B7%D0%B0%D1%8F%D0%B2%D0%BE%D0%BA.docx?d=wce77e8a0d19d49b5b2fcdf13828e8bb3&csf=1&web=1&e=p9V3hU';
/**
* Страница портала в SharePoint — ссылка «Открыть портал в браузере» в
* объявлении для Teams (см. components/TeamsBrowserNotice). Внутри Teams
* портал свой адрес не знает (вкладка Teams — служебная страница), поэтому задан явно.
*/
export const PORTAL_PAGE_URL = `${DATA_SITE_URL}/SitePages/${encodeURIComponent('Портал-Заявок')}.aspx`;
/**
* Внешний approval-route API (маршруты согласования по ролям).
* Авторизация — AadHttpClient (SSO текущего пользователя) по clientId
* зарегистрированного AAD-приложения. Разрешение объявлено в
* config/package-solution.json (webApiPermissionRequests) и одобряется
* в Центре администрирования SharePoint (API access) при развёртывании.
*/
export const APPROVAL_API_CLIENT_ID = '9232d9e2-9807-4753-808f-07250e5474b0';
/** Прод — то же самое значение, что default в свойстве approvalApiBaseUrl веб-части. */
export const APPROVAL_API_BASE_URL_PROD = 'https://api.tnsu.kz/approval-route';
/** Локальный backend — порт из tnsu-approval-route/backend/.../launchSettings.json. */
export const APPROVAL_API_BASE_URL_LOCAL = 'http://localhost:5080';
// Настраивается свойством веб-части approvalApiBaseUrl (селект в панели свойств,
// см. RequestPortalWebPart.render) — тот же приём, что и у debugUserQuery/
// setImpersonatedIdentity (shared/impersonation.ts): модульный синглтон, а не
// проброс параметром через все вызовы approvalRoute.service.ts, потому что эти
// вызовы разбросаны по формам и не имеют общего входа, через который можно
// было бы прокинуть контекст.
let _approvalApiBaseUrl = APPROVAL_API_BASE_URL_PROD;
export function setApprovalApiBaseUrl(url: string): void {
_approvalApiBaseUrl = url || APPROVAL_API_BASE_URL_PROD;
}
export function getApprovalApiBaseUrl(): string {
return _approvalApiBaseUrl;
}
/**
* ЭДО (TANSU ЭДО) — подписание документов. Токен берётся для того же AAD-приложения, что и
* у approval-route и справочников: бэкенд ЭДО принимает токены сотрудников этого приложения
* (Auth:Entra:Audience). Если ЭДО переедет на своё приложение — поменять только эту константу
* и объявить доступ в package-solution.json (webApiPermissionRequests).
*/
export const EDO_API_CLIENT_ID = APPROVAL_API_CLIENT_ID;
/** Локальный бэкенд ЭДО — порт из launchSettings.json (https). */
export const EDO_API_BASE_URL_LOCAL = 'https://localhost:7016';
/** Стейджинг ЭДО. Сертификат самоподписанный: браузер должен ему доверять, иначе запрос не уйдёт. */
export const EDO_API_BASE_URL_STAGE = 'https://10.0.1.225:8443';
/** Прод ЭДО — значение по умолчанию. */
export const EDO_API_BASE_URL_PROD = 'https://edo.tnsu.kz';
// Настраивается свойством веб-части edoApiBaseUrl (селект в панели свойств) — тот же
// модульный синглтон, что и approvalApiBaseUrl выше.
let _edoApiBaseUrl = EDO_API_BASE_URL_PROD;
export function setEdoApiBaseUrl(url: string): void {
// Хвостовые слэши отбрасываем: адрес склеивается с путём «/api/...», а в панели
// свойств его могли вписать с «/» на конце.
_edoApiBaseUrl = (url || EDO_API_BASE_URL_PROD).trim().replace(/\/+$/, '');
}
export function getEdoApiBaseUrl(): string {
return _edoApiBaseUrl;
}
/**
* Отправка согласованного заявления на отпуск на подпись в ЭДО (forms/Vacation/VacationView).
* Временно выключена: решение последнего согласующего применяется без обращения к ЭДО.
* Чтобы вернуть — поставить true, остальной код отправки на месте.
*/
export const VACATION_EDO_SIGNING_ENABLED = false;
// Уведомления согласующим (поток Power Automate, см. approval/notify.service.ts).
// Настраивается свойствами веб-части notificationsEnabled / notifyOnlyTo — тот
// же модульный синглтон, что и у approvalApiBaseUrl выше.
// enabled — false выключает рассылку целиком;
// onlyTo — непустой адрес: письма уходят ТОЛЬКО если активный шаг назначен
// на него (обкатка рассылки); пусто — всем.
let _notificationsEnabled = true;
let _notifyOnlyTo = '';
export function setNotificationSettings(enabled: boolean, onlyTo: string): void {
_notificationsEnabled = enabled;
_notifyOnlyTo = (onlyTo || '').trim();
}
export function getNotificationsEnabled(): boolean {
return _notificationsEnabled;
}
export function getNotifyOnlyTo(): string {
return _notifyOnlyTo;
}
/**
* Справочники (Dictionary API) — тот же gateway api.tnsu.kz и тот же AAD-app
* (APPROVAL_API_CLIENT_ID), но другой путь/сервис. Используется, например, для
* GET /Dictionary/Departments.
*/
export const DICTIONARY_API_BASE_URL = 'https://api.tnsu.kz';
/**
* Проходы СКУД (Hikvision) текущего пользователя — тот же gateway и AAD-app,
* что у справочников. Сотрудника API определяет по токену, e-mail не передаём.
* Используется формой «Объяснительная по СКУД» (forms/SkudExplanation).
*/
export const SKUD_EVENTS_PATH = '/Hikvision/Events/CurrentUser';
/**
* Значения параметра `source` внешней ссылки (см. shared/routing, sourceSystem) —
* из какой системы открыли форму и, значит, куда отвечать ссылкой на заявку.
* Без source (или с неизвестным значением) — ссылка пришла из Dataverse.
*/
export const EXTERNAL_SOURCE = {
/** КПС: positionId — id позиции акта дефектов, ответ — в Inventory API. */
kps: 'kps'
} as const;
/**
* Inventory API (КПС) — приём ссылки на заявку «СЗ на перемещение ТМЦ» для
* позиции акта дефектов: POST {DICTIONARY_API_BASE_URL}<путь> с телом { url }.
* {partId} = positionId из внешней ссылки с source=kps. Тот же gateway
* api.tnsu.kz — авторизация та же, что у справочников (APPROVAL_API_CLIENT_ID).
*/
export const INVENTORY_TMC_TRANSFER_PATH = '/inventory/api/defect-acts/parts/{partId}/tmc-transfer';
/**
* Производственный календарь (праздничные выходные) — SP-список на корневом
* сайте, где живёт главная страница портала (portal-main-page читает его же).
* Недоступен — календарь СКУД просто не красит праздники.
*/
export const HOLIDAYS_SITE_URL = 'https://tnsukz.sharepoint.com';
export const HOLIDAYS_LIST_TITLE = 'Производственный Календарь';
/**
* Power Automate: поток оповещения согласующего/исполнителя.
* Триггер «When an HTTP request is received» защищён Entra («любой пользователь
* тенанта»), поэтому URL без подписи (`sig`), а вызываем через AadHttpClient с
* ресурсом Flow Service (обычный fetch → 401; так же решается CORS). Разрешение
* «Microsoft Flow Service / Flows.Read.All» объявлено в package-solution.json и
* одобряется в Центре администрирования SharePoint (API access) при развёртывании.
* Шлём тело `{ RowID: <id строки списка согласования> }` — поток сам находит по
* строке согласующего, статус и заявку и уведомляет. (Паттерн из kps-ct-form.)
*/
export const FLOW_RESOURCE = 'https://service.flow.microsoft.com/';
export const FLOW_NOTIFY_URL =
'https://eab3d2815f2beb42bf2e7ec1731708.92.environment.api.powerplatform.com:443/powerautomate/automations/direct/cu/07/workflows/b21516edf8a14287a65a39e4116dfe4f/triggers/manual/paths/invoke?api-version=1';
/**
* Power Automate: поток оповещения о новом сообщении в чате заявки (shared/chat).
* Тот же механизм вызова, что у FLOW_NOTIFY_URL (Entra-триггер, AadHttpClient с
* ресурсом FLOW_RESOURCE), но тело другое — получателей поток НЕ ищет сам, их
* передаёт портал (см. shared/chat/chatNotify.ts):
* отмеченным через «@» — «Вас упомянули в чате по заявке №…»,
* инициатору (если его не отметили) — «Новое сообщение в чате по вашей заявке №…».
*/
export const FLOW_CHAT_NOTIFY_URL =
'https://eab3d2815f2beb42bf2e7ec1731708.92.environment.api.powerplatform.com:443/powerautomate/automations/direct/cu/07/workflows/325d735d32de4e6989f4f06fc347e833/triggers/manual/paths/invoke?api-version=1';
// Чат заявки, режим проверки (свойство веб-части chatSelfNotify): письма уходят и
// о СВОИХ сообщениях, и себя можно отметить через «@». По умолчанию выключен —
// писать самому себе в обычной работе незачем.
let _chatSelfNotify = false;
export function setChatSelfNotify(enabled: boolean): void {
_chatSelfNotify = enabled;
}
export function getChatSelfNotify(): boolean {
return _chatSelfNotify;
}
/**
* Microsoft Dataverse (Power Platform) — рабочая среда «По умолчанию»
* (org95ca17d6.crm4.dynamics.com). NB: таблица cree9_demandline существует и в
* другой среде (tansu.crm5, из проекта stroy-bot) — писать нужно ИМЕННО в эту.
* Авторизация через AadHttpClient (SSO текущего пользователя): ресурс = URL
* среды, разрешение объявлено в package-solution.json (webApiPermissionRequests,
* scope user_impersonation) и одобряется в Центре администрирования SharePoint
* (API access) при развёртывании — тот же механизм, что у approval-route API.
*
* Используется формой «СЗ на перемещение ТМЦ»: после отправки на согласование
* в строку-источник (таблица cree9_demandline) записывается ссылка на заявку
* портала — см. shared/dataverse/demandLine.service.ts.
*/
export const DATAVERSE_RESOURCE_URL = 'https://org95ca17d6.crm4.dynamics.com';
export const DATAVERSE_API_VERSION = 'v9.2';
/**
* EntitySetName таблицы «Строка заявки» (LogicalName cree9_demandline).
* NB: значение выведено по конвенции Dataverse (logical + 's') — проверить в
* проде через EntityDefinitions(LogicalName='cree9_demandline')?$select=EntitySetName.
*/
export const DATAVERSE_DEMANDLINE_ENTITY_SET = 'cree9_demandlines';
/**
* Колонка, куда пишем ссылку на заявку портала (создана вручную в Dataverse,
* отображаемое имя «cree9_tmctransferurl», однострочный текст). Указана владельцами
* внешней системы; прежняя URL-колонка cr498_portalrequesturl больше не используется.
* NB: логическое имя с префиксом cr498_ (издатель среды org95ca17d6.crm4 по
* умолчанию) — так сгенерировалось при создании колонки.
* Ссылка на заявку ~200+ символов (кириллица в пути страницы кодируется), поэтому
* макс. длина колонки должна быть не меньше 500 — иначе PATCH отклонится.
*/
export const DATAVERSE_DEMANDLINE_LINK_FIELD = 'cr498_cree9_tmctransferurl';
/**
* Колонка «ID позиции ЛЗК» на «Строке заявки» — GUID строки cree9_lzkposition
* ХРАНИТСЯ ТЕКСТОМ (не lookup-связью), поэтому её нельзя $expand'нуть — только
* читать значение и адресовать таблицу lzk position отдельным запросом.
* Используется для определения проекта-получателя («Куда») в форме «СЗ на
* перемещение ТМЦ» — см. shared/dataverse/lzkPosition.service.ts.
*/
export const DATAVERSE_DEMANDLINE_LZKPOSITION_FIELD = 'cree9_lzkpositionid';
/**
* EntitySetName таблицы «lzk position» (LogicalName cree9_lzkposition) — выведено
* по той же конвенции (logical + 's'), что и DATAVERSE_DEMANDLINE_ENTITY_SET.
*/
export const DATAVERSE_LZKPOSITION_ENTITY_SET = 'cree9_lzkpositions';
/** Lookup-поле на lzk position, ссылающееся на родительскую запись LZK. */
export const DATAVERSE_LZKPOSITION_LZK_LOOKUP_FIELD = 'cree9_lzkid';
/** EntitySetName таблицы «LZK» (LogicalName cree9_lzk). */
export const DATAVERSE_LZK_ENTITY_SET = 'cree9_lzks';
/** Колонка «project id» на LZK — GUID проекта текстом, совпадает по формату с oid из /Project. */
export const DATAVERSE_LZK_PROJECT_FIELD = 'cree9_projectid';
/**
* Сайт сравнительных таблиц (СТ): списки «Сравнительная таблица», «Позиции СТ»,
* «Оценка поставщиков», «Критерии», «Поставщики», «Курс Валют» живут здесь, а не
* на сайте данных портала. Читаем их из «Заявки на оплату» (forms/PaymentRequest).
*/
export const CT_SITE_URL = 'https://tnsukz.sharepoint.com/sites/sed';