fix(ct): файлы КП сохраняются под латинским именем
Бэкенд extractPDFText теряет расширение у PDF с кириллицей в имени (OCR 415 «unsupported file type ''») и отвечает 500 — КП падало при каждой первой загрузке. Повторная загрузка «проходила» лишь потому, что текст бэкенд успевал записать, и фронт пропускал извлечение. Имя в библиотеке КП транслитерируется, остальные символы — в «_». Результат детерминированный: поиск уже загруженного файла и проверка дублей работают как раньше.
This commit is contained in:
@@ -4,14 +4,44 @@ describe('buildKpStorageName (имя файла КП в библиотеке)',
|
|||||||
// КП сохранялись под исходным именем с перезаписью: «КП.pdf» из одной СТ затирал «КП.pdf» другой,
|
// КП сохранялись под исходным именем с перезаписью: «КП.pdf» из одной СТ затирал «КП.pdf» другой,
|
||||||
// и согласующий первой СТ открывал чужое КП. Префикс СТ делает имя уникальным.
|
// и согласующий первой СТ открывал чужое КП. Префикс СТ делает имя уникальным.
|
||||||
it('добавляет префикс СТ', () => {
|
it('добавляет префикс СТ', () => {
|
||||||
expect(buildKpStorageName('357', 'КП.pdf')).toBe('357_КП.pdf');
|
expect(buildKpStorageName('357', 'KP.pdf')).toBe('357_KP.pdf');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('не добавляет префикс второй раз', () => {
|
it('не добавляет префикс второй раз', () => {
|
||||||
expect(buildKpStorageName('357', '357_КП.pdf')).toBe('357_КП.pdf');
|
expect(buildKpStorageName('357', '357_KP.pdf')).toBe('357_KP.pdf');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('без СТ — исходное имя', () => {
|
it('без СТ — имя без префикса', () => {
|
||||||
expect(buildKpStorageName(undefined, 'КП.pdf')).toBe('КП.pdf');
|
expect(buildKpStorageName(undefined, 'KP.pdf')).toBe('KP.pdf');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('buildKpStorageName — только латиница', () => {
|
||||||
|
// СТ 365, файл 306: PDF с кириллицей в имени → extractPDFText 500 (OCR 415 «unsupported file type ''»),
|
||||||
|
// тот же PDF с латинским именем проходит. Бэкенд не наш — храним файл под латинским именем.
|
||||||
|
it('транслитерирует кириллицу', () => {
|
||||||
|
expect(buildKpStorageName('365', 'КП_тест_кириллица.pdf')).toBe('365_KP_test_kirillitsa.pdf');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('транслитерирует заглавные и казахские буквы', () => {
|
||||||
|
expect(buildKpStorageName('274', 'Қорғаныш_Әлем_Щит.pdf')).toBe('274_Qorganysh_Alem_Shchit.pdf');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('пробелы, №, кавычки и тире — в безопасные символы', () => {
|
||||||
|
expect(buildKpStorageName('363', 'Счет №1891 ТОО «Орда» — копия (2).pdf'))
|
||||||
|
.toBe('363_Schet_N1891_TOO_Orda_kopiya_(2).pdf');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('латинское имя не меняется', () => {
|
||||||
|
expect(buildKpStorageName('364', 'KP_02_Qurylys-Material_KZT.pdf')).toBe('364_KP_02_Qurylys-Material_KZT.pdf');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('уже сохранённое имя при повторном вызове не меняется (по нему ищем загруженный файл)', () => {
|
||||||
|
const stored = buildKpStorageName('274', 'Северный мост.pdf');
|
||||||
|
expect(buildKpStorageName('274', stored)).toBe(stored);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('расширение сохраняется', () => {
|
||||||
|
expect(buildKpStorageName('1', 'КП.PDF')).toBe('1_KP.PDF');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,42 @@
|
|||||||
|
// Транслитерация кириллицы (русский + казахский) для имени файла в SP.
|
||||||
|
const TRANSLIT: Record<string, string> = {
|
||||||
|
а: 'a', б: 'b', в: 'v', г: 'g', д: 'd', е: 'e', ё: 'e', ж: 'zh', з: 'z', и: 'i', й: 'y',
|
||||||
|
к: 'k', л: 'l', м: 'm', н: 'n', о: 'o', п: 'p', р: 'r', с: 's', т: 't', у: 'u', ф: 'f',
|
||||||
|
х: 'kh', ц: 'ts', ч: 'ch', ш: 'sh', щ: 'shch', ъ: '', ы: 'y', ь: '', э: 'e', ю: 'yu', я: 'ya',
|
||||||
|
ә: 'a', ғ: 'g', қ: 'q', ң: 'n', ө: 'o', ұ: 'u', ү: 'u', һ: 'h', і: 'i',
|
||||||
|
'№': 'N',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Всё, кроме этих символов, заменяем на «_».
|
||||||
|
const UNSAFE_CHARS = /[^A-Za-z0-9_.()-]/g;
|
||||||
|
|
||||||
|
function transliterateChar(char: string): string {
|
||||||
|
const lower = char.toLowerCase();
|
||||||
|
const mapped = TRANSLIT[lower];
|
||||||
|
if (mapped === undefined) return char;
|
||||||
|
// Заглавная буква: «Щ» → «Shch», а не «SHCH».
|
||||||
|
if (char !== lower && mapped.length > 0) return mapped[0].toUpperCase() + mapped.slice(1);
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Бэкенд extractPDFText теряет расширение у PDF с кириллицей в имени (OCR 415 «unsupported file type ''»)
|
||||||
|
// и отвечает 500 — КП падало при каждой первой загрузке. Бэкенд не наш, поэтому храним файл под именем
|
||||||
|
// только из латиницы, цифр и «_ - . ( )». Результат детерминированный: по нему ищем уже загруженный файл.
|
||||||
|
function toSafeFileName(name: string): string {
|
||||||
|
return Array.from(name)
|
||||||
|
.map(transliterateChar)
|
||||||
|
.join('')
|
||||||
|
.replace(UNSAFE_CHARS, '_')
|
||||||
|
.replace(/_+/g, '_')
|
||||||
|
.replace(/_+\./g, '.');
|
||||||
|
}
|
||||||
|
|
||||||
// Имя файла КП в библиотеке «Коммерческие предложения» — с префиксом СТ. Файлы лежат в корне библиотеки
|
// Имя файла КП в библиотеке «Коммерческие предложения» — с префиксом СТ. Файлы лежат в корне библиотеки
|
||||||
// и загружались под исходным именем с перезаписью: «КП.pdf» одной СТ затирал «КП.pdf» другой, и
|
// и загружались под исходным именем с перезаписью: «КП.pdf» одной СТ затирал «КП.pdf» другой, и
|
||||||
// согласующий открывал чужое КП. Ссылки, выгрузка INCORE и восстановление по ссылке работают по этому имени.
|
// согласующий открывал чужое КП. Ссылки, выгрузка INCORE и восстановление по ссылке работают по этому имени.
|
||||||
export function buildKpStorageName(ctId: string | undefined, originalName: string): string {
|
export function buildKpStorageName(ctId: string | undefined, originalName: string): string {
|
||||||
if (!ctId) return originalName;
|
const safeName = toSafeFileName(originalName);
|
||||||
|
if (!ctId) return safeName;
|
||||||
const prefix = `${ctId}_`;
|
const prefix = `${ctId}_`;
|
||||||
return originalName.startsWith(prefix) ? originalName : `${prefix}${originalName}`;
|
return safeName.startsWith(prefix) ? safeName : `${prefix}${safeName}`;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user