arrow_back Back to blog

Odoo and Shopify order totals don't match: 8 real causes

Shopify says the order is 128.45. The sale order in Odoo says 106.16. Nobody touched anything, the connector reports no errors, and yet the two numbers disagree. In practice, almost every Odoo–Shopify amount mismatch comes down to one of eight causes — and each of them leaves a different fingerprint. This guide walks through all eight, tells you the symptom that identifies each one, and points at the exact field to check on both sides.

It applies to any Shopify order living in Odoo, whichever connector imported it: Emipro's Shopify Odoo Connector, Webkul, an OCA module or an in-house bridge. The field names below use Odoo's standard models plus, where relevant, the settings a connector typically exposes on its instance record.

First, make sure you are comparing comparable numbers

Before hunting for a bug, check what you are actually comparing. Both systems display several "totals", and they do not mean the same thing.

  • Shopify current total — what the customer owes today, after refunds and order edits. In the Admin API: currentTotalPriceSet.
  • Shopify original total — what was agreed at checkout, before any later change: originalTotalPriceSet.
  • Odoo sale order total — amount_total on sale.order: what the connector imported, plus whatever it re-synced afterwards.
  • Odoo net invoiced — posted customer invoices minus posted credit notes (account.move, move_type out_invoice and out_refund).

Those four numbers being different is not, by itself, a problem. The only real question is which pair should match, and that depends on how far the order has travelled: an order that has not been invoiced yet is a different comparison from one that has been invoiced, refunded and edited.

info Rule of thumb: once an order has been invoiced in Odoo, compare Shopify's current total against Odoo's net invoiced amount. Before that, compare it against the sale order total and treat the answer as provisional.

1 Cause 1: tax-inclusive prices

This is the most common false alarm, and the easiest to rule out. If your Shopify prices include tax and your Odoo taxes are configured as "Included in price", then Shopify's subtotal and Odoo's untaxed amount are measuring two different things: the first is gross of tax, the second is net of it.

The symptom is unmistakable: the gap is exactly the tax rate. 128.45 against 106.16 is not a rounding problem, it is 21% VAT.

The fix is to pick a comparison that survives the setting. Compare totals with tax — Shopify's current total against Odoo's amount_total — or compare nets on both sides, taking amount_total minus amount_tax in Odoo. What you must never do is compare Shopify's subtotal against Odoo's amount_untaxed unless you are certain both sides are configured the same way.

warning A tax-inclusive mismatch is invisible on zero-tax orders — exports, EU reverse charge, tax-exempt customers — so the setup can look correct for weeks and then break on the first domestic order.

2 Cause 2: the sale order is frozen, the invoice moved on

Connectors import a Shopify order into a sale.order. Once that order is invoiced, most of them stop pushing later changes into it — and rightly so: an invoiced order is an accounting document, not a mirror. From that moment, the sale order records what was ordered, and the invoices record what was actually charged.

So when a refund arrives after invoicing, Shopify's current total drops and the Odoo sale order does not move. The money did change hands in Odoo, only somewhere else: in a credit note, an account.move with move_type = out_refund.

Where to look: sale.order.invoice_ids gives you every invoice and credit note attached to the order. Add up the posted out_invoice records, subtract the posted out_refund records, and that net figure is what should match Shopify's current total. Per line, the authoritative quantity is qty_invoiced, not product_uom_qty.

This is also why comparing the sale order against Shopify gives an increasing number of false alarms as an order ages: you are comparing a snapshot taken at import time against a live document.

3 Cause 3: refunds — the money always matches, the quantities may not

Shopify refunds come in two flavours, and only one of them touches inventory. A refund can return the money and restock the units, or return the money and leave the units where they are — a damaged product, a goodwill gesture, a partial price adjustment.

In the Admin API the distinction lives on each refund line as restockType: NO_RESTOCK, CANCEL, RETURN or LEGACY_RESTOCK. The consequence for reconciliation is that a NO_RESTOCK refund lowers the amount without changing the line's live quantity, so a per-line quantity check can look wrong while the money is perfect.

This is the most misread mismatch of all, because the instinct is to trust quantities over amounts. Here the amounts are right and the quantities are telling you something else: what was invoiced versus what was physically returned.

info When you want "what is still live on this line", read currentQuantity, not quantity. The second one is what was ordered at checkout and never changes.

4 Cause 4: the order was edited in Shopify after the fact

Shopify's order editing changes an order in place: lines get added, removed or re-quantified after the customer has paid. Shopify keeps both figures — originalTotalPriceSet for the checkout, currentTotalPriceSet for the edited order — and most connectors re-sync the sale order, so Odoo's amount_total follows the edit and stops matching what was ordered.

That, by itself, is harmless. The dangerous variant is an edit that arrives after invoicing: a line that Odoo has already invoiced is removed in Shopify. Now Odoo has invoiced more than Shopify will ever collect. That is not a display artefact, it is a real over-invoice, and it needs a credit note.

Symptom: Odoo's net invoiced is higher than Shopify's current total, there is no refund on the order, and Shopify's original total is higher than its current total. Whenever those three hold at once, the order has been edited after invoicing.

5 Cause 5: gift cards, sold and spent

