Ontbrekende transacties in TransactionLinesIncremental

Advies is om bij geval 3 de HTTP-statuscode te controleren. De tabel ExactOnlineREST.FinancialTransaction.TransactionLinesIncremental@eol bestaat niet; wel bestaat deze tabelnaam in het Incremental-schema (zie TransactionLinesIncremental: Exact Online Transactieregels (Incrementeel) - Exact Online API Data Model).

Merk op dat het downloaden relatief lang duurt om het een Free Exact Online Plan betreft; deze kent 1 parallel downloadkanaal.

Geval 1

Bij geval 1 komen inderdaad vier regelnummers retour in de browser: 0, 1, 2 en 9999.

Het SQL-statement in Invantive Bridge Online Monitoring is:

select t.*
from   ExactOnlineREST.FinancialTransaction.TransactionLines@eol t
where  ([EntryNumber] = :w1)

met w1: 20230416

De data is opgeslagen als geval1.json (12 kB) en vervolgens gecontroleerd via:

select jte.*
from   read_file_text@Os('C:\temp\forums3468\geval1.json') rft
join   jsontable
       ( 'value[*]'
         passing rft.file_contents
         columns EntryNumber int64 path 'EntryNumber'
         ,       LineNumber  int16 path 'LineNumber'
       ) jte
where  jte.EntryNumber = 20230416
order
by     jte.LineNumber

met als resultaat:

image

Geval 2

Bij geval 2 worden initieel eerst alle transactieregels gedownload via:

curl --output c:\temp\geval2.json --user vimak@favoreclame.nl:Q0i9h@7uiI6GDAorsgmq https://bridge-online.cloud/7c5b8d84-16a0-49bb-b4e7-3a9a73df59ff/odata4/ExactOnlineREST.FinancialTransaction.TransactionLines@eol

Het SQL-statement in Invantive Bridge Online Monitoring is:

select t.*
from   ExactOnlineREST.FinancialTransaction.TransactionLines@eol t

De data is opgeslagen als geval2.json (373.870 kB) en vervolgens gecontroleerd via:

select jte.*
from   read_file_text@Os('C:\temp\forums3468\geval2.json') rft
join   jsontable
       ( 'value[*]'
         passing rft.file_contents
         columns EntryNumber int64 path 'EntryNumber'
         ,       LineNumber  int16 path 'LineNumber'
       ) jte
where  jte.EntryNumber = 20230416
order
by     jte.LineNumber

met als resultaat enkel regels 0 en 2:

image

Geval 3

Bij geval 3 is met de gecorrigeerde URL de download uitgevoerd: .../odata4/ExactOnlineREST.Incremental.TransactionLinesIncremental@eol via:

curl --output geval3metcorrectie.json --user john.doe@acme.com:secret https://bridge-online.cloud/ACME/odata4/ExactOnlineREST.Incremental.TransactionLinesIncremental@eol

Het SQL-statement in Invantive Bridge Online Monitoring is:

select t.*
from   ExactOnlineREST.Incremental.TransactionLinesIncremental@eol t

De data is opgeslagen als geval3metcorrectie.json (177.780 kB) en vervolgens gecontroleerd via:

select jte.*
from   read_file_text@Os('C:\temp\forums3468\geval3metcorrectie.json') rft
join   jsontable
       ( 'value[*]'
         passing rft.file_contents
         columns EntryNumber int64 path 'EntryNumber'
         ,       LineNumber  int16 path 'LineNumber'
       ) jte
where  jte.EntryNumber = 20230416
order
by     jte.LineNumber

Deze query leidt tot nul regels.

Analyse

Analyse 1: volledigheid en consistentie

Als eerste stap is gekeken naar volledigheid en consistentie. De volgende statistieken zijn opgevraagd met onderliggende SQL:

Geval #Rijen #LineNumber = 0 #LineNumber = 1 #LineNumber = 9999
1 4 1 1 1
2 130621 17100 22775 15394
3 130621 16904 22590 15146

De verwachting is dat de laatste LineNumber 0 en 9999-kolommen grofweg dezelfde waardes bevatten. Dit geldt alleen voor geval 1.

Ter verdieping is in lijn met Schatten omvang Exact Online administratie een server-side telling uitgevoerd van de data in Exact Online. Het aantal rijen was:

Divisiecode Rijen
2575738 2793
1861032 127828
Totaal 130621

