Gå til innhold

Amili-synk

Amili er en skybasert regnskaps- og faktureringsplattform. ConnectMonitor eksporterer kunder og fakturaer fra Visma Business til Amili (som sender ut faktura og driver purring/inkasso), og importerer innbetalinger, renter og gebyrer tilbake som bilag i Visma. I tillegg genereres en Blocfil til banken (Blocfactoring) fra åpne fordringer i Amili.

Helhetsbilde

flowchart LR
    subgraph visma["Visma Business / DataHost"]
        VC[Kunder - Actor]
        VI[Fakturerte ordrer]
        DHI[DataHost-fakturaer]
    end
    subgraph cm["ConnectMonitor (ctrlAmili)"]
        EXP[Eksport: kunder + dokumenter]
        IMP[Import: AR/GL → Visma-bilag]
        BLOC[Blocfil → bank]
    end
    AMILI[(Amili API<br/>REST + OAuth2)]

    VC --> EXP
    VI --> EXP
    DHI --> EXP
    EXP -->|POST batches| AMILI
    AMILI -->|GET aritems/glitems| IMP
    IMP -->|CreateVismaVoucher| visma
    AMILI -->|åpne AR-poster| BLOC
    BLOC -->|semikolonfil| BANK[(Bank / Factolink)]

    classDef sys fill:#f8d7da,stroke:#b53b46
    class EXP,IMP,BLOC sys

Jobber og flagg

Amili-jobbene startes med langform-flagg (ingen enkeltbokstav), ett per Task Scheduler-oppgave. Definert i Helpers/Options.cs, kjøres fra Program.cs.

Flagg Retning Hva den gjør
--AmiliInvoiceSync Visma/DataHost → Amili Hovedjobben. Synker kunder og fakturaer. Looper først alle DataHost-ID-er (fra SQL) → DataHost-fakturaer, deretter Visma-fakturaer.
--AmiliPaymentSync Amili → Visma Henter innbetalinger (AR-poster) og fører dem som bilag i Visma; pusher også DataHost-betalinger.
--AmiliInterestSync Amili → Visma Henter renter (GL-konto 8030), purregebyr (3313) og bankgebyr (8141) og fører dem som bilag.
--AmiliBlocSync Amili → bank Genererer Blocfil (semikolonseparert) av åpne AR-poster og laster opp til banken via Factolink (eller disk).
--AmiliCustomerSync (Visma → Amili) Deaktivert i Program.cs (kallet er kommentert ut). Ren kundesynk skjer i praksis inne i --AmiliInvoiceSync.

Kundesynk kjører ikke alene

Selv om flagget --AmiliCustomerSync finnes, er selve kjøringen kommentert ut. Kundene synkes automatisk som første steg i --AmiliInvoiceSync. Vil du bare synke kunder, må du kjøre fakturasynken.

Eksportflyt (Visma/DataHost → Amili)

--AmiliInvoiceSync er en kjede: for hver DataHost-ID kjøres DataHost-fakturaer, deretter kjøres Visma-fakturaer. Begge synker kundene først.

flowchart TD
    START([--AmiliInvoiceSync]) --> LOOP{For hver<br/>DataHost-ID i SQL}
    LOOP -->|DataHost| DHC[SyncCustomersToAmili<br/>for DataHost-kunder]
    DHC --> DHD[SyncDatahostInvoicesToAmili<br/>AmiliDatahostInvoiceMapper]
    LOOP -->|etter loop| VC[SyncCustomersToAmili<br/>Visma-kunder]
    VC --> VD[SyncInvoicesToAmili<br/>AmiliDocumentMapper]
    DHD --> POLL[Vent på batch → PROCESSED]
    VD --> POLL
    POLL --> LOG[Logg + e-postrapport<br/>oppdater OrdDok/Kunde med BatchUuid]

    classDef sys fill:#f8d7da,stroke:#b53b46
    class DHC,DHD,VC,VD,POLL,LOG sys

