From 4b05da54690ee08400ab0753837b448eb977773f 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: Mon, 12 Oct 2026 13:59:58 +0500 Subject: [PATCH] =?UTF-8?q?feat:=20=D1=82=D1=80=D1=83=D0=B4=D0=BE=D0=B2?= =?UTF-8?q?=D0=BE=D0=B9=20=D0=BE=D1=82=D0=BF=D1=83=D1=81=D0=BA=20=E2=80=94?= =?UTF-8?q?=20=D0=BD=D0=B5=20=D0=B1=D0=BE=D0=BB=D0=B5=D0=B5=2024=20=D0=B4?= =?UTF-8?q?=D0=BD=D0=B5=D0=B9=20=D0=B2=20=D0=BE=D0=B4=D0=BD=D0=BE=D0=B9=20?= =?UTF-8?q?=D0=B7=D0=B0=D1=8F=D0=B2=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Требование HR: ограничение на поле «Количество дней» и проверка при отправке с понятным сообщением. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../requestPortal/forms/Vacation/VacationForm.tsx | 7 +++++-- src/webparts/requestPortal/forms/Vacation/model.ts | 2 ++ .../forms/Vacation/sections/RequestSection.tsx | 11 +++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/webparts/requestPortal/forms/Vacation/VacationForm.tsx b/src/webparts/requestPortal/forms/Vacation/VacationForm.tsx index e45441a..7d56922 100644 --- a/src/webparts/requestPortal/forms/Vacation/VacationForm.tsx +++ b/src/webparts/requestPortal/forms/Vacation/VacationForm.tsx @@ -44,7 +44,7 @@ import { dataListTitle, dataListTitleForListId } from '../../shared/listTitles'; -import { FORM_ID, IFormData, EMPTY_DATA, SetField, MIN_NOTICE_DAYS } from './model'; +import { FORM_ID, IFormData, EMPTY_DATA, SetField, MIN_NOTICE_DAYS, MAX_DAYS } from './model'; import { saveVacationRequest, vacationApprovalService } from './submit'; import { loadVacationRequest } from './load'; import { RequestSection } from './sections/RequestSection'; @@ -202,17 +202,20 @@ const VacationForm: React.FC = ({ // выбрать более раннюю дату, но проверяем и здесь на случай обхода UI. const minStartDate = isoDatePlusDays(MIN_NOTICE_DAYS); const isStartDateValid = !data.StartDate || data.StartDate >= minStartDate; + const isDaysCountTooLarge = Number.isFinite(daysNum) && daysNum > MAX_DAYS; const isValid = !!( data.Employee && data.StartDate && isStartDateValid && Number.isFinite(daysNum) && - daysNum > 0 + daysNum > 0 && + !isDaysCountTooLarge ); const validate = (): string | undefined => { if (data.StartDate && !isStartDateValid) { return `Заявку на отпуск нужно подать не позднее чем за ${MIN_NOTICE_DAYS} дней до даты начала — минимально доступная дата: ${formatIsoDate(minStartDate)}.`; } + if (isDaysCountTooLarge) return `Отпуск — не более ${MAX_DAYS} календарных дней в одной заявке.`; if (!isValid) return 'Заполните обязательные поля: сотрудник, дата начала, количество дней (> 0).'; if (requestTypeIdState.error) return `Не удалось определить тип заявки: ${requestTypeIdState.error}`; if (!requestTypeId) return 'Тип заявки ещё определяется — подождите немного и повторите.'; diff --git a/src/webparts/requestPortal/forms/Vacation/model.ts b/src/webparts/requestPortal/forms/Vacation/model.ts index e220565..2cf15d8 100644 --- a/src/webparts/requestPortal/forms/Vacation/model.ts +++ b/src/webparts/requestPortal/forms/Vacation/model.ts @@ -80,5 +80,7 @@ export const EMPTY_DATA: IFormData = { export const EMPLOYEE_PLACEHOLDER = 'Начните вводить Ф.И.О. сотрудника…'; /** Заявку нужно подавать не позднее чем за столько дней до даты начала отпуска. */ export const MIN_NOTICE_DAYS = 7; +/** Ежегодный трудовой отпуск — не более стольких календарных дней в одной заявке (требование HR). */ +export const MAX_DAYS = 24; /** Плейсхолдер для поля выбора замещающего. */ export const SUBSTITUTE_PLACEHOLDER = 'Начните вводить Ф.И.О. замещающего…'; diff --git a/src/webparts/requestPortal/forms/Vacation/sections/RequestSection.tsx b/src/webparts/requestPortal/forms/Vacation/sections/RequestSection.tsx index 8c2652e..6f9baba 100644 --- a/src/webparts/requestPortal/forms/Vacation/sections/RequestSection.tsx +++ b/src/webparts/requestPortal/forms/Vacation/sections/RequestSection.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { FormGrid, FormField, TextInput, PeopleField, OrganizationTable } from '../../../shared/ui'; import { ProjectPicker } from '../../../shared/dictionary'; -import { ISectionProps, EMPLOYEE_PLACEHOLDER, SUBSTITUTE_PLACEHOLDER, MIN_NOTICE_DAYS } from '../model'; +import { ISectionProps, EMPLOYEE_PLACEHOLDER, SUBSTITUTE_PLACEHOLDER, MIN_NOTICE_DAYS, MAX_DAYS } from '../model'; import { isoDatePlusDays } from '../../../shared/dateUtils'; /** Единственная секция формы «Заявка на отпуск»: сотрудник (должность и подразделение подтягиваются из справочника), даты. */ @@ -38,7 +38,14 @@ export const RequestSection: React.FC = ({ data, set, context, on /> - set('DaysCount', v)} /> + set('DaysCount', v)} + min={1} + max={MAX_DAYS} + />