fix(ct): файлы КП сохраняются под латинским именем
Бэкенд extractPDFText теряет расширение у PDF с кириллицей в имени (OCR 415 «unsupported file type ''») и отвечает 500 — КП падало при каждой первой загрузке. Повторная загрузка «проходила» лишь потому, что текст бэкенд успевал записать, и фронт пропускал извлечение. Имя в библиотеке КП транслитерируется, остальные символы — в «_». Результат детерминированный: поиск уже загруженного файла и проверка дублей работают как раньше.
This commit is contained in:
@@ -4,14 +4,44 @@ describe('buildKpStorageName (имя файла КП в библиотеке)',
|
||||
// КП сохранялись под исходным именем с перезаписью: «КП.pdf» из одной СТ затирал «КП.pdf» другой,
|
||||
// и согласующий первой СТ открывал чужое КП. Префикс СТ делает имя уникальным.
|
||||
it('добавляет префикс СТ', () => {
|
||||
expect(buildKpStorageName('357', 'КП.pdf')).toBe('357_КП.pdf');
|
||||
expect(buildKpStorageName('357', 'KP.pdf')).toBe('357_KP.pdf');
|
||||
});
|
||||
|
||||
it('не добавляет префикс второй раз', () => {
|
||||
expect(buildKpStorageName('357', '357_КП.pdf')).toBe('357_КП.pdf');
|
||||
expect(buildKpStorageName('357', '357_KP.pdf')).toBe('357_KP.pdf');
|
||||
});
|
||||
|
||||
it('без СТ — исходное имя', () => {
|
||||
expect(buildKpStorageName(undefined, 'КП.pdf')).toBe('КП.pdf');
|
||||
it('без СТ — имя без префикса', () => {
|
||||
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» другой, и
|
||||
// согласующий открывал чужое КП. Ссылки, выгрузка INCORE и восстановление по ссылке работают по этому имени.
|
||||
export function buildKpStorageName(ctId: string | undefined, originalName: string): string {
|
||||
if (!ctId) return originalName;
|
||||
const safeName = toSafeFileName(originalName);
|
||||
if (!ctId) return safeName;
|
||||
const prefix = `${ctId}_`;
|
||||
return originalName.startsWith(prefix) ? originalName : `${prefix}${originalName}`;
|
||||
return safeName.startsWith(prefix) ? safeName : `${prefix}${safeName}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user