Kundesynk (SyncCustomersToAmili):

  1. api.Invoice.GetCustomersForTransfer(...) henter Visma-kunder (Actor).
  2. AmiliCustomerMapper.MapToAmiliCustomers() mapper til Amili-format.
  3. POST /clients/{clientCode}/customers/batches → Amili svarer med BatchUuid.
  4. Etter polling: api.Invoice.UpdateCustomers(..., BatchUuid) skriver Amili-kontonr tilbake på kunden (kobling Visma-kunde ↔ Amili-konto).

Fakturasynk – Visma (SyncInvoicesToAmili):

  1. api.Invoice.GetInvoicedOrdersForTransfer(...) henter fakturerte ordrer.
  2. AmiliDocumentMapper.MapToAmiliDocuments() mapper til Amili-dokumenter (håndterer GAS-dokumenter og kortsalg, fikser norske tegn).
  3. POST /clients/{clientCode}/documents/batches.
  4. Etter polling: api.Invoice.UpdateOrdDoc(..., BatchUuid) + logg til InvoiceTransferLog (SourceSystem='Visma', Status='PROCESSED').

Fakturasynk – DataHost (SyncDatahostInvoicesToAmili):

  1. Leser startpunkt (GetNextInvoiceNo("Datahost","Invoice"), fallback til Amili_DHFromInvoice i App.config).
  2. Henter fakturaer fra CO DataHost: …/invoices?detaillevel=2&filter=InvoiceNo gt ‹n› and CustomerNo gt 100000&orderby=InvoiceNo&take=‹Amili_Take›.
  3. Dedupliserer mot allerede overførte (GetDatahostInvoicesNotToTransfer).
  4. Synker kundene for disse fakturaene, så mapper via AmiliDatahostInvoiceMapper (malkode per DataHost-ID, se under).
  5. POST …/documents/batches + logg (SourceSystem='Datahost').

Sekvens – eksport med batch-polling

sequenceDiagram
    autonumber
    participant CM as ConnectMonitor
    participant V as Visma / DataHost
    participant A as Amili API
    CM->>A: OAuth2 token (client_credentials)
    CM->>V: Hent kunder / fakturaer
    V-->>CM: Data
    CM->>A: POST .../customers/batches
    A-->>CM: { batchUuid, status: NOT-PROCESSED }
    loop Til status = PROCESSED (timeout ~10 min)
        CM->>A: GET .../batches/{uuid}
        A-->>CM: { status, items[] }
    end
    CM->>V: UpdateCustomers/UpdateOrdDoc (lagre BatchUuid + Amili-konto)
    CM->>CM: AmiliBatchEmailFormatter → e-postrapport

Importflyt (Amili → Visma-bilag)

Innbetalinger – --AmiliPaymentSync

flowchart LR
    WM[Vannmerke StrFvar<br/>NmSp=Amili, VarNm=OCR_Amili] --> AR[GET aritems<br/>VoucherType 21]
    AR --> DD{Dedup mot<br/>WaVo/UpdWaVo.Txt2<br/>prefiks AMILI BET}
    DD -->|ny| VB[Lag Visma-bilag<br/>serie+bankkonto fra StrFvar]
    DD -->|finnes| SKIP[Hopp over]
    VB --> DH{DataHost-faktura?}
    DH -->|ja| CP[CoDataHost CustomerPayment S25]
    VB --> UPD[Oppdater vannmerke]

    classDef sys fill:#f8d7da,stroke:#b53b46
    class AR,DD,VB,CP,UPD sys
  • Henter AR-poster: GetArItemsAsync(clientCode, fromDate, toDate), filtrerer på VoucherType == 21 (innbetalinger).
  • Dedup: referansenøkkel (DocumentNo ?? ArItemId) sjekkes mot WaVo/UpdWaVo.Txt2 med prefiks AMILI BET — hindrer dobbeltføring.
  • Bilag: Voucher/VoucherLine med serie og bankkonto hentet fra StrFvar (OCR_Amili_Serie, HBKonto). Negativt beløp = innbetaling (kredit kunde), positivt = reversering.
  • Hører fakturaen til DataHost, sendes betalingen også dit (CoDataHostAPI.CustomerPayment, S25-record).
  • CreateVismaVoucher bokfører, og vannmerket (OCR_Amili) oppdateres.

