feat: Duplikate im Import als neue Version behandeln #6
No files matched your search
@@ -166,8 +166,11 @@ Geprüft wird immer gegen Paperless, und zwar über **beide** Merkmale, die aus
|
||||
der internen Belegnummer folgen:
|
||||
|
||||
- CF7 = interne Belegnummer (z. B. `2026-000123`)
|
||||
- ASN = interne Belegnummer ohne Bindestrich (z. B. `2026000123`) – auch im
|
||||
Posteingang, unabhängig von einem im Template separat hinterlegten ASN-Feld.
|
||||
- ASN = die ASN, die das Dokument beim Upload **tatsächlich bekäme**: im
|
||||
Posteingang aus dem ASN-Feld der Aktion, falls gesetzt, sonst aus der
|
||||
Belegnummer ohne Bindestrich (z. B. `2026000123`); im E-Mail-Import immer aus
|
||||
der Belegnummer. Prüfung, Versions-Upload und normaler Upload verwenden
|
||||
dieselbe ASN.
|
||||
|
||||
„Als neue Version hinzufügen“ wird nur angeboten, wenn alle Treffer auf **genau
|
||||
ein** Dokument zeigen. Zeigen CF7- und ASN-Treffer auf verschiedene Dokumente
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import { InboxPostprocessorService } from './inbox-postprocessor.service';
|
||||
import { originalSeitenDesAbschnitts } from './paperless-entscheidung.util';
|
||||
import {
|
||||
ermittleUploadAsn,
|
||||
originalSeitenDesAbschnitts,
|
||||
} from './paperless-entscheidung.util';
|
||||
|
||||
describe('InboxPostprocessorService.runForDocument', () => {
|
||||
it('nimmt eine Duplikat-Entscheidung nur für genau einen Abschnitt an', async () => {
|
||||
@@ -25,3 +28,18 @@ describe('originalSeitenDesAbschnitts', () => {
|
||||
expect(originalSeitenDesAbschnitts([1, 3, 4, 5], [1, 2])).toEqual([3, 4]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ermittleUploadAsn', () => {
|
||||
it('nimmt die ASN aus dem Template-Feld, wenn gesetzt', () => {
|
||||
expect(ermittleUploadAsn('ASN 555', '2026-000123')).toBe(555);
|
||||
});
|
||||
|
||||
it('bildet sie sonst aus der Belegnummer', () => {
|
||||
expect(ermittleUploadAsn('', '2026-000123')).toBe(2026000123);
|
||||
expect(ermittleUploadAsn(null, '2026-000123')).toBe(2026000123);
|
||||
});
|
||||
|
||||
it('liefert undefined, wenn keine Ziffern vorhanden sind', () => {
|
||||
expect(ermittleUploadAsn(null, 'abc')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -26,6 +26,7 @@ import { applyTemplate, buildVariables } from './variable-resolver';
|
||||
import { DokumentVersionService } from '../paperless/dokument-version.service';
|
||||
import {
|
||||
aktionenAbEntscheidung,
|
||||
ermittleUploadAsn,
|
||||
originalSeitenDesAbschnitts,
|
||||
type PaperlessEntscheidung,
|
||||
} from './paperless-entscheidung.util';
|
||||
@@ -471,12 +472,18 @@ export class InboxPostprocessorService {
|
||||
if (!interneBelegnummer)
|
||||
throw new Error('Interne Belegnummer konnte nicht aufgelöst werden');
|
||||
|
||||
// 2. Vom Benutzer bestätigt: als neue Version an das Stammdokument hängen
|
||||
// 2. ASN, die das Dokument beim Upload bekäme – gegen genau diese wird geprüft
|
||||
const asnTpl = String(content.asn ?? '').trim();
|
||||
const asn = asnTpl ? applyTemplate(asnTpl, variables).trim() : null;
|
||||
const archiveSerialNumber = ermittleUploadAsn(asn, interneBelegnummer);
|
||||
|
||||
// 3. Vom Benutzer bestätigt: als neue Version an das Stammdokument hängen
|
||||
if (entscheidung?.aktion === 'version') {
|
||||
await this.dokumentVersion.haengeVersionAn(
|
||||
entscheidung.documentId,
|
||||
pdfPath,
|
||||
interneBelegnummer,
|
||||
archiveSerialNumber ?? null,
|
||||
);
|
||||
this.logger.log(
|
||||
`Belegnummer ${interneBelegnummer} als neue Version zu Dokument ${entscheidung.documentId} hinzugefügt`,
|
||||
@@ -486,22 +493,11 @@ export class InboxPostprocessorService {
|
||||
};
|
||||
}
|
||||
|
||||
// 3. ASN bestimmen (für den Upload)
|
||||
const asnTpl = String(content.asn ?? '').trim();
|
||||
const asn = asnTpl ? applyTemplate(asnTpl, variables).trim() : null;
|
||||
let archiveSerialNumber: number | undefined;
|
||||
if (asn) {
|
||||
const n = parseInt(asn.replace(/[^0-9]/g, ''), 10);
|
||||
if (!Number.isNaN(n)) archiveSerialNumber = n;
|
||||
}
|
||||
if (archiveSerialNumber === undefined) {
|
||||
const n = parseInt(interneBelegnummer.replace(/[^0-9]/g, ''), 10);
|
||||
if (!Number.isNaN(n)) archiveSerialNumber = n;
|
||||
}
|
||||
|
||||
// 4. Duplikat-Check Paperless: CF7 + aus der Belegnummer abgeleitete ASN
|
||||
const pruefung =
|
||||
await this.paperlessService.pruefeBelegnummer(interneBelegnummer);
|
||||
// 4. Duplikat-Check Paperless: CF7 + Upload-ASN
|
||||
const pruefung = await this.paperlessService.pruefeBelegnummer(
|
||||
interneBelegnummer,
|
||||
archiveSerialNumber ?? null,
|
||||
);
|
||||
if (pruefung.status !== 'frei') {
|
||||
this.logger.warn(
|
||||
`Duplikat (Paperless): Belegnummer ${interneBelegnummer} bereits vorhanden (Dokument(e) ${pruefung.kandidaten.join(', ')})`,
|
||||
|
||||
@@ -27,3 +27,19 @@ export function originalSeitenDesAbschnitts(
|
||||
): number[] {
|
||||
return pageIndices.map((idx) => survivingOriginalPages[idx]);
|
||||
}
|
||||
|
||||
/**
|
||||
* ASN, die das Dokument beim Upload bekommt: aus dem ASN-Feld der Aktion
|
||||
* (bereits aufgelöst), sonst aus der internen Belegnummer – jeweils nur die Ziffern.
|
||||
*/
|
||||
export function ermittleUploadAsn(
|
||||
asnAusTemplate: string | null,
|
||||
interneBelegnummer: string,
|
||||
): number | undefined {
|
||||
for (const quelle of [asnAusTemplate, interneBelegnummer]) {
|
||||
if (!quelle) continue;
|
||||
const n = parseInt(quelle.replace(/[^0-9]/g, ''), 10);
|
||||
if (!Number.isNaN(n)) return n;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -56,6 +56,17 @@ describe('DokumentVersionService.haengeVersionAn', () => {
|
||||
expect(paperless.uploadDocumentVersion).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('prüft die Upload-ASN, wenn sie übergeben wird', async () => {
|
||||
paperless.getDocument.mockResolvedValue({ id: 42, tags: [] });
|
||||
|
||||
await service.haengeVersionAn(42, '/tmp/x.pdf', '2026-000123', 555);
|
||||
|
||||
expect(paperless.pruefeBelegnummer).toHaveBeenCalledWith(
|
||||
'2026-000123',
|
||||
555,
|
||||
);
|
||||
});
|
||||
|
||||
it('wartet auf die Paperless-Verarbeitung der Version', async () => {
|
||||
paperless.getDocument.mockResolvedValue({ id: 42, tags: [] });
|
||||
paperless.getTask
|
||||
|
||||
@@ -32,8 +32,12 @@ export class DokumentVersionService {
|
||||
rootDocumentId: number,
|
||||
pdfPath: string,
|
||||
belegnummer: string,
|
||||
uploadAsn?: number | null,
|
||||
): Promise<void> {
|
||||
const pruefung = await this.paperlessService.pruefeBelegnummer(belegnummer);
|
||||
const pruefung = await this.paperlessService.pruefeBelegnummer(
|
||||
belegnummer,
|
||||
uploadAsn,
|
||||
);
|
||||
if (
|
||||
pruefung.status !== 'eindeutig' ||
|
||||
pruefung.documentId !== rootDocumentId
|
||||
|
||||
@@ -85,4 +85,17 @@ describe('PaperlessService – Belegnummer-Suche', () => {
|
||||
kandidaten: [42],
|
||||
});
|
||||
});
|
||||
|
||||
it('prüft die übergebene Upload-ASN statt der aus der Belegnummer gebildeten', async () => {
|
||||
get.mockResolvedValue({ data: { results: [] } });
|
||||
|
||||
await service.pruefeBelegnummer('2026-000123', 555);
|
||||
|
||||
const asnWerte = (
|
||||
get.mock.calls as [string, { params: Record<string, unknown> }][]
|
||||
)
|
||||
.map(([, cfg]) => cfg.params.archive_serial_number)
|
||||
.filter((wert) => wert !== undefined);
|
||||
expect(asnWerte).toEqual([555]);
|
||||
});
|
||||
});
|
||||
@@ -500,12 +500,16 @@ export class PaperlessService {
|
||||
|
||||
/**
|
||||
* Prüft, ob die interne Belegnummer in Paperless schon vergeben ist – über
|
||||
* CF7 und die daraus abgeleitete ASN.
|
||||
* CF7 und die ASN, die das Dokument beim Upload bekäme (`uploadAsn`; ohne
|
||||
* Angabe die aus der Belegnummer gebildete).
|
||||
*/
|
||||
async pruefeBelegnummer(belegnummer: string): Promise<BelegnummerPruefung> {
|
||||
async pruefeBelegnummer(
|
||||
belegnummer: string,
|
||||
uploadAsn?: number | null,
|
||||
): Promise<BelegnummerPruefung> {
|
||||
const nummer = (belegnummer ?? '').trim();
|
||||
if (!nummer) return klassifiziereTreffer([], []);
|
||||
const asn = belegnummerZuAsn(nummer);
|
||||
const asn = uploadAsn ?? belegnummerZuAsn(nummer);
|
||||
const [cf7, asnTreffer] = await Promise.all([
|
||||
this.findRootDocumentIdsByCustomField(7, nummer),
|
||||
asn !== null
|
||||
|
||||
Reference in new issue
Block a user