Gift cards produce two completely different mismatches depending on which side of the transaction they are on, and they are worth separating carefully.

Selling a gift card. In Shopify the line item is flagged isGiftCard and usually carries no SKU at all. Connectors do not match it by SKU — they detect the flag at import and route the line to a dedicated product configured on the connector instance (in Emipro, gift_card_product_id). If your reconciliation matches lines by SKU, the same card shows up as two half-empty rows: one on the Shopify side with a name and no SKU, one on the Odoo side with the synthetic product's internal reference. The card is fine; the matching key is wrong.

Paying with a gift card. Here Shopify has no line item whatsoever: a gift card used as payment is a tender, and it only appears among the order's payment gateway names. Connectors that need the payment to balance in Odoo add a negative line — "Gift card for …" — pointing at that same synthetic product. Odoo then has one more line than Shopify, and any naive line-by-line view shows a row with no counterpart.

info If the gap is a suspiciously round number that also appears among the order's payment methods, look for a gift card before you look for a pricing bug.

6 Cause 6: discounts, free gifts and artifact products

Shopify and Odoo model the same commercial gesture in different shapes. A free gift in Shopify is a second line at 0.00. A discount is an amount carried on the line itself. Odoo connectors frequently express both as an extra order line pointing at a synthetic product configured on the instance — typically a discount product, a refund adjustment product, a duties product, a tip product and a shipping product.

Those artifact lines have no counterpart in Shopify's lineItems. Line by line they look like phantom rows; on the total, they are precisely what makes the two totals agree. Deleting them would break the order, not fix it.

How to compare properly: aggregate lines by SKU before matching them, and exclude the products configured as artifacts on the connector instance. Once those two rules are in place, the remaining differences are real.

7 Cause 7: shipping, duties and fees

Shipping is its own concept in Shopify — a shipping line with its own price and tax — while in Odoo it usually arrives as a regular order line flagged is_delivery, priced from the connector's shipping product. Duties, tips and payment surcharges follow the same pattern.

The practical consequence is that the line count almost never matches, even on a perfectly synced order, and any subtotal comparison inherits that difference. Compare totals first; compare lines only after excluding delivery and fee lines on the Odoo side.

The same applies to the delivery axis: a service line has no stock move behind it, so its delivered quantity stays at zero forever. That is correct behaviour, not a pending shipment.

8 Cause 8: currency, presentment money and rounding

Shopify returns every monetary field twice: shopMoney, in your store's currency, and presentmentMoney, in the currency the customer actually saw. If you sell across currencies, the connector imported one of them and you may well be reading the other.

Odoo adds a second conversion of its own: the order is stored in its own currency and converted into the company currency at the rate of the day. A stale exchange rate produces a small gap that always leans the same way — a reliable tell, because genuine errors are rarely so consistent.

Then there is plain rounding. Differences of one to three cents on multi-line orders with per-line percentage discounts are rounding, and chasing them is a waste of an afternoon. Anything larger has one of the causes above behind it.

Quick reference: symptom to cause

What you see Most likely cause Where to check
The gap is exactly the tax rate Tax-inclusive prices Odoo tax "Included in price"; compare totals with tax
Odoo higher than Shopify, order has a refund Refund posted after invoicing account.move out_refund; qty_invoiced per line
Odoo higher, no refund, Shopify original > current Order edited after invoicing (over-invoice) originalTotalPriceSet vs currentTotalPriceSet
Amounts match, one line's quantity doesn't Refund without restock refundLineItems.restockType; currentQuantity
One extra line in Odoo, round-number gap Gift card used as payment Payment gateway names; negative line in Odoo
One gift card shown as two half-empty rows Matched by SKU instead of the gift-card flag isGiftCard; the instance's gift card product
Extra Odoo lines named after discounts or duties Connector artifact products Discount / duties / tip / shipping product on the instance
A difference of a few cents Rounding or exchange rate shopMoney vs presentmentMoney; Odoo rate of the day

A five-minute check you can repeat

When a mismatch lands on your desk, this sequence resolves it faster than staring at both screens:

  1. Open the Shopify order. Write down the current total, and note whether it carries refunds or has been edited.
  2. Open the Odoo sale order. Write down amount_total and look at invoice_status.
  3. If it is invoiced: add up the posted invoices, subtract the posted credit notes, and compare that figure — not the sale order total — against Shopify's current total.
  4. If it is not invoiced yet: compare against amount_total, and treat the result as provisional. It will change the moment the invoice is posted.
  5. If the two still disagree, walk the eight causes in order: tax first, then invoicing, refunds, edits, gift cards, artifacts, shipping, currency. The first one that fits is almost always the answer.

Five minutes per order is fine when it happens twice a month. It stops being fine when a customer service agent has to do it before answering every "you charged me the wrong amount" email, or when someone has to spot-check a day's worth of orders before closing the books.

Keep reading

Or check it in one click

Odoo–Shopify Order Check runs this whole comparison for you, inside the order page you already have open: totals, taxes, quantities, lines, addresses and delivery, with refunds and gift cards handled as context instead of as errors. It is read-only — it never writes to Odoo or Shopify — and it takes no API keys to set up.

Try it free for 14 days