Renter, purregebyr og bankgebyr – --AmiliInterestSync

Samme mønster som innbetalinger, men leser GL-poster (GetGlItemsAsync) og skiller på GL-konto:

Type GL-konto i Amili Dedup-prefiks Bankkonto
Renter 8030 AMILI RENTER StrFvar Rente_HBKonto
Purregebyr 3313 AMILI PURREGEBYR StrFvar Purregebyr_HBKonto
Bankgebyr 8141 AMILI BANKGEBYR 7770 (hardkodet)

Alle tre bruker bilagsserien fra StrFvar Rente_Serie, slår opp Visma-kundenr via GetCustNoFromAmiliAccount(ArAccountNo), og oppdaterer vannmerket Rente_Dato.

sequenceDiagram
    autonumber
    participant CM as ConnectMonitor
    participant A as Amili API
    participant V as Visma
    CM->>V: Les vannmerke (OCR_Amili / Rente_Dato)
    CM->>A: GET aritems / glitems (fra-til dato)
    A-->>CM: Poster
    CM->>V: Dedup mot WaVo.Txt2 (AMILI BET/RENTER/…)
    CM->>V: CreateVismaVoucher (serie + konto fra StrFvar)
    CM->>V: Oppdater vannmerke
    Note over CM,V: Feil → e-post til driftsmottaker

Blocfil til bank – --AmiliBlocSync

ExportAndUploadBlocAsync() henter alle åpne AR-poster (GetAllOpenArItemsAsync), bygger en semikolonseparert tekstfil (Blocfactoring- format, ett felt-sett per fordring: klient, kunde, org.nr, adresse, beløp, forfallsdato, purregrad, m.m.) via GenerateBlocSemicolonTxtAsync, og enten:

  • laster den opp til banken via Factolink (FileUploadDualFilenameAsync), eller
  • skriver den til disk hvis Amili_BlockPath er satt.

Til slutt sendes en HTML-rapport (BlocFileReportBuilder) på e-post til rapportmottakeren. Klientnummeret i filen styres av blockClient.

API-flate (AmiliApiClient)

Autentisering: OAuth2 client credentials. AmiliAuthTokenManager POSTer grant_type=client_credentials med client_id/client_secret til Amili_AuthAPI, cacher tokenet og fornyer det 60 sekunder før utløp (expires_in − 60). Bearer-token settes på alle kall.

