Andrey Epifancev 78227b46c0 AURORA-1980: переименование программы и проекта
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 22:32:42 +04:00

125 lines
4.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const ROW_TYPE_LEVELS = {
ROOT: 0,
GROUP: 1,
ITEM: 2,
SUB_ITEM: 3,
INPUT: 4,
};
export function buildRowHierarchy(items, mapItem = (item) => item) {
const result = [];
const stack = [];
for (const [index, item] of items.entries()) {
const currentLevel = ROW_TYPE_LEVELS[item.row_type] ?? 999;
while (stack.length > 0 && (ROW_TYPE_LEVELS[stack.at(-1).row_type] ?? 999) >= currentLevel) {
stack.pop();
}
const parent = stack.at(-1);
const newNode = { ...mapItem(item, index), subRows: [] };
if (parent) parent.subRows.push(newNode);
else result.push(newNode);
if (item.row_type !== 'INPUT') stack.push(newNode);
}
return result;
}
export function getDataRowId(data) {
return data?.id || data?.line_id || null;
}
export function getRowId(row) {
return getDataRowId(row.original.data);
}
// AURORA-1978: что удаляет кнопка «Удалить строку» для выделенной строки.
// На форме 4 у строк программы (GROUP) и проекта (ITEM) нет line_id — их
// удаляют отдельные события program_deleted / project_deleted по id из header.
// Непустые программу/проект сервер не удалит (v3.del_form4_program /
// v3.del_form4_project), поэтому причину показываем сразу, без запроса.
// rowData — row.original (row_type, data, subRows).
export function getDeleteTarget(rowData, { isForm4 = false } = {}) {
if (!rowData) return null;
const header = rowData.data?.header;
const hasChildren = Array.isArray(rowData.subRows) && rowData.subRows.length > 0;
if (isForm4 && rowData.row_type === 'GROUP' && header?.program_id != null) {
return {
kind: 'program',
id: header.program_id,
name: header.name ?? '',
blockedReason: hasChildren ? 'Нельзя удалить программу: в ней есть проекты или расходы' : null,
};
}
if (isForm4 && rowData.row_type === 'ITEM' && header?.project_id != null) {
return {
kind: 'project',
id: header.project_id,
name: header.name ?? '',
blockedReason: hasChildren ? 'Нельзя удалить проект: в нём есть расходы' : null,
};
}
const id = getDataRowId(rowData.data);
return id == null ? null : { kind: 'row', id };
}
// AURORA-1984: цель добавления проекта по выделенной строке. Программа задана
// только если у GROUP/ITEM/INPUT header.program_id != null; иначе проект
// самостоятельный. rowData — row.original.
export function getProjectAddTarget(rowData) {
const programId = rowData?.data?.header?.program_id;
const typed = ['GROUP', 'ITEM', 'INPUT'].includes(rowData?.row_type);
return typed && programId != null ? { program_id: programId } : {};
}
export function isVspRow(row) {
if (row?.original?.data?.id) {
return true;
}
return false;
}
export function isVspNewRow(rowData) {
if (!rowData) return false;
const newRow = rowData[3];
return !!newRow.id;
}
export function getParentId(data) {
return data?.project_id || data?.expense_item_id || null;
}
// AURORA-1887: лист AHR отдаёт header.can_add_row=false для статей
// аренды/коммуналки/охраны (у записи сателлита нет понятия "сверх лимита" —
// ручная строка там не имеет смысла). На остальных листах и для остальных
// статей (включая лимитные — AURORA-1907 "сверх лимита" не трогаем) поле
// либо true, либо вовсе отсутствует/null — по умолчанию добавление строки
// разрешено.
export function canAddRowForHeader(header) {
return header?.can_add_row !== false;
}
// AURORA-1980: что переименовывает кнопка панели — программу (GROUP) или проект (ITEM) формы 4.
export function getRenameTarget(rowData) {
const header = rowData?.data?.header;
if (rowData?.row_type === 'GROUP' && header?.program_id != null) {
return { kind: 'program', id: header.program_id, name: header.name ?? '' };
}
if (rowData?.row_type === 'ITEM' && header?.project_id != null) {
return { kind: 'project', id: header.project_id, name: header.name ?? '' };
}
return null;
}
// AURORA-1980: WS без идентификатора запроса — свой ответ на своё переименование
// узнаём по is_self, событию и id узла (чужое переименование окно не закрывает).
export function isRenameResponse(msg, target) {
if (!msg || !target || msg.is_self !== true) return false;
return msg.event === `${target.kind}_updated` && msg.data?.[`${target.kind}_id`] === target.id;
}