Gå til innhold

Synk med Business NXT

Foreløpig — til kundeavklaring

Lesekontrakten er verifisert mot et reelt NXT-selskap med representative data (3 391 masterordrer, 2 373 produkter, 33 244 serienummerrader og 332 189 leveransetransaksjoner tilgjengelig for lesing).

Formål og avgrensning

ePortal eier masterordre-registeret; NXT eier produkter, priser, ekspedering og leveransedata. Synken har derfor to helt ulike deler:

  1. Løpende synk — polling, daglig og ved behov.
  2. Engangsmigrering — de eksisterende masterordrene flyttes inn i ePortal én gang og arkiveres deretter i NXT.

Løpende synk

Datatype Retning Brukes til
Produkter med klassifisering og strukturer NXT → ePortal Produktvalg, instrumentklassifisering (type 1, 2 og 10)
Priser NXT → ePortal (live-oppslag ved behov) Alltid fersk pris i tilbud og ordre
Kunder / skip / management Begge veier En ny kunde kan opprettes i ePortal og få tilbud; overføres til NXT senest ved første kjøp
Salgsordrer ved akseptert tilbud ePortal → NXT NXT ekspederer og fakturerer — se Datomotor og ordre til NXT
Leveransetransaksjoner NXT → ePortal Grunnlaget for datomotoren — se klassifisering
Serienumre NXT → ePortal Ett instrument per serienummerrad
flowchart LR
    A["Planlagt kjøring<br/>(daglig + ved behov)"] --> B["Les endringer fra NXT<br/>(vannmerke: endringstidspunkt)"]
    B --> C["Lagre rådata som<br/>uforanderlig historikk"]
    C --> D["Klassifiser transaksjoner"]
    D --> E["Oppdater leveransebildet<br/>per masterordre"]
    E --> F{"Avvik mot lokalt<br/>akseptert tilstand?"}
    F -- "Nei" --> G["Ferdig"]
    F -- "Ja" --> H["Trenger gjennomgang<br/>(aldri stille overskriving)"]

Beslutningspunkt — kundesynk i konflikt

Kundedata kan endres i begge systemer mellom synkene. Hvem vinner — per feltgruppe eller per system? Og hvilke utfyllende kundefelter må legges til i NXT når en ePortal-først-kunde overføres? Dette må avklares.

Engangsmigreringen av masterordrer

  • Alle aktive masterordrer (ordretype 6, unntatt ordrer med Gr7 = 9) i Business leses inn og blir ePortal-masterordrer med tilstand «Importert». Gr7 = 9-ordrer holdes bevisst utenfor importen (filtreres bort på Business-siden ved henting).
  • Tilknyttet historikk (tilbud, salgsordrer, leveranser, serienumre) følger med som uforanderlige observasjoner — det er slik datomotoren kan forklare datoer fra dag én.
  • Etter migreringen arkiveres masterordrene i NXT. Ingen aktive masterordrer vedlikeholdes i NXT etterpå.
  • Migreringen kan kjøres på nytt uten duplikater: hver rad har en stabil identitet (f.eks. selskap + journalnummer + transaksjonsnummer for leveranser). Importen overskriver aldri eksisterende rader — en ny kjøring legger kun til nye ordrer, nye linjer og nye observasjoner.

Importere kun ordrer og linjer (uten observasjoner)

Observasjons-hentingen (leveringstransaksjonene som mater datomotoren) skanner et bredt datovindu og kan mot en full prod-kopi overskride tidsgrensen. Du kan importere kun ordrene og linjene — som allerede har sine egne datoer fra NXT — ved å sende includeObservations: false:

POST api/SubscriptionImport/run-nxt
{ "maxOrders": 100, "includeObservations": false }

Da hoppes leveringstransaksjons-skanningen over. Ordrene og linjene kommer inn med riktige datoer; observasjonene (og datomotorens historiske forklaring fra leveranser) tas ikke med. Standard er includeObservations: true (full import).