Base-URL: Amili_BaseAPI (staging f.eks. https://api.staging.amili.no/arm/v1).

Metode Verb + endepunkt Bruk
GetCustomerAsync GET /clients/{c}/customers/{customerNo} Slå opp én kunde
GetCustomerListAsync GET /clients/{c}/customers?customercategory=BUS Liste bedriftskunder
CreateCustomerAsync POST /clients/{c}/customers Enkeltkunde
PostCustomerBatchAsync POST /clients/{c}/customers/batches Kundesynk (batch)
GetDocumentAsync / GetDocumentsAsync GET /clients/{c}/documents[/{n}] Slå opp dokument
PostDocumentsAsync POST /clients/{c}/documents/batches Fakturasynk (batch)
GetArItemsAsync GET /clients/{c}/aritems?… Innbetalinger (paginert, dato/referanse)
GetAllOpenArItemsAsync GET /clients/{c}/aritems?aritemtype=open Blocfil
GetGlItemsAsync GET /clients/{c}/glitems?… Renter/gebyrer
UploadFileAsync POST /clients/{c}/files Vedlegg (base64)
GetBatchAsync GET /clients/{c}/batches/{uuid} Batch-polling
CheckHealthAsync GET /health Helsesjekk

Batch-respons (GetBatchResponse) inneholder Status (PROCESSED osv.), tellere (CountSuccessful/CountFailed/…) og Items[] med feil/advarsler per element — dette er grunnlaget for e-postrapporten.

Feltmapping

Visma-kunde (Actor) → Amili Customer

Verifisert i AmiliCustomerMapper.MapToAmiliCustomer().

Visma Amili Merknad
CustNo CustomerNo til streng
CustomerName Name
Address1/2/3, PostalCode, City, Country PostalAddress.*
CustomerType CustomerCategory
InvoiceEmail + Email EmailAddressDocument deduplisert liste, tomme fjernes
Mobile PhoneNo
Phone SecondaryPhoneNo
EuVatRegNo OrganizationNo bevisst: EU-MVA-nr brukes som org.nr i Amili
PaymentTerms DueDays parses til heltall
Lang Language

Org.nr-feltet inneholder EU-MVA-nummer (tilsiktet)

Kundesynken legger bevisst EuVatRegNo i Amilis OrganizationNo-felt. Dette er ønsket oppførsel — vær oppmerksom på det dersom du sammenligner mot norsk organisasjonsnummer fra andre kilder.

Visma-faktura → Amili Document

Verifisert i AmiliDocumentMapper (MapToAmiliDocument + MapToAmiliDocumentLine).

Hode (faktura → dokument):

Visma-felt (order) Amili Document-felt Merknad / kriterium
DocumentType DocumentType
CustNo CustomerNo til streng
InvoiceNo DocumentNo
DocumentDate DocumentDate yyyy-MM-dd; demo-modus (Amili_Demo) → dagens dato
DueDate (utledet) DueDate start = fakturadato, eller 1. i neste måned ved «fri måned»; + kredittdager / EkstraForfallsdager
Currency Currency
Σ linjer AmountExVat CurrencyAmounts.ExclVat summeres fra linjene
Σ linjer VatAm CurrencyAmounts.Vat
ExVat + Vat CurrencyAmounts.InclVat
Data1 ClientCustomData1 norske tegn fikses (FixNorwegianEncoding)
Data2 ClientCustomData2
Lang Language
CustPoNo PurchaseOrderId
OurRef SellerReference
YrRef BuyerReference
OrdNo SalesOrderId
(ordrenr + sum kg) CustomerMessage kun når Data1 = 'GAS'

Linjer (OrdLnDocumentLine):

Visma-felt (line) Amili DocumentLine-felt Merknad / kriterium
LnNo LineNo
Linetype LineType produkt → PLH, tekst → TXT
ProdNo ArticleNo
Descr ArticleName + LineText
Unit QuantityUnit
Quantity Quantity
UnitPrice Price
AmountExVat CurrencyAmounts.ExclVat
VatAm CurrencyAmounts.Vat
Group1/2/3 ItemGroup1/2/3 «KORTNR.»-overskrift arves til neste produktlinje (ItemGroup1)
InvoiceDate (yyyyMM) glDim1 GL-dimensjon
TransactionDate / ConfirmedDate ClientCustomData1 f.eks. CARD5
leveringsadresse (linje) ClientCustomData2 når linjen har egen adresse
Trinf2 ClientCustomData3 GAS
Trinf3 ClientCustomData4

Kriterier som styrer valgene (linjenivå):

flowchart TD
    L[Fakturalinje] --> KN{Overskrift med «KORTNR.»?}
    KN -->|ja| ARV[Ekskluder linje; tekst arves til ItemGroup1 på neste produktlinje]
    KN -->|nei| ADR{Linjen har egen leveringsadresse?}
    ADR -->|ja| CCD[Adresse → ClientCustomData2]
    ADR -->|nei| ORD[Ordrenivå DeliveryAddress brukes]

    classDef sys fill:#f8d7da,stroke:#b53b46
    class ARV,CCD,ORD sys

DataHost-faktura → Amili Document

Verifisert i AmiliDatahostInvoiceMapper. Kilden er en CO DataHost-faktura (kortsalg) — ikke en Visma-ordre.

Hode (DataHost-faktura → dokument):

DataHost-felt (invoice) Amili Document-felt Merknad / kriterium
AmountToPay (fortegn) DocumentType > 0IN (faktura), ellers CR (kreditnota)
CustomerNo CustomerNo
InvoiceNo DocumentNo + SalesOrderId
InvoiceDate DocumentDate yyyy-MM-dd
DueDate DueDate + DueDateAddition
Currency Currency standard NOK
Σ SubTotal (kun LineType 1) CurrencyAmounts.ExclVat kun produktlinjer
Σ SubTotalWithVAT − SubTotal CurrencyAmounts.Vat
Σ SubTotalWithVAT CurrencyAmounts.InclVat
malkode (per DataHost-ID) ClientCustomData1 se maltabell under
CustomerReferenceText BuyerReference + PurchaseOrderId

Linjer (DataHost-linje → DocumentLine) — kun LineType 1 (produkt) blir egne linjer; LineType 2 (overskrift) brukes som kontekst; 0/3 (subtotal) hoppes over:

DataHost-felt (line) Amili DocumentLine-felt Merknad / kriterium
LineNo LineNo
LineType LineType 1 → PLH, ellers TXT
PartNo / Product.PartNo ArticleNo
ProductName ArticleName + LineText
UnitOfMeasure QuantityUnit
Quantity Quantity 2 desimaler
UnitPrice Price 4 desimaler
SubTotal CurrencyAmounts.ExclVat
SubTotalWithVAT − SubTotal CurrencyAmounts.Vat
SubTotalWithVAT CurrencyAmounts.InclVat
InvoiceDate (yyyyMM) glDim1
overskrift / PAN / kjøretøy ItemGroup1/2/3 avhenger av malkode (under)
DeliveryTime ClientCustomData1 dd.MM.yyyy HH.mm
avdelingsnavn (fra DataHost-ID) ClientCustomData2 fallback «Automat ‹SourceSiteNo›»
kilometerstand ClientCustomData3 kun mal CARD2
rabatt per enhet ClientCustomData4 kun mal CARD3

Malkode per DataHost-ID (hardkodet) — bestemmer både ClientCustomData1 og hvordan ItemGroup1-3 fylles:

DataHost-ID IL0000 IL0002 IL0004 IL0005 IL0006 IL0003
325 CARD CARD3 CARD2 CARD4
369 CARD2 CARD2
368 CARD CARD2 CARD3 CARD4
378 CARD CARD2 CARD3
383 CARD CARD2 CARD3

Fallback-mal = CARD. Nye DataHost-stasjoner krever kodeendring her.

Hva malkoden styrer i ItemGroup:

Mal ItemGroup1 ItemGroup2 ItemGroup3
CARD2 overskriftstekst kjøretøynr (beriket) korteiers etternavn
CARD4 PAN (kortnr) overskrift uten navn fullt navn
CARD (standard) PAN (kortnr) overskrift uten navn fullt navn

Kriterier som styrer valgene:

flowchart TD
    INV[DataHost-faktura] --> T{AmountToPay &gt; 0?}
    T -->|ja| DIN[DocumentType = IN]
    T -->|nei| DCR[DocumentType = CR]
    INV --> M[Slå opp malkode<br/>DataHost-ID + fakturamal]
    M --> MC{Malkode}
    MC -->|CARD2| C2[ItemGroup: kjøretøy + etternavn]
    MC -->|CARD4 / CARD| C4[ItemGroup: PAN + fullt navn]
    LN[Linje] --> LT{LineType}
    LT -->|1 produkt| PLH[Egen DocumentLine PLH]
    LT -->|2 overskrift| CTX[Brukes som kontekst]
    LT -->|0 / 3 subtotal| SKIP[Hoppes over]

    classDef sys fill:#f8d7da,stroke:#b53b46
    class DIN,DCR,M,C2,C4,PLH,CTX,SKIP sys

Vannmerker (StrFVar, NmSp = 'Amili')

Importjobbene husker fremdrift i StrFVar. Definert som prefikser i jhlCommon/GlobalVariables.cs.

VarNm Betydning
OCR_Amili Sist importerte innbetalingsdato
OCR_Amili_Serie Bilagsserie for innbetalinger
HBKonto Bankkonto for innbetalinger
DatahostID DataHost-ID for betalingsruting
Rente_Dato Sist importerte rente-/gebyrdato
Rente_Serie Bilagsserie for renter/gebyr
Rente_HBKonto Bankkonto for renter
Purregebyr_HBKonto Konto for purregebyr
EkstraForfallsdager Dager lagt til forfallsdato ved eksport

Eksportjobbene seedes derimot fra App.config (Amili_DHFromInvoice, Amili_VismaFromInvoice) og sporer fremdrift via GetNextInvoiceNo.

Konfignøkler (App.config)

Nøkkel Betydning
Amili_ClientCode Amili-klientnummer (numerisk, f.eks. 732/740/733/730)
Amili_ClientId / Amili_ClientSecret OAuth2 client credentials
Amili_AuthAPI Token-endepunkt
Amili_BaseAPI API base-URL
Amili_DHFromInvoice (+ _‹DHID›) Startfakturanr for DataHost-eksport (override per ID)
Amili_VismaFromInvoice Startfakturanr for Visma-eksport
Amili_Take Batch-størrelse (fakturaer per kjøring)
Amili_Demo Bruk dagens dato i stedet for faktisk fakturadato
Amili_GasDocument Marker dokumenter som GAS
Amili_Report E-postmottaker for batch-rapporter
Amili_BlockPath Disk-sti for Blocfil (ellers Factolink-opplasting)
blockClient Klientnummer i Blocfil

Idempotens og rekjøring

Jobb Duplikatvern Rekjøring
Innbetalinger (--AmiliPaymentSync) WaVo.Txt2 LIKE 'AMILI BET%' + datovannmerke Trygt — dupliserte poster hoppes over
Renter/gebyr (--AmiliInterestSync) WaVo.Txt2 prefiks per type + Rente_Dato Trygt
Fakturasynk (--AmiliInvoiceSync) BatchUuid på OrdDok/kunde + DataHost-dedup; Amili svarer «already exists» og markeres OK Trygt
Blocfil (--AmiliBlocSync) Ingen — bygger alltid full fil av åpne poster Sjekk at banken ikke får filen to ganger

Teknisk gjeld (viktig ved overtakelse)

  • Død/duplisert kode: prosjektet jhlAmili (egen Customer-modell, CustomerBatchController som bare returnerer hardkodet OK, tom amiliAPI.cs) brukes ikke — all aktiv kommunikasjon går via AmiliApiClient. Kan forvirre; ikke bygg videre på jhlAmili.
  • Hardkodet bankgebyrkonto 7770 og malkoder per DataHost-ID ligger i koden, ikke i konfig.
  • Eldre *ToERP2-metoder uten dedup finnes ved siden av de aktive *ToERP-metodene — bruk ikke de gamle.

Feilsøking

Symptom Sjekk
Innbetaling ikke ført i Visma StrFVar Amili/OCR_Amili (vannmerke), WaVo.Txt2 for «AMILI BET ‹ref›» (allerede ført?), driftslogg/e-post
Faktura ikke i Amili Batch-rapport på e-post (item.Errors), InvoiceTransferLog, at kunden ble synket først
DataHost-faktura mangler Amili_DHFromInvoice_‹DHID› vs. faktisk fakturanr; malkode finnes for DataHost-ID?
Token-feil (401) Amili_ClientId/Amili_ClientSecret/Amili_AuthAPI; tokenet fornyes 60 s før utløp
Renter/gebyr uteblir Riktig GL-konto (8030/3313/8141) i Amili; Rente_Dato-vannmerke
Blocfil sendt to ganger Jobben har ingen duplikatsperre — kontroller banken/Factolink

Relaterte sider