Gå til indhold

Verificeret mod app@9d146f9, 16 skal genverificeres, 0 brudt

OCR og kontering

Nordstjernen

domaene.ocr-og-kontering

Faste regler 60

Vis alle 60

Eksterne services 5

  • AnthropicOCR af bilag, filter-AI på indgående mail og kontoforslag på bilag og leverandørfakturaer.
  • BillyBogføring, spejling af posteringer og kortet over Billy-vedhæftninger.
  • Business CentralBogføring, spejling af finansposter, bilagskort og fornyelse af change-notification-abonnementer.
  • DineroBogføring, spejling af poster og kortet fra bilagsnummer til fil.
  • e-conomicBogføring af bilag, spejling af poster, kontoplan, kassekladde-kladder og dokumentkortet economic_documents.
0

På 30 sekunder

For alle

Et bilag er en fil, som en kunde har uploadet eller fået med en mail. OCR og kontering er det, der gør filen til et bilag, man kan forstå og bogføre.

Uden at nogen trykker på noget sker følgende. En AI-model læser filen og finder leverandør, dato, beløb, moms og linjer. PayFriend finder leverandøren blandt de kendte, gør linjerne om til linjer, der kan bogføres, og foreslår en konto til hver linje. Derefter leder den efter dubletter og prøver at koble bilaget til en banklinje. Hvis selskabet har slået auto-bogføring til, og alt passer, bogføres bilaget uden et menneske.

Domænet dækker også to ting, der opstår, når der ikke er et bilag. Mangler bilag er posteringer i regnskabet, hvor en kvittering burde ligge. Tabt kvittering er en PDF, PayFriend selv laver, når kvitteringen er væk.

Hvem bruger det: bogholdere og ejere under Kvitteringer. Selve scanningen er ikke noget, de ser, andet end at en række først shimmer og så fyldes ud.

Hvorfor står en kvittering som "Scanning fejlet", og hvad gør kunden så?

Sektion kaldt “Hvorfor står en kvittering som "Scanning fejlet", og hvad gør kunden så?”

Status scan_failed (se tilstand.voucher.scan-forloeb) betyder, at udlæsningen ikke gav et resultat, vi turde bruge. Det sker i fire tilfælde:

Kunden har fire veje, og de to første er de almindelige:

  1. Trykke Scan igen i rækkemenuen eller i bilagets skuffe. Læser scanningen noget (konfidens over 0), går bilaget tilbage til Afventer.
  2. Rette leverandør, beløb og linjer i skuffen og bogføre selv. scan_failed kan bogføres.
  3. Vedhæfte en bedre fil, hvis den første var utydelig.
  4. Slette bilaget.

Et bogført, tilbageført eller konverteret bilag og et erstatningsbilag kan ikke scannes igen. Står rækken i stedet ved med at vise "AI scanner", er det en anden fejl: faelde.ocr.undtagelse-giver-evig-scanning.

Forslaget kommer fra ét af to steder, og det sted, der kan svare med sikkerhed, spørges først.

  1. Historik. For et bilag med én linje slår PayFriend leverandørens tekst op i selskabets egen historik: bogføringsspejlet, bilag bogført i PayFriend og konterede regningslinjer. Har mindst to posteringer (regel.kontoforslag.historik-min-antal) og mindst 70 procent (regel.kontoforslag.historik-min-andel) peget på samme konto, er det forslaget. Der spørges ingen AI. Konfidensen ligger mellem regel.kontoforslag.historik-konfidens-bund og regel.kontoforslag.historik-konfidens-loft.
  2. AI. Hvis historikken er tynd eller delt, eller bilaget har flere linjer, spørger PayFriend Claude med kontoplanen, historikken og leverandørens fordeling som bevis.

Forslaget sættes først på linjen som konto, når konfidensen er mindst 0,80 (regel.kontoforslag.min-confidence). Under det vises det kun som forslag. Begrundelsen står som tooltip på Konto-kolonnen, fx hvor mange af de tidligere posteringer der pegede på kontoen. Et forslag ændrer aldrig en konto, som et menneske har valgt. Kommer leverandøren først bagefter, lægges et nyt forslag som revideret forslag. Se job.suggest-voucher-accounts.

