Merge branch 'main' of http://git.tnsu.kz/timur.gorodetskiy/request-portal
Tests / frontend-test (push) Successful in 1m9s

This commit is contained in:
Тимур Городецкий
2026-08-26 16:38:20 +05:00
18 changed files with 271 additions and 244 deletions
@@ -36,7 +36,7 @@ RequestTypeId» в `docs/forms.md`). Тип заведён в админке app
отдельных полей для них нет (были бы дублированием). `data.Organization:
IOrganizationRecord[]` — массив `{company, department, position}`; поле называется
`department` (тот же справочник `Section`, что и раньше — см. следующий абзац),
колонка называется «Структурное подразделение», и то и другое read-only. Должность
колонка называется «Подразделение», и то и другое read-only. Должность
редактируема В ЛЮБОЙ строке (справочник не всегда точен), но маршрут (условие
«IsManager») и обязательность по валидации завязаны ТОЛЬКО на должность ПЕРВОЙ
(основной) записи — `primaryPosition(data.Organization)`; должности остальных
@@ -52,8 +52,6 @@ RequestTypeId» в `docs/forms.md`). Тип заведён в админке app
/ `organizationColumn` в `model.ts`/`submit.ts`; не-JSON значение (заявка,
сохранённая до перехода на этот формат) не роняет чтение — трактуется как одна
компания (см. `parseOrganizationColumn`).
- **Самостоятельное подразделение** (`StandaloneUnit`) — в справочнике его нет,
свободный ввод, единственное текстовое поле, оставшееся отдельно от таблицы.
- **Последний рабочий день** (`LastWorkingDay`) — обязателен, не раньше чем через
`MIN_NOTICE_MONTHS` (1) календарный месяц от сегодня (`isoDatePlusMonths`,
`min` у `input type=date` + проверка в `validate` на случай обхода UI) — срок
@@ -173,9 +171,8 @@ approval-route (`getPlannedMatrix` для фаз «Согласование» и
## Секции формы
RequestSection (сотрудник · организация/департамент/должность (таблица —
редактируемая на форме подачи, read-only в просмотре) · самостоятельное
подразделение · последний рабочий день · телефон · комментарий · ссылка на
обходной лист · лицо, принимающее ОС/НМА)
редактируемая на форме подачи, read-only в просмотре) · последний рабочий день ·
телефон · комментарий · ссылка на обходной лист · лицо, принимающее ОС/НМА)
## Точки входа
- rptype: `hr-dismissal`
@@ -27,7 +27,6 @@ export async function loadDismissalRequest(itemId: number, listId?: string): Pro
'Id',
'Position',
'Department',
'StandaloneUnit',
'LastWorkingDay',
'Phone',
'Comment',
@@ -48,7 +47,6 @@ export async function loadDismissalRequest(itemId: number, listId?: string): Pro
const data: IFormData = {
...EMPTY_DATA,
Employee: employee?.EMail || '',
StandaloneUnit: (item.StandaloneUnit as string) || '',
LastWorkingDay: lastDay.slice(0, 10), // ISO → YYYY-MM-DD для input type=date
Phone: (item.Phone as string) || '',
Comment: (item.Comment as string) || '',
@@ -86,8 +86,6 @@ export function zipOrganization(
export interface IFormData {
/** E-mail сотрудника, за которого заявка (People). Пусто = не выбран. */
Employee: string;
/** Самостоятельное подразделение — в справочнике его нет, вводится вручную. */
StandaloneUnit: string;
/** Последний рабочий день, ISO YYYY-MM-DD (input type=date). */
LastWorkingDay: string;
/** Контактный телефон (маска, см. PhoneInput). */
@@ -139,7 +137,6 @@ export interface ISectionProps {
export const EMPTY_DATA: IFormData = {
Employee: '',
StandaloneUnit: '',
LastWorkingDay: '',
Phone: '',
Comment: '',
@@ -7,7 +7,6 @@
{ "internalName": "Employee", "title": "Сотрудник", "type": "User", "userSelectionMode": "PeopleOnly", "required": true },
{ "internalName": "Position", "title": "Должность", "type": "Text" },
{ "internalName": "Department", "title": "Департамент", "type": "Text" },
{ "internalName": "StandaloneUnit", "title": "Самостоятельное подразделение", "type": "Text" },
{ "internalName": "LastWorkingDay", "title": "Последний рабочий день", "type": "DateTime", "dateOnly": true },
{ "internalName": "Phone", "title": "Контактный телефон", "type": "Text" },
{ "internalName": "Comment", "title": "Комментарий", "type": "Note" },
@@ -56,7 +56,7 @@ export const OrganizationsTable: React.FC<IOrganizationsTableProps> = ({ records
<thead>
<tr>
<th className={styles.company}>Компания</th>
<th className={styles.department}>Структурное подразделение</th>
<th className={styles.department}>Подразделение</th>
<th className={styles.position}>Должность</th>
{onRemove && <th className={styles.action} />}
</tr>
@@ -1,5 +1,5 @@
import * as React from 'react';
import { CollapseSection, FormField, TextInput, TextArea, PhoneInput, PeopleField, LinkField } from '../../../shared/ui';
import { FormGrid, FormField, TextInput, TextArea, PhoneInput, PeopleField, LinkField } from '../../../shared/ui';
import { ISectionProps, EMPLOYEE_PLACEHOLDER, MIN_NOTICE_MONTHS } from '../model';
import { isoDatePlusMonths } from '../../../shared/dateUtils';
import { OrganizationsTable } from './OrganizationsTable';
@@ -28,7 +28,7 @@ export const RequestSection: React.FC<IRequestSectionProps> = ({
onRemoveOrganization,
assetsAcceptorEditable
}) => (
<CollapseSection title="Заявление на увольнение" icon="document">
<FormGrid>
<FormField label="Сотрудник" htmlFor="Employee" required full>
<PeopleField
context={context}
@@ -55,9 +55,6 @@ export const RequestSection: React.FC<IRequestSectionProps> = ({
}
onRemove={onRemoveOrganization}
/>
<FormField label="Самостоятельное подразделение" htmlFor="StandaloneUnit">
<TextInput id="StandaloneUnit" value={data.StandaloneUnit} onChange={(v) => set('StandaloneUnit', v)} />
</FormField>
{/* Заявление подаётся заранее — датой увольнения нельзя назначить ближе,
чем через MIN_NOTICE_MONTHS месяц от сегодня (min). */}
<FormField label="Последний рабочий день" htmlFor="LastWorkingDay" required>
@@ -94,5 +91,5 @@ export const RequestSection: React.FC<IRequestSectionProps> = ({
readOnly={assetsAcceptorEditable ? false : true}
/>
</FormField>
</CollapseSection>
</FormGrid>
);
@@ -85,7 +85,6 @@ function buildItem(
Position: organizationColumn(data.Organization, (r) => r.position, 'Должность', TEXT_COLUMN_LIMIT),
Department: organizationColumn(data.Organization, (r) => r.department, 'Департамент', TEXT_COLUMN_LIMIT),
Organization: organizationColumn(data.Organization, (r) => r.company, 'Организация'),
StandaloneUnit: data.StandaloneUnit || null,
LastWorkingDay: data.LastWorkingDay || null,
Phone: data.Phone || null,
Comment: data.Comment || null