How the Wbsync replacement works

Derwent Distributors: website orders into MYOB, and MYOB stock and prices back onto the website. Status as of 6 Oct 2026: shadow mode (watching, not writing).

The big picture

Website WooCommerce dealers order here 5 dealer price levels Our bridge data VPS order poller (1 min) stock sync (15 min) MYOB data pull (30 min) status page + alerts MYOB AccountRight (live file) Tim invoices here source of truth for stock new orderssales orders stock + pricesstock + price

Each kind of data moves one way only, and every field has exactly one owner, so the two systems never fight over a number.

Wbsync today vs our bridge

Wbsync (current)Our bridge
Orders → MYOBOn-hold orders become sales ordersSame, polled every minute
Stock → websiteHourly, raw MYOB on-handEvery 15 min, on-hand minus stock on open sales orders
Price → websiteRegular price onlyRegular price only (dealer levels via a later upload tool)
Duplicate protectionUnknownChecks MYOB for the Woo order number before every post
ProblemsSilentOrder held + alert; status page lists everything
CostA$29/month (card failing)Runs on the existing data VPS

What happens when a dealer places an order

  1. Dealer checks out on the website

    The site creates the order as On hold with the dealer's tier prices, and Woo lowers stock straight away.
  2. The bridge picks it up within a minute

    It only looks at on-hold orders after an agreed start number, so nothing Wbsync already posted is touched.
  3. Find the dealer's MYOB customer card

    Uses the link saved on the dealer's website account. No link yet? If exactly one MYOB card has the same email, it links automatically. Otherwise the order is held and flagged.
  4. Find every product in MYOB

    By exact SKU. If any line can't be found, the whole order is held — never a partial order.
  5. Check it hasn't been posted already

    Searches MYOB for a sales order carrying this Woo order number. If one exists, it links to it instead of posting again.
  6. Create the MYOB sales order

    PO field = Woo order number, prices exactly as charged (GST-inclusive, GST split out), customer note copied across.
  7. Read it back and compare

    Customer, items, quantities, prices and total must match. Any mismatch raises an alert.
  8. Stamp the website order

    Saves the MYOB sales order number on the Woo order with a note. Tim still marks orders Completed himself.
If MYOB times out mid-post, the order is marked unknown. The next run searches MYOB for it instead of retrying blindly — so a slow connection can never create a double order.

How stock and prices get to the website

Stock

MYOB's on-hand count only drops when Tim invoices a sales order — often days after the website sale. Pushing raw on-hand (what Wbsync does) makes stock bounce back up in between.

So the bridge sends on hand − units on open sales orders, never below 0. That includes Tim's phone and counter orders.

Prices

The MYOB selling price (GST-inclusive) becomes the website's regular price. The five dealer price levels are not touched — MYOB's web version doesn't expose them.

Only products that actually changed are updated.

Safety brakes

The run stops without writing anything if MYOB returns fewer than 500 items, or plans more than 300 stock or 100 price changes at once. (This already caught one bug during testing.)

Who owns which field

FieldOwnerHow it moves
SKU, name, active/inactiveMYOB→ website
StockMYOB (minus committed)→ website, every 15 min
Regular priceMYOB→ website, every 15 min
5 dealer price levelsMYOB desktop export→ website, via a planned upload tool
Photos, descriptions, categoriesWebsitestays on the website
OrdersWebsite→ MYOB sales orders

Inside the bridge

Code lives in derwent-myob-api/bridge/ and runs on the data VPS as two scheduled jobs. Each run starts, does its work, and exits — there's no long-running process to crash.

Order timerevery 1 min Stock timerevery 15 min Order runbridge.run Stock runbridge.stock_run Woo clientREST API key MYOB clientweb-app requests Browser loginshared + locked State databasebridge.sqlite Status page/v1/bridge/status Catalogue fileprices, every 30 min The browser login is only opened when a run actually has work, so quiet minutes don't log in to MYOB.

The pieces

PieceWhat it does
bff.pyOpens the shared MYOB browser login, copies the security headers the MYOB web app itself uses, and replays them for our requests.
myob.pyThe MYOB calls: find item by SKU, item account and tax code, customer address, new sales order, create, read back, search by PO number, item list with stock, open sales orders.
woo.pyWebsite calls: on-hold orders, customers, products and variations, batch updates, order notes.
payload.pyTurns a Woo order into a MYOB sales order. Pure maths, no network — GST split, line totals, read-back comparison.
service.pyThe order process: link customer, map items, dedupe, post, read back, stamp Woo.
stock.pyWorks out available stock and price per SKU, diffs against the website, applies safety caps, then writes (or records, in shadow).
store.pyThe SQLite memory: what happened to each order, cached sales order details, stock run history.
routes.pyThe status page: counts by state, problem orders, latest stock run.