Hvorfor står en postering under Mangler bilag, når kunden har uploadet bilaget i e-conomic?

Sektion kaldt “Hvorfor står en postering under Mangler bilag, når kunden har uploadet bilaget i e-conomic?”

Mangler bilag spørger ikke e-conomic live. Det spørger tabellen tabel.economic-documents, som job.economic-sync-documents genopbygger hver nat kl. 04.05 UTC. Et bilag, kunden lægger direkte i e-conomic, er derfor først med næste morgen. Et bilag uploadet i PayFriend tæller med det samme. Det er forklaret i faelde.mangler-bilag.orakel-halter-et-doegn.

Tre andre ting kan også holde posteringen på listen. Tallet gemmes i 5 minutter. Bilaget skal sidde på samme regnskabsår og bilagsnummer som posteringen. Og kontoen skal være en, der forventer et bilag (job.chart-accounts-classify).

Den model, indstilling.env.ocr-model peger på. Er den tom, følger OCR indstilling.env.anthropic-model, hvis default i koden er claude-sonnet-4-6. Hvad produktion har sat, står ikke i koden og er ikke undersøgt her. En kommentar i config/ocr.php siger, at produktion skiftede til claude-sonnet-5 den 20. juli 2026 ved at ændre ANTHROPIC_MODEL.

Der er kun én udbyder, integration.anthropic. Smart routing er slået fra som standard. Er den til, læses PDF'er med tekstlag først af en billig model og eskaleres højst én gang til hovedmodellen. Kontoforslag følger altid ANTHROPIC_MODEL og aldrig OCR_MODEL.

2

Hvad kunden ser og kan gøre

For alle

Et bilag bliver en dublet, når PayFriend tror, det er samme dokument som et, der allerede findes. Det sker på fire punkter:

  • Ved upload. Filens hash slås op i selskabet. Findes den, vises en advarsel, og kunden kan vælge at uploade alligevel.
  • Efter OCR, samme kanal. Samme selskab, leverandør, total og dato i samme indgangskilde. Et bilag regnes ikke for dublet, hvis både filnavn og dokumentnummer kan læses på begge og begge er forskellige. Det beskytter fx Meta, der sender flere ægte kvitteringer på samme beløb samme dag.
  • Efter OCR, på tværs af kanaler. Samme selskab, valuta, dato og total i en anden indgangskilde, uden krav om leverandør. Det fanger den samme kvittering, der kommer både med mail og via regnskabssystemets sync.
  • Når et bilag fra regnskabet kommer bagefter. En observer kører samme tjek, når et bilag oprettes udefra.

I alle tilfælde sættes duplicate_of_id på det nyere bilag. Det bliver ikke bogført automatisk, og scanningen stopper, før den forsøger automatch og auto-bogføring. Kun en fil, som kunden selv har trukket ned på en bestemt banklinje, fortsætter. Er det en upload på bankfanen, flyttes banklinjen over på det bilag, der blev beholdt.

Kunden kan afvise advarslen ("Afvis dublet"). Det gemmer et valg om det konkrete par, fjerner markeringen, og det samme par flagges ikke igen. Kunden kan også slette dubletten. Se begreb.ocr.dublet.

kode: app/Services/Ap/VoucherDuplicateDetector.php · VoucherDuplicateDetector::siblingForkode: app/Services/Ap/VoucherMutations.php · VoucherMutations::dismissDuplicate

En kandidat er en postering i regnskabet, ikke et bilag i PayFriend. Den skal opfylde alt det her:

  • Den ligger på en resultatkonto, der forventer et bilag.
  • Den er ikke afvist af kunden.
  • Der er ingen lokal fil i PayFriend for samme regnskabsår og bilagsnummer.
  • Regnskabssystemets eget bilagsorakel har intet bilag.
  • Flere linjer på samme bilagsnummer tæller som én række.

Oraklet er forskelligt for hver udbyder, se begreb.ocr.bilagsorakel:

Tallet vises først, når oraklet er klar. Det er det, når både chart_receipt_classified_at og bilag_oracle_ready_at er sat på selskabet. Indtil da står der "opgør bilag" og ikke et forkert tal. Tallet vises heller ikke, før hver bankforbindelse, der har hentet data, har en afstemningsstart. Ellers ville et helt importeret regnskab blive til en liste på tusinder.

