Application Studio - Assign Object in epBinding Error - Help Needed

Continuing the discussion had in Application Studio - Function Output to Grid - Help Needed.

I am now attempting to write a slightly more complex function to a grid.

using (DataSet outDS = new DataSet("outDS"))
{
    var oDT = new DataTable("outResult"); // Create the DataTable with the name "outResult"
    var dfq = new Dictionary<Tuple<string, string>, DataRow>(); // Use string-based tuples for job number and due date
    oDT.Columns.Add("OpSeq", typeof(string)); // First column is OpSeq (row labels)

    // Sample data (this should be replaced with your actual table from the previous step)
    var data = new[] {
        new { JobNumber = "109673", DueDate = new DateTime(2025, 2, 8), OpSeq = "10" },
        new { JobNumber = "109673", DueDate = new DateTime(2025, 2, 8), OpSeq = "20" },
        new { JobNumber = "109673", DueDate = new DateTime(2025, 2, 8), OpSeq = "30" },
        new { JobNumber = "109673", DueDate = new DateTime(2025, 2, 8), OpSeq = "40" },
        new { JobNumber = "184438", DueDate = new DateTime(2025, 3, 9), OpSeq = "10" },
        new { JobNumber = "184438", DueDate = new DateTime(2025, 3, 9), OpSeq = "20" },
        new { JobNumber = "184438", DueDate = new DateTime(2025, 3, 9), OpSeq = "30" },
        new { JobNumber = "184438", DueDate = new DateTime(2025, 3, 9), OpSeq = "40" },
        new { JobNumber = "960313", DueDate = new DateTime(2025, 2, 25), OpSeq = "10" },
        new { JobNumber = "960313", DueDate = new DateTime(2025, 2, 25), OpSeq = "20" },
        new { JobNumber = "960313", DueDate = new DateTime(2025, 2, 25), OpSeq = "30" },
        new { JobNumber = "960313", DueDate = new DateTime(2025, 2, 25), OpSeq = "40" },
        new { JobNumber = "996094", DueDate = new DateTime(2025, 3, 6), OpSeq = "10" },
        new { JobNumber = "996094", DueDate = new DateTime(2025, 3, 6), OpSeq = "20" },
        new { JobNumber = "996094", DueDate = new DateTime(2025, 3, 6), OpSeq = "30" },
        new { JobNumber = "996094", DueDate = new DateTime(2025, 3, 6), OpSeq = "40" }
    };

    // Add columns for JobNumbers
    var jobNumbers = data.Select(d => d.JobNumber).Distinct().ToList();
    foreach (var jobNumber in jobNumbers)
    {
        oDT.Columns.Add(jobNumber, typeof(string)); // Ensure all columns are of type string
    }

    // Populate the DataTable with data
    foreach (var entry in data)
    {
        var jobNumber = entry.JobNumber;
        var dueDate = entry.DueDate.ToString("yyyy-MM-dd"); // Convert DateTime to string (YYYY-MM-DD format)
        var opSeq = entry.OpSeq; // OpSeq is already a string

        // Check if we have already encountered this OpSeq
        var existingRow = oDT.AsEnumerable().FirstOrDefault(r => r.Field<string>("OpSeq") == opSeq);

        if (existingRow == null)
        {
            // Create a new row for this OpSeq if it doesn't already exist
            var newRow = oDT.NewRow();
            newRow["OpSeq"] = opSeq; // Set the OpSeq (row label) as string
            oDT.Rows.Add(newRow);
            existingRow = newRow;
        }

        // Find the column for the JobNumber and assign the DueDate value as a string
        existingRow[jobNumber] = dueDate;
    }

    // Create a simplified output structure
    var simplifiedOutput = new List<object>();
    foreach (DataRow row in oDT.Rows)
    {
        var rowData = new Dictionary<string, string>();
        foreach (DataColumn column in oDT.Columns)
        {
            rowData[column.ColumnName] = row[column].ToString(); // Ensure everything is converted to string
        }
        simplifiedOutput.Add(rowData); // Add the row to the simplified list
    }

    // Add the simplified output to the DataSet
    outDS.Tables.Add(oDT);  // Add the DataTable to the DataSet

    // Optionally assign the outDS to a field, if necessary
    this.outDS = outDS; // Assuming you need this reference elsewhere in your class
}

I have the function outputting as it should:

I can see the data attempt to parse when I do my button click event:

The issue I’m running into is that even though the button click successfully shows me the data I am after, it gives me the following error:

image

And the grid I am using attempts to load columns that were previously in the dataview but have been removed:

What would cause these ghost columns to appear despite not being any of the columns listed within the data?

What is with the random error about everything must be a string when I have everything already converted to a string before it goes into the dataset?

I get the epBinding error about not knowing how to handle a DataSet. But it always works, so I think safe to ignore. Maybe we should file a bug on that one.

Re:ghost columns, it’s not coming from dataview - columns here don’t map to a grid directly. Check the customer grid panel-card-grid grid model object, there are columns here. Likely where it’s coming from. (There is one more set of columns that could be defined under provider model too)

Just a note, that this step is unecessary:

this.outDS = outDS;

Epicor’s Function Editor exposes the in/out variables you define in the function signature as local variables. They are aliased, so this.localVariable is the same as localVariable.
So this step is setting varX = varX, or no action. The DT is already added to the DS and ready for consumption by the function’s caller in the previous step. In the background you can imagine that epicor is piecing together a full method internally, with the usings and in/out variables declared above our code.

outDS.Tables.Add(oDT);

@GabeFranco you’re awesome!

The quick fix was to clear out the columns in the grid. After that, everything flowed as expected.

When I run this function and it populates the columns for the dv/grid, will it always populate the columns section? The reason I ask is that I am looking to have variable columns returned by this function. If these column headers need to be set in stone for this to work, I think I will have to come up with a way to flush all column header information before data is received.

When you don’t populate the columns section yourself (leave it completely empty) - Kinetic will load columns dynamically into the grid, yes.
However, this has drawbacks, as it uses an “epEditor” different from all the rest, more like the out of box Kendo UI Grid Column:


You lose specific filtering options that you’d get for dates for example (between date xxx and date xxx) it seems.

Kinetic highly prefers you specifically define the epEditor, if you run into a scenario where you need to because the out of box grid columns don’t do what you want, you can dynamically build the columns object with a property-set component. (but it sucks!)

I’ve run into something interesting. If I put all my data together the way I want and pass it along, all the rows come out exactly the way they should. However, the columns appear out of order. I thought it would follow the order at which I have the columns established. But, instead it acts almost like an alphanumeric sort on the columns. So I went ahead and changed the leading character on the column to be a 0 which I thought would move it to the front of the line, but instead it just puts it in the exact same place.

Any ideas on how to force the grid to simply put the data in based on the order it is received instead of just putting it in however it wants?