Hva importen setter på ordre og linjer

  • Kundenavn på ordren — hentes fra aktørregisteret (wv_Actor.actName via kundenummeret); finnes ingen lokal aktør, brukes navnet fra NXT-associaten. Navnet driver masterordre-listens kundenavn-kolonne.
  • Linjepris og rabatt — NXT-ordrelinjens priceInCurrency (enhetspris) og discountPercent1 (rabatt %). Beløp per linje beregnes i visningen som antall × pris × (1 − rabatt %/100).
  • Produkt-snapshot per linje — instrumentgruppene (Gruppe 1–6) samt beskrivelse og enhet hentes fra produktet i NXT (samme snapshot som når en linje opprettes manuelt). Feiler produktoppslaget, importeres linjen uten snapshot — importen stopper ikke.

Plassholder-produktnumre

Et produktnummer fra NXT er ikke alltid et ekte produkt. Når et produkt tas ut av sortimentet, kan NXT legge et erstatningsmerke i produktnummer-feltet på ordrelinjen og beholde den opprinnelige beskrivelsen. Et slikt merke ser ut som et produktnummer, men peker ikke på ett bestemt instrument — flere ulike instrumenter kan ende opp med samme merke.

Derfor kan hver kunde liste opp de produktnumrene som er plassholdere hos seg. Listen er tom som standard; da oppfører importen seg nøyaktig som før.

Når importen møter et produktnummer som står på listen:

  • Produktet slås ikke opp. Linjen får verken varegrupper, beskrivelse eller enhet — det er bedre at feltene står tomme enn at et vilkårlig plassholder- produkts verdier settes på et ekte instrument.
  • Verdien lagres nøyaktig slik NXT oppga den. ePortal speiler NXT og finner aldri på et erstatningsnummer.
  • Ordren flagges for gjennomgang med en systemkommentar som navngir de berørte linjene. Kommentaren ligger i kommentarfeltet på ordren og blir liggende også etter at gjennomgangen er kvittert ut.

Det samme gjelder leveransene: kommer en leveranse inn med et plassholder- nummer, flagges ordren selv om selve linjen har et ekte produkt.

Merk at ordrer som ble importert før listen ble fylt ut, ikke flagges tilbake i tid. Etterfyllingen (enrich-nxt) slår heller ikke opp et plassholder-nummer, men den flagger ingenting.

Et plassholder-nummer brukes heller ikke til å koble noe sammen

Å la være å hente produktinformasjon er bare halve jobben. Produktnummeret er også nøkkelen resten av modulen kobler på, og der ville et merke som flere instrumenter deler gjøre reell skade. Står nummeret på listen, behandles det derfor som «ikke et produkt» overalt — og det betyr forskjellige ting alt etter hva stedet gjør:

Hvor Hva som skjer Hvorfor
Instrumentregisteret Linjen registreres ikke som instrument Et instrument som er identifisert med et merke flere andre deler, er verre enn ingen registrering. Ordren står allerede til gjennomgang
Retur av instrument Returen matcher ikke noe instrument Ellers kunne den pensjonere et helt annet instrument som tilfeldigvis har samme merke. Ubehandlet retur er synlig; feil pensjonering er det ikke
Malpakker — forslag Ingen malpakke foreslås for linjen «Ingen forslag» leses som ingen forslag. Feil malpakke leses som et svar
Malpakker — malavvik Instrumentet telles som uten produktnummer og sammenlignes ikke mot noen mal. En ordrelinje med merket regnes ikke som dekning for et behov Tallet «instrumenter uten produktnummer» står allerede i panelet, så instrumentet forsvinner ikke — det står synlig som uavklart. Motsatt vei er verre: to urelaterte linjer med samme merke ville ellers dekke hverandres behov og få ordren til å se komplett ut. Gjelder både panelet og nattjobben som fyller oversikten
Prismatrise Linjen får ingen automatisk pris — prisfeltet står tomt og må fylles ut manuelt Prisgruppene ePortal ville regnet ut fra, tilhører merkets egen katalogoppføring, ikke instrumentet som faktisk ble levert. En pris hentet derfra er ikke et forsiktig anslag, den er et galt tall — og et tall som fylles inn automatisk og lagres på linjen. Tom pris er samme situasjon som et ukjent produkt gir i dag
Overføring av tilbud til salgsordre i NXT Hele overføringen stoppes før noe sendes, og tilbudet havner blant overføringene som trenger tilsyn med produktnumrene som stoppet det En salgsordrelinje i NXT er en regnskapspost hos kunden, og et merke der peker ikke på noe produkt. Linjen kan ikke rettes fra ePortal etterpå. Hele tilbudet stoppes framfor å utelate linjen, for en kortere ordre enn kunden har akseptert er en stille feil
Abonnementsmotoren Linjen får sin egen referanse i stedet for produktnummeret Uten dette ville to helt urelaterte linjer med samme merke smelte sammen til ett abonnement
Tilbakelesing fra NXT etter tilbudsoverføring Linjen parres ikke automatisk, men settes til manuell håndtering Samme utfall som alle andre uklare produktkoblinger: rapportert og overlatt til et menneske, aldri gjettet