job.chart-accounts-classify kører hver nat kl. 05.05 UTC, efter fuldsyncen. Den sætter, hvilke konti der forventer et bilag, ud fra kontonummer, og på femcifrede kontoplaner ud fra navnet. Indkomst, løn, finans og skat forventer ikke et bilag. Den advarer også i loggen, hvis Dinero har en posteringstype, som tallet ikke kender.

Push-beskeden "vi mangler et bilag" kommer fra job.push-notify-missing-receipts hver dag kl. 08.40 UTC. Den ser regel.manglende-bilag.lookback tilbage, kigger på de regel.manglende-bilag.pr-firma nyeste kandidater og sender højst én besked pr. selskab pr. kørsel. Samme postering får aldrig to beskeder. Selskaber uden aktiv betalt adgang og suspenderede selskaber springes over.

kode: app/Services/Vouchers/MissingReceiptService.php · MissingReceiptService::isReadykode: app/Console/Commands/SendMissingReceiptPushNotifications.php · SendMissingReceiptPushNotifications::handle

Når en kvittering er væk, kan kunden lade PayFriend lave et begreb.ocr.erstatningsbilag. Der er tre indgange:

  • På et eksisterende bilag. Bilagets fil erstattes af erstatningsbilaget, og den gamle fil slettes. Det kræver en begrundelse på op til 1.000 tegn og kan ikke gøres på et bilag, der bogføres, er bogført eller tilbageført.
  • Som nyt bilag under Kvitteringer. Beskrivelse og begrundelse er påkrævet. Beløb, moms, valuta, dato og leverandør er valgfrie. Bilagsloftet tjekkes først.
  • Fra en banklinje. Bilaget oprettes og kobles straks til linjen. Her kan en ny leverandør oprettes, og en leverandør med samme CVR genbruges i stedet for at blive oprettet to gange.

PDF'en indeholder selskabets navn, mærket ERSTATNINGSBILAG, beskrivelse, leverandør, dato, beløb uden moms, moms, total og valuta, begrundelsen og en attestation med navnet på den bruger, der har oprettet den, og tidspunktet. Filen gemmes som erstatningsbilag.pdf på standarddisken.

Bilaget er markeret is_replacement_receipt. Et nyt erstatningsbilag får konfidens regel.tabt-kvittering.konfidens, og på et eksisterende sættes den til samme tal, hvis den mangler, så rækken ikke bliver stående som scanner. Bilaget scannes aldrig, kan ikke få en ny fil og kan ikke flyttes til Regninger. Et nyt erstatningsbilag får én udgiftslinje uden konto, så kunden kun skal vælge kontoen.

Omregningen af beløb til øre er forkert i det nye bilag. Se faelde.kvitteringer.tabt-kvittering-oere.

kode: app/Services/Ap/LostReceiptService.php · LostReceiptService::createAsVoucher
3

Livscyklus

For alle

Bilaget og mailvedhæftningen har hver sin status under scanning. Overgangene er fulde i tilstandsposterne tilstand.voucher.scan-forloeb og tilstand.email-attachment.ocr-status.

ufuldstændig OCR eller tofejlny scanning læser nogetpendingscan_failed
OCR læstufuldstændig OCR eller tofejlbilagsloft nåetfølgeseddel, ikke et bilageller eget salggjort til bilagpending_ocrreadyocr_failedquota_blockeddismissedpromoted

Hvordan leverandøren blev fundet, står i tilstand.voucher.supplier-resolution. Hvorfor en vedhæftning blev afvist, står i tilstand.email-attachment.afvisningsgrund.

Herfra er det teknisk

4

Hvor kommer data fra

Teknisk

Scanningen af et bilag køres af job.scan-voucher. Manuel upload og rescan lægges på den interaktive kø koe.voucher-scan-interactive, mens mail, indgående mailtekst og masseimport lægges på bulkkøen koe.voucher-scan, så en mailimport ikke sulter en upload. Jobbet har to forsøg, timeout 120 sekunder og backoff på 60 og 300 sekunder.

