Sync Service
Introduction
The Sync Service is a custom API whose primary function is to provide a REST endpoint for integrating with Galaxy for the purpose of synchronizing data with online shops. As such, the Sync service is also known as the WebShop API.
Sync provides three types of endpoints:
- Misc Lookups
- Entity lists, with synchronization
- “Online” calls, to send data to the back-end in real time
All the endpoints provided by the Sync API are geared towards enabling the client create a Commercial Entry payload and communicate purchases back to Galaxy. It also enables the client to create and post Customer data to the back-end.
Anatomy of a Sync solution
Sync is designed primarily for offline use. Clients are meant to sychronize their database using bulk updates at predefined intervals. Sync provides a synchronization mechanism based on Revision Numbers for several entities, including customers, items ( products ), item features and stock balances and others.
An API client is meant, in general terms, to implement a simple synchronization workflow:
- Sync misc Lookups
- Sync Entity tables, most commonly in a pre-defined order
- Construct the appropriate purchase or customer payload[s]
- Post the purchase and/or customer data to the server
This sequence is meant to be repeated at regular intevals during the day, to keep the two systems synchronized.
Alternatively, the client developer has the option of posting purchase or customer data to the server in real-time, depending on their needs for the solution at hand.
There is also a recent extension to the Galaxy API, that allows the back-end to perform Http Requests to pre-defined URLs when certain events occur in the system ( Galaxy WebHooks ). This is another available option for the client developer, should they need to keep the two systems in sync in near-real-time.
Galaxy WebHooks could be used to trigger synchronization from the client’s side. Note however, that they cannot, in no shape of form, be considered replacements of the synchronization mechanism, but rather useful extensions.
Galaxy Web Scenarios
The Galaxy platform supports e-shop synchronization scenarios, under Web Scenarios. A configured and working Web Scenario is a base requirement for the Sync API.
Unless there is a functioning Web Scenario defined as the default Web Scenario, all calls to the Sync service must include the WebScenID URL parameter.
WebScenID: alphanumeric identifier of a configured WebScenario
The Sync API provides an endpoint from which a list of configured Web Scenarios can be retrieved:
/services/sync/webscenarios
Response:
[
{
"IsDefault": 1,
"Type": 2,
"CompanyID": "743d7942-4ccb-474b-b140-06011f6795cc",
"ID": "d6c30ca7-e52f-4a47-a567-8a5545030936",
"Code": "Default B2C",
"Description": "Default B2C Web Scenario"
},
...
]
Web Scenarios define several aspects regarding how the integration will work. For example, the types of purchase documents produced, transport types, product categories exposed to the API and several other features can only be administered through a Web Scenario.
In addition, in a Galaxy installation we can have more than one companies, and more than one functioning Web Scenarios. Unless the Web Scenario we need to use is the default, we always need to pass the Web Scenario ID to the WebScenID URL parameter.
Synchronizing Lookups
The structure of a sales document ( we will be referring to it as Commercial Entry in this document ) contains placeholders where various values originating from lookup lists are to be inserted. Those can range from city and region codes to dispatch purposes and payment types.
Lookups are typically static lists in the Galaxy platform. As such, they only be retrieved and cached once.
The endpoints returning Lookup lists support GET Http Requests, and a return an array it items, typically sharing the same payload schema.
The generic structure of a Lookup URL is:
/services/sync/{LookupName}
Typically, the response payload is:
[
{
"ID": "String",
"Code": "String",
"Description": "String"
}
]
For example, to retrieve the Countries lookup, we would execute a GET request to /services/sync/countries
:
Response:
[
{
"ID": "e51120fe-d246-460d-b8a6-06e4e47ff281",
"Code": "JP",
"Description": "Ιαπωνία"
},
{
"ID": "d55d61a4-99ad-424a-9484-0c6d81920c57",
"Code": "HU",
"Description": "Ουγγαρία"
},
{
"ID": "25c27d9c-3a91-445d-867f-0ed3c2ca335c",
"Code": "LT",
"Description": "Λιθουανία"
},
{
"ID": "06cddf3b-cd0f-435a-9a14-1083014b3a12",
"Code": "IQ",
"Description": "Ιράκ"
},
{
"ID": "dd6f7e9a-f70d-4dc5-a9d1-1183bcb3ec70",
"Code": "CU",
"Description": "Κούβα"
},
...
]
Note: All Lookups operate in the same way, and return the same payload schema.
The Sync API exposes the following lookup lists:
- Countries
- Prefectures
- Cities
- Currencies
- PaymentMethods
- Charges
- DispatchPurposes
- ShippingMethods
- Agencies
Synchronizing Entity Lists
Entity Lists originate from data in a database, and their contents might change at any time. Synchronizing Entity lists is a little more involved, since fetching the complete dataset every time we sync would lead to possible performance degradation.
The Galaxy platform introduces the concept of a Revision Number, to provide the ability for clients to retrieve only modified data when synchronizing an entity list.
In general:
- Every row in the database, includes a Revision Number integer field
- Every created or modified row gets it’s Revision Number incremented to an auto-number value.
- A client retrieves changed or created rows for a list by providing the max Revision Number found in it’s local data, or zero if it has none
- The API will examine the Revision Number parameter, and only return rows that have a greater revision number
Let’s see a more concrete example. Consider a client that needs to maintain a local copy of all customers in our database, and synchronize changes when they occur:
- The client has no data in their Customers table
- The client issues a GET request at
/services/sync/customers?RevisionNumber=0
- The client stores the data in it’s local database
- The largest revision number retrieved is 3
- At this point, an operator modifies a customer in the Galaxy back-end
- The customer’s revision number is updated to a value that is the greatest in the customers table, let’s assume it’s 4.
- The client attempts to sync at some pre-defined interval
- Now, the
max(Revision Number)
in the clients local DB is 3, so it is included in the RevisionNumber parameter - The API will now only select rows whose Revision Number is greater than 3
- The API returns a single row, the customer that changed between sync invocations
- The client stores the data in it’s local database
- The largest revision number retrieved is now 4
Following this procedure, we can ensure that clients will only ever retrieve changes to their locally cached dataset, rather than the full data which could be thousands of rows to process, drastically minimizing the hardware resources and time required to synchronize the data between the two systems.
Entities & Lookups, relations
There are four distinct areas of data the Sync API is primarily involved with:
- Customers
- Companies
- Items
- Commercial Entries
Each area consists of a number of Lookups and Entities, working togetehr to describe a certain area of functionality. In this section, we will attempt to analyse those areas, and inspect the relationships between the participating entities and lookups in each one.
Customers
Customers are relatively simple entities, consisting of:
- Basic Customer data ( name, company they belong to, comments etc. )
- Customer Sites, describing contact details like addresses, telephones, emails etc.
The two lists can be found at:
/services/sync/customers
/services/sync/customersites
Todo: Ref links
Companies
Companies are similar in form with Customers. They also are made up of just two lists:
- Basic Company data ( name, code, description and Tax no )
- Company Sites ( simple entity containing basically only a name and a code )
The two lists can be found at:
/services/sync/companies
/services/sync/companysites
Todo: Ref links
Items
Items are perhaps the most complex Entity in the API. It consists of several lists, all working together to describe a single product for sale.
Those include:
- Items
- Item Categories
- Item Attributes
- Item Dimensions
- Item Alter Codes
- Item Balances
- Item Images
Items
The Items payload is an array of items:
[
{
"ItemID": "e6771313-f189-486b-9691-50ed5657e262",
"AlterCode": null,
"ExtDescription": null,
"Weight": 0,
"Unit1ID": "682fabc4-cf84-4afc-90a4-9de1bc69c81d",
"Unit2ID": null,
"Unit2Numerator": 1,
"Unit2Denominator": 1,
"Unit1NoOfDecimals": 0,
"Unit2NoOfDecimals": 0,
"UserDefinedString1": null,
"UserDefinedString2": null,
"IsActive": 1,
"IsActiveWeb": 0,
"ItemPrice": 0,
"WebRetailPrice": null,
"WebRetailOfferPrice": null,
"WebNetPrice": null,
"WebMarketPrice": null,
"WebDiscount": null,
"IsSalesBlocked": 0,
"IsService": 0,
"IsCRMResult": 0,
"IsUsedInCRM": 0,
"Classification": 2,
"ManufacturerID": null,
"SupplierID": null,
"VATsMasterID": "53614470-5279-476c-aa67-4f6ec970749b",
"Dimension1ID": null,
"Dimension2ID": null,
"Dimension3ID": null,
"ItemCategories": [],
"ItemAttributes": [],
"ItemDimensions": [],
"ItemLanguages": [],
"StringField1": null,
"StringField2": null,
"StringField3": null,
"StringField4": null,
"StringField5": null,
"StringField6": null,
"StringField7": null,
"FloatField1": 0,
"FloatField2": 0,
"FloatField3": 0,
"FloatField4": 0,
"FloatField5": 0,
"FloatField6": 0,
"FloatField7": 0,
"LookupField1": null,
"LookupField2": null,
"LookupField3": null,
"LookupField4": null,
"LookupField5": null,
"LookupField6": null,
"LookupField7": null,
"DateField1": "0001-01-01T00:00:00.0000000",
"DateField2": "0001-01-01T00:00:00.0000000",
"DateField3": "0001-01-01T00:00:00.0000000",
"DateField4": "0001-01-01T00:00:00.0000000",
"BooleanField1": 0,
"BooleanField2": 0,
"BooleanField3": 0,
"BooleanField4": 0,
"CompanyID": "743d7942-4ccb-474b-b140-06011f6795cc",
"ID": "0036eac4-480e-4216-a188-d011ee531c47",
"Code": "21-1288",
"Description": "ΡΑΚΕΤΑ 12mm ΔΕΛΦΙΝΙ test revnum 3",
"RevisionNumber": 11
},
...
]
We can filter the results we need from the list by using the ids URL parameter: /services/sync/items?ids=[<ID>,...]
.
Item Categories
In Galaxy item categories are a tree of infinite nesting, although in real-life deployments we seldom see anything beyond two or three levels of nesting.
The Item payload itself maintains an array of category assosciations in the form:
"ItemCategories": [
{
"CategoryLeafID": "78cfaaf3-60ed-4db2-847c-d6394c3692c0",
"CategoryRootID": "78cfaaf3-60ed-4db2-847c-d6394c3692c0",
"IsPerCompany": 1
},
{
"CategoryLeafID": "eb7c4691-097d-450e-9f72-e3d3c288bfec",
"CategoryRootID": "eb7c4691-097d-450e-9f72-e3d3c288bfec",
"IsPerCompany": 1
}
]
As we can see, on the Item itself, we store the Root and Leaf categories for each item, and whether the ascociation is specific per company and not for all companies that stock the item.
The Categories payload is an array of category items:
[
{
"ParentNodeID": "4fd95611-d6af-43e6-b397-73f6be1516fa",
"LevelOfNode": 3,
"CatLanguages": [],
"ID": "01cad8e6-3e96-4d1b-997b-0285049ec654",
"Code": "600-01-02",
"Description": "Γάλα",
"RevisionNumber": 0
},
{
"ParentNodeID": "78cfaaf3-60ed-4db2-847c-d6394c3692c0",
"LevelOfNode": 2,
"CatLanguages": [],
"ID": "03be03e9-966e-4196-9672-0bad3f09f3a3",
"Code": "300-99",
"Description": "Λοιπά Brand",
"RevisionNumber": 0
},
...
]
Inside the Item payload, we maintain the values for the ID property of the root and final leaf categories we assosciate the Item with.
Item Attributes
Item Attributes are named groups of values, identifying specific characteristics of items. They can range from manufacturer name, to types of daw for pastries, screen sizes in inches etc.
The Item payload also maintains an array of attribute items, just like it does for categories:
"ItemAttributes": [
{
"ID": "d2873c5c-cad8-49fa-8f35-0212b590f8f3",
"AttrID": "18236384-2671-45ba-acf8-2f7bcf89152e",
"FldBoolean": null,
"FldDateTime": null,
"FldDecimal": null,
"FldString": null,
"FldAttrValueID": "df7460fb-31f7-449f-b912-3808639cc410"
},
{
"ID": "844749ee-4ca3-4727-ad10-0e5ea70e46ad",
"AttrID": "e4a57e81-287d-4ddf-b1e8-10be9e63d63e",
"FldBoolean": null,
"FldDateTime": null,
"FldDecimal": null,
"FldString": null,
"FldAttrValueID": "52605325-d296-404d-b02b-a8fa333e21b4"
}
]
The keys that interest us and identify an attribute group and a specific value are:
AttrID - points to the ID property of an ItemAttribute FldAttrValueID - points to the exact value off the attribute values list
Consider our first attribute item in the array. Lets retrieve /services/sync/itemattributes
:
[
{
"Kind": 7,
"AttrValues": [
{
"ID": "df7460fb-31f7-449f-b912-3808639cc410",
"Code": "Women",
"Description": "Women",
"Color": null,
"AttrValuesLanguages": []
},
{
"ID": "d45a0923-1050-42cf-b69f-9390a1b2823c",
"Code": "Men",
"Description": "Men",
"Color": null,
"AttrValuesLanguages": []
},
{
"ID": "ec9a1412-061b-4b56-8c5f-59fdfcd1af4c",
"Code": "Kids",
"Description": "Kids",
"Color": null,
"AttrValuesLanguages": []
},
{
"ID": "bb0693a4-ac7b-4bde-a2ba-f6d991090698",
"Code": "Unisex",
"Description": "Unisex",
"Color": null,
"AttrValuesLanguages": []
}
],
"AttrLanguages": [],
"ID": "18236384-2671-45ba-acf8-2f7bcf89152e",
"Code": "ΚΛΦ",
"Description": "Κατηγορία Καλλυντικών",
"RevisionNumber": 0
}
]
As we can see, we can find the Attribute whose ID is 18236384-2671-45ba-acf8-2f7bcf89152e
and a nested Value with ID df7460fb-31f7-449f-b912-3808639cc410
. Our Item’s attribute value is therefore:
{
"ID": "df7460fb-31f7-449f-b912-3808639cc410",
"Code": "Women",
"Description": "Women",
"Color": null,
"AttrValuesLanguages": []
}
As you can see, Item Attributes are mainly used for grouping or categorizing Items.
Item Dimensions
Item Dimensions are very similar to Attributes, but are typically used in Galaxy for qualitative categorization, for item characteristics like Size or Color. For example, the manufacturer is not a dimension for a mobile phone, but the screen size is.
The payload for the /services/sync/itemdimensions
list is very similar to attributes:
[
{
"DimKind": null,
"DimValues": [
{
"ID": "e7658aba-2046-4aa7-919e-53133d7e9012",
"Code": "SM",
"Description": "Small",
"Color": null,
"AttrValuesLanguages": []
},
{
"ID": "6e1309ec-d85c-4c4a-b9ea-c097d277b59d",
"Code": "MED",
"Description": "Medium",
"Color": null,
"AttrValuesLanguages": []
},
{
"ID": "013afeb8-b170-499c-b792-2e66fd68e08b",
"Code": "LA",
"Description": "Large",
"Color": null,
"AttrValuesLanguages": []
},
{
"ID": "cea89fe3-0c7f-4428-9f7f-f201063e092f",
"Code": "XL",
"Description": "X Large",
"Color": null,
"AttrValuesLanguages": []
}
],
"DimLanguages": [],
"ID": "4d272b09-9d2a-44e6-849f-f6727028bb43",
"Code": "ΕΝΔ20",
"Description": "Γενικό Μεγεθολόγιο SML",
"RevisionNumber": 0
}
]
As we can see, the payload looks just like Attributes. However, use within the Item payload is different.
Each Item can have up to tree Dimensions attached to it. The actual Item payload will inform us that there are three dimensions for the Item, but not specific Dimension values.
Note: We will find this piece of information, linking to specific dimension values in another table later on in this section.
Each Item payload, contains three DimensionID values that indicate the dimensions of our item:
...
"Dimension1ID": "c170a53e-9fcd-4c7e-a0f1-a22c461bdfe9",
"Dimension2ID": "6d93d492-227f-488b-a5d7-c92b33a5ef9b",
"Dimension3ID": null,
...
Item Alter Codes
Galaxy allows giving an alternative code to items, that also binds them to specific dimension and attribute values. The Item Alter Codes table is where these associations are persisted.
[
...
{
"ItemID": "16095384-1a6b-4a3a-8e5a-82bae27332de",
"UnitID": "ea284b69-77d1-45b2-8bff-5559f7181715",
"Quantity": 1,
"ScaleType": 0,
"DimVal1ID": "370e1751-a7a5-4db1-a622-2af9ce0a26cc",
"DimVal2ID": null,
"DimVal3ID": null,
"ID": "8f5c0e12-dd57-4bdc-84f3-10fa8429c1a5",
"Code": "0044671001",
"Description": null,
"RevisionNumber": 0
},
...
]
Remember that, in Items, we get an ItemID property, and up to three Dimension IDs. The Alter Code data provide the missing link, the actual dimension value.
Let’s see the the corresponding Item data:
{
"ItemID": "16095384-1a6b-4a3a-8e5a-82bae27332de",
"ExtDescription": null,
"Weight": 0.1,
"Unit1ID": "ea284b69-77d1-45b2-8bff-5559f7181715",
...
"Classification": 2,
"ManufacturerID": null,
"SupplierID": null,
"Dimension1ID": "6963da4b-681d-4dea-96a2-4bbaa55c73a1",
"Dimension2ID": null,
"Dimension3ID": null,
...
}
And now let’s see the corresponding Item Dimensions data:
{
"DimKind": null,
"DimValues": [
{
"ID": "370e1751-a7a5-4db1-a622-2af9ce0a26cc",
"Code": "001",
"Description": "Siren",
"Color": -65536,
"AttrValuesLanguages": []
},
...
{
"ID": "7827e673-9031-42c1-a17a-37ae578139b0",
"Code": "007",
"Description": "Blue",
"Color": null,
"AttrValuesLanguages": []
},
{
"ID": "7bb95bd2-5199-4aa3-86fc-34e41e907f30",
"Code": "008",
"Description": "Orange",
"Color": -32768,
"AttrValuesLanguages": []
}
],
"DimLanguages": [],
"ID": "6963da4b-681d-4dea-96a2-4bbaa55c73a1",
"Code": "100",
"Description": "lip_color",
"RevisionNumber": 0
}
If we follow the Dimension1ID from Item, and DimVal1ID from Item Alter Codes, we can see that our Item, when referred to with Code “0044671001” has a lip_color Dimension value of “Siren”.
Item Balances
Item Balances informs us of available stock for our items. The list data can also include Attribute and Dimension values, so that we get very detailed stock information, taking all Item options and features into account.
{
"AttCombinationID": null,
"DimVal1ID": null,
"DimVal2ID": null,
"DimVal3ID": null,
"Balance": 1,
"ID": "34b3a164-480a-40e2-a50e-09eec9ea5663",
"Code": "003472",
"Description": null,
"RevisionNumber": 0
}
Note: The ID property is the actual Item ID. The Balance property tells us the actual number of items in our warehouse.
As in Items list we can filter the results by using the ids URL parameter: /services/sync/itembalances?ids=[<ID>,...]
.
An extra URL parameter we can use is the FromDateTime to filter results by GXItemTrans.GXSysDBDate: /services/sync/itembalances?FromDateTime=<Date-Time>
. The Date-Time value should be given in ISO-8601 format and must not be less than 10 days from current date.
Item Images
Items can have a multitude of images accompanying them. These are stored in a database and assosciated with attribute and dimension values:
{
"ItemID": "0f34e0e3-8fe6-456b-9514-78ccb3481638",
"DimVal1ID": null,
"DimVal2ID": null,
"DimVal3ID": null,
"TypeCode": "001",
"TypeDescription": "WebShop",
"ID": "c64b10b3-4008-4b5e-8fb0-003b5cabe0d8",
"Code": null,
"Description": "104510_0.jpg"
}
We can filter the resuls by itemID using the ids URL parameter: /services/sync/itemimages?ids=[<ID>,...]
.
Once we have an Item Image ID, we can retrieve the actual image from the API using the ItemImage entity GET request:
/api/glx/entities/itemimage/{ImageID}
The generic URI: /entities/itemimages/{ImageID}/imageBytes
is depreciated.
Commercial Entries
We could say that the ultimate purpose of the API is to allow the developer to post invoices to our Galaxy back-end. This section of the document will attempt to summarize the invoice payload, and provide reference for constructing one after we’ve synchronized all lookups and misc data. Generally, an invoice contains the following sections:
- Generic Header
- Company and Company Site
- Customer, Billing and Delivery addresses
- Currency, Transport mode, Transporter, Dispatch Purpose
- Payment Terms and extra Charges
- Line items and their Dimensions ( options like Size & Color )
Moving on, we will detail the process of creating a payload, section by section, to reach a complete payload by the end of this document.
Header
"header": {
"version": "2.3.2",
"processtype": "B2C",
"source": "Webshop"
}
The header is typically constant, use it as-is.
Company and Company Site
{
"company": {
"identifier": {
"id": "743d7942-4ccb-474b-b140-06011f6795cc",
"codelist": "RCP",
"idspecifier": null
}
},
"companysite": {
"identifier": {
"id": "7380ce05-8688-4c86-9e23-b4db372cf185",
"codelist": "RCP",
"idspecifier": null
}
}
Company and Company Site originate directly from the two corresponding webservices:
/services/sync/companies
/services/sync/companysites
Typically, those two values will also remain constant in all invoices. It is the company the shop is running for, and the warehouse that holds the items to be sold. Galaxy supports multiple companies and sites per installation, and that’s the reason we need to specify those in the payload.
The two payloads are quite simple, we use the ID value in both cases, and there is a CompanyID field on each Company Site that we can use to associate the two lists.
Customer, Billing and Delivery addresses
The payload includes three sections that deal with the customer making a purchase, a delivery and a billing address.
We can omit the two address sections, in which case the back-end will use the customer’s primary address for both billing and delivery. However, we are free to specify different addresses for both elements.
An address is made up of six parts. Country, prefecture and city are lookup values that originate from /services/sync/countries, /services/sync/prefectures & /services/sync/cities accordingly. Then we also have three string fields where we fill-in zipcode, streetname and streetnum.
So, if we were to use a new address for the billtoaddress section, the JSON payload would look like this:
"billtoaddress": {
"identifier": {
"id": "246e6c01-135b-45ee-82ed-4c6d86ed211f",
"codelist": "RCP",
"idspecifier": null
},
"country": {
"identifier": {
"id": "6a9a5bbe-3198-4ff7-8752-20e6dab2cda0",
"codelist": "RCP",
"idspecifier": null
},
"descr": "Ελλάδα"
},
"prefecture": {
"identifier": {
"id": "89fc0de8-bd2f-4792-bab9-01d79cbe6f00",
"codelist": "RCP",
"idspecifier": null
},
"descr": "Αττική"
},
"city": {
"identifier": {
"id": "4ba007cb-fc73-4bf9-913c-0216dca9a2b7",
"codelist": "RCP",
"idspecifier": null
},
"descr": "Κηφισιά"
},
"zipcode": "97643",
"streetname": "Πλουτάρχου",
"streetnum": "1313Α"
}
The delivery info section is very similar, only adding a delivdate field, indicating the date of delivery to the customer, telephone, fax, email and additionalinfo (comments) fields:
"deliveryinfo": {
"delivdate": "2016-03-09T10:44:40",
"address": {
"identifier": {
"id": "246e6c01-135b-45ee-82ed-4c6d86ed211f",
"codelist": "RCP",
"idspecifier": null
},
"country": {
"identifier": {
"id": "6a9a5bbe-3198-4ff7-8752-20e6dab2cda0",
"codelist": "RCP",
"idspecifier": null
},
"descr": "Ελλάδα"
},
"municipality": null,
"prefecture": {
"identifier": {
"id": "89fc0de8-bd2f-4792-bab9-01d79cbe6f00",
"codelist": "RCP",
"idspecifier": null
},
"descr": "Αττική"
},
"city": {
"identifier": {
"id": "4ba007cb-fc73-4bf9-913c-0216dca9a2b7",
"codelist": "RCP",
"idspecifier": null
},
"descr": "Κηφισιά"
},
"zipcode": "97643",
"streetname": "Πλουτάρχου",
"streetnum": "1313Α"
},
"telephone": "2108196764",
"fax": null,
"email": null,
"additionalinfo": null
}
The customer making the purchase is included in the payload in the trader field. Please note that the customer must be an existing customer, and her ID is included in the payload:
"trader": {
"identifier": {
"id": "8e367db7-16a3-462e-a6c1-9d8686f637f0",
"codelist": "RCP",
"idspecifier": null
}
}
The id originates from the /services/sync/
customers data, using the ID field.
Currency, Transport mode, Transporter, Dispatch Purpose
The currency of the transaction is specified in the doccurrency field:
"doccurrency": {
"identifier": {
"id": "9efe2b51-f986-4071-9ce4-65d42075740b",
"codelist": "RCP",
"idspecifier": null
},
"descr": "Ευρώ"
}
The id originates from /services/sync/currencies
using the ID field.
The transport mode indicates the way the goods are to be delivered to the customer ( via air, train, ship etc. ). It originates from the /services/sync/agencies
web service, again using the ID field.
"transportmode": {
"identifier": {
"id": "c6115df2-e203-4d84-91ec-eb6743c8a787",
"codelist": "RCP",
"idspecifier": null
},
"descr": "Οδική μεταφορά"
}
The transporter field is a somewhat similar piece of information, originating from /services/sync/shippingmethods
. Typically, we use the ID field in the payload:
"transporter": {
"identifier": {
"id": "b2cef0cb-43a9-4b5e-9092-c72e9e1a7e62",
"codelist": "RCP",
"idspecifier": null
},
"descr": "Courier"
}
The dispatch purpose field indicates why we are delivering the goods to the customer ( you have to take into account that there are many business reasons why a good can be delivered to a trader, and the invoice payload must support them all, otherwise most commonly for e-shops this value could be a constant, indicating a Sale )
The value we want to use in the payload originates from /services/sync/dispatchpurposes
:
"dispatchpurpose": {
"identifier": {
"id": "9350af80-ec80-43cb-aad2-74dfdf99bf00",
"codelist": "RCP",
"idspecifier": null
},
"descr": "Πώληση"
}
Payment Terms and extra Charges
Galaxy supports various ways of payment, provided by the /services/sync/paymethods
service. The selection of a payment method is included in the invoice payload in the paymentagreement field:
"paymentagreement": {
"paymentplan": {
"identifier": {
"id": "e6858a58-7368-4396-8758-bec724c9510f",
"codelist": "RCP",
"idspecifier": null
},
"descr": "Payment in 90 days"
}
}
Our backend also supports additional charges for the sale, like delivery fees etc. Those are provided by the /services/sync/charges
service.
NOTE: Please note that for the purposes of e-shops, only charges with Type == 2 should be used
To include additional charges in the invoice payload, we use the charges field:
"charges": [
{
"identifier": {
"id": "956a37af-3953-4e7c-9ddb-d0248c254890"
}
}]
Line items and their Dimensions
All that is missing from our invoice payload is the actual items sold, possible options like size and/or color, and calculating the price, taxes etc.
Line items information is included in the lines field. Below we can see what a lines payload looks like for a single product:
"lines": [
{
"qty": 1,
"item": {
"identifier": {
"id": "13f89cea-0355-4aba-b3fb-ad713eeabbf8",
"codelist": "RCP"
}
},
"itemattributes": [
{
"identifier": {
"id": "a997fac8-770c-4dff-ac50-ec7674b045ed",
"codelist": "RCP"
},
"value": "001"
},
{
"identifier": {
"id": "20671e5e-78ff-4588-a1de-fd5642a722d5",
"codelist": "RCP"
},
"value": "001"
}
],
"qtymunit": {
"identifier": {
"id": "18c685c1-e9c1-41fc-b8dc-2d0d54da06a4",
"codelist": "RCP",
"idspecifier": null
}
},
"pricemunit": {
"identifier": {
"id": "18c685c1-e9c1-41fc-b8dc-2d0d54da06a4",
"codelist": "RCP",
"idspecifier": null
}
},
"netprice": 626.612,
"netamount": 626.612,
"vatprc": 24,
"vatamount": 150.387,
"discamount": 0,
"extrataxes": null,
"extrataxestotal": null,
"deductions": null,
"deductionstotal": null
}
]
A line item contains all information regarding a single item sold. Of course, there can be many items inside the lines array.
In short, a line item contains:
- Quantity of items sold
- The ID of the item we are referring to
- Unit of Quantity
- Unit of Price
- Item options ( size, color etc. ) referred to as “attributes”
- Totals ( net price, vat percentage, vat amount etc. )
Quantity is self-explanatory, it signifies how many items are being purchased.
The item ID refers to the the ItemID field of an item that originates from the /services/sync/items service.
Quantity and price units are typically the same, and refer to the Unit1ID field in the item object.
Attributes like size and color are referred to as “dimensions” in Galaxy. Every item natively supports up to three dimension values, giving us three fields containing a Dimension ID, called Dimension1ID, Dimension2ID and Dimension3ID.
At this point, we need to take a look at the structure of item dimensions, as they are returned by the /services/sync/itemdimensions
service:
{
"DimKind": 2,
"DimValues": [
{
"ID": "ea83fae7-7451-4bf5-b479-512c5bd548ef",
"Code": "001",
"Description": "390",
"Color": null,
"AttrValuesLanguages": []
},
{
"ID": "6b8334f9-1c9e-450d-9469-96ac17d82680",
"Code": "005",
"Description": "410",
"Color": null,
"AttrValuesLanguages": []
},
{
"ID": "e319c808-e07e-4574-b849-17eec34ec236",
"Code": "007",
"Description": "420",
"Color": null,
"AttrValuesLanguages": []
}
],
"DimLanguages": [],
"ID": "24f383e5-3394-4852-868e-bc93e637be0d",
"Code": "001",
"Description": "ΑΝΔΡΙΚΟ ΜΕΓΕΘΟΛΟΓΙΟ ΥΠΟΔΗΜΑΤΩΝ",
"RevisionNumber": 0
}
As you can see, a dimension is a container with an ID and a name that holds an array of possible values. Let’s revisit our json fragment for line item attributes:
"itemattributes": [
{
"identifier": {
"id": "24f383e5-3394-4852-868e-bc93e637be0d",
"codelist": "RCP"
},
"value": "001"
},
{
"identifier": {
"id": "20671e5e-78ff-4588-a1de-fd5642a722d5",
"codelist": "RCP"
},
"value": "001"
}
]
The identifier -> id fields in the payload refer to the parent ID in the dimensions array. Then, the value field accepts the code of a value coming from the dimension’s values array.
So, for example, if we examine the two JSON fragments, we can see that our first attribute refers to size 39 for men’s shoes.
Now all that is left is to calculate prices and totals for all items, and for the complete invoice. To illustrate that, we will see some Javascript code from our reference application that emulates an e-shop using the Sync API.
The function app.calculateEntry
accepts an invoice and attempts to calculate prices and amounts for each item and the whole invoice ( “entry” in the code ):
app.calculateEntry = function (entry)
{
for (var i = 0; i < entry.lines.length; i++)
{
var oLine = entry.lines[i];
var oLineItem = _.find(app.entityCollections.items, function (item) { return item.ItemID == oLine.item.identifier.id });
var price = oLineItem.WebRetailPrice || oLineItem.ItemPrice;
// Now, the assumption is that for calculated entries the price includes VAT.
// So I need to calculate several amounts here
var vatPerc = _.find(app.entityCollections.vatsmaster, function (item) { return item.ID == oLineItem.VATsMasterID; }).NormalPcd;
var netPrice = price / (1 + vatPerc / 100); // net price, excluding vat
var netAmount = netPrice * oLine.qty; // net total
var vatAmount = (price - netPrice) * oLine.qty
oLine.netprice = netPrice;
oLine.netamount = netAmount;
oLine.vatprc = vatPerc;
oLine.vatamount = vatAmount;
oLine.discamount = 0;
oLine.extrataxes = null;
oLine.extrataxestotal = null;
oLine.deductions = null;
oLine.deductionstotal = null;
}
entry.netamount = _.reduce(entry.lines, function (memo, item) { return memo + item.netamount; }, 0);
entry.vatamount = _.reduce(entry.lines, function (memo, item) { return memo + item.vatamount; }, 0);
entry.totalamount = entry.payableamount = (entry.netamount + entry.vatamount);
entry.discamount = 0;
entry.extrataxes = null;
entry.extrataxestotal = null;
entry.extrataxesgrandtotal = null;
entry.deductions = [];
entry.deductionstotal = 0;
entry.deductionsgrandtotal = 0;
};
The app.entityCollections.vatsmaster mentioned in the code refers to the contents of the vatsmaster lookup, returned by the /services/sync/vatsmaster
service.
Note: The_.find(…)
and _.reduce(…)
methods are part of Underscore.js, a Javascript library that helps with operations on arrays and collections