Pushing orders
You need the PushSalesOrder permission to enable calls to this service. Further, special API user types will only be allowed to call this service. Effective dates and more context can be found here.
EVA allows for orders that were created in external systems to be pushed into EVA using one all-encompassing web service: PushSalesOrder. This is a massive, massive service that can be used for all different kinds of orders. For the sake of clarity, we will cut the service into segments and explain them one by one.
Basic information
We always start off a PushSalesOrder request with some basic information, most of which are identifiers:
{
"BackendSystemID": "salesforce", // mandatory
"BackendID": "salesforce_order_1234", // mandatory
"SoldFromOrganizationUnitBackendID": "almere_store", // mandatory
"PickupOrganizationUnitBackendID": "SomeOUid",
"ShipToOrganizationUnitBackendID": "SomeOUid",
"CurrencyID": "EUR", // mandatory
"FulfillmentMethod": 1,
"Remark": "randomremark"
}
- BackendSystemID and BackendID: know more about what those represent.
- SoldFromOrganizationUnitBackendID represents the Organization Unit (OU) in which the product/service was sold from. In an OMS context this is typically a (country-level) e-commerce organization like your "German Webshop", your customer app where sales can be conducted, or a marketplace platform such as Zalando or Amazon for which an OU in EVA was created. The field requires an input which reflects the BackendID of the relevant OU which you can retrieve from the Organization unit chapter on Admin Suite.
- PickupOrganizationUnitBackendID is only needed in case of click and collect orders and automatically marks the order as click and collect. Your input here should typically be BackendID of the store/OU in which the consumer wants to pick up their order. Again, you can retrieve from the Organization unit chapter on Admin Suite**.
- ShipToOrganizationUnitBackendID is only needed in case of replenishment orders.
- FulfillmentMethod, "Description": How this order is fulfilled, this can be overridden on line level. It is an optional property and its value expects an integer. Name: "Delivery", value:"1", name: "Pickup", value: "2", name: "CarryOut", value: "3". The value will default to Delivery or Pickup when a PickupOrganizationUnit is defined.
After this basic information, we have a ton of objects containing various properties to spruce up your sales order. These objects are:
- Customer
- ShippingAddress
- BillingAddress
- Lines
- Discounts
- Payments
- Shipments
- ShippingInformation
- Properties
- Options
- CustomFields
- BackfillInformation
- Coupons
We will walk you through these objects one by one.
Customer
The Customer object specifies all customer information and is mandatory for all orders besides carryout orders.
{
"Customer": {
"BackendID": "string",
"EmailAddress": "string",
"Gender": "M, F or O",
"Initials": "string",
"FirstName": "string",
"LastName": "string",
"PhoneNumber": "string",
"DateOfBirth": "2000-12-31",
"PlaceOfBirth": "string",
"BankAccount": "string",
"NickName": "string",
"LanguageID": "ISO 639-1",
"CountryID": "ISO 3166-1",
"FiscalID": "string",
"SocialSecurityNumber": "string",
"Title": "string",
"Salutation": "string",
"AccountType": "standard",
"BackendRelationID": "BackendRelationID",
"Company": {
"Name": "CompanyName",
"RegistrationNumber": "CompanyRegistrationNumber",
"VatNumber": "CompanyVatNumber",
"FiscalID": "CompanyFiscalID"
}
}
}
Most of these properties should make absolute sense. However, worth highlighting that whenever the email address is known to EVA, EVA will add the order to the account of this customer. In the scenario an order is being pushed to EVA on an existing email address but with new/different values such as a different FirstName, EVA will in turn in its records update the field with the new value.
In a scenario where the email address is not yet know/does not exist in EVA, a new consumer profile will be created.
Guest Checkout
The AccountType property can be used to determine what type of user is associated with the order. There are three types/value you can use here, described as follows:
- Standard (0): has a unique login identifier (email/nickname), can log in (if they have password access), can be found in the UserSearch.
- Basic (1): no unique identifier, can't log in, can be found in the UserSearch.
- Incognito (2): no unique identifier, can't log in, cannot be found in the UserSearch.
When set as Incognito EVA will create the order, and store the consumer details - but no account is created for this consumer. When later searching for this user in for example POS, no account will appear.
This is used to handle 'guest check-out' flows in e-commerce.
ShippingAddress
{
"ShippingAddress": {
"Address1": "Conch Street",
"Address2": "124",
"FirstName": "Spongebob",
"LastName": "Squarepants",
"ZipCode": "96970",
"City": "Bikini Bottom",
"Reqion": "Pacific Ocean",
"District": "string",
"SubDistrict": "string",
"State": "String",
"CountryID": "MH"
}
}
Addresses are pretty straightforward as well. The only thing that's good to know here, is that Address1 and Address2 are optional substitutes for Street and HouseNumber. Address1 and Address2 allow for a more free form interpretation which might be useful in some countries.
Read more about addresses.
BillingAddress
BillingAddress follows the exact same structure as ShippingAddress but is of course allowed to differ from the ShippingAddress.
PickupAddress
PickupAddress follows the exact same structure as ShippingAddress. It can be used to store the Address of a parcel pick-up point. In some countries items are first sent to this address, and in case they are not picked-up sent to the ShippingAddress or vice-versa. This third address field is available for such scenarios.
Lines
This is probably the most important object in any PushSalesOrder
requests, as it specifies exactly what products are contained in the order. The Lines object is basically an array of actual Line objects:
{
"Lines":[
{
"GroupID":"string",
"BackendID":"LineBackendID",
"CustomID":"ProductCustomID",
"ProductID":"ProductID",
"Amount":"UnitPrice",
"Quantity":"NumberOfProducts",
"TaxRate":1.21,
"TaxAmount":5,
"Description":"string",
"RequestedDeliveryDate":"datetime",
"SerialNumber":"string",
"PaymentLineIdentifier":"string",
"FulfillmentMethod":1,
"Discounts":{
},
"Shipments":{
},
"CustomFields":{
},
"ProductRequirements":{
}
}
]
}
Every line represents a specific product, but can hold various quantities of a product. An additional product always lives on an additional line. Individual lines can be grouped when given the same GroupID.
Most of these properties should be really self-explanatory, but the taxes and amounts require some additional information. For amounts and taxes, we have the following properties:
- Amount
- TaxRate
- TaxAmount
Amount is mandatory, it contains the price at which product/service were sold at in the system from which you sales order was pushed. Depending on your needs, you can have EVA calculate the tax on this or have it also push such information. In the first case EVA will calculate taxes based on the Amount specified/pushed. For this it would look at the listens to your pricelists configuration attached to the respective OU in which the product/service was sold from.
Alternatively you can then add both TaxRate and TaxAmount or just one of them, or neither. EVA will fill in the gaps where necessary. Examples with Amount = 100
and a 21% tax rate:
{
"Amount": 100,
"TaxRate": 1.21,
"TaxAmount": 17.36
}
// or
{
"Amount": 100,
"TaxRate": 1.21
}
// or
{
"Amount": 100,
"TaxAmount": 17.36
}
{
"Amount": 100,
"TaxRate": 1.21,
"TaxAmount": 21
}
// or
{
"Amount": 100,
"TaxRate": 1.21
}
// or
{
"Amount": 100,
"TaxAmount": 21
}
Filling the gaps can sometimes lead to rounding issues however, especially where amounts are excluding tax. When letting EVA fill the gaps, make sure to enable the zero check under the Options object.
Now, the lines object contains some objects in itself:
- Discounts
- Shipments
- CustomFields
- ProductRequirements
Discounts
The discount object is comparable to the order-level Discounts object, except that it only accepts line-level discounts here.
Shipments
The Shipments object on the line level lets you connect lines to shipments from you order level Shipments object to specify what lines are included in which shipments.
CustomFields
The CustomFields line object is the same as the CustomFields order level object, the only difference being that it takes order line CustomFields here.
ProductRequirements
Some products might require additional information to be specified due to ProductRequirements. The object can be filled with values as follows: "ProductRequirementID": "value"
:
{
"ProductRequirements": {
"1": true
}
}
Final sales
By default, orders pushed in via PushSalesOrder
service can later on be returned (for example via POS). In case the items sold in the order are not eligible for a return a flag is required to indicate it as a non-returnable item. To indicate a line as being a 'final sale' and therefore excluding them from being returned, you can set the IsFinalSale boolean to true.
Find out more about order returns.
CustomFields
Custom fields can be set on either the Order or order line level via the CustomFields object. This can be used to store additional data from your 3rd party system. For example: information that EVA would need to pass on to a WMS system for further handling.
Before you push in values on these fields you need to first create the CustomField on Admin Suite.
CustomFields can be specified as follows:
{
"CustomFields": {
"MyCustomField": true,
"LookAnotherCustomField": "this is so nice"
}
}
Any custom field that is not yet created/known to EVA but is included in the pushed order, will be created in EVA records automatically.
Discounts
When pushing in items with a Discount, you can set these on both the Order or order line. To identify the discount you can use a BackendID, which contains the system identifier of the discount in your 3rd party system or EVA Admin Suite. This is contained in the Discounts object under the Lines.
Discounts
can be specified as follows:
{
"Discounts": {
"ID": "54",
}
}
The description field will be shown in all front-ends (such as POS), allowing customers and store staff to see which discounts were applied.
When pushed in like this, the discount will be attached to the order line, meaning that in case the order line is later returned, the discount is also ‘returned’.
In case you have applied an Order-level discount (ex: 10% on all items) we expect you to distribute the discount over the order lines, and push the discount in as such. Say you have a €6 and €4 euro product in a basket - and 10% discount is applied to the entire basket we expect you to push in a €0.60 and €0.40 discount amount on each order line.
Payments
Paid and unpaid
Orders from an external system can be pushed with an "unpaid", "partially paid" or "fully paid" state. In an OMS scenario in which EVA handles e-commerce orders, they typically arrive in a "fully paid" state.
Payment information
Payments information lives in the OrderPayment object. This object includes a (3rd party) payment identifier in the BackendID, the payment method, and the paid amount. Note that you cannot create a payment with methods not configured in EVA. Here is where you can create Payment methods
Optionally you can let EVA know which amount was captured already.
Payments
can be specified as follows:
{
"Payments": {
"BackendID": "12361",
"Method": "PIN",
"Amount": "50.98",
"AutoConfirm": false,
"Data": null,
"ForeignCurrencyID": "GBP",
"ForeignAmount": 60,
"CapturedAmount": "50.98",
"Data": "storeddatatoverifypaymentforexample"
}
}
The ForeignCurrencyID
and ForeignAmount
parameters come into action in instances where the payment of the pushed sales order is made using a foreign currency.
The AutoConfirm
parameter default value is true. If set to false it will respect the payment configurations status (pending, refundPending) in the corresponding PaymentType set in EVA.
Adyen payments
Payments handled via Adyen have a particular format when pushed in to EVA. This allows you to later refund these transactions via example: our POS App.
- The BackendID contains the PSP reference from Adyen.
- The Method has to be pushed in (and configured) as ADYEN_BRANDNAME.
- The brandname adheres to this mapping from Adyen.
- The Data field needs to contain "{\"MerchantAccount\":\"YourUniqueName\"}" where only the “YourUniqueName” value needs to be replaced by the name of the Adyen MerchantAccount where the payment was done.
Here is an example:
{
"Payments": {
"BackendID": "883633685125942G",
"Method": "ADYEN_VISA",
"Amount": 59.89,
"CapturedAmount": null,
"Data": "{\"MerchantAccount\":\"YourUniqueName\"}"
}
}
Shipments
Shipped or unshipped
When pushing in orders, you can push them in at various stages of their lifecycle. In a typical OMS context in which EVA routes a (paid) e-commerce order to the correct fulfillment location, orders are then pushed in without the full shipment details yet. The push will include just some information on the shipping method and costs.
In case your order is later shipped by a 3rd party warehouse system, you can use the ShipExternalOrder service.
Shipping methods and costs
Even if you do not create shipments via PushSalesOrder
, you will likely want to fill out the shipping method and costs associated to this order.
This is part of the ShippingInformation object, and it includes the following:
- Method: The ShippingMethod - as configured in EVA Shipping methods
- Amount: The shipping costs on the order. Empty in case of free shipping.
- TaxRate: The tax rate applicable to the shipping cost
- TaxAmount: The subsequent tax amount
- PaymentLineIdentifier : Some payment integrations (ex: Klarna) require EVA to know about a unique identifier on each order line (including shipping costs) in case of later refund/return requests.
Here is an example:
{
"ShippingInformation": {
"Method": "PostNL",
"Amount": "5",
"TaxRate": "1.21",
"TaxAmount": "1.05",
"PaymentLineIdentifier": "874589654"
}
}
Creating shipments on the order
In case you want to push in shipped orders you can add your shipment details to the OrderShipments object of the PushSalesOrder
service.
This only creates a ‘container’ (one or multiple shipments associated with the order). It does not specify which items are in each shipment, this is pushed in on the order line level.
Use the BackendID in this object to give each shipment a unique identifier. Further, the "tracking information" can be added for use in EVA front-ends and emails.
Here is an example:
{
"Shipments": {
"BackendID": "12361",
"ShipmentDate": "8/23/2022",
"TrackingCode": "874589654",
"TrackingLink": "insertlinkhere"
}
}
Properties
The Properties object contains some general order properties to identify the type of order (true/false):
- IsInterbranch
- Replenishment
- ReturnToSupplier
Since these ‘B2B’ order types are also just SalesOrders in EVA, you can use the same PushSalesOrder
web service to push them in. If none are specified, the SalesOrders are by default considered ‘consumer’ orders.
Determining your order type
You can determine what kind of order type(s) an order has via its properties.
You can check this in for example a GetOrder
call by filtering on the Properties property. The value it contains is an enum value. We can check exactly what enum values correspond with what order type by calling GetEnumValues
:
If an order IsPickUp (1
) and CreatedByEmployee (2
), the order's Properties value will be set to 3
.
If an order IsReturned (65536
), is ImportedFromLiveGuard (2048
) and is Duplicated (16
), the order's Properties will be set to 67600
.
To make this easier, each table containing enum values on docs has an additional input field where you can enter a (combined) enum value and we'll show you just what values the enum contains.
Options
The Options object contains some general order options (true/false):
- DoNotInvoice - whether to generate an invoice for this order or not
- AllowPartialFulfillment - whether to allow this order to be partially fulfilled or not
- CheckIfOpenAmountIsZero - when set to true, orders where the open amount is not 0 will return an error
Example:
{
"Options": {
"DoNotInvoice": false,
"AllowPartialFulfillment": true,
"CheckIfOpenAmountIsZero": true
}
}
Additional services
Set BackendID
When you push in orders via PushSalesOrder
you set a BackendID and BackendSystemID covered in the basic information section. There are cases in which this value is not set (for example orders fully initiated in EVA). To resolve this, you can retro-actively set a BackendID on these orders using the SetOrderBackendIdentifier service.
Back filling historical orders
PushSalesOrder
is not only used to push in new orders, it’s also the go-to service to backfill orders when first migrating a web shop or store(s) to EVA. This way you make sure recent orders from your ‘old’ set-up can be found and returned via EVA too.
BackfillInformation
The BackfillInformation object contains some properties that can be used when using PushSalesOrder
to make older orders known in EVA just for historic relevance:
- OriginalInvoiceDate
- OriginalCreationDate
- OriginalInvoiceNumber
When doing this, you can set DoNotInvoice to true under Options in your request. This prevents EVA from creating new invoices for these old orders and thus avoid duplicating those orders when generating an audit file for compliance purposes.
The value in the OriginalInvoiceDate is leading for EVA’s calculation of orders when determining the number of days a customer can return the purchased product. The setting designated for this purpose is called Returnable:InvoicedDays
- returns always take into account this setting from the original SoldBy OU.
Coupons
The coupon property is mainly used to notify EVA that a coupon is being claimed. The property is a list of objects which looks like this:
{
"DiscountBackendID": "10PERCENT",
"CouponCode": "1234567890",
}
Some things to keep in mind when it comes to the Coupon property:
- The claimed discount coupon requires that the discount is actually active on the given
SoldFromOrganizationUnit
. If not the coupon would not be claimed. - If the DiscountBackendID does not resolve to a valid discount, the discount would not be claimed.
- It is not required that the DiscountBackendID on order or order line level also match with the DiscountBackendID mentioned here under the Coupon property.
- A CouponCode is created based on scripting. This triggers an email, and thus an event export namely,
EventExportTarget
19
"Coupon", which can then be retrieved by external systems via GetCouponByToken.
PushSalesOrder Examples
Example 1
This is a simple e-commerce order paid with VISA via Adyen and has no shipment details:
{
"BackendSystemID": "WEBSHOP",
"BackendID": "externalorderid5555",
"CurrencyID": "USD",
"SoldFromOrganizationUnitBackendID": "WAREHOUSE_1",
"Customer": {
"EmailAddress": "ianbusher@email.com",
"Gender": "M",
"FirstName": "Ian",
"LastName": "Busher",
"PhoneNumber": "9177680202",
"CountryID": "US"
},
"BillingAddress": {
"Address1": "110 Greene St",
"Address2": "Apt 5",
"ZipCode": "10012",
"City": "New York",
"State": "NY",
"CountryID": "US"
},
"ShippingAddress": {
"Address1": "110 Greene St",
"Address2": "Apt 5",
"ZipCode": "10012",
"City": "New York",
"State": "NY",
"CountryID": "US"
},
"Lines": [
{
"BackendID": "linebackendid8",
"ProductID": 269095,
"Quantity": 1,
"Amount": 49.95,
"TaxRate": 0.8875
}
],
"Payments": [
{
"BackendID": "88362512342345942G",
"Method": "ADYEN_VISA",
"Amount": 49.95,
"CapturedAmount": null,
"Data": "{\"MerchantAccount\":\"ECOM_USA\"}"
}
]
}
Example 2
This is a simple Click & Collect e-commerce order paid with VISA via Adyen with the PickupOrganizationUnitBackendID triggering a reservation of the specified item in the store with having a BackendID STORE_NYC_1.
{
"BackendSystemID": "WEBSHOP",
"BackendID": "externalorderid5555",
"CurrencyID": "USD",
"SoldFromOrganizationUnitBackendID": "WAREHOUSE_1",
"PickupOrganizationUnitBackendID":"STORE_NYC_1",
"Customer": {
"EmailAddress": "ianbusher@email.com",
"Gender": "M",
"FirstName": "Ian",
"LastName": "Busher",
"PhoneNumber": "9177680202",
"CountryID": "US"
},
"BillingAddress": {
"Address1": "110 Greene St",
"Address2": "Apt 5",
"ZipCode": "10012",
"City": "New York",
"State": "NY",
"CountryID": "US"
},
"ShippingAddress": {
"Address1": "110 Greene St",
"Address2": "Apt 5",
"ZipCode": "10012",
"City": "New York",
"State": "NY",
"CountryID": "US"
},
"Lines": [
{
"BackendID": "linebackendid8",
"ProductID": 269095,
"Quantity": 1,
"Amount": 49.95,
"TaxRate": 0.8875
}
],
"Payments": [
{
"BackendID": "88362512342345942G",
"Method": "ADYEN_VISA",
"Amount": 49.95,
"CapturedAmount": null,
"Data": "{\"MerchantAccount\":\"ECOM_USA\"}"
}
]
}