Script to fetch all invoices from Fortnox

This simple script fetch all invoices (header & lines) from Fortnox and store them @inmemory for use. It also feature an adaptive column structure in case the API evolves.

-----------------------------------------------
--- script to fetch all invoices from fortnox
--- Datacontainer : @fnx
---------------------------------------------------
--
-- 1) Create the inmemory tables for 
--		invoices_headers filled with data from fortnox
-- 		empty invoices_lines just to have the columns + datatypes
DECLARE
	docnb varchar2;
BEGIN
	create or replace table invoicesHeaders@inmemorystorage as 
		select * from  Invoices.Invoices@fnx;
	select DocumentNumber into docnb from invoicesHeaders@inmemorystorage LIMIT 1 ;
	create or replace table invoicesLines@inmemorystorage as 
		select * from Invoices.InvoiceLinesByDocumentNumber@fnx (docnb) LIMIT 0;
END

--select * from invoicesHeaders@inmemorystorage

-- 2) Build explicit column list for invoices_lines from DataDictionary
create or replace table invlines_cols@inmemorystorage as
select
  name,
  database_data_type,
  provider_name
from DataDictionary.Invantive.SYSTEMTABLECOLUMNS@DataDictionary
where Provider_Data_Container_Alias = 'fnx' AND table_name = 'InvoiceLinesByDocumentNumber'

select * from invlines_cols@inmemorystorage

-- 3) Build ONE explicit column list (unquoted), and the source list with il.
DECLARE
  v_cols    CLOB := '';   -- => Col1, Col2, ...
  v_cols_il CLOB := '';   -- => il.Col1, il.Col2, ...
  v_first   NUMBER := 1;
BEGIN
  FOR r IN (select name from invlines_cols@inmemorystorage)
  LOOP
    IF v_first = 1 THEN
      v_cols    := r.name;
      v_cols_il := 'il.'||r.name;
      v_first   := 0;
    ELSE
      v_cols    := v_cols    || ', '|| r.name;
      v_cols_il := v_cols_il || ', il.'|| r.name;
    END IF;
  END LOOP;
  create or replace table invlines_lists@inmemorystorage as
  select v_cols as cols_csv, v_cols_il as cols_il_csv
  from dual@datadictionary;
END;

--select * from invlines_lists@inmemorystorage

-- 4) Insert invoice lines using the same explicit column list on both sides
--    Progress every 50 invoices
DECLARE
  v_cols    CLOB;
  v_cols_il CLOB;
  v_sql     CLOB;
  v_cnt     integer := 0;
  v_total   integer;
  t0        datetime := sysdate;
  tb        datetime := sysdate;
  secs      decimal;
BEGIN
  SELECT cols_csv, cols_il_csv
    INTO v_cols, v_cols_il
    FROM invlines_lists@inmemorystorage;
  SELECT COUNT(*) INTO v_total FROM invoicesHeaders@inmemorystorage;
  dbms_output.put_line('Invoice lines fetch started at '||
                       to_char(t0,'YYYY-MM-DD HH24:MI:SS')||
                       ' | invoices='||to_char(v_total));
  FOR r IN (SELECT DocumentNumber FROM invoicesHeaders@inmemorystorage ORDER BY DocumentNumber) LOOP
    -- Looks like: insert into invoicesLines (col1, col2, ...) select il.col1, il.col2, ...
    v_sql :=
      'insert into invoicesLines@inmemorystorage ('|| v_cols ||')
       select '|| v_cols_il ||'
       from Invoices.InvoiceLinesByDocumentNumber@fnx(' || r.DocumentNumber || ') il';
    execute immediate v_sql;
    --dbms_output.put_line(v_sql);
    --dbms_output.put_line(v_cnt);
    v_cnt := v_cnt + 1;
    IF MOD(v_cnt, 50) = 0 OR v_cnt = v_total THEN
      secs := round((sysdate - tb)*60*60*24,3);
      dbms_output.put_line('Processed '|| v_cnt || '/' || v_total ||
                           ' invoices; last 50 in '|| round(secs,2) ||'s');
      tb := sysdate;
    END IF;
  END LOOP;
  secs := (sysdate - t0) * 86400;
  dbms_output.put_line('Completed '||to_char(v_cnt)||'/'||to_char(v_total)||
                       ' invoices; total time '||to_char(round(secs,2))||'s');
   exception
      when others
      then
         dbms_output.put_line('error: ' || sqlerrm);
         dbms_output.put_line(chr(13) || 'counter stopped at :' || v_cnt);
         dbms_output.put_line(chr(13) || v_sql);
end;



-- see the restults
select * except rowid$ from invoicesHeaders@inmemorystorage
--
select * except rowid$ from invoicesLines@inmemorystorage