- Developers: Anonymous User
- Integrations: Accounts (To), Products (To), Inventory (To), Sales Orders (To/From),
- Current Branches:
support/clients/tec/*
- API authentication is achieved through HTTP Basic user/pass auth on each request, so developers need to have an account in E10 with appropriate permissions to manage everything. This account’s credentials are what will be passed in each request.
Typically, a REST URL will look like this:
<Host>/<EpicorInstance>/api/v1/<Table>/<View>
- Most tables have a standard /List view on them which can be thought of as a “lite mapping”, as well as another view named similarly to the table (e.g Erp.BO.PartSvc has a view named Parts) that gives a full mapping of the primary entity for that table.
GET requests against the /api/v1 level will return a list of all possible tables, and a GET against the /api/v1/<Table> will return a list of all views in that table. At time of writing and on the instance this guide was written against, E10 has 1,039 tables available. At least some of these will actually just be re-exporting the same data available in another view in another table, so some care is required to figure out which are needed for a given entity’s operations.
When doing a GET against a table to see its views, as well as when doing requests against any interior views, the API will return an odata.metadata field containing a link to an xml metadata file which has definitions for all entities in that table. The recommended course of action here is to use OData2POCO to generate C# class files based on this definition. An example invocation could look like:
dotnet o2pgen -r ./Erp.BO.Erp.BO.SalesOrderSvc.xml --nullable
This will generate a file named poco.cs which can be hand tweaked to add anything else. The nullable flag is important because otherwise C#’s JSON deserializer will error out if it finds nulls on value types like DateTime.
It is recommended to hand-tweak certain fields like OrderNum to be nullable, so as to not send them over the wire when creating a new order, for example.
- To use a datetime value, use the syntax
DateTime’XXXX-XX-XXTXX:XX:XX’, where the Xs stand in for the actual date.
- Strings must be enclosed in single quotes
- Integers sit naked
- Field names are naked
- Expressions can be enclosed in parentheses and combined with and/or operators just like a standard programming language
- There are some useful functions that can be applied, such as substringof
- For example, to filter to all orders after the beginning of the year:
- $filter=OrderDate gt DateTime’2021-01-01’
- $top: Set the number of results to be returned
- See previous note on E10’s insanity regarding pagination. You’ll usually just want to set this to either 100 (for testing) to 9999999 (for production).
- $select: Filter which fields get returned
- GET /Foo(Key1=foo,Key2=bar…) – get a particular entity based on its key(s).
- Key values follow the same syntax rules as filter values.
- As previously mentioned, nearly all entities will need a Company=’…’ key set appropriately.
- DELETE /Foo(Key1=foo,Key2=bar…) – delete a particular entity.
- POST /Foo – send (in the body as a JSON payload) an entity to be upserted.
- The entity being upserted should either have no values for the SysRev family of fields or should have values matching E10’s most recent copies of them. This is used as a basic concurrency sanity check so that multiple programs do not update the same thing without seeing the others’ changes.
- PUT /Foo(Key...) - update an entity. Actually does the same as POST if you’re POSTing with keys in the body, so this endpoint can usually be ignored.
The existing connector was written with Connect V4 in mind, but can be pulled into and used in any version of Connect, so long as its dependencies (C4 and its utility libraries) can be linked against.
All data classes inherit from E10Base, which defines a helper property named E10Key that pulls out the primary key for that entity. This is the same key returned by PushEnt and consumed by PickOne (see below). All data classes additionally have a [Record] attribute, an [EndpointInfo] attribute, and appropriate [Primary] attributes on their primary keys.
Read endpoints are understood to have the {{key}} macro defined (note that the value of {{key}} will not have parentheses included in it). We do not define the Update endpoint explicitly as Create acts as an upsert (combined create + update if existing). Note that we also define which fields this entity should use for checking creation and modification dates.
All operations are defined under the E10Syncer, which itself is generic over a TEnt that stands in for the particular entity being used.
- PushEnt(Ent) – pushes an entity back to E10. Should be assumed to be an upsert. Returns the portion of the key inside the parenthesis (e.g “Key1=foo,Key2=bar”). This key can be used as-is for subsequent calls to PickOne.
- PickOne(string) – Read an entity based on its key. Returns null if not found.
- FindAll(hints) – Based on the filters defined in hints, list all of that entity Meaning of hints’s fields:
- CreatedSince – gets rolled into filters based on that entity’s [EndpointInfo] settings
- UpdatedSince – gets rolled into filters based on that entity’s [EndpointInfo] settings
- ChangedFromDB – ignored by this connector
- Filters – a series of simple eq/gt/lt/etc filters which are understood to be ANDed together and placed into the $filter param.
E10 entities can also have custom fields (_c fields). In order to use those, inherit your model from E10Base and index into it with a string (like salesOrder[“MyField_c”]).
TEC: https://clarityventures.sharepoint.com/:w:/s/TEC-TectranInc/Eapil_4I1g5Ark2qLaUj3K8BX2uY8BY73bevhfq_5dkrtQ?e=Ara2th
- Products (To)
- Product Visibility Restrictions
- Inventory (To)
- Separate US/CA Inventories
- We primarily integrate with E10 via its REST API, not WCF.
- Epicor E10’s REST API is a mostly but seemingly not completely compliant implementation of ODataV3.
- In particular, fetching a page further along in the results seems to take just as long as requesting all data up to that page. For example, skipping 40,000 parts and taking the top 100 from that point will likely take just as long as simply taking the top 40,100 parts from the beginning. For this reason:
- It’s usually recommended to set $top on the URL to some massive number
- (Dev/IT) Most syncs that deal with massive amounts of data from a single table will have a high startup time (i.e we can’t fetch a page, sync that, and repeat. We need to fetch all, then sync all)
- (Dev) It’s typically best to put a “limiter” on syncs when testing so you only
- See also: ODataV3 spec
- Nearly all, if not all, entities in E10 have a Company field on them that’s part of their primary key. Care must be taken to set this to the appropriate value for the integration (i.e a static value for an integration involving 1 company, or managing which value in an integration involving multiple E10 companies). This value must also be sent when doing a GET for a particular entity.
- See also: later note on CallSettings.
- Most numbers are passed as strings with set decimal precisions that they’ll come back in. If you’re copy/pasting the JSON from the endpoint into C# to use as a sort of “form” to fill out for mapping, I recommend preserving the number of decimal places in any constants.
- E10 has the concept of a “CallSettings” header. Make sure it contains something like
{"Company": "MyEpicorCompanyName"}
- If you don’t set this, calls like creating an object in a particular company or listing data from a particular company may fail.
- When listing from 2+ companies at once, it’s recommended to do a separate listing call for each company with a different call header (our connector will take care of this automatically)
- Do not populate order header fields for totals. Each time you insert a line item, it will add to these (not recalculate them)
- Many entities have sub-entities (like order lines) you can expand. In the API, this is the $expand query parameter, and in the connector you add to the `Expands` list on the PickHints.
- O2pgen doesn’t automatically make fields for the expanded properties.
- You can't do a straight read request for all entities. If part of the entity's key contains a forward slash, there is no (currently known) way to read it by key. Instead you'll need to list with a filter for the key's parts and do a
SingleOrDefault().
Pricing is complicated – ask the client how they do it.
https://clarityventures-my.sharepoint.com/:x:/g/personal/eric_weathers_claritymis_com/EbtkC5a0pXNEr5tkT6WFVYwB6hYa5sbYcAcgTbdzh-uD5g
Phase 2