Gå til innhold

Visma Business NXT-integrasjon

Visma Business NXT er Visma sin skybaserte ERP-plattform. ePortal integrerer mot NXT via GraphQL API for synkronisering av timer, bilag, aktører (kunder/leverandører), bank- og hovedboksdata, samt org-enheter (avdelinger/prosjekter).

Type: GraphQL, OAuth2 (Visma Connect), per-adapter integrasjon Modul i ePortal: Konfigureres og overvåkes i Konti Connect-modulen Vs. klassisk: Visma Business klassisk er on-premise ERP via PortalConnector WinService. NXT er moderne, skyhostet, GraphQL.


Adaptere — hva som finnes i koden

Hver NXT-adapter har en egen IntegrationTypeCode og registreres som en separat integrasjon i Konti Connect:

Adapter Type-kode Retning Hovedoppgave
Tids­registreringer VismaBusinessNXT_TimeRegs ePortal → NXT Eksporter timer som freeInformation3-rader
Bilag VismaBusinessNXT_Vouchers ePortal → NXT Opprett bilag i batcher, valgfri "bokfør"-mutasjon
Aktører VismaBusinessNXT_Actors Toveis Synke kunder/leverandører via associate-entiteten
Kontoplan VismaBusinessNXT_Accounts NXT → ePortal Hent hovedbokskontoer
Org-enheter VismaBusinessNXT_OrgUnits Toveis Synke avdelinger/prosjekter (orgUnit1, orgUnit2)
Ordrer VismaBusinessNXT_Orders (per adapter) Ordrelinjer
Produkter VismaBusinessNXT_Products (per adapter) Produkter/varer

Aktør-import (retning Import) skriver direkte til ePortals aktørregister. Hver kunde/leverandør hentet fra associate upsertes rett inn i wv_Actor via en felles, testet aktør-upsert (ActorUpsertRepository.cs, kalt fra VismaBusinessNXTActorAdapter.cs), ikke lenger via et HTTP-kall til KontiConnect. Upserten matcher på kunde-/leverandørnummer, slår sammen kunde- og leverandørrolle på samme organisasjonsnummer (ingen dupliserte rader for samme juridiske enhet), renser e-post, og hopper over aktører uten navn eller med «0»/tomt referansenummer — per aktør, så én dårlig aktør stopper ikke resten. Eksport-retningen (ePortal → NXT) er uendret.

Aktør-klassifisering (IMO/type/BPP/flåte/kundestatus). Aktør-synkroniseringen skriver også klassifiseringsfelt fra NXT sin associate-entitet inn i wv_Actor, overskrevet ved hver kjøring (NXT er kilde for sannhet):

ePortal-kolonne NXT-felt Betydning
actInfo1 information1 (String) IMO-nummer
actGroup2 group2 (Int) Kundetype-kode
actGroup3 group3 (Int) BPP-status-kode
actGroup7 group7 (Int) Flåte-kode
actGroup9 group9 (Int) Kundestatus-kode (Aktiv/Ekstern/Sluttet/Solgt…)

MO-126 (retting): Kundetype er NXT group2 (actGroup2) og Flåte er NXT group7 (actGroup7). Tidligere pekte Kundetype på group1 (actGroup1) og Flåte på employeePriceGroup (actEmployeePriceGroup) — feil NXT-felt (verifisert mot NXT). actGroup1 er nå en generisk gruppe, og actEmployeePriceGroup er en reell prisgruppe (ikke en klassifisering).

MO-126 Slice 2 (nytt felt): Kundestatus = NXT group9 (actGroup9, kolonnen lagt til i migrasjon 20261016900083) hentes nå også. Kundestatus flyter foreløpig kun via den direkte NXT GraphQL-synken; KontiConnect Integration-API-produsenten (eget repo) eksponerer ikke group9 ennå — klienter som synkroniseres via KontiConnect får Kundestatus først når produsenten utvides (samme åpne port som gruppe 1–8-produsenten).

0/tom verdi normaliseres til NULL (ActorUpsertRepository.cs:313-329). Feltene skrives fra både kunde- og leverandør-passet, slik at en aktør med begge roller ikke mister verdien avhengig av rekkefølgen (VismaBusinessNXTActorAdapter.cs:520-525). «IMO-nummer»/«Kundetype»/«BPP-status»/«Flåte»/«Kundestatus» er standard­etikettene — visningsnavnene kan tilpasses per kunde i etikett-katalogen (Innstillinger → Abonnement – feltoppsett, feltnøklene ActorInfo1/ActorGroup2/ActorGroup3/ActorGroup7/ActorGroup9; standardetikettene rettet i migrasjon 20261016900082, Kundestatus-etiketten seedet i 20261016900083).

Alle åtte klassifiseringsgruppene (group1group8, alle Int) synkroniseres nå inn i wv_Actor.actGroup1actGroup8 (group2/4/5/6/7/8 i tillegg til de to over).

Skriv-tilbake av klassifiseringskoder (ePortal → NXT). Kodene skrives tilbake til NXT via associate-entiteten på to måter:

  • Aktør-eksport/-synk (associate_create/associate_update) tar med gruppekodene sist i payloaden (NXT bryr seg om feltrekkefølgen) — kun satte koder skrives, en ukjent/uendret gruppe utelates (klobrer aldri en gruppe den ikke kjenner).
  • Kundekortet (CRM → «ERP-klassifisering», redigerbare nedtrekk) gjør en synkron skriv-før-lagring: de åtte kodene pushes til NXT (associate_update, filtrert på customerNo/supplierNo) og lagres kun i wv_Actor hvis NXT godtar dem. Her betyr «(ingen)» en eksplisitt tømming som skrives som 0 (NXTs «ingen verdi»; leses tilbake som NULL). Uten en NXT-integrasjon er kortets klassifiseringsblokk skrivebeskyttet (VismaBusinessNXTActorAdapter.PushClassificationUpdateAsync, NxtActorClassificationPusher.cs).

