feat: отметка «Согласован СБ» с примечанием в заявке на приём
Инициатор при подаче отмечает, что кандидат согласован со службой безопасности; при отметке обязательно примечание СБ (без отметки поле скрыто и очищается). Поле информационное, на маршрут не влияет. Раздел «6. Служба безопасности» — перед вложениями, «Примечание для согласующих» стало разделом 7. Колонки HireSecurityApproved (Boolean), HireSecurityNote (Note). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -73,6 +73,7 @@ import { PpeSection } from './sections/PpeSection';
|
||||
import { ConditionsSection } from './sections/ConditionsSection';
|
||||
import { AgencySection } from './sections/AgencySection';
|
||||
import { NoteSection } from './sections/NoteSection';
|
||||
import { SecuritySection } from './sections/SecuritySection';
|
||||
import { useStableCallback } from '../../shared/useStableCallback';
|
||||
|
||||
const RecruitingHireForm: React.FC<IRequestFormProps> = ({
|
||||
@@ -409,6 +410,7 @@ const RecruitingHireForm: React.FC<IRequestFormProps> = ({
|
||||
<PpeSection {...sectionProps} />
|
||||
<ConditionsSection {...sectionProps} />
|
||||
<AgencySection {...sectionProps} />
|
||||
<SecuritySection {...sectionProps} />
|
||||
</FormModeProvider>
|
||||
</FieldChangesProvider>
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ import { PpeSection } from './sections/PpeSection';
|
||||
import { ConditionsSection } from './sections/ConditionsSection';
|
||||
import { AgencySection } from './sections/AgencySection';
|
||||
import { NoteSection } from './sections/NoteSection';
|
||||
import { SecuritySection } from './sections/SecuritySection';
|
||||
import { ApproverNoteBanner } from '../../shared/approverNote';
|
||||
import { useStableCallback } from '../../shared/useStableCallback';
|
||||
|
||||
@@ -416,6 +417,7 @@ const RecruitingHireView: React.FC<IRecruitingHireViewProps> = ({ context, itemI
|
||||
<PpeSection {...sectionProps} />
|
||||
<ConditionsSection {...sectionProps} />
|
||||
<AgencySection {...sectionProps} />
|
||||
<SecuritySection {...sectionProps} />
|
||||
</FormModeProvider>
|
||||
</FieldChangesProvider>
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ const SCALAR_FIELDS: (keyof IFormData)[] = [
|
||||
'HireCandidateName', 'HireHireDate', 'Position',
|
||||
'Department', 'SiteLocation', 'PpeHeight', 'PpeClothingSize', 'PpeShoeSize', 'ProbationPeriod', 'ProbationPeriodOther',
|
||||
'SalaryProbation', 'SalaryAfterProbation', 'WorkSchedule', 'WorkScheduleOther',
|
||||
'AgencyName', 'AgencyTerms', 'HireApproverNote',
|
||||
'AgencyName', 'AgencyTerms', 'HireSecurityNote', 'HireApproverNote',
|
||||
'SelectionRequestId', 'SelectionRequestListId', 'SelectionRequestCode'
|
||||
];
|
||||
|
||||
@@ -62,6 +62,7 @@ export async function loadHireRequest(itemId: number, listId?: string): Promise<
|
||||
'ProjectCode',
|
||||
'RecruitingAgency',
|
||||
'HireOnArrival',
|
||||
'HireSecurityApproved',
|
||||
'HireApproverNoteImportant',
|
||||
APPROVER_CHANGES_FIELD,
|
||||
'RequestStatus',
|
||||
@@ -79,6 +80,7 @@ export async function loadHireRequest(itemId: number, listId?: string): Promise<
|
||||
// Флаг агентства — булево поле (Yes/No), не строка.
|
||||
(data as unknown as Record<string, unknown>).RecruitingAgency = !!item.RecruitingAgency;
|
||||
(data as unknown as Record<string, unknown>).HireOnArrival = !!item.HireOnArrival;
|
||||
data.HireSecurityApproved = !!item.HireSecurityApproved;
|
||||
data.HireApproverNoteImportant = !!item.HireApproverNoteImportant;
|
||||
|
||||
SCALAR_FIELDS.forEach((key) => {
|
||||
|
||||
@@ -79,6 +79,11 @@ export interface IFormData {
|
||||
RecruitingAgency: boolean;
|
||||
AgencyName: string;
|
||||
AgencyTerms: string;
|
||||
// Служба безопасности: инициатор при подаче отмечает, что кандидат согласован
|
||||
// с СБ; при отметке обязательно примечание (см. validation.ts). Информационное —
|
||||
// на маршрут и решения не влияет.
|
||||
HireSecurityApproved: boolean;
|
||||
HireSecurityNote: string;
|
||||
// Примечание для согласующих (shared/approverNote): текст + «Важное».
|
||||
// Префикс Hire — чтобы internalName не совпал с одноимёнными колонками других форм.
|
||||
HireApproverNote: string;
|
||||
@@ -124,6 +129,8 @@ export const EMPTY_DATA: IFormData = {
|
||||
RecruitingAgency: false,
|
||||
AgencyName: '',
|
||||
AgencyTerms: '',
|
||||
HireSecurityApproved: false,
|
||||
HireSecurityNote: '',
|
||||
HireApproverNote: '',
|
||||
HireApproverNoteImportant: false
|
||||
};
|
||||
|
||||
@@ -46,6 +46,9 @@
|
||||
{ "internalName": "AgencyName", "title": "Название компании (агентство)", "type": "Text" },
|
||||
{ "internalName": "AgencyTerms", "title": "Условия (агентство)", "type": "Note" },
|
||||
|
||||
{ "internalName": "HireSecurityApproved", "title": "Согласован СБ", "type": "Boolean" },
|
||||
{ "internalName": "HireSecurityNote", "title": "Примечание СБ", "type": "Note" },
|
||||
|
||||
{ "internalName": "HireApproverNote", "title": "Примечание для согласующих", "type": "Note" },
|
||||
{ "internalName": "HireApproverNoteImportant", "title": "Примечание — важное", "type": "Boolean" }
|
||||
]
|
||||
|
||||
@@ -8,10 +8,10 @@ const NOTE_FIELD_KEYS: IApproverNoteFieldKeys = {
|
||||
important: 'HireApproverNoteImportant'
|
||||
};
|
||||
|
||||
/** 6. Примечание для согласующих — общий раздел shared/approverNote на полях приёма. */
|
||||
/** 7. Примечание для согласующих — общий раздел shared/approverNote на полях приёма. */
|
||||
export const NoteSection: React.FC<ISectionProps> = ({ data, set }) => (
|
||||
<ApproverNoteSection
|
||||
title="6. Примечание для согласующих"
|
||||
title="7. Примечание для согласующих"
|
||||
value={{ text: data.HireApproverNote, important: data.HireApproverNoteImportant }}
|
||||
onChange={(note) => {
|
||||
set('HireApproverNote', note.text);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import * as React from 'react';
|
||||
import { CollapseSection, FormField, TextArea, Toggle } from '../../../shared/ui';
|
||||
import { ISectionProps } from '../model';
|
||||
|
||||
/**
|
||||
* 6. Служба безопасности: инициатор отмечает, что кандидат согласован с СБ, и
|
||||
* тогда обязательно пишет примечание (см. validation.ts). Поле информационное —
|
||||
* на маршрут и решения согласующих не влияет. При снятии отметки примечание
|
||||
* очищается, чтобы в заявку не ушёл текст скрытого поля (как в AgencySection).
|
||||
*/
|
||||
export const SecuritySection: React.FC<ISectionProps> = ({ data, set }) => {
|
||||
const handleToggle = (value: boolean): void => {
|
||||
set('HireSecurityApproved', value);
|
||||
if (!value) set('HireSecurityNote', '');
|
||||
};
|
||||
|
||||
return (
|
||||
<CollapseSection title="6. Служба безопасности" icon="user-check">
|
||||
<FormField label="6.1 Согласован СБ" fieldKey="HireSecurityApproved">
|
||||
<Toggle checked={data.HireSecurityApproved} onChange={handleToggle} onLabel="Да" />
|
||||
</FormField>
|
||||
{data.HireSecurityApproved && (
|
||||
<FormField label="6.2 Примечание СБ" htmlFor="HireSecurityNote" required full>
|
||||
<TextArea
|
||||
id="HireSecurityNote"
|
||||
value={data.HireSecurityNote}
|
||||
onChange={(v) => set('HireSecurityNote', v)}
|
||||
placeholder="Кто и когда согласовал, замечания СБ"
|
||||
/>
|
||||
</FormField>
|
||||
)}
|
||||
</CollapseSection>
|
||||
);
|
||||
};
|
||||
@@ -38,6 +38,16 @@ describe('RecruitingHire validation', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('«Согласован СБ» требует примечание СБ, без отметки — не требует', () => {
|
||||
expect(labels(submitIssues(DRAFT_READY))).not.toContain('6.2 Примечание СБ');
|
||||
expect(labels(submitIssues({ ...DRAFT_READY, HireSecurityApproved: true }))).toContain('6.2 Примечание СБ');
|
||||
expect(
|
||||
labels(submitIssues({ ...DRAFT_READY, HireSecurityApproved: true, HireSecurityNote: 'Проверка пройдена' }))
|
||||
).not.toContain('6.2 Примечание СБ');
|
||||
// Черновик без примечания сохранить можно.
|
||||
expect(draftIssues({ ...DRAFT_READY, HireSecurityApproved: true })).toEqual([]);
|
||||
});
|
||||
|
||||
it('«По приезду на объект» снимает обязательность даты приёма', () => {
|
||||
expect(labels(submitIssues({ ...DRAFT_READY, HireOnArrival: true }))).not.toContain('2.2 Дата приёма');
|
||||
});
|
||||
|
||||
@@ -19,6 +19,7 @@ interface IRequiredRule extends IFieldIssue {
|
||||
* агентство» не проверяется — у булевых нет пустого состояния.
|
||||
* 2.2 «Дата приёма» не нужна, если отмечено «По приезду на объект»
|
||||
* (HireOnArrival) — тогда дату проставляет исполнитель на своём шаге.
|
||||
* 6.2 «Примечание СБ» обязательно только при отметке 6.1 «Согласован СБ».
|
||||
*
|
||||
* 1.3 «Проект» — хотя бы один проект из справочника: первый уходит в resolve
|
||||
* маршрута (projectId), а без него approval-route отвечал 404 и заявка ушла бы
|
||||
@@ -48,7 +49,13 @@ const REQUIRED_RULES: ReadonlyArray<IRequiredRule> = [
|
||||
{ field: 'ProbationPeriod', label: '4.1 Испытательный срок', ok: (d) => !!d.ProbationPeriod },
|
||||
{ field: 'SalaryProbation', label: '4.2 Оклад при испытательном сроке', ok: (d) => !!d.SalaryProbation.trim() },
|
||||
{ field: 'SalaryAfterProbation', label: '4.3 Оклад по завершении ИС', ok: (d) => !!d.SalaryAfterProbation.trim() },
|
||||
{ field: 'WorkSchedule', label: '4.4 График работы', ok: (d) => !!d.WorkSchedule }
|
||||
{ field: 'WorkSchedule', label: '4.4 График работы', ok: (d) => !!d.WorkSchedule },
|
||||
{
|
||||
field: 'HireSecurityNote',
|
||||
label: '6.2 Примечание СБ',
|
||||
message: 'отмечено «Согласован СБ» — укажите примечание',
|
||||
ok: (d) => !d.HireSecurityApproved || !!d.HireSecurityNote.trim()
|
||||
}
|
||||
];
|
||||
|
||||
function toIssue({ field, label, message }: IRequiredRule): IFieldIssue {
|
||||
|
||||
Reference in New Issue
Block a user