diff --git a/paperless-backend/package-lock.json b/paperless-backend/package-lock.json index 0ac7259..ee01b34 100644 --- a/paperless-backend/package-lock.json +++ b/paperless-backend/package-lock.json @@ -30,7 +30,6 @@ "jwks-rsa": "^4.0.1", "mailparser": "^3.9.8", "mysql2": "^3.20.0", - "node-html-parser": "^7.1.0", "nodemailer": "^8.0.5", "passport": "^0.7.0", "passport-jwt": "^4.0.1", @@ -4863,7 +4862,7 @@ }, "node_modules/agrarmonitor-connector": { "version": "0.1.0", - "resolved": "git+https://gitea.poettker-cloud.de/bjoernpoettker/AgrarmonitorConnector.git#921c67503b68e46d504ac1c72fb1372cda633006", + "resolved": "git+https://gitea.poettker-cloud.de/bjoernpoettker/AgrarmonitorConnector.git#cf6bc1b5cc7e5ffa060c4a37bcea7d9ea6635527", "license": "MIT", "dependencies": { "axios": "^1.7.9", @@ -5337,12 +5336,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "license": "ISC" - }, "node_modules/brace-expansion": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", @@ -6024,22 +6017,6 @@ "node": "*" } }, - "node_modules/css-select": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", - "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, "node_modules/css-tree": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", @@ -6053,18 +6030,6 @@ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, - "node_modules/css-what": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", - "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -9976,16 +9941,6 @@ "url": "https://opencollective.com/node-fetch" } }, - "node_modules/node-html-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-7.1.0.tgz", - "integrity": "sha512-iJo8b2uYGT40Y8BTyy5ufL6IVbN8rbm/1QK2xffXU/1a/v3AAa0d1YAoqBNYqaS4R/HajkWIpIfdE6KcyFh1AQ==", - "license": "MIT", - "dependencies": { - "css-select": "^5.1.0", - "he": "1.2.0" - } - }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -10032,18 +9987,6 @@ "node": ">=8" } }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", diff --git a/paperless-backend/package.json b/paperless-backend/package.json index e8a1d3e..5f4d168 100644 --- a/paperless-backend/package.json +++ b/paperless-backend/package.json @@ -41,7 +41,6 @@ "jwks-rsa": "^4.0.1", "mailparser": "^3.9.8", "mysql2": "^3.20.0", - "node-html-parser": "^7.1.0", "nodemailer": "^8.0.5", "passport": "^0.7.0", "passport-jwt": "^4.0.1", diff --git a/paperless-backend/src/agrarmonitor/agrarmonitor-polling.service.ts b/paperless-backend/src/agrarmonitor/agrarmonitor-polling.service.ts index 6346718..890f00f 100644 --- a/paperless-backend/src/agrarmonitor/agrarmonitor-polling.service.ts +++ b/paperless-backend/src/agrarmonitor/agrarmonitor-polling.service.ts @@ -1,16 +1,15 @@ -// paperless-backend/src/agrarmonitor/agrarmonitor-polling.service.ts import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; import { Cron } from '@nestjs/schedule'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { AgrarmonitorService } from './agrarmonitor.service'; -import { AgrarmonitorWebService } from './agrarmonitor-web.service'; import { PaperlessService } from '../paperless/paperless.service'; import { Setting } from '../database/entities/setting.entity'; import { Client } from '../database/entities/client.entity'; const INTERN_BELEGNUMMER_FIELD_ID = 7; const EINGANGSDATUM_FIELD_ID = 9; +const DOCS_PAGE_SIZE = 500; export interface PollingResult { processed: number; @@ -22,10 +21,10 @@ export interface PollingResult { @Injectable() export class AgrarmonitorPollingService implements OnModuleInit { private readonly logger = new Logger(AgrarmonitorPollingService.name); + private pollingRunning = false; constructor( private readonly agrarmonitorService: AgrarmonitorService, - private readonly webService: AgrarmonitorWebService, private readonly paperlessService: PaperlessService, @InjectRepository(Setting) private readonly settingRepo: Repository, @InjectRepository(Client) private readonly clientRepo: Repository, @@ -60,172 +59,201 @@ export class AgrarmonitorPollingService implements OnModuleInit { } async runPolling(): Promise { + if (this.pollingRunning) { + this.logger.warn('Polling läuft bereits, überspringe'); + return { processed: 0, updated: 0, skipped: 0, errors: ['Polling bereits aktiv'] }; + } + this.pollingRunning = true; + const result: PollingResult = { processed: 0, updated: 0, skipped: 0, errors: [] }; this.logger.log('Starte Agrarmonitor-Polling'); - const [tagFertigSetting, tagVerbuchtSetting] = await Promise.all([ - this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_fertig' }), - this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_verbucht' }), - ]); - const tagFertigId = parseInt(tagFertigSetting?.Wert ?? '4', 10); - const tagVerbuchtId = parseInt(tagVerbuchtSetting?.Wert ?? '9', 10); - - let amClient: Awaited>; try { - amClient = await this.agrarmonitorService.getClient(); - } catch (err: unknown) { - const msg = `Connector-Fehler: ${err instanceof Error ? err.message : 'unbekannt'}`; - this.logger.error(msg); - return { ...result, errors: [msg] }; - } + const [tagFertigSetting, tagVerbuchtSetting] = await Promise.all([ + this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_fertig' }), + this.settingRepo.findOneBy({ Tag: 'agrarmonitor_tag_verbucht' }), + ]); + const tagFertigId = parseInt(tagFertigSetting?.Wert ?? '4', 10); + const tagVerbuchtId = parseInt(tagVerbuchtSetting?.Wert ?? '9', 10); - let customers: Awaited>; - try { - customers = await amClient.fetchCustomers(); - } catch (err: unknown) { - const msg = `Kunden-Abruf fehlgeschlagen: ${err instanceof Error ? err.message : 'unbekannt'}`; - this.logger.error(msg); - return { ...result, errors: [msg] }; - } - - for (const customer of customers.filter( - (c) => Number(c['ist_lieferant']) === 1 && Number(c['ist_aktiv']) === 1, - )) { - const lieferantennummer = (customer['lieferantennummer'] as string) ?? ''; - const searchName = `(${lieferantennummer})`; - const displayName = this.buildCustomerName(customer, lieferantennummer); - const existing = await this.paperlessService.getCorrespondentByName(searchName); - if (!existing) { - await this.paperlessService.addCorrespondent({ - name: displayName, - match: '', - matching_algorithm: 0, - is_insensitive: true, - owner: null, - }); - } - } - - const docsResponse = await this.paperlessService.getDocuments({ - page: 1, - page_size: 9999, - truncate_content: true, - tags__id__all: tagFertigId, - }); - const docs: any[] = docsResponse?.results ?? []; - this.logger.log(`${docs.length} Dokumente fertig in Agrarmonitor`); - - for (const doc of docs) { - result.processed++; - - const interneBelegnummer = - ((doc.custom_fields as any[]) ?? []).find( - (cf: any) => cf.field === INTERN_BELEGNUMMER_FIELD_ID, - )?.value as string ?? ''; - - if (!interneBelegnummer) { - this.logger.log(`Dokument ${doc.id as number} hat keine interne Belegnummer`); - result.skipped++; - await this.delay(500); - continue; - } - - let amResults: Awaited>; - try { - amResults = await this.webService.eingangsrechnungenLivesearch(interneBelegnummer); - } catch (err: unknown) { - const msg = `${interneBelegnummer}: Livesearch-Fehler`; - this.logger.error(`${msg}: ${err instanceof Error ? err.message : err}`); - result.errors.push(msg); - await this.delay(500); - continue; - } - - if (amResults.length === 0) { - this.logger.log(`${interneBelegnummer} nicht in Agrarmonitor gefunden`); - result.skipped++; - await this.delay(500); - continue; - } - - if (amResults.length > 1) { - const msg = `${interneBelegnummer}: Mehrfach gefunden`; + if (isNaN(tagFertigId) || isNaN(tagVerbuchtId)) { + const msg = 'Tag-IDs ungültig (keine Zahlen)'; this.logger.error(msg); - result.errors.push(msg); - await this.delay(500); - continue; + return { ...result, errors: [msg] }; } - const amDoc = amResults[0]; - - if (!amDoc.interneBelegNummer && interneBelegnummer) { - await this.webService.setLieferscheinNummer(amDoc.eingangId, interneBelegnummer); + let amClient: Awaited>; + try { + amClient = await this.agrarmonitorService.getClient(); + } catch (err: unknown) { + const msg = `Connector-Fehler: ${err instanceof Error ? err.message : 'unbekannt'}`; + this.logger.error(msg); + return { ...result, errors: [msg] }; } - if (!amDoc.eingangsDatum) { - const eingangsdatumField = ((doc.custom_fields as any[]) ?? []).find( - (cf: any) => cf.field === EINGANGSDATUM_FIELD_ID, - ); - if (eingangsdatumField?.value) { - const eingangsdatum = new Date(eingangsdatumField.value as string); - if (!isNaN(eingangsdatum.getTime())) { - await this.webService.setEingangsdatum(amDoc.eingangId, eingangsdatum); - this.logger.log(`Eingangsdatum für ${interneBelegnummer} gesetzt`); - } - } - } else if (amDoc.buchungsDatum) { + let customers: Awaited>; + try { + customers = await amClient.fetchCustomers(); + } catch (err: unknown) { + const msg = `Kunden-Abruf fehlgeschlagen: ${err instanceof Error ? err.message : 'unbekannt'}`; + this.logger.error(msg); + return { ...result, errors: [msg] }; + } + + for (const customer of customers.filter( + (c) => Number(c['ist_lieferant']) === 1 && Number(c['ist_aktiv']) === 1, + )) { try { - let correspondentId: number | undefined; - const customer = customers.find((c) => Number(c.id) === amDoc.kundenId); - if (customer) { - const lieferantennummer = (customer['lieferantennummer'] as string) ?? ''; - const searchName = `(${lieferantennummer})`; - const displayName = this.buildCustomerName(customer, lieferantennummer); - let corr = await this.paperlessService.getCorrespondentByName(searchName); - if (!corr) { - corr = await this.paperlessService.addCorrespondent({ - name: displayName, - match: '', - matching_algorithm: 0, - is_insensitive: true, - owner: null, - }); - } - if (corr) correspondentId = corr.id as number; + const lieferantennummer = (customer['lieferantennummer'] as string) ?? ''; + const searchName = `(${lieferantennummer})`; + const displayName = this.buildCustomerName(customer, lieferantennummer); + const existing = await this.paperlessService.getCorrespondentByName(searchName); + if (!existing) { + await this.paperlessService.addCorrespondent({ + name: displayName, + match: '', + matching_algorithm: 0, + is_insensitive: true, + owner: null, + }); } - - let ownerId: number | undefined; - const matchedClient = await this.clientRepo.findOneBy({ - AgrarmonitorBetriebId: amDoc.betriebId, - }); - if (matchedClient) ownerId = matchedClient.PaperlessUserId; - - const currentTags: number[] = (doc.tags as number[]) ?? []; - const newTags = currentTags.filter((t) => t !== tagFertigId).concat([tagVerbuchtId]); - - const updateData: Record = { tags: newTags }; - if (correspondentId !== undefined) updateData.correspondent = correspondentId; - if (ownerId !== undefined) updateData.owner = ownerId; - - await this.paperlessService.updateDocument(doc.id as number, updateData); - this.logger.log(`Beleg ${interneBelegnummer} gebucht`); - result.updated++; } catch (err: unknown) { - const msg = `${interneBelegnummer}: Update-Fehler`; + this.logger.warn(`Korrespondenten-Sync fehlgeschlagen: ${err instanceof Error ? err.message : err}`); + } + } + + const docsResponse = await this.paperlessService.getDocuments({ + page: 1, + page_size: DOCS_PAGE_SIZE, + truncate_content: true, + tags__id__all: tagFertigId, + }); + const docs: any[] = docsResponse?.results ?? []; + if ((docsResponse?.count ?? 0) > DOCS_PAGE_SIZE) { + this.logger.warn(`Mehr als ${DOCS_PAGE_SIZE} Dokumente bereit — nur erste ${DOCS_PAGE_SIZE} werden verarbeitet`); + } + this.logger.log(`${docs.length} Dokumente fertig in Agrarmonitor`); + + for (const doc of docs) { + result.processed++; + + const interneBelegnummer = + ((doc.custom_fields as any[]) ?? []).find( + (cf: any) => cf.field === INTERN_BELEGNUMMER_FIELD_ID, + )?.value as string ?? ''; + + if (!interneBelegnummer) { + this.logger.log(`Dokument ${doc.id as number} hat keine interne Belegnummer`); + result.skipped++; + await this.delay(500); + continue; + } + + let amResults: Awaited>; + try { + amResults = await amClient.eingangsrechnungenLivesearch(interneBelegnummer); + } catch (err: unknown) { + const msg = `${interneBelegnummer}: Livesearch-Fehler`; this.logger.error(`${msg}: ${err instanceof Error ? err.message : err}`); result.errors.push(msg); + await this.delay(500); + continue; } - } else { - result.skipped++; + + if (amResults.length === 0) { + this.logger.log(`${interneBelegnummer} nicht in Agrarmonitor gefunden`); + result.skipped++; + await this.delay(500); + continue; + } + + if (amResults.length > 1) { + const msg = `${interneBelegnummer}: Mehrfach gefunden`; + this.logger.error(msg); + result.errors.push(msg); + await this.delay(500); + continue; + } + + const amDoc = amResults[0]; + + if (!amDoc.interneBelegNummer && interneBelegnummer) { + try { + await amClient.setLieferscheinNummer(amDoc.eingangId, interneBelegnummer); + } catch (err: unknown) { + this.logger.warn(`${interneBelegnummer}: Lieferscheinnummer setzen fehlgeschlagen: ${err instanceof Error ? err.message : err}`); + } + } + + if (!amDoc.eingangsDatum) { + const eingangsdatumField = ((doc.custom_fields as any[]) ?? []).find( + (cf: any) => cf.field === EINGANGSDATUM_FIELD_ID, + ); + if (eingangsdatumField?.value) { + const eingangsdatum = new Date(eingangsdatumField.value as string); + if (!isNaN(eingangsdatum.getTime())) { + await amClient.setEingangsdatum(amDoc.eingangId, eingangsdatum); + this.logger.log(`Eingangsdatum für ${interneBelegnummer} gesetzt`); + } + } + result.skipped++; + } else if (amDoc.buchungsDatum) { + try { + let correspondentId: number | undefined; + const customer = customers.find((c) => Number(c.id) === amDoc.kundenId); + if (customer) { + const lieferantennummer = (customer['lieferantennummer'] as string) ?? ''; + const searchName = `(${lieferantennummer})`; + const displayName = this.buildCustomerName(customer, lieferantennummer); + let corr = await this.paperlessService.getCorrespondentByName(searchName); + if (!corr) { + corr = await this.paperlessService.addCorrespondent({ + name: displayName, + match: '', + matching_algorithm: 0, + is_insensitive: true, + owner: null, + }); + } + if (corr) correspondentId = corr.id as number; + } + + let ownerId: number | undefined; + const matchedClient = await this.clientRepo.findOneBy({ + AgrarmonitorBetriebId: amDoc.betriebId, + }); + if (matchedClient) ownerId = matchedClient.PaperlessUserId; + + const currentTags: number[] = (doc.tags as number[]) ?? []; + const newTags = [...new Set(currentTags.filter((t) => t !== tagFertigId).concat([tagVerbuchtId]))]; + + const updateData: Record = { tags: newTags }; + if (correspondentId !== undefined) updateData.correspondent = correspondentId; + if (ownerId !== undefined) updateData.owner = ownerId; + + await this.paperlessService.updateDocument(doc.id as number, updateData); + this.logger.log(`Beleg ${interneBelegnummer} gebucht`); + result.updated++; + } catch (err: unknown) { + const msg = `${interneBelegnummer}: Update-Fehler`; + this.logger.error(`${msg}: ${err instanceof Error ? err.message : err}`); + result.errors.push(msg); + } + } else { + result.skipped++; + } + + await this.delay(500); } - await this.delay(500); + this.logger.log( + `Polling abgeschlossen: ${result.processed} verarbeitet, ${result.updated} aktualisiert, ` + + `${result.skipped} übersprungen, ${result.errors.length} Fehler`, + ); + } finally { + this.pollingRunning = false; } - this.logger.log( - `Polling abgeschlossen: ${result.processed} verarbeitet, ${result.updated} aktualisiert, ` + - `${result.skipped} übersprungen, ${result.errors.length} Fehler`, - ); return result; } diff --git a/paperless-backend/src/agrarmonitor/agrarmonitor-web.service.ts b/paperless-backend/src/agrarmonitor/agrarmonitor-web.service.ts deleted file mode 100644 index da50743..0000000 --- a/paperless-backend/src/agrarmonitor/agrarmonitor-web.service.ts +++ /dev/null @@ -1,207 +0,0 @@ -// paperless-backend/src/agrarmonitor/agrarmonitor-web.service.ts -import { Injectable, Logger } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { parse } from 'node-html-parser'; -import { AgrarmonitorService } from './agrarmonitor.service'; - -export interface EingangsrechnungEntry { - eingangId: number; - belegNummer: string; - interneBelegNummer: string; - kundenId: number; - betriebId: number; - buchungsDatum: Date | null; - eingangsDatum: Date | null; -} - -@Injectable() -export class AgrarmonitorWebService { - private readonly logger = new Logger(AgrarmonitorWebService.name); - private readonly baseUrl: string; - - constructor( - private readonly agrarmonitorService: AgrarmonitorService, - private readonly configService: ConfigService, - ) { - this.baseUrl = this.configService.get( - 'AGRARMONITOR_BASE_URL', - 'https://admin7.agrarmonitor.de', - ); - } - - async eingangsrechnungenLivesearch(suchstring: string): Promise { - const client = await this.agrarmonitorService.getClient(); - - try { - await client.http.get('/'); - } catch (err: any) { - this.logger.warn(`Session-Refresh fehlgeschlagen: ${err?.message}`); - } - const searchUrl = - `/module/dateien/livesearch.php?suchstring=${encodeURIComponent(suchstring)}` + - `&stammdatum_typ=-1&mobil=-1&sensibel=-1&firma=0&itemsperpage=100000&seite=1`; - const { data: html } = await client.http.get(searchUrl, { responseType: 'text' }); - - const root = parse('' + html + ''); - const table = root.querySelector('table#dateien'); - if (!table) return []; - - const rows = table.querySelectorAll('tbody tr'); - const results: EingangsrechnungEntry[] = []; - - for (const row of rows) { - const tds = row.querySelectorAll('td'); - if (tds.length < 4) continue; - if (!tds[3].text.trim().startsWith('Eingangsrechnungen')) continue; - - const linkEl = tds[3].querySelector('a'); - if (!linkEl) continue; - - const href = linkEl.getAttribute('href') ?? ''; - const eingangId = parseInt(href.split('/').pop() ?? '0', 10); - if (!eingangId) continue; - - const belegText = linkEl.text; - const belegParts = belegText.split(','); - const belegNummer = belegParts[0]?.trim() ?? ''; - - const { data: editHtml } = await client.http.get( - `/module/eingangsrechnungen/api/eingangsrechnungen.php?id=edit&rechnungId=${eingangId}`, - { responseType: 'text' }, - ); - const editRoot = parse(editHtml); - - const interneBelegNummer = - editRoot.querySelector('input[name="lieferscheinnummer"]')?.getAttribute('value') ?? ''; - const kundenId = parseInt( - editRoot - .querySelector('select[name="rgempf"] option[selected]') - ?.getAttribute('value') ?? '0', - 10, - ); - const betriebId = parseInt( - editRoot - .querySelector('select[name="firma_id"] option[selected]') - ?.getAttribute('value') ?? '0', - 10, - ); - - const { data: detailHtml } = await client.http.get( - `/eingangsrechnungen/detail/${eingangId}`, - { responseType: 'text' }, - ); - const detailRoot = parse(detailHtml); - const receivedEl = detailRoot.getElementById('receivedStatus'); - - let eingangsDatum: Date | null = null; - let buchungsDatum: Date | null = null; - - if (receivedEl) { - const eingangsText = receivedEl.text.trim(); - const eingangsMatch = eingangsText.match(/Empfangen am (\d{2}\.\d{2}\.\d{2,4})/); - if (eingangsMatch) eingangsDatum = this.parseGermanDate(eingangsMatch[1]); - - const parentText = receivedEl.parentNode?.text ?? ''; - const buchenMatch = parentText.match(/Gebucht am (\d{2}\.\d{2}\.\d{2,4})/); - if (buchenMatch) buchungsDatum = this.parseGermanDate(buchenMatch[1]); - } - - results.push({ eingangId, belegNummer, interneBelegNummer, kundenId, betriebId, buchungsDatum, eingangsDatum }); - } - - return results; - } - - async setEingangsdatum(eingangId: number, datum: Date): Promise { - const client = await this.agrarmonitorService.getClient(); - const params = new URLSearchParams(); - params.append('datum', this.formatGermanDate(datum)); - params.append('receiptID', String(eingangId)); - try { - const res = await client.http.post( - '/module/eingangsrechnungen/api/updateReceived.php', - params.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Referer: `${this.baseUrl}/eingangsrechnungen/detail/${eingangId}`, - Origin: this.baseUrl, - }, - }, - ); - return res.status < 400; - } catch (err: any) { - this.logger.error(`setEingangsdatum(${eingangId}) Fehler: ${err?.message}`); - return false; - } - } - - async setLieferscheinNummer(eingangId: number, lieferscheinNummer: string): Promise { - const client = await this.agrarmonitorService.getClient(); - try { - const { data: editHtml } = await client.http.get( - `/module/eingangsrechnungen/api/eingangsrechnungen.php?id=edit&rechnungId=${eingangId}`, - { responseType: 'text' }, - ); - const editRoot = parse(editHtml); - - const rechnungsnummer = - editRoot.querySelector('input[name="rechnungsnummer"]')?.getAttribute('value') ?? ''; - const rechnungsdatum = - editRoot.querySelector('input[name="rechnungsdatum"]')?.getAttribute('value') ?? ''; - const rgempf = - editRoot - .querySelector('select[name="rgempf"] option[selected]') - ?.getAttribute('value') ?? ''; - const addressName = - editRoot.querySelector('input[name="addressName"]')?.getAttribute('value') ?? ''; - - if (!rgempf) { - this.logger.warn(`setLieferscheinNummer(${eingangId}): kein Empfänger im Formular, übersprungen`); - return false; - } - - const params = new URLSearchParams(); - params.append('lieferscheinnummer', lieferscheinNummer); - params.append('rechnungsnummer', rechnungsnummer); - params.append('rechnungsdatum', rechnungsdatum); - params.append('rgempf', rgempf); - params.append('adresstext', addressName); - - const res = await client.http.post( - `/module/eingangsrechnungen/api/eingangsrechnungen.php?id=update&rechnungId=${eingangId}`, - params.toString(), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Referer: `${this.baseUrl}/eingangsrechnungen/detail/${eingangId}`, - Origin: this.baseUrl, - }, - }, - ); - return res.status < 400; - } catch (err: any) { - this.logger.error(`setLieferscheinNummer(${eingangId}) Fehler: ${err?.message}`); - return false; - } - } - - private parseGermanDate(str: string): Date | null { - const parts = str.trim().split('.'); - if (parts.length !== 3) return null; - const [dd, mm, yy] = parts; - const yearRaw = parseInt(yy, 10); - const year = yy.length === 2 - ? (yearRaw < 50 ? 2000 + yearRaw : 1900 + yearRaw) - : yearRaw; - const d = new Date(year, parseInt(mm, 10) - 1, parseInt(dd, 10)); - return isNaN(d.getTime()) ? null : d; - } - - private formatGermanDate(date: Date): string { - const dd = String(date.getDate()).padStart(2, '0'); - const mm = String(date.getMonth() + 1).padStart(2, '0'); - const yy = String(date.getFullYear()).slice(-2); - return `${dd}.${mm}.${yy}`; - } -} diff --git a/paperless-backend/src/agrarmonitor/agrarmonitor.module.ts b/paperless-backend/src/agrarmonitor/agrarmonitor.module.ts index ec9a75e..f805cfe 100644 --- a/paperless-backend/src/agrarmonitor/agrarmonitor.module.ts +++ b/paperless-backend/src/agrarmonitor/agrarmonitor.module.ts @@ -1,9 +1,18 @@ import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; import { AgrarmonitorService } from './agrarmonitor.service'; +import { AgrarmonitorPollingService } from './agrarmonitor-polling.service'; import { AgrarmonitorController } from './agrarmonitor.controller'; +import { PaperlessModule } from '../paperless/paperless.module'; +import { Setting } from '../database/entities/setting.entity'; +import { Client } from '../database/entities/client.entity'; @Module({ - providers: [AgrarmonitorService], + imports: [ + TypeOrmModule.forFeature([Setting, Client]), + PaperlessModule, + ], + providers: [AgrarmonitorService, AgrarmonitorPollingService], controllers: [AgrarmonitorController], exports: [AgrarmonitorService], })