feat: тип приёма «Вместо переведенного сотрудника»

This commit is contained in:
Тимур Городецкий
2026-09-30 12:03:20 +05:00
parent 4f292dde3d
commit c95118a216
7 changed files with 37 additions and 14 deletions
@@ -22,7 +22,8 @@
`RecruitingAgency`; при включении показываются «Название компании» (`AgencyName`)
и «Условия» (`AgencyTerms`), при выключении оба поля очищаются.
- **Условное поле «Взамен кого» (ReplacedEmployee)** — показывается только когда
тип приёма = замена уволенного (см. `HIRE_TYPE_REPLACEMENT` в модели).
тип приёма = вместо уволенного / вместо переведенного / декрет
(см. `REPLACED_EMPLOYEE_LABELS` в `sections/ApplicationSection.tsx`).
- Секция **СИЗ** (рост, размеры одежды/обуви).
- **Является источником для «Организация рабочего места»** — из просмотра этой
заявки кнопка «Организация рабочего места на основе этой» открывает форму
@@ -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);
});
});
@@ -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<string, string> = {
[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;
@@ -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,
@@ -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<string, string> = {
'Вместо уволенного': HIRE_TYPE_REPLACEMENT
};
/** Значение-триггер «Другое»: при нём показываем поле ручного ввода. */
export const OTHER_VALUE = 'Другое';
@@ -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" },
@@ -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<string, string> = {
[HIRE_TYPE_REPLACEMENT]: 'Взамен кого (Ф.И.О. уволенного, полностью, пример: Иванов Иван Иванович)',
[HIRE_TYPE_TRANSFER]: 'Взамен кого (Ф.И.О. переведенного сотрудника, полностью, пример: Иванов Иван Иванович)',
[HIRE_TYPE_MATERNITY]: 'Взамен кого (Ф.И.О. сотрудника в декрете, полностью, пример: Иванова Анна Ивановна)'
};