feat: Duplikate im Import als neue Version behandeln #6
No files matched your search
@@ -14,6 +14,7 @@ import { PdfService } from '../preprocessing/pdf.service';
|
||||
import { EmailPageCacheService } from './email-page-cache.service';
|
||||
import { ImapFolderService } from './imap-folder.service';
|
||||
import { AttachmentFingerprintService } from './attachment-fingerprint.service';
|
||||
import { DokumentVersionService } from '../paperless/dokument-version.service';
|
||||
|
||||
async function pdfMitSeiten(anzahl: number): Promise<Buffer> {
|
||||
const doc = await PDFDocument.create();
|
||||
@@ -29,11 +30,19 @@ describe('EmailImportService – Duplikatprüfung', () => {
|
||||
andWhere: jest.Mock;
|
||||
getCount: jest.Mock;
|
||||
};
|
||||
let attachmentRepo: { findOne: jest.Mock };
|
||||
let attachmentRepo: { findOne: jest.Mock; save: jest.Mock };
|
||||
let taskRepo: {
|
||||
createQueryBuilder: jest.Mock;
|
||||
create: jest.Mock;
|
||||
save: jest.Mock;
|
||||
};
|
||||
let emailRepo: { update: jest.Mock; findOne: jest.Mock };
|
||||
let dokumentVersion: { haengeVersionAn: jest.Mock };
|
||||
let contentRepo: { findOne: jest.Mock };
|
||||
let paperlessService: {
|
||||
checksumExists: jest.Mock;
|
||||
uploadDocument: jest.Mock;
|
||||
validateAsnNotExists: jest.Mock;
|
||||
};
|
||||
let fingerprints: { ensure: jest.Mock; hasImportedTwin: jest.Mock };
|
||||
|
||||
@@ -50,11 +59,25 @@ describe('EmailImportService – Duplikatprüfung', () => {
|
||||
Checksum: 'abc123',
|
||||
FileName: 'rechnung.pdf',
|
||||
}),
|
||||
save: jest.fn().mockImplementation((a: unknown) => Promise.resolve(a)),
|
||||
};
|
||||
taskRepo = {
|
||||
createQueryBuilder: jest.fn().mockReturnValue(taskQuery),
|
||||
create: jest.fn().mockImplementation((t: unknown) => t),
|
||||
save: jest.fn(),
|
||||
};
|
||||
emailRepo = {
|
||||
update: jest.fn().mockResolvedValue({}),
|
||||
findOne: jest.fn().mockResolvedValue(null),
|
||||
};
|
||||
dokumentVersion = {
|
||||
haengeVersionAn: jest.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
contentRepo = { findOne: jest.fn() };
|
||||
paperlessService = {
|
||||
checksumExists: jest.fn().mockResolvedValue(false),
|
||||
uploadDocument: jest.fn(),
|
||||
validateAsnNotExists: jest.fn(),
|
||||
};
|
||||
fingerprints = {
|
||||
ensure: jest.fn().mockResolvedValue(null),
|
||||
@@ -65,24 +88,20 @@ describe('EmailImportService – Duplikatprüfung', () => {
|
||||
providers: [
|
||||
EmailImportService,
|
||||
{ provide: ConfigService, useValue: { get: jest.fn() } },
|
||||
{ provide: getRepositoryToken(Email), useValue: {} },
|
||||
{ provide: getRepositoryToken(Email), useValue: emailRepo },
|
||||
{ provide: getRepositoryToken(Attachment), useValue: attachmentRepo },
|
||||
{ provide: getRepositoryToken(Content), useValue: contentRepo },
|
||||
{
|
||||
provide: getRepositoryToken(CorrespondentEmailMapping),
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: getRepositoryToken(Task),
|
||||
useValue: {
|
||||
createQueryBuilder: jest.fn().mockReturnValue(taskQuery),
|
||||
},
|
||||
},
|
||||
{ provide: getRepositoryToken(Task), useValue: taskRepo },
|
||||
{ provide: PaperlessService, useValue: paperlessService },
|
||||
{ provide: PdfService, useValue: {} },
|
||||
{ provide: EmailPageCacheService, useValue: {} },
|
||||
{ provide: ImapFolderService, useValue: {} },
|
||||
{ provide: AttachmentFingerprintService, useValue: fingerprints },
|
||||
{ provide: DokumentVersionService, useValue: dokumentVersion },
|
||||
],
|
||||
}).compile();
|
||||
|
||||
@@ -189,6 +208,48 @@ describe('EmailImportService – Duplikatprüfung', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Import als Version', () => {
|
||||
it('hängt den Beleg als Version an, statt ein neues Dokument anzulegen', async () => {
|
||||
contentRepo.findOne.mockResolvedValue({
|
||||
Content1: await pdfMitSeiten(1),
|
||||
});
|
||||
|
||||
await service.executeImport({
|
||||
emailDate: '2026-09-25',
|
||||
attachments: [
|
||||
{
|
||||
attachmentId: 7,
|
||||
type: 'MAIN',
|
||||
belegnummer: '2026-000123',
|
||||
barcode: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
nummer: '000123',
|
||||
datum: '2026-09-25',
|
||||
jahr: '2026',
|
||||
},
|
||||
versionZuDokumentId: 42,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(dokumentVersion.haengeVersionAn).toHaveBeenCalledTimes(1);
|
||||
expect(dokumentVersion.haengeVersionAn).toHaveBeenCalledWith(
|
||||
42,
|
||||
expect.stringMatching(/\.pdf$/),
|
||||
);
|
||||
expect(paperlessService.uploadDocument).not.toHaveBeenCalled();
|
||||
expect(paperlessService.validateAsnNotExists).not.toHaveBeenCalled();
|
||||
expect(taskRepo.save).not.toHaveBeenCalled();
|
||||
expect(attachmentRepo.save).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
PaperlessDocumentIds: { full: 42 },
|
||||
ImportStatus: 1,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('executeImport', () => {
|
||||
const importDaten = (splitRanges?: { start: number; end: number }[]) => ({
|
||||
emailDate: '2026-09-25',
|
||||
|
||||
@@ -21,6 +21,7 @@ import * as QRCode from 'qrcode';
|
||||
import { EmailPageCacheService } from './email-page-cache.service';
|
||||
import { ImapFolderService } from './imap-folder.service';
|
||||
import { AttachmentFingerprintService } from './attachment-fingerprint.service';
|
||||
import { DokumentVersionService } from '../paperless/dokument-version.service';
|
||||
import { PdfService } from '../preprocessing/pdf.service';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
@@ -73,6 +74,7 @@ export class EmailImportService {
|
||||
private readonly pageCache: EmailPageCacheService,
|
||||
private readonly imapFolderService: ImapFolderService,
|
||||
private readonly fingerprints: AttachmentFingerprintService,
|
||||
private readonly dokumentVersion: DokumentVersionService,
|
||||
) {}
|
||||
|
||||
async ensurePreviews(emailId: number): Promise<void> {
|
||||
@@ -568,6 +570,8 @@ export class EmailImportService {
|
||||
jahr: string;
|
||||
};
|
||||
belegnummer?: string;
|
||||
/** Stammdokument in Paperless, an das der Beleg als neue Version kommt */
|
||||
versionZuDokumentId?: number | null;
|
||||
}[];
|
||||
emailDate: string;
|
||||
}): Promise<{ success: boolean; results: any[] }> {
|
||||
@@ -634,6 +638,26 @@ export class EmailImportService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hängt jede Upload-Datei als neue Version an das Stammdokument. Kein Task,
|
||||
* keine Belegnummer-Bestätigung – Metadaten des Dokuments bleiben unverändert.
|
||||
*/
|
||||
private async importiereAlsVersion(
|
||||
documentId: number,
|
||||
uploadItems: { path: string; filename: string; rangeKey: string }[],
|
||||
paperlessIds: Record<string, number>,
|
||||
jobId?: string,
|
||||
): Promise<void> {
|
||||
for (const item of uploadItems) {
|
||||
this.setJobStatus(
|
||||
jobId,
|
||||
`Hänge ${item.filename} als neue Version an Dokument ${documentId}...`,
|
||||
);
|
||||
await this.dokumentVersion.haengeVersionAn(documentId, item.path);
|
||||
paperlessIds[item.rangeKey] = documentId;
|
||||
}
|
||||
}
|
||||
|
||||
private async runImport(data: {
|
||||
jobId?: string;
|
||||
attachments: {
|
||||
@@ -650,6 +674,8 @@ export class EmailImportService {
|
||||
jahr: string;
|
||||
};
|
||||
belegnummer?: string;
|
||||
/** Stammdokument in Paperless, an das der Beleg als neue Version kommt */
|
||||
versionZuDokumentId?: number | null;
|
||||
}[];
|
||||
emailDate: string;
|
||||
}): Promise<{ success: boolean; results: any[] }> {
|
||||
@@ -677,7 +703,7 @@ export class EmailImportService {
|
||||
const baseFilename = sanitizeFilename(
|
||||
attachmentEntity.FileName.replace(/\.pdf$/i, ''),
|
||||
);
|
||||
const paperlessIds: any = {};
|
||||
const paperlessIds: Record<string, number> = {};
|
||||
|
||||
const uploadPromises = [];
|
||||
|
||||
@@ -735,6 +761,28 @@ export class EmailImportService {
|
||||
});
|
||||
}
|
||||
|
||||
// Vom Benutzer bestätigt: als neue Version an das vorhandene Dokument
|
||||
if (att.versionZuDokumentId) {
|
||||
await this.importiereAlsVersion(
|
||||
att.versionZuDokumentId,
|
||||
uploadPromises,
|
||||
paperlessIds,
|
||||
data.jobId,
|
||||
);
|
||||
attachmentEntity.PaperlessDocumentIds = paperlessIds;
|
||||
attachmentEntity.ImportStatus = 1;
|
||||
if (att.belegnummer) {
|
||||
attachmentEntity.InterneBelegnummer = att.belegnummer;
|
||||
}
|
||||
await this.attachmentRepo.save(attachmentEntity);
|
||||
results.push({
|
||||
attachmentId: att.attachmentId,
|
||||
paperlessIds,
|
||||
alsVersion: true,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// 0. Check if ASN already exists
|
||||
if (att.belegnummer) {
|
||||
await this.paperlessService.validateAsnNotExists(att.belegnummer);
|
||||
|
||||
@@ -20,6 +20,8 @@ export interface AttachmentImportData {
|
||||
barcode?: { x: number; y: number; nummer: string; datum: string; jahr: string };
|
||||
belegnummer?: string;
|
||||
isDuplicate?: boolean;
|
||||
/** Stammdokument in Paperless, an das der Beleg als neue Version kommt */
|
||||
versionZuDokumentId?: number | null;
|
||||
}
|
||||
|
||||
export const emailImportApi = {
|
||||
|
||||
Reference in new issue
Block a user