скрипт

This commit is contained in:
gorbenko.pavel
2026-08-17 13:19:43 +05:00
parent 364835eaf7
commit e2c7f96021
37 changed files with 377 additions and 198 deletions
@@ -36,8 +36,9 @@ import {
} from '../../shared/attachments/attachments.service';
import { effectiveUserEmail } from '../../shared/impersonation';
import { IRequestFormProps } from '../../models/types';
import { IFormData, EMPTY_DATA, SetField } from './model';
import { saveHireRequest, hireApprovalService, DATA_LIST_TITLE, APPROVAL_LIST_TITLE } from './submit';
import { approvalListTitle, dataListTitle } from '../../shared/listTitles';
import { FORM_ID, IFormData, EMPTY_DATA, SetField } from './model';
import { saveHireRequest, hireApprovalService } from './submit';
import { loadHireRequest } from './load';
import { ApplicationSection } from './sections/ApplicationSection';
import { CandidateSection } from './sections/CandidateSection';
@@ -66,7 +67,7 @@ const RecruitingHireForm: React.FC<IRequestFormProps> = ({ requestType, context,
return;
}
let alive = true;
Promise.all([loadHireRequest(editId), getAttachments(DATA_LIST_TITLE, editId)])
Promise.all([loadHireRequest(editId), getAttachments(dataListTitle(FORM_ID), editId)])
.then(([loaded, atts]) => {
if (alive) {
setData(loaded.data);
@@ -157,7 +158,7 @@ const RecruitingHireForm: React.FC<IRequestFormProps> = ({ requestType, context,
if (requestId === undefined) {
return;
}
deleteAttachment(DATA_LIST_TITLE, requestId, fileName)
deleteAttachment(dataListTitle(FORM_ID), requestId, fileName)
.then(() => setExistingAttachments((prev) => prev.filter((a) => a.fileName !== fileName)))
.catch(() => undefined);
};
@@ -184,9 +185,9 @@ const RecruitingHireForm: React.FC<IRequestFormProps> = ({ requestType, context,
setRequestCode(code);
}
if (files.length > 0) {
await uploadAttachments(DATA_LIST_TITLE, id, files); // файлы — после создания элемента
await uploadAttachments(dataListTitle(FORM_ID), id, files); // файлы — после создания элемента
setFiles([]); // выгруженные файлы убираем, чтобы пересохранение не дублировало
setExistingAttachments(await getAttachments(DATA_LIST_TITLE, id)); // показать добавленные
setExistingAttachments(await getAttachments(dataListTitle(FORM_ID), id)); // показать добавленные
}
if (onSaved) {
onSaved();
@@ -195,8 +196,8 @@ const RecruitingHireForm: React.FC<IRequestFormProps> = ({ requestType, context,
if (status === REQUEST_STATUS.onApproval) {
await notifyActiveStep(context, approval, id, {
requestTypeId: rtId,
dataListName: DATA_LIST_TITLE,
approvalListName: APPROVAL_LIST_TITLE,
dataListName: dataListTitle(FORM_ID),
approvalListName: approvalListTitle(FORM_ID),
});
}
};
@@ -236,7 +237,7 @@ const RecruitingHireForm: React.FC<IRequestFormProps> = ({ requestType, context,
<FormCard
title={requestType.title}
subtitle="Категория: Отдел рекрутинга"
belowSubtitle={<InitiatorLine listTitle={DATA_LIST_TITLE} itemId={requestId} />}
belowSubtitle={<InitiatorLine listTitle={dataListTitle(FORM_ID)} itemId={requestId} />}
headerRight={
requestId || requestStatus ? (
<>
@@ -15,10 +15,11 @@ import {
} from '../../shared/approval';
import { navigate } from '../../shared/routing';
import { getAttachments, IAttachmentInfo } from '../../shared/attachments/attachments.service';
import { IFormData, EMPTY_DATA, SetField } from './model';
import { approvalListTitle, dataListTitle } from '../../shared/listTitles';
import { FORM_ID, IFormData, EMPTY_DATA, SetField } from './model';
import { loadHireRequest } from './load';
import { confirmDiscardFormChanges } from '../../shared/unsavedChanges';
import { hireApprovalService, DATA_LIST_TITLE, APPROVAL_LIST_TITLE } from './submit';
import { hireApprovalService } from './submit';
import { ApplicationSection } from './sections/ApplicationSection';
import { CandidateSection } from './sections/CandidateSection';
import { PpeSection } from './sections/PpeSection';
@@ -53,7 +54,7 @@ const RecruitingHireView: React.FC<IRecruitingHireViewProps> = ({ context, itemI
// Сервис решений согласующего: список согласования + список данных этой формы.
const approverService = useMemo(
() => new ApproverService(hireApprovalService(), DATA_LIST_TITLE),
() => new ApproverService(hireApprovalService(), dataListTitle(FORM_ID)),
[]
);
@@ -90,7 +91,7 @@ const RecruitingHireView: React.FC<IRecruitingHireViewProps> = ({ context, itemI
useEffect(() => {
let alive = true;
setLoading(true);
Promise.all([loadHireRequest(itemId), getAttachments(DATA_LIST_TITLE, itemId)])
Promise.all([loadHireRequest(itemId), getAttachments(dataListTitle(FORM_ID), itemId)])
.then(([loaded, files]) => {
if (alive) {
setData(loaded.data);
@@ -129,8 +130,8 @@ const RecruitingHireView: React.FC<IRecruitingHireViewProps> = ({ context, itemI
if (!requestTypeId) return;
notifyActiveStep(context, approval, itemId, {
requestTypeId,
dataListName: DATA_LIST_TITLE,
approvalListName: APPROVAL_LIST_TITLE,
dataListName: dataListTitle(FORM_ID),
approvalListName: approvalListTitle(FORM_ID),
}).catch(() => undefined);
};
@@ -159,7 +160,7 @@ const RecruitingHireView: React.FC<IRecruitingHireViewProps> = ({ context, itemI
<FormCard
title="Заявка на прием"
subtitle={`№${code || itemId}`}
belowSubtitle={<InitiatorLine listTitle={DATA_LIST_TITLE} itemId={itemId} />}
belowSubtitle={<InitiatorLine listTitle={dataListTitle(FORM_ID)} itemId={itemId} />}
headerRight={status ? <StatusBadge status={status} /> : undefined}
>
<FormModeProvider readOnly>
@@ -1,7 +1,6 @@
import { getSP } from '../../shared/pnp/pnpjs';
import { IFormData, EMPTY_DATA } from './model';
const LIST_TITLE = 'Прием на работу — данные';
import { dataListTitle } from '../../shared/listTitles';
import { FORM_ID, IFormData, EMPTY_DATA } from './model';
// Скалярные поля (текст/выбор/число/дата) — читаем как есть, приводим к строке.
const SCALAR_FIELDS: (keyof IFormData)[] = [
@@ -21,7 +20,7 @@ export interface ILoadedRequest {
export async function loadHireRequest(itemId: number): Promise<ILoadedRequest> {
const sp = getSP();
const item = (await sp.web.lists
.getByTitle(LIST_TITLE)
.getByTitle(dataListTitle(FORM_ID))
.items.getById(itemId)
.select(...SCALAR_FIELDS, 'RequestStatus', 'RequestUniqueCode')()) as Record<string, unknown>;
@@ -1,9 +1,16 @@
import { WebPartContext } from '@microsoft/sp-webpart-base';
import { ISelectOption } from '../../shared/ui';
import { RequestTypeId } from '../../models/types';
/**
* Данные формы «Заявка на прием». Ключи = internalName колонок SP-списка
* «Прием на работу — данные», чтобы при записи маппинг был 1:1.
* Тип заявки этой формы — ключ реестра форм (forms/registry.ts). Имена списков
* SharePoint выводятся из него, см. shared/listTitles.ts.
*/
export const FORM_ID: RequestTypeId = 'recruiting-hire';
/**
* Данные формы «Заявка на прием». Ключи = internalName колонок списка данных
* этой формы (см. shared/listTitles.ts), чтобы при записи маппинг был 1:1.
*/
export interface IFormData {
// Заявка (шапка)
@@ -1,17 +1,15 @@
import { getSP } from '../../shared/pnp/pnpjs';
import { ApprovalListService, IApproverToWrite, writeRoute } from '../../shared/approval';
import { buildRequestUniqueCode } from '../../shared/requestCode';
import { IFormData } from './model';
export const DATA_LIST_TITLE = 'Прием на работу — данные';
export const APPROVAL_LIST_TITLE = 'Прием на работу — согласование';
import { approvalListTitle, dataListTitle } from '../../shared/listTitles';
import { FORM_ID, IFormData } from './model';
// Числовые поля: пустая строка → null, иначе Number().
const NUMBER_FIELDS: (keyof IFormData)[] = ['PpeHeight', 'PpeShoeSize', 'SalaryProbation', 'SalaryAfterProbation'];
/** Экземпляр сервиса списка согласования этой формы. */
export function hireApprovalService(): ApprovalListService {
return new ApprovalListService(APPROVAL_LIST_TITLE);
return new ApprovalListService(approvalListTitle(FORM_ID));
}
/** Осмысленный заголовок элемента списка (системная колонка Title). */
@@ -56,7 +54,7 @@ export async function saveHireRequest(
executors: IApproverToWrite[],
status: string
): Promise<ISaveResult> {
const list = getSP().web.lists.getByTitle(DATA_LIST_TITLE);
const list = getSP().web.lists.getByTitle(dataListTitle(FORM_ID));
const item = buildItem(data, status);
let id: number;