BookVoucherJobVoucherAutoBookPolicyAutomatchDubletdetektionVoucherPopulatorLeverandørOCR hos AnthropicScanVoucherJobKøBookVoucherJobVoucherAutoBookPolicyAutomatchDubletdetektionVoucherPopulatorLeverandørOCR hos AnthropicScanVoucherJobKøalt[erstatningsbilag]alt[ocr_incomplete]alt[dublet]bilag, forsøg 1 af 2springes overudlæs filenfelter og konfidensstatus scan_failed og stopforeslå Flyt til Regninger hvis document_class er supplier_invoicefind leverandørsupplier_id og supplier_resolutionpopulate med beløb i øre, linjer og kontoforslagsamme kanal og på tværs af kanalerduplicate_of_id og stopforsøg bankmatch hvis ingen linje er kobletskal bilaget bogføres uden opsynjabogfør med autoBook
TrinHvad skerEntitet
UdlæsFilen læses med tekstlag eller som billede. Beløb normaliseres til tal, og ulæselige felter lægges i unparsed_numeric_fields.integration.anthropic
UfuldstændigEt afskåret eller ulæseligt svar sætter scan_failed og gemmer ocr_raw.tilstand.voucher.scan-forloeb
RegningsforslagKun den præcise klasse supplier_invoice foreslår Flyt til Regninger. Bilaget holdes fra auto-bogføring, til kunden har valgt.begreb.ocr.dokumentklasse
LeverandørCVR, lært navn, eksakt navn, uklart navn, oprettelse.tilstand.voucher.supplier-resolution
PopulateBeløb til øre, linjer, beskrivelse og kontoforslag.faelde.kontering.produktlinjer-slaas-sammen
DubletSe afsnit 7.begreb.ocr.dublet
AutomatchSamme funktion som den planlagte genmatch, med kilden auto_voucher_scan. Kører også uden auto-bogføring.indstilling.company.voucher-auto-match-enabled
Auto-bogføringAlle porte skal bestå. Dispatch sker sidst, så et nyt forsøg ikke kan dispatche to gange.indstilling.company.voucher-auto-book-enabled, regel.bogfoering.auto-book-min-confidence

Auto-bogføringens porte er: globalt flag, bogførbar status, ingen dublet, ikke hentet ved kilden, ikke afventende regningsbeslutning, selskabets flag, OCR-konfidens mindst selskabets tærskel, bekræftet bankmatch, ikke løn, en konto på hver linje, linjesum lig total, og ingen blokering fra bogførbarhedstjekket. Dispatchen er med vilje ikke afhængig af, om selskabet er suspenderet, for scanningen er en kæde, der allerede var i gang.

Mailvedhæftninger scannes af job.scan-email-attachment. Her er OCR'en på vedhæftningen og ikke på et bilag, og der opstår først et bilag, når vedhæftningen promoveres.

RegningBankmatchKontoforslagKlassifikatorFiltreOCR hos AnthropicBilagsloftScanEmailAttachmentJobKø voucher-scanRegningBankmatchKontoforslagKlassifikatorFiltreOCR hos AnthropicBilagsloftScanEmailAttachmentJobKø voucher-scanalt[ocr_incomplete]alt[match][intet match]vedhæftning, unik pr. vedhæftninger selskabet over loftetjastatus quota_blocked og stopudlæs fil eller mailtekstfelter og konfidensstatus ocr_failed og stopeget salg, afvis og stopfind leverandør og sæt status readyfølgeseddel og ikke et bilag, afvis og stopregning eller kvitteringforeslå udgiftskontoforsøg bankmatchforslag til banklinjedispatch automatisk promoveringforsøg at oprette regning

Forskellen fra bilagsjobbet er tydelig. Der er ingen VoucherPopulator, ingen linjer og ingen dubletdetektion af den slags her. Jobbet har timeout 270 sekunder, fordi udlæsning, klassifikation og bankmatch kører efter hinanden, og det er unikt i 1.200 sekunder, så et nyt forsøg ikke giver dobbelt OCR. En regning oprettes kun på positive tegn. Et kortbetalt bon har aldrig et match ved scanningstid og bliver derfor liggende i indbakken. Beløb på vedhæftningen står i kroner, mens bilagets står i øre, jf. tabel.email-voucher-attachments.

