Deleted everything.
Imported eveything.
Worked flawlessly in the new company.
Great program!!!
I love it!!!
Thank you!!!
Richard
Deleted everything.
Imported eveything.
Worked flawlessly in the new company.
Great program!!!
I love it!!!
Thank you!!!
Richard
Probably just had to add the other company to the security in the function library.
Wait until you see whatâs next..
Iâm late to the party here, but I just tried this out and LOVE it! Thank you @klincecum!!!
This makes refreshing a Pilot db that users are still testing in so, so, much easier.
Has anyone tried exporting BOOs/BOMs? We often have people trying out new manufacturing routings in Pilot db, and when theyâre happy with the new routing, they have to copy/paste them into the Live db.
@klincecum , can I export myself?
Exporting BOM data is straightforward. The table you export from is PartMtl, and the DMT upload is âBill of Materialsâ. All the fields in PartMtl are in the DMT upload, AND you need both a âPlantâ and âECOGroupIDâ field for the DMT upload.
BOO data is a little more complicated. There are two linked tables, PartOpr and PartOpDtl (the second table holds the ResourceGroup/Resource/Capability capacity constraint). The DMT upload is âBill of Operationsâ, and also requires a Plant and an ECOGroupID.
The way the DMT works for this is literally the same way youâd add (or modify) a Part Revision record through the user interface. Each line in the DMT upload essentially checks out the Part Revision to the Engineering Workbench (which is why you need the ECOGroupID field), makes the change, and then approves and checks the part revision back in.
These are NOT fast DMT uploads. Your RPM (records per minute) will almost certainly be below 100, and depending on complexity, maybe even less than 50. Do it in small chunks⌠error message tend to be, shall we say, not incredibly helpful.
Question: Would it be possible to use the task agent to complete this in the background so that it doesnât freeze the front end, e.g., export to Excel?
Something else that might also be useful is the ability to export KNTCPersLayer KNTCCustLayer and possibly components (we arenât using any).
It is possible to âbulk exportâ from Application Studio, but it would be nice to have an âall-in-oneâ tool.
{
"method": "POST",
"url": "https://host.epicorsaas.com/tenant/api/v2/odata/ACME_LAB/Ice.LIB.MetaFXSvc/GetApplications",
"queryParams": {},
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer ....",
"callSettings": "{\"Company\":\"ACME_LAB\",\"Plant\":\"ACME_01\"}",
"SessionInfo": "{\"sessionId\":\"45058\"}",
"x-epi-request-etag": "true",
"x-epi-extension-serialization": "full-metadata",
"Accept": "application/json, text/plain, */*"
},
"body": {
"request": {
"Type": "view",
"SubType": "",
"SearchText": "",
"IncludeAllLayers": true,
"IncludePersLayers": true
}
}
}
{
"headers": {
"access-control-allow-credentials": "true",
"access-control-allow-origin": "https://host.epicorsaas.com",
"access-control-expose-headers": "ContextHeader",
"cache-control": "no-cache",
"callinfo": "{\"CorrelationId\":\"bc9f\"}",
"connection": "keep-alive",
"content-length": "461910",
"content-type": "application/json; charset=utf-8",
"date": "Tue, 20 Jan 2026 16:34:36 GMT",
"telemetry": "{\"TelemetryKey\":\"b6f48\",\"Telemetry\":true}",
"vary": "Origin"
},
"body": {
"returnObj": [
{
"Id": "Erp.UI.ProjectEntry",
"Type": "view",
"SubType": "Apps",
"LastUpdated": "2026-01-20T00:00:00",
"IsLayerDisabled": false,
"SystemFlag": true,
"HasDraftContent": false,
"CreatedBy": "",
"CanAccessBase": false,
"SecurityCode": "",
"LastUpdatedBy": ""
},
{
"Id": "Erp.UI.POEntry",
"Type": "view",
"SubType": "Apps",
"LastUpdated": "2026-01-19T00:00:00",
"IsLayerDisabled": false,
"SystemFlag": true,
"HasDraftContent": false,
"Layers": [
{
"Id": "Erp.UI.POEntry",
"SubType": "Apps",
"LastUpdated": "2024-08-24T00:00:00",
"IsPublished": true,
"IsSilentExport": false,
"TypeCode": "KNTCCustLayer",
"Company": "ACME_LAB",
"LayerName": "ACME_Science",
"DeviceType": "Desktop",
"CGCCode": "",
"SystemFlag": false,
"HasDraftContent": false,
"LastUpdatedBy": "EpicAdmin"
}
},
{
"Id": "Erp.UI.PartEntry",
"Type": "view",
"SubType": "Apps",
"LastUpdated": "2026-01-20T00:00:00",
"IsLayerDisabled": false,
"SystemFlag": true,
"HasDraftContent": false,
"Layers": [
{
"Id": "Erp.UI.PartEntry",
"SubType": "Apps",
"LastUpdated": "2025-12-17T00:00:00",
"IsPublished": true,
"IsSilentExport": false,
"TypeCode": "KNTCPersLayer",
"Company": "ACME_LAB",
"LayerName": "userID_1^ACME_Science",
"DeviceType": "Desktop",
"CGCCode": "",
"SystemFlag": false,
"HasDraftContent": false,
"LastUpdatedBy": "userID_1"
}.
{
"Id": "Erp.UI.PartEntry",
"SubType": "Apps",
"LastUpdated": "2025-04-09T00:00:00",
"IsPublished": true,
"IsSilentExport": false,
"TypeCode": "KNTCPersLayer",
"Company": "ACME_LAB",
"LayerName": "userID_2^ACME_Science",
"DeviceType": "Desktop",
"CGCCode": "",
"SystemFlag": false,
"HasDraftContent": false,
"LastUpdatedBy": "userID_2"
}
}
]
},
"status": 200,
"statusText": "OK"
}
{
"method": "POST",
"url": "https://host.epicorsaas.com/SaaS681/api/v2/odata/ACME_LAB/Ice.LIB.MetaFXSvc/ExportLayers",
"queryParams": {},
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer .....",
"callSettings": "{\"Company\":\"ACME_LAB\",\"Plant\":\"ACME_01\"}",
"SessionInfo": "{\"sessionId\":\"45058b\"}",
"x-epi-request-etag": "true",
"x-epi-extension-serialization": "full-metadata",
"Accept": "application/json, text/plain, */*"
},
"body": {
"apps": [
{
"Id": "Erp.UI.POEntry",
"SubType": "Apps",
"LastUpdated": "2024-08-24T06:00:00.000Z",
"IsPublished": true,
"IsSilentExport": false,
"TypeCode": "KNTCCustLayer",
"Company": "ACME_LAB",
"LayerName": "ACME_Science",
"DeviceType": "Desktop",
"CGCCode": "",
"SystemFlag": false,
"HasDraftContent": false,
"LastUpdatedBy": "EpicAdmin",
"Type": "view",
"initialPosition": 1815,
"UniqueId": "Erp.UI.POEntryACME_ScienceKNTCCustLayerDesktop",
"Status": 0,
"IsLayerDisabled": false
},
{
"Id": "Erp.UI.PartEntry",
"SubType": "Apps",
"LastUpdated": "2025-12-17T07:00:00.000Z",
"IsPublished": true,
"IsSilentExport": false,
"TypeCode": "KNTCPersLayer",
"Company": "ACME_LAB",
"LayerName": "UserID_1^ACME_Science",
"DeviceType": "Desktop",
"CGCCode": "",
"SystemFlag": false,
"HasDraftContent": false,
"LastUpdatedBy": "cbanwo",
"Type": "view",
"initialPosition": 1906,
"UniqueId": "Erp.UI.PartEntryUserID_1^ACME_ScienceKNTCPersLayerDesktop",
"Status": 0,
"IsLayerDisabled": false
},
{
"Id": "Erp.UI.PartEntry",
"SubType": "Apps",
"LastUpdated": "2025-04-09T06:00:00.000Z",
"IsPublished": true,
"IsSilentExport": false,
"TypeCode": "KNTCPersLayer",
"Company": "ACME_LAB",
"LayerName": "UserID_2^ACME_Science",
"DeviceType": "Desktop",
"CGCCode": "",
"SystemFlag": false,
"HasDraftContent": false,
"LastUpdatedBy": "choneyman",
"Type": "view",
"initialPosition": 1907,
"UniqueId": "Erp.UI.PartEntryUserID_2^ACME_ScienceKNTCPersLayerDesktop",
"Status": 0,
"IsLayerDisabled": false
}
]
}
}
{
"headers": {
"access-control-allow-credentials": "true",
"access-control-allow-origin": "https://host.epicorsaas.com",
"access-control-expose-headers": "ContextHeader",
"cache-control": "no-cache",
"callinfo": "{\"CorrelationId\":\"f03c827d-7e89-44d1-8397-544199ae8c71\"}",
"connection": "keep-alive",
"content-length": "22744",
"content-type": "application/json; charset=utf-8",
"date": "Tue, 20 Jan 2026 16:55:11 GMT",
"telemetry": "{\"TelemetryKey\":\"b6f48e22-5af5-4dd9-a40c-769900bae46c\",\"Telemetry\":true}",
"vary": "Origin"
},
"body": {
"returnObj": "<BLOB>"
},
"status": 200,
"statusText": "OK"
}
I thought I would turn my hand at this; but Iâm running into an error:
{
"method": "POST",
"url": "https://host.epicorsaas.com/instance/api/v2/odata/CompanyID/Ice.Lib.FileTransferSvc/DownloadFileForCompany",
"queryParams": {},
"headers": {
"Content-Type": "application/json; charset=utf-8",
"Authorization": "Bearer ey....ek",
"SessionInfo": "{\"sessionId\":\"79cf60ab-081c-44c9-8a97-0a1c93280251\"}",
"callSettings": "{\"Company\":\"CompanyID\",\"Plant\":\"Plant_ID\"}",
"x-epi-request-etag": "true",
"x-epi-extension-serialization": "full-metadata",
"Accept": "application/json, text/plain, */*"
},
"body": {
"folder": 5,
"serverPath": "EATT://ExportAllKineticPersonalizationLayers",
"companyID": "MS67964"
}
}
{
"headers": {
"access-control-allow-credentials": "true",
"access-control-allow-origin": "https://host.epicorsaas.com",
"access-control-expose-headers": "ContextHeader",
"cache-control": "no-cache,no-store",
"callinfo": "{\"CorrelationId\":\"1ad3cf82-5dfa-4596-bfc4-6e12091a2360\"}",
"connection": "keep-alive",
"content-type": "application/json",
"date": "Thu, 22 Jan 2026 15:09:25 GMT",
"expires": "-1",
"pragma": "no-cache",
"server": "nginx",
"telemetry": "{\"TelemetryKey\":\"\",\"Telemetry\":true}",
"transfer-encoding": "chunked",
"vary": "Origin"
},
"body": {
"HttpStatus": 400,
"ReasonPhrase": "REST API Exception",
"ErrorMessage": "Ice.BLException: Failure running plugin with path: \"EATT://ExportAllKineticPersonalizationLayers\"\r\n at Epicor.Customization.Bpm.BO.DownloadFileForCompanyPreProcessingDirective_InterceptFileDownloadForCompanyRequest_61344827A11840BB948C74C379F718B0.A001_CustomCodeAction()",
"ErrorType": "Ice.BLException",
"ErrorDetails": [
{
"Message": "Ice.BLException: Failure running plugin with path: \"EATT://ExportAllKineticPersonalizationLayers\"\r\n at Epicor.Customization.Bpm.BO.DownloadFileForCompanyPreProcessingDirective_InterceptFileDownloadForCompanyRequest_61344827A11840BB948C74C379F718B0.A001_CustomCodeAction()",
"Type": "Error",
"Program": "Ice.Lib.FileTransfer.DownloadFileForCompany.dll",
"Method": "A001_CustomCodeAction"
}
],
"CorrelationId": "1ad3cf82-5dfa-4596-bfc4-6e12091a2360"
},
"status": 400,
"statusText": "Bad Request"
}
/*
* ==========================================================================================
* AUTHOR: Kevin Barrow
* COPYRIGHT: Kevin Barrow 2026
* LICENSE: MIT
* ==========================================================================================
* Library: ExportAllTheThings
* Function: ExportAllKineticPersonalizationLayers
* Description: This plugin downloads all Kinetic Personalization Layers.
* ==========================================================================================
*
* INPUTS: NONE
*
* OUTPUTS:
* BOOL: Success -> Function Success / Failure
* STRING: ListErrorsJson -> Json Serialized List<Exception>
* STRING: ZipBase64 -> Base64 Encoded Byte Array
*
* CHANGELOG:
* 01/21/2026 | kbarrow | Kevin Barrow | Initial Implementation via Core_ExportKineticMetaFX
*
* ==========================================================================================
*/
//Helper Functions Section----------------------------------------------------------------------------------------------------------------------------------------->
Func<Exception, string, string> AddExceptionToList = (exception, exceptionListJson) =>
{
List<Exception> exceptionList = new List<Exception>(){exception};
if(!String.IsNullOrEmpty(exceptionListJson)) { try { exceptionList.AddRange( JsonConvert.DeserializeObject<List<Exception>>(exceptionListJson) ); } catch {} }
return JsonConvert.SerializeObject(exceptionList);
};
//<-----------------------------------------------------------------------------------------------------------------------------------------Helper Functions Section
try
{
//****
CallService<Ice.Contracts.MetaFXSvcContract>(metaFX =>
{
// Configuration for Personalization Layers
var config = new
{
ExportBaseApps = false,
ExportLayers = true,
SystemFlag = false,
LayerTypeCodes = new [] { "KNTCPersLayer" },
IncludePersLayers = true
};
string configJson = JsonConvert.SerializeObject(config);
// Call Centralized Core Function
string resultJson = ThisLib.Core_ExportKineticMetaFX(configJson);
dynamic result = JsonConvert.DeserializeObject(resultJson);
Success = result.Success;
ListErrorJson = result.ListErrorJson;
ZipBase64 = result.ZipBase64;
});
//****
}
catch (Exception ex)
{
Success = false;
ListErrorJson = AddExceptionToList(ex, ListErrorJson);
}
finally
{
//Maybe later?
}
/*
* ==========================================================================================
* AUTHOR: Kevin Barrow
* COPYRIGHT: Kevin Barrow 2026
* LICENSE: MIT
* ==========================================================================================
* Library: ExportAllTheThings
* Function: Core_ExportKineticMetaFX
* Description: Centralized logic for exporting Kinetic MetaFX artifacts.
* ==========================================================================================
*
* INPUTS:
* STRING: ConfigJson -> JSON configuration object defining what to export.
* {
* "ExportBaseApps": bool,
* "ExportLayers": bool,
* "SystemFlag": bool? (null=all, true=system, false=custom),
* "LayerTypeCodes": [string],
* "CreatedBy": string,
* "SearchText": string,
* "IncludePersLayers": bool
* }
*
* OUTPUTS:
* STRING: ResultJson -> JSON object { Success, ListErrorJson, ZipBase64 }
*
* CHANGELOG:
* 01/21/2026 | kbarrow | Kevin Barrow | Initial Implementation
* 01/22/2026 | kbarrow | Kevin Barrow | Added SearchText and IncludePersLayers support
*
* ==========================================================================================
*/
Func<Exception, string, string> AddExceptionToList = (exception, exceptionListJson) =>
{
List<Exception> exceptionList = new List<Exception>(){exception};
if(!String.IsNullOrEmpty(exceptionListJson)) { try { exceptionList.AddRange( JsonConvert.DeserializeObject<List<Exception>>(exceptionListJson) ); } catch {} }
return JsonConvert.SerializeObject(exceptionList);
};
string zipBase64 = "";
string listErrorJson = "";
bool success = false;
try
{
dynamic config = JsonConvert.DeserializeObject(ConfigJson);
bool exportBaseApps = config.ExportBaseApps ?? false;
bool exportLayers = config.ExportLayers ?? false;
bool? systemFlag = config.SystemFlag;
JArray layerTypeCodesJ = config.LayerTypeCodes;
List<string> layerTypeCodes = layerTypeCodesJ?.ToObject<List<string>>();
string createdBy = config.CreatedBy;
string searchText = config.SearchText;
bool includePersLayers = config.IncludePersLayers ?? false;
// Auto-enable personalization fetch if we are asking for personalization layers
if (layerTypeCodes != null && layerTypeCodes.Contains("KNTCPersLayer"))
{
includePersLayers = true;
}
CallService<Ice.Contracts.MetaFXSvcContract>(metaFX =>
{
var request = new Epicor.MetaFX.Core.Models.Applications.ApplicationRequest()
{
Type = "view",
SubType = "",
SearchText = searchText ?? "",
IncludeAllLayers = true,
IncludePersLayers = includePersLayers
};
var applications = metaFX.GetApplications(request);
Dictionary<string, string> mainZipFileDictionary = new Dictionary<string, string>();
foreach(var app in applications)
{
// Base Apps Logic
if (exportBaseApps)
{
bool match = true;
if (systemFlag.HasValue) match = (app.SystemFlag == systemFlag.Value);
if (match)
{
try
{
var list = new List<Epicor.MetaFX.Core.Models.Layers.EpMetaFxLayerForApplication> {
new Epicor.MetaFX.Core.Models.Layers.EpMetaFxLayerForApplication() { Id = app.Id }
};
byte[] bytes = metaFX.ExportLayers(list);
mainZipFileDictionary.Add($"{app.Id}.zip", Convert.ToBase64String(bytes));
}
catch (Exception ex)
{
ex.Data.Add("AppID", app.Id);
listErrorJson = AddExceptionToList(ex, listErrorJson);
}
}
}
// Layers Logic
if (exportLayers && app.Layers != null)
{
foreach(var layer in app.Layers)
{
bool match = true;
// For layers, SystemFlag usually denotes if the layer itself is system-owned
if (systemFlag.HasValue) match = (layer.SystemFlag == systemFlag.Value);
if (match && layerTypeCodes != null && layerTypeCodes.Count > 0)
match = layerTypeCodes.Contains(layer.TypeCode);
if (match && !string.IsNullOrEmpty(createdBy))
match = (layer.LastUpdatedBy == createdBy || layer.CreatedBy == createdBy);
if (match)
{
try
{
var list = new List<Epicor.MetaFX.Core.Models.Layers.EpMetaFxLayerForApplication> { layer };
byte[] bytes = metaFX.ExportLayers(list);
string name = $"{app.Id}_{layer.LayerName}_{layer.TypeCode}.zip";
name = name.Replace(" ", "_").Replace("^", "_");
mainZipFileDictionary.Add(name, Convert.ToBase64String(bytes));
}
catch (Exception ex)
{
ex.Data.Add("Layer", layer.LayerName);
listErrorJson = AddExceptionToList(ex, listErrorJson);
}
}
}
}
}
if (mainZipFileDictionary.Count > 0)
{
string dictJson = JsonConvert.SerializeObject(mainZipFileDictionary);
zipBase64 = ThisLib.ZipFiles(dictJson);
}
});
success = true;
}
catch (Exception ex)
{
success = false;
listErrorJson = AddExceptionToList(ex, listErrorJson);
}
ResultJson = JsonConvert.SerializeObject(new { Success = success, ListErrorJson = listErrorJson, ZipBase64 = zipBase64 });
@spaceage did you ever figure out the sytax for getting custom layers for both system apps and non-system? I tried the above and got all system apps but not custom layer .jsonc
I never did figure it out, but I didnât put a ton of time into it either.
If you take a look at following it may give you the insight into what needs done, I tried turning my hand at it, but I havenât had a lot of time to delve into it.
Essentially though âLayersâ are a sub-component of a âBase Layerâ
My code was executing but wasnât returning anything, it may be the push you need to get everything working.
@klincecum So Iâm actually getting a similar error with my attempt at a custom function as I get when I try to run your code for system layers.
I wonder if that means my code will work for someone with the Epicor SDK?
âŚ
That one is broken for me too. I havenât had time to revisit.
WOOT!!! WOOT!!!
@klincecum @jbooker @spaceage
/*
* ==========================================================================================
* AUTHOR: Kevin Barrow
* COPYRIGHT: Kevin Barrow 2026
* LICENSE: MIT
* ==========================================================================================
* Library: ExportAllTheThings
* Function: ExportAllKineticPersonalizationLayers
* Description: This plugin downloads all Kinetic Personalization Layers.
* ==========================================================================================
*
* INPUTS: NONE
*
* OUTPUTS:
* BOOL: Success -> Function Success / Failure
* STRING: ListErrorsJson -> Json Serialized List<Exception>
* STRING: ZipBase64 -> Base64 Encoded Byte Array
*
* CHANGELOG:
* 01/21/2026 | kbarrow | Initial Implementation via Core_ExportKineticMetaFX
* 01/28/2026 | kbarrow | BPM-safe normalize + single-pass + no member declarations
* ==========================================================================================
*/
// --- Helpers as delegates (BPM-safe: no method declarations) ---
Func<object, string> ToBase64 = exportResult =>
{
if (exportResult == null) return null;
var bytes = exportResult as byte[];
if (bytes != null && bytes.Length > 0)
return Convert.ToBase64String(bytes);
var s = exportResult as string;
if (!string.IsNullOrWhiteSpace(s))
{
// If it looks like a file path, try to read bytes
if (s.IndexOfAny(new[] {'\\','/'}) >= 0)
{
try
{
var fileBytes = System.IO.File.ReadAllBytes(s);
return Convert.ToBase64String(fileBytes);
}
catch
{
// If file read fails, fall through and assume it's already base64
}
}
return s;
}
return null;
};
Func<Exception, string, string> AddExceptionToList = (exception, exceptionListJson) =>
{
List<Exception> exceptionList = new List<Exception>() { exception };
if (!String.IsNullOrEmpty(exceptionListJson))
{
try
{
var prior = JsonConvert.DeserializeObject<List<Exception>>(exceptionListJson);
if (prior != null) exceptionList.AddRange(prior);
}
catch
{
// ignore JSON issues
}
}
return JsonConvert.SerializeObject(exceptionList);
};
// --- Outputs ---
string zipBase64 = "";
string listErrorJson = "";
bool success = false;
try
{
// --- Input config (FIRST) ---
dynamic config = JsonConvert.DeserializeObject(ConfigJson);
bool exportBaseApps = config.ExportBaseApps ?? false;
bool exportLayers = config.ExportLayers ?? false;
bool? systemFlag = config.SystemFlag;
JArray layerTypeCodesJ = config.LayerTypeCodes;
List<string> layerTypeCodes = layerTypeCodesJ?.ToObject<List<string>>();
string createdBy = config.CreatedBy;
string searchText = config.SearchText;
bool includePersLayers = config.IncludePersLayers ?? false;
// Ensure personalization layers included if requested explicitly
if (layerTypeCodes != null && layerTypeCodes.Contains("KNTCPersLayer"))
includePersLayers = true;
// If nothing to export, finish early
if (!exportBaseApps && !exportLayers)
{
success = true;
ResultJson = JsonConvert.SerializeObject(new { Success = success, ListErrorJson = listErrorJson, ZipBase64 = zipBase64 });
return;
}
// --- Main service call (SINGLE PASS) ---
CallService<Ice.Contracts.MetaFXSvcContract>(metaFX =>
{
var request = new Epicor.MetaFX.Core.Models.Applications.ApplicationRequest()
{
Type = "view",
SubType = "",
SearchText = searchText ?? "",
IncludeAllLayers = true,
IncludePersLayers = includePersLayers
};
var applications = metaFX.GetApplications(request);
Dictionary<string, string> mainZipFileDictionary = new Dictionary<string, string>();
foreach (dynamic app in applications)
{
// --- BASE APPS ---
if (exportBaseApps)
{
bool match = true;
if (systemFlag.HasValue) match = (app.SystemFlag == systemFlag.Value);
if (match)
{
try
{
var baseAppExport = new Epicor.MetaFX.Core.Models.Layers.EpMetaFxLayerForApplication
{
Id = app.Id.ToString(),
Company = Session.CompanyID,
DeviceType = "Desktop"
};
var list = new List<Epicor.MetaFX.Core.Models.Layers.EpMetaFxLayerForApplication> { baseAppExport };
var exportObj = metaFX.ExportLayers(list);
string base64 = ToBase64(exportObj);
if (!string.IsNullOrEmpty(base64))
{
string fileName = $"{app.Id.ToString()}.zip";
if (!mainZipFileDictionary.ContainsKey(fileName))
mainZipFileDictionary.Add(fileName, base64);
}
}
catch (Exception ex)
{
listErrorJson = AddExceptionToList(ex, listErrorJson);
}
}
}
// --- LAYERS ---
if (exportLayers && app.Layers != null)
{
foreach (dynamic layer in app.Layers)
{
bool match = true;
if (systemFlag.HasValue) match = (layer.SystemFlag == systemFlag.Value);
if (match && layerTypeCodes != null && layerTypeCodes.Count > 0)
match = layerTypeCodes.Contains((string)layer.TypeCode);
if (match && !string.IsNullOrEmpty(createdBy))
match = (layer.LastUpdatedBy == createdBy || layer.CreatedBy == createdBy);
if (!match) continue;
try
{
var exportItem = new Epicor.MetaFX.Core.Models.Layers.EpMetaFxLayerForApplication
{
Id = layer.Id.ToString(),
LayerName = layer.LayerName.ToString(),
TypeCode = layer.TypeCode.ToString(),
Company = (layer.Company != null) ? layer.Company.ToString() : Session.CompanyID,
DeviceType = (layer.DeviceType != null) ? layer.DeviceType.ToString() : "Desktop",
IsPublished = true
};
var list = new List<Epicor.MetaFX.Core.Models.Layers.EpMetaFxLayerForApplication> { exportItem };
var exportObj2 = metaFX.ExportLayers(list);
string base64Data = ToBase64(exportObj2);
if (!string.IsNullOrEmpty(base64Data))
{
var safeLayerName = System.Text.RegularExpressions.Regex.Replace(
layer.LayerName.ToString(), @"[^a-zA-Z0-9_.\- ]", "_");
string fileName = $"{app.Id.ToString()}_{safeLayerName}.zip";
if (!mainZipFileDictionary.ContainsKey(fileName))
mainZipFileDictionary.Add(fileName, base64Data);
}
}
catch (Exception ex)
{
listErrorJson = AddExceptionToList(ex, listErrorJson);
}
}
}
}
if (mainZipFileDictionary.Count > 0)
{
string dictJson = JsonConvert.SerializeObject(mainZipFileDictionary);
zipBase64 = ThisLib.ZipFiles(dictJson);
}
});
success = true;
}
catch (Exception ex)
{
success = false;
listErrorJson = AddExceptionToList(ex, listErrorJson);
}
ResultJson = JsonConvert.SerializeObject(new { Success = success, ListErrorJson = listErrorJson, ZipBase64 = zipBase64 });
ExportAllTheThings.efxj (85.8 KB)
@klincecum Iâve forked your repo and submitted a pull request with some significant enhancements.
For those looking for the most current version while the PR is pending, you can find the updated file and source below.
This version acts as a drop-in replacement for the original, adding comprehensive support for Kinetic layers and cross-company stability:
Kevin, when you have an opportunity to test/review, it would be great to get these updates merged and pinned to the top!
Best regards,
Kevin