feat: добавил поле организация во все заявки, а также расширил компонет выбора даты для ui-kit
This commit is contained in:
@@ -26,6 +26,7 @@ import {
|
||||
unassignedStepMessage
|
||||
} from '../../shared/approval';
|
||||
import { IRequestFormProps } from '../../models/types';
|
||||
import { getEmployeeOrganizations, formatEmployeeOrganizations } from '../../shared/dictionary';
|
||||
import { friendlyErrorMessage } from '../../shared/diagnostics';
|
||||
import {
|
||||
approvalListTitle,
|
||||
@@ -100,19 +101,29 @@ const ClearanceForm: React.FC<IRequestFormProps> = ({
|
||||
setData((prev) => ({ ...prev, TerminationDate: prev.TerminationDate || today }));
|
||||
}, [editId]);
|
||||
|
||||
// Должность/подразделение больше не вводятся вручную — подтягиваются из
|
||||
// SharePoint User Profile (SPS-JobTitle/SPS-Department) при выборе сотрудника.
|
||||
// Если профиль не синхронизирован и поле там пусто — пока просто остаётся
|
||||
// пустым (TODO: фолбэк на справочник api.tnsu.kz, если это окажется частой
|
||||
// проблемой на практике).
|
||||
// Подразделение подтягивается из SharePoint User Profile (SPS-Department) при
|
||||
// выборе сотрудника — если профиль не синхронизирован, поле остаётся пустым.
|
||||
// Должность и организации — из Employee/ByEmail (см. shared/dictionary/
|
||||
// employeeOrganizations.service.ts), а не из профиля: сотрудник может состоять
|
||||
// сразу в нескольких организациях, должность берём из ПЕРВОЙ записи, чтобы
|
||||
// Должность и Организация оставались парой из одной записи.
|
||||
const handleEmployeeChange = (login: string): void => {
|
||||
setData((prev) => ({ ...prev, Employee: login, Position: '', Department: '' }));
|
||||
setData((prev) => ({ ...prev, Employee: login, Position: '', Department: '', Organization: '' }));
|
||||
if (!login) {
|
||||
return;
|
||||
}
|
||||
approval
|
||||
.getProfileFields(login)
|
||||
.then((profile) => setData((prev) => ({ ...prev, Position: profile.jobTitle, Department: profile.department })))
|
||||
.then((profile) => setData((prev) => ({ ...prev, Department: profile.department })))
|
||||
.catch(() => undefined);
|
||||
getEmployeeOrganizations(context, login)
|
||||
.then((records) =>
|
||||
setData((prev) => ({
|
||||
...prev,
|
||||
Position: records[0]?.position || prev.Position,
|
||||
Organization: formatEmployeeOrganizations(records)
|
||||
}))
|
||||
)
|
||||
.catch(() => undefined);
|
||||
};
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ export async function loadClearanceRequest(itemId: number, listId?: string): Pro
|
||||
'ParkingCard',
|
||||
'AccessCard',
|
||||
'TrainingAmount',
|
||||
'Organization',
|
||||
'RequestStatus',
|
||||
'RequestUniqueCode',
|
||||
'Employee/Title',
|
||||
@@ -50,7 +51,8 @@ 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)
|
||||
TrainingAmount: amount === null || amount === undefined ? '' : String(amount),
|
||||
Organization: (item.Organization as string) || ''
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -25,6 +25,12 @@ export interface IFormData {
|
||||
AccessCard: boolean;
|
||||
/** Сумма обучения — «сырые» цифры без пробелов (см. MoneyInput). Пусто = не указана. */
|
||||
TrainingAmount: string;
|
||||
/**
|
||||
* Организации сотрудника (`\n`-joined «Компания — должность» на строку) —
|
||||
* подтягивается автоматически из Employee/ByEmail при выборе Employee,
|
||||
* read-only (см. shared/ui/OrganizationField).
|
||||
*/
|
||||
Organization: string;
|
||||
}
|
||||
|
||||
/** Обновление одного поля формы с сохранением типа значения. */
|
||||
@@ -49,7 +55,8 @@ export const EMPTY_DATA: IFormData = {
|
||||
TerminationDate: '',
|
||||
ParkingCard: false,
|
||||
AccessCard: false,
|
||||
TrainingAmount: ''
|
||||
TrainingAmount: '',
|
||||
Organization: ''
|
||||
};
|
||||
|
||||
/** Плейсхолдер для поля выбора сотрудника. */
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
{ "internalName": "ParkingCard", "title": "Парковочная карта", "type": "Boolean" },
|
||||
{ "internalName": "AccessCard", "title": "Карта СКУД", "type": "Boolean" },
|
||||
{ "internalName": "TrainingAmount", "title": "Обучение: сумма", "type": "Number", "decimals": 2 }
|
||||
{ "internalName": "TrainingAmount", "title": "Обучение: сумма", "type": "Number", "decimals": 2 },
|
||||
{ "internalName": "Organization", "title": "Организация", "type": "Note" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { CollapseSection, FormField, TextInput, MoneyInput, Toggle, PeopleField } from '../../../shared/ui';
|
||||
import { CollapseSection, FormField, TextInput, MoneyInput, Toggle, PeopleField, OrganizationField } from '../../../shared/ui';
|
||||
import { ISectionProps, EMPLOYEE_PLACEHOLDER } from '../model';
|
||||
|
||||
// Подразделение подтягивается автоматически при выборе сотрудника (см.
|
||||
@@ -54,6 +54,7 @@ export const RequestSection: React.FC<IRequestSectionProps> = ({
|
||||
placeholder="Подставится автоматически по выбранному сотруднику"
|
||||
/>
|
||||
</FormField>
|
||||
<OrganizationField value={data.Organization} />
|
||||
<FormField label="Дата расторжения договора" htmlFor="TerminationDate" required>
|
||||
<TextInput
|
||||
id="TerminationDate"
|
||||
|
||||
@@ -67,7 +67,8 @@ 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
|
||||
TrainingAmount: Number.isFinite(amount) && data.TrainingAmount.trim() !== '' ? amount : null,
|
||||
Organization: data.Organization || null
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user