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 на
// бэкенде не подтверждена снятой — поэтому передаём ПЕРВЫЙ выбранный проект
// (поле — мультивыбор) и ждём его выбора, как раньше.
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);
// Несколько шагов маршрута условны по компании (см. панель «Маршруты»:
// «CompaniesJson = TOO QAZGEOMAX», «CompaniesJson = TOO CT Global», шаг с
@@ -35,8 +35,15 @@ describe('RecruitingHire projects', () => {
expect(projectsFromItem({ Project: 'КЭС Курчатов', ProjectId: 'g-kes', ProjectCode: 'БК0000068' })).toEqual([KES]);
});
it('старая заявка с проектом, введённым вручную (без Id), — проектов нет', () => {
expect(projectsFromItem({ Project: 'что-то вручную', ProjectId: null })).toEqual([]);
it('старая заявка с проектом, введённым вручную (без Id), — проект сохраняется по тексту', () => {
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 не роняет загрузку', () => {
@@ -30,7 +30,8 @@ export function projectColumns(projects: IProjectRef[]): IProjectColumns {
return {
ProjectsJson: JSON.stringify(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
};
}
@@ -42,22 +43,21 @@ export function projectNames(projects: IProjectRef[]): string {
/**
* Проекты из элемента списка. Заявки, поданные до мультивыбора, ProjectsJson не
* имеют — тогда собираем один проект из старых колонок (если он был выбран из
* справочника, т.е. есть ProjectId).
* имеют — тогда собираем один проект из старых колонок.
*
* Старый одиночный пикер допускал ручной ввод без выбора из справочника (Id
* пустой). Такой проект тоже сохраняем — с пустым Id: иначе поле открылось бы
* пустым, а пересохранение заявки (например, правка всех полей согласующим)
* стёрло бы текст проекта в колонке Project.
*/
export function projectsFromItem(item: Record<string, unknown>): IProjectRef[] {
const fromJson = parseProjectsJson(item.ProjectsJson);
if (fromJson.length > 0) return fromJson;
const id = typeof item.ProjectId === 'string' ? item.ProjectId : '';
if (!id) return [];
return [
{
Id: id,
Name: typeof item.Project === 'string' ? item.Project : '',
Code: typeof item.ProjectCode === 'string' ? item.ProjectCode : ''
}
];
const name = typeof item.Project === 'string' ? item.Project.trim() : '';
if (!id && !name) return [];
return [{ Id: id, Name: name, Code: typeof item.ProjectCode === 'string' ? item.ProjectCode : '' }];
}
function parseProjectsJson(raw: unknown): IProjectRef[] {