ExactOnline XML Manufacturing lacks Routing and Items tables

I need to create 1000+ new manufactured Bills of Materials versions in order to deploy a new process.

It had seemed the best approach would be via the XML interface.

However, I find that the ManufacturedBillOfMaterialRoutingDetails and ManufacturedBillOfMaterialItemDetails objects don’t have the fields of their corresponding tables available. Instead, they consist of mostly references to the parent ManufacturedBillOfMaterials.

I can successfully query ManufacturedBillOfMaterials, but find the child tables blank. I can successfully query via the REST interface for the parent and child tables. Although I can translate these results into an XML file for upload, it seems unlikely that I can use the Invantive XML interface to insert the new records since it lacks reference to the required fields.

image

This leaves inserting as independent tables via REST or manually breaking up an XML and using the Exact XML upload process. I may of course be missing something.

I would be grateful for any insight.

Is it possible to add the queries used?

Thank you for your prompt late night reply.

I’ve kept the queries simple for demonstration.

These queries of XML ManufacturedBillOfMaterialItemDetails and ManufacturedBillOfMaterialRoutingDetails produce no results. The fields of the associated tables are not listed as columns in the Invantive Query Tool.

image

select *
from   ExactOnlineXML.XML.ManufacturedBillOfMaterialItemDetails
limit  5000

select *
from   ExactOnlineXML.XML.ManufacturedBillOfMaterialRoutingDetails
limit  5000

This query of XML ManufacturedBillOfMaterial returns the expected data, except that no child table data is returned:

select *
from   ExactOnlineXML.XML.ManufacturedBillOfMaterials
limit  10

This query of REST ManufacturedBillOfMaterialMaterials corresponds to the XML ManufacturedBillOfMaterialItemDetails and returns the expected data:

image

select *
from   ExactOnlineREST.Manufacturing.BillOfMaterialMaterials
limit  10

This query of REST ManufacturedBillOfMaterialRoutings corresponds to XML ManufacturedBillOfMaterialRoutingDetails and returns the expected data:

image

select *
from   ExactOnlineREST.manufacturing.BillOfMaterialRoutings
limit  10

This query of REST ManufacturedBillOfMaterialVersions corresponds to XML ManufacturedBillOfMaterial and returns the expected data:

image

select *
from   ExactOnlineREST.Manufacturing.BillOfMaterialVersions
limit  10

It is possible that XML-tables are lacking fields that actually can have content due to limitations of the XML-documentation (the so-called “XSD” which can also be downloaded through the “Import/Export” menu in Exact Online). Exact has made little changes to extend or update the XML API in terms of documentation and functionality over the last five years. However, this limitation does not in any way restrict the uploading of XML. In fact, the use of insert, update and delete on the XML schema is not recommended if possible at all (there are few tables for which a module has been created).

The recommended way, which also opens all capabilities as described in the XSD, is the use of UploadXMLTopics. The use of UploadXMLTopics as illustrated in Mass copy pictures across Exact Online articles also has exactly the same functionality as the XML file that can be uploaded via the user interface of Exact Online.

Another example can be found below, which is a simplified version of the transaction mass import on https://get-my-report.com

It reads from an Excel file uploaded through a website, but exceltable also accepts local files when run on a device. Next, it transforms the data in the needed XML format for Exact Online. Then it uses UploadXMLTopics to upload the data, whereas the last queries select an overview of the results.

