feat: один общий комментарий к списанию вместо шести, «Подразделение» вместо «Структурное»
Обходной лист: 6 отдельных Comment-колонок (по одной на категорию списания) консолидированы в одну WriteOffComment — комментарий и так один по смыслу («за что списываем»), дублировать текст в 6 колонок не было смысла. IWriteOffCategory больше не хранит commentField, WriteOffFieldKey сузился до 6 сумм. Заявление на увольнение: заголовок колонки таблицы организаций — «Подразделение» вместо «Структурное подразделение» (данные по-прежнему из Section справочника, только подпись короче). Со схемой ничего руками не делал — команда для провижининга ниже. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -45,7 +45,7 @@
|
|||||||
| [`recruiting-referral`](../src/webparts/requestPortal/forms/Referral/README.md) | Реферальная рекомендация | Рекрутинг | да | да | да | кандидат — внешний (текстовые поля, не People); маршрут по рекомендателю; исполнитель переназначает на реального; условное уточнение «откуда знаете» | ✅ |
|
| [`recruiting-referral`](../src/webparts/requestPortal/forms/Referral/README.md) | Реферальная рекомендация | Рекрутинг | да | да | да | кандидат — внешний (текстовые поля, не People); маршрут по рекомендателю; исполнитель переназначает на реального; условное уточнение «откуда знаете» | ✅ |
|
||||||
| [`it-workplace-setup`](../src/webparts/requestPortal/forms/WorkplaceSetup/README.md) | Организация рабочего места | IT | да | да | да | префилл из «прием», поля ПО/доступы правит руководитель | ✅ |
|
| [`it-workplace-setup`](../src/webparts/requestPortal/forms/WorkplaceSetup/README.md) | Организация рабочего места | IT | да | да | да | префилл из «прием», поля ПО/доступы правит руководитель | ✅ |
|
||||||
| [`it-1c-ticket`](../src/webparts/requestPortal/forms/Ticket1C/README.md) | Заявка в отдел 1С | IT | да | нет | да | контакты предзаполняются из Graph (/me); вместо фазы исполнения — карточка в Microsoft Planner (согласовано/сразу без согласования — рабочий бакет, отклонено — отдельный бакет) | ✅ |
|
| [`it-1c-ticket`](../src/webparts/requestPortal/forms/Ticket1C/README.md) | Заявка в отдел 1С | IT | да | нет | да | контакты предзаполняются из Graph (/me); вместо фазы исполнения — карточка в Microsoft Planner (согласовано/сразу без согласования — рабочий бакет, отклонено — отдельный бакет) | ✅ |
|
||||||
| [`clearance-form`](../src/webparts/requestPortal/forms/Clearance/README.md) | Обходной лист | Отдел кадров | да | нет | да | подаётся HR за увольняемого (People-picker) либо создаётся сам по согласованному «Заявлению на увольнение»; условие «IsManager» резолвится по должности из самой заявки; да/нет-поля (парковка, СКУД); 6 независимых сумм к списанию компактной сеткой (ОС, СИЗ, подотчётная сумма, обучение, НМА, ТМЦ) — вводит любой активный согласующий, блокируется навсегда после первого ввода; заполнил хоть одну — общий комментарий «за что» становится обязательным | ✅ |
|
| [`clearance-form`](../src/webparts/requestPortal/forms/Clearance/README.md) | Обходной лист | Отдел кадров | да | нет | да | подаётся HR за увольняемого (People-picker) либо создаётся сам по согласованному «Заявлению на увольнение»; условие «IsManager» резолвится по должности из самой заявки; да/нет-поля (парковка, СКУД); 6 независимых сумм к списанию компактной сеткой (ОС, СИЗ, подотчётная сумма, обучение, НМА, ТМЦ) — вводит любой активный согласующий, блокируется навсегда после первого ввода; заполнил хоть одну — ОДИН общий комментарий «за что» становится обязательным | ✅ |
|
||||||
| [`hr-dismissal`](../src/webparts/requestPortal/forms/Dismissal/README.md) | Заявление на увольнение | Отдел кадров | да | нет | да | совместители — таблица компаний с удалением строки (снимает и согласующего из матрицы); должность/организация из справочника; **после согласования сам создаёт обходной лист**; печать заблокирована до согласования обходного листа; «Лицо, принимающее ОС/НМА» — заполняет только непосредственный руководитель | ✅ |
|
| [`hr-dismissal`](../src/webparts/requestPortal/forms/Dismissal/README.md) | Заявление на увольнение | Отдел кадров | да | нет | да | совместители — таблица компаний с удалением строки (снимает и согласующего из матрицы); должность/организация из справочника; **после согласования сам создаёт обходной лист**; печать заблокирована до согласования обходного листа; «Лицо, принимающее ОС/НМА» — заполняет только непосредственный руководитель | ✅ |
|
||||||
| `hr-transfer` | Перемещение сотрудников | Отдел кадров | — | — | — | — | ⏳ заглушка (плитка есть, форма не сделана) |
|
| `hr-transfer` | Перемещение сотрудников | Отдел кадров | — | — | — | — | ⏳ заглушка (плитка есть, форма не сделана) |
|
||||||
|
|
||||||
|
|||||||
@@ -172,11 +172,9 @@ const ClearanceView: React.FC<IClearanceViewProps> = ({ context, itemId, formKey
|
|||||||
// Пишем ТОЛЬКО те отметки, на которые у согласующего есть права: иначе он
|
// Пишем ТОЛЬКО те отметки, на которые у согласующего есть права: иначе он
|
||||||
// затёр бы чужие поля значениями, которые видел у себя на экране. Суммы к
|
// затёр бы чужие поля значениями, которые видел у себя на экране. Суммы к
|
||||||
// списанию — только НЕПУСТЫЕ и только среди editableWriteOffKeys; комментарий
|
// списанию — только НЕПУСТЫЕ и только среди editableWriteOffKeys; комментарий
|
||||||
// ОДИН на все суммы, заполняемые СЕЙЧАС (не пара на каждую, см. IFormData.
|
// ОДИН на все суммы (IFormData.WriteOffComment, не пара на каждую категорию)
|
||||||
// WriteOffComment) — если хоть одна из них не пуста, комментарий обязателен
|
// — если хоть одна из редактируемых сумм не пуста, комментарий обязателен
|
||||||
// (бросает ДО записи чего бы то ни было) и при записи дублируется в
|
// (бросает ДО записи чего бы то ни было).
|
||||||
// Comment-колонку КАЖДОЙ такой категории — так каждая остаётся
|
|
||||||
// прокомментированной в самом списке.
|
|
||||||
const saveApproverFields = useCallback(async (): Promise<void> => {
|
const saveApproverFields = useCallback(async (): Promise<void> => {
|
||||||
const fields: Partial<IApproverEditableFields> = {};
|
const fields: Partial<IApproverEditableFields> = {};
|
||||||
if (hrRecordsEditable) {
|
if (hrRecordsEditable) {
|
||||||
@@ -198,8 +196,8 @@ const ClearanceView: React.FC<IClearanceViewProps> = ({ context, itemId, formKey
|
|||||||
}
|
}
|
||||||
for (const cat of touched) {
|
for (const cat of touched) {
|
||||||
writeOffFields[cat.amountField] = data[cat.amountField];
|
writeOffFields[cat.amountField] = data[cat.amountField];
|
||||||
writeOffFields[cat.commentField] = comment;
|
|
||||||
}
|
}
|
||||||
|
writeOffFields.WriteOffComment = comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateApproverFields(itemId, fields, listId);
|
await updateApproverFields(itemId, fields, listId);
|
||||||
|
|||||||
@@ -55,10 +55,10 @@ RequestTypeId» в `docs/forms.md`). Тип уже зарегистрирова
|
|||||||
заведённых руками, ссылки нет и поле не рендерится.
|
заведённых руками, ссылки нет и поле не рендерится.
|
||||||
|
|
||||||
## Суммы к списанию (согласование)
|
## Суммы к списанию (согласование)
|
||||||
6 НЕЗАВИСИМЫХ сумм, компактной сеткой (короткие подписи, без комментария под
|
6 НЕЗАВИСИМЫХ сумм, компактной сеткой (короткие подписи, обычная 2-колоночная
|
||||||
каждой — см. ниже), колонки `WriteOff{Assets,Ppe,Accountable,Training,Nma,Tmc}
|
сетка секции), колонки `WriteOff{Assets,Ppe,Accountable,Training,Nma,Tmc}Amount`
|
||||||
{Amount,Comment}` (см. `model.ts WRITE_OFF_CATEGORIES`): «ОС», «СИЗ», «Подотчётная
|
(см. `model.ts WRITE_OFF_CATEGORIES`): «ОС», «СИЗ», «Подотчётная сумма»,
|
||||||
сумма», «Обучение», «НМА», «ТМЦ».
|
«Обучение», «НМА», «ТМЦ». Плюс ОДИН общий комментарий на всех — `WriteOffComment`.
|
||||||
|
|
||||||
- **Не привязаны к должности**, в отличие от отметок выше: заполнить может ЛЮБОЙ
|
- **Не привязаны к должности**, в отличие от отметок выше: заполнить может ЛЮБОЙ
|
||||||
согласующий, у которого сейчас активный шаг (`ClearanceView.canEditWriteOffs`,
|
согласующий, у которого сейчас активный шаг (`ClearanceView.canEditWriteOffs`,
|
||||||
@@ -68,15 +68,16 @@ RequestTypeId» в `docs/forms.md`). Тип уже зарегистрирова
|
|||||||
Признак блокировки — сама непустая сумма на МОМЕНТ ЗАГРУЗКИ страницы
|
Признак блокировки — сама непустая сумма на МОМЕНТ ЗАГРУЗКИ страницы
|
||||||
(`ClearanceView.lockedWriteOffs`, снимок при загрузке, а не реактивно от
|
(`ClearanceView.lockedWriteOffs`, снимок при загрузке, а не реактивно от
|
||||||
текущего `data` — иначе поле блокировалось бы прямо во время набора цифр).
|
текущего `data` — иначе поле блокировалось бы прямо во время набора цифр).
|
||||||
- **Комментарий — ОДИН на все суммы, заполняемые в ЭТОМ сохранении** (не пара на
|
- **Комментарий («за что конкретно») — ОДИН на ВСЕ суммы**, не пара на каждую
|
||||||
каждую категорию, как раньше): появляется под сеткой, только когда хоть одна
|
категорию: одна Note-колонка (`WriteOffComment`), не шесть. Read-only, пока
|
||||||
редактируемая сумма не пуста (`writeOffTouchedCategories` в `RequestSection`),
|
нечего заполнять (показывает то, что уже написали раньше); становится
|
||||||
и обязателен в этот момент — `saveApproverFields` бросает ДО записи чего бы то
|
редактируемым и ОБЯЗАТЕЛЬНЫМ ровно тогда, когда хоть одна редактируемая сумма
|
||||||
ни было, если сумма есть, а комментарий пуст. Плейсхолдер называет категории,
|
не пуста (`writeOffTouchedCategories` в `RequestSection`) — `saveApproverFields`
|
||||||
для которых он сейчас пишется («Введите, за что сумма: ОС, ТМЦ»). При записи
|
бросает ДО записи чего бы то ни было, если сумма есть, а комментарий пуст.
|
||||||
этот ОДИН текст дублируется в Comment-колонку КАЖДОЙ заполняемой сейчас
|
Плейсхолдер называет категории, для которых он сейчас пишется («Введите, за
|
||||||
категории — так индивидуальный комментарий по категории в списке остаётся, а
|
что сумма: ОС, ТМЦ»). Следующий согласующий, заполняющий ДРУГУЮ сумму, увидит
|
||||||
форма не заставляет писать одно и то же под каждой суммой отдельно.
|
и сможет дописать то, что уже есть в поле, — это обычный редактируемый текст,
|
||||||
|
без автоматического версионирования/склейки.
|
||||||
- Сохраняются той же кнопкой «Сохранить» (`reviewBar`), что и остальные отметки,
|
- Сохраняются той же кнопкой «Сохранить» (`reviewBar`), что и остальные отметки,
|
||||||
и тем же `onBeforeDecide` — при решении несохранённые суммы уйдут вместе с ним.
|
и тем же `onBeforeDecide` — при решении несохранённые суммы уйдут вместе с ним.
|
||||||
- Это ОТДЕЛЬНЫЙ механизм от `TrainingAmount` («Обучение: сумма» выше, привязана к
|
- Это ОТДЕЛЬНЫЙ механизм от `TrainingAmount` («Обучение: сумма» выше, привязана к
|
||||||
|
|||||||
@@ -39,17 +39,12 @@ export async function loadClearanceRequest(itemId: number, listId?: string): Pro
|
|||||||
'Organization',
|
'Organization',
|
||||||
'DismissalUrl',
|
'DismissalUrl',
|
||||||
'WriteOffAssetsAmount',
|
'WriteOffAssetsAmount',
|
||||||
'WriteOffAssetsComment',
|
|
||||||
'WriteOffPpeAmount',
|
'WriteOffPpeAmount',
|
||||||
'WriteOffPpeComment',
|
|
||||||
'WriteOffAccountableAmount',
|
'WriteOffAccountableAmount',
|
||||||
'WriteOffAccountableComment',
|
|
||||||
'WriteOffTrainingAmount',
|
'WriteOffTrainingAmount',
|
||||||
'WriteOffTrainingComment',
|
|
||||||
'WriteOffNmaAmount',
|
'WriteOffNmaAmount',
|
||||||
'WriteOffNmaComment',
|
|
||||||
'WriteOffTmcAmount',
|
'WriteOffTmcAmount',
|
||||||
'WriteOffTmcComment',
|
'WriteOffComment',
|
||||||
'RequestStatus',
|
'RequestStatus',
|
||||||
'RequestUniqueCode',
|
'RequestUniqueCode',
|
||||||
'Employee/Title',
|
'Employee/Title',
|
||||||
@@ -74,17 +69,12 @@ export async function loadClearanceRequest(itemId: number, listId?: string): Pro
|
|||||||
// Гиперссылка приходит объектом {Url, Description} — модели нужен только адрес.
|
// Гиперссылка приходит объектом {Url, Description} — модели нужен только адрес.
|
||||||
DismissalUrl: (item.DismissalUrl as { Url?: string } | undefined)?.Url || '',
|
DismissalUrl: (item.DismissalUrl as { Url?: string } | undefined)?.Url || '',
|
||||||
WriteOffAssetsAmount: numberField(item.WriteOffAssetsAmount),
|
WriteOffAssetsAmount: numberField(item.WriteOffAssetsAmount),
|
||||||
WriteOffAssetsComment: (item.WriteOffAssetsComment as string) || '',
|
|
||||||
WriteOffPpeAmount: numberField(item.WriteOffPpeAmount),
|
WriteOffPpeAmount: numberField(item.WriteOffPpeAmount),
|
||||||
WriteOffPpeComment: (item.WriteOffPpeComment as string) || '',
|
|
||||||
WriteOffAccountableAmount: numberField(item.WriteOffAccountableAmount),
|
WriteOffAccountableAmount: numberField(item.WriteOffAccountableAmount),
|
||||||
WriteOffAccountableComment: (item.WriteOffAccountableComment as string) || '',
|
|
||||||
WriteOffTrainingAmount: numberField(item.WriteOffTrainingAmount),
|
WriteOffTrainingAmount: numberField(item.WriteOffTrainingAmount),
|
||||||
WriteOffTrainingComment: (item.WriteOffTrainingComment as string) || '',
|
|
||||||
WriteOffNmaAmount: numberField(item.WriteOffNmaAmount),
|
WriteOffNmaAmount: numberField(item.WriteOffNmaAmount),
|
||||||
WriteOffNmaComment: (item.WriteOffNmaComment as string) || '',
|
|
||||||
WriteOffTmcAmount: numberField(item.WriteOffTmcAmount),
|
WriteOffTmcAmount: numberField(item.WriteOffTmcAmount),
|
||||||
WriteOffTmcComment: (item.WriteOffTmcComment as string) || ''
|
WriteOffComment: (item.WriteOffComment as string) || ''
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -46,80 +46,52 @@ export interface IFormData {
|
|||||||
* ClearanceView.canEditWriteOffs), но как только сумма непустая — блокируется
|
* ClearanceView.canEditWriteOffs), но как только сумма непустая — блокируется
|
||||||
* НАВСЕГДА, для всех (включая того, кто её ввёл). Признак блокировки — сама
|
* НАВСЕГДА, для всех (включая того, кто её ввёл). Признак блокировки — сама
|
||||||
* непустая сумма, отдельного флага в списке нет.
|
* непустая сумма, отдельного флага в списке нет.
|
||||||
*
|
|
||||||
* Комментарий («за что конкретно») — ОБЩИЙ на все суммы, вводимые ОДНИМ
|
|
||||||
* сохранением (WriteOffComment, ниже), не отдельная пара на каждую категорию:
|
|
||||||
* при записи он дублируется в Comment-колонку КАЖДОЙ категории, чью сумму
|
|
||||||
* заполнили именно сейчас (см. ClearanceView.saveApproverFields) — так каждая
|
|
||||||
* категория остаётся комментированной в самом списке, а форма не заставляет
|
|
||||||
* писать одно и то же по кругу под каждой суммой.
|
|
||||||
*/
|
*/
|
||||||
WriteOffAssetsAmount: string;
|
WriteOffAssetsAmount: string;
|
||||||
WriteOffAssetsComment: string;
|
|
||||||
WriteOffPpeAmount: string;
|
WriteOffPpeAmount: string;
|
||||||
WriteOffPpeComment: string;
|
|
||||||
WriteOffAccountableAmount: string;
|
WriteOffAccountableAmount: string;
|
||||||
WriteOffAccountableComment: string;
|
|
||||||
WriteOffTrainingAmount: string;
|
WriteOffTrainingAmount: string;
|
||||||
WriteOffTrainingComment: string;
|
|
||||||
WriteOffNmaAmount: string;
|
WriteOffNmaAmount: string;
|
||||||
WriteOffNmaComment: string;
|
|
||||||
WriteOffTmcAmount: string;
|
WriteOffTmcAmount: string;
|
||||||
WriteOffTmcComment: string;
|
|
||||||
/**
|
/**
|
||||||
* Комментарий к сумме(ам), заполняемым ПРЯМО СЕЙЧАС (не из списка — вводится
|
* Комментарий «за что конкретно» — ОДИН на ВСЕ суммы к списанию (не пара на
|
||||||
* заново при каждом сохранении, см. комментарий выше). Обязателен, если хоть
|
* каждую категорию — раньше было так, но колонок-комментариев на каждую
|
||||||
* одна из редактируемых сумм не пуста (см. RequestSection).
|
* категорию не завели: одного общего достаточно). Обязателен, если хоть одна
|
||||||
|
* из РЕДАКТИРУЕМЫХ сумм не пуста (см. RequestSection.writeOffTouchedCategories),
|
||||||
|
* и до этого момента read-only — показывает то, что уже написали раньше.
|
||||||
*/
|
*/
|
||||||
WriteOffComment: string;
|
WriteOffComment: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Имена всех 12 строковых полей сумм/комментариев к списанию — см. IFormData.WriteOff*. */
|
/** Имена всех 6 сумм к списанию — см. IFormData.WriteOff*Amount. */
|
||||||
export type WriteOffFieldKey =
|
export type WriteOffFieldKey =
|
||||||
| 'WriteOffAssetsAmount'
|
| 'WriteOffAssetsAmount'
|
||||||
| 'WriteOffAssetsComment'
|
|
||||||
| 'WriteOffPpeAmount'
|
| 'WriteOffPpeAmount'
|
||||||
| 'WriteOffPpeComment'
|
|
||||||
| 'WriteOffAccountableAmount'
|
| 'WriteOffAccountableAmount'
|
||||||
| 'WriteOffAccountableComment'
|
|
||||||
| 'WriteOffTrainingAmount'
|
| 'WriteOffTrainingAmount'
|
||||||
| 'WriteOffTrainingComment'
|
|
||||||
| 'WriteOffNmaAmount'
|
| 'WriteOffNmaAmount'
|
||||||
| 'WriteOffNmaComment'
|
| 'WriteOffTmcAmount';
|
||||||
| 'WriteOffTmcAmount'
|
|
||||||
| 'WriteOffTmcComment';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Одна из 6 независимых категорий списания — см. IFormData.WriteOff*.
|
* Одна из 6 независимых категорий списания — см. IFormData.WriteOff*Amount.
|
||||||
* amountField/commentField сужены до WriteOffFieldKey (а не keyof IFormData
|
* amountField сужен до WriteOffFieldKey (а не keyof IFormData целиком): у
|
||||||
* целиком): у IFormData есть и булевы поля (ParkingCard/AccessCard), и
|
* IFormData есть и булевы поля (ParkingCard/AccessCard), и `data[cat.amountField]`
|
||||||
* `data[cat.amountField]` должен типизироваться строго строкой — иначе
|
* должен типизироваться строго строкой — иначе `.trim()` в валидации и
|
||||||
* `.trim()` в валидации и передача в MoneyInput/TextArea не собираются.
|
* передача в MoneyInput не собираются.
|
||||||
*/
|
*/
|
||||||
export interface IWriteOffCategory {
|
export interface IWriteOffCategory {
|
||||||
key: string;
|
key: string;
|
||||||
label: string;
|
label: string;
|
||||||
amountField: WriteOffFieldKey;
|
amountField: WriteOffFieldKey;
|
||||||
commentField: WriteOffFieldKey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WRITE_OFF_CATEGORIES: IWriteOffCategory[] = [
|
export const WRITE_OFF_CATEGORIES: IWriteOffCategory[] = [
|
||||||
{ key: 'assets', label: 'ОС', amountField: 'WriteOffAssetsAmount', commentField: 'WriteOffAssetsComment' },
|
{ key: 'assets', label: 'ОС', amountField: 'WriteOffAssetsAmount' },
|
||||||
{ key: 'ppe', label: 'СИЗ', amountField: 'WriteOffPpeAmount', commentField: 'WriteOffPpeComment' },
|
{ key: 'ppe', label: 'СИЗ', amountField: 'WriteOffPpeAmount' },
|
||||||
{
|
{ key: 'accountable', label: 'Подотчётная сумма', amountField: 'WriteOffAccountableAmount' },
|
||||||
key: 'accountable',
|
{ key: 'training', label: 'Обучение', amountField: 'WriteOffTrainingAmount' },
|
||||||
label: 'Подотчётная сумма',
|
{ key: 'nma', label: 'НМА', amountField: 'WriteOffNmaAmount' },
|
||||||
amountField: 'WriteOffAccountableAmount',
|
{ key: 'tmc', label: 'ТМЦ', amountField: 'WriteOffTmcAmount' }
|
||||||
commentField: 'WriteOffAccountableComment'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'training',
|
|
||||||
label: 'Обучение',
|
|
||||||
amountField: 'WriteOffTrainingAmount',
|
|
||||||
commentField: 'WriteOffTrainingComment'
|
|
||||||
},
|
|
||||||
{ key: 'nma', label: 'НМА', amountField: 'WriteOffNmaAmount', commentField: 'WriteOffNmaComment' },
|
|
||||||
{ key: 'tmc', label: 'ТМЦ', amountField: 'WriteOffTmcAmount', commentField: 'WriteOffTmcComment' }
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/** Обновление одного поля формы с сохранением типа значения. */
|
/** Обновление одного поля формы с сохранением типа значения. */
|
||||||
@@ -148,17 +120,11 @@ export const EMPTY_DATA: IFormData = {
|
|||||||
Organization: '',
|
Organization: '',
|
||||||
DismissalUrl: '',
|
DismissalUrl: '',
|
||||||
WriteOffAssetsAmount: '',
|
WriteOffAssetsAmount: '',
|
||||||
WriteOffAssetsComment: '',
|
|
||||||
WriteOffPpeAmount: '',
|
WriteOffPpeAmount: '',
|
||||||
WriteOffPpeComment: '',
|
|
||||||
WriteOffAccountableAmount: '',
|
WriteOffAccountableAmount: '',
|
||||||
WriteOffAccountableComment: '',
|
|
||||||
WriteOffTrainingAmount: '',
|
WriteOffTrainingAmount: '',
|
||||||
WriteOffTrainingComment: '',
|
|
||||||
WriteOffNmaAmount: '',
|
WriteOffNmaAmount: '',
|
||||||
WriteOffNmaComment: '',
|
|
||||||
WriteOffTmcAmount: '',
|
WriteOffTmcAmount: '',
|
||||||
WriteOffTmcComment: '',
|
|
||||||
WriteOffComment: ''
|
WriteOffComment: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -17,16 +17,11 @@
|
|||||||
{ "internalName": "DismissalUrl", "title": "Ссылка на заявление на увольнение", "type": "Url" },
|
{ "internalName": "DismissalUrl", "title": "Ссылка на заявление на увольнение", "type": "Url" },
|
||||||
|
|
||||||
{ "internalName": "WriteOffAssetsAmount", "title": "Сумма к списанию: ОС", "type": "Number", "decimals": 2 },
|
{ "internalName": "WriteOffAssetsAmount", "title": "Сумма к списанию: ОС", "type": "Number", "decimals": 2 },
|
||||||
{ "internalName": "WriteOffAssetsComment", "title": "Комментарий: ОС", "type": "Note" },
|
|
||||||
{ "internalName": "WriteOffPpeAmount", "title": "Сумма к списанию: СИЗ", "type": "Number", "decimals": 2 },
|
{ "internalName": "WriteOffPpeAmount", "title": "Сумма к списанию: СИЗ", "type": "Number", "decimals": 2 },
|
||||||
{ "internalName": "WriteOffPpeComment", "title": "Комментарий: СИЗ", "type": "Note" },
|
|
||||||
{ "internalName": "WriteOffAccountableAmount", "title": "Сумма к списанию: подотчётная сумма", "type": "Number", "decimals": 2 },
|
{ "internalName": "WriteOffAccountableAmount", "title": "Сумма к списанию: подотчётная сумма", "type": "Number", "decimals": 2 },
|
||||||
{ "internalName": "WriteOffAccountableComment", "title": "Комментарий: подотчётная сумма", "type": "Note" },
|
|
||||||
{ "internalName": "WriteOffTrainingAmount", "title": "Сумма к списанию: обучение", "type": "Number", "decimals": 2 },
|
{ "internalName": "WriteOffTrainingAmount", "title": "Сумма к списанию: обучение", "type": "Number", "decimals": 2 },
|
||||||
{ "internalName": "WriteOffTrainingComment", "title": "Комментарий: обучение", "type": "Note" },
|
|
||||||
{ "internalName": "WriteOffNmaAmount", "title": "Сумма к списанию: НМА", "type": "Number", "decimals": 2 },
|
{ "internalName": "WriteOffNmaAmount", "title": "Сумма к списанию: НМА", "type": "Number", "decimals": 2 },
|
||||||
{ "internalName": "WriteOffNmaComment", "title": "Комментарий: НМА", "type": "Note" },
|
|
||||||
{ "internalName": "WriteOffTmcAmount", "title": "Сумма к списанию: ТМЦ", "type": "Number", "decimals": 2 },
|
{ "internalName": "WriteOffTmcAmount", "title": "Сумма к списанию: ТМЦ", "type": "Number", "decimals": 2 },
|
||||||
{ "internalName": "WriteOffTmcComment", "title": "Комментарий: ТМЦ", "type": "Note" }
|
{ "internalName": "WriteOffComment", "title": "Комментарий к списанию", "type": "Note" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,8 +112,8 @@ export const RequestSection: React.FC<IRequestSectionProps> = ({
|
|||||||
от отметок выше) — но как только сумма указана, блокируется навсегда
|
от отметок выше) — но как только сумма указана, блокируется навсегда
|
||||||
для всех, поэтому у незаблокированных (writeOffEditableKeys.has(cat.key))
|
для всех, поэтому у незаблокированных (writeOffEditableKeys.has(cat.key))
|
||||||
есть readOnly={false}, у остальных — обычное поведение FormMode
|
есть readOnly={false}, у остальных — обычное поведение FormMode
|
||||||
(просмотр — заблокировано и так). Комментарий — ОДИН на все суммы,
|
(просмотр — заблокировано и так). Комментарий — ОДИН на ВСЕ суммы
|
||||||
вводимые сейчас (см. WRITE_OFF_CATEGORIES/WriteOffComment в model.ts). */}
|
(не пара на каждую категорию, см. IFormData.WriteOffComment). */}
|
||||||
{writeOffEditableKeys && (
|
{writeOffEditableKeys && (
|
||||||
<>
|
<>
|
||||||
{WRITE_OFF_CATEGORIES.map((cat) => {
|
{WRITE_OFF_CATEGORIES.map((cat) => {
|
||||||
@@ -129,15 +129,28 @@ export const RequestSection: React.FC<IRequestSectionProps> = ({
|
|||||||
</FormField>
|
</FormField>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{writeOffTouchedCategories.length > 0 && (
|
{/* Read-only, если сейчас нечего заполнять (нет свежих сумм) — просто
|
||||||
<FormField label="Комментарий: за что списание" htmlFor="WriteOffComment" required full>
|
показывает, что написали раньше; редактируема и обязательна
|
||||||
|
ровно тогда, когда есть непустая сумма, которую надо объяснить. */}
|
||||||
|
{(writeOffTouchedCategories.length > 0 || data.WriteOffComment) && (
|
||||||
|
<FormField
|
||||||
|
label="Комментарий: за что списание"
|
||||||
|
htmlFor="WriteOffComment"
|
||||||
|
required={writeOffTouchedCategories.length > 0}
|
||||||
|
full
|
||||||
|
>
|
||||||
<TextArea
|
<TextArea
|
||||||
id="WriteOffComment"
|
id="WriteOffComment"
|
||||||
value={data.WriteOffComment}
|
value={data.WriteOffComment}
|
||||||
onChange={(v) => set('WriteOffComment', v)}
|
onChange={(v) => set('WriteOffComment', v)}
|
||||||
placeholder={`Введите, за что сумма${
|
readOnly={writeOffTouchedCategories.length > 0 ? false : true}
|
||||||
|
placeholder={
|
||||||
|
writeOffTouchedCategories.length > 0
|
||||||
|
? `Введите, за что сумма${
|
||||||
writeOffTouchedCategories.length > 1 ? 'ы' : ''
|
writeOffTouchedCategories.length > 1 ? 'ы' : ''
|
||||||
}: ${writeOffTouchedCategories.map((c) => c.label).join(', ')}`}
|
}: ${writeOffTouchedCategories.map((c) => c.label).join(', ')}`
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</FormField>
|
</FormField>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -3,18 +3,18 @@ import { ISaveItemResult, resolveDataList, saveRequestItem } from '../../shared/
|
|||||||
import { ensureEmployee } from '../../shared/employee';
|
import { ensureEmployee } from '../../shared/employee';
|
||||||
import { FORM_ID, IFormData, WriteOffFieldKey } from './model';
|
import { FORM_ID, IFormData, WriteOffFieldKey } from './model';
|
||||||
|
|
||||||
/** Одна из 12 колонок сумм/комментариев к списанию — см. model.ts WRITE_OFF_CATEGORIES. */
|
/** 6 сумм к списанию + общий комментарий — см. model.ts WRITE_OFF_CATEGORIES/IFormData.WriteOffComment. */
|
||||||
export type IWriteOffFields = Pick<IFormData, WriteOffFieldKey>;
|
export type IWriteOffFields = Pick<IFormData, WriteOffFieldKey | 'WriteOffComment'>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Записать сумму(ы)/комментарий(и) к списанию. Пишет РОВНО переданные поля —
|
* Записать сумму(ы) к списанию и/или общий комментарий. Пишет РОВНО переданные
|
||||||
* как updateApproverFields, каждая пара независима.
|
* поля — как updateApproverFields, каждое независимо.
|
||||||
*
|
*
|
||||||
* ВАЖНО: вызывающая сторона (ClearanceView) обязана передавать сюда ТОЛЬКО те
|
* ВАЖНО: вызывающая сторона (ClearanceView) обязана передавать сюда ТОЛЬКО ещё
|
||||||
* пары, что ещё не заблокированы (см. IFormData.WriteOff* — блокировка по
|
* не заблокированные суммы (см. IFormData.WriteOff* — блокировка по непустой
|
||||||
* непустой сумме) и заполнены целиком (сумма+комментарий вместе, см.
|
* сумме) и проверенный (непустой, если хоть одна сумма заполняется) комментарий
|
||||||
* validateWriteOffPairs) — эта функция сама повторно ничего не проверяет и
|
* — эта функция сама повторно ничего не проверяет и молча перезапишет то, что
|
||||||
* молча перезапишет то, что ей передали.
|
* ей передали.
|
||||||
*/
|
*/
|
||||||
export async function updateWriteOffFields(
|
export async function updateWriteOffFields(
|
||||||
id: number,
|
id: number,
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ RequestTypeId» в `docs/forms.md`). Тип заведён в админке app
|
|||||||
отдельных полей для них нет (были бы дублированием). `data.Organization:
|
отдельных полей для них нет (были бы дублированием). `data.Organization:
|
||||||
IOrganizationRecord[]` — массив `{company, department, position}`; поле называется
|
IOrganizationRecord[]` — массив `{company, department, position}`; поле называется
|
||||||
`department` (тот же справочник `Section`, что и раньше — см. следующий абзац),
|
`department` (тот же справочник `Section`, что и раньше — см. следующий абзац),
|
||||||
колонка называется «Структурное подразделение», и то и другое read-only. Должность
|
колонка называется «Подразделение», и то и другое read-only. Должность
|
||||||
редактируема В ЛЮБОЙ строке (справочник не всегда точен), но маршрут (условие
|
редактируема В ЛЮБОЙ строке (справочник не всегда точен), но маршрут (условие
|
||||||
«IsManager») и обязательность по валидации завязаны ТОЛЬКО на должность ПЕРВОЙ
|
«IsManager») и обязательность по валидации завязаны ТОЛЬКО на должность ПЕРВОЙ
|
||||||
(основной) записи — `primaryPosition(data.Organization)`; должности остальных
|
(основной) записи — `primaryPosition(data.Organization)`; должности остальных
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export const OrganizationsTable: React.FC<IOrganizationsTableProps> = ({ records
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className={styles.company}>Компания</th>
|
<th className={styles.company}>Компания</th>
|
||||||
<th className={styles.department}>Структурное подразделение</th>
|
<th className={styles.department}>Подразделение</th>
|
||||||
<th className={styles.position}>Должность</th>
|
<th className={styles.position}>Должность</th>
|
||||||
{onRemove && <th className={styles.action} />}
|
{onRemove && <th className={styles.action} />}
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
Reference in New Issue
Block a user