Every order has a state

The bridge writes a row for each Woo order before it does anything risky, so if the server restarts mid-way it knows exactly where it was.

StateMeaningWhat happens next
syncedMYOB sales order exists and matchesDone. MYOB number saved on the Woo order.
shadow_readyShadow mode: payload built and saved to a file, nothing postedCompared by hand against what Wbsync posts.
submittingAbout to post, or posting nowIf found here after a crash, MYOB is searched first.
unknownMYOB timed out or errored mid-postEvery run searches MYOB for the PO number. Found → synced. Never re-posted blindly.
held_customerNo linked MYOB card and no unique email match (or a guest order)Alert. Link the dealer once; the next run posts it.
held_skuA product SKU isn't in MYOB, or matches twiceAlert. Fix the SKU; the whole order then posts.
held_otherFee lines, or lines + shipping don't add up to the Woo totalAlert for a person to look at.
readback_mismatchPosted, but MYOB's saved copy differsAlert. Not deleted automatically.

Worked example

A level-5 dealer orders 2 tyres at $202.67 each. Woo stores the line total $405.34 with tax 0 (the site doesn't calculate tax). MYOB prices include GST, so the bridge sends:

FieldValueFrom
Customerdealer's MYOB cardlink on the Woo account
PO numberWoo order numberso it can always be found again
ItemMYOB item for the SKUexact SKU match
Units / unit price2 × $202.67exactly what the dealer was charged
Line amount$405.34GST-inclusive
GST$36.85$405.34 ÷ 11
Ex-GST$368.49amount − GST
Account, tax codeitem's income account, GSTMYOB item settings
Notedealer's checkout noteWoo customer note

This is the same shape Wbsync produces — we checked a test order against one of Wbsync's, field by field, before deleting it.

Settings

SettingNowPurpose
BRIDGE_MODEshadowshadow = check and record only; live = write to MYOB and Woo
BRIDGE_START_AFTER_ORDER_ID3670Only orders after this number. Set to Wbsync's last order at switch-over.
STOCK_SUBTRACT_COMMITTEDonSubtract units on open MYOB sales orders
STOCK_SYNC_PRICESonPush MYOB regular price
STOCK_MAX_CHANGES / PRICE_MAX_CHANGES300 / 100Brakes: above these, nothing is written and an alert fires

Running it day to day

Is it working?

Open the status page: counts by state, any held orders, and the last stock run with its change counts.

An order is held

Read the reason, fix the cause (link the dealer, correct the SKU), and the next minute's run posts it. No manual re-entry.

Pause everything

Stop the two timers on the VPS. Orders simply wait on hold in Woo and are picked up when it's restarted.

MYOB login expired

Runs exit without writing; the existing login-rescue process restores the session, then runs resume.

How the bridge talks to MYOB

The data VPS already keeps a logged-in MYOB browser session for the 30-minute data pull. The bridge borrows that session and makes the same requests the MYOB web app makes when you click Save — so there's no extra MYOB subscription.

The data pull, the daily item job, the order bridge and the stock sync share one lock, so they take turns with the login instead of colliding.

Shadow mode, then switch-over

  1. Shadow running now

    Both syncs do all their checks and record what they would do, but write nothing. First stock run: 2,069 MYOB items, all matched; 20 stock and 7 price changes planned.
  2. Compare against Wbsync

    For the next few real orders, check our would-be sales order matches the one Wbsync posts.
  3. Link dealers

    6 dealers already matched from past orders with no conflicts; the rest link automatically by email on their next order.
  4. Switch to live

    Set the start order to the last one Wbsync posted, turn on live mode, stop Wbsync. Never both at once.

Proven so far

done

Live MYOB write

Test customer created; a test sales order created, read back identical to a Wbsync order, then deleted.
done

Deployed in shadow

Order poller every minute, stock sync every 15 minutes, 98 automated tests passing.
waiting

Decisions

Approve the 6 dealer links; Tim to confirm the new Michelin prices in MYOB before price sync goes live.