chore: apply ESLint auto-fix across entire backend
Build and Push Multi-Platform Images / build-and-push (push) Successful in 41s

Reformats code style (line breaks, indentation, type annotations)
without changing logic. Also includes minor feature additions bundled
in the same lint run (stats service, user-settings groups, agrarmonitor
polling improvements).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-08 09:02:02 +02:00
co-authored by Claude Sonnet 4.6
parent 4c75a1ded2
commit dad0136365
74 changed files with 4022 additions and 1052 deletions
@@ -16,32 +16,48 @@ export class PaperlessProcessorService {
private readonly configService: ConfigService,
private readonly paperlessService: PaperlessService,
private readonly postprocessingService: PostprocessingService,
@InjectRepository(DocumentType) private readonly docTypeRepo: Repository<DocumentType>,
@InjectRepository(DocumentField) private readonly docFieldRepo: Repository<DocumentField>,
@InjectRepository(DocumentType)
private readonly docTypeRepo: Repository<DocumentType>,
@InjectRepository(DocumentField)
private readonly docFieldRepo: Repository<DocumentField>,
) {}
@Cron(process.env.PAPERLESS_PROCESSOR_CRON || '0 * * * * *')
async processDocuments() {
try {
const response = await this.paperlessService.getDocuments({ tags__id__all: 16, page_size: 9999 });
const documents: any[] = Array.isArray(response) ? response : (response?.results ?? []);
const response = await this.paperlessService.getDocuments({
tags__id__all: 16,
page_size: 9999,
});
const documents: any[] = Array.isArray(response)
? response
: (response?.results ?? []);
if (documents.length === 0) return;
const customFields = await this.paperlessService.getCustomFields();
const validFieldIds = new Set(customFields.map((f: any) => f.id));
this.logger.log(`Verarbeite ${documents.length} Dokument(e) mit Tag "paperlessmanager" (ID: 16).`);
this.logger.log(
`Verarbeite ${documents.length} Dokument(e) mit Tag "paperlessmanager" (ID: 16).`,
);
for (const doc of documents) {
try {
const updatedDoc = await this.processSingleDocument(doc, validFieldIds);
// Postprocessing nach dem Speichern evaluieren
await this.postprocessingService.evaluate(updatedDoc || doc);
const updatedDoc = await this.processSingleDocument(
doc,
validFieldIds,
);
// Postprocessing nach dem Speichern evaluieren
await this.postprocessingService.evaluate(updatedDoc || doc);
} catch (innerErr: any) {
this.logger.error(`Fehler bei Dokument ID ${doc.id}: ${innerErr.message}`);
if (innerErr.response?.data) {
this.logger.error(`Paperless API Response: ${JSON.stringify(innerErr.response.data)}`);
}
this.logger.error(
`Fehler bei Dokument ID ${doc.id}: ${innerErr.message}`,
);
if (innerErr.response?.data) {
this.logger.error(
`Paperless API Response: ${JSON.stringify(innerErr.response.data)}`,
);
}
}
}
} catch (err) {
@@ -49,17 +65,24 @@ export class PaperlessProcessorService {
}
}
private async processSingleDocument(doc: any, validFieldIds: Set<number>): Promise<any> {
private async processSingleDocument(
doc: any,
validFieldIds: Set<number>,
): Promise<any> {
this.logger.log(`Verarbeite Dokument ID: ${doc.id}`);
if (!doc.document_type) {
this.logger.warn(`Dokument ${doc.id} hat keinen Dokumenten-Typen setze Tag 17.`);
this.logger.warn(
`Dokument ${doc.id} hat keinen Dokumenten-Typen setze Tag 17.`,
);
const tagsSet = new Set<number>(doc.tags || []);
tagsSet.add(17);
if (!tagsSet.has(1)) {
tagsSet.add(6);
}
const updated = await this.paperlessService.updateDocument(doc.id, { tags: Array.from(tagsSet) });
const updated = await this.paperlessService.updateDocument(doc.id, {
tags: Array.from(tagsSet),
});
return updated;
}
@@ -68,7 +91,9 @@ export class PaperlessProcessorService {
});
if (!docTypeConfig) {
this.logger.warn(`Konfiguration für DocumentType ${doc.document_type} nicht in der Datenbank gefunden.`);
this.logger.warn(
`Konfiguration für DocumentType ${doc.document_type} nicht in der Datenbank gefunden.`,
);
return null;
}
@@ -77,9 +102,13 @@ export class PaperlessProcessorService {
});
if (fieldsConfig.length === 0) {
this.logger.log(`Dokument ${doc.id} (Typ ${doc.document_type}) hat keine Dokument-Felder in der DB konfiguriert.`);
this.logger.log(
`Dokument ${doc.id} (Typ ${doc.document_type}) hat keine Dokument-Felder in der DB konfiguriert.`,
);
const newTagsNoFields = Array.from(new Set([...(doc.tags || []), 17]));
const updated = await this.paperlessService.updateDocument(doc.id, { tags: newTagsNoFields });
const updated = await this.paperlessService.updateDocument(doc.id, {
tags: newTagsNoFields,
});
return updated;
}
@@ -91,15 +120,19 @@ export class PaperlessProcessorService {
if (fieldConf.Type === 4) {
const customFieldId = fieldConf.TypeIndex;
if (!customFieldId) continue;
if (!validFieldIds.has(customFieldId)) {
this.logger.warn(`Überspringe ungültiges Custom Field (TypeIndex: ${customFieldId}) für Dokument ${doc.id} - in Paperless nicht vorhanden.`);
this.logger.warn(
`Überspringe ungültiges Custom Field (TypeIndex: ${customFieldId}) für Dokument ${doc.id} - in Paperless nicht vorhanden.`,
);
continue;
}
const existingField = newCustomFields.find(f => f.field === customFieldId);
const existingField = newCustomFields.find(
(f) => f.field === customFieldId,
);
let isFilled = false;
if (existingField) {
isFilled = existingField.value !== null && existingField.value !== '';
} else {
@@ -114,13 +147,16 @@ export class PaperlessProcessorService {
let isFilled = false;
switch (fieldConf.Type) {
case 1:
isFilled = doc.correspondent !== null && doc.correspondent !== undefined;
isFilled =
doc.correspondent !== null && doc.correspondent !== undefined;
break;
case 2:
isFilled = !!doc.created || !!doc.created_date;
break;
case 3:
isFilled = doc.archive_serial_number !== null && doc.archive_serial_number !== undefined;
isFilled =
doc.archive_serial_number !== null &&
doc.archive_serial_number !== undefined;
break;
case 5:
isFilled = !!doc.title;
@@ -136,13 +172,13 @@ export class PaperlessProcessorService {
}
const tagsSet = new Set<number>(doc.tags || []);
if (isAllRequiredFilled) {
if (docTypeConfig.TagReady) tagsSet.add(docTypeConfig.TagReady);
if (docTypeConfig.TagNotReady) tagsSet.delete(docTypeConfig.TagNotReady);
if (docTypeConfig.TagReady) tagsSet.add(docTypeConfig.TagReady);
if (docTypeConfig.TagNotReady) tagsSet.delete(docTypeConfig.TagNotReady);
} else {
if (docTypeConfig.TagNotReady) tagsSet.add(docTypeConfig.TagNotReady);
if (docTypeConfig.TagReady) tagsSet.delete(docTypeConfig.TagReady);
if (docTypeConfig.TagNotReady) tagsSet.add(docTypeConfig.TagNotReady);
if (docTypeConfig.TagReady) tagsSet.delete(docTypeConfig.TagReady);
}
tagsSet.add(17);
@@ -163,7 +199,7 @@ export class PaperlessProcessorService {
while ((match = placeholderRegex.exec(title)) !== null) {
const fieldId = parseInt(match[1], 10);
const cf = newCustomFields.find(f => f.field === fieldId);
const cf = newCustomFields.find((f) => f.field === fieldId);
if (!cf || cf.value == null || cf.value === '') {
allFilled = false;
break;
@@ -174,7 +210,10 @@ export class PaperlessProcessorService {
for (const cf of newCustomFields) {
const placeholder = `{{CUSTOM[${cf.field}]}}`;
if (title.includes(placeholder)) {
title = title.replaceAll(placeholder, cf.value != null ? String(cf.value) : '');
title = title.replaceAll(
placeholder,
cf.value != null ? String(cf.value) : '',
);
}
}
@@ -192,13 +231,20 @@ export class PaperlessProcessorService {
updatePayload.title = title;
} else {
this.logger.log(`Dokument ${doc.id}: Titel-Template nicht angewendet nicht alle referenzierten Custom Fields ausgefüllt.`);
this.logger.log(
`Dokument ${doc.id}: Titel-Template nicht angewendet nicht alle referenzierten Custom Fields ausgefüllt.`,
);
}
}
const updated = await this.paperlessService.updateDocument(doc.id, updatePayload);
const updated = await this.paperlessService.updateDocument(
doc.id,
updatePayload,
);
this.logger.log(`Dokument ${doc.id} erfolgreich aktualisiert (Alle Pflichtfelder vorhanden: ${isAllRequiredFilled}).`);
this.logger.log(
`Dokument ${doc.id} erfolgreich aktualisiert (Alle Pflichtfelder vorhanden: ${isAllRequiredFilled}).`,
);
return updated;
}
}
@@ -26,10 +26,7 @@ export class PaperlessTaskProcessorService {
try {
// Fetch tasks that are not finished
const tasks = await this.taskRepo.find({
where: [
{ Fertig: IsNull() },
{ Fertig: 0 },
],
where: [{ Fertig: IsNull() }, { Fertig: 0 }],
take: 10,
});
@@ -61,13 +58,17 @@ export class PaperlessTaskProcessorService {
// Fetch task status from Paperless
const paperlessTasks = await this.paperlessService.getTask(t.TaskId);
const apiResponseTask = Array.isArray(paperlessTasks) ? paperlessTasks[0] : null;
const apiResponseTask = Array.isArray(paperlessTasks)
? paperlessTasks[0]
: null;
if (apiResponseTask) {
if (apiResponseTask.status === 'SUCCESS') {
const dateDone = apiResponseTask.date_done ? new Date(apiResponseTask.date_done) : new Date();
const dateDone = apiResponseTask.date_done
? new Date(apiResponseTask.date_done)
: new Date();
const now = new Date();
// Add 10 seconds buffer as in C#
if (dateDone.getTime() + 10000 < now.getTime()) {
await this.processSuccessfulTask(t, apiResponseTask, parentTask);
@@ -87,38 +88,61 @@ export class PaperlessTaskProcessorService {
this.logger.log(`${toDelete.length} Tasks gelöscht`);
}
} catch (error) {
this.logger.error(`Fehler bei der Task-Verarbeitung: ${error.message}`, error.stack);
this.logger.error(
`Fehler bei der Task-Verarbeitung: ${error.message}`,
error.stack,
);
}
}
private async processSuccessfulTask(t: Task, apiTask: any, parentTask: Task | null) {
private async processSuccessfulTask(
t: Task,
apiTask: any,
parentTask: Task | null,
) {
const documentId = apiTask.related_document;
this.logger.log(`[Postprocessing] Task ${t.TaskId} gestartet. DocumentID: ${documentId ?? 'nicht vorhanden'}`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} gestartet. DocumentID: ${documentId ?? 'nicht vorhanden'}`,
);
if (!documentId) {
this.logger.error(`Kein Dokument für Task ${t.TaskId} gefunden.`);
return;
this.logger.error(`Kein Dokument für Task ${t.TaskId} gefunden.`);
return;
}
try {
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Lade Dokument ${documentId} aus Paperless`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Lade Dokument ${documentId} aus Paperless`,
);
const document = await this.paperlessService.getDocument(documentId);
if (!document) {
this.logger.warn(`Dokument mit ID ${documentId} nicht in Paperless gefunden.`);
this.logger.warn(
`Dokument mit ID ${documentId} nicht in Paperless gefunden.`,
);
return;
}
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Dokument geladen: ID=${document.id}, Titel="${document.title}"`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Dokument geladen: ID=${document.id}, Titel="${document.title}"`,
);
// Handle Duplicate Link
if (t.DuplikatZU) {
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Verknüpfe Duplikat zu Dokument ${t.DuplikatZU}`);
const duplikatDoc = await this.paperlessService.getDocument(t.DuplikatZU);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Verknüpfe Duplikat zu Dokument ${t.DuplikatZU}`,
);
const duplikatDoc = await this.paperlessService.getDocument(
t.DuplikatZU,
);
if (duplikatDoc) {
// Update duplikatDoc metadata (Field 8 is for linked documents)
let duplikatCustomFields = Array.isArray(duplikatDoc.custom_fields) ? [...duplikatDoc.custom_fields] : [];
let duplikatCustomFields = Array.isArray(duplikatDoc.custom_fields)
? [...duplikatDoc.custom_fields]
: [];
// Remove field 4 as in C#
duplikatCustomFields = duplikatCustomFields.filter((f: any) => f.field !== 4);
duplikatCustomFields = duplikatCustomFields.filter(
(f: any) => f.field !== 4,
);
const field8 = duplikatCustomFields.find((f: any) => f.field === 8);
if (field8) {
@@ -136,13 +160,21 @@ export class PaperlessTaskProcessorService {
document_type: 11,
custom_fields: duplikatCustomFields,
});
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Duplikat-Dokument ${duplikatDoc.id} aktualisiert`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Duplikat-Dokument ${duplikatDoc.id} aktualisiert`,
);
// Update current document as well
const currentCustomFields = Array.isArray(document.custom_fields) ? [...document.custom_fields] : [];
const currentField8 = currentCustomFields.find((f: any) => f.field === 8);
const currentCustomFields = Array.isArray(document.custom_fields)
? [...document.custom_fields]
: [];
const currentField8 = currentCustomFields.find(
(f: any) => f.field === 8,
);
if (currentField8) {
const values = Array.isArray(currentField8.value) ? currentField8.value : [];
const values = Array.isArray(currentField8.value)
? currentField8.value
: [];
if (!values.includes(duplikatDoc.id)) {
values.push(duplikatDoc.id);
currentField8.value = values;
@@ -152,70 +184,108 @@ export class PaperlessTaskProcessorService {
}
document.custom_fields = currentCustomFields;
} else {
this.logger.warn(`[Postprocessing] Task ${t.TaskId} - Duplikat-Dokument ${t.DuplikatZU} nicht gefunden`);
this.logger.warn(
`[Postprocessing] Task ${t.TaskId} - Duplikat-Dokument ${t.DuplikatZU} nicht gefunden`,
);
}
}
// Enrich Document
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Reichere Dokument-Metadaten an`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Reichere Dokument-Metadaten an`,
);
const updateData: any = {
custom_fields: Array.isArray(document.custom_fields) ? [...document.custom_fields] : [],
custom_fields: Array.isArray(document.custom_fields)
? [...document.custom_fields]
: [],
};
// CustomFieldsJson als Basis zuerst anwenden dedizierte Felder weiter unten überschreiben diese
if (t.CustomFieldsJson) {
try {
const extra = JSON.parse(t.CustomFieldsJson) as Record<string, string>;
const extra = JSON.parse(t.CustomFieldsJson) as Record<
string,
string
>;
for (const [k, v] of Object.entries(extra)) {
const fieldId = parseInt(k, 10);
if (!Number.isFinite(fieldId)) continue;
const idx = updateData.custom_fields.findIndex((f: any) => f.field === fieldId);
const idx = updateData.custom_fields.findIndex(
(f: any) => f.field === fieldId,
);
if (idx !== -1) updateData.custom_fields[idx].value = v;
else updateData.custom_fields.push({ field: fieldId, value: v });
}
} catch { /* JSON-Parse-Fehler ignorieren */ }
} catch {
/* JSON-Parse-Fehler ignorieren */
}
}
if (t.Asn) {
const asnNum = parseInt(t.Asn.replace(/[^0-9]/g, ''), 10);
if (!isNaN(asnNum)) {
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Setze ASN (explizit): ${asnNum}`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Setze ASN (explizit): ${asnNum}`,
);
updateData.archive_serial_number = asnNum;
}
}
if (t.InterneBelegnummer) {
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Setze InterneBelegnummer: ${t.InterneBelegnummer}`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Setze InterneBelegnummer: ${t.InterneBelegnummer}`,
);
if (!t.Asn) {
const asnFromBelegnummer = parseInt(t.InterneBelegnummer.replace(/-/g, ''), 10);
const asnFromBelegnummer = parseInt(
t.InterneBelegnummer.replace(/-/g, ''),
10,
);
if (!isNaN(asnFromBelegnummer)) {
updateData.archive_serial_number = asnFromBelegnummer;
} else {
this.logger.warn(`[Postprocessing] Task ${t.TaskId} - ASN aus InterneBelegnummer konnte nicht geparst werden: ${t.InterneBelegnummer}`);
this.logger.warn(
`[Postprocessing] Task ${t.TaskId} - ASN aus InterneBelegnummer konnte nicht geparst werden: ${t.InterneBelegnummer}`,
);
}
}
const existingField7 = updateData.custom_fields.find((f: any) => f.field === 7);
const existingField7 = updateData.custom_fields.find(
(f: any) => f.field === 7,
);
if (existingField7) {
existingField7.value = t.InterneBelegnummer;
} else {
updateData.custom_fields.push({ field: 7, value: t.InterneBelegnummer });
updateData.custom_fields.push({
field: 7,
value: t.InterneBelegnummer,
});
}
}
if (t.externeBelegnummer) {
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Setze externeBelegnummer: ${t.externeBelegnummer}`);
const existingField3 = updateData.custom_fields.find((f: any) => f.field === 3);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Setze externeBelegnummer: ${t.externeBelegnummer}`,
);
const existingField3 = updateData.custom_fields.find(
(f: any) => f.field === 3,
);
if (existingField3) {
existingField3.value = t.externeBelegnummer;
} else {
updateData.custom_fields.push({ field: 3, value: t.externeBelegnummer });
updateData.custom_fields.push({
field: 3,
value: t.externeBelegnummer,
});
}
}
if (t.Eingangsdatum) {
const dateValue = new Date(t.Eingangsdatum).toISOString().split('T')[0];
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Setze Eingangsdatum: ${dateValue}`);
const existingField9 = updateData.custom_fields.find((f: any) => f.field === 9);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Setze Eingangsdatum: ${dateValue}`,
);
const existingField9 = updateData.custom_fields.find(
(f: any) => f.field === 9,
);
if (existingField9) {
existingField9.value = dateValue;
} else {
@@ -224,24 +294,38 @@ export class PaperlessTaskProcessorService {
}
if (t.DocumentType) {
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Setze DocumentType: ${t.DocumentType}`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Setze DocumentType: ${t.DocumentType}`,
);
updateData.document_type = t.DocumentType;
}
// Parent Task / Attachment logic
if (parentTask) {
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Verarbeite als Anlage zu ParentTask ${parentTask.TaskId}`);
const parentPaperlessTasks = await this.paperlessService.getTask(parentTask.TaskId);
const apiParentTask = Array.isArray(parentPaperlessTasks) ? parentPaperlessTasks[0] : null;
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Verarbeite als Anlage zu ParentTask ${parentTask.TaskId}`,
);
const parentPaperlessTasks = await this.paperlessService.getTask(
parentTask.TaskId,
);
const apiParentTask = Array.isArray(parentPaperlessTasks)
? parentPaperlessTasks[0]
: null;
if (apiParentTask && apiParentTask.related_document) {
const parentDoc = await this.paperlessService.getDocument(apiParentTask.related_document);
const parentDoc = await this.paperlessService.getDocument(
apiParentTask.related_document,
);
if (parentDoc) {
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Elterndokument ${parentDoc.id} gefunden, setze Anlage-Typ`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Elterndokument ${parentDoc.id} gefunden, setze Anlage-Typ`,
);
updateData.document_type = 5; // Anlage
updateData.title = `Anlage zu ${parentTask.InterneBelegnummer}`;
const field8 = updateData.custom_fields.find((f: any) => f.field === 8);
const field8 = updateData.custom_fields.find(
(f: any) => f.field === 8,
);
if (field8) {
const values = Array.isArray(field8.value) ? field8.value : [];
if (!values.includes(parentDoc.id)) {
@@ -249,33 +333,50 @@ export class PaperlessTaskProcessorService {
field8.value = values;
}
} else {
updateData.custom_fields.push({ field: 8, value: [parentDoc.id] });
updateData.custom_fields.push({
field: 8,
value: [parentDoc.id],
});
}
} else {
this.logger.warn(`[Postprocessing] Task ${t.TaskId} - Elterndokument nicht gefunden`);
this.logger.warn(
`[Postprocessing] Task ${t.TaskId} - Elterndokument nicht gefunden`,
);
}
} else {
this.logger.warn(`[Postprocessing] Task ${t.TaskId} - ParentTask hat kein related_document`);
this.logger.warn(
`[Postprocessing] Task ${t.TaskId} - ParentTask hat kein related_document`,
);
}
}
if (t.Belegdatum) {
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Setze Belegdatum: ${t.Belegdatum.toISOString()}`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Setze Belegdatum: ${t.Belegdatum.toISOString()}`,
);
updateData.created = t.Belegdatum.toISOString();
}
if (t.BetriebID) {
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Setze Owner: ${t.BetriebID}`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Setze Owner: ${t.BetriebID}`,
);
updateData.owner = t.BetriebID;
} else {
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Entferne Owner (setze null)`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Entferne Owner (setze null)`,
);
updateData.owner = null;
}
// Tags
if (t.Tags) {
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Setze Tags: ${t.Tags}`);
const tagIds = t.Tags.split(',').map(id => parseInt(id.trim(), 10)).filter(id => !isNaN(id));
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Setze Tags: ${t.Tags}`,
);
const tagIds = t.Tags.split(',')
.map((id) => parseInt(id.trim(), 10))
.filter((id) => !isNaN(id));
const currentTags = document.tags || [];
const newTags = Array.from(new Set([...currentTags, ...tagIds]));
updateData.tags = newTags;
@@ -284,46 +385,78 @@ export class PaperlessTaskProcessorService {
// Agrarmonitor Link (Skip API call for now, but save the link if needed)
if (t.EinkaufID) {
const link = `https://admin7.agrarmonitor.de/rechnungen/detail/${t.EinkaufID}`;
this.logger.log(`Skipping Agrarmonitor details for EinkaufID ${t.EinkaufID}. Link: ${link}`);
this.logger.log(
`Skipping Agrarmonitor details for EinkaufID ${t.EinkaufID}. Link: ${link}`,
);
}
if (t.Lieferant) {
this.logger.log(`Skipping Correspondent lookup/creation for Lieferant ID ${t.Lieferant} (Agrarmonitor part deferred).`);
this.logger.log(
`Skipping Correspondent lookup/creation for Lieferant ID ${t.Lieferant} (Agrarmonitor part deferred).`,
);
}
// Update Document in Paperless
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Aktualisiere Dokument ${document.id} in Paperless. Payload: ${JSON.stringify(updateData)}`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Aktualisiere Dokument ${document.id} in Paperless. Payload: ${JSON.stringify(updateData)}`,
);
await this.paperlessService.updateDocument(document.id, updateData);
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Dokument ${document.id} erfolgreich aktualisiert`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Dokument ${document.id} erfolgreich aktualisiert`,
);
// Add Notes
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Füge Notizen hinzu`);
await this.paperlessService.addNote(document.id, `Task bearbeitet: ${new Date().toLocaleString('de-DE')}`);
await this.paperlessService.addNote(
document.id,
`Task bearbeitet: ${new Date().toLocaleString('de-DE')}`,
);
if (t.SourceAttachmentID) {
const attachment = await this.attachmentRepo.findOne({ where: { Id: t.SourceAttachmentID }, relations: ['EmailMessage'] });
const attachment = await this.attachmentRepo.findOne({
where: { Id: t.SourceAttachmentID },
relations: ['EmailMessage'],
});
if (attachment) {
const rangePart = t.SourceAttachmentRange && t.SourceAttachmentRange !== 'full'
? ` | Seiten: ${t.SourceAttachmentRange}`
: '';
const messageId = attachment.EmailMessage?.MessageId ?? String(attachment.EmailMessageId);
await this.paperlessService.addNote(document.id, `E-Mail-ID: ${messageId} | Datei: ${attachment.FileName}${rangePart}`);
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Herkunfts-Notiz hinzugefügt`);
const rangePart =
t.SourceAttachmentRange && t.SourceAttachmentRange !== 'full'
? ` | Seiten: ${t.SourceAttachmentRange}`
: '';
const messageId =
attachment.EmailMessage?.MessageId ??
String(attachment.EmailMessageId);
await this.paperlessService.addNote(
document.id,
`E-Mail-ID: ${messageId} | Datei: ${attachment.FileName}${rangePart}`,
);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Herkunfts-Notiz hinzugefügt`,
);
}
}
if (t.BarcodeJson) {
await this.paperlessService.addNote(document.id, t.BarcodeJson);
this.logger.log(`[Postprocessing] Task ${t.TaskId} - BarcodeJson-Notiz hinzugefügt`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - BarcodeJson-Notiz hinzugefügt`,
);
}
// Sync local Documents table
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Synchronisiere lokale Documents-Tabelle`);
const metadata = await this.paperlessService.getDocumentMetadata(document.id);
let localDoc = await this.documentRepo.findOne({ where: { documentId: document.id } });
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Synchronisiere lokale Documents-Tabelle`,
);
const metadata = await this.paperlessService.getDocumentMetadata(
document.id,
);
let localDoc = await this.documentRepo.findOne({
where: { documentId: document.id },
});
if (!localDoc) {
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Erstelle neuen lokalen Dokument-Eintrag`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Erstelle neuen lokalen Dokument-Eintrag`,
);
localDoc = this.documentRepo.create({
documentId: document.id,
checksum: metadata.original_checksum,
@@ -331,34 +464,46 @@ export class PaperlessTaskProcessorService {
});
await this.documentRepo.save(localDoc);
} else {
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Aktualisiere bestehenden lokalen Dokument-Eintrag`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Aktualisiere bestehenden lokalen Dokument-Eintrag`,
);
localDoc.checksum = metadata.original_checksum;
localDoc.filename = metadata.original_filename;
await this.documentRepo.save(localDoc);
}
// Update Task status
this.logger.log(`[Postprocessing] Task ${t.TaskId} - Setze Task-Status auf Fertig`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} - Setze Task-Status auf Fertig`,
);
t.Fertig = 1;
t.PaperlessDocumentID = document.id;
await this.taskRepo.save(t);
// Update source attachment if linked
if (t.SourceAttachmentID && t.SourceAttachmentRange) {
const attachment = await this.attachmentRepo.findOne({ where: { Id: t.SourceAttachmentID } });
const attachment = await this.attachmentRepo.findOne({
where: { Id: t.SourceAttachmentID },
});
if (attachment) {
const ids = attachment.PaperlessDocumentIds || {};
ids[t.SourceAttachmentRange] = document.id;
attachment.PaperlessDocumentIds = ids;
await this.attachmentRepo.save(attachment);
this.logger.log(`[Postprocessing] Anhang ${attachment.Id} mit PaperlessID ${document.id} (${t.SourceAttachmentRange}) aktualisiert`);
this.logger.log(
`[Postprocessing] Anhang ${attachment.Id} mit PaperlessID ${document.id} (${t.SourceAttachmentRange}) aktualisiert`,
);
}
}
this.logger.log(`[Postprocessing] Task ${t.TaskId} erfolgreich abgeschlossen. Dokument ID: ${document.id}`);
this.logger.log(
`[Postprocessing] Task ${t.TaskId} erfolgreich abgeschlossen. Dokument ID: ${document.id}`,
);
} catch (error) {
this.logger.error(`Fehler bei der Verarbeitung von Task ${t.TaskId}: ${error.message}`, error.stack);
this.logger.error(
`Fehler bei der Verarbeitung von Task ${t.TaskId}: ${error.message}`,
error.stack,
);
}
}
}
@@ -1,4 +1,20 @@
import { Controller, Get, Param, Post, Put, Delete, UseGuards, UseInterceptors, UploadedFile, Body, Logger, HttpException, HttpStatus, Res, Query } from '@nestjs/common';
import {
Controller,
Get,
Param,
Post,
Put,
Delete,
UseGuards,
UseInterceptors,
UploadedFile,
Body,
Logger,
HttpException,
HttpStatus,
Res,
Query,
} from '@nestjs/common';
import type { Response } from 'express';
import { RequirePermissions } from '../auth/permissions.decorator';
import { Permission } from '../auth/permissions.enum';
@@ -14,7 +30,6 @@ import { Document } from '../database/entities/document.entity';
import { DocumentField } from '../database/entities/document-field.entity';
import { DocumentType } from '../database/entities/document-type.entity';
@Controller('api/paperless')
export class PaperlessController {
private readonly logger = new Logger(PaperlessController.name);
@@ -36,7 +51,7 @@ export class PaperlessController {
const exists = await this.paperlessService.checksumExists(checksum);
return { exists };
}
@Get('documents')
async getDocuments(
@Query('search') search?: string,
@@ -67,7 +82,7 @@ export class PaperlessController {
async getTag(@Param('id') id: string) {
// If the service doesn't have getTag(id), I should add it or just fetch all and find
const tags = await this.paperlessService.getTags();
return tags.find(t => t.id === parseInt(id, 10));
return tags.find((t) => t.id === parseInt(id, 10));
}
@Get('document-types')
@@ -107,7 +122,11 @@ export class PaperlessController {
const documents = await this.paperlessService.getInboxDocuments();
// In old C# logic: only return docs where archive_serial_number is not null
return documents
.filter((doc: any) => doc.archive_serial_number !== null && doc.archive_serial_number !== undefined)
.filter(
(doc: any) =>
doc.archive_serial_number !== null &&
doc.archive_serial_number !== undefined,
)
.map((doc: any) => ({
id: doc.id,
title: doc.title,
@@ -127,7 +146,11 @@ export class PaperlessController {
async getManuellList() {
const documents = await this.paperlessService.getManuellDocuments();
return documents
.filter((doc: any) => doc.archive_serial_number !== null && doc.archive_serial_number !== undefined)
.filter(
(doc: any) =>
doc.archive_serial_number !== null &&
doc.archive_serial_number !== undefined,
)
.map((doc: any) => ({
id: doc.id,
title: doc.title,
@@ -146,16 +169,24 @@ export class PaperlessController {
@Get('inbox/preview/:id')
async getInboxPreview(@Param('id') id: string, @Res() res: Response) {
try {
const stream = await this.paperlessService.getDocumentPreviewStream(parseInt(id, 10));
const stream = await this.paperlessService.getDocumentPreviewStream(
parseInt(id, 10),
);
res.set({
'Content-Type': 'image/png',
'Content-Disposition': `inline; filename="${id}preview.png"`,
});
stream.on('error', () => { if (!res.headersSent) res.status(HttpStatus.INTERNAL_SERVER_ERROR).end(); else res.end(); });
stream.on('error', () => {
if (!res.headersSent)
res.status(HttpStatus.INTERNAL_SERVER_ERROR).end();
else res.end();
});
stream.pipe(res);
} catch (error) {
if (!res.headersSent) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send('Error fetching preview');
res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.send('Error fetching preview');
}
}
}
@@ -164,12 +195,18 @@ export class PaperlessController {
@Get('inbox/pdf/:id')
async getInboxPdf(@Param('id') id: string, @Res() res: Response) {
try {
const stream = await this.paperlessService.getDocumentPdfStream(parseInt(id, 10));
const stream = await this.paperlessService.getDocumentPdfStream(
parseInt(id, 10),
);
res.set({
'Content-Type': 'application/pdf',
'Content-Disposition': `inline; filename="${id}.pdf"`,
});
stream.on('error', () => { if (!res.headersSent) res.status(HttpStatus.INTERNAL_SERVER_ERROR).end(); else res.end(); });
stream.on('error', () => {
if (!res.headersSent)
res.status(HttpStatus.INTERNAL_SERVER_ERROR).end();
else res.end();
});
stream.pipe(res);
} catch (error) {
if (!res.headersSent) {
@@ -179,25 +216,28 @@ export class PaperlessController {
}
@Get('requirements/:id')
async getRequirements(@Param('id') id: string, @Query('Posteingang') posteingang: string) {
async getRequirements(
@Param('id') id: string,
@Query('Posteingang') posteingang: string,
) {
const documentTypeId = parseInt(id, 10);
const isPosteingang = posteingang === '1';
const requirements = await this.documentFieldRepo.find({
where: { DocumentType: documentTypeId },
});
// Custom fields fetching inside here could be slow, but this is the simplest translation of the old API
// Actually, getting all CFs doesn't take too long in Paperless API.
const customFields = await this.paperlessService.getCustomFields();
const retVal: any[] = [];
for (const req of requirements) {
if (isPosteingang && !req.VisiblePosteingang) {
continue;
}
const tmp: any = {
id: req.Id,
feldId: req.Type + (req.TypeIndex !== null ? '-' + req.TypeIndex : ''),
@@ -214,7 +254,7 @@ export class PaperlessController {
if (cf) {
tmp.feldName = cf.name;
tmp.feldTyp = cf.id === 12 ? 'int' : cf.data_type;
if (cf.extra_data && cf.extra_data.select_options) {
tmp.fieldOptions = cf.extra_data.select_options
.filter((o: any) => o !== null)
@@ -227,9 +267,14 @@ export class PaperlessController {
} else if (req.Type === 1) {
tmp.feldName = 'Absender';
tmp.feldTyp = 'select';
const response = await this.paperlessService.getCorrespondents({ page_size: 9999 });
const response = await this.paperlessService.getCorrespondents({
page_size: 9999,
});
const correspondents = response.results;
tmp.fieldOptions = correspondents.map((c: any) => ({ id: c.id.toString(), label: c.name }));
tmp.fieldOptions = correspondents.map((c: any) => ({
id: c.id.toString(),
label: c.name,
}));
} else if (req.Type === 2) {
tmp.feldName = 'Belegdatum';
tmp.feldTyp = 'date';
@@ -246,7 +291,7 @@ export class PaperlessController {
retVal.push(tmp);
}
return retVal;
}
@@ -265,34 +310,49 @@ export class PaperlessController {
if (body.date) {
let docDate = new Date(body.date);
if (docDate.getHours() > 22) {
docDate = new Date(docDate.getTime() + 24 * 60 * 60 * 1000 - docDate.getHours() * 60 * 60 * 1000);
docDate = new Date(
docDate.getTime() +
24 * 60 * 60 * 1000 -
docDate.getHours() * 60 * 60 * 1000,
);
}
oldDocument.created_date = docDate.toISOString().split('T')[0];
}
const cfDefinitions = await this.paperlessService.getCustomFields();
// update custom fields
if (body.customFields) {
for (const [key, value] of Object.entries(body.customFields)) {
const fieldId = parseInt(key, 10);
const cfDef = cfDefinitions.find((c: any) => c.id === fieldId);
let processedValue = value;
if (cfDef?.data_type === 'documentlink' && value !== null && value !== '' && !Array.isArray(value)) {
if (
cfDef?.data_type === 'documentlink' &&
value !== null &&
value !== '' &&
!Array.isArray(value)
) {
processedValue = [value];
}
const existingFieldIndex = oldDocument.custom_fields.findIndex((f: any) => f.field === fieldId);
const existingFieldIndex = oldDocument.custom_fields.findIndex(
(f: any) => f.field === fieldId,
);
if (existingFieldIndex !== -1) {
if (processedValue === null || processedValue === '') {
oldDocument.custom_fields.splice(existingFieldIndex, 1);
} else {
oldDocument.custom_fields[existingFieldIndex].value = processedValue;
oldDocument.custom_fields[existingFieldIndex].value =
processedValue;
}
} else if (processedValue !== null && processedValue !== '') {
oldDocument.custom_fields.push({ field: fieldId, value: processedValue });
oldDocument.custom_fields.push({
field: fieldId,
value: processedValue,
});
}
}
}
@@ -301,7 +361,7 @@ export class PaperlessController {
const reqs = await this.documentFieldRepo.find({
where: { DocumentType: oldDocument.document_type },
});
let isReady = true;
let isReadyPosteingang = true;
@@ -309,12 +369,19 @@ export class PaperlessController {
let isFieldValid = false;
if (req.Type === 1) isFieldValid = oldDocument.correspondent !== null;
if (req.Type === 2) isFieldValid = oldDocument.created_date !== null;
if (req.Type === 3) isFieldValid = oldDocument.archive_serial_number !== null;
if (req.Type === 4) isFieldValid = !!oldDocument.custom_fields.find((cf: any) => cf.field === req.TypeIndex && cf.value !== null && cf.value !== '');
if (req.Type === 5) isFieldValid = oldDocument.title !== null && oldDocument.title !== '';
if (req.Type === 3)
isFieldValid = oldDocument.archive_serial_number !== null;
if (req.Type === 4)
isFieldValid = !!oldDocument.custom_fields.find(
(cf: any) =>
cf.field === req.TypeIndex && cf.value !== null && cf.value !== '',
);
if (req.Type === 5)
isFieldValid = oldDocument.title !== null && oldDocument.title !== '';
if (req.IsRequired && !isFieldValid) isReady = false;
if (req.IsRequiredPosteingang && !isFieldValid) isReadyPosteingang = false;
if (req.IsRequiredPosteingang && !isFieldValid)
isReadyPosteingang = false;
}
const docType = await this.documentTypeRepo.findOne({
@@ -325,7 +392,10 @@ export class PaperlessController {
if (isReady) {
oldDocument.tags = oldDocument.tags.filter((t: number) => t !== 1);
if (docType?.TagNotReady) oldDocument.tags = oldDocument.tags.filter((t: number) => t !== docType.TagNotReady);
if (docType?.TagNotReady)
oldDocument.tags = oldDocument.tags.filter(
(t: number) => t !== docType.TagNotReady,
);
if (docType?.TagReady && !oldDocument.tags.includes(docType.TagReady)) {
oldDocument.tags.push(docType.TagReady);
}
@@ -335,17 +405,24 @@ export class PaperlessController {
for (const cf of oldDocument.custom_fields) {
const placeholder = `{{CUSTOM[${cf.field}]}}`;
if (titleTemplate.includes(placeholder)) {
titleTemplate = titleTemplate.replace(placeholder, cf.value?.toString() ?? '');
titleTemplate = titleTemplate.replace(
placeholder,
cf.value?.toString() ?? '',
);
}
}
titleTemplate = titleTemplate.replace('{{DATE}}', oldDocument.created_date);
titleTemplate = titleTemplate.replace(
'{{DATE}}',
oldDocument.created_date,
);
oldDocument.title = titleTemplate;
}
} else {
if (docType?.TagNotReady) {
if (isReadyPosteingang) {
oldDocument.tags = oldDocument.tags.filter((t: number) => t !== 1);
if (!oldDocument.tags.includes(docType.TagNotReady)) oldDocument.tags.push(docType.TagNotReady);
if (!oldDocument.tags.includes(docType.TagNotReady))
oldDocument.tags.push(docType.TagNotReady);
} else {
if (!oldDocument.tags.includes(1)) oldDocument.tags.push(1);
}
@@ -353,7 +430,9 @@ export class PaperlessController {
if (!oldDocument.tags.includes(1)) oldDocument.tags.push(1);
}
if (docType?.TagReady) {
oldDocument.tags = oldDocument.tags.filter((t: number) => t !== docType.TagReady);
oldDocument.tags = oldDocument.tags.filter(
(t: number) => t !== docType.TagReady,
);
}
}
@@ -391,16 +470,21 @@ export class PaperlessController {
@UploadedFile() file: Express.Multer.File,
@Body() dto: UploadExternalDto,
) {
this.logger.log(`Externer Upload gestartet: ${dto.interneBelegnummer} (${file?.originalname})`);
this.logger.log(
`Externer Upload gestartet: ${dto.interneBelegnummer} (${file?.originalname})`,
);
try {
// 0. Check if ASN already exists
await this.paperlessService.validateAsnNotExists(dto.interneBelegnummer);
// 1. Forward to Paperless
const paperlessTaskId = await this.paperlessService.uploadDocument(file.path, {
title: `Beleg ${dto.interneBelegnummer}`,
});
const paperlessTaskId = await this.paperlessService.uploadDocument(
file.path,
{
title: `Beleg ${dto.interneBelegnummer}`,
},
);
// 2. Create local Task
const task = this.taskRepo.create({
@@ -422,10 +506,15 @@ export class PaperlessController {
await this.taskRepo.save(task);
this.logger.log(`Externer Upload erfolgreich: ${task.TaskId} für Beleg ${dto.interneBelegnummer}`);
this.logger.log(
`Externer Upload erfolgreich: ${task.TaskId} für Beleg ${dto.interneBelegnummer}`,
);
return task.TaskId;
} catch (err) {
this.logger.error(`Fehler beim externen Upload für Beleg ${dto.interneBelegnummer}: ${err.message}`, err.stack);
this.logger.error(
`Fehler beim externen Upload für Beleg ${dto.interneBelegnummer}: ${err.message}`,
err.stack,
);
throw new HttpException(
`Fehler beim Verarbeiten des Dokumenten-Uploads: ${err.message}`,
HttpStatus.INTERNAL_SERVER_ERROR,
@@ -14,12 +14,22 @@ import { AuthModule } from '../auth/auth.module';
@Module({
imports: [
TypeOrmModule.forFeature([DocumentType, DocumentField, Task, Document, Attachment]),
TypeOrmModule.forFeature([
DocumentType,
DocumentField,
Task,
Document,
Attachment,
]),
forwardRef(() => PostprocessingModule),
AuthModule,
],
controllers: [PaperlessController],
providers: [PaperlessService, PaperlessProcessorService, PaperlessTaskProcessorService],
providers: [
PaperlessService,
PaperlessProcessorService,
PaperlessTaskProcessorService,
],
exports: [PaperlessService],
})
export class PaperlessModule {}
@@ -11,7 +11,10 @@ export class PaperlessService {
private readonly client: AxiosInstance;
constructor(private readonly configService: ConfigService) {
const baseURL = this.configService.get<string>('PAPERLESS_URL', 'http://localhost:8000');
const baseURL = this.configService.get<string>(
'PAPERLESS_URL',
'http://localhost:8000',
);
const token = this.configService.get<string>('PAPERLESS_TOKEN', '');
this.client = axios.create({
@@ -49,16 +52,22 @@ export class PaperlessService {
if (options?.title) form.append('title', options.title);
if (options?.created) form.append('created', options.created);
if (options?.documentType) form.append('document_type', String(options.documentType));
if (options?.correspondent) form.append('correspondent', String(options.correspondent));
if (options?.storagePath) form.append('storage_path', String(options.storagePath));
if (options?.documentType)
form.append('document_type', String(options.documentType));
if (options?.correspondent)
form.append('correspondent', String(options.correspondent));
if (options?.storagePath)
form.append('storage_path', String(options.storagePath));
if (options?.owner !== undefined && options.owner !== null) {
form.append('owner', String(options.owner));
}
if (options?.tags) {
options.tags.forEach((tag) => form.append('tags', String(tag)));
}
if (options?.archiveSerialNumber !== undefined && !Number.isNaN(options.archiveSerialNumber)) {
if (
options?.archiveSerialNumber !== undefined &&
!Number.isNaN(options.archiveSerialNumber)
) {
form.append('archive_serial_number', String(options.archiveSerialNumber));
}
if (options?.customFields && Object.keys(options.customFields).length > 0) {
@@ -92,27 +101,30 @@ export class PaperlessService {
async getInboxDocuments(): Promise<any[]> {
// API pagination to get large amount of inbox documents (assuming max 9999 like C# app)
const response = await this.client.get('/documents/', {
params: {
page: 1,
page_size: 9999,
ordering: '-added',
truncate_content: true,
tags__id__all: 1
}
params: {
page: 1,
page_size: 9999,
ordering: '-added',
truncate_content: true,
tags__id__all: 1,
},
});
return response.data.results;
}
async getManuellDocuments(): Promise<any[]> {
const errorTag = this.configService.get<number>('MANUELL_BEARBEITEN_TAG', 6);
const errorTag = this.configService.get<number>(
'MANUELL_BEARBEITEN_TAG',
6,
);
const response = await this.client.get('/documents/', {
params: {
page: 1,
page_size: 9999,
ordering: '-added',
truncate_content: true,
tags__id__all: errorTag
}
params: {
page: 1,
page_size: 9999,
ordering: '-added',
truncate_content: true,
tags__id__all: errorTag,
},
});
return response.data.results;
}
@@ -124,7 +136,9 @@ export class PaperlessService {
} catch (err: any) {
const body = err?.response?.data;
if (body) {
this.logger.error(`Paperless updateDocument(${id}) Fehlerdetails: ${JSON.stringify(body)}`);
this.logger.error(
`Paperless updateDocument(${id}) Fehlerdetails: ${JSON.stringify(body)}`,
);
}
throw err;
}
@@ -132,14 +146,14 @@ export class PaperlessService {
async getDocumentTypes(): Promise<any[]> {
const response = await this.client.get('/document_types/', {
params: { page_size: 9999 }
params: { page_size: 9999 },
});
return response.data.results;
}
async getTags(): Promise<any[]> {
const response = await this.client.get('/tags/', {
params: { page_size: 9999 }
params: { page_size: 9999 },
});
return response.data.results;
}
@@ -156,7 +170,7 @@ export class PaperlessService {
async getCustomFields(): Promise<any[]> {
const response = await this.client.get('/custom_fields/', {
params: { page_size: 9999 }
params: { page_size: 9999 },
});
return response.data.results;
}
@@ -184,10 +198,14 @@ export class PaperlessService {
await this.client.delete(`/correspondents/${id}/`);
}
async downloadDocument(id: number, type: 'original' | 'archive' = 'archive'): Promise<Buffer> {
const endpoint = type === 'original'
? `/documents/${id}/download/`
: `/documents/${id}/download/`;
async downloadDocument(
id: number,
type: 'original' | 'archive' = 'archive',
): Promise<Buffer> {
const endpoint =
type === 'original'
? `/documents/${id}/download/`
: `/documents/${id}/download/`;
const response = await this.client.get(endpoint, {
responseType: 'arraybuffer',
params: type === 'original' ? { original: true } : {},
@@ -204,10 +222,14 @@ export class PaperlessService {
return response.data;
}
async getDocumentPdfStream(id: number, type: 'original' | 'archive' = 'archive'): Promise<any> {
const endpoint = type === 'original'
? `/documents/${id}/download/`
: `/documents/${id}/download/`;
async getDocumentPdfStream(
id: number,
type: 'original' | 'archive' = 'archive',
): Promise<any> {
const endpoint =
type === 'original'
? `/documents/${id}/download/`
: `/documents/${id}/download/`;
const response = await this.client.get(endpoint, {
responseType: 'stream',
params: type === 'original' ? { original: true } : {},
@@ -222,7 +244,9 @@ export class PaperlessService {
}
async addNote(id: number, note: string): Promise<any> {
const response = await this.client.post(`/documents/${id}/notes/`, { note });
const response = await this.client.post(`/documents/${id}/notes/`, {
note,
});
return response.data;
}
@@ -248,7 +272,7 @@ export class PaperlessService {
*/
async validateAsnNotExists(interneBelegnummer: string): Promise<void> {
if (!interneBelegnummer) return;
// Logic like in PaperlessTaskProcessorService
const asnNum = parseInt(interneBelegnummer.replace(/-/g, ''), 10);
if (isNaN(asnNum)) return;
@@ -271,7 +295,7 @@ export class PaperlessService {
params: { archive_serial_number: asn, page_size: 5 },
});
if ((response.data.count ?? 0) === 0) return null;
const match = (response.data.results as any[] ?? []).find(
const match = ((response.data.results as any[]) ?? []).find(
(doc: any) => Number(doc.archive_serial_number) === asn,
);
return match ? Number(match.id) : null;
@@ -281,7 +305,10 @@ export class PaperlessService {
* Liefert die Paperless-Doc-ID des passenden Dokuments oder null.
* Paperless kann den custom_fields-Filter ignorieren — daher manuell verifizieren.
*/
async findDocumentIdByCustomField(fieldId: number, value: string): Promise<number | null> {
async findDocumentIdByCustomField(
fieldId: number,
value: string,
): Promise<number | null> {
const response = await this.client.get('/documents/', {
params: {
[`custom_fields__${fieldId}__value__iexact`]: value,
@@ -291,9 +318,14 @@ export class PaperlessService {
});
if ((response.data.count ?? 0) === 0) return null;
const valueLower = value.toLowerCase();
const match = (response.data.results as any[] ?? []).find((doc: any) =>
(Array.isArray(doc.custom_fields) ? doc.custom_fields as any[] : []).some(
(cf: any) => cf.field === fieldId && String(cf.value ?? '').toLowerCase() === valueLower,
const match = ((response.data.results as any[]) ?? []).find((doc: any) =>
(Array.isArray(doc.custom_fields)
? (doc.custom_fields as any[])
: []
).some(
(cf: any) =>
cf.field === fieldId &&
String(cf.value ?? '').toLowerCase() === valueLower,
),
);
return match ? Number(match.id) : null;