fix(ct): файл КП сохраняется с префиксом СТ — одноимённый КП другой СТ не перезаписывает его
КП загружались в корень библиотеки под исходным именем с перезаписью: «КП.pdf» одной СТ затирал «КП.pdf» другой, и согласующий первой СТ открывал чужое КП. Теперь файл хранится как «<id СТ>_<имя>» (buildKpStorageName); это имя идёт в поиск уже загруженного файла, поток, список «Загруженные КП» и ссылку на файл. Повторная загрузка того же файла в эту же СТ по-прежнему распознаётся.
This commit is contained in:
@@ -13,6 +13,7 @@ export interface KpFile {
|
||||
file: File;
|
||||
status: 'uploading' | 'extracting' | 'flowing' | 'done' | 'error';
|
||||
spId?: number;
|
||||
storedName?: string; // имя в библиотеке КП (с префиксом СТ) — по нему ссылка на файл
|
||||
supplierName?: string;
|
||||
supplierSpId?: number;
|
||||
supplierBIN?: string;
|
||||
|
||||
+14
-8
@@ -4,6 +4,7 @@ import { CTFormData } from '../hooks/useCtForm';
|
||||
import { KpFile, FlowPositionResult, POLL_INTERVAL_MS, MAX_POLL_RETRIES } from './kpTypes';
|
||||
import { buildKpSuppliersAndOffers } from './step2Persist';
|
||||
import { buildKpOffersToSave } from './kpOfferRecords';
|
||||
import { buildKpStorageName } from '../utils/kpFileName';
|
||||
|
||||
let localIdCounter = 0;
|
||||
|
||||
@@ -51,7 +52,7 @@ export function useKpProcessing(
|
||||
const currentUploaded = fd.uploadedKpFiles ?? [];
|
||||
const newEntries = doneFiles
|
||||
.filter(f => !currentUploaded.some(u => u.spId === f.spId))
|
||||
.map(f => ({ name: f.file.name, spId: f.spId as number, supplierName: f.supplierName }));
|
||||
.map(f => ({ name: f.storedName ?? f.file.name, spId: f.spId as number, supplierName: f.supplierName }));
|
||||
if (newEntries.length > 0) {
|
||||
patch.uploadedKpFiles = [...currentUploaded, ...newEntries];
|
||||
}
|
||||
@@ -193,13 +194,17 @@ export function useKpProcessing(
|
||||
};
|
||||
|
||||
const handleFilesAdded = useCallback(async (files: File[]): Promise<void> => {
|
||||
const ctId = formDataRef.current.ct?.id;
|
||||
// В «Загруженных КП» имена хранения (с префиксом СТ), в очереди — исходные: сверяем с обоими.
|
||||
const existingNames = new Set([
|
||||
...kpFilesRef.current.map(f => f.file.name),
|
||||
...(formDataRef.current.uploadedKpFiles ?? []).map(f => f.name),
|
||||
]);
|
||||
const isUploaded = (f: File): boolean =>
|
||||
existingNames.has(f.name) || existingNames.has(buildKpStorageName(ctId, f.name));
|
||||
|
||||
const newFiles = files.filter(f => !existingNames.has(f.name));
|
||||
const skipped = files.filter(f => existingNames.has(f.name));
|
||||
const newFiles = files.filter(f => !isUploaded(f));
|
||||
const skipped = files.filter(isUploaded);
|
||||
|
||||
if (skipped.length > 0) {
|
||||
setDuplicateWarning(`Файл(ы) уже загружен(ы) в рамках этой СТ: ${skipped.map(f => f.name).join(', ')}`);
|
||||
@@ -222,25 +227,26 @@ export function useKpProcessing(
|
||||
if (mountedRef.current) setKpFiles(prev => [...prev, ...entries]);
|
||||
|
||||
for (const entry of entries) {
|
||||
const storageName = buildKpStorageName(ctId, entry.file.name);
|
||||
try {
|
||||
let cachedSpId: number | undefined;
|
||||
try {
|
||||
const existing = await service.findExistingKpFile(entry.file.name, entry.file.size);
|
||||
const existing = await service.findExistingKpFile(storageName, entry.file.size);
|
||||
if (existing) cachedSpId = existing.spId;
|
||||
} catch {
|
||||
// lookup failed — proceed with normal upload
|
||||
}
|
||||
|
||||
if (cachedSpId !== undefined) {
|
||||
patchFile(entry.localId, { spId: cachedSpId, status: 'extracting' });
|
||||
pollSyntex(entry.localId, cachedSpId, entry.file.name, 0).catch(err => {
|
||||
patchFile(entry.localId, { spId: cachedSpId, storedName: storageName, status: 'extracting' });
|
||||
pollSyntex(entry.localId, cachedSpId, storageName, 0).catch(err => {
|
||||
patchFile(entry.localId, { status: 'error', error: (err as Error).message });
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const { spId, name } = await service.uploadKpFile(entry.file);
|
||||
patchFile(entry.localId, { spId, status: 'extracting' });
|
||||
const { spId, name } = await service.uploadKpFile(entry.file, storageName);
|
||||
patchFile(entry.localId, { spId, storedName: name, status: 'extracting' });
|
||||
|
||||
const isWord = /\.(doc|docx|xls|xlsx)$/i.test(entry.file.name);
|
||||
const isPdf = /\.pdf$/i.test(entry.file.name);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { buildKpStorageName } from './kpFileName';
|
||||
|
||||
describe('buildKpStorageName (имя файла КП в библиотеке)', () => {
|
||||
// КП сохранялись под исходным именем с перезаписью: «КП.pdf» из одной СТ затирал «КП.pdf» другой,
|
||||
// и согласующий первой СТ открывал чужое КП. Префикс СТ делает имя уникальным.
|
||||
it('добавляет префикс СТ', () => {
|
||||
expect(buildKpStorageName('357', 'КП.pdf')).toBe('357_КП.pdf');
|
||||
});
|
||||
|
||||
it('не добавляет префикс второй раз', () => {
|
||||
expect(buildKpStorageName('357', '357_КП.pdf')).toBe('357_КП.pdf');
|
||||
});
|
||||
|
||||
it('без СТ — исходное имя', () => {
|
||||
expect(buildKpStorageName(undefined, 'КП.pdf')).toBe('КП.pdf');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
// Имя файла КП в библиотеке «Коммерческие предложения» — с префиксом СТ. Файлы лежат в корне библиотеки
|
||||
// и загружались под исходным именем с перезаписью: «КП.pdf» одной СТ затирал «КП.pdf» другой, и
|
||||
// согласующий открывал чужое КП. Ссылки, выгрузка INCORE и восстановление по ссылке работают по этому имени.
|
||||
export function buildKpStorageName(ctId: string | undefined, originalName: string): string {
|
||||
if (!ctId) return originalName;
|
||||
const prefix = `${ctId}_`;
|
||||
return originalName.startsWith(prefix) ? originalName : `${prefix}${originalName}`;
|
||||
}
|
||||
@@ -1002,9 +1002,10 @@ export class CtService {
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
}
|
||||
|
||||
async uploadKpFile(file: File): Promise<{ spId: number; name: string }> {
|
||||
// storageName — имя в библиотеке (с префиксом СТ, см. buildKpStorageName); по умолчанию исходное.
|
||||
async uploadKpFile(file: File, storageName: string = file.name): Promise<{ spId: number; name: string }> {
|
||||
const buffer = await this._readFile(file);
|
||||
const uploadUrl = `${this.baseUrl}/_api/web/GetFolderByServerRelativeUrl('${encodeURIComponent(this.kpFolder)}')/Files/add(url='${encodeURIComponent(file.name)}',overwrite=true)`;
|
||||
const uploadUrl = `${this.baseUrl}/_api/web/GetFolderByServerRelativeUrl('${encodeURIComponent(this.kpFolder)}')/Files/add(url='${encodeURIComponent(storageName)}',overwrite=true)`;
|
||||
|
||||
const uploadRes = await this.http.post(
|
||||
uploadUrl,
|
||||
@@ -1020,7 +1021,7 @@ export class CtService {
|
||||
if (!uploadRes.ok)
|
||||
throw new Error(`Ошибка загрузки файла: ${uploadRes.status}`);
|
||||
|
||||
const fileSRUrl = `${this.kpFolder}/${file.name}`;
|
||||
const fileSRUrl = `${this.kpFolder}/${storageName}`;
|
||||
const itemRes = await this.http.get(
|
||||
`${this.baseUrl}/_api/web/GetFileByServerRelativeUrl('${encodeURIComponent(fileSRUrl)}')/ListItemAllFields?$select=Id`,
|
||||
SPHttpClient.configurations.v1,
|
||||
@@ -1028,7 +1029,7 @@ export class CtService {
|
||||
if (!itemRes.ok)
|
||||
throw new Error(`Ошибка получения ID файла: ${itemRes.status}`);
|
||||
const itemData = await itemRes.json();
|
||||
return { spId: Number(itemData.Id), name: file.name };
|
||||
return { spId: Number(itemData.Id), name: storageName };
|
||||
}
|
||||
|
||||
async getKpItemFields(fileSpId: number): Promise<SyntexFields> {
|
||||
|
||||
Reference in New Issue
Block a user