fix(agrarmonitor): Karenzzeit vor "Von AM zurück"-Markierung
Build and Push Multi-Platform Images / build-and-push (push) Successful in 39s
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:
@@ -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[]) ?? [];
|
||||
|
||||
@@ -42,6 +42,8 @@ export class AgrarmonitorController {
|
||||
tagHochgeladen: string;
|
||||
linkField: string;
|
||||
tagManuell: string;
|
||||
importWartezeitMinuten: string;
|
||||
notizMarker: string;
|
||||
},
|
||||
) {
|
||||
return this.pollingService.updatePollingConfig(
|
||||
@@ -50,6 +52,8 @@ export class AgrarmonitorController {
|
||||
body.tagHochgeladen,
|
||||
body.linkField,
|
||||
body.tagManuell ?? '',
|
||||
body.importWartezeitMinuten ?? '10',
|
||||
body.notizMarker ?? 'Agrarmonitor',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -250,6 +250,14 @@ export class PaperlessService {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async getNotes(
|
||||
id: number,
|
||||
): Promise<Array<{ id: number; note: string; created: string }>> {
|
||||
const response = await this.client.get(`/documents/${id}/notes/`);
|
||||
const data = response.data;
|
||||
return Array.isArray(data) ? data : (data?.results ?? []);
|
||||
}
|
||||
|
||||
async checksumExists(checksum: string): Promise<boolean> {
|
||||
const response = await this.client.get('/documents/', {
|
||||
params: { checksum__iexact: checksum },
|
||||
|
||||
@@ -208,6 +208,8 @@ export interface AgrarmonitorPollingConfig {
|
||||
tagHochgeladen: string;
|
||||
linkField: string;
|
||||
tagManuell: string;
|
||||
importWartezeitMinuten: string;
|
||||
notizMarker: string;
|
||||
}
|
||||
|
||||
export interface AgrarmonitorPollingResult {
|
||||
|
||||
@@ -2600,6 +2600,20 @@ function AgrarmonitorTab() {
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="importWartezeitMinuten"
|
||||
label="Wartezeit bis „zurück“ (Minuten)"
|
||||
tooltip="Wie lange nach dem Versand gewartet wird, bevor ein Beleg als „Von Agrarmonitor zurück“ markiert wird (Import kann bis zu 10 Min dauern)."
|
||||
>
|
||||
<Input placeholder="10" style={{ width: 120 }} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="notizMarker"
|
||||
label="Notiz-Marker (Text der AM-Sende-Notiz)"
|
||||
tooltip="Text, an dem die Sende-Notiz erkannt wird (Teil-Übereinstimmung, Groß-/Kleinschreibung egal). Deren Zeitstempel bestimmt die Wartezeit."
|
||||
>
|
||||
<Input placeholder="Agrarmonitor" style={{ width: 280 }} />
|
||||
</Form.Item>
|
||||
<Button type="primary" loading={pollingSaving} onClick={handleSavePollingConfig}>
|
||||
Speichern
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user