Het positieve is dat 130621 aansluit bij de tweede kolom van de eerste tabel bij analyse 1. Echter, het is bijzonder vreemd dat regelnummer 0, 1 en 9999 een verschillende distributie hebben.

Vervolgens is de JSON geanalyseerd voor het aantal transacties overall en per LineNumber 0, 1, 2 en 9999. De getallen bij #LineNumber = 0 en #LineNumber = 9999 zouden grofweg gelijk moeten zijn, en met regelnummer 1 in de buurt. Later is ook #LineNumber is null toegevoegd omdat dat als onverwachte uitkomst er uit kwam.

Geval #Overall #LineNumber=0 #LineNumber=1 #LineNumber=2 #LineNumber=9999 #LineNumber is null
1 1 1 1 0 1 0
2 25058 17013 19988 6130 15306 3
3 19218 16845 19191 5688 15072 3

Het voorkomen van LineNumber is null is uitzonderlijk. Historisch werd - leek het - in de API’s het regelnummer berekend i.p.v. ruw doorgegeven, waardoor regels soms een andere volgorde kregen. Een jaar of 6 geleden is hierin tenminste een verbetering doorgevoerd, maar het ontbreken van een LineNumber leidt tot per definitie tot problemen in de uitkomsten. Het is dan niet bekend of een regel een kopregel of BTW-regel is, anders dan uit de context van andere velden (zie ook Bijzondere regelnummers zoals 9999 op financiële transacties in Exact Online).

De transacties met een boeking zonder regelnummer zijn zowel in geval 2 als in geval 3: 16900002, 17900033, 18900066. Uit controle blijkt dat dit resultaatboekingen zijn over de jaren 2016, 2017 en 2018. Een controle tegen een aantal andere administraties die nog in gebruik zijn leert dat dit voor die administraties ook geldt: in de jaren 2009…2015 zijn er een aantal boekingen voor BTW en resultaat zonder regelnummer. Blijkbaar is dit probleem niet in 2016 gecorrigeerd, maar in 2019 lijkt het.

Het probleem met LineNumber is null vervalt hiermee.

Blijft het probleem met de afwijkende aantallen boekstukken per regelnummer, terwijl het totaal wel sluit qua aantal boekstukregels.

Vervolgens is de scope uitgebreid met twee gevallen: TransactionLinesBulk (geval 4) en SyncTransactionLines (geval 5), in eerste instantie via enkele SQL-queries.

Metingen met rijen geven:

Geval #Rijen #LineNumber = 0 #LineNumber = 1 #LineNumber = 9999
1 (OData4) 4 1 1 1
2 (OData4) 130621 17100 22775 15394
3 (OData4) 130621 16904 22590 15146
4 (SQL) 130621 22503 29627 20128
5 (SQL) 130621 22503 29627 20128

Vervolgens zijn de metingen uitgebreid met ook gevallen 1, 2 en 3 via SQL uit te voeren en geval 4 en 5 via OData4:

Geval #Rijen #LineNumber = 0 #LineNumber = 1 #LineNumber = 9999
1 (OData4) 4 1 1 1
1 (SQL) 4 1 1 1
2 (OData4) 130621 17100 22775 15394
2 (SQL) itgeneor229 itgeneor229 itgeneor229 itgeneor229
3 (OData4) 130621 16904 22590 15146
3 (SQL) 130621 22503 29627 20128
4 (SQL) 130621 22503 29627 20128
5 (SQL) 130621 22503 29627 20128

Hierbij valt het op dat de uitkomsten bij verwerking met SQL en OData4/JSON anders zijn voor geval 3.

Vervolgens is een analyse gemaakt van de meeste voorkomende omschrijvingen. Hierbij kwam naar voren dat het abonnement geen downloads groter dan 100.000 rijen toestaat (deze controle wordt momenteel niet uitgevoerd bij de SQL Editor van Invantive Cloud):

Omschrijving Aantal
invantive.com 15.212
itgenboe115 TOO MANY 15.189
Prijs ex. BTW 10.410

De foutcode itgenboe115 (evenals itgenboe116, itgenboe114, itgenboe294 en itgenboe295) geeft aan dat de regel betrekking heeft op rij boven de 100.000.

