Files
paperlessmanager/paperless-backend/src/agrarmonitor/lieferantennummern.service.spec.ts
T
bjoernpoettkerandClaude Opus 5.5 3d4125b29a
Build and Push Multi-Platform Images / build-and-push (push) Successful in 1m1s
feat(agrarmonitor): fehlende Lieferantennummern vergeben
Neuer Bereich „Lieferantennummern überprüfen“ in den Agrarmonitor-
Einstellungen. Er vergibt allen aktiven Lieferanten ohne
Lieferantennummer eine fortlaufende Nummer im Schema 1555xxxx, ab der
höchsten vergebenen Nummer (Inaktive zählen mit, Altlasten werden
ignoriert).

- Vorschau „Lieferant → neue Nummer“, geschrieben wird erst nach
  Bestätigung; Einzelmodus zum Testen an einem Lieferanten
- formData wird wie in der Weboberfläche aus #form_detail aufgebaut,
  nur die Lieferantennummer ändert sich
- Dublettencheck über vorhanden.php, Erfolgskontrolle durch Neuladen
  des Details, Abbruch beim ersten Fehler
- Lauf im Hintergrund mit Status-Abfrage (wegen Proxy-Timeouts),
  Listenfilter werden danach zurückgesetzt
- HTML-Parsing mit cheerio, Unit-Tests für Parser und Vergabe-Ablauf

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 08:51:13 +02:00

152 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { AxiosInstance } from 'axios';
import type { AgrarmonitorService } from './agrarmonitor.service';
import {
LieferantennummernService,
type LieferantennummerLaufErgebnis,
} from './lieferantennummern.service';
import type { ListenLieferant } from './lieferantennummern.parser';
// Der echte Connector lädt jsdom (ESM), das Jest nicht verarbeiten kann – hier wird er nicht gebraucht.
jest.mock('agrarmonitor-connector', () => ({}));
interface UpdateBody {
kundeID: number;
formData: Record<string, unknown>;
}
function detail(nummer: string): string {
return (
'<form id="form_detail">' +
'<input type="text" name="kundennummer" id="kundennummer" value="21207">' +
`<input type="text" name="lieferantennummer" id="lieferantennummer" value="${nummer}">` +
'<input type="text" name="firma" value="Firma">' +
'<input type="checkbox" name="ist_kunde" value="1">' +
'<input type="checkbox" name="ist_lieferant" value="1" checked>' +
'</form>'
);
}
function kandidat(kundeId: number, firma: string): ListenLieferant {
return { kundeId, lieferantennummer: '', firma, name: '', aktiv: true };
}
function neuerLauf(gesamt: number): LieferantennummerLaufErgebnis {
return {
modus: 'alle',
gestartetAm: '',
beendetAm: null,
gesamt,
vergeben: 0,
uebersprungen: 0,
zuPruefen: 0,
fehler: 0,
abgebrochen: false,
eintraege: [],
};
}
/**
* Simuliert Agrarmonitor: speichert Nummern aus updateKunde.php pro Kunde,
* vorhanden.php meldet für die Ids in `dubletten` ähnliche Kontakte.
*/
function fakeHttp(opts: {
nummern: Record<number, string>;
dubletten?: number[];
speichernIgnorieren?: number[];
}) {
const aufrufe: string[] = [];
const updates: UpdateBody[] = [];
const http = {
get: jest.fn((url: string) => {
aufrufe.push(`GET ${url}`);
const m = /\/kunden\/detail\/(\d+)/.exec(url);
if (m) {
return Promise.resolve({
data: detail(opts.nummern[Number(m[1])] ?? ''),
});
}
return Promise.resolve({ data: '' });
}),
post: jest.fn((url: string, body: string | UpdateBody) => {
aufrufe.push(`POST ${url}`);
if (typeof body === 'string') {
const id = Number(new URLSearchParams(body).get('customerID'));
return Promise.resolve({
data: opts.dubletten?.includes(id) ? '<div>Ähnlich</div>' : '',
});
}
updates.push(body);
if (!opts.speichernIgnorieren?.includes(body.kundeID)) {
opts.nummern[body.kundeID] = String(body.formData.lieferantennummer);
}
return Promise.resolve({ data: '/kunden/detail/' + body.kundeID });
}),
};
return { http: http as unknown as AxiosInstance, aufrufe, updates };
}
function service() {
const agrarmonitor = { clearClient: jest.fn() };
return new LieferantennummernService(
agrarmonitor as unknown as AgrarmonitorService,
);
}
describe('LieferantennummernService.vergeben', () => {
it('vergibt fortlaufend, überspringt Vorhandene und verbraucht bei Dubletten keine Nummer', async () => {
const { http, updates, aufrufe } = fakeHttp({
nummern: { 2: '15550010' },
dubletten: [3],
});
const lauf = neuerLauf(4);
await service().vergeben(
http,
[kandidat(1, 'A'), kandidat(2, 'B'), kandidat(3, 'C'), kandidat(4, 'D')],
15550706,
lauf,
);
expect(lauf.eintraege.map((e) => [e.kundeId, e.status, e.nummer])).toEqual([
[1, 'vergeben', '15550706'],
[2, 'uebersprungen', undefined],
[3, 'pruefen', undefined],
[4, 'vergeben', '15550707'],
]);
expect(lauf).toMatchObject({
vergeben: 2,
uebersprungen: 1,
zuPruefen: 1,
fehler: 0,
abgebrochen: false,
});
expect(updates[0].kundeID).toBe(1);
expect(updates[0].formData).toMatchObject({
kundennummer: '21207',
lieferantennummer: '15550706',
ist_kunde: '',
ist_lieferant: '1',
});
expect(aufrufe[aufrufe.length - 1]).toContain('itemsperpage=25');
expect(lauf.beendetAm).not.toBeNull();
});
it('bricht ab, wenn die Nummer nach dem Speichern nicht übernommen wurde', async () => {
const { http, updates, aufrufe } = fakeHttp({
nummern: {},
speichernIgnorieren: [1],
});
const lauf = neuerLauf(2);
await service().vergeben(
http,
[kandidat(1, 'A'), kandidat(2, 'B')],
15550706,
lauf,
);
expect(updates).toHaveLength(1);
expect(lauf).toMatchObject({ vergeben: 0, fehler: 1, abgebrochen: true });
expect(lauf.eintraege[0].hinweis).toContain('15550706');
expect(aufrufe[aufrufe.length - 1]).toContain('itemsperpage=25');
});
});