fix: «Обучение» подведено под общий заголовок «Суммы к списанию»

TrainingAmount («Обучение: сумма», правит только КДП, не блокируется после
ввода) физически перенесено рядом с 5 суммами списания (правит любой активный
согласующий, блокируются навсегда) — под один заголовок-подпись «СУММЫ К
СПИСАНИЮ». Права и поведение остались разными (readOnly у TrainingAmount
по-прежнему считается отдельно, hrRecordsEditable, а не writeOffEditableKeys),
изменилось только визуальное соседство: раньше поле стояло сразу после «Дата
расторжения договора», далеко от одноимённой группы, из которой недавно убрали
дублирующее «Обучение».

Заголовок группы рисуется независимо от TrainingAmount (только когда
writeOffEditableKeys задан, т.е. в режиме просмотра) — само поле показывается
и на форме подачи, без заголовка, как и раньше.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
gorbenko.pavel
2026-08-26 14:22:47 +05:00
parent 89fceaab1d
commit 27e156abf8
2 changed files with 59 additions and 51 deletions
@@ -55,13 +55,21 @@ RequestTypeId» в `docs/forms.md`). Тип уже зарегистрирова
заведённых руками, ссылки нет и поле не рендерится. заведённых руками, ссылки нет и поле не рендерится.
## Суммы к списанию (согласование) ## Суммы к списанию (согласование)
5 НЕЗАВИСИМЫХ сумм, компактной сеткой под подписью «СУММЫ К СПИСАНИЮ» (короткие Общий заголовок-подпись «СУММЫ К СПИСАНИЮ» (`RequestSection.module.scss`,
подписи, обычная 2-колоночная сетка секции), колонки `.writeOffLabel`) объединяет визуально ДВЕ РАЗНЫЕ по правам вещи, идущие сразу
`WriteOff{Assets,Ppe,Accountable,Nma,Tmc}Amount` (см. `model.ts за подписью:
WRITE_OFF_CATEGORIES`): «ОС», «СИЗ», «Подотчётная сумма», «НМА», «ТМЦ».
Обучение сюда НЕ входит — у него уже есть `TrainingAmount` (см. выше) с ДРУГИМ 1. **`TrainingAmount`** («Обучение: сумма») — уже существовавшее поле, правит
поведением (правит только КДП); заводить второе одноимённое поле с иным только КДП (`hrRecordsEditable`, `approverRights.ts`), сумма НЕ блокируется
поведением рядом только путало. после ввода. Заголовок рисуется независимо от него (`writeOffEditableKeys &&
<div>…</div>`, а само поле — БЕЗ этого условия), поэтому на форме подачи
(`ClearanceForm`, `writeOffEditableKeys` не передан) поле видно и без заголовка.
2. **5 НЕЗАВИСИМЫХ сумм**, компактной сеткой (короткие подписи, обычная
2-колоночная сетка секции), колонки `WriteOff{Assets,Ppe,Accountable,Nma,
Tmc}Amount` (см. `model.ts WRITE_OFF_CATEGORIES`): «ОС», «СИЗ», «Подотчётная
сумма», «НМА», «ТМЦ» — правит любой активный согласующий, см. ниже. Раньше
среди них было ещё и «Обучение» — убрали как дубль `TrainingAmount` с ДРУГИМ
поведением под тем же названием; общий заголовок группы остался.
- **Не привязаны к должности**, в отличие от отметок выше: заполнить может ЛЮБОЙ - **Не привязаны к должности**, в отличие от отметок выше: заполнить может ЛЮБОЙ
согласующий, у которого сейчас активный шаг (`ClearanceView.canEditWriteOffs`, согласующий, у которого сейчас активный шаг (`ClearanceView.canEditWriteOffs`,
@@ -89,9 +97,9 @@ WRITE_OFF_CATEGORIES`): «ОС», «СИЗ», «Подотчётная сумм
поведение существующего поля. поведение существующего поля.
## Секции формы ## Секции формы
RequestSection (сотрудник · должность · подразделение · дата расторжения · сумма RequestSection (сотрудник · должность · подразделение · дата расторжения ·
обучения · парковочная карта · карта СКУД · суммы к списанию (компактно) · парковочная карта · карта СКУД · «Суммы к списанию»: обучение + 5 сумм списания
ссылка на заявление) компактно под общим заголовком · ссылка на заявление)
## Точки входа ## Точки входа
- rptype: `clearance-form` - rptype: `clearance-form`
@@ -74,16 +74,6 @@ export const RequestSection: React.FC<IRequestSectionProps> = ({
onChange={(v) => set('TerminationDate', v)} onChange={(v) => set('TerminationDate', v)}
/> />
</FormField> </FormField>
{/* Явный readOnly={false} перебивает read-only контекст просмотра (профильный
согласующий правит своё поле); undefined — обычное поведение из FormMode. */}
<FormField label="Обучение: сумма" htmlFor="TrainingAmount">
<MoneyInput
id="TrainingAmount"
value={data.TrainingAmount}
onChange={(v) => set('TrainingAmount', v)}
readOnly={hrRecordsEditable ? false : undefined}
/>
</FormField>
<FormField label="Парковочная карта"> <FormField label="Парковочная карта">
<Toggle <Toggle
checked={data.ParkingCard} checked={data.ParkingCard}
@@ -100,37 +90,47 @@ export const RequestSection: React.FC<IRequestSectionProps> = ({
readOnly={hrRecordsEditable ? false : undefined} readOnly={hrRecordsEditable ? false : undefined}
/> />
</FormField> </FormField>
{/* Суммы к списанию — 6 независимых сумм, компактной сеткой (обычная {/* Суммы к списанию — TrainingAmount («Обучение: сумма», правит только
2-колоночная сетка секции, короткие подписи — без full). Заголовок КДП) ПОДВЕДЕНА под тот же заголовок, что и 5 сумм ниже (правит любой
группы ОБЯЗАТЕЛЕН: без него «Обучение» здесь читается продолжением активный согласующий, см. WRITE_OFF_CATEGORIES) — визуально одна
«Обучение: сумма» чуть выше (разные поля — то принадлежит КДП, это группа «Суммы к списанию», хотя права на них разные. Без заголовка
любому согласующему, см. README «Отдельный механизм от TrainingAmount»). «Обучение» из WRITE_OFF_CATEGORIES читалось бы дублем «Обучение:
Каждую сумму может заполнить ЛЮБОЙ активный согласующий (не только сумма» — их когда-то было два похожих поля, оставили одно (TrainingAmount,
профильный, в отличие от отметок выше) — но как только сумма указана, см. README «Отдельный механизм от TrainingAmount»), но заголовок группы
блокируется навсегда для всех, поэтому у незаблокированных пригодился и без второго «Обучение».
(writeOffEditableKeys.has(cat.key)) есть readOnly={false}, у остальных Явный readOnly={false} у TrainingAmount перебивает read-only контекст
— обычное поведение FormMode (просмотр — заблокировано и так). «За что просмотра (профильный согласующий правит своё поле); undefined —
конкретно» — своего поля здесь нет: заполнил хоть одну сумму — обычное поведение из FormMode. У сумм из WRITE_OFF_CATEGORIES —
обязательным становится СТАНДАРТНЫЙ комментарий решения в DecisionPanel заполнить может ЛЮБОЙ активный согласующий (не только профильный), но
(«Ваше решение»), см. ClearanceView (commentRequired/commentPlaceholder). */} как только сумма указана, блокируется навсегда для всех, поэтому у
{writeOffEditableKeys && ( незаблокированных (writeOffEditableKeys.has(cat.key)) readOnly={false},
<> у остальных — обычное поведение FormMode. «За что конкретно» — своего
<div className={styles.writeOffLabel}>Суммы к списанию</div> поля здесь нет: заполнил хоть одну сумму — обязательным становится
{WRITE_OFF_CATEGORIES.map((cat) => { СТАНДАРТНЫЙ комментарий решения в DecisionPanel («Ваше решение»), см.
const editable = writeOffEditableKeys.has(cat.key); ClearanceView (commentRequired/commentPlaceholder). */}
return ( {writeOffEditableKeys && <div className={styles.writeOffLabel}>Суммы к списанию</div>}
<FormField key={cat.key} label={cat.label} htmlFor={cat.amountField}> <FormField label="Обучение: сумма" htmlFor="TrainingAmount">
<MoneyInput <MoneyInput
id={cat.amountField} id="TrainingAmount"
value={data[cat.amountField]} value={data.TrainingAmount}
onChange={(v) => set(cat.amountField, v)} onChange={(v) => set('TrainingAmount', v)}
readOnly={editable ? false : undefined} readOnly={hrRecordsEditable ? false : undefined}
/> />
</FormField> </FormField>
); {writeOffEditableKeys &&
})} WRITE_OFF_CATEGORIES.map((cat) => {
</> const editable = writeOffEditableKeys.has(cat.key);
)} return (
<FormField key={cat.key} label={cat.label} htmlFor={cat.amountField}>
<MoneyInput
id={cat.amountField}
value={data[cat.amountField]}
onChange={(v) => set(cat.amountField, v)}
readOnly={editable ? false : undefined}
/>
</FormField>
);
})}
{/* Есть только у листов, созданных автоматически по согласованному заявлению {/* Есть только у листов, созданных автоматически по согласованному заявлению
на увольнение (см. forms/Dismissal/createClearance.ts). */} на увольнение (см. forms/Dismissal/createClearance.ts). */}
<LinkField <LinkField