Vervolgens is gekeken wat de EntryNumber en LineNumber zijn van deze gevallen. Hierbij bleek dat deze volledig ontbreken. Bij controle blijkt dit gewenst gedrag te zijn. Als er meer gedownload wordt dan het abonnement toestaat, dan worden deze velden leeggemaakt indien het datatype dat toestaat.

Eindconclusie is dat het gedrag verklaarbaar is doordat er meer gedownload wordt dan het abonnement toestaat.

Advies is om het abonnement te upgraden van het Free Exact Online Plan naar Invantive Office for Entrepreneurs.

In de programmatuur zijn een aantal verbeteringen uitgevoerd om deze analyse onnodig te maken:

  • De lijst met stappen onderaan bij elk requestdetails toont itgenboe375 en itgenboe376 indien er voor demo-, licentie- of anonymisatie-doeleinden gegevens gewijzigd zijn.
  • De datawijziging voor licentiebeperking is aangepast zodat er meer duidelijk herkenbare punten zijn, zoals de GUID “CA5B2B7F-7D93-49CB-BE8A-716A78C1D692” en “90C55B00-AD37-404D-A0FC-A3B0F2CACB71”.
Gebruikte SQL

Gebruikte SQL:

select count(*)
from   read_file_text@Os('FILENAME.json') rft
join   jsontable
       ( 'value[*]'
         passing rft.file_contents
         columns EntryNumber int64 path 'EntryNumber'
         ,       LineNumber  int16 path 'LineNumber'
       ) jte

select jte.LineNumber
,      count(*)
from   read_file_text@Os('FILENAME.json') rft
join   jsontable
       ( 'value[*]'
         passing rft.file_contents
         columns EntryNumber int64 path 'EntryNumber'
         ,       LineNumber  int16 path 'LineNumber'
       ) jte
where  jte.LineNumber in (0, 1, 9999)
group 
by     jte.LineNumber
order
by     LineNumber

--
-- Tel rijen.
--
create or replace table countqueries@inmemorystorage
as
select ste.name
,      sdn.label
,      sdn.code
,      replace(ODATA_SERVICE_URL_FOR_SELECT, '{division}', sdn.code) || '/$count'
       url
,      ste.name || ' @ ' || sdn.label
       orig_system_group
from   SystemDivisions@eol sdn
join   SYSTEMTABLES@DataDictionary ste
on     ste.PROVIDER_NAME = 'ExactOnlineAll'
and    ste.CATALOG       = 'ExactOnlineREST'
and    ste.schema        = 'Sync'
and    ste.name          = 'SyncTransactionLines'

--
-- Stuur de API-verzoeken zonder verdere interpretatie door als een
-- "Native Scalar Request". De resultaten worden vastgelegd in
-- dezelfde tabel "NativePlatformScalarRequest".
--
-- Er wordt gebruik gemaakt van XML-output. JSON wordt verkregen
-- door Content-Type in te stellen.
--
insert into exactonlinerest..nativeplatformscalarrequests@eol
( url
, orig_system_group
, fail_on_error
)
select url
,      orig_system_group
--
-- Vervang door false als je niet overal rechten op hebt en true
-- als je dat wel zeker hebt.
-- Tabellen waar de huidige Exact Online gebruiker geen rechten 
-- op heeft worden bij false stilletjes overgeslagen.
--
,      false
from   COUNTQUERIES@InMemoryStorage

--
-- Per tabel en administratie het aantal rijen.
--
select orig_system_group
,      RESULT_TEXT
from   exactonlinerest..NATIVEPLATFORMSCALARREQUESTS@eol
where  SUCCESSFUL = 'Y'
and    orig_system_group is not null

select jte.Description
,      count(*)
from   read_file_text@Os('C:\temp\forums3468\geval2.json') rft
join   jsontable
       ( 'value[*]'
         passing rft.file_contents
         columns EntryNumber int64    path 'EntryNumber'
         ,       LineNumber  int16    path 'LineNumber'
         ,       Description varchar2 path 'Description'
       ) jte
group
by     jte.Description

select jte.*
from   read_file_text@Os('C:\temp\forums3468\geval2.json') rft
join   jsontable
       ( 'value[*]'
         passing rft.file_contents
         columns EntryNumber int64    path 'EntryNumber'
         ,       LineNumber  int16    path 'LineNumber'
         ,       Description varchar2 path 'Description'
       ) jte
where  jte.Description like 'invantive%'
        or jte.Description like 'itgenboe115%'