fix: проект, введённый вручную в старой заявке на приём, не теряется
Tests / frontend-test (push) Successful in 1m57s

Старый одиночный пикер допускал ручной ввод без выбора из справочника
(пустой ProjectId). Такие заявки открывались с пустым полем «Проект», а
пересохранение (правка всех полей согласующим) стирало текст проекта в
колонке Project. Теперь он читается как проект без Id и сохраняется как есть.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Тимур Городецкий
2026-10-08 13:02:54 +05:00
parent 57efa7620e
commit 8a22767dd3
3 changed files with 22 additions and 14 deletions
@@ -158,7 +158,8 @@ const RecruitingHireForm: React.FC<IRequestFormProps> = ({
// найден». Сейчас в маршруте условий по проекту нет, но проверка projectId на // найден». Сейчас в маршруте условий по проекту нет, но проверка projectId на
// бэкенде не подтверждена снятой — поэтому передаём ПЕРВЫЙ выбранный проект // бэкенде не подтверждена снятой — поэтому передаём ПЕРВЫЙ выбранный проект
// (поле — мультивыбор) и ждём его выбора, как раньше. // (поле — мультивыбор) и ждём его выбора, как раньше.
const projectId = data.Projects.length > 0 ? data.Projects[0].Id : null; // Пустой Id — проект старой заявки, введённый вручную (см. projectsFromItem).
const projectId = (data.Projects.length > 0 && data.Projects[0].Id) || null;
const matrixReady = !!editId || (!!projectId && !!requestTypeId); const matrixReady = !!editId || (!!projectId && !!requestTypeId);
// Несколько шагов маршрута условны по компании (см. панель «Маршруты»: // Несколько шагов маршрута условны по компании (см. панель «Маршруты»:
// «CompaniesJson = TOO QAZGEOMAX», «CompaniesJson = TOO CT Global», шаг с // «CompaniesJson = TOO QAZGEOMAX», «CompaniesJson = TOO CT Global», шаг с
@@ -35,8 +35,15 @@ describe('RecruitingHire projects', () => {
expect(projectsFromItem({ Project: 'КЭС Курчатов', ProjectId: 'g-kes', ProjectCode: 'БК0000068' })).toEqual([KES]); expect(projectsFromItem({ Project: 'КЭС Курчатов', ProjectId: 'g-kes', ProjectCode: 'БК0000068' })).toEqual([KES]);
}); });
it('старая заявка с проектом, введённым вручную (без Id), — проектов нет', () => { it('старая заявка с проектом, введённым вручную (без Id), — проект сохраняется по тексту', () => {
expect(projectsFromItem({ Project: 'что-то вручную', ProjectId: null })).toEqual([]); const projects = projectsFromItem({ Project: 'что-то вручную', ProjectId: null });
expect(projects).toEqual([{ Id: '', Name: 'что-то вручную', Code: '' }]);
// Пересохранение не стирает текст и не пишет пустой Id.
expect(projectColumns(projects)).toMatchObject({ Project: 'что-то вручную', ProjectId: null });
});
it('старая заявка без проекта — проектов нет', () => {
expect(projectsFromItem({ Project: null, ProjectId: null })).toEqual([]);
}); });
it('битый JSON не роняет загрузку', () => { it('битый JSON не роняет загрузку', () => {
@@ -30,7 +30,8 @@ export function projectColumns(projects: IProjectRef[]): IProjectColumns {
return { return {
ProjectsJson: JSON.stringify(projects), ProjectsJson: JSON.stringify(projects),
Project: truncateSpText(projectNames(projects)), Project: truncateSpText(projectNames(projects)),
ProjectId: projects[0].Id, // Пустой Id — проект из старой заявки, введённый вручную (см. projectsFromItem).
ProjectId: projects[0].Id || null,
ProjectCode: codes.length > 0 ? truncateSpText(codes.join(LEGACY_SEPARATOR)) : null ProjectCode: codes.length > 0 ? truncateSpText(codes.join(LEGACY_SEPARATOR)) : null
}; };
} }
@@ -42,22 +43,21 @@ export function projectNames(projects: IProjectRef[]): string {
/** /**
* Проекты из элемента списка. Заявки, поданные до мультивыбора, ProjectsJson не * Проекты из элемента списка. Заявки, поданные до мультивыбора, ProjectsJson не
* имеют — тогда собираем один проект из старых колонок (если он был выбран из * имеют — тогда собираем один проект из старых колонок.
* справочника, т.е. есть ProjectId). *
* Старый одиночный пикер допускал ручной ввод без выбора из справочника (Id
* пустой). Такой проект тоже сохраняем — с пустым Id: иначе поле открылось бы
* пустым, а пересохранение заявки (например, правка всех полей согласующим)
* стёрло бы текст проекта в колонке Project.
*/ */
export function projectsFromItem(item: Record<string, unknown>): IProjectRef[] { export function projectsFromItem(item: Record<string, unknown>): IProjectRef[] {
const fromJson = parseProjectsJson(item.ProjectsJson); const fromJson = parseProjectsJson(item.ProjectsJson);
if (fromJson.length > 0) return fromJson; if (fromJson.length > 0) return fromJson;
const id = typeof item.ProjectId === 'string' ? item.ProjectId : ''; const id = typeof item.ProjectId === 'string' ? item.ProjectId : '';
if (!id) return []; const name = typeof item.Project === 'string' ? item.Project.trim() : '';
return [ if (!id && !name) return [];
{ return [{ Id: id, Name: name, Code: typeof item.ProjectCode === 'string' ? item.ProjectCode : '' }];
Id: id,
Name: typeof item.Project === 'string' ? item.Project : '',
Code: typeof item.ProjectCode === 'string' ? item.ProjectCode : ''
}
];
} }
function parseProjectsJson(raw: unknown): IProjectRef[] { function parseProjectsJson(raw: unknown): IProjectRef[] {