kode: app/Jobs/ScanVoucherJob.php · ScanVoucherJob::scanVoucherkode: app/Jobs/ScanEmailAttachmentJob.php · ScanEmailAttachmentJob::scanAttachmentkode: app/Services/Ap/VoucherAutoBookPolicy.php · VoucherAutoBookPolicy::shouldAutoBook

Herfra er det teknisk

6

Indstillinger

Teknisk
IndstillingNiveauDefaultHvem kan ændre
indstilling.env.ocr-providerenvanthropicKun PayFriend
indstilling.env.ocr-modelenvtom, følger ANTHROPIC_MODELKun PayFriend
indstilling.env.anthropic-modelenvclaude-sonnet-4-6Kun PayFriend
indstilling.env.anthropic-enabledenvtilKun PayFriend
indstilling.env.ocr-smart-routingenvfraKun PayFriend
indstilling.env.ocr-smart-routing-company-idsenvtom, alle selskaberKun PayFriend
indstilling.env.ocr-cheap-modelenvtom, følger grading-modellenKun PayFriend
indstilling.env.ocr-max-passesenv2Kun PayFriend
indstilling.env.ocr-escalate-below-confidenceenv0,85Kun PayFriend
indstilling.env.ocr-escalate-below-medianenv0,15Kun PayFriend
indstilling.env.ocr-escalate-missing-fienvfraKun PayFriend
indstilling.env.ocr-baseline-min-supplier-readsenv5Kun PayFriend
indstilling.env.ocr-baseline-min-company-readsenv20Kun PayFriend
indstilling.env.ocr-min-text-charsenv100Kun PayFriend
indstilling.env.ocr-min-text-densityenv0,005Kun PayFriend
indstilling.env.ocr-max-garbled-ratioenv0,10Kun PayFriend
indstilling.env.account-history-min-matchesenv2Kun PayFriend
indstilling.env.account-history-min-shareenv0,70Kun PayFriend
indstilling.env.account-suggestion-min-confidenceenv0,80Kun PayFriend
indstilling.env.supplier-profile-vat-min-matchesenv2Kun PayFriend
indstilling.env.supplier-profile-vat-min-shareenv0,80Kun PayFriend
indstilling.env.vat-engine-enabledenvfraKun PayFriend
indstilling.env.vat-engine-shadow-modeenvtilKun PayFriend
indstilling.env.vat-engine-kill-switchenvfraKun PayFriend
indstilling.company.ai-monthly-token-budgetcompanytom, ubegrænsetKun PayFriend, i admin
indstilling.company.voucher-auto-match-enabledcompanytilKunden og PayFriend
indstilling.company.voucher-auto-book-enabledcompanyfraKun PayFriend
indstilling.company.voucher-auto-book-min-confidencecompany0,95Kun PayFriend

Herfra er det teknisk

7

Regler

Teknisk

Kontering har to forskellige skalaer. Tolerancerne på beløb er absolutte tal i kroner. Konfidenserne er 0 til 1. Bankmatchets e-mail-arm bruger 0 til 100, men den hører til Bankmatch-domænet.

Herfra er det teknisk

8

Teknisk flow

Teknisk

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:

  1. 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.
  2. Ét ekstra billedpas efter en tekstlæsning, hvis leverandøren mangler, eller hvis linjernes beløb ikke kan gå op.
  3. Tidsbudgettet regel.ocr.tidsbudget deles af alle kald. Er der under regel.ocr.min-resttid tilbage, springes næste kald over.
  4. Smart routing, kun hvis den er slået til. Den er beskrevet nedenfor.
