feat: причина «Декрет» в подборе и приёме, тип приёма подтягивается из подбора
Tests / frontend-test (push) Successful in 1m13s
Tests / frontend-test (push) Successful in 1m13s
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
import { hireTypeFromSelection } from './hireTypeFromSelection';
|
||||
import { HIRE_TYPE_OPTIONS } from './model';
|
||||
|
||||
describe('hireTypeFromSelection', () => {
|
||||
it('«Декрет» → тип «Декрет» + ФИО замещаемого', () => {
|
||||
expect(hireTypeFromSelection('Декрет', 'Иванова Анна Ивановна')).toEqual({
|
||||
hireType: 'Декрет',
|
||||
replacedEmployee: 'Иванова Анна Ивановна'
|
||||
});
|
||||
});
|
||||
|
||||
it('«Замена уволенного сотрудника» → «Вместо уволенного» + ФИО', () => {
|
||||
expect(hireTypeFromSelection('Замена уволенного сотрудника', 'Петров Пётр Петрович')).toEqual({
|
||||
hireType: 'Вместо уволенного',
|
||||
replacedEmployee: 'Петров Пётр Петрович'
|
||||
});
|
||||
});
|
||||
|
||||
it('«Новая штатная единица» → «Новый сотрудник» без ФИО', () => {
|
||||
expect(hireTypeFromSelection('Новая штатная единица', 'что-то')).toEqual({
|
||||
hireType: 'Новый сотрудник',
|
||||
replacedEmployee: ''
|
||||
});
|
||||
});
|
||||
|
||||
it('перевод, «Другое» и пустая причина — без подстановки', () => {
|
||||
expect(hireTypeFromSelection('Перевод сотрудника', 'Сидоров')).toBeUndefined();
|
||||
expect(hireTypeFromSelection('Другое', 'уточнение')).toBeUndefined();
|
||||
expect(hireTypeFromSelection('', '')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('подставляемый тип всегда есть среди вариантов «Тип приёма»', () => {
|
||||
const allowed = HIRE_TYPE_OPTIONS.map((o) => o.value);
|
||||
['Декрет', 'Замена уволенного сотрудника', 'Новая штатная единица'].forEach((reason) => {
|
||||
expect(allowed).toContain(hireTypeFromSelection(reason, '')?.hireType);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import {
|
||||
OPEN_REASON_REPLACE,
|
||||
OPEN_REASON_NEW,
|
||||
OPEN_REASON_MATERNITY
|
||||
} from '../RecruitingSelection/model';
|
||||
import { HIRE_TYPE_NEW, HIRE_TYPE_REPLACEMENT, HIRE_TYPE_MATERNITY } from './model';
|
||||
|
||||
/**
|
||||
* Причина открытия вакансии (заявка на подбор) → тип приёма (заявка на приём).
|
||||
* «Перевод сотрудника» и «Другое» однозначного соответствия не имеют — их тут
|
||||
* нет, тип приёма в таком случае выбирает инициатор сам.
|
||||
*/
|
||||
const REASON_TO_HIRE_TYPE: Record<string, string> = {
|
||||
[OPEN_REASON_REPLACE]: HIRE_TYPE_REPLACEMENT,
|
||||
[OPEN_REASON_MATERNITY]: HIRE_TYPE_MATERNITY,
|
||||
[OPEN_REASON_NEW]: HIRE_TYPE_NEW
|
||||
};
|
||||
|
||||
/** Типы приёма, для которых переносим ФИО замещаемого сотрудника. */
|
||||
const HIRE_TYPES_WITH_REPLACED_EMPLOYEE = [HIRE_TYPE_REPLACEMENT, HIRE_TYPE_MATERNITY];
|
||||
|
||||
export interface IHireTypePrefill {
|
||||
hireType: string;
|
||||
replacedEmployee: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Что подставить в «Тип приёма» / «Взамен кого» по связанной заявке на подбор.
|
||||
* undefined — соответствия нет, поля приёма не трогаем.
|
||||
*/
|
||||
export function hireTypeFromSelection(openReason: string, openReasonDetails: string): IHireTypePrefill | undefined {
|
||||
const hireType = REASON_TO_HIRE_TYPE[openReason];
|
||||
if (!hireType) return undefined;
|
||||
const replacedEmployee = HIRE_TYPES_WITH_REPLACED_EMPLOYEE.indexOf(hireType) !== -1 ? openReasonDetails : '';
|
||||
return { hireType, replacedEmployee };
|
||||
}
|
||||
@@ -124,13 +124,19 @@ export const EMPTY_DATA: IFormData = {
|
||||
/** Подпись флажка HireOnArrival рядом с датой приёма. */
|
||||
export const HIRE_ON_ARRIVAL_LABEL = 'По приезду на объект';
|
||||
|
||||
export const HIRE_TYPE_NEW = 'Новый сотрудник';
|
||||
|
||||
/** Значение «Тип приёма», при котором показывается поле «Взамен кого». */
|
||||
export const HIRE_TYPE_REPLACEMENT = 'Вместо уволенного';
|
||||
|
||||
/** Приём на время декретного отпуска сотрудника — тоже спрашиваем «Взамен кого». */
|
||||
export const HIRE_TYPE_MATERNITY = 'Декрет';
|
||||
|
||||
/** Варианты «Тип приёма» — совпадают с choices колонки HireType. */
|
||||
export const HIRE_TYPE_OPTIONS: ISelectOption[] = [
|
||||
{ value: 'Новый сотрудник', label: 'Новый сотрудник' },
|
||||
{ value: HIRE_TYPE_REPLACEMENT, label: HIRE_TYPE_REPLACEMENT }
|
||||
{ value: HIRE_TYPE_NEW, label: HIRE_TYPE_NEW },
|
||||
{ value: HIRE_TYPE_REPLACEMENT, label: HIRE_TYPE_REPLACEMENT },
|
||||
{ value: HIRE_TYPE_MATERNITY, label: HIRE_TYPE_MATERNITY }
|
||||
];
|
||||
|
||||
/** Значение-триггер «Другое»: при нём показываем поле ручного ввода. */
|
||||
|
||||
@@ -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,17 +1,23 @@
|
||||
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 } from '../model';
|
||||
import { ISectionProps, HIRE_TYPE_OPTIONS, HIRE_TYPE_REPLACEMENT, 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_MATERNITY]: 'Взамен кого (Ф.И.О. сотрудника в декрете, полностью, пример: Иванова Анна Ивановна)'
|
||||
};
|
||||
|
||||
export const ApplicationSection: React.FC<ISectionProps> = ({ data, set, context }) => {
|
||||
const isReplacement = data.HireType === HIRE_TYPE_REPLACEMENT;
|
||||
const replacedEmployeeLabel = REPLACED_EMPLOYEE_LABELS[data.HireType];
|
||||
|
||||
const handleHireTypeChange = (value: string): void => {
|
||||
set('HireType', value);
|
||||
if (value !== HIRE_TYPE_REPLACEMENT) {
|
||||
set('HireReplacedEmployee', ''); // при не-замене очищаем скрытое поле
|
||||
if (!REPLACED_EMPLOYEE_LABELS[value]) {
|
||||
set('HireReplacedEmployee', ''); // тип без замещения — очищаем скрытое поле
|
||||
}
|
||||
};
|
||||
|
||||
@@ -77,9 +83,9 @@ export const ApplicationSection: React.FC<ISectionProps> = ({ data, set, context
|
||||
placeholder="— выберите —"
|
||||
/>
|
||||
</FormField>
|
||||
{isReplacement && (
|
||||
{replacedEmployeeLabel && (
|
||||
<FormField
|
||||
label="Взамен кого (Ф.И.О. уволенного, полностью, пример: Иванов Иван Иванович)"
|
||||
label={replacedEmployeeLabel}
|
||||
htmlFor="HireReplacedEmployee"
|
||||
full
|
||||
>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { routeUrl } from '../../../shared/routing';
|
||||
import { getApprovedSelectionRequests, ISelectionRequestOption } from '../selectionLookup';
|
||||
import { loadSelectionRequest } from '../../RecruitingSelection/load';
|
||||
import { IFormData, SetField } from '../model';
|
||||
import { hireTypeFromSelection } from '../hireTypeFromSelection';
|
||||
|
||||
/** Слаг формы подбора в реестре форм — цель ссылки (rptype). */
|
||||
const SELECTION_FORM_KEY = 'recruiting-selection';
|
||||
@@ -104,6 +105,11 @@ export const SelectionRequestField: React.FC<ISelectionRequestFieldProps> = ({ d
|
||||
set('ProbationPeriodOther', selection.SelectionProbationPeriodOther);
|
||||
set('SalaryProbation', selection.RateProbation);
|
||||
set('SalaryAfterProbation', selection.RateAfterProbation);
|
||||
const hireTypePrefill = hireTypeFromSelection(selection.VacancyOpenReason, selection.VacancyOpenReasonDetails);
|
||||
if (hireTypePrefill) {
|
||||
set('HireType', hireTypePrefill.hireType);
|
||||
set('HireReplacedEmployee', hireTypePrefill.replacedEmployee);
|
||||
}
|
||||
})
|
||||
.catch(() => undefined); // связь уже сохранена по id/коду — сбой обогащения не критичен
|
||||
};
|
||||
|
||||
@@ -161,12 +161,15 @@ export const OTHER_VALUE = 'Другое';
|
||||
export const OPEN_REASON_REPLACE = 'Замена уволенного сотрудника';
|
||||
export const OPEN_REASON_TRANSFER = 'Перевод сотрудника';
|
||||
export const OPEN_REASON_NEW = 'Новая штатная единица';
|
||||
/** Декрет — временная вакансия на время отпуска сотрудника; ФИО замещаемого нужно кадрам для срочного договора. */
|
||||
export const OPEN_REASON_MATERNITY = 'Декрет';
|
||||
|
||||
/** Варианты «Причина открытия вакансии» — совпадают с choices колонки VacancyOpenReason. */
|
||||
export const OPEN_REASON_OPTIONS: ISelectOption[] = [
|
||||
{ value: OPEN_REASON_REPLACE, label: OPEN_REASON_REPLACE },
|
||||
{ value: OPEN_REASON_TRANSFER, label: OPEN_REASON_TRANSFER },
|
||||
{ value: OPEN_REASON_NEW, label: OPEN_REASON_NEW },
|
||||
{ value: OPEN_REASON_MATERNITY, label: OPEN_REASON_MATERNITY },
|
||||
{ value: OTHER_VALUE, label: OTHER_VALUE }
|
||||
];
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
{ "internalName": "RequestStatus", "title": "Статус", "type": "Choice", "choices": ["Черновик", "На согласовании", "Согласовано", "Отклонено", "Возвращена на доработку", "Выполнение", "Закрыта"] },
|
||||
{ "internalName": "Responsible", "title": "Ответственный за подачу заявки", "type": "User", "userSelectionMode": "PeopleOnly", "required": true },
|
||||
{ "internalName": "SubmitDate", "title": "Дата подачи заявки", "type": "DateTime", "dateOnly": true },
|
||||
{ "internalName": "VacancyOpenReason", "title": "Причина открытия вакансии", "type": "Choice", "choices": ["Замена уволенного сотрудника", "Перевод сотрудника", "Новая штатная единица", "Другое"] },
|
||||
{ "internalName": "VacancyOpenReason", "title": "Причина открытия вакансии", "type": "Choice", "choices": ["Замена уволенного сотрудника", "Перевод сотрудника", "Новая штатная единица", "Декрет", "Другое"] },
|
||||
{ "internalName": "VacancyOpenReasonDetails", "title": "Причина открытия — ФИО / уточнение", "type": "Text" },
|
||||
{ "internalName": "CompaniesJson", "title": "Компании (JSON)", "type": "Note" },
|
||||
{ "internalName": "ObjectProject", "title": "Объект/Проект", "type": "Text" },
|
||||
|
||||
+6
-2
@@ -7,14 +7,18 @@ import {
|
||||
OPEN_REASON_OPTIONS,
|
||||
OPEN_REASON_REPLACE,
|
||||
OPEN_REASON_TRANSFER,
|
||||
OPEN_REASON_NEW
|
||||
OPEN_REASON_NEW,
|
||||
OPEN_REASON_MATERNITY
|
||||
} from '../model';
|
||||
|
||||
/** Причины, где вакансия открывается под конкретного сотрудника — спрашиваем его ФИО. */
|
||||
const REASONS_WITH_EMPLOYEE_NAME = [OPEN_REASON_REPLACE, OPEN_REASON_TRANSFER, OPEN_REASON_MATERNITY];
|
||||
|
||||
export const ApplicationSection: React.FC<ISectionProps> = ({ data, set, context, onResponsibleChange }) => {
|
||||
const reason = data.VacancyOpenReason;
|
||||
const showReasonDetails = reason !== '' && reason !== OPEN_REASON_NEW;
|
||||
const reasonDetailsLabel =
|
||||
reason === OPEN_REASON_REPLACE || reason === OPEN_REASON_TRANSFER
|
||||
REASONS_WITH_EMPLOYEE_NAME.indexOf(reason) !== -1
|
||||
? 'ФИО сотрудника (Полностью, пример: Иванов Иван Иванович)'
|
||||
: 'Уточните причину';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user