Dette gjelder også ordrer som ble importert før listen ble fylt ut: koblingen vurderes hver gang, ikke én gang ved import. Fyller du ut listen i dag, er gamle linjer beskyttet fra neste gang de leses.

Slik løser du opp et tilbud som ble stoppet

Et tilbud som stoppes på vei til salgsordre er kjent ikke sendt — ingenting er opprettet i Business NXT. Verken selve stoppen eller opprettingen etterpå bruker opp noen av forsøkene tilbudet har igjen. (Hadde tilbudet feilet mot NXT en tidligere gang, teller den ordinære omkjøringen av det feilede forsøket ett forsøk, slik den alltid har gjort — det er omkjøringen som teller, ikke plassholder-stoppen.) Slik kommer du videre:

  1. Åpne NXT-overføringer som trenger tilsyn fra tilbudslisten. Det stoppede tilbudet står der, og feilteksten navngir produktnumrene som stoppet det.
  2. Trekk tilbudet tilbake til utkast. Tilbudslinjer kan bare endres mens tilbudet er et utkast, og tilbaketrekking er bare mulig så lenge tilbudet ikke har gitt en masterordre eller en salgsordre.
  3. Bytt ut plassholder-numrene med de virkelige produktnumrene på linjene.
  4. Produser tilbudet på nytt, og ta det videre til den statusen det sto i før — sendt eller akseptert. Dette steget er lett å hoppe over, og da stopper du opp: handlingen Overfør til NXT finnes ikke på et utkast. Den vises bare på tilbud som er produsert, sendt, purret eller akseptert, så knappen er rett og slett ikke der før tilbudet er tilbake i en av de statusene.
  5. Kjør Overfør til NXT på tilbudet igjen. Linjene leses på nytt før noe sendes: er de rettet, går overføringen gjennom; er de ikke det, stoppes den igjen på nøyaktig samme måte, uten at noe sendes og uten at et forsøk brukes opp.

Den planlagte kø-jobben plukker ikke opp et stoppet tilbud

Et stoppet tilbud er tatt ut av overføringskøen med vilje — ellers ville nattjobben forsøkt det samme igjen hver eneste kjøring. Kjøringen der tilbudet ble stoppet rapporteres som feilet, med en forklaring i kjøreloggen, slik at overvåkingen ikke viser grønt mens et tilbud venter på en person. Etter det er det den manuelle handlingen som tar tilbudet videre.

Listen er bundet til skrivestartet, ikke til selve sendingen

Lagrer noen plassholder-listen før en overføring har passert skrivestartet — det ene punktet der overføringen får lov til å skrive til NXT — stanses overføringen der. Ingenting sendes, den blir liggende i køen, ingen forsøk brukes opp, og neste gang den kjøres — nattjobben eller Overfør til NXT — vurderes tilbudslinjene mot den listen som nå gjelder.

Har overføringen allerede passert skrivestartet, fullfører den. Den er da bare ett kall unna, og den ble godkjent mot listen som gjaldt i det øyeblikket den passerte. Et nummer du legger til akkurat da stopper altså ikke den overføringen — den nye listen styrer neste overføring.

Er tilbudet kommet så langt at det verken kan endres eller trekkes tilbake, kan ikke linjen rettes i ePortal i dag. Da må salgsordren opprettes og håndteres direkte i Business NXT.

Innstillingen må kunne leses

