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_tag_hochgeladen', '');
|
||||||
await this.upsertSetting('agrarmonitor_link_field', '');
|
await this.upsertSetting('agrarmonitor_link_field', '');
|
||||||
await this.upsertSetting('agrarmonitor_tag_manuell', '');
|
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 * * * *')
|
@Cron(process.env['AGRARMONITOR_POLLING_CRON'] || '0 */30 * * * *')
|
||||||
@@ -80,14 +82,20 @@ export class AgrarmonitorPollingService implements OnModuleInit {
|
|||||||
tagHochgeladen: string;
|
tagHochgeladen: string;
|
||||||
linkField: string;
|
linkField: string;
|
||||||
tagManuell: string;
|
tagManuell: string;
|
||||||
|
importWartezeitMinuten: string;
|
||||||
|
notizMarker: string;
|
||||||
}> {
|
}> {
|
||||||
const [fertig, verbucht, hochgeladen, linkField, manuell] =
|
const [fertig, verbucht, hochgeladen, linkField, manuell, wartezeit, marker] =
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_fertig' }),
|
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_fertig' }),
|
||||||
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_verbucht' }),
|
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_verbucht' }),
|
||||||
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_hochgeladen' }),
|
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_hochgeladen' }),
|
||||||
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_link_field' }),
|
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_link_field' }),
|
||||||
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_manuell' }),
|
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_manuell' }),
|
||||||
|
this.settingRepo.findOneBy({
|
||||||
|
Tag: 'agrarmonitor_import_wartezeit_minuten',
|
||||||
|
}),
|
||||||
|
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_notiz_marker' }),
|
||||||
]);
|
]);
|
||||||
return {
|
return {
|
||||||
tagFertig: fertig?.Wert ?? '4',
|
tagFertig: fertig?.Wert ?? '4',
|
||||||
@@ -95,6 +103,8 @@ export class AgrarmonitorPollingService implements OnModuleInit {
|
|||||||
tagHochgeladen: hochgeladen?.Wert ?? '',
|
tagHochgeladen: hochgeladen?.Wert ?? '',
|
||||||
linkField: linkField?.Wert ?? '',
|
linkField: linkField?.Wert ?? '',
|
||||||
tagManuell: manuell?.Wert ?? '',
|
tagManuell: manuell?.Wert ?? '',
|
||||||
|
importWartezeitMinuten: wartezeit?.Wert ?? '10',
|
||||||
|
notizMarker: marker?.Wert ?? 'Agrarmonitor',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,12 +114,16 @@ export class AgrarmonitorPollingService implements OnModuleInit {
|
|||||||
tagHochgeladen: string,
|
tagHochgeladen: string,
|
||||||
linkField: string,
|
linkField: string,
|
||||||
tagManuell: string,
|
tagManuell: string,
|
||||||
|
importWartezeitMinuten: string,
|
||||||
|
notizMarker: string,
|
||||||
): Promise<{
|
): Promise<{
|
||||||
tagFertig: string;
|
tagFertig: string;
|
||||||
tagVerbucht: string;
|
tagVerbucht: string;
|
||||||
tagHochgeladen: string;
|
tagHochgeladen: string;
|
||||||
linkField: string;
|
linkField: string;
|
||||||
tagManuell: string;
|
tagManuell: string;
|
||||||
|
importWartezeitMinuten: string;
|
||||||
|
notizMarker: string;
|
||||||
}> {
|
}> {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.settingRepo.update(
|
this.settingRepo.update(
|
||||||
@@ -132,8 +146,24 @@ export class AgrarmonitorPollingService implements OnModuleInit {
|
|||||||
{ Tag: 'agrarmonitor_tag_manuell' },
|
{ Tag: 'agrarmonitor_tag_manuell' },
|
||||||
{ Wert: tagManuell },
|
{ 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> {
|
async runPolling(): Promise<PollingResult> {
|
||||||
@@ -393,17 +423,26 @@ export class AgrarmonitorPollingService implements OnModuleInit {
|
|||||||
fertigSetting,
|
fertigSetting,
|
||||||
linkFieldSetting,
|
linkFieldSetting,
|
||||||
manuellSetting,
|
manuellSetting,
|
||||||
|
wartezeitSetting,
|
||||||
|
markerSetting,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_hochgeladen' }),
|
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_hochgeladen' }),
|
||||||
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_fertig' }),
|
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_fertig' }),
|
||||||
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_link_field' }),
|
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_link_field' }),
|
||||||
this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_manuell' }),
|
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 tagHochgeladenId = parseInt(hochgeladenSetting?.Wert ?? '', 10);
|
||||||
const tagFertigId = parseInt(fertigSetting?.Wert ?? '4', 10);
|
const tagFertigId = parseInt(fertigSetting?.Wert ?? '4', 10);
|
||||||
const linkFieldId = parseInt(linkFieldSetting?.Wert ?? '', 10);
|
const linkFieldId = parseInt(linkFieldSetting?.Wert ?? '', 10);
|
||||||
const tagManuellId = parseInt(manuellSetting?.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)) {
|
if (isNaN(tagHochgeladenId)) {
|
||||||
this.logger.warn(
|
this.logger.warn(
|
||||||
@@ -512,6 +551,40 @@ export class AgrarmonitorPollingService implements OnModuleInit {
|
|||||||
continue;
|
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
|
// Weder verbucht noch im Dateieingang → Tags "Manuell bearbeiten" + "Von AM zurück" setzen
|
||||||
if (!isNaN(tagManuellId)) {
|
if (!isNaN(tagManuellId)) {
|
||||||
const currentTags: number[] = (doc.tags as number[]) ?? [];
|
const currentTags: number[] = (doc.tags as number[]) ?? [];
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ export class AgrarmonitorController {
|
|||||||
tagHochgeladen: string;
|
tagHochgeladen: string;
|
||||||
linkField: string;
|
linkField: string;
|
||||||
tagManuell: string;
|
tagManuell: string;
|
||||||
|
importWartezeitMinuten: string;
|
||||||
|
notizMarker: string;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
return this.pollingService.updatePollingConfig(
|
return this.pollingService.updatePollingConfig(
|
||||||
@@ -50,6 +52,8 @@ export class AgrarmonitorController {
|
|||||||
body.tagHochgeladen,
|
body.tagHochgeladen,
|
||||||
body.linkField,
|
body.linkField,
|
||||||
body.tagManuell ?? '',
|
body.tagManuell ?? '',
|
||||||
|
body.importWartezeitMinuten ?? '10',
|
||||||
|
body.notizMarker ?? 'Agrarmonitor',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -250,6 +250,14 @@ export class PaperlessService {
|
|||||||
return response.data;
|
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> {
|
async checksumExists(checksum: string): Promise<boolean> {
|
||||||
const response = await this.client.get('/documents/', {
|
const response = await this.client.get('/documents/', {
|
||||||
params: { checksum__iexact: checksum },
|
params: { checksum__iexact: checksum },
|
||||||
|
|||||||
@@ -208,6 +208,8 @@ export interface AgrarmonitorPollingConfig {
|
|||||||
tagHochgeladen: string;
|
tagHochgeladen: string;
|
||||||
linkField: string;
|
linkField: string;
|
||||||
tagManuell: string;
|
tagManuell: string;
|
||||||
|
importWartezeitMinuten: string;
|
||||||
|
notizMarker: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgrarmonitorPollingResult {
|
export interface AgrarmonitorPollingResult {
|
||||||
|
|||||||
@@ -2600,6 +2600,20 @@ function AgrarmonitorTab() {
|
|||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</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}>
|
<Button type="primary" loading={pollingSaving} onClick={handleSavePollingConfig}>
|
||||||
Speichern
|
Speichern
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user