begin
  --
  -- Load transactions in Exact Online from an uploaded Excel sheet.
  --
  -- Retrieve transaction lines from Excel source.
  --
  create or replace table source@inmemorystorage
  as
  select cast(coalesce(division_code, to_number(:division_code_default)) as int32)
         division_code
         label "{res:itgen_eol_administration}"
  ,      trim(journal_code)
         journal_code
         label "{res:itgen_eol_journal_code}"
  ,      entry_number
         label "{res:itgen_eol_journal_entry_number}"
  ,      financial_year
         label "{res:itgen_eol_financial_year}"
  ,      financial_period
         label "{res:itgen_eol_financial_period}"
  ,      trim(description)
         description
         label "{res:itgen_eol_description}"
  ,      '[GMRIVEIMP:'
         || to_char(cast(coalesce(division_code, to_number(:division_code_default)) as int32))
         || '-'
         || :orig_system_group
         || '-'
         || trim(journal_code)
         || '-'
         || entry_number
         -- || '-'
         -- Not yet available in exceltable. Needs row_number from ITGEN-3552.
         -- || linenumber
         || '] '
         || replace(translate_resources('{res:itgen_gmr_imported_note_par1}'), '{0}', to_char(sysdate))
         note
         label "{res:itgen_eol_note}"
  ,      entry_date
         label "{res:itgen_eol_entry_date}"
  ,      trim(gl_account_code)
         gl_account_code
         label "{res:itgen_eol_general_ledger_account}"
  ,      trim(cost_center_code)
         cost_center_code
         label "{res:itgen_eol_cost_center}"
  ,      trim(cost_unit_code)
         cost_unit_code
         label "{res:itgen_eol_cost_unit}"
  ,      trim(currency_code)
         currency_code
         label "{res:itgen_eol_currency_code}"
  ,      trim(vat_code)
         vat_code
         label "{res:itgen_eol_vatcode}"
  ,      trim(account_code)
         account_code
         label "{res:itgen_eol_account_code}"
  ,      trim(project_code)
         project_code
         label "{res:itgen_eol_project_code}"
  ,      start_date
         label "{res:itgen_eol_start_date}"
  ,      end_date
         label "{res:itgen_eol_end_date}"
  ,      amount
         label "{res:itgen_eol_amount}"
  ,      null /* Not yet used. */
         references
         label "{res:itgen_reference}"
  ,      to_char(sysdate, 'YYYYMMDDHH24MISS') file_timestamp
         label "{res:itgen_time_stamp}"
  from   exceltable
         ( worksheet 'General Ledger Transactions'
           passing :upload_file
           skip empty rows
           skip first 1 rows
           columns division_code        int64    position 1
           ,       journal_code         varchar2 position 2
           ,       entry_number         int32    position 3
           ,       financial_year       int16    position 4
           ,       financial_period     int16    position 5
           ,       description          varchar2 position 6
           ,       entry_date           date     position 7
           ,       gl_account_code      varchar2 position 8
           ,       amount               number   position 9
           ,       cost_center_code     varchar2 position 10
           ,       cost_unit_code       varchar2 position 11
           ,       currency_code        varchar2 position 12
           ,       vat_code             varchar2 position 13
           ,       account_code         varchar2 position 14
           ,       project_code         varchar2 position 15
           ,       start_date           date     position 16
           ,       end_date             date     position 17
         )
  ;
  set use-http-disk-cache-read@eol false;
  set use-http-disk-cache-write@eol false;
  set use-http-memory-cache-read@eol false;
  set use-http-memory-cache-write@eol false;
  --
  -- Optional checks removed.
  --
  --
  -- Actual upload.
  --
  create or replace table sourcelinesxml@inmemorystorage
  as
  select src.division_code
  ,      src.entry_date
  ,      src.entry_number
  ,      src.journal_code
  ,      src.financial_year
  ,      src.financial_period
  ,      src.journal_code
         || '/'
         || to_char(src.entry_number)
         || ':'
         || to_char(src.financial_year)
         || '-'
         || to_char(src.financial_period)
         description
  ,      '<GLTransaction entry="'
         || src.entry_number
         || '">'
         || chr(13)
         || '<Journal code="'
         || src.journal_code
         || '" />'
         || chr(13)
         || '<FinYear number="'
         || src.financial_year
         || '" />'
         || '<FinPeriod number="'
         || src.financial_period
         || '" />'
         head
  ,      '<GLTransactionLine>'
         || '<Date>'
         || substr(xmlencode(src.entry_date), 1, 10)
         || '</Date>'
         || '<FinYear number="'
         || src.financial_year
         || '" />'
         || '<FinPeriod number="'
         || src.financial_period
         || '" />'
         || '<GLAccount code="'
         || src.gl_account_code
         || '" />'
         || case
            when src.cost_center_code is not null
            then '<Costcenter code="'
                 || src.cost_center_code
                 || '" />'
            end
         || case
            when src.cost_unit_code is not null
            then '<Costunit code="'
                 || src.cost_unit_code
                 || '" />'
            end
         || case
            when src.project_code is not null
            then '<Project code="'
                 || src.project_code
                 || '" />'
            end
         || case
            when src.account_code is not null
            then '<Account code="'
                 || src.account_code
                 || '" />'
            end
         || case
            when src.description is not null
            then xmlelement('Description', src.description, false)
            end
         || case
            when src.note is not null
            then xmlelement('Note', src.note, false)
            end
         || case
            when src.references is not null
            then '<References>'
                 || xmlelement('InvoiceNumber', src.references, false)
                 || '</References>'
            end
         || '<Amount>'
         || xmlelement('Value', src.amount, false)
         || case
            when src.currency_code is not null
            then '<Currency code="'
                 || src.currency_code
                 || '" />'
            end
         || case
            when src.vat_code is not null
            then '<VAT code="'
                 || src.vat_code
                 || '" />'
            end
         || '</Amount>'
         || case
            when src.start_date is not null
            then xmlelement('DeferredFrom', src.start_date, false)
            end
         || case
            when src.end_date is not null
            then xmlelement('DeferredTo', src.end_date, false)
            end
         || '</GLTransactionLine>'
         line
  from   source@inmemorystorage src
  ;
  --
  create or replace table xmlupload@inmemorystorage
  as
  select 'GLTransactions' topic
  ,      division_code
  ,      null orig_system_reference
  ,      :orig_system_group orig_system_group
  ,      xmlformat
         ( '<?xml version="1.0" encoding="utf-8"?>'
           || '<eExact xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="eExact-XML.xsd">'
           || xmlcomment(replace(translate_resources('{res:itgen_gmr_for_company_par1}'), '{0}', division_code))
           || xmlcomment(replace(translate_resources('{res:itgen_gmr_orig_system_group_par1}'), '{0}', :orig_system_group))
           || '<GLTransactions>'
           || xml
           || '</GLTransactions>'
           || '</eExact>'
         )
         filecontents
  from   ( select division_code
           ,      listagg
                  ( head
                    || lines
                    || '</GLTransaction>'
                  , ''
                  )
                  xml
           from   ( select brl.division_code
                    ,      brl.head
                    ,      listagg(brl.line, '') lines
                    from   sourcelinesxml@inmemorystorage brl
                    group
                    by     brl.division_code
                    ,      brl.head
                  )
           group
           by     division_code
         )
  ;
  --
  -- Upload into Exact Online.
  --
  insert into UploadXMLTopics@eol
  ( topic
  , payload
  , division_code
  , orig_system_reference
  , orig_system_group
  , fragment_payload_flag
  , fragment_max_size_characters
  , fail_on_error
  )
  select topic
  ,      filecontents
  ,      to_char(division_code)
  ,      orig_system_reference
  ,      orig_system_group
  --
  -- Try to split into pieces of 25 KB at most.
  --
  ,      true fragment_payload_flag
  ,      25000 fragment_max_size_characters
  --
  -- Returns error through user interface, not as a HTTP 500 error.
  --
  ,      false fail_on_error
  from   xmlupload@inmemorystorage
  order
  by     division_code
  ,      orig_system_reference
  ,      orig_system_group
  ;
end;

--
-- Include input in resulting spreadsheet.
--
select /*+ result_set_name("Source") */
       *
from   source@inmemorystorage

--
-- Results.
--
select /*+ result_set_name("Results") */
       uxt.transaction_id
,      uxt.successful
,      uxt.division_code
,      uxt.orig_system_reference
,      uxt.orig_system_group
,      uxt.date_started_utc
,      uxt.date_ended_utc
,      substr(uxt.payload, 1, 32767)
       payload_32k
       label "{res:itgen_eol_payload}"
,      substr(uxt.result, 1, 32767)
       result_32k
       label "{res:itgen_result}"
,      substr(uxt.result_errors, 1, 32767)
       result_errors_32k
       label "{res:itgen_eol_result_errors}"
,      substr(uxt.payload_retry_failed, 1, 32767)
       payload_retry_failed
       label "{res:itgen_eol_payload_retry_failed}"
from   UploadXMLTopicFragments@eol uxt
order
by     uxt.transaction_id desc

Ah. Excellent.
That all makes sense. You have created an impressively powerful tool. I have things to learn - my favorite thing!
Thank you. This is very helpful.

You are welcome! Thank you for the appreciation.