fix: отпуск без содержания — без лимита 5 дней, «Иная причина»

5 дней работодатель обязан дать только при браке/рождении/смерти,
сверх — по договорённости, поэтому форма больше не ограничивает число
дней. Вариант «В иных случаях» подписан как «Иная причина» — читается
как ответ на «Причина»; значение в SP прежнее.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Тимур Городецкий
2026-10-12 11:24:42 +05:00
parent e46632d67a
commit 755cfc8329
4 changed files with 14 additions and 13 deletions
@@ -22,10 +22,11 @@
- **Период вводится двумя датами** (с / по), обе обязательны, конец ≥ начала. - **Период вводится двумя датами** (с / по), обе обязательны, конец ≥ начала.
`DaysCount` считается при сохранении (включительно) для отчётности — в UI не показывается. `DaysCount` считается при сохранении (включительно) для отчётности — в UI не показывается.
- **Причина** (`UnpaidLeaveReasonType`, Choice) — обязательна: брак / рождение ребёнка / - **Причина** (`UnpaidLeaveReasonType`, Choice) — обязательна: брак / рождение ребёнка /
смерть близких / «В иных случаях». Только при «иных случаях» появляется обязательный смерть близких / «В иных случаях» (в форме подписано «Иная причина», значение в SP прежнее). Только при «иных случаях» появляется обязательный
текст причины (`Reason`). У заявок до появления колонки причины нет — их прежний текст причины (`Reason`). У заявок до появления колонки причины нет — их прежний
текст из `Reason` по-прежнему показывается (`isReasonTextShown`). текст из `Reason` по-прежнему показывается (`isReasonTextShown`).
- **Не более 5 календарных дней** (`MAX_DAYS`) — при любой причине. - **Количество дней без верхнего предела** — 5 дней работодатель обязан дать при браке /
рождении / смерти, остальное — по договорённости, поэтому форма лимит не ставит.
- **Правило «не позднее чем за 14 дней»** (`MIN_NOTICE_DAYS`) — только для «иных случаев» - **Правило «не позднее чем за 14 дней»** (`MIN_NOTICE_DAYS`) — только для «иных случаев»
(`requiresAdvanceNotice`): брак, рождение и смерть заранее не планируются. (`requiresAdvanceNotice`): брак, рождение и смерть заранее не планируются.
- **Подсветка проблемных полей** — `FieldIssuesProvider` + правила в `validation.ts` - **Подсветка проблемных полей** — `FieldIssuesProvider` + правила в `validation.ts`
@@ -94,8 +94,6 @@ export const SUBSTITUTE_PLACEHOLDER = 'Начните вводить Ф.И.О.
* Действует только для «иных случаев» — см. requiresAdvanceNotice. * Действует только для «иных случаев» — см. requiresAdvanceNotice.
*/ */
export const MIN_NOTICE_DAYS = 14; export const MIN_NOTICE_DAYS = 14;
/** Отпуск без сохранения заработной платы — не более стольких календарных дней (по любой причине). */
export const MAX_DAYS = 5;
/** Причины отпуска. Значения совпадают с choices колонки UnpaidLeaveReasonType. */ /** Причины отпуска. Значения совпадают с choices колонки UnpaidLeaveReasonType. */
export const REASON_MARRIAGE = 'Регистрация брака'; export const REASON_MARRIAGE = 'Регистрация брака';
@@ -108,7 +106,8 @@ export const UNPAID_LEAVE_REASONS: ISelectOption[] = [
{ value: REASON_MARRIAGE, label: REASON_MARRIAGE }, { value: REASON_MARRIAGE, label: REASON_MARRIAGE },
{ value: REASON_CHILD_BIRTH, label: REASON_CHILD_BIRTH }, { value: REASON_CHILD_BIRTH, label: REASON_CHILD_BIRTH },
{ value: REASON_RELATIVE_DEATH, label: REASON_RELATIVE_DEATH }, { value: REASON_RELATIVE_DEATH, label: REASON_RELATIVE_DEATH },
{ value: REASON_OTHER, label: REASON_OTHER } // Значение в SP остаётся прежним (choices колонки и старые заявки), меняется только подпись.
{ value: REASON_OTHER, label: 'Иная причина' }
]; ];
/** /**
@@ -6,7 +6,6 @@ import {
EMPLOYEE_PLACEHOLDER, EMPLOYEE_PLACEHOLDER,
SUBSTITUTE_PLACEHOLDER, SUBSTITUTE_PLACEHOLDER,
MIN_NOTICE_DAYS, MIN_NOTICE_DAYS,
MAX_DAYS,
REASON_OTHER, REASON_OTHER,
UNPAID_LEAVE_REASONS, UNPAID_LEAVE_REASONS,
requiresAdvanceNotice, requiresAdvanceNotice,
@@ -61,14 +60,13 @@ export const RequestSection: React.FC<ISectionProps> = ({ data, set, context, on
min={requiresAdvanceNotice(data.ReasonType) ? isoDatePlusDays(MIN_NOTICE_DAYS) : undefined} min={requiresAdvanceNotice(data.ReasonType) ? isoDatePlusDays(MIN_NOTICE_DAYS) : undefined}
/> />
</FormField> </FormField>
<FormField label={`Количество дней (не более ${MAX_DAYS})`} htmlFor="DaysCount" required> <FormField label="Количество дней" htmlFor="DaysCount" required>
<TextInput <TextInput
id="DaysCount" id="DaysCount"
type="number" type="number"
value={data.DaysCount} value={data.DaysCount}
onChange={(v) => set('DaysCount', v)} onChange={(v) => set('DaysCount', v)}
min={1} min={1}
max={MAX_DAYS}
/> />
</FormField> </FormField>
<FormField label="Причина" htmlFor="UnpaidLeaveReasonType" required full> <FormField label="Причина" htmlFor="UnpaidLeaveReasonType" required full>
@@ -1,6 +1,6 @@
import type { IFieldIssue } from '../../shared/ui'; import type { IFieldIssue } from '../../shared/ui';
import { isoDatePlusDays, formatIsoDate } from '../../shared/dateUtils'; import { isoDatePlusDays, formatIsoDate } from '../../shared/dateUtils';
import { IFormData, MAX_DAYS, MIN_NOTICE_DAYS, REASON_OTHER, requiresAdvanceNotice } from './model'; import { IFormData, MIN_NOTICE_DAYS, REASON_OTHER, requiresAdvanceNotice } from './model';
/** Дата начала: обязательна, а в «иных случаях» — не раньше чем через MIN_NOTICE_DAYS дней. */ /** Дата начала: обязательна, а в «иных случаях» — не раньше чем через MIN_NOTICE_DAYS дней. */
function startDateIssue(data: IFormData): IFieldIssue | undefined { function startDateIssue(data: IFormData): IFieldIssue | undefined {
@@ -12,17 +12,20 @@ function startDateIssue(data: IFormData): IFieldIssue | undefined {
return { return {
field: 'StartDate', field: 'StartDate',
label: 'Дата начала', label: 'Дата начала',
message: `в иных случаях заявление подают не позднее чем за ${MIN_NOTICE_DAYS} дней — не раньше ${formatIsoDate(minStartDate)}` message: `при иной причине заявление подают не позднее чем за ${MIN_NOTICE_DAYS} дней — не раньше ${formatIsoDate(minStartDate)}`
}; };
} }
/** Количество дней: обязательно, от 1 до MAX_DAYS. */ /**
* Количество дней: обязательно, целое и больше нуля. Верхнего предела нет:
* сверх 5 дней (обязательных при браке/рождении/смерти) — по договорённости с работодателем.
*/
function daysCountIssue(data: IFormData): IFieldIssue | undefined { function daysCountIssue(data: IFormData): IFieldIssue | undefined {
if (!data.DaysCount.trim()) return { field: 'DaysCount', label: 'Количество дней' }; if (!data.DaysCount.trim()) return { field: 'DaysCount', label: 'Количество дней' };
const days = Number(data.DaysCount); const days = Number(data.DaysCount);
if (Number.isFinite(days) && days > 0 && days <= MAX_DAYS) return undefined; if (Number.isInteger(days) && days > 0) return undefined;
return { field: 'DaysCount', label: 'Количество дней', message: `от 1 до ${MAX_DAYS} календарных дней` }; return { field: 'DaysCount', label: 'Количество дней', message: 'целое число больше нуля' };
} }
/** /**