Udbyderen vælges af OCR_PROVIDER (indstilling.env.ocr-provider), hvis eneste registrerede værdi er anthropic. Fabrikken afviser et navn, der ikke er registreret, og en udbyder, der er slået fra med OCR_ANTHROPIC_ENABLED. Config-kommentaren oplyser, at elleve kandidater blev målt på samme testsæt, og at ingen kom i nærheden. Det er en intern måling, som ikke er gentaget her.
Modellen findes sådan. AnthropicOcrProvider tager OCR_MODEL og sætter den som services.anthropic.model under selve kaldet. Er den tom, bruges ANTHROPIC_MODEL. Kaldet sendes med tænkning slået fra og et svarloft på regel.ocr.max-output-tokens.
Vejen fra fil til model er:
- Tekstlag først. En PDF læses med en PDF-parser. Teksten bruges, hvis den har mindst regel.ocr.min-tegn tegn, en tæthed på mindst regel.ocr.min-taethed tegn pr. byte og højst regel.ocr.stoey i ulæselige tegn. Ellers sendes siden som billede.
- Ét ekstra billedpas efter en tekstlæsning, hvis leverandøren mangler, eller hvis linjernes beløb ikke kan gå op.
- Tidsbudgettet regel.ocr.tidsbudget deles af alle kald. Er der under regel.ocr.min-resttid tilbage, springes næste kald over.
- Smart routing, kun hvis den er slået til. Den er beskrevet nedenfor.
| Rute | Hvornår | Model | Eskalering |
|---|
| Billig | PDF med brugbart tekstlag | indstilling.env.ocr-cheap-model, ellers grading-modellen | Højst én gang til hovedmodellen |
| Hoved | Billeder og PDF uden tekstlag | OCR_MODEL eller ANTHROPIC_MODEL | Ingen |
Eskaleringen sker ved ufuldstændigt svar, manglende beløb, manglende leverandør, manglende FI-kort hvis indstilling.env.ocr-escalate-missing-fi er til, eller en konfidens over 0 under gulvet. Gulvet er regel.ocr.eskaler-konfidens. Har leverandøren mindst regel.ocr.baseline-leverandoer tidligere læsninger med samme model, eller selskabet mindst regel.ocr.baseline-firma, er gulvet i stedet medianen af de seneste læsninger minus regel.ocr.eskaler-median. Et tredje pas findes ikke, fordi regel.ocr.max-pas er 2. Hvilken model der vandt og hvorfor, ligger i ocr_passes på bilaget.
kode: app/Services/Ap/Providers/AnthropicOcrProvider.php · AnthropicOcrProvider::withPinnedModelkode: app/Services/Ap/OcrSmartRouter.php · OcrSmartRouter::extractUsingkode: config/ocr.php · ocr.default
| Felt | Hvad det er | Bruges til |
|---|
document_class | receipt eller supplier_invoice | Forslag om Flyt til Regninger, se begreb.ocr.dokumentklasse |
supplier_name, supplier_cvr | Sælgeren | Leverandør-opløsning |
supplier_address, _city, _zip, _country, _phone, _email | Sælgerens kontaktdata | Udfylder leverandøren. Landet styrer standardsatsen. |
supplier_bank_reg, _bank_account, _iban, _swift | Betalingsoplysninger | Udfylder leverandøren. Kun når identiteten er til at stole på. |
fi_kortart, fi_creditor_id, fi_payment_id | FI-kort | Regninger. Kan udløse eskalering. |
recipient_name, recipient_cvr | Køberen | Hindrer, at en regning til en anden oprettes |
invoice_number | Dokumentnummer | Dubletdetektion |
payment_terms, payment_marks, merchant_category | Beviser for kvittering eller regning | Dokumentklassen |
posting_text | Bogføringstekst | Bilagets beskrivelse |
amount, vat_amount, total_amount | Netto, moms og total i kroner | Bilagets beløb i øre |
issue_date, due_date | Datoer | Bilagsdato og forfald. Er en banklinje koblet, vinder dens dato. |
currency | Valuta | Bilagets valuta og omregning |
confidence | Hvor godt den blev læst | Auto-bogføring og eskalering |
line_items | Linjer med tekst, antal, stykpris, sats og beløb | Konsolideringen |
Dokumentklassen er et første bud fra samme kald. Kun den præcise værdi supplier_invoice betyder noget, og et ukendt svar behandles som kvittering. Mailens klassifikator ser kun beviserne og kan også svare credit_note. Resultatet gemmes helt i ocr_raw, og de felter, der ikke kunne læses som tal, gemmes i unparsed_numeric_fields.
Beløbene kommer fra modellen som decimaltal i hele kroner. OcrNumber::parse gør teksten til tal, og VoucherPopulator::populate gør hvert beløb til øre med MinorUnits::toMinor i bilagets valuta. Funktionen kalder Money::fromMajor, som regner med bcmath og runder halv op, og som kender valutaer uden to decimaler. Derfor står vat_amount i øre på bilaget.
Linjerne bygges af VoucherLineConsolidator. Bilagets egne tal er facit. Nævner det en moms, er nettoen total minus moms, og ingen sats, modellen har gættet pr. linje, flytter den. Reglerne er:
Momsmotoren bag VAT_ENGINE_* er ikke i brug. indstilling.env.vat-engine-enabled er fra, indstilling.env.vat-engine-shadow-mode er til, og klassifikatoren, der skulle skrive afgørelserne, er fjernet. Se faelde.moms.momsmotoren-holder-intet. En afgift læst som moms er rettet i prompten og ikke i koden, se faelde.moms.afgift-laest-som-moms.
kode: app/Services/Ap/VoucherLineConsolidator.php · VoucherLineConsolidator::consolidatekode: app/Services/Ap/MixedRateProductSplitter.php · MixedRateProductSplitter::splitkode: app/Services/Money/MinorUnits.php · MinorUnits::toMinorsupplier_resolution siger, på hvilket trin leverandøren blev fundet. Rækkefølgen er fast og stopper ved første træf. Ingen af trinnene finder eller opretter selskabet selv.
| Værdi | Trin | Hvad det betyder |
|---|
cvr | 1 | CVR matcher, også med DK foran |
override | 2 | En tidligere rettelse eller et lært alias, eller en lært afsender når navn mangler |
exact | 3 | Normaliseret navn, eller uden accenter og mellemrum |
fuzzy | 4 | Mindst regel.leverandoer.fuzzy-lighed procents lighed |
created | 5 | En ny leverandør er oprettet |
none | | Hverken navn eller CVR, eller kun selskabet selv. Det er målet for, at scanningen ikke fandt leverandøren. |
Fuld beskrivelse i tilstand.voucher.supplier-resolution. Værdien læses aldrig af matching eller bogføring.
kode: app/Services/Ap/SupplierMatchingService.php · SupplierMatchingService::resolveForslagene skrives af AccountSuggestionService og lægges på linjerne af AccountSuggestionRevisor.
Historik først. Et bilag med én linje svares af AccountHistoryMatcher før en AI-model spørges. Den slår leverandørens tekst op i tre kilder, i den rækkefølge: bogføringsspejlet, bilag bogført i PayFriend og konterede regningslinjer. Kontoen skal have mindst regel.kontoforslag.historik-min-antal træf og mindst regel.kontoforslag.historik-min-andel af historikken. Konfidensen skaleres fra regel.kontoforslag.historik-konfidens-bund til regel.kontoforslag.historik-konfidens-loft, så den altid består gaten. Et bilag med flere linjer kontoføres pr. linje og går til Claude, som får leverandørens fordeling som bevis.
Konfidensgaten. En konto lægges kun på linjen, når konfidensen er mindst regel.kontoforslag.min-confidence. Ellers gemmes den som forslag. Samme tal bruges, når en bruger trykker AI Bogføring: BookVoucherJob spørger efter en konto og bruger den kun, hvis den når gaten.
Momskode. suggested_vat_code lægges på linjen i den her rækkefølge:
- En sats på 0 eller derunder giver ingen kode, så et gebyr ikke arver 25 procent.
- En udenlandsk leverandør får omvendt betaling, uanset kontoens standard.
- Har kontoen en synkroniseret momskode, foreslås der ingen. Kontoens egen kode vinder.
- Historik for leverandøren: mindst regel.kontoforslag.moms-min-antal linjer og mindst regel.kontoforslag.moms-min-andel med samme kode.
- Claudes egen kode, hvis den findes i selskabets momskatalog.
Når bilaget bogføres, vælges koden i en anden rækkefølge: linjens egen, kontoens, den eneste købskode for den bekræftede sats, og først til sidst forslaget. Se faelde.kontering.suggested-vat-code.
Hvem udløser job.suggest-voucher-accounts:
| Udløser | Hvornår |
|---|
VoucherPopulator::populate med udskudt forslag | Når et bilag laves af en mailvedhæftning, og når linjer genopbygges |
VoucherAccountRevisionObserver | Når en leverandør lander på et bilag, der blev konteret uden |
AccountSuggestionRevisor::queue | Efter en bankmatch-handling, så et nyt forslag kan afløse det gamle |
| Knappen AI foreslå konti | Én gang pr. tryk for de nyeste bilag uden konto |
Scanningen af et bilag kører forslaget direkte i samme job og ikke i en separat kø.
Knappen AI foreslå konti tjekker først, om selskabet har tokens tilbage (indstilling.company.ai-monthly-token-budget, målt af AiUsageTracker). Så lægges højst regel.kontoforslag.sweep-loft jobs i kø for de nyeste afventende bilag uden konto, og listen genindlæses hvert tredje sekund i op til tre minutter. Budgettet tjekkes kun her, og et tomt budget er ubegrænset. Se faelde.kontoforslag.ai-budget-staar-kun-ved-knappen.
kode: app/Services/Ap/AccountSuggestionService.php · AccountSuggestionService::suggestForVoucherLineskode: app/Services/Ap/AccountHistoryMatcher.php · AccountHistoryMatcher::suggestkode: app/Services/Ap/VoucherLineAccountSuggester.php · VoucherLineAccountSuggester::bulkSuggestAccounts