From c95118a2163a295ed7a90d14fa2644c7c2d991d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=93=D0=BE=D1=80=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5=D1=86=D0=BA=D0=B8=D0=B9?= Date: Wed, 30 Sep 2026 12:03:20 +0500 Subject: [PATCH] =?UTF-8?q?feat:=20=D1=82=D0=B8=D0=BF=20=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=D1=91=D0=BC=D0=B0=20=C2=AB=D0=92=D0=BC=D0=B5=D1=81=D1=82=D0=BE?= =?UTF-8?q?=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B2=D0=B5=D0=B4=D0=B5=D0=BD=D0=BD?= =?UTF-8?q?=D0=BE=D0=B3=D0=BE=20=D1=81=D0=BE=D1=82=D1=80=D1=83=D0=B4=D0=BD?= =?UTF-8?q?=D0=B8=D0=BA=D0=B0=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../requestPortal/forms/RecruitingHire/README.md | 3 ++- .../RecruitingHire/hireTypeFromSelection.test.ts | 16 +++++++++++----- .../RecruitingHire/hireTypeFromSelection.ts | 10 ++++++---- .../requestPortal/forms/RecruitingHire/load.ts | 3 ++- .../requestPortal/forms/RecruitingHire/model.ts | 14 +++++++++++++- .../forms/RecruitingHire/provisioning.json | 2 +- .../sections/ApplicationSection.tsx | 3 ++- 7 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/webparts/requestPortal/forms/RecruitingHire/README.md b/src/webparts/requestPortal/forms/RecruitingHire/README.md index 11336a2..93d96ff 100644 --- a/src/webparts/requestPortal/forms/RecruitingHire/README.md +++ b/src/webparts/requestPortal/forms/RecruitingHire/README.md @@ -22,7 +22,8 @@ `RecruitingAgency`; при включении показываются «Название компании» (`AgencyName`) и «Условия» (`AgencyTerms`), при выключении оба поля очищаются. - **Условное поле «Взамен кого» (ReplacedEmployee)** — показывается только когда - тип приёма = замена уволенного (см. `HIRE_TYPE_REPLACEMENT` в модели). + тип приёма = вместо уволенного / вместо переведенного / декрет + (см. `REPLACED_EMPLOYEE_LABELS` в `sections/ApplicationSection.tsx`). - Секция **СИЗ** (рост, размеры одежды/обуви). - **Является источником для «Организация рабочего места»** — из просмотра этой заявки кнопка «Организация рабочего места на основе этой» открывает форму diff --git a/src/webparts/requestPortal/forms/RecruitingHire/hireTypeFromSelection.test.ts b/src/webparts/requestPortal/forms/RecruitingHire/hireTypeFromSelection.test.ts index 7b538ee..8bbd96b 100644 --- a/src/webparts/requestPortal/forms/RecruitingHire/hireTypeFromSelection.test.ts +++ b/src/webparts/requestPortal/forms/RecruitingHire/hireTypeFromSelection.test.ts @@ -9,13 +9,20 @@ describe('hireTypeFromSelection', () => { }); }); - it('«Замена уволенного сотрудника» → «Вместо уволенного» + ФИО', () => { + it('«Замена уволенного сотрудника» → «Вместо уволенного сотрудника» + ФИО', () => { expect(hireTypeFromSelection('Замена уволенного сотрудника', 'Петров Пётр Петрович')).toEqual({ - hireType: 'Вместо уволенного', + hireType: 'Вместо уволенного сотрудника', replacedEmployee: 'Петров Пётр Петрович' }); }); + it('«Перевод сотрудника» → «Вместо переведенного сотрудника» + ФИО', () => { + expect(hireTypeFromSelection('Перевод сотрудника', 'Сидоров Сидор Сидорович')).toEqual({ + hireType: 'Вместо переведенного сотрудника', + replacedEmployee: 'Сидоров Сидор Сидорович' + }); + }); + it('«Новая штатная единица» → «Новый сотрудник» без ФИО', () => { expect(hireTypeFromSelection('Новая штатная единица', 'что-то')).toEqual({ hireType: 'Новый сотрудник', @@ -23,15 +30,14 @@ describe('hireTypeFromSelection', () => { }); }); - it('перевод, «Другое» и пустая причина — без подстановки', () => { - expect(hireTypeFromSelection('Перевод сотрудника', 'Сидоров')).toBeUndefined(); + it('«Другое» и пустая причина — без подстановки', () => { expect(hireTypeFromSelection('Другое', 'уточнение')).toBeUndefined(); expect(hireTypeFromSelection('', '')).toBeUndefined(); }); it('подставляемый тип всегда есть среди вариантов «Тип приёма»', () => { const allowed = HIRE_TYPE_OPTIONS.map((o) => o.value); - ['Декрет', 'Замена уволенного сотрудника', 'Новая штатная единица'].forEach((reason) => { + ['Декрет', 'Замена уволенного сотрудника', 'Перевод сотрудника', 'Новая штатная единица'].forEach((reason) => { expect(allowed).toContain(hireTypeFromSelection(reason, '')?.hireType); }); }); diff --git a/src/webparts/requestPortal/forms/RecruitingHire/hireTypeFromSelection.ts b/src/webparts/requestPortal/forms/RecruitingHire/hireTypeFromSelection.ts index 215c8aa..03115fe 100644 --- a/src/webparts/requestPortal/forms/RecruitingHire/hireTypeFromSelection.ts +++ b/src/webparts/requestPortal/forms/RecruitingHire/hireTypeFromSelection.ts @@ -1,23 +1,25 @@ import { OPEN_REASON_REPLACE, + OPEN_REASON_TRANSFER, OPEN_REASON_NEW, OPEN_REASON_MATERNITY } from '../RecruitingSelection/model'; -import { HIRE_TYPE_NEW, HIRE_TYPE_REPLACEMENT, HIRE_TYPE_MATERNITY } from './model'; +import { HIRE_TYPE_NEW, HIRE_TYPE_REPLACEMENT, HIRE_TYPE_TRANSFER, HIRE_TYPE_MATERNITY } from './model'; /** * Причина открытия вакансии (заявка на подбор) → тип приёма (заявка на приём). - * «Перевод сотрудника» и «Другое» однозначного соответствия не имеют — их тут - * нет, тип приёма в таком случае выбирает инициатор сам. + * «Другое» однозначного соответствия не имеет — его тут нет, тип приёма в таком + * случае выбирает инициатор сам. */ const REASON_TO_HIRE_TYPE: Record = { [OPEN_REASON_REPLACE]: HIRE_TYPE_REPLACEMENT, + [OPEN_REASON_TRANSFER]: HIRE_TYPE_TRANSFER, [OPEN_REASON_MATERNITY]: HIRE_TYPE_MATERNITY, [OPEN_REASON_NEW]: HIRE_TYPE_NEW }; /** Типы приёма, для которых переносим ФИО замещаемого сотрудника. */ -const HIRE_TYPES_WITH_REPLACED_EMPLOYEE = [HIRE_TYPE_REPLACEMENT, HIRE_TYPE_MATERNITY]; +const HIRE_TYPES_WITH_REPLACED_EMPLOYEE = [HIRE_TYPE_REPLACEMENT, HIRE_TYPE_TRANSFER, HIRE_TYPE_MATERNITY]; export interface IHireTypePrefill { hireType: string; diff --git a/src/webparts/requestPortal/forms/RecruitingHire/load.ts b/src/webparts/requestPortal/forms/RecruitingHire/load.ts index 196f226..f645ab3 100644 --- a/src/webparts/requestPortal/forms/RecruitingHire/load.ts +++ b/src/webparts/requestPortal/forms/RecruitingHire/load.ts @@ -2,7 +2,7 @@ import { getSP } from '../../shared/pnp/pnpjs'; import { ICompany } from '../../shared/dictionary'; import { dataListTitle } from '../../shared/listTitles'; import { IFieldChange, parseFieldChanges } from '../../shared/fieldChanges/fieldChanges'; -import { FORM_ID, IFormData, EMPTY_DATA, ICompanyShare } from './model'; +import { FORM_ID, IFormData, EMPTY_DATA, ICompanyShare, LEGACY_HIRE_TYPES } from './model'; import { APPROVER_CHANGES_FIELD } from './changeTracking'; /** Безопасный разбор JSON-массива из Note-колонки (пустой при ошибке/пустом). */ @@ -81,6 +81,7 @@ export async function loadHireRequest(itemId: number, listId?: string): Promise< writable[key] = String(value); } }); + data.HireType = LEGACY_HIRE_TYPES[data.HireType] || data.HireType; return { data, diff --git a/src/webparts/requestPortal/forms/RecruitingHire/model.ts b/src/webparts/requestPortal/forms/RecruitingHire/model.ts index 7fb4420..94606a2 100644 --- a/src/webparts/requestPortal/forms/RecruitingHire/model.ts +++ b/src/webparts/requestPortal/forms/RecruitingHire/model.ts @@ -127,7 +127,10 @@ export const HIRE_ON_ARRIVAL_LABEL = 'По приезду на объект'; export const HIRE_TYPE_NEW = 'Новый сотрудник'; /** Значение «Тип приёма», при котором показывается поле «Взамен кого». */ -export const HIRE_TYPE_REPLACEMENT = 'Вместо уволенного'; +export const HIRE_TYPE_REPLACEMENT = 'Вместо уволенного сотрудника'; + +/** Приём на место сотрудника, переведённого на другую должность, — тоже спрашиваем «Взамен кого». */ +export const HIRE_TYPE_TRANSFER = 'Вместо переведенного сотрудника'; /** Приём на время декретного отпуска сотрудника — тоже спрашиваем «Взамен кого». */ export const HIRE_TYPE_MATERNITY = 'Декрет'; @@ -136,9 +139,18 @@ export const HIRE_TYPE_MATERNITY = 'Декрет'; export const HIRE_TYPE_OPTIONS: ISelectOption[] = [ { value: HIRE_TYPE_NEW, label: HIRE_TYPE_NEW }, { value: HIRE_TYPE_REPLACEMENT, label: HIRE_TYPE_REPLACEMENT }, + { value: HIRE_TYPE_TRANSFER, label: HIRE_TYPE_TRANSFER }, { value: HIRE_TYPE_MATERNITY, label: HIRE_TYPE_MATERNITY } ]; +/** + * Прежние значения «Тип приёма» → текущие. В уже поданных заявках SP хранит + * старый текст; без замены при загрузке поле 1.5 в них показывалось бы пустым. + */ +export const LEGACY_HIRE_TYPES: Record = { + 'Вместо уволенного': HIRE_TYPE_REPLACEMENT +}; + /** Значение-триггер «Другое»: при нём показываем поле ручного ввода. */ export const OTHER_VALUE = 'Другое'; diff --git a/src/webparts/requestPortal/forms/RecruitingHire/provisioning.json b/src/webparts/requestPortal/forms/RecruitingHire/provisioning.json index dcadc87..830f713 100644 --- a/src/webparts/requestPortal/forms/RecruitingHire/provisioning.json +++ b/src/webparts/requestPortal/forms/RecruitingHire/provisioning.json @@ -9,7 +9,7 @@ { "internalName": "CompanySharesJson", "title": "Распределение ЗП по компаниям (JSON)", "type": "Note" }, { "internalName": "Phone", "title": "Телефон", "type": "Text" }, - { "internalName": "HireType", "title": "Тип приёма", "type": "Choice", "choices": ["Новый сотрудник", "Вместо уволенного", "Декрет"] }, + { "internalName": "HireType", "title": "Тип приёма", "type": "Choice", "choices": ["Новый сотрудник", "Вместо уволенного сотрудника", "Вместо переведенного сотрудника", "Декрет"] }, { "internalName": "HireReplacedEmployee", "title": "Взамен кого (Ф.И.О. уволенного)", "type": "Text" }, { "internalName": "Project", "title": "Проект", "type": "Text" }, diff --git a/src/webparts/requestPortal/forms/RecruitingHire/sections/ApplicationSection.tsx b/src/webparts/requestPortal/forms/RecruitingHire/sections/ApplicationSection.tsx index 1d085a7..297cbc5 100644 --- a/src/webparts/requestPortal/forms/RecruitingHire/sections/ApplicationSection.tsx +++ b/src/webparts/requestPortal/forms/RecruitingHire/sections/ApplicationSection.tsx @@ -1,13 +1,14 @@ import * as React from 'react'; import { CollapseSection, FormField, TextInput, PhoneInput, Select } from '../../../shared/ui'; import { CompanyPickerMulti, ProjectPicker, ICompany } from '../../../shared/dictionary'; -import { ISectionProps, HIRE_TYPE_OPTIONS, HIRE_TYPE_REPLACEMENT, HIRE_TYPE_MATERNITY } from '../model'; +import { ISectionProps, HIRE_TYPE_OPTIONS, HIRE_TYPE_REPLACEMENT, HIRE_TYPE_TRANSFER, HIRE_TYPE_MATERNITY } from '../model'; import { SelectionRequestField } from './SelectionRequestField'; import { CompanySharesField } from './CompanySharesField'; /** Подпись поля «Взамен кого» — уточняем, чьё ФИО вписывать, по типу приёма. */ const REPLACED_EMPLOYEE_LABELS: Record = { [HIRE_TYPE_REPLACEMENT]: 'Взамен кого (Ф.И.О. уволенного, полностью, пример: Иванов Иван Иванович)', + [HIRE_TYPE_TRANSFER]: 'Взамен кого (Ф.И.О. переведенного сотрудника, полностью, пример: Иванов Иван Иванович)', [HIRE_TYPE_MATERNITY]: 'Взамен кого (Ф.И.О. сотрудника в декрете, полностью, пример: Иванова Анна Ивановна)' };