feat: add AgrarmonitorWebService with livesearch and date setters
This commit is contained in:
@@ -0,0 +1,199 @@
|
|||||||
|
// 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<string>(
|
||||||
|
'AGRARMONITOR_BASE_URL',
|
||||||
|
'https://admin7.agrarmonitor.de',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async eingangsrechnungenLivesearch(suchstring: string): Promise<EingangsrechnungEntry[]> {
|
||||||
|
const client = await this.agrarmonitorService.getClient();
|
||||||
|
|
||||||
|
await client.http.get('/');
|
||||||
|
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<string>(searchUrl, { responseType: 'text' });
|
||||||
|
|
||||||
|
const root = parse('<doc>' + html + '</doc>');
|
||||||
|
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<string>(
|
||||||
|
`/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<string>(
|
||||||
|
`/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();
|
||||||
|
if (eingangsText !== 'Nicht empfangen' && eingangsText.length > 13) {
|
||||||
|
eingangsDatum = this.parseGermanDate(eingangsText.substring(13));
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentText = receivedEl.parentNode?.text ?? '';
|
||||||
|
const dashIdx = parentText.lastIndexOf('-');
|
||||||
|
const buchenText = dashIdx >= 0 ? parentText.substring(dashIdx + 1).trim() : '';
|
||||||
|
if (buchenText !== 'Nicht gebucht' && buchenText.length > 11) {
|
||||||
|
buchungsDatum = this.parseGermanDate(buchenText.substring(11));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results.push({ eingangId, belegNummer, interneBelegNummer, kundenId, betriebId, buchungsDatum, eingangsDatum });
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setEingangsdatum(eingangId: number, _belegNummer: string, datum: Date): Promise<boolean> {
|
||||||
|
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<boolean> {
|
||||||
|
const client = await this.agrarmonitorService.getClient();
|
||||||
|
const { data: editHtml } = await client.http.get<string>(
|
||||||
|
`/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') ?? '';
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.append('lieferscheinnummer', lieferscheinNummer);
|
||||||
|
params.append('rechnungsnummer', rechnungsnummer);
|
||||||
|
params.append('rechnungsdatum', rechnungsdatum);
|
||||||
|
params.append('rgempf', rgempf);
|
||||||
|
params.append('adresstext', addressName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
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 year = parseInt(yy, 10) < 50 ? 2000 + parseInt(yy, 10) : 1900 + parseInt(yy, 10);
|
||||||
|
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}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user