Solved

Leverage import/export scenario data extraction logic in graph extension?

  • 24 August 2022
  • 9 replies
  • 201 views

I'm building an integration that needs to extract data from an Acumatica record and build a json string. My process for doing this is using an export scenario to define what data gets pulled from the record. Is there any way that I can leverage the logic in SYMappingUtils/SYImportProcessor to pull data? Ideally I want to pass the record and the mapping to a method, and it returns the mapped data/formula evaluations from that record. Below is how I've been able to get a portion of the data, but it doesn't work for most PXSelector fields. It pulls the integer key value and doesn't return the CD value like the export scenario does.

 

Base.Views[objectName].Cache.GetValue(Base.Views[objectName].Cache.Current, fieldName);
icon

Best answer by rosenjon 31 August 2022, 18:57

View original

9 replies

Userlevel 6
Badge +5

@jacobslotta06 Why not use the REST API for this instead?

@jacobslotta06 Why not use the REST API for this instead?

I guess we could, but we want it to be real time and able to be configured via the UI so others can change it later on. I know we could use push notifications to solve the first issue, but nothing I can think of would solve the other issue.

Userlevel 6
Badge +5

 we want it to be real time

 

Can you explain what you mean by “real time”? If you use an export scenario, doesn’t this mean someone has to run the export scenario in order for the export to happen?

I’m using the export scenario as a screen that users can use to map values from the Acumatica screen to the json request. The export scenario isn’t actually run. It’s just a mapping table. Say someone changes the status on a stock item from “Inactive” to “Active.” When they save the record, we need to look at the export scenario defined as the mapping table for that message, build the message (a custom Acumatica record), and send it via Rest API.

Userlevel 6
Badge +5

@jacobslotta06 I think it might be helpful if you describe this end-to-end process in more detail. Forget about mapping things. What are you trying to do, end-to-end.

For example, “every time a user updates a sales order, I want Acumatica to send the contents of that sales order as a json encoded string to system x’s REST Api”.

Each Acumatica entity corresponds to a message in this external API system. Each message is basically just a JSON object with certain keys. There are several scenarios, but I’ll stick with a stock item. If someone creates a new stock item, we need to create a JSON object for the 1110 message and send it to this receiving endpoint. For testing right now, I have the message creation attached to actions. When someone creates a new stock item and saves it, the action is called. When someone updates an existing stock item, the action is called. We build the message and then need to populate it with the values from the Acumatica record (currently being done with the export scenario mappings). The message is saved as a string on a custom Acumatica record. An action on this custom record will send the JSON value via the Rest API to the external system. All of this works, but I wanted an easier way to extract the value similar to how Acumatica does in export scenarios.

public PXAction<InventoryItem> Export1110;
        [PXUIField(DisplayName = "Export 1110", MapViewRights = PXCacheRights.Select, MapEnableRights = PXCacheRights.Update)]
        [PXButton(CommitChanges = true, VisibleOnDataSource = true)]
        public virtual IEnumerable export1110(PXAdapter adapter)
        {
            if (ItemHelper.shouldPullItem(Base) == true)
            {
                MWA_1110.export1110Process(null, Base, MWASetup.Current);
            }
            return adapter.Get();

        }

 

public static void export1110Process(string mode, InventoryItemMaint Base, MWAIntegrationSetup mwaSetup)
        {

            Dictionary<string, string> dictToSend = new Dictionary<string, string>();
            if (mode != null)
            {
                dictToSend.Add("Mode", mode);
            }
            DataExtractor extractor = new DataExtractor(Base, mwaSetup);
            extractor.prepareData("MWA_1110", dictToSend, null);

        }