Tømming (bekreftet): å skrive 0 tømmer gruppen i NXT — det setter ikke kategori 0. «(ingen)» i kortet er derfor en reell tømming som leses tilbake som NULL. Verifisert mot NXT (eier-bekreftet).

Avstemming ved full synk (MO-091 #13). Når aktør-importen kjører som full synk (EnableDeltaSync = false), avstemmes aktørregisteret mot det komplette NXT-uttrekket per rolle: aktører hvis kunde-/leverandørnummer ikke lenger finnes i NXT får ERP-koblingsfeltene fjernet — kundenummer (actCustNo + actErpCustomerNo) hhv. leverandørnummer (actSupNo + actSupplierCode), og klassifiseringsfeltene over når aktøren mister sin siste ERP-rolle (de er NXT-eide). Aktøren slettes eller deaktiveres aldri — den lever videre i CRM uten ERP-kobling. Sikkerhetsgrense: avstemmingen hoppes over (med advarsel i kjøringsloggen) hvis uttrekket dekker under 50 % av lokalt koblede aktører for rollen (ActorErpReconciliationLogic.MinimumFetchCoverageRatio — beskytter mot at en delvis/feilet henting nuller hele registeret). Delta-synk kan ikke observere fravær og kjører aldri avstemmingen. Slås av med integrasjonsinnstillingen ReconcileMissingActors = false. Kjøringsloggen viser «X aktører fikk fjernet ERP-kobling (finnes ikke lenger i NXT)» (VismaBusinessNXTActorAdapter.cs, ReconcileErpLinkageAsync).

Masterordre-søkekolonner oppdateres ved aktørsynk (MO-116). Etter en fullført aktør-import/-synk kjører adapteren en full gjenoppbygging av masterordre-listens denormaliserte søkekolonner (SearchText/SearchNumberswv_Subscription_MasterOrder), slik at NXT-drevne kunde-/management-navneendringer blir søkbare i masterordre-listen. Ren tilleggseffekt — påvirker ikke aktør-import, klassifisering eller avstemming over (VismaBusinessNXTActorAdapter.cs, RecomputeSearchTextForAllAsync; se Masterordre-datamodell).

Feltmapping av klassifisering og egendefinerte felt (MO-130, fase 1). Feltmapper-fanen på en NXT aktørintegrasjon (Konti Connect → integrasjon → Feltmapping) tilbyr nå:

  • Klassifiseringskilder: NXT associate-feltene information1 og group1group9.
  • Faste klassifiseringsmål: ImoNo, Kundetype, BppStatus, Fleet, CustomerStatus — med forhåndsutfylte standardrader (information1→ImoNo, group2→Kundetype, group3→BppStatus, group7→Fleet, group9→CustomerStatus). Radene vises uten lagret databaserad; en rad lagres kun når en administrator overstyrer via «Legg til ekstra mapping».
  • Egendefinerte CRM-kundefelt som mål: hvert aktivt wv_CRM_CustomField med EntityType='Customer' tilbys som mål (nøkkel customField:{FieldId}).

Fase 1 endrer ikke selve klassifiserings-synken — de faste identitetene følger fortsatt de generiske actGroup/actInfo-kolonnene 1:1 (fase 2 lar lese-flatene respektere en om-mapping). Den eneste nye synk-oppførselen er skriving til egendefinerte felt: når en mapping peker et NXT-kildefelt til et egendefinert kundefelt, skrives NXT-verdien til wv_CRM_CustomFieldValue for den synkroniserte aktøren (nøkkel EntityType='Customer', EntityId = actID) og vises på kundekortet. Skrivingen er robust per aktør (én feilende skriving stopper ikke synken), typekonverterer NXT-verdien til feltets FieldType, respekterer ValidationRegex, hopper over slettede felt, og er idempotent (upsert). NXTs numeriske «ingen verdi»-sentinel 0 skrives ikke. Referanse: VismaBusinessNXTActorAdapter.cs (BuildCustomFieldValueUpserts, ApplyCustomFieldMappingsAsync).

I tillegg leveres NXT bank- og hovedboksdata til Bankavstemming og hovedboksdata til KAI Live regnskap via en egen tjeneste (VismaBusinessNXTBankDataProvider) som bruker samme NXT-integrasjon, men er ikke en IntegrationAdapter.

Referanse: api/ePortal.API/ePortal.Base/Services/Integration/VismaBusinessNXT/ og api/ePortal.API/ePortal.Base/Services/BankReconciliation/VismaBusinessNXTBankDataProvider.cs.

integrasjonslisten i Konti Connect med flere NXT-adaptere som hver har egen status-rad


Tilgang

Hvem Hva trengs
Hos Visma NXT-abonnement med tilgang til Visma Connect (OAuth2) — ClientId, ClientSecret, TenantId
Hos Konti Administrator-tilgang til Konti Connect
Hos kunden NXT selskaps­nummer (CompanyNo); for bilag i tillegg VoucherSeriesNo

CompanyNo resolves enten via eksplisitt erpClientCode-override, via wv_Domain.ErpClientNumber, eller fra integrasjons­konfigurasjonen (VismaBusinessNXTBankDataProvider.cs:60-79).


Slik gjør du — første oppsett

1. Generer NXT API-tilgang (hos Visma)

I Visma Connect / NXT-administrasjon: opprett en applikasjon for ePortal og noter ClientId, ClientSecret, TenantId. Velg scope som dekker de adaptere som er aktuelle (timer, bilag, hovedbok, bank, aktører, org-enheter).

2. Konfigurer i Konti Connect

For hver adapter du vil aktivere: opprett en separat integrasjon i Konti Connect (type = adapter-koden over) og legg inn:

  • ClientId, ClientSecret, TenantId — Visma Connect-credentials
  • CompanyNo — NXT selskaps­nummer
  • Adapter-spesifikke nøkler (se neste seksjon)

ePortal lagrer ClientSecret kryptert. IntegrationService.GetDecryptedConfigAsync brukes ved hver kjøring — egen-kode som leser credentials direkte fra wv_Integration får kryptert verdi og feiler med 401.

3. Verifiser tilkobling

Tids­registrerings-adapteren har en ConnectionTestResult-test som spør freeInformation3(first: 1) { totalCount } for å bekrefte at token og scope fungerer (VismaBusinessNXTTimeRegistrationAdapter.cs:803).

oppsettsskjerm for en NXT-adapter med felter for ClientId/Secret/TenantId/CompanyNo


Konfigurasjon — feltforklaring

Felles autentiserings-felter (alle NXT-adaptere)

Felt Plassering Type Påkrevd Hva det betyr
ClientId Auth eller Config String (GUID) Ja OAuth2 client_id fra Visma Connect Developer Portal. Adapter leser Auth.ClientId med fallback til Config.ClientId (VismaBusinessNXTHttpHelper.cs:59)
ClientSecret Auth (krypteres) String Ja OAuth2 client_secret. Lagres kryptert. Krever GetDecryptedConfigAsync ved lesing (VismaBusinessNXTHttpHelper.cs:50)
TenantId Auth eller Config String Ja Visma Connect tenant-ID (kundens NXT-tenant)
CompanyNo Config Integer Ja NXT-selskaps­nummer. Verifiseres som heltall i hver adapter — feiler hard om verdien ikke kan parses (VismaBusinessNXTVoucherAdapter.cs:1511, VismaBusinessNXTTimeRegistrationAdapter.cs:863)

Auth-flyt: POST https://connect.visma.com/connect/token med grant_type=client_credentials + scope business-graphql-service-api:access-group-based. Token brukes som Authorization: Bearer <token> mot https://business.visma.net/api/graphql-service. Token caches per credential-sett — nøkkelen er et ikke-reversibelt digest av token-endepunkt, scope, ClientId og ClientSecret (NxtTokenCacheIdentity.cs), og invalideres ved 401 (VismaBusinessNXTHttpHelper.cs). En konfigurasjon som oppgir riktig ClientId men feil ClientSecret treffer derfor ikke et eksisterende token: forespørselen går til Visma Connect, som avviser den. Hemmeligheten lagres aldri i nøkkelen, loggen eller feilmeldinger.

Eksempel ConfigurationJson (felles for alle NXT-adaptere)

{
  "ClientId": "<visma-connect-client-id>",
  "ClientSecret": "<visma-connect-client-secret>",
  "TenantId": "<tenant-id>",
  "CompanyNo": "12345",
  "EnableDeltaSync": true
}

Adapter-spesifikke felter (VoucherSeriesNo, MaxRowsPerRun, BatchSize osv.) legges på toppen av dette.

Felles for de fleste adaptere

Nøkkel Standard Hva den gjør
EnableDeltaSync true Når aktivert henter adapteren kun rader endret siden forrige kjøring. Aktor-, konto-, ordre- og org-enhets-adapterne sjekker denne flaggen direkte (VismaBusinessNXTActorAdapter.cs:1144, VismaBusinessNXTAccountAdapter.cs:455)
MaxRowsPerRun 25000 (timer) Begrenser hvor mange rader som behandles per kjøring. Tids­registrerings-adapteren stopper ved grensen og fortsetter fra cursor neste kjøring (VismaBusinessNXTTimeRegistrationAdapter.cs:177-201)

Varslingsinnstillinger (alle NXT-adaptere)

E-postvarsling ved feilede kjøringer styres per integrasjon med tre nøkler i ConfigurationJson. De godtas på alle NXT-typene og lagres alltid med akkurat disse navnene — skriver du dem med annen bruk av store/små bokstaver, rettes navnet automatisk ved lagring, og en konfigurasjon som inneholder samme nøkkel i to skrivemåter avvises (NxtIntegrationConfigurationPolicy.cs:19-20, :192-210).

Nøkkel Type Hva den gjør
NotificationEmails tekst Mottakere, komma- eller semikolonseparert (f.eks. "drift@firma.no;vakt@firma.no"). Må være en tekstverdi — andre JSON-typer avvises ved lagring (NxtIntegrationConfigurationPolicy.cs:220-228)
AlertLevel tekst Når det varsles: None (aldri), Error (kun feilede kjøringer), Warning (feilede og delvis vellykkede) eller All (alle). Andre verdier avvises ved lagring; skrivemåten rettes til disse (NxtIntegrationConfigurationPolicy.cs:220-228, IntegrationAlertService.cs:145-153)
AlertDigestMinutes heltall ≥ 1 Minste antall minutter mellom to varsler fra samme integrasjon (standard 60). Skriver du verdien som tekst i JSON-fanen (f.eks. "30"), gjøres den om til et ekte tall ved lagring; null og negative verdier avvises (NxtIntegrationConfigurationPolicy.cs:249-253, IntegrationAlertService.cs:161-169)

Varselvurderingen kjører for alle avsluttede kjøringer som er logget — også når kjøringen stopper før adapteren kommer i gang, for eksempel når ingen adapter finnes for typen, konfigurasjonen avvises av adapterens validering, eller kjøringen kaster en feil (IntegrationService.cs:1120-1128).

Bilags-adapter (VismaBusinessNXT_Vouchers)

Konfigurasjons­nøkler (VismaBusinessNXTVoucherAdapter.cs:24-38):

Nøkkel Standard Hva den gjør
VoucherSeriesNo (påkrevd) Bilags­serie i NXT som batcher opprettes mot
AutomaticBatchUpdates false Når sann: kjører "bokfør" (batch_update-processing) automatisk etter opprettelse
EnableBookkeepingControl true Validerer mot bokførings­regler før posting
EnableProjectBlocking false For avskrivnings­prosjekter: leser nåværende status på orgUnit2, setter midlertidig status=0 under eksport, gjenoppretter etterpå (VismaBusinessNXTVoucherAdapter.cs:1303-1451)
DepreciationVoucherSeries 600 Bilags­serie (VoSerie) som identifiserer avskrivnings­bilag
BatchSize 500 Maks antall bilags­linjer per NXT-batch
DebugLogging false Skrur på utvidet loggning

Eldre bilagsintegrasjoner migreres automatisk. Integrasjoner opprettet før bilagseksporten ble skrevet om, har DepreciationVoucherType, EnableDimensionMapping og PageSize liggende i konfigurasjonen. Disse feltene finnes ikke lenger. Ved første lagring eller kjøring blir DepreciationVoucherType overført til DepreciationVoucherSeries (verdien beholdes), de to andre fjernes, og SyncDirection: "Import" rettes til "Export" — adapteren har alltid vært eksport-only. Ingen handling kreves.

Hvert bilag som eksporteres får sitt eget bilagsnummer reservert atomisk hos NXT rett før det opprettes, via _suggest: { voucherNo: true } med temporary: true mot en midlertidig, aldri-lagret enkeltlinje (samme konto på debet og kredit — ingen økonomisk effekt uansett beløp; kontoen gjenbrukes fra bilagets egne linjer, så ingen ny konfigurasjon kreves). Det reserverte tallet skrives deretter, én gang, til det ekte bilaget — ingen egen "reservasjonsbilag" opprettes eller blir liggende synlig i NXT-boken. Implementert i ReserveVoucherNoAsync (VismaBusinessNXTVoucherAdapter.cs), kalt fra CreateBatchWithVouchersAsync for hvert bilag i chunken før batchen opprettes. Hvis reservasjonen feiler for ett eneste bilag, avbrytes hele chunken (ingen batch opprettes) og alt i den prøves igjen ved neste kjøring — ingen fallback til NXTs egen auto-nummerering, som ikke garanterer ett felles nummer per flerlinjers bilag.

Dette erstatter en tidligere metode som leste voucherSeries { nextVoucherNo } direkte og skrev tallet eksplisitt — live-testet 2026-08-05 og bekreftet utrygt: NXT validerer ikke at et eksplisitt angitt bilagsnummer faktisk er ledig, så to bilag registrert omtrent samtidig (f.eks. ett fra ePortal og ett manuelt i NXT-klienten) kunne stille få samme nummer. En mellomliggende variant (reservasjon via ett synlig ekstra "reservasjonsbilag" per kjøring) ble forkastet etter Codex-review 2026-08-05: den kunne i praksis gjenbruke reservasjonens eget nummer på det første ekte bilaget, altså nøyaktig samme kollisjon fiksen skulle løse.

Valutafelt på bilagslinjer (BRQ-11)

MapSingleVoucherLine sender nå ett felles beløpsfelt for alle bilag denne adapteren eksporterer (bankavstemming, bilagsimport, lagertransaksjon-til-bokføring — VismaBusinessNXTVoucherAdapter.cs:1330-1402):

NXT-felt Sendes Verdi
currencyNo kun når linjen har en registrert valuta (i dag kun bankavstemmingsbilag) NXTs eget valutanummer — se advarsel under
exchangeRate kun sammen med currencyNo kursen fra banktransaksjonen
amountInCurrency alltid valutabeløpet; uten currencyNo leses det av NXT som det domestiske (NOK) beløpet — derfor er ett felles felt trygt for alle produsenter
amountDomestic alltid (uendret felt) NOK-beløpet

⚠️ Feltrekkefølgen er bindende. currencyNo MÅ skrives før exchangeRate i den serialiserte GraphQL-payloaden — å sette valuta gjør at NXT slår opp valutaens standardkurs, som overskriver en kurs skrevet tidligere (live-API-verifisert). Begge må skrives før beløpsfeltene, ellers leser NXT beløpet i feil enhet før valutakonteksten er satt — nøyaktig den opprinnelige feilen dette elementet fikser. Rekkefølgen er pinnet av en regresjonstest på den serialiserte JSON-en, ikke bare konstruksjons­rekkefølgen i C#-koden (en Dictionary kan i prinsippet endre rekkefølge).

⚠️ currencyNo er et NUMMER, ikke en ISO-kode, og nummereringen er tenant-spesifikk — én kundes «1 = USD» kan være en annen kundes annen valuta. Nummeret slås derfor opp live per tenant mot NXTs egen currency-entitet (INxtCurrencyLookupService, samme mekanisme master­ordre-importen bruker) — aldri en hardkodet oversettelsestabell. Finnes ingen match, opprettes ikke bilaget (se Bankavstemming: Manuell matching → «Konto i utenlandsk valuta»).

Se også Bilagskø og bilagsarkiv (ExtCache) — datamodell for kolonnene på kø- og arkivtabellen.

Ordre-adapter (VismaBusinessNXT_Orders)

Nøkkel Standard Hva den gjør
OrderType 2 Ordretypen i Visma Business NXT. Velges i editoren: 1 eller 2. Begge er ordre — den ene har logistikk aktivert, den andre ikke. Velg den typen firmaet bruker (VismaBusinessNXTOrderAdapter.cs:26)
SyncDirection Export Eneste lovlige verdi — adapteren importerer ikke ordre

Overføring på forespørsel fra Bankavstemming (BRQ-8)

Når en bruker oppretter et bilag fra bankavstemmingens arbeidsbenk («Opprett bilag»), overføres det nå umiddelbart til denne adapteren i stedet for å vente på neste planlagte kjøring av VismaBusinessNXT_Vouchers — se Bankavstemming: Manuell matching → «Opprett bilag og overfør til Visma» for utfallene brukeren ser. AutomaticBatchUpdates avgjør utfallet på nøyaktig samme måte som ved en planlagt kjøring:

  • AutomaticBatchUpdates = true (og bokføringskontrollen godkjenner batchen): bilaget bokføres, brukeren ser «Bokført i Visma».
  • AutomaticBatchUpdates = false, eller bokføringskontrollen (EnableBookkeepingControl) avviser batchen: bilaget forblir kun overført (opprettet i NXT, ikke bokført), brukeren ser «Overført, men ikke bokført» og må bokføre det manuelt i Visma.
  • Feiler selve bokførings-steget i Visma etter en vellykket overføring, ser brukeren «Overført, men bokføringen feilet» med Vismas feiltekst — bilaget er allerede ute av ePortals kø på dette tidspunktet, så det finnes ingen retry-vei i ePortal for den kjøringen.

Denne umiddelbare overføringen bruker samme adapter, kø (wv_ExtCache_Voucher/wv_ExtCache_VoucherLine) og dobbeltkjørings­beskyttelse (sp_getapplock) som den planlagte kjøringen — er en planlagt kjøring allerede i gang for integrasjonen, legges bilaget i kø og tas med av den i stedet (brukeren ser «Ligger i kø»). Tenanter uten en aktiv VismaBusinessNXT_Vouchers-integrasjon påvirkes ikke — bilaget legges i den vanlige køen som før.

Bilagstype som sendes til NXT for bankavstemmings-bilag (BRQ-9)

Adapteren leser bilagstype (voucherType) fra samme rad i wv_ExtCache_VoucherLine som alle andre bilag (VismaBusinessNXTVoucherAdapter.cs:554): VoucherType = (first.VoType ?? 0) > 0 ? first.VoType : null. For bilag opprettet fra Bankavstemming settes denne verdien av innstillingen Accounting.BankReconciliationVoucherType (BankReconciliationController.cs:3084), lest på samme måte som Accounting.DefaultVoucherSeries:

  • Standard 0 — ingen voucherType sendes til NXT i det hele tatt, og NXT bruker seriens standard bilagstype. Dette er riktig for de fleste selskaper.
  • Verdi > 0 — sendes verbatim som NXT sin voucherType. Sett denne kun hvis selskapet i NXT krever en spesifikk bilagstype for disse bilagene.

Ingen migrasjon seeder denne innstillingen — den leses via ISettingsService, som returnerer standardverdien 0 når nøkkelen ikke finnes i wv_SystemConfiguration.

Historikk: før BRQ-9 var bilagstypen hardkodet til 700 som en intern ePortal-markør for "dette er et bankavstemmings-bilag". NXT tolker imidlertid VoType som en reell bilagstype, og et selskap uten type 700 (f.eks. selskap 6106857) fikk hele batchen avvist med «Voucher type 700 does not exist» — hver eneste «Opprett bilag»-overføring feilet. Bankavstemmings-bilag identifiseres uansett unikt via ExtID = "BR{banktransaksjonID}", så bilagstype-feltet var aldri nødvendig som markør.

Tids­registrerings-adapter (VismaBusinessNXT_TimeRegs)

Bruker NXT-entiteten freeInformation3 ("Fri informasjon 3"). ePortal sin wrID lagres som primaryKey for å gi 1:1 oppslag mellom systemene (VismaBusinessNXTTimeRegistrationAdapter.cs:21).

Felt-mapping (ePortal → NXT) (VismaBusinessNXTTimeRegistrationAdapter.cs:621-660):

ePortal-felt NXT-felt Notat
wrID primaryKey Idempotent nøkkel
wrEmplNo employeeNo
wrDate date1 (YYYYMMDD int) + realisedYearAndPeriod (YYYYMM int)
wrDep orgUnit1 Avdeling
wrProj orgUnit2 Prosjekt
wrWorkOrd orgUnit5 Arbeidsordre
wrAccX orgUnit9 (string) KontoX
wrQty value1 Antall timer
Lønnsart text1
Beskrivelse text2

Operasjoner: freeInformation3_create(values: [...]), freeInformation3_update(filters, values), freeInformation3_delete(filter: { primaryKey: { _in: [...] } }).

Bilags-vedlegg (Excel/PDF)

Vedlegg legges til NXT-bilaget via mutasjonen voucher_processings.addNewDocument(...) med base64-innhold. Mutasjonen ligger i den delte NxtVoucherDocumentGateway (BRQ-10) — ett sted som brukes av to kall­steder: den planlagte kø-eksporten (VismaBusinessNXTVoucherAdapter.UploadVoucherDocumentsAsync, for vedlegg med UploadToNxt = 1) og bankavstemmingens direkte post-overførings­opplasting (se under). NXT begrenser én GraphQL-forespørsel til ~15 MB; ePortal kaster vedlegg over MAX_VOUCHER_DOCUMENT_BASE64_LENGTH = 14_000_000 (VismaBusinessNXTVoucherAdapter.cs:76) — denne grensen gjelder foreløpig ikke bankavstemmingens direkte opplasting (se under).

Bankavstemmings-vedlegg til et allerede overført bilag (BRQ-10)

Etter at et bilag fra bankavstemmingen er overført (se BRQ-8 over), kan brukeren laste opp vedlegg direkte til det NXT-bilaget mens bokførings­modalen er åpen — se Bankavstemming: Manuell matching → «Vedlegg til bilaget». Dette er en egen, direkte vei til samme mutasjon — ikke den delte kø-eksporten over:

  • Backend-endepunktet UploadVoucherAttachmentToNxt validerer eierskap likt BRQ-8s PostVoucher (bilaget må tilhøre den oppgitte banktransaksjonen) — og at det valgte vedlegget faktisk tilhører DEN bilagslinjen (VoucherLineAttachmentBelongsToAsync, R1-review-funn: en AttachmentID er global, ikke voucher-scoped, så uten denne sjekken kunne en person med tilgang til én egen bankavstemmings-voucher pushe et hvilket som helst vedlegg i tenanten til et hvilket som helst bilag). Endepunktet tar ikke imot batch-/bilagsnummer fra klienten (samme review-funn) — det slår opp det virkelige (batchNo, voucherNo) selv via NxtVoucherDocumentGateway.ResolveVoucherCoordinatesByVolIdAsync (samme externalReference3 = "EPVOL:{VolID}"-markør adapteren stempler på eksport) og kaller så AddDocumentAsync med de server-utledede tallene — ingen kø.
  • Bankavstemmings-vedlegg lagres alltid med UploadToNxt = 0 og går aldri inn i den delte kø-eksporten over — den køen laster ned blob'en fra det lagrede BlobContainer-navnet uendret, mens bankavstemming lagrer det uprefikserte navnet (se ExtCacheVoucherDocumentRepository-klassekommentaren og "Vedlegg" i Bilagshistorikk-seksjonen for hvorfor de to ikke kan blandes).
  • SentToNxt settes kun etter en vellykket opplasting, gjenbruker samme kolonne som kø-veien.
  • Vinduet er den åpne dialogen — en produktbeslutning, ikke en teknisk begrensning: frontenden forsøker kun å pushe et vedlegg mens den fortsatt har et bilagsnummer fra denne sesjonens PostVoucher-svar; lukkes bokførings­modalen, slutter frontenden å prøve. Serveren selv trenger ikke dette tallet — den slår opp de virkelige NXT-koordinatene på nytt hver gang, uavhengig av om modalen er åpen. Et vedlegg lastet opp før en vellykket overføring, eller mens frontenden ikke har et kjent bilagsnummer, forblir derfor lagret i ePortal uten å nå Visma i denne sesjonen.

Bilagshistorikk og re-kjøring («Bilagshistorikk»-fanen)

Bilags-integrasjonen har en egen Bilagshistorikk-fane som viser alle bilag som er eksportert til Business NXT. Ved eksport flyttes bilaget fra den aktive køen (wv_ExtCache_Voucher / wv_ExtCache_VoucherLine) til arkivtabellene (wv_ExtCache_Voucher_imported / wv_ExtCache_VoucherLine_imported) av databasetriggeren trg_Voucher_delete. Fanen leser arkivet og viser eksporttidspunkt, linjeantall, beløp og eventuelle re-kjøringer (hvem/når).

Kjør på nytt: velg ett eller flere bilag og trykk «Kjør på nytt». Bilaget legges da tilbake i eksportkøen med sine originale ID-er, og dukker opp under «Nye bilag». Arkivkopien blir liggende med stempel for re-kjøringen (RestoredDate/RestoredBy).

  • Duplikatvern: eksporten dedupliserer mot NXT på externalReference3 (EPVOL:<VolID>). Et bilag som fortsatt finnes i NXT blir derfor ikke bokført på nytt — det ryddes bare fra køen igjen ved neste kjøring. Re-kjøring har kun effekt når bilaget faktisk er slettet i NXT.
  • Vedlegg: bilagsvedlegg (f.eks. Aktivering-timer-Excel) re-armes ved re-kjøring (SentToNxt nullstilles) og lastes opp på nytt hvis bilaget gjenopprettes i NXT.
  • Hoppet over: bilag som allerede ligger i køen («allerede i kø») eller mangler arkiverte linjer («ingen arkiverte linjer») hoppes over med begrunnelse i varselet.

Kilde: api/ePortal.API/ePortal.Base/Repository/ExtCacheVoucherHistoryRepository.cs (restore/lesing), api/ePortal.API/ePortal.Base/Services/Integration/VismaBusinessNXT/VismaBusinessNXTVoucherAdapter.cs (dedup og vedleggsopplasting), api/ePortal.API/ePortal.DbUp/v1/Migration_20261016900021.cs (arkiv-audit og trigger).

Vanlige spørsmål om re-kjøring

Situasjon Forklaring
Re-kjørt bilag forsvinner fra køen uten å dukke opp i NXT Bilaget finnes fortsatt i NXT — duplikatvernet ryddet det fra køen. Slett bilaget i NXT først hvis det skal bokføres på nytt.
Eldre bilag mangler serie/tekst i historikken Bilagshoder eksportert før v-migrasjonen 20261016900021 kunne gå tapt (trigger-feil, nå fikset). Bilaget kan fortsatt kjøres på nytt; eksporten bruker da konfigurert VoucherSeriesNo.
Eldre bilag mangler eksportdato Eksporter gjort før migrasjonen har ingen tidsstempel i arkivet.
Re-kjørt bilag ble ryddet av duplikatvernet — hva skjer med vedlegg? Vedleggene forblir re-armet (SentToNxt = 0) til en senere re-kjøring der bilaget faktisk gjenopprettes i NXT. Opplasting skjer kun etter at bilaget er opprettet i NXT, så ingenting lastes opp dobbelt.

OrgUnit-mapping (avdelinger / prosjekter / arbeidsordre)

NXT bruker orgUnit1orgUnit12 som ressurs­dimensjoner. Standardene ePortal sender er konsekvente på tvers av adaptere:

ePortal-konsept NXT-felt
Avdeling orgUnit1
Prosjekt orgUnit2
Arbeidsordre orgUnit5
KontoX orgUnit9 (string)

Standard­mapping passer Vismas typiske R-matrise for konsulent-/installasjons­firmaer. Egne integrasjons-spesifikke field-mapping kan settes per integrasjon — adapterne eksponerer en liste av FieldMappingOption for tilgjengelige NXT-felt.

GraphQL-filter syntax — _and-array

NXT GraphQL krever _and-array-syntax når flere felt skal filtreres. Flere betingelser som ikke pakkes i _and, ignoreres stille av NXT — spørringen returnerer da alle rader.

_or finnes i operatortabellen, men ePortal sender det ikke. Vismas egen dokumentasjon beskriver at et _or-deluttrykk kan bli stille fjernet fra filteret, og et filter som mister en betingelse utvider resultatsettet i stedet for å feile høylytt. På en hentegrense som avgjør hvilke regnskapsposter som i det hele tatt kommer inn, er stille utvidelse den verste feilmåten. Samme valg er tatt i NxtPriceLookupService, NxtProductLookupService og VismaBusinessNXTBudgetLineProvider. _is_null sendes heller ikke — operatoren står i tabellen, men har ingen dokumentert argumentform.

I stedet kjøres to enkle spørringer per entitet, én per datoakse, som slås sammen og dedupliseres i C#. Hovedboken bruker BuildLedgerTransactionQueryByValueDate og BuildLedgerTransactionQueryByVoucherDate (banksiden tilsvarende ...ByInterestDate / ...ByBookingDate); BuildLedgerTransactionQuery og BuildBankTransactionsQuery er interne hjelpere bak disse, ikke inngangspunkter.

Verdiaksen — bare rader som faktisk har en valuteringsdato:

filter: {
  _and: [
    { accountNo:  { _eq: $accountNo } }
    { valueDate:  { _gt: 0 } }
    { valueDate:  { _gte: $fromDate } }
    { valueDate:  { _lte: $toDate } }
  ]
}

Bilagsaksen — ingen betingelse på valuteringsdato i det hele tatt:

filter: {
  _and: [
    { accountNo:    { _eq: $accountNo } }
    { voucherDate:  { _gte: $fromDate } }
    { voucherDate:  { _lte: $toDate } }
  ]
}

Unionen er et supersett av de radene som hører til perioden: har raden valuteringsdato, returnerer verdiaksen den; mangler den, er bilagsdatoen den effektive datoen og bilagsaksen returnerer den. Fordi bilagsaksen med vilje henter for mye, trimmes unionen etterpå i C# på den effektive datoen (IsInEffectiveNxtWindow), og dedupliseres på radens primærnøkkel — den sammensatte (voucherJournalNo, auditNo) for hovedbok, accountStatementTransactionNo for bank. Ingen del av regelen avhenger av hvordan NXT sammenligner NULL.

Datoformat er int YYYYMMDD (eks. 20260131). Strenger eller ISO-format gir filterfeil.

Paginering

Alle GraphQL-queryer i VismaBusinessNXTBankDataProvider (og adapterne) paginerer via pageInfo { hasNextPage endCursor } med first: 500 per side (VismaBusinessNXTBankDataProvider.cs). Cursor sendes som after-variabel ved påfølgende sider.


Bank- og hovedboks-data (for Bankavstemming og KAI Live regnskap)

VismaBusinessNXTBankDataProvider leverer data via GraphQL — ikke via CAMT-filer:

Funksjon NXT-entitet (alias)
Hovedboks­transaksjoner generalLedgerTransaction (acTr68)
Hovedbokssaldo generalLedgerTransaction (acTr68) — summert i ePortal, se under
Reskontro (kunde) customerTransaction (custTr54)
Reskontro (leverandør) supplierTransaction (supTr61)
Bank-utdrag (transaksjons­hode) accountStatementTransaction (acStmtTr434)
Bank-utdrag (detalj­linjer) accountStatementDetail (acStmtDe437)
Aktører (kunde/leverandør master) associate (Actor, tabell 152)
Hovedbokskontoer generalLedgerAccount

Hovedbokssaldo beregnes i ePortal, ikke lest fra NXT sin egen periodesaldo. GetLedgerAccountBalanceAsync summerer enteredAmountDomestic fra generalLedgerTransaction for alle rader med effektiv dato til og med den forespurte datoen — samme to-spørrings-mekanikk (verdidato-akse og bilagsdato-akse, slått sammen og deduplisert) og samme effektive-dato-regel som Hovedboks­transaksjoner under, bare uten noen nedre datogrense (VismaBusinessNXTBankDataProvider.cs:634-675). NXT sin egen generalLedgerBalance-entitet (periode­granulær, følger bokførings­datoen) brukes ikke.

Viktig: Det er ingen customer- eller supplier-rotfelt på useCompany — kunde- og leverandør-navn slås opp via den unifiserte associate-entiteten filtrert på customerNo/supplierNo (VismaBusinessNXTBankDataProvider.cs:583-611).

Periode­filteret bruker valuteringsdato, med bilags-/bokføringsdato som reserve. Alle spørringene under henter på den effektive datoen: valuteringsdato når raden har en, ellers bilags-/bokføringsdato. Det er samme dato­akse som resten av bankavstemmings­modulen bruker når den avgjør hvilke rader som hører til perioden — henter vi på en annen akse enn vi slipper inn på, forsvinner rader vi skulle hatt, og de kan aldri hentes igjen (eier­beslutning 2026-08-05, BRQ-32).

Spørring Valuteringsdato Reserve når valuteringsdato mangler
Hovedbok (generalLedgerTransaction) valueDate (kode) voucherDate (kode)
Reskontro (customerTransaction/supplierTransaction) valueDate (kode) voucherDate (kode)
Banktransaksjoner (accountStatementTransaction) interestDate (kode) bookingDate (kode)

accountStatementTransaction har ikke noe valueDate-felt — interestDate er NXT-navnet på valuteringsdato der (samme felt som IntDt on-prem). Reserve­aksen er nødvendig fordi rene GL-posteringer (konsernmellom­værende, periodiserings­konto-bevegelser) mangler valuteringsdato, og et filter på valuteringsdato alene ville returnert tomt for dem.

En manglende dato har to mulige representasjoner: 0 (heltalls­standarden fra on-prem) og GraphQL null. Introspeksjonen viser at både valueDate og voucherDate er nullbare Int (SCALAR Int, uten NON_NULL-innpakning), så null er en reell verdi og ikke bare en teoretisk mulighet. Begge betyr det samme her, og begge håndteres: DTO-feltene er int? — som non-nullable int ville en JSON-null fått hele siden til å feile under deserialisering — og den effektive datoen regnes som «satt» bare når verdien er større enn 0.

CAMT 053/054 er en separat, manuell bank-fil-import (CamtFileParser, api/ePortal.API/ePortal.Base/Services/BankReconciliation/CamtFileParser.cs) — ikke noe NXT-integrasjonen leverer.


Sikkerhet og driftsbeskyttelse

Krypterte credentials

Tjenester som henter NXT-credentials utenfor IntegrationService må bruke GetDecryptedConfigAsync(integrationId). FromIntegration returnerer fortsatt kryptert ClientSecret → kallet til Visma Connect svarer 401.

Dobbeltkjørings­beskyttelse

IntegrationLockRepository bruker sp_getapplock med LockMode = 'Exclusive', LockOwner = 'Session', LockTimeout = 0 per integrasjon (IntegrationLockRepository.cs:23-28). Hvis samme integrasjon allerede kjører, gir låsen < 0 retur og forsøket avvises umiddelbart — uten å vente.


Vanlige problemer

"Cannot query field 'X' on type 'Y'"

NXT GraphQL endrer feltnavn av og til (Visma oppgraderer skjema). Sjekk at ePortal-versjonen er oppdatert.

Hovedboks tom — for streng dato­filter

Rene GL-posteringer og konsern­mellom­værende-konti har ofte ingen valuteringsdato (0 eller null). Et filter som bare ser på valuteringsdato returnerer derfor tomt for disse. ePortal håndterer dette selv — bilagsaksen (BuildLedgerTransactionQueryByVoucherDate) henter disse radene uavhengig av valuteringsdato — så dette skal ikke lenger være årsaken. Er hovedboken likevel tom, sjekk kontonummer, selskaps­nummer og at datointervallet faktisk dekker perioden.

"401 Unauthorized" mot Visma Connect

ClientSecret brukt direkte uten dekryptering. Bruk IntegrationService.GetDecryptedConfigAsync (eller hjelpere i VismaBusinessNXTHttpHelper som invaliderer cached token ved 401 og henter friskt).

Aktør-synk finner ikke kunde/leverandør

customer/supplier finnes ikke som rotfelt på useCompany. Bruk associate(filter: { customerNo: { _eq: $no } }) eller tilsvarende for leverandør (VismaBusinessNXTBankDataProvider.cs:583-611).

Tids­eksport stopper midt i kjøringen

Når MaxRowsPerRun (standard 25 000) nås, stopper adapteren og fortsetter fra cursor ved neste planlagte kjøring. Dette er forventet for store backfills — loggen sier "Nådde MaxRowsPerRun (...) — fortsetter fra cursor ved neste kjøring".

Bilags­vedlegg avvises av NXT

Base64-innhold over ~14 MB avvises lokalt før send (NXT cap ~15 MB per GraphQL-request). Komprimer vedlegget eller del det opp.

"Dobbeltkjøring forhindret"

sp_getapplock per integrasjon avviser samtidige kjøringer av samme integrasjon. Vent på pågående kjøring; den slipper låsen ved fullføring.

"Konfigurasjonsfeltet «...» må være et positivt tall" ved lagring

Rettet. Tidligere kunne denne meldingen komme selv om feltet inneholdt et gyldig tall: skjemavisningen sendte tallfeltene som tekst i konfigurasjonen, og valideringen krevde et ekte tall. Den slo bare til på felt du faktisk hadde redigert, så en urørt integrasjon fortsatte å virke mens den samme integrasjonen ikke lot seg lagre etter en endring.

Kommer meldingen fortsatt, er verdien reelt ugyldig: feltet er tomt, inneholder noe annet enn siffer, eller er null eller negativt. Feltet må ha et helt tall som er 1 eller høyere. Meldingen navngir hvilket felt det gjelder. Gjelder DeltaSyncLookbackDays, ExportBatchSize, MaxRowsPerRun, BatchSize, AlertDigestMinutes, MaxPagesPerRun, MaxRuntimeSeconds, ReconcileEveryNRuns og CompanyNo (NxtIntegrationConfigurationPolicy.cs:249-253).

OrderType og OrgUnitLevel har egne meldinger og er strengere med vilje — de bestemmer hvilke poster adapteren skriver, så bare de gyldige kodene godtas (OrderType 1 eller 2, OrgUnitLevel 1–12).

"VoucherSeriesNo ikke konfigurert"

Bilags-adapteren krever VoucherSeriesNo i integrasjonens config — "VoucherSeriesNo '...' er ikke et gyldig heltall" kastes hvis verdien mangler eller ikke kan parses (VismaBusinessNXTVoucherAdapter.cs:250-252).


Migrering fra Visma Business klassisk

Hvis kunden migrerer fra klassisk Visma Business (PortalConnector WinService) til NXT:

  1. Ikke kjør begge adapter-sett samtidig på samme datatype — risiko for konfliktende skriv
  2. Aktiver NXT-adaptere i parallell, men hold dem på lese-bare områder først (hovedbok, aktører) for sammenligning
  3. Bytte over én datatype om gangen (timer → bilag → ordre)
  4. Pause klassisk-integrasjonen når NXT-eksporten er bekreftet

Konti har egen rutine for migrering — kontakt support før du starter.


Relaterte sider