Kan ePortal ikke lese innstillingene i det hele tatt — for eksempel under en databasefeil — stopper importen med en feilmelding i stedet for å kjøre videre uten beskyttelse. Det er gjort med vilje: en import som stoppet kan kjøres på nytt, mens en linje som er lagret med feil produktidentitet ikke kan rettes av importen etterpå. At nøkkelen ikke er satt er noe helt annet og er ikke en feil — da er listen bare tom, og alt går som før.

Tekniske detaljer

Innstillingen er nøkkelen Subscription.Nxt.PlaceholderProductNos i wv_SystemConfiguration (klientdatabasen, altså per kunde). Verdien er en kommaseparert liste; oppslaget trimmer og ignorerer store/små bokstaver. Tom eller manglende nøkkel slår av hele mekanismen. Nøkkelen settes av en systemadministrator via POST api/systemconfiguration — den har foreløpig ingen egen skjermflate.

Avgjørelsen tas ett sted: PlaceholderProductLogic.IsPlaceholder, med settet fra IPlaceholderProductCatalog. Katalogen leser nøkkelen utenom 15-minutters-cachen og med SettingReadFailurePolicy.Throw, og memoiserer svaret for levetiden til den injiserte instansen (én forespørsel eller én importkjøring). Uten dette ville en instans som hadde cachet «ingen plassholdere» kjøre ubeskyttet i inntil et kvarter etter at nøkkelen ble satt på en annen instans. Registrert Scoped i Program.cs.

Konsumentene: NxtMasterOrderImportService (import, etterfylling og leveranse-delta), ServiceObjectAutoRegistrarLogic.BuildCandidates / BuildCandidatesFromOfferLines (instrumentregister og retur), TemplateProductLogic.ResolveTriggerKey via TemplateProductRepository (malpakke-triggere), TemplateDeviationService.Project (malavvik, både panelet og nattjobbens cache — instrument- og ordrelinje-nummer prosjekteres som fraværende identitet gjennom samme ResolveTriggerKey), NxtPriceLookupService.ResolvePriceAsync (prismatrise — returnerer Source = None før kandidatradene i det hele tatt hentes; den eldre NxtPriceResolutionLogic.GetProductRank står igjen og hindrer at en rad som er nøklet på merket prises inn på et ekte produkt), OfferNxtTransferService.SubmitAndFinishAsync (overføring til salgsordre — utfallet PlaceholderProductBlocked, og TryMarkInFlightAsync kalles aldri), ScheduleApplyLoadLogic.BuildOfferingReference (OfferingReference faller tilbake til line:<id>) og NxtOfferLineMatchLogic.Match (tilbakelesing).

Den stoppede overføringen får sin egen tilstand i overførings-ledgeren: wv_ExtCache_OfferNxtTransfer.Status = 'PlaceholderBlocked', satt av MarkPlaceholderBlockedSql, som bare treffer en rad som fortsatt står Queued. Det er poenget: NeedsReconciliation-setningen guarder bare på «ikke Completed» og traff dermed også InFlight, så et avslag kunne overskrive en overføring som allerede var på vei ut — og siden PersistOperationIdSql guarder på InFlight, forsvant gjenopptaks- håndtaket til en ordre som kanskje allerede fantes hos kunden. Taper compare-and-set-en, leser tjenesten raden på nytt og rapporterer den faktiske tilstanden i stedet for å skrive. Opprettingen er RequeueFromPlaceholderBlockedSql (PlaceholderBlockedQueued, i én setning), og hverken avslaget eller opprettingen rører AttemptCount. Raden vises via StuckSelectSql, og MasterOrderNxtTransferDrainAdapter teller utfallet som en feilet post slik at kjøringen ikke rapporteres vellykket.

