#How the connector moves stock
Microsoft's own description is plain: inventory synchronisation "sends current stock levels from Business Central to Shopify", and Business Central "acts as the system of record for inventory". Nothing comes back the other way as stock. Shopify's sales come back as orders, and only change Business Central's figure once they become sales documents.
Two consequences follow. Shopify is overwritten: when its figure differs from what Business Central calculates, the connector updates Shopify, so a count corrected by hand in the Shopify admin lasts until the next sync. And the figure sent is a calculation, not the quantity on hand.
The post on Shopify inventory not updating covers connectors in general, and the one on Shopify POS covers the till. This one is about the Business Central connector's own settings.
#The 7 causes
All the settings below are on the Shopify Shop Card page in Business Central (search for Shopify Shops and open the shop), or on the Shopify Shop Locations page it opens with the Locations action.
#1. The location has no stock calculation
Microsoft lists four conditions for a sync, and one of them is that "the Shopify location has a Stock calculation method". A location brought in with Get Shopify Locations and never given one is never sent a figure.
How to fix it: on Shopify Shop Locations, choose a method for each store's row. The standard two are Projected Available Balance at Today and Free Inventory (not reserved). If an extension added the method you use, Microsoft's troubleshooting says to ask its provider whether it permits inventory export.
The pattern: every SKU at that store stays frozen in Shopify while Business Central keeps moving.
#2. The Location Filter is blank
The Location Filter decides which Business Central locations make up a Shopify location's figure. Microsoft's instruction is "Leave the field blank to include all locations." For one online store, that may be what you want. For a chain whose stores are Shopify locations, it means every store is sent the stock of every location added together, warehouse included.
How to fix it: on each store's row, put that store's Business Central location code in Location Filter, for example ST-022. Microsoft notes that after you change it you run Sync inventory again, and the connector recalculates.
The pattern: a small store showing the chain's total. Its Shopify figure equals the sum of every Business Central location for that SKU, at every store at once.
#3. The variant isn't tracked, or has no record at that store
Stock is only sent where "inventory tracking is enabled for a product in Shopify" and "Shopify has an inventory record for the product variant and location". In the Shopify admin, open the product or variant: Track quantity must be on, and the store must appear under Inventory.
New stores are where this bites. Microsoft warns that when you add a location in Shopify, Shopify doesn't add inventory records for existing products, and the connector doesn't sync them there. Default Product Location only applies to products the connector creates later. For the rest, assign the inventory to the new location in Shopify before you sync.
The Inventory Tracked setting on the shop card sets Track quantity only when a product is exported for the first time, so changing it later doesn't switch tracking on for products already in Shopify.
The pattern: the SKU is missing from Shopify's export for that store, or never changes there.
#4. The variant is linked to the wrong item
On the Shopify Products page, each Shopify variant shows its Item No. and Variant No. in the Shopify Variants part. The stock sent is that item's. If SKU Mapping on the shop card doesn't match how SKUs are built (item number, item number plus variant code, vendor item number or barcode), a variant can be linked to a different item, or to none.
Two more cases from Microsoft's pages: non-inventory and service items are excluded from inventory sync, and units of measure sold as variants share one availability figure per item, which Microsoft says can leave Shopify's available quantity inaccurate until the next sync.
The pattern: a SKU whose Shopify figure matches another item's stock in Business Central, or never moves at all.
#5. Shopify sales that Business Central doesn't have yet
Store managers notice this one most: a sale at the till brings Shopify's count down, and minutes later it is back up. A Shopify POS sale is usually fulfilled in Shopify straight away. Microsoft's POS page explains what happens next: creating the sales document for an order already fulfilled gives a Sales Invoice, and "a sales invoice doesn't reduce inventory levels". Until the invoice is posted, Business Central still counts the item as on the shelf, so the next sync sends the old figure back to Shopify. Microsoft suggests posting those invoices as soon as you import them, on a schedule.
Orders that never arrive do the same. The connector can't import an order archived in Shopify, so Microsoft says to keep Automatically archive the order switched off in Shopify's general settings. An order can also stop on an error: open Shopify Orders to Import and look at Has Error. POS orders without a customer address are a common one, because the connector needs an address to create the customer.
The pattern: Shopify and Business Central agree with each other, and both are higher than the store's count by the units on unposted or unimported orders.
#6. The calculation subtracts orders you forgot about
Projected Available Balance at Today takes the stock and subtracts sales orders due to ship by today. Microsoft's example: 10 on hand, one order of 1 for Monday and one of 2 for Thursday; a sync on Tuesday sends 9, on Friday 7. Free Inventory (not reserved) subtracts only quantities reserved from stock.
This one isn't a fault, but know it before anyone "fixes" it by hand.
The pattern: Shopify lower than Business Central's quantity by exactly the open sales orders for that item and location.
#7. The sync didn't run, or skipped the update
Microsoft suggests running inventory sync on the job queue every 15 to 30 minutes. If the entry stopped, nothing moves. To see what happened to one variant:
- On Shopify Products, select the product and look at the Shopify Inventory FactBox. It shows, per location, Shopify Stock, Last Calculated Stock, Last Synced On and Last Calculated On. If the two stock figures are equal, the connector sends nothing.
- Open Shopify Log Entries, filter Request Preview on
*inventorySetQuantities*, and look for Has Error. The Response Data shows Shopify's reason under userErrors. - To force one variant, run the Sync Stock to Shopify report with its Variant ID Filter, first with Skip Import Stock off and then, if it is still wrong, with it on.
Bundles are a special case: the Shopify Bundles app calculates bundle stock itself and refuses updates through the API. Microsoft suggests mapping bundles to non-inventory items.
The pattern: dates in the FactBox days old, and differences that grow with every sale.
#Line up every store at once
One product page tells you a count is wrong. Every location side by side tells you which cause it is.
From Shopify:
- In the Shopify admin, go to Products > Inventory and click Export.
- Under Export from, choose all locations, and under Inventory states shown, select All states.
- Export as CSV. The file has one row per variant per location, with SKU, Location and Available.
From Business Central: the Items by Location page shows each item's quantity at each location. Save those quantities as bc_stock.csv with three columns, sku, location and qty, naming each location as Shopify does. Items by Location works per item, not per item variant, so this suits SKUs mapped to item numbers.
This script prints every SKU and store where the two differ, and flags a Shopify figure that equals the chain's total in Business Central (cause 2):
import csv
import sys
from collections import defaultdict
def number(text):
text = (text or "").strip()
if text == "":
return 0
try:
return int(float(text))
except ValueError:
return None # "not stocked", "not tracked" and the like
def load(path, sku_col, loc_col, qty_col):
counts, flags = {}, {}
with open(path, newline="", encoding="utf-8-sig") as f:
for row in csv.DictReader(f):
sku = (row.get(sku_col) or "").strip()
if not sku:
continue
key = (sku, row[loc_col].strip())
qty = number(row[qty_col])
if qty is None:
flags[key] = row[qty_col].strip()
else:
counts[key] = counts.get(key, 0) + qty
return counts, flags
shop, shop_flags = load(sys.argv[1], "SKU", "Location", "Available")
bc, _ = load(sys.argv[2], "sku", "location", "qty")
chain_total = defaultdict(int)
for (sku, _loc), qty in bc.items():
chain_total[sku] += qty
print("sku,location,shopify,business_central,difference,note")
for key in sorted(set(shop) | set(bc) | set(shop_flags)):
sku, loc = key
if key in shop_flags:
print(f"{sku},{loc},,{bc.get(key, '')},,{shop_flags[key]}")
elif key not in shop:
print(f"{sku},{loc},,{bc[key]},,missing in Shopify")
elif key not in bc:
print(f"{sku},{loc},{shop[key]},,,missing in Business Central")
elif shop[key] != bc[key]:
note = "equals chain total" if shop[key] == chain_total[sku] else ""
print(f"{sku},{loc},{shop[key]},{bc[key]},{shop[key] - bc[key]},{note}")Run it as python compare_stock.py inventory_export.csv bc_stock.csv > differences.csv. Expect small differences from cause 6 wherever orders are open, and check those against the open sales orders before you call them errors.
#A worked example
An invented chain of 120 stores sells through Shopify POS at every till and the online store, with Business Central as the ERP and each store a Shopify location. On Monday morning the script returns four rows, and the store manager at ST-014 rings about a fifth:
| SKU | Location | Shopify available | Business Central | Difference | Note |
|---|---|---|---|---|---|
| MUG-WHT | ST-022 | 212 | 6 | +206 | equals chain total |
| CAP-NVY | ST-004 | 6 | missing in Shopify | ||
| SCARF-RED | ST-037 | 4 | 7 | −3 | |
| SOCK-GRY | ST-011 | 20 | 12 | +8 | |
| TEE-BLK-M | ST-014 | 14 | 14 | 0 | store counted 9 |
Invented figures. Read the patterns:
- ST-022, 212 against 6. 212 is MUG-WHT across every Business Central location. ST-022's row on Shopify Shop Locations has a blank Location Filter (cause 2). Setting it to
ST-022and running Sync inventory sends 6. - ST-004, missing. ST-004 opened last month; CAP-NVY existed before, so it has no inventory record there (cause 3). Assign it to the location in Shopify, then sync.
- ST-037, −3. A sales order for three scarves ships from ST-037 today, and Projected Available Balance at Today subtracts it (cause 6). Nothing to fix.
- ST-011, +8. The Shopify Inventory FactBox shows Last Calculated On three days ago: the job queue entry for the sync is on hold (cause 7). The eight socks that have left ST-011 in Business Central since then never reached Shopify.
- ST-014, 14 and 14, shelf 9. The two systems agree, which is why the script can't see it. Five T-shirts sold at the till on Saturday sit on unposted sales invoices, and every sync puts them back (cause 5). Posting the invoices on a schedule stops it.
#Doing it every morning
The script takes minutes, but the causes come back: another store opens without inventory records, another invoice batch waits over a weekend.
DataMosaic runs that comparison every night against the systems themselves. Its Shopify connection asks for read-only access to orders, products, stock and locations, and arrives with a Stock by location query and an Orders query that names the store for POS sales. Microsoft Dynamics 365 is in Mosaic's catalogue of ERPs. A check sets Shopify's stock by location against Business Central's, per SKU and store, and its rules say how far apart is too far. A row that differs offers an investigation, so a store showing the chain's total points at a location setting rather than a stock-take. If you want Mosaic to act, say by emailing whoever looks after the connector when the same store differs two nights running, that is a workflow you build.
The same idea, comparing at a grain both systems keep, is how POS and ERP sales are checked each morning.
#Questions people ask
#Why is Business Central not updating inventory in Shopify?
Usually the Shopify location has no Stock calculation method, the variant isn't tracked or has no inventory record at that location, or the variant isn't linked to an inventory item. Check the Shopify Inventory FactBox and the Shopify Log Entries for that variant.
#Does the Shopify Connector sync inventory both ways?
No. Stock goes from Business Central to Shopify only. Shopify sales come back as orders, and change Business Central's stock once they become sales documents and, for invoices, once those are posted.
#Why does Shopify show more stock than one store has?
Most often that store's Location Filter on Shopify Shop Locations is blank, so it is sent the stock of every Business Central location added together. Put the store's own location code in the filter and sync again.
#How often does Business Central sync inventory to Shopify?
As often as you schedule it. You can run Sync inventory by hand from the shop card, and Microsoft suggests a job queue entry every 15 to 30 minutes.
#Why does stock go back up in Shopify after a POS sale?
The sale arrives in Business Central as a sales invoice, and an unposted invoice doesn't reduce Business Central's stock. The next sync sends the old figure back to Shopify. Post those invoices as soon as they are imported.