Shopify as ERP Product Management Technical Scope
In order to be able to get stock levels and create orders we will need to get all product IDs from Shopify and store them on Item level because they will be the same for all marketplaces.
Summary of Changes: (The purpose of this table is to keep traceability and Product team to highlight the things that were changed into the scope, based on comments or discussions)
Version | Date | Created / Updated | Notes |
---|---|---|---|
1.0 | 23.03.2022 | Bogomil Pavlov | First publish |
1.1 | 04.07.2022 | Bogomil Pavlov | Get Product Prices Taxable node |
1.2 | 24.09.2024 | Bogomil Pavov | Get Product Details Validations |
Get Products
API Call: GET /admin/api/2022-01/products.json
API Docs: https://shopify.dev/api/admin-rest/2022-01/resources/product#get-products
<v1.2>
When we are obtaining the Shopify ids we want to have couple of validations which should work with priority as follows:
1 We check if we have duplicate SKUs on Shopify because the SKU is not a unique identifier in Shopify and we want to set an error on the SKU in Hemi so if we have the same SKU assigned on different products in Shopify we want to set Item Shopify > Get Product Details Error - “The SKU is assigned on more than one product in shopify!” and Item Shopify >Product Details Received = No.
2 Second check is to see if the SKU in Hemi is actually existing on the client Shopify and if no we want to set Item Shopify > Get Product Details Error - “SKU does not exist in Shopify“ and Item Shopify >Product Details Received= No.
3 If we match the SKU we store all the details and set **Shopify > Get Product Details = Yes
4 **If we already have a match of the SKU with already stored Shopify IDs in Hemi we want to check if the ids are different and if yes to update them and set Shopify > Get Product Details = Yes
5 **If we dont have a match of the SKU with already stored Shopify IDs in Hemi we want to remove the ids and set Shopify > Get Product Details = Yes and Item Shopify > Get Product Details Error -** “SKU does not exist in Shopify“
</v1.2>
Example Call:
-X GET "https://your-development-store.myshopify.com/admin/api/2022-01/products.json"
-H "X-Shopify-Access-Token: {access_token}"
-P "limit:250"
-P "fields":"id,variants"
Example Response:
{
"product": {
"id": 7022212612273,
"variants": [
{
"id": 41014312566961,
"product_id": 7022212612273,
"title": "50мл",
"price": "121.00",
"sku": "GP-12487",
"position": 1,
"inventory_policy": "deny",
"compare_at_price": "125.00",
"fulfillment_service": "manual",
"inventory_management": "shopify",
"option1": "50мл",
"option2": null,
"option3": null,
"created_at": "2021-10-22T22:38:45+03:00",
"updated_at": "2022-03-09T18:36:31+02:00",
"taxable": true,
"barcode": "GP-12487",
"grams": 1000,
"image_id": null,
"weight": 1.0,
"weight_unit": "kg",
"inventory_item_id": 43105192149169,
"inventory_quantity": 1000,
"old_inventory_quantity": 1000,
"requires_shipping": true,
"admin_graphql_api_id": "gid://shopify/ProductVariant/41014312566961"
},
{
"id": 41014312599729,
"product_id": 7022212612273,
"title": "100мл",
"price": "165.00",
"sku": "GP-53705",
"position": 2,
"inventory_policy": "deny",
"compare_at_price": null,
"fulfillment_service": "manual",
"inventory_management": "shopify",
"option1": "100мл",
"option2": null,
"option3": null,
"created_at": "2021-10-22T22:38:45+03:00",
"updated_at": "2022-03-12T16:00:26+02:00",
"taxable": true,
"barcode": "GP-53705",
"grams": 1000,
"image_id": null,
"weight": 1.0,
"weight_unit": "kg",
"inventory_item_id": 43105192181937,
"inventory_quantity": 999,
"old_inventory_quantity": 999,
"requires_shipping": true,
"admin_graphql_api_id": "gid://shopify/ProductVariant/41014312599729"
}
]
}
}
Response Mapping:
Integration Field | Integration Notes | Integration required | Hemi Mapping | Hemi Notes | ||
---|---|---|---|---|---|---|
product | ||||||
id | Yes | N/A | ||||
variants | ||||||
id | Yes | Item Shopify > Variant ID | ||||
product_id | Yes | Item Shopify > Product ID | ||||
sku | Yes | Used for the mapping | ||||
inventory_item_id | Yes | Item Shopify > Inventory Item ID |
Get Stock
API Call: GET /admin/api/2022-01/inventory_levels.json
API Docs: https://shopify.dev/api/admin-rest/2022-01/resources/inventorylevel#top
Basically depends how many location ids we have mapped in Shopify Connector Locations table we can either get for all of the location ids if we specify them in the location_ids
parameter separated by comma or
we can call each ID with a separate call.
Please note if we have inventory item ID in Item Shopify which is missing from the payload which we receive from Shopify we will have to set the Item Location Quantity > Quantity = 0.
Please have in mind the right location should be selected.
Example Call:
-X GET "https://your-development-store.myshopify.com/admin/api/2022-01/inventory_levels.json"
-H "X-Shopify-Access-Token: {access_token}"
-P "location_ids: {A comma-separated list of location IDs. To find the ID of a location, use the}"
-P "limit:250"
Example Response:
"inventory_levels":[
{
"inventory_item_id": 43105057669297,
"location_id": 64966426801,
"available": 1000,
"updated_at": "2021-11-11T10:20:35+02:00",
"admin_graphql_api_id": "gid://shopify/InventoryLevel/99293921457?inventory_item_id=43105057669297"
}
Response Mapping:
Integration Field | Integration Notes | Integration required | Hemi Mapping | Hemi Notes | |
---|---|---|---|---|---|
inventory_levels |
|||||
inventory_item_id |
Yes | We use this ID in order to Map Item Shopify > Inventory Item ID and get the SKU on Item level | |||
location_id |
Yes | Based on the Mapping we have and the Locations added in the Account we will: | |||
Map this location_id with Shopify Connector Locations > External ID and get the Location >Name which must be added in the Account as Account Location and them store the Quantity in the correct Item Location Quantity > Location |
|||||
available |
Yes | Location Quantity > Quantity | |||
updated_at |
No | N/A | |||
admin_graphql_api_id |
No | N/A |
If the there are inventory_item_id which do not match any of our Item Shopify > Inventory Item ID we skip them.
History need to be incorporated and keep track of all stock changes in Item Location Quantity.
Get Prices
API Call: GET /admin/api/2022-01/products.json
API Docs: https://shopify.dev/api/admin-rest/2022-01/resources/product#get-products
Here we will have two options on how we will be getting the prices. Case1 we get the “price” and “compare at price” and store it as Item Account > Original Price and Item Account > Original RRP Case2 we get the “price” and “compare at price” and store them as Item Account > Price and Item Account > RRP These will be controlled by parameter added in the Account Shopify Connector > Get Prices Update Original Price.
Only in Case2 if there is price difference we will need to set the Item Account > Update Price = pending
The way we are getting prices is we call all products which have the Item Shopify > Product ID and depends on the parameter we update all Item Accounts for that Item.
Example Call:
-X GET "https://your-development-store.myshopify.com/admin/api/2022-01/products.json"
-H "X-Shopify-Access-Token: {access_token}"
-P "fields:variants"
-P "ids: {add all Item Shopify > Product IDs separated by comma}"
-P "limit:250"
Example Response:
{
"products": [
{
"variants": [
{
"id": 42538050027735,
"product_id": 7586215395543,
"title": "Default Title",
"price": "125.00",
"sku": "TE-1205181201",
"position": 1,
"inventory_policy": "deny",
"compare_at_price": null,
"fulfillment_service": "manual",
"inventory_management": "shopify",
"option1": "Default Title",
"option2": null,
"option3": null,
"created_at": "2022-03-21T21:15:12+02:00",
"updated_at": "2022-03-21T21:15:12+02:00",
"taxable": true,
"barcode": "TT-1205181201",
"grams": 1000,
"image_id": null,
"weight": 1.0,
"weight_unit": "kg",
"inventory_item_id": 44629971763415,
"inventory_quantity": 10,
"old_inventory_quantity": 10,
"requires_shipping": true,
"admin_graphql_api_id": "gid://shopify/ProductVariant/42538050027735"
}
]
},
{
"variants": [
{
"id": 42538050683095,
"product_id": 7586215755991,
"title": "Default Title",
"price": "107.60",
"sku": "TE-04788",
"position": 1,
"inventory_policy": "deny",
"compare_at_price": null,
"fulfillment_service": "manual",
"inventory_management": "shopify",
"option1": "Default Title",
"option2": null,
"option3": null,
"created_at": "2022-03-21T21:15:50+02:00",
"updated_at": "2022-03-21T21:15:50+02:00",
"taxable": true,
"barcode": "TT-04788",
"grams": 1000,
"image_id": null,
"weight": 1.0,
"weight_unit": "kg",
"inventory_item_id": 44629972418775,
"inventory_quantity": 10,
"old_inventory_quantity": 10,
"requires_shipping": true,
"admin_graphql_api_id": "gid://shopify/ProductVariant/42538050683095"
}
]
},
{
"variants": [
{
"id": 42538050257111,
"product_id": 7586215559383,
"title": "Default Title",
"price": "24.20",
"sku": "TE-12051609",
"position": 1,
"inventory_policy": "deny",
"compare_at_price": null,
"fulfillment_service": "manual",
"inventory_management": "shopify",
"option1": "Default Title",
"option2": null,
"option3": null,
"created_at": "2022-03-21T21:15:23+02:00",
"updated_at": "2022-03-21T21:15:23+02:00",
"taxable": true,
"barcode": "TT-12051609",
"grams": 1000,
"image_id": null,
"weight": 1.0,
"weight_unit": "kg",
"inventory_item_id": 44629971992791,
"inventory_quantity": 10,
"old_inventory_quantity": 10,
"requires_shipping": true,
"admin_graphql_api_id": "gid://shopify/ProductVariant/42538050257111"
}
]
}
]
}
*Response Mapping:*Account Shopify Connector > Get Prices Update Original Price = NO
Integration Field | Integration Notes | Integration required | Hemi Mapping | Hemi Notes | |
---|---|---|---|---|---|
variants |
|||||
price |
Product Account > Price | ||||
compare_at_price |
Product Account > RRP | ||||
taxable |
Product Account > Prices Includes VAT | If we receive: |
"taxable": true
– Item Account > Prices Includes VAT = 1
"taxable": false
– Item Account > Prices Includes VAT = 0 |
If there are changes in the prices after successful update we will also have to set Item Account > Update Price = pending
Account Shopify Connector > Get Prices Update Original Price = YES
Integration Field | Integration Notes | Integration required | Hemi Mapping | Hemi Notes | |
---|---|---|---|---|---|
variants |
|||||
price |
Product Account > Original Price | ||||
compare_at_price |
Product Account > Original RRP | ||||
taxable |
Product Account > Prices Includes VAT | If we receive: |
"taxable": true
– Item Account > Prices Includes VAT = 1
"taxable": false
– Item Account > Prices Includes VAT = 0 |
If there are changes in the original price we just update it and do nothing.
History need to be incorporated and keep track of all price changes.
Get Full Product Details File
API Call: GET /admin/api/2022-01/products.json
API Docs: https://shopify.dev/api/admin-rest/2022-01/resources/product#get-products
The purpose of this call is to be able to get all the product details information from Shopify in a csv file from where we can pick the data optimize it and manually import it in Hemi. The CSV delimiter will be comma (,) and we need to place all the enclosing (“) and escaping end line characters (\r\n)
Get Products Response
{
"product":{
"id":5076458340485,
"title":"Боди С Къс Ръкав Папагали Summer Nice Day",
"body_html":"Боди с къс ръкав Папагали SUMMER NICE DAY",
"vendor":"Pinokio",
"product_type":"Боди Къс Ръкав",
"created_at":"2020-05-27T23:00:07+03:00",
"handle":"боди-с-къс-ръкав-папагали-summer-nice-day",
"updated_at":"2022-05-03T02:45:07+03:00",
"published_at":"2020-05-27T23:00:07+03:00",
"template_suffix":null,
"status":"active",
"published_scope":"web",
"tags":"color_colorful, katieghorii_bodi-ks-rkav, marka_pinokio, price_0-50lv, razmier_62sm-1-3m, Пол_Универсални",
"admin_graphql_api_id":"gid:\/\/shopify\/Product\/5076458340485",
"variants":[
{
"id":34279122075781,
"product_id":5076458340485,
"title":"62",
"price":"15.90",
"sku":"PNK00004",
"position":1,
"inventory_policy":"deny",
"compare_at_price":"22.59",
"fulfillment_service":"manual",
"inventory_management":"shopify",
"option1":"62",
"option2":null,
"option3":null,
"created_at":"2020-05-27T23:00:07+03:00",
"updated_at":"2022-04-03T17:52:19+03:00",
"taxable":true,
"barcode":"PNK00004",
"grams":1000,
"image_id":17459333890215,
"weight":1.0,
"weight_unit":"kg",
"inventory_item_id":36161640038533,
"inventory_quantity":1,
"old_inventory_quantity":1,
"requires_shipping":true,
"admin_graphql_api_id":"gid:\/\/shopify\/ProductVariant\/34279122075781"
},
{
"id":34279122108549,
"product_id":5076458340485,
"title":"68",
"price":"15.90",
"sku":"PNK00005",
"position":2,
"inventory_policy":"deny",
"compare_at_price":"22.59",
"fulfillment_service":"manual",
"inventory_management":"shopify",
"option1":"68",
"option2":null,
"option3":null,
"created_at":"2020-05-27T23:00:07+03:00",
"updated_at":"2022-04-03T17:52:19+03:00",
"taxable":true,
"barcode":"PNK00005",
"grams":1000,
"image_id":null,
"weight":1.0,
"weight_unit":"kg",
"inventory_item_id":36161640104069,
"inventory_quantity":1,
"old_inventory_quantity":1,
"requires_shipping":true,
"admin_graphql_api_id":"gid:\/\/shopify\/ProductVariant\/34279122108549"
},
{
"id":34279122141317,
"product_id":5076458340485,
"title":"74",
"price":"15.90",
"sku":"PNK00006",
"position":3,
"inventory_policy":"deny",
"compare_at_price":"22.59",
"fulfillment_service":"manual",
"inventory_management":"shopify",
"option1":"74",
"option2":null,
"option3":null,
"created_at":"2020-05-27T23:00:07+03:00",
"updated_at":"2022-04-03T17:52:19+03:00",
"taxable":true,
"barcode":"PNK00006",
"grams":1000,
"image_id":null,
"weight":1.0,
"weight_unit":"kg",
"inventory_item_id":36161640169605,
"inventory_quantity":1,
"old_inventory_quantity":1,
"requires_shipping":true,
"admin_graphql_api_id":"gid:\/\/shopify\/ProductVariant\/34279122141317"
},
{
"id":34279122174085,
"product_id":5076458340485,
"title":"80",
"price":"15.90",
"sku":"PNK00007",
"position":4,
"inventory_policy":"deny",
"compare_at_price":"22.59",
"fulfillment_service":"manual",
"inventory_management":"shopify",
"option1":"80",
"option2":null,
"option3":null,
"created_at":"2020-05-27T23:00:07+03:00",
"updated_at":"2022-04-03T17:52:20+03:00",
"taxable":true,
"barcode":"PNK00007",
"grams":1000,
"image_id":null,
"weight":1.0,
"weight_unit":"kg",
"inventory_item_id":36161640202373,
"inventory_quantity":0,
"old_inventory_quantity":0,
"requires_shipping":true,
"admin_graphql_api_id":"gid:\/\/shopify\/ProductVariant\/34279122174085"
}
],
"options":[
{
"id":6609445912709,
"product_id":5076458340485,
"name":"variations",
"position":1,
"values":[
"62",
"68",
"74",
"80"
]
}
],
"images":[
{
"id":17459333890215,
"product_id":5076458340485,
"position":1,
"created_at":"2020-06-17T20:35:22+03:00",
"updated_at":"2022-05-03T02:45:07+03:00",
"alt":"Боди С Къс Ръкав Папагали Summer Nice Day Боди Къс Ръкав BebeMama Pinokio",
"width":1400,
"height":1400,
"src":"https:\/\/cdn.shopify.com\/s\/files\/1\/0257\/1868\/5778\/products\/pinokio--62-summer-nice-day-pnk00004-summer-nice-day-pnk00004-16554482008197.jpg?v=1651535107",
"variant_ids":[
34279122075781
],
"admin_graphql_api_id":"gid:\/\/shopify\/ProductImage\/17459333890215"
}
],
"image":{
"id":17459333890215,
"product_id":5076458340485,
"position":1,
"created_at":"2020-06-17T20:35:22+03:00",
"updated_at":"2022-05-03T02:45:07+03:00",
"alt":"Боди С Къс Ръкав Папагали Summer Nice Day Боди Къс Ръкав BebeMama Pinokio",
"width":1400,
"height":1400,
"src":"https:\/\/cdn.shopify.com\/s\/files\/1\/0257\/1868\/5778\/products\/pinokio--62-summer-nice-day-pnk00004-summer-nice-day-pnk00004-16554482008197.jpg?v=1651535107",
"variant_ids":[
34279122075781
],
"admin_graphql_api_id":"gid:\/\/shopify\/ProductImage\/17459333890215"
}
}
}
Get Products Response Mapping
Field | CSV Header Name | CSV Position | |||
---|---|---|---|---|---|
product | |||||
id | Shopify Product ID | 14 | Will be the same for all variants | ||
title | Title | 2 | Will be the same for all variants | ||
body_html | Description | 4 | Will be the same for all variants | ||
vendor | Brand | 5 | Will be the same for all variants | ||
product_type | Category | 6 | Will be the same for all variants | ||
created_at | |||||
handle | |||||
updated_at | Updated Date | 18 | Will be the same for all variants | ||
published_at | |||||
template_suffix | |||||
status | Shopify Status | 19 | Will be the same for all variants | ||
published_scope | |||||
tags | Tags | 12 | Will be the same for all variants | ||
admin_graphql_api_id | |||||
variants | |||||
id | Shopify Variant ID | 15 | |||
product_id | |||||
title | Variant | 3 | |||
price | Price | 7 | |||
sku | SKU | 1 | |||
position | |||||
inventory_policy | |||||
compare_at_price | RRP | 8 | |||
fulfillment_service | |||||
inventory_management | |||||
option1 | |||||
option2 | |||||
option3 | |||||
updated_at | |||||
created_at | |||||
taxable | |||||
barcode | Barcode | 10 | |||
grams | Weight (g) | 11 | |||
image_id | |||||
weight | |||||
weight_unit | |||||
inventory_item_id | Inventory Item ID | 13 | |||
inventory_quantity | Quantity | 9 | |||
old_inventory_quantity | |||||
requires_shipping | |||||
admin_graphql_api_id | |||||
options | |||||
id | |||||
product_id | |||||
name | |||||
position | |||||
values | |||||
images | |||||
id | |||||
product_id | |||||
position | |||||
created_at | |||||
updated_at | |||||
alt | |||||
width | |||||
height | |||||
src | More Images | 17 | Need to combine all Image > src and concatenate them separated by ; | ||
variant_ids | |||||
image | |||||
id | |||||
product_id | |||||
position | |||||
created_at | |||||
updated_at | |||||
alt | |||||
width | |||||
height | |||||
src | Main Image | 16 | |||
variant_ids | |||||
admin_graphql_api_id |
We will need to be able to decide where to drop the file and on which FTP so we will need to use FTP Credentials (130) table and setup the FTP details there . File name - GetProducts{{YYYYMMDD_HHMMSS}}.csv
FTP Path Label: ExportProductDetails