SalesRepSvc OData Metadata / PATCH datatype discrepancy for CommissionPercent & SysRevID


(Matt Yutzy) #1

Hello,

I’ve recently started interacting with the Epicor REST API (10.2.100) and am confused about what appears to be a datatype discrepancy between the OData Metadata and the PATCH method for two fields in the SalesRepRow model within SalesRepSvc. I’m probably misunderstanding something but thought I’d ask here to find out.

In {{Host}}/api/help/odata/Erp.BO.SalesRepSvc/index#!/OData/getMetadata the fields in question are defined as follows:

<Property Name="CommissionPercent" Type="Edm.Decimal" />
<Property Name="SysRevID" Type="Edm.Int64" />

However when I execute the PATCH method against {{Host}}/api/v1/Erp.Bo.SalesRepSvc/SalesReps(‘Denmar’,‘TEST’) using Postman and a JSON body containing a SalesRepRow object with those datatypes, the API responds with “400 Bad Request”

This is the error returned by PATCH when using a decimal CommissionPercent:

patch : Cannot convert a primitive value to the expected type ‘Edm.Decimal’. See the inner exception for more details.

This is the error returned by PATCH when using an integer SysRevID:

patch : Cannot convert a primitive value to the expected type ‘Edm.Int64’. See the inner exception for more details.

When testing this in Postman, if I ignore the OData metadata and instead submit the problematic fields as strings, the PATCH is successful.

The reason this is creating a problem for me is because I’m generating C# client classes from the OData metadata using NSwag.CodeGeneration.CSharp, but if this is indeed a datatype discrepancy I’ll need to manually change the datatypes for the affected model fields within the generated client classes.

Is this a known issue, or am I misunderstanding the relationship between the REST methods and the OData metadata?

Thanks!


(Mark Wonsil) #2

I like what you’re trying to do. I can’t help you but @ewelsh did something like this too. Maybe some inspiration for you:

Mark W.


(Olga Klimova) #3

OData has special requirements for decimal and long types - they should be sent as strings - in quotes “”


(Matt Yutzy) #4

Thanks for pointing me in the right direction. I’ll need to account for that in my C# service class generation.

Related:

https://msdn.microsoft.com/en-us/library/dn260778.aspx

http://www.odata.org/documentation/odata-version-2-0/json-format/

http://docs.oasis-open.org/odata/odata-json-format/v4.01/odata-json-format-v4.01.html

Thanks for your time!


(Matt Yutzy) #5

I thought I should follow-up with details about how I worked around this. I went down a rabbit hole studying the OData JSON specifications and learned that OData 3 (the version Epicor 10.2.100 is using) seemingly doesn’t have a provision for what is referred to as “IEEE 754 Compatibility” which specifies that Edm.Int64 and Edm.Decimal properties are to be serialized as strings. This support was added in OData 4. Here is the related excerpt from the OData V4.0 JSON specification at http://docs.oasis-open.org/odata/odata-json-format/v4.0/os/odata-json-format-v4.0-os.html:

The IEEE754Compatible=true format parameter indicates that the service MUST serialize Edm.Int64 and Edm.Decimal numbers (including the odata.count, if requested) as strings. If not specified, or specified as IEEE754Compatible=false, all numbers MUST be serialized as JSON numbers.

This enables support for JavaScript numbers that are defined to be 64-bit binary format IEEE 754 values [ECMAScript] (see section 4.3.1.9) resulting in integers losing precision past 15 digits, and decimals losing precision due to the conversion from base 10 to base 2.

OData JSON payloads that format Edm.Int64 and Edm.Decimal values as strings MUST specify this format parameter in the media type returned in the Content-Type header.

At that point I decided to make the class generator replace the generated class text with a patch for these properties (nullable doubles and nullable longs) when necessary.

// Convert doubles and longs to strings (IEEE754Compatible)
.Replace("private double?", "private string")
.Replace("public double?", "public string")
.Replace("private long?", "private string")
.Replace("public long?", "public string")

If you’re interested in seeing the complete generator it’s available here: https://github.com/DenmarAssociates/EpicorRESTClientGenerator

Thanks to the generator creator “EdWelsh” for making it available!


(Ed Welsh) #6

Hey that is awesome! I will add it to the original project!