Automating Serial Number matching w/ function - Help debugging, defining classes

Background on me: 15+ years in IT, but not a dev by any means.
Our environment: Kinetic 2024.2 | Cloud

I’m attempting to use a function to automate serial number matching. In this case, the parent and child serial numbers always literally match, so if the assembly is S/N 11111 then material is also S/N 11111. There’s a BPM that fires when material is issued to a job which automates serial number assignment (which works) and then calls this function to do the matching.

This is my current code using dynamic classes.

using Newtonsoft.Json;
using RestSharp;

// Input values - these are passed to the function as request parameters
string ipSerialNo = this.ipSerialNo.ToString();
string ipPartNum = this.ipPartNum.ToString();
string ipRevNum = this.ipRevNum.ToString();
string ipJobNum = this.ipJobNum.ToString();
int ipAssemblySeq = Convert.ToInt32(this.ipAssemblySeq);

// Base URL
string baseUrl = "https://.../api/v2/odata/000000/Erp.BO.SerialMatchingSvc/";

// API Key
string apiKey = "...";

// Basic Auth
string username = "...";
string password = "...";

// ChangeSerialNum
var client1 = new RestClient(baseUrl + "ChangeSerialNum");
var request1 = new RestRequest(Method.POST);
request1.AddHeader("Content-Type", "application/json");
request1.AddHeader("Authorization", $"Basic {basicAuth}"); // Basic Authentication
request1.AddHeader("X-API-Key", apiKey); // API key using X-API-Key header
request1.AddJsonBody(new
{
    ipSerialNo,
    ipPartNum,
    ipRevNum,
    ipJobNum,
    ipAssemblySeq
});
var response1 = client1.Execute(request1);
if (!response1.IsSuccessful)
{
    throw new Exception($"API call to ChangeSerialNum failed: {response1.Content}");
}
dynamic changeSerialNumResponse = JsonConvert.DeserializeObject<dynamic>(response1.Content);
Console.WriteLine($"ChangeSerialNum Response: {JsonConvert.SerializeObject(changeSerialNumResponse)}");

// GetAvailableToMatch
var client2 = new RestClient(baseUrl + "GetAvailableToMatch");
var request2 = new RestRequest(Method.POST);
request2.AddHeader("Content-Type", "application/json");
request2.AddHeader("Authorization", $"Basic {basicAuth}"); 
request2.AddHeader("X-API-Key", apiKey); 
request2.AddJsonBody(new
{
    ipType = "mtl",
    ds = new
    {
        AvailToMatch = new object[] { },
        SerialMatchAsmbl = new object[] { },
        SerialMatchHdr = changeSerialNumResponse.returnObj.SerialMatchHdr,
        SerialMatchMtl = changeSerialNumResponse.returnObj.SerialMatchMtl
    }
});
var response2 = client2.Execute(request2);
if (!response2.IsSuccessful)
{
    throw new Exception($"API call to GetAvailableToMatch failed: {response2.Content}");
}
dynamic getAvailableToMatchResponse = JsonConvert.DeserializeObject<dynamic>(response2.Content);

// UpdateSMMtl
var client3 = new RestClient(baseUrl + "UpdateSMMtl");
var request3 = new RestRequest(Method.POST);
request3.AddHeader("Content-Type", "application/json");
request3.AddHeader("Authorization", $"Basic {basicAuth}"); 
request3.AddHeader("X-API-Key", apiKey); 
var availToMatch = ((IEnumerable<dynamic>)getAvailableToMatchResponse.parameters.ds.AvailToMatch).Select(m => new
{
    m.Company,
    m.PartNum,
    m.SerialNumber,
    m.JobNum,
    m.IssueToAssembly,
    m.MtlSeq,
    Matched = true, // Match the material serial number
    Selected = true, // Select the material serial number
    m.AttributeSetID,
    m.AttributeSetIDDescription,
    m.AttributeSetIDShortDescription,
    m.RevisionNum,
    m.SysRowID,
    RowMod = "U"
});
request3.AddJsonBody(new
{
    ipMode = "J",
    ds = new
    {
        AvailToMatch = availToMatch,
        SerialMatchAsmbl = getAvailableToMatchResponse.parameters.ds.SerialMatchAsmbl,
        SerialMatchHdr = getAvailableToMatchResponse.parameters.ds.SerialMatchHdr,
        SerialMatchMtl = getAvailableToMatchResponse.parameters.ds.SerialMatchMtl
    }
});
var response3 = client3.Execute(request3);
if (!response3.IsSuccessful)
{
    throw new Exception($"API call to UpdateSMMtl failed: {response3.Content}");
}
dynamic updateSMMtlResponse = JsonConvert.DeserializeObject<dynamic>(response3.Content);

// SetFullyMatchedHidden
var client4 = new RestClient(baseUrl + "SetFullyMatchedHidden");
var request4 = new RestRequest(Method.POST);
request4.AddHeader("Content-Type", "application/json");
request4.AddHeader("Authorization", $"Basic {basicAuth}"); 
request4.AddHeader("X-API-Key", apiKey);
request4.AddJsonBody(new
{
    ipAssmblSeq = ipAssemblySeq,
    ipHideFullyMatched = false,
    ds = updateSMMtlResponse.parameters.ds
});
var response4 = client4.Execute(request4);
if (!response4.IsSuccessful)
{
    throw new Exception($"API call to SetFullyMatchedHidden failed: {response4.Content}");
}
dynamic setFullyMatchedHiddenResponse = JsonConvert.DeserializeObject<dynamic>(response4.Content);

// Log the final response
// Ice.Diagnostics.Log.WriteEntry($"SetFullyMatchedHidden Response: {JsonConvert.SerializeObject(setFullyMatchedHiddenResponse)}", System.Diagnostics.EventLogEntryType.Information);

Currently it fails at GetAvailableToMatch with this error.

"API call to GetAvailableToMatch failed: {\"HttpStatus\":400,\"ReasonPhrase\":\"REST API Exception\",\"ErrorMessage\":\"Serial Match Material not available.\",\"ErrorType\":\"Ice.BLException\",\"ErrorDetails\":[{\"Message\":\"Serial Match Material not available.\",\"Type\":\"Error\",\"Table\":\"SerialMatchMtl\",\"Program\":\"Erp.Services.BO.SerialMatching.dll\",\"Method\":\"GetAvailableToMatch\",\"LineNumber\":1563,\"ColumnNumber\":21}],\"CorrelationId\":\"e716d15a-1b43-40f8-affc-04660557a307\"}"

I thought maybe if I used strongly typed classes it would be easier to debug but when I tried, I couldn’t work out how to define the classes. I always got this error.

BPM009	Member declaration is not allowed inside code block

I’ve seen posts stating you can just create and reference a DLL but I don’t think I can do that on cloud. I thought maybe I could put the class declarations in its own function, but I get the same declaration error.

Any tips or advice at all are greatly appreciated. Thanks in advance.

Not being familiar with these BO methods, I can’t help much other than to suggest that you check your inputs. For now, spit those values into your exception message so you can see whether something is getting mangled in the request - especially these two that are coming directly from the response of the previous API call: