fix(agrarmonitor): Karenzzeit vor "Von AM zurück"-Markierung
Build and Push Multi-Platform Images / build-and-push (push) Successful in 39s

Belege, die per E-Mail an Agrarmonitor gesendet werden, brauchen bis zu
10 Minuten bis zum Import in den Dateieingang. Bisher wurden sie im
Upload-Check sofort als "Manuell bearbeiten" + Tag 19 markiert, wenn sie
weder verbucht noch im Dateieingang waren – auch mitten im Import-Fenster.

Jetzt wird vor dem Markieren geprüft, ob die jüngste Notiz mit dem
konfigurierbaren Marker-Text jünger als die Wartezeit ist; falls ja, wird
der Beleg übersprungen und beim nächsten Lauf erneut geprüft. Wartezeit
(agrarmonitor_import_wartezeit_minuten, Standard 10) und Marker
(agrarmonitor_notiz_marker, Standard "Agrarmonitor") sind als Settings
konfigurierbar und in der Settings-Seite editierbar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 12:40:59 +02:00
parent 8fda248683
commit 0765d14d3b
5 changed files with 103 additions and 2 deletions
@@ -56,6 +56,8 @@ export class AgrarmonitorPollingService implements OnModuleInit {
await this.upsertSetting('agrarmonitor_tag_hochgeladen', '');
await this.upsertSetting('agrarmonitor_link_field', '');
await this.upsertSetting('agrarmonitor_tag_manuell', '');
await this.upsertSetting('agrarmonitor_import_wartezeit_minuten', '10');
await this.upsertSetting('agrarmonitor_notiz_marker', 'Agrarmonitor');
}
@Cron(process.env['AGRARMONITOR_POLLING_CRON'] || '0 */30 * * * *')
@@ -80,14 +82,20 @@ export class AgrarmonitorPollingService implements OnModuleInit {
tagHochgeladen: string;
linkField: string;
tagManuell: string;
importWartezeitMinuten: string;
notizMarker: string;
}> {
const [fertig, verbucht, hochgeladen, linkField, manuell] =
const [fertig, verbucht, hochgeladen, linkField, manuell, wartezeit, marker] =
await Promise.all([
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_fertig' }),
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_verbucht' }),
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_hochgeladen' }),
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_link_field' }),
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_manuell' }),
this.settingRepo.findOneBy({
Tag: 'agrarmonitor_import_wartezeit_minuten',
}),
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_notiz_marker' }),
]);
return {
tagFertig: fertig?.Wert ?? '4',
@@ -95,6 +103,8 @@ export class AgrarmonitorPollingService implements OnModuleInit {
tagHochgeladen: hochgeladen?.Wert ?? '',
linkField: linkField?.Wert ?? '',
tagManuell: manuell?.Wert ?? '',
importWartezeitMinuten: wartezeit?.Wert ?? '10',
notizMarker: marker?.Wert ?? 'Agrarmonitor',
};
}
@@ -104,12 +114,16 @@ export class AgrarmonitorPollingService implements OnModuleInit {
tagHochgeladen: string,
linkField: string,
tagManuell: string,
importWartezeitMinuten: string,
notizMarker: string,
): Promise<{
tagFertig: string;
tagVerbucht: string;
tagHochgeladen: string;
linkField: string;
tagManuell: string;
importWartezeitMinuten: string;
notizMarker: string;
}> {
await Promise.all([
this.settingRepo.update(
@@ -132,8 +146,24 @@ export class AgrarmonitorPollingService implements OnModuleInit {
{ Tag: 'agrarmonitor_tag_manuell' },
{ Wert: tagManuell },
),
this.settingRepo.update(
{ Tag: 'agrarmonitor_import_wartezeit_minuten' },
{ Wert: importWartezeitMinuten },
),
this.settingRepo.update(
{ Tag: 'agrarmonitor_notiz_marker' },
{ Wert: notizMarker },
),
]);
return { tagFertig, tagVerbucht, tagHochgeladen, linkField, tagManuell };
return {
tagFertig,
tagVerbucht,
tagHochgeladen,
linkField,
tagManuell,
importWartezeitMinuten,
notizMarker,
};
}
async runPolling(): Promise<PollingResult> {
@@ -393,17 +423,26 @@ export class AgrarmonitorPollingService implements OnModuleInit {
fertigSetting,
linkFieldSetting,
manuellSetting,
wartezeitSetting,
markerSetting,
] = await Promise.all([
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_hochgeladen' }),
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_fertig' }),
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_link_field' }),
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_manuell' }),
this.settingRepo.findOneBy({
Tag: 'agrarmonitor_import_wartezeit_minuten',
}),
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_notiz_marker' }),
]);
const tagHochgeladenId = parseInt(hochgeladenSetting?.Wert ?? '', 10);
const tagFertigId = parseInt(fertigSetting?.Wert ?? '4', 10);
const linkFieldId = parseInt(linkFieldSetting?.Wert ?? '', 10);
const tagManuellId = parseInt(manuellSetting?.Wert ?? '', 10);
const importWartezeitMinuten =
parseInt(wartezeitSetting?.Wert ?? '10', 10) || 10;
const notizMarker = (markerSetting?.Wert ?? 'Agrarmonitor').trim();
if (isNaN(tagHochgeladenId)) {
this.logger.warn(
@@ -512,6 +551,40 @@ export class AgrarmonitorPollingService implements OnModuleInit {
continue;
}
// Karenzzeit: evtl. hat Agrarmonitor die E-Mail noch nicht importiert.
// Ist die Sende-Notiz jünger als die konfigurierte Wartezeit, nicht markieren.
const marker = notizMarker.toLowerCase();
if (marker) {
let notes: Array<{ note: string; created: string }>;
try {
notes = await this.paperlessService.getNotes(doc.id as number);
} catch (err: unknown) {
// Vorsichtig: bei Notiz-Abruf-Fehler NICHT markieren, nächster Lauf prüft erneut
this.logger.warn(
`${interneBelegnummer}: Notiz-Abruf fehlgeschlagen — nicht markiert: ${err instanceof Error ? err.message : err}`,
);
result.skipped++;
await this.delay(500);
continue;
}
const newest = notes
.filter((n) => (n.note ?? '').toLowerCase().includes(marker))
.map((n) => new Date(n.created).getTime())
.filter((t) => !isNaN(t))
.reduce((max, t) => Math.max(max, t), 0);
if (
newest > 0 &&
Date.now() - newest < importWartezeitMinuten * 60 * 1000
) {
this.logger.log(
`${interneBelegnummer}: Sende-Notiz jünger als ${importWartezeitMinuten} Min — warte auf Import`,
);
result.skipped++;
await this.delay(500);
continue;
}
}
// Weder verbucht noch im Dateieingang → Tags "Manuell bearbeiten" + "Von AM zurück" setzen
if (!isNaN(tagManuellId)) {
const currentTags: number[] = (doc.tags as number[]) ?? [];