assert(!$svc->isEnabled(), 'ki_vorschlag_aktiv sollte per Default 0 sein'); // 2. saveKiVorschlag speichert und liest korrekt zurück $belegId = $this->createTestBeleg(); $vorschlag = [ 'buchungstyp' => 'Ausgabe', 'kategorie_konto_id' => 42, 'zahlungskonto_id' => 10, 'ust_satz' => 19, 'betrag_netto' => 33.64, 'buchungstext' => 'Testrechnung', ]; Beleg::saveKiVorschlag($belegId, $vorschlag); $beleg = Beleg::find($belegId); $this->assertNotEmpty($beleg['ki_vorschlag'], 'ki_vorschlag sollte gespeichert sein'); $this->assertNotEmpty($beleg['ki_vorschlag_at'], 'ki_vorschlag_at sollte gesetzt sein'); $decoded = json_decode($beleg['ki_vorschlag'], true); $this->assertEquals('Ausgabe', $decoded['buchungstyp'], 'buchungstyp korrekt gespeichert'); $this->assertEquals(19, $decoded['ust_satz'], 'ust_satz korrekt gespeichert'); $this->assertEquals(33.64, $decoded['betrag_netto'], 'betrag_netto korrekt gespeichert'); // 3. saveKiVorschlag(null) löscht den Vorschlag Beleg::saveKiVorschlag($belegId, null); $beleg = Beleg::find($belegId); $this->assert($beleg['ki_vorschlag'] === null, 'ki_vorschlag sollte nach Reset null sein'); // 4. JSON-Extraktion: Prosa vor JSON wird korrekt extrahiert $this->testJsonExtraktion(); } private function createTestBeleg(): int { Database::execute( "INSERT INTO belege (beleg_nr, filename, storage_path, mime_type, file_hash, belegtyp) VALUES ('TEST-KI-0001', 'test.pdf', 'static/uploads/test/test.pdf', 'application/pdf', SHA2('test', 256), 'Eingangsrechnung')" ); return (int)Database::lastInsertId(); } private function testJsonExtraktion(): void { // Simuliert die Regex-Extraktion aus einer LLM-Antwort mit vorangehendem Text $antwort = "Hier ist mein Kontierungsvorschlag:\n{\"buchungstyp\":\"Ausgabe\",\"kategorie_konto_id\":5,\"zahlungskonto_id\":2,\"ust_satz\":19,\"betrag_netto\":100.00,\"buchungstext\":\"Test\"}"; preg_match('/\{[^{}]*\}/s', $antwort, $m); $data = json_decode($m[0] ?? '{}', true); $this->assertEquals('Ausgabe', $data['buchungstyp'] ?? '', 'JSON-Extraktion aus LLM-Antwort mit Prosa'); $this->assertEquals(19, $data['ust_satz'] ?? -1, 'ust_satz aus extrahiertem JSON'); // Antwort ohne JSON preg_match('/\{[^{}]*\}/s', 'Keine Angabe möglich.', $m2); $this->assert(empty($m2), 'Keine JSON-Extraktion ohne JSON-Block'); } }