Selve skrivestartet er bundet til den listen forespørselen ble bygget på. IPlaceholderProductCatalog.GetPolicyAsync leser først et stempel — den lagrede verdien av konfigurasjonsraden, uten tolkning — og deretter selve listen, og memoiserer de to som én enhet. Stempelet sendes med til TryMarkInFlightAsync, og MarkInFlightSql leser den autoritative raden på nytt under samme UPDLOCK, HOLDLOCK som go-live-bryteren allerede tas under, i samme transaksjon som overgangen Queued → InFlight. Overgangen skjer bare når stempelet fortsatt stemmer. En lagring som rakk å committe før lesingen blir dermed sett (stempelet spriker → avslag); en lagring som er underveis blokkerer skrivestartet og blir sett når den slippes gjennom; og en lagring som kommer etter lesingen må vente til overgangen har committet, slik at godkjenningen ligger foran den nye listen. Rekkefølgen stempel-før-liste er med vilje: en liste som er nyere enn stempelet sitt feiler lukket, mens motsatt rekkefølge ville godkjent en forespørsel bygget på en utgått liste. Utfallet av et avslag her er TransferInProgress med raden fortsatt Queued og AttemptCount urørt. Sammenligningen gjøres med binær kollasjon (Latin1_General_BIN2): databasens standardkollasjon er aksent-ufølsom, og en aksent-endring som endrer settet ville da lest som «uendret».

Selve opprettelsen av tilbud fra masterordre kopierer fortsatt produktnummeret ordrett — både manuelt (CreateOfferFromMasterLogic) og automatisk (OfferLifecycleLogic). Et merke kan altså stå på en tilbudslinje; sperren ligger på overføringen, ikke på opprettelsen. Om tilbudsopprettelsen i tillegg skal blokkere eller utelate slike linjer, er en åpen beslutning (MO-148 R3).

Flagget bruker samme mekanisme som MO-091: NeedsReviewReason settes til PlaceholderProductNo og systemkommentaren lagres som markøren [[review:PlaceholderProductNo|lines=…|observationLines=…]]. Får ordren både manglende NXT-dato og plassholder, beholder MissingNxtLineDate årsakskoden i kolonnen — begge kommentarene skrives uansett. Berørte kolonner: wv_Subscription_MasterOrderLine.ProductNo og wv_Subscription_DeliveryObservation.ProductNo (begge uendret i skjema).

Importsammendraget skiller mellom hva kjøringen og hva den lagret: PlaceholderProductLinesEncountered / PlaceholderProductObservationsEncountered teller rader lest fra NXT, mens PlaceholderProductRowsPersisted (og PlaceholderProductObservationsPersisted i delta-kjøringen) bare teller rader databasen bekreftet at den satte inn. En ordre som feilet, eller som allerede fantes, øker derfor ikke det siste tallet.

Etterfylle allerede importerte ordrer (enrich-nxt)

Ordrer som ble importert før feltene over ble en del av importen kan etterfylles uten re-import:

POST api/SubscriptionImport/enrich-nxt
{ "maxOrders": 2100 }            // eller { "masterOrderNos": [ ... ] }

Endepunktet leser samme utvalg fra NXT og fyller kun blanke verdier på eksisterende rader:

  • Kundenavn — kun når feltet er blankt på ordren.
  • Linjepris/rabatt — kun når linjens enhetspris er tom eller 0.
  • Instrumentgrupper (+ beskrivelse/enhet) — kun når alle seks gruppene er tomme/0 på linjen.

Manuelt redigerte verdier røres aldri, og endepunktet oppretter aldri nye ordrer eller linjer (ordrer som ikke finnes lokalt telles bare i svaret). Kjøringen er idempotent og kan gjentas. Tilgang styres av samme datatilgang som importen (Masterordre-import, opprette).

Spilleregler som beskytter dataene

  • Lesing endrer aldri noe i NXT. Skriving skjer kun som salgsordre ved akseptert tilbud, kundeoverføring og selve arkiveringen ved migrering.
  • Historikk er uforanderlig. En tidligere levert ordrelinje eller en gammel transaksjon skrives aldri om.
  • Avvik gir gjennomgang, ikke overskriving. Hvis NXT-data ikke stemmer med det ePortal allerede har akseptert, flagges saken for manuell gjennomgang.
  • Endringstidspunkt brukes kun som vannmerke for å hente nytt — aldri som identitet.

Avvik og feil

Situasjon Håndtering
NXT utilgjengelig Kjøringen prøves igjen; neste kjøring tar igjen etterslepet via vannmerket
Rad med endret innhold for kjent identitet Registreres som kildeavvik og flagges — den lagrede historikken endres ikke
Ufullstendig kobling (f.eks. transaksjon uten gjenkjennbar ordrelinje) Lagres som uavklart faktum; ingen videre effekt før den er avklart

Relaterte sider