RuteHvornårModelEskalering
BilligPDF med brugbart tekstlagindstilling.env.ocr-cheap-model, ellers grading-modellenHøjst én gang til hovedmodellen
HovedBilleder og PDF uden tekstlagOCR_MODEL eller ANTHROPIC_MODELIngen

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
FeltHvad det erBruges til
document_classreceipt eller supplier_invoiceForslag om Flyt til Regninger, se begreb.ocr.dokumentklasse
supplier_name, supplier_cvrSælgerenLeverandør-opløsning
supplier_address, _city, _zip, _country, _phone, _emailSælgerens kontaktdataUdfylder leverandøren. Landet styrer standardsatsen.
supplier_bank_reg, _bank_account, _iban, _swiftBetalingsoplysningerUdfylder leverandøren. Kun når identiteten er til at stole på.
fi_kortart, fi_creditor_id, fi_payment_idFI-kortRegninger. Kan udløse eskalering.
recipient_name, recipient_cvrKøberenHindrer, at en regning til en anden oprettes
invoice_numberDokumentnummerDubletdetektion
payment_terms, payment_marks, merchant_categoryBeviser for kvittering eller regningDokumentklassen
posting_textBogføringstekstBilagets beskrivelse
amount, vat_amount, total_amountNetto, moms og total i kronerBilagets beløb i øre
issue_date, due_dateDatoerBilagsdato og forfald. Er en banklinje koblet, vinder dens dato.
currencyValutaBilagets valuta og omregning
confidenceHvor godt den blev læstAuto-bogføring og eskalering
line_itemsLinjer med tekst, antal, stykpris, sats og beløbKonsolideringen

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::toMinor

supplier_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ærdiTrinHvad det betyder
cvr1CVR matcher, også med DK foran
override2En tidligere rettelse eller et lært alias, eller en lært afsender når navn mangler
exact3Normaliseret navn, eller uden accenter og mellemrum
fuzzy4Mindst regel.leverandoer.fuzzy-lighed procents lighed
created5En ny leverandør er oprettet
noneHverken 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::resolve

Forslagene 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:

  1. En sats på 0 eller derunder giver ingen kode, så et gebyr ikke arver 25 procent.
  2. En udenlandsk leverandør får omvendt betaling, uanset kontoens standard.
  3. Har kontoen en synkroniseret momskode, foreslås der ingen. Kontoens egen kode vinder.
  4. Historik for leverandøren: mindst regel.kontoforslag.moms-min-antal linjer og mindst regel.kontoforslag.moms-min-andel med samme kode.
  5. 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øserHvornår
VoucherPopulator::populate med udskudt forslagNår et bilag laves af en mailvedhæftning, og når linjer genopbygges
VoucherAccountRevisionObserverNår en leverandør lander på et bilag, der blev konteret uden
AccountSuggestionRevisor::queueEfter 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

Herfra er det teknisk

10

Kendte fælder

Teknisk

De tre fælder fra memory om moms og linjer er hver kontrolleret mod origin/main på den låste SHA:

Fælde fra memoryVerdict mod mainPost
Afgift læst som momsRettet i prompten. Ingen kodevagt.faelde.moms.afgift-laest-som-moms
Bilagslinjer kastet væk i konsolideringenStadig sandt, og med vilje. Enkeltlinjerne ligger i ocr_raw.faelde.kontering.produktlinjer-slaas-sammen
suggested_vat_code er dødÆndret. Feltet læses nu som led 4 i valget af momskode.faelde.kontering.suggested-vat-code

Dertil kommer de fælder, der blev fundet undervejs:

FældePost
Momsmotoren er en tom kontaktfaelde.moms.momsmotoren-holder-intet
OCR-budgettet er længere end jobbets timeoutfaelde.ocr.tidsbudget-over-job-timeout
En fejl i scanningen kan efterlade bilaget som scannerfaelde.ocr.undtagelse-giver-evig-scanning
AI-budgettet stopper kun knappenfaelde.kontoforslag.ai-budget-staar-kun-ved-knappen
Et bilag uploadet i e-conomic står under Mangler bilag til næste natfaelde.mangler-bilag.orakel-halter-et-doegn
Tabt kvittering regner øre med round(v * 100)faelde.kvitteringer.tabt-kvittering-oere

Herfra er det teknisk

11

Kilder og dækning

Teknisk

Alt på siden er læst mod origin/main for payfriend på den SHA, som atlas.config.ts låser, og for payfriend-admin hvor admin skriver en indstilling. Hver regel, indstilling, tilstand og fælde har sit eget kildemærke i kataloget.

Verificeret mod app@9d146f9, 16 skal genverificeres, 0 brudt

Domænet har ingen handlinger endnu, så dækningen af ruter og UI-kald ligger hos Handlinger-issuet.