diff --git a/src/webparts/requestPortal/forms/Clearance/README.md b/src/webparts/requestPortal/forms/Clearance/README.md
index 9709ab2..073ff57 100644
--- a/src/webparts/requestPortal/forms/Clearance/README.md
+++ b/src/webparts/requestPortal/forms/Clearance/README.md
@@ -59,11 +59,16 @@ RequestTypeId» в `docs/forms.md`). Тип уже зарегистрирова
`.writeOffLabel`) объединяет визуально ДВЕ РАЗНЫЕ по правам вещи, идущие сразу
за подписью:
-1. **`TrainingAmount`** («Обучение: сумма») — уже существовавшее поле, правит
+1. **`TrainingAmount`** («Обучение: сумма») — доступность КАК И РАНЬШЕ: правит
только КДП (`hrRecordsEditable`, `approverRights.ts`), сумма НЕ блокируется
- после ввода. Заголовок рисуется независимо от него (`writeOffEditableKeys &&
-
…
`, а само поле — БЕЗ этого условия), поэтому на форме подачи
- (`ClearanceForm`, `writeOffEditableKeys` не передан) поле видно и без заголовка.
+ после ввода, никак не участвует в WRITE_OFF_CATEGORIES/`writeOffTouchedCategories`
+ (не требует комментария решения). Изменилась ТОЛЬКО колонка хранения — пишется
+ и читается через `WriteOffTrainingAmount` (`ClearanceView.saveApproverFields`,
+ `submit.ts.buildItem`, `load.ts`), а не через одноимённую `TrainingAmount` —
+ та осталась в схеме списка неиспользуемой. Заголовок рисуется независимо от
+ поля (`writeOffEditableKeys && …
`, а само поле — БЕЗ этого условия),
+ поэтому на форме подачи (`ClearanceForm`, `writeOffEditableKeys` не передан)
+ поле видно и без заголовка.
2. **5 НЕЗАВИСИМЫХ сумм**, компактной сеткой (короткие подписи, обычная
2-колоночная сетка секции), колонки `WriteOff{Assets,Ppe,Accountable,Nma,
Tmc}Amount` (см. `model.ts WRITE_OFF_CATEGORIES`): «ОС», «СИЗ», «Подотчётная
diff --git a/src/webparts/requestPortal/forms/Clearance/load.ts b/src/webparts/requestPortal/forms/Clearance/load.ts
index 5c390fa..cc647f1 100644
--- a/src/webparts/requestPortal/forms/Clearance/load.ts
+++ b/src/webparts/requestPortal/forms/Clearance/load.ts
@@ -35,12 +35,12 @@ export async function loadClearanceRequest(itemId: number, listId?: string): Pro
'TerminationDate',
'ParkingCard',
'AccessCard',
- 'TrainingAmount',
'Organization',
'DismissalUrl',
'WriteOffAssetsAmount',
'WriteOffPpeAmount',
'WriteOffAccountableAmount',
+ 'WriteOffTrainingAmount',
'WriteOffNmaAmount',
'WriteOffTmcAmount',
'RequestStatus',
@@ -52,7 +52,6 @@ export async function loadClearanceRequest(itemId: number, listId?: string): Pro
const employee = item.Employee as { Title?: string; EMail?: string } | undefined;
const termination = (item.TerminationDate as string) || '';
- const amount = item.TrainingAmount;
const data: IFormData = {
...EMPTY_DATA,
@@ -62,7 +61,9 @@ export async function loadClearanceRequest(itemId: number, listId?: string): Pro
TerminationDate: termination.slice(0, 10), // ISO → YYYY-MM-DD для input type=date
ParkingCard: !!item.ParkingCard,
AccessCard: !!item.AccessCard,
- TrainingAmount: amount === null || amount === undefined ? '' : String(amount),
+ // Хранится в WriteOffTrainingAmount, не в одноимённом TrainingAmount — см.
+ // IFormData.TrainingAmount.
+ TrainingAmount: numberField(item.WriteOffTrainingAmount),
Organization: (item.Organization as string) || '',
// Гиперссылка приходит объектом {Url, Description} — модели нужен только адрес.
DismissalUrl: (item.DismissalUrl as { Url?: string } | undefined)?.Url || '',
diff --git a/src/webparts/requestPortal/forms/Clearance/model.ts b/src/webparts/requestPortal/forms/Clearance/model.ts
index 3e84256..2866c57 100644
--- a/src/webparts/requestPortal/forms/Clearance/model.ts
+++ b/src/webparts/requestPortal/forms/Clearance/model.ts
@@ -23,7 +23,17 @@ export interface IFormData {
TerminationDate: string;
ParkingCard: boolean;
AccessCard: boolean;
- /** Сумма обучения — «сырые» цифры без пробелов (см. MoneyInput). Пусто = не указана. */
+ /**
+ * Сумма обучения — «сырые» цифры без пробелов (см. MoneyInput). Пусто = не
+ * указана. Доступность — КАК И РАНЬШЕ: правит только начальник КДП
+ * (`hrRecordsEditable`, `approverRights.ts`), не блокируется после ввода (в
+ * отличие от WRITE_OFF_CATEGORIES ниже). Единственное, что изменилось, —
+ * КОЛОНКА ХРАНЕНИЯ: пишется и читается через `WriteOffTrainingAmount` (см.
+ * ClearanceView.saveApproverFields/load.ts), а не через одноимённый
+ * `TrainingAmount` в списке — тот остался в схеме неиспользуемым (как и
+ * другие столбцы, из которых код этой сессии перестал писать/читать, см.
+ * README «Убрано поле…»).
+ */
TrainingAmount: string;
/**
* Организации сотрудника (`\n`-joined «Компания — должность» на строку) —
@@ -40,15 +50,12 @@ export interface IFormData {
/**
* Суммы к списанию по обходному листу — 5 независимых сумм (компактная сетка
- * полей, см. WRITE_OFF_CATEGORIES). Обучение сюда НЕ входит — для него уже
- * есть отдельное поле `TrainingAmount` (см. выше) с ДРУГИМ поведением
- * (правит только КДП, не любой согласующий); заводить второе «Обучение» с
- * иным поведением рядом только путало (полностью совпадающие подписи).
- * «Сырые» цифры без пробелов, как TrainingAmount (см. MoneyInput). Пусто =
- * не заполнено. КАЖДАЯ — независимая: заполнить может любой активный
- * согласующий (см. ClearanceView.canEditWriteOffs), но как только сумма
- * непустая — блокируется НАВСЕГДА, для всех (включая того, кто её ввёл).
- * Признак блокировки — сама непустая сумма, отдельного флага в списке нет.
+ * полей, см. WRITE_OFF_CATEGORIES). Обучение сюда НЕ входит — у него другая
+ * доступность (см. TrainingAmount выше): эти пять доступны ЛЮБОМУ активному
+ * согласующему и блокируются НАВСЕГДА после первого ввода (включая того, кто
+ * ввёл) — признак блокировки: сама непустая сумма, отдельного флага в
+ * списке нет. «Сырые» цифры без пробелов (см. MoneyInput). Пусто = не
+ * заполнено.
*
* «За что конкретно» — своего поля НЕТ: заполнил хоть одну сумму — становится
* обязательным СТАНДАРТНЫЙ комментарий решения (DecisionPanel, «Ваше решение»),
@@ -62,9 +69,16 @@ export interface IFormData {
WriteOffAccountableAmount: string;
WriteOffNmaAmount: string;
WriteOffTmcAmount: string;
+ /**
+ * Колонка ХРАНЕНИЯ для TrainingAmount (см. выше) — НЕ участвует в
+ * WRITE_OFF_CATEGORIES/WriteOffFieldKey и в общем механизме «любой
+ * согласующий, блокировка навсегда»: доступность и незаблокируемость у
+ * «Обучение: сумма» остались как были, изменилась только SharePoint-колонка.
+ */
+ WriteOffTrainingAmount: string;
}
-/** Имена всех 5 сумм к списанию — см. IFormData.WriteOff*Amount. */
+/** Имена 5 сумм к списанию (не считая WriteOffTrainingAmount — у него своя доступность) — см. IFormData.WriteOff*Amount. */
export type WriteOffFieldKey =
| 'WriteOffAssetsAmount'
| 'WriteOffPpeAmount'
@@ -121,6 +135,7 @@ export const EMPTY_DATA: IFormData = {
WriteOffAssetsAmount: '',
WriteOffPpeAmount: '',
WriteOffAccountableAmount: '',
+ WriteOffTrainingAmount: '',
WriteOffNmaAmount: '',
WriteOffTmcAmount: ''
};
diff --git a/src/webparts/requestPortal/forms/Clearance/provisioning.json b/src/webparts/requestPortal/forms/Clearance/provisioning.json
index 7c82054..d9b321c 100644
--- a/src/webparts/requestPortal/forms/Clearance/provisioning.json
+++ b/src/webparts/requestPortal/forms/Clearance/provisioning.json
@@ -19,6 +19,7 @@
{ "internalName": "WriteOffAssetsAmount", "title": "Сумма к списанию: ОС", "type": "Number", "decimals": 2 },
{ "internalName": "WriteOffPpeAmount", "title": "Сумма к списанию: СИЗ", "type": "Number", "decimals": 2 },
{ "internalName": "WriteOffAccountableAmount", "title": "Сумма к списанию: подотчётная сумма", "type": "Number", "decimals": 2 },
+ { "internalName": "WriteOffTrainingAmount", "title": "Сумма к списанию: обучение", "type": "Number", "decimals": 2 },
{ "internalName": "WriteOffNmaAmount", "title": "Сумма к списанию: НМА", "type": "Number", "decimals": 2 },
{ "internalName": "WriteOffTmcAmount", "title": "Сумма к списанию: ТМЦ", "type": "Number", "decimals": 2 }
]
diff --git a/src/webparts/requestPortal/forms/Clearance/sections/RequestSection.tsx b/src/webparts/requestPortal/forms/Clearance/sections/RequestSection.tsx
index 2dfca87..1043212 100644
--- a/src/webparts/requestPortal/forms/Clearance/sections/RequestSection.tsx
+++ b/src/webparts/requestPortal/forms/Clearance/sections/RequestSection.tsx
@@ -90,24 +90,22 @@ export const RequestSection: React.FC = ({
readOnly={hrRecordsEditable ? false : undefined}
/>
- {/* Суммы к списанию — TrainingAmount («Обучение: сумма», правит только
- КДП) ПОДВЕДЕНА под тот же заголовок, что и 5 сумм ниже (правит любой
- активный согласующий, см. WRITE_OFF_CATEGORIES) — визуально одна
- группа «Суммы к списанию», хотя права на них разные. Без заголовка
- «Обучение» из WRITE_OFF_CATEGORIES читалось бы дублем «Обучение:
- сумма» — их когда-то было два похожих поля, оставили одно (TrainingAmount,
- см. README «Отдельный механизм от TrainingAmount»), но заголовок группы
- пригодился и без второго «Обучение».
- Явный readOnly={false} у TrainingAmount перебивает read-only контекст
- просмотра (профильный согласующий правит своё поле); undefined —
- обычное поведение из FormMode. У сумм из WRITE_OFF_CATEGORIES —
- заполнить может ЛЮБОЙ активный согласующий (не только профильный), но
- как только сумма указана, блокируется навсегда для всех, поэтому у
- незаблокированных (writeOffEditableKeys.has(cat.key)) readOnly={false},
- у остальных — обычное поведение FormMode. «За что конкретно» — своего
- поля здесь нет: заполнил хоть одну сумму — обязательным становится
- СТАНДАРТНЫЙ комментарий решения в DecisionPanel («Ваше решение»), см.
- ClearanceView (commentRequired/commentPlaceholder). */}
+ {/* Суммы к списанию — TrainingAmount («Обучение: сумма») ПОДВЕДЕНА под
+ тот же заголовок, что и 5 сумм ниже, — визуально одна группа «Суммы к
+ списанию», хотя ДОСТУПНОСТЬ у них РАЗНАЯ:
+ - TrainingAmount — как раньше: правит только КДП (hrRecordsEditable),
+ без блокировки после ввода; хранится в WriteOffTrainingAmount (не
+ одноимённой колонке, см. IFormData.TrainingAmount), но это деталь
+ хранения, на форму не влияет;
+ - WRITE_OFF_CATEGORIES (5 сумм) — заполнить может ЛЮБОЙ активный
+ согласующий (не только профильный), но как только сумма указана,
+ блокируется НАВСЕГДА для всех, поэтому у незаблокированных
+ (writeOffEditableKeys.has(cat.key)) readOnly={false}, у остальных
+ — обычное поведение FormMode. «За что конкретно» у них — своего
+ поля нет: заполнил хоть одну сумму — обязательным становится
+ СТАНДАРТНЫЙ комментарий решения в DecisionPanel («Ваше решение»),
+ см. ClearanceView (commentRequired/commentPlaceholder). У
+ TrainingAmount этого правила нет — комментарий не требует. */}
{writeOffEditableKeys && Суммы к списанию
}
;
/**
@@ -56,6 +56,11 @@ export interface IApproverEditableFields {
* Пишутся ровно переданные поля: у каждого отдела свой набор прав, и лишние
* ключи затёрли бы чужие отметки значениями из формы этого согласующего.
*
+ * fields.TrainingAmount пишется в колонку WriteOffTrainingAmount, а НЕ в
+ * одноимённый TrainingAmount — см. IFormData.TrainingAmount; доступность
+ * (только КДП, без блокировки навсегда) не изменилась, изменилась только
+ * колонка хранения.
+ *
* listId — партиция ОБНОВЛЯЕМОЙ заявки (см. loadClearanceRequest); не задан —
* текущая партиция, как раньше.
*/
@@ -69,7 +74,7 @@ export async function updateApproverFields(
if (fields.ParkingCard !== undefined) item.ParkingCard = fields.ParkingCard;
if (fields.TrainingAmount !== undefined) {
const amount = Number(fields.TrainingAmount);
- item.TrainingAmount =
+ item.WriteOffTrainingAmount =
Number.isFinite(amount) && fields.TrainingAmount.trim() !== '' ? amount : null;
}
if (Object.keys(item).length === 0) return;
@@ -87,7 +92,13 @@ function buildTitle(employeeName: string): string {
return `Обходной лист — ${employeeName} — ${pad2(now.getDate())}.${pad2(now.getMonth() + 1)}.${now.getFullYear()}`;
}
-/** Поля элемента списка данных из модели формы (+ Employee по Id). */
+/**
+ * Поля элемента списка данных из модели формы (+ Employee по Id).
+ *
+ * Обычно data.TrainingAmount тут пуст (заявитель его не заполняет, только КДП
+ * на своём шаге — см. updateApproverFields), но пишем на случай редактирования
+ * уже созданной заявки: колонка WriteOffTrainingAmount, та же, что и там.
+ */
function buildItem(data: IFormData, status: string, employeeId: number, employeeName: string): Record {
const amount = Number(data.TrainingAmount);
return {
@@ -99,7 +110,7 @@ function buildItem(data: IFormData, status: string, employeeId: number, employee
TerminationDate: data.TerminationDate || null,
ParkingCard: data.ParkingCard,
AccessCard: data.AccessCard,
- TrainingAmount: Number.isFinite(amount) && data.TrainingAmount.trim() !== '' ? amount : null,
+ WriteOffTrainingAmount: Number.isFinite(amount) && data.TrainingAmount.trim() !== '' ? amount : null,
Organization: data.Organization || null
};
}