protected virtual void InventoryItem_RowPersisted(PXCache sender,
                                                     PXRowPersistedEventArgs e)
        {
         
            if (e.TranStatus == PXTranStatus.Completed)
            {
                switch (e.Operation.Command())
                {
                    case PXDBOperation.Insert:

                        if (ItemHelper.shouldPullItem(Base) == true)
                        {

// “U” corresponds to “Upsert”
                            MWA_1110.export1110Process("U", Base, MWASetup.Current);
                       
                        }
                        break;

………………………………..

 public void prepareData(string objectName, Dictionary<string, string> extraVals, object currentRecord)
        {

// Find export scenario with name “MWA_1110”

 SYMapping map = PXSelect<SYMapping, Where<SYMapping.name, Equal<Required<SYMapping.name>>>>.Select(graph, objectName);
            
            if (map != null)
            {

// get all mapping details for current scenario
                var mapDetails = PXSelect<SYMappingField, Where<SYMappingField.mappingID, Equal<Required<SYMappingField.mappingID>>>, OrderBy<Asc<SYMappingField.orderNumber>>>.Select(graph, map.MappingID);
                foreach (SYMappingField d in mapDetails)
                {
                    if (d.IsVisible == true)
                    {

// call function that extracts value from currentRecord based on the mappings we have set up
                        string translatedVal = getValueFromText(d.ObjectNameHidden, d.FieldNameHidden, currentRecord, map.ScreenID);
                        if (!dictToSend.ContainsKey(d.Value))
                        {
                            dictToSend.Add(d.Value, translatedVal);
                        }
                    }
                }
            }

 CreateJSONRequest(dictToSend, messageType);

………………………………………………………

 public void CreateJSONRequest(Dictionary<string, string> dictToSend, string messageType)
        {
            var json = JsonConvert.SerializeObject(dictToSend, Newtonsoft.Json.Formatting.Indented);
            MWATestRequestMaint trMaint = PXGraph.CreateInstance<MWATestRequestMaint>();
            MWARequest tr = new MWARequest();
            tr.RequestJsonData = json;
            tr.MessageType = messageType;
            trMaint.TestRequest.Insert(tr);
            trMaint.Actions.PressSave();
            if (MWASetup.AutoExport == true)
            {
                trMaint.Export.Press();

// export to external Rest API, save response and mark as successful/failed for further processing
            }
        }

 

….

Userlevel 6
Badge +5

Ok I see now. Basically you want to tap into the native UI for export mapping, so that end users can maintain the mappings using the already available tools.

I mean, for starters, have you tried creating a customization project and creating an instance of SYMappingTools/SYImportProcessor? It’s possible you can just use them directly….

I think you might have better luck writing a translation layer that is external to Acumatica, that pulls the data you want, translates it into the format for sending to the external system, and then mediates that interaction. There is the possibility that the import processor changes quite a bit between versions and then you are on a rollercoaster trying to chase its modifications. The REST API, on the other hand, is built to be relatively stable across upgrade cycles...

 

Yeah I tried accessing the SYMappingTools and other classes, but they’re all private. My translation layer is external so I can build some functions/translations that aren’t dependent on Acumatica’s logic. I could always fall back on that if needed. I was just wondering if it was possible.

Userlevel 6
Badge +5

@jacobslotta06 FWIW, I have basically built something similar for our own internal transition from a legacy ERP system to Acumtica. I wrote scripts that takes the legacy data structure, exports individual data items as JSON objects, and then have scripts that load Acumatica from those exported data objects.

The goal is to have a “continuous integration” style flow of data from the old to the new ERP, so that users can train on the new ERP but have all the same data that is in the old ERP.

Where necessary, I reprocess the base data items into “intermediate” objects that make them easier to import into Acumatica. As long as the export format of your data from system A is predictable, then your scripts should run reliabily no matter what the actual data is. Some people think in terms of individual pieces of data to export, but I think that exporting data as “objects” that contain all the data in the system makes it way easier to add on to later when you need another piece of data that you hadn't required in the beginning.

You can get fancy with this as well. For example, if you setup replication with SQL Server, you could use the replicated instance as the basis for your data exports, so you are never hitting the production system database.

Reply


About Acumatica ERP system
Acumatica Cloud ERP provides the best business management solution for transforming your company to thrive in the new digital economy. Built on a future-proof platform with open architecture for rapid integrations, scalability, and ease of use, Acumatica delivers unparalleled value to small and midmarket organizations. Connected Business. Delivered.
© 2008 — 2024  Acumatica, Inc. All rights reserved