Initial commit with Email Import Wizard and Task Processor updates
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { Cron } from '@nestjs/schedule';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { DocumentField } from '../database/entities/document-field.entity';
|
||||
import { DocumentType } from '../database/entities/document-type.entity';
|
||||
import { PaperlessService } from './paperless.service';
|
||||
import { PostprocessingService } from '../postprocessing/postprocessing.service';
|
||||
|
||||
@Injectable()
|
||||
export class PaperlessProcessorService {
|
||||
private readonly logger = new Logger(PaperlessProcessorService.name);
|
||||
|
||||
constructor(
|
||||
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>,
|
||||
) {}
|
||||
|
||||
@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 ?? []);
|
||||
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).`);
|
||||
|
||||
for (const doc of documents) {
|
||||
try {
|
||||
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)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
this.logger.error('Fehler bei der Dokumentenverarbeitung:', err);
|
||||
}
|
||||
}
|
||||
|
||||
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.`);
|
||||
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) });
|
||||
return updated;
|
||||
}
|
||||
|
||||
const docTypeConfig = await this.docTypeRepo.findOne({
|
||||
where: { DocumentTypeId: doc.document_type },
|
||||
});
|
||||
|
||||
if (!docTypeConfig) {
|
||||
this.logger.warn(`Konfiguration für DocumentType ${doc.document_type} nicht in der Datenbank gefunden.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const fieldsConfig = await this.docFieldRepo.find({
|
||||
where: { DocumentType: doc.document_type },
|
||||
});
|
||||
|
||||
if (fieldsConfig.length === 0) {
|
||||
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 });
|
||||
return updated;
|
||||
}
|
||||
|
||||
const currentCustomFields = doc.custom_fields || [];
|
||||
const newCustomFields = [...currentCustomFields];
|
||||
let isAllRequiredFilled = true;
|
||||
|
||||
for (const fieldConf of fieldsConfig) {
|
||||
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.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const existingField = newCustomFields.find(f => f.field === customFieldId);
|
||||
let isFilled = false;
|
||||
|
||||
if (existingField) {
|
||||
isFilled = existingField.value !== null && existingField.value !== '';
|
||||
} else {
|
||||
newCustomFields.push({ field: customFieldId, value: null });
|
||||
}
|
||||
|
||||
if (fieldConf.IsRequired && !isFilled) {
|
||||
isAllRequiredFilled = false;
|
||||
}
|
||||
} else {
|
||||
if (fieldConf.IsRequired) {
|
||||
let isFilled = false;
|
||||
switch (fieldConf.Type) {
|
||||
case 1:
|
||||
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;
|
||||
break;
|
||||
case 5:
|
||||
isFilled = !!doc.title;
|
||||
break;
|
||||
default:
|
||||
isFilled = true;
|
||||
}
|
||||
if (!isFilled) {
|
||||
isAllRequiredFilled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const tagsSet = new Set<number>(doc.tags || []);
|
||||
|
||||
if (isAllRequiredFilled) {
|
||||
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);
|
||||
}
|
||||
|
||||
tagsSet.add(17);
|
||||
|
||||
// Titel-Template auflösen
|
||||
const updatePayload: any = {
|
||||
custom_fields: newCustomFields,
|
||||
tags: Array.from(tagsSet),
|
||||
};
|
||||
|
||||
if (docTypeConfig.TitelTemplate) {
|
||||
let title = docTypeConfig.TitelTemplate;
|
||||
|
||||
// Prüfen ob alle im Template referenzierten Custom Fields ausgefüllt sind
|
||||
const placeholderRegex = /\{\{CUSTOM\[(\d+)\]\}\}/g;
|
||||
let allFilled = true;
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = placeholderRegex.exec(title)) !== null) {
|
||||
const fieldId = parseInt(match[1], 10);
|
||||
const cf = newCustomFields.find(f => f.field === fieldId);
|
||||
if (!cf || cf.value == null || cf.value === '') {
|
||||
allFilled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allFilled) {
|
||||
for (const cf of newCustomFields) {
|
||||
const placeholder = `{{CUSTOM[${cf.field}]}}`;
|
||||
if (title.includes(placeholder)) {
|
||||
title = title.replaceAll(placeholder, cf.value != null ? String(cf.value) : '');
|
||||
}
|
||||
}
|
||||
|
||||
if (title.includes('{{DATE}}')) {
|
||||
const created = doc.created ? new Date(doc.created) : new Date();
|
||||
const dateStr = `${String(created.getDate()).padStart(2, '0')}.${String(created.getMonth() + 1).padStart(2, '0')}.${created.getFullYear()}`;
|
||||
title = title.replaceAll('{{DATE}}', dateStr);
|
||||
}
|
||||
|
||||
if (title.includes('{{ZEITSTEMPEL}}')) {
|
||||
const now = new Date();
|
||||
const ts = `${String(now.getDate()).padStart(2, '0')}.${String(now.getMonth() + 1).padStart(2, '0')}.${now.getFullYear()} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
|
||||
title = title.replaceAll('{{ZEITSTEMPEL}}', ts);
|
||||
}
|
||||
|
||||
updatePayload.title = title;
|
||||
} else {
|
||||
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);
|
||||
|
||||
this.logger.log(`Dokument ${doc.id} erfolgreich aktualisiert (Alle Pflichtfelder vorhanden: ${isAllRequiredFilled}).`);
|
||||
return updated;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user