Not all of your business happens inside BlossomERP. Six channel connections bring the outside in, so the numbers of this chapter can cover the whole business rather than the part that was typed in:
- Shopify and WooCommerce — your online stores. Orders, products and customers flow in, and your inventory position flows back out to the store, so what the shop promises a buyer is what the warehouse actually holds.
- Amazon — your marketplace selling. Orders flow in, and stock held for you by Amazon (FBA) is read into a warehouse you choose, so fulfilment-by-Amazon stock stands in the same stock picture as your own shelves.
- Meta Ads, TikTok and Google Ads — your advertising platforms. Each brings in its ad spend, daily. Advertising money is the classic orphan figure — spent in one system, earned back in another, compared in a spreadsheet. Landed beside the orders it produced, in the same governed measures, it can finally be read as what it is: a cost of the revenue next to it.
What the ad platforms report — read it as theirs. Each platform's figures arrive exactly as the platform reports them: spend, impressions, clicks, purchases, and the purchase value the platform attributes to your advertising. That purchase value is the platform's own attribution, in the platform's own words — it is not your sales revenue, which is counted once, in your books, from your documents. Each ad account's currency is recorded with its figures; if you advertise in more than one currency, spend totals add those currencies together as reported, so read them broken down by currency when that matters. The ROAS dashboard widget divides your governed revenue by this spend — a useful reading, with that currency point kept in mind.
Three things are true of every connection. BlossomERP remains the source of truth: channels read your products and your inventory from here; the connections feed the business's records, they do not compete with them. Credentials are your administrator's, held safely: each connection is switched on and given its keys on the company record, the keys are stored encrypted and never displayed in full again, and a connection can be switched off without disturbing anything it already brought in. And each connection keeps its own rhythm — trading channels refresh through the day, ad platforms daily — with the freshness chip of 2.11 telling you, as always, exactly which data you are looking at.
Connecting Shopify the standard way — the BlossomERP app.
Install the BlossomERP app on your store from the Shopify App
Store and approve the access it asks for. Shopify sends you back
here: sign in, pick the company the store belongs to, and the
connection is made — there is nothing to copy and no keys to
type. You can also start the same flow from inside BlossomERP:
while no store is connected through the app, the Shopify
section of the company record's Integrations tab offers
Connect a Shopify store, and the page it takes you to asks
for the store's domain — the store's name alone is enough, and
the full yourstore.myshopify.com address works too.
BlossomERP then takes you to Shopify to approve the access,
and Shopify sends you back to pick the company, exactly as
above. If the domain cannot be used — or the store already
belongs to another company — the page says so by name before
anything leaves BlossomERP. That short flow is the whole setup, and it is the safe one:
the app's access is approved once, centrally — including the
customer details it needs (names and emails, so store orders can
belong to real customers here) — and the store's permission lives
with the app rather than in values pasted into a form. Only a
company admin of a company can link a store to it, and one
store links to exactly one company. The Shopify section of the
company record's Integrations tab shows the connection's
state at its top, and the page you land on after linking offers
Continue to company setup, which takes you straight to that
tab to finish the setup — the clearing bank account, the
default warehouse and the store location. If the app is ever uninstalled from the store,
the connection shows as disconnected there by name and
everything pauses cleanly; reinstalling the app restores the
same pairing.
That disconnected state is not guesswork: when the store connects, BlossomERP asks Shopify to notify it if the app is ever removed, and it is that notification which flips the connection the moment it happens. The page Shopify sends you back to after installing (the store-linking page) shows whether the notification is armed for the store. If it could not be set up, the page says so plainly and names Shopify's reason, and a company admin can retry it from the same page — until it is armed, removing the app would not be heard about, and the page will keep telling you so.
Releasing a store — unlinking. A company can also let a store go without touching the store itself. The same button that connected the store releases it: while a store is connected, the Shopify section's button on the company record's Integrations tab reads Disconnect Shopify store, in red, where Connect a Shopify store stood — press it, confirm in plain words, and the pairing is released. The store-linking page offers the same release as Unlink this store to a company admin of the company the store is linked to. Either way, unlinking releases the pairing — the company stops syncing with the store, BlossomERP keeps none of the store's credentials, and the BlossomERP app stays installed on the store. The store then waits to be linked again the usual way, by whichever company it belongs to next — which suits a store changing hands, a business reorganising its companies, or a test store being re-homed. Unlinking is not uninstalling: uninstalling removes the app from the store and shows here as disconnected, while an unlinked store simply waits, app in place, to be claimed.
While the store is connected, the working Shopify settings — the store location ID used for stock counts, the clearing bank account and the default storefront warehouse — are there in the same section to set and change. There are no credential fields to fill in: the app is the connection. And a company save never removes a setting it did not show you: what you saved before stays saved.
Receiving Shopify orders as documents. The connection above feeds your analytics. Separately, BlossomERP can receive each Shopify order as a real document in the business, because BlossomERP — not the store — is where the order is fulfilled, invoiced and collected. Switch it on in the Shopify section of the company record's Integrations tab: Shopify order intake — everything Shopify sits together there, with the connection it serves, and the switches appear while a store is connected. In the same section, set the Shopify clearing bank account — the bank account store payouts settle into, which prepayments are receipted against. Then, once an hour, BlossomERP receives the store's recent orders:
- The customer is matched to your register — first by their store identity, then by email — and used as they are. A shopper who is not yet a customer is created, with the details the store holds, so the order always belongs to someone real. Their address is recorded the way BlossomERP records every address: country and state as the standard codes (US, NY), never the store's display names, so the new record reads exactly like one you typed in yourself.
- The order lands as a draft sales order on the Shopify channel, priced exactly as sold. From there it is an ordinary BlossomERP order: approve it, deliver it, invoice it — the normal flow, including credit control. The invoice raised from it — directly or from its waybill — keeps the Shopify channel, so sales-by-channel reporting credits the sale to the channel that actually made it.
- An order the store has already been paid for is receipted automatically as a prepayment into the clearing bank account you chose, held as a customer deposit against the order until invoicing settles it. An unpaid order is received with nothing receipted. Nothing is ever received twice: pulling the same order again changes nothing.
When the store has already been paid, the order can approve itself. Approval normally means a person has agreed the sale should proceed — but for a prepaid store order, the customer's payment is that agreement. Switch on Shopify prepaid progression (the same Shopify section of the Integrations tab, beside Shopify order intake) and a paid or partially paid order lands approved instead of as a draft. The approval is the same one a person performs — totals recalculated, credit control run — and it is recorded in the order's activity log ("Auto-approved — prepaid via Shopify #7001"), so the order's timeline always shows why no person approved it. The prepayment receipt posts in the same pass; if it cannot post, it stays a draft with its reason on the exceptions worklist while the order itself still lands. Unpaid orders are untouched by the switch: they land as drafts awaiting a person, as before. An order already sitting as a draft progresses too — turn the switch on, and the next hourly pull approves the paid ones it finds. One step is never automated: fulfilment. A waybill is a physical act a person records, so progression stops at the approved order and the posted receipt; delivery, invoicing, and the deposit settling itself against the order's invoice when that invoice posts, all happen exactly as they do for any other order.
Working a backlog in bulk. What progression does not cover — the switch off, a backlog of drafts, receipts that waited on a fix — can be done in bulk from the lists. On Sales Order Management → Sales Orders, select orders and choose Approve selected; on Sales → Receipts, select receipts and choose Post selected, or Match deposits to invoices to settle each selected down payment's held deposit against the posted invoices raised from its own order (the matching an invoice's posting normally performs, done afterwards for receipts whose invoice already stood posted). Every selected document passes the same checks as opening it and doing it by hand — bulk never smuggles a refusal through. The result counts what succeeded and names every document that could not be done, with its reason, so a partial success is never a silent one. A deposit left over after its own order's invoices are settled is not chased onto other invoices in bulk; use that receipt's own Apply deposit action, where a person chooses the invoice.
When an order cannot be received — a product whose SKU does not exist here, a currency with no exchange rate, or a paid order while no clearing bank account is set — it does not vanish and it does not block the others. It waits in Sales Order Management → Shopify Intake Exceptions, with the reason named on the row. The reason is always in plain words: when the blocker is a technical failure rather than something you can fix, the row names its kind — a value from the store that is longer than the field here allows, a database error — and asks you to contact support with the order number; the full technical detail is kept in the system log for support, never on the row itself. Fix the cause — create the product with that SKU, add the rate, set the clearing account — and press Retry on the row; the order is received and the exception clears itself. And if an order that was already received is later cancelled or refunded on Shopify, an exception flags it for a person: BlossomERP never changes or reverses the documents on its own, because a store refund is a commercial decision, not a sync event.
Telling the store when an order ships. Delivery happens in BlossomERP — you pick, pack and post the waybill here. With Shopify fulfilment sync on (the same Shopify section of the Integrations tab), posting a waybill on a Shopify order also reports the fulfilment back to the store, in the background, in exactly the quantities that waybill shipped: a partial delivery fulfils the order partially, and the next waybill completes it. The store then shows the order fulfilled, and the customer gets Shopify's usual fulfilment notification, without anyone updating the store by hand.
The report can never hold a delivery up: the waybill posts first,
and the message to the store travels afterwards on its own. If
the store is unreachable or refuses, the delivery stands, and
the failure waits in Sales Order Management → Shopify Intake
Exceptions — the same worklist as intake — naming the order,
the waybill and the cause; fix the cause and press Retry to
send the fulfilment again. Two things to know: the app in the
Shopify Dev Dashboard needs two extra access scopes for this —
read_merchant_managed_fulfillment_orders and
write_merchant_managed_fulfillment_orders — added to the app
and released as a new app version, after which the connection
picks them up on its own. The waybill itself carries the
report's outcome: open it and its Shopify fulfilment block
names the linked shop, the fulfilment ID Shopify returned and
when the report was last sent — or says plainly that nothing
has been reported yet.
The delivery's tracking travels with the report. Capture the carrier and tracking number on the waybill — its Delivery & Tracking section while it is a draft, or the posted waybill's Delivery tracking action afterwards — and the fulfilment reported to the store carries them, so the store shows the tracking and the customer's fulfilment notification includes it. Tracking captured or corrected after the fulfilment was reported is sent after it: the store's fulfilment is updated with the new tracking, never re-created.
Your catalogue flows out from here, too. Products are mastered in BlossomERP and pushed to Shopify: each product's E-commerce tab (§6.13) holds its storefront profile — images, description, tags, price touches — and the channels it sells on. Once an hour, what changed is pushed to the store as one product write, and the store's product and variant IDs are recorded back on the tab, with any failure named there rather than lost.
You do not have to wait for the hour. On the product's E-commerce tab, in the Shopify block, Push to Shopify now sends this one product immediately — the same listing write and the same stock level the hourly pushes make — and tells you the result right there: pushed, with the store's product ID, or refused with the reason (no warehouse resolved, the channel off, Shopify itself declining) before anything is sent. The button appears only while your store is connected and the product's Shopify channel is on, and a product you pushed by hand is not pushed again by the next hourly run.
The same tab decides which warehouse a channel's stock levels come from: a per-product override on the tab, or the channel's default warehouse — set the Shopify default warehouse in the Shopify section of the company record's Integrations tab (WooCommerce has its own; Amazon's is the warehouse mapping in its section, because Amazon's stock sits with Amazon). Stock levels pushed to the store are the stock at that one resolved warehouse — they are no longer the sum of every warehouse you run. If no warehouse resolves for Shopify, the hourly push skips your company and says why, leaving the store's levels untouched rather than guessing.
Importing an existing Shopify store. Everything above assumes you connect a store and then build your catalogue in BlossomERP. If the store already exists — products, customers and stock already trading — the store import brings it across instead, the way the Migration Runner (§0.12) brings a whole business across from another accounting product. You will find Import this store into BlossomERP beside Continue to company setup on the store-linking page, and in the Shopify section of the company record's Integrations tab. Only a company admin of the linked company can run it.
The import works in three steps, and nothing lands before its step. First it pulls the whole store — products, customers, stock on hand — into a run you can inspect. Then it asks the one question only you can answer: which warehouse holds each store location's stock? A location can also be left unmapped — its products still come across, its stock does not, and the report says so. The same screen tells you, when the store trades in a different currency from your books, the exchange rate the opening stock will be valued at. Confirm the mapping and the landing runs:
- Products land with their storefront profiles and images (the images are downloaded into your own Documents, not hot-linked). A product with variants becomes a product family (§6.14): one BlossomERP product per variant, each keeping its own SKU from the store — a SKU is never invented. A variant the store carries without a SKU waits, named, while its siblings land. Each product is stamped with its store listing, so from then on BlossomERP adopts the listing: pushes update it, they never create a duplicate.
- Customers land as masters, matched by email: a customer you already have is recognised and stamped with their store identity, never duplicated. A store identity that conflicts with your records waits, named, for you to settle — nothing is overwritten on a guess. Store guests with no email address are not masters and are skipped, by name.
- Opening stock lands as a snapshot of the moment of the pull, valued at the store's cost-per-item (converted at the rate the mapping screen stated), posted as one journal batch per warehouse against opening balance equity — so the stock is worth something real from day one, never entered at zero. Pause selling on the store while the import runs: sales after the pull are not in the snapshot.
- Categories and their accounts. Every product type in the store lands in a product category of that name: one you already have is used as it stands, and where none stands the import creates the category — carrying no account defaults, because accounts are never invented — and the coverage report names each category it created. A family's variants take their accounts from the family's category, so when a product's stock waits for an account, set the defaults once on its category (Products → Product Categories; a variant's category is its family's) and press Re-run the landing: the product takes the accounts from there and its waiting stock posts in the same run. The category only ever fills a field that is still blank — an account a product already carries is never overwritten.
What cannot land waits by name, in a worklist on the import page, each item with its reason and its fix: a variant with no SKU; a product whose cost-per-item is not set in the store (its stock cannot be valued — the product itself still lands); a missing exchange rate; an image that would not download. One choice is yours to make on the mapping screen: a variant the store shows oversold (more sold than stocked) normally waits too — switch on Import oversold variants at zero only if you would rather it landed at zero, named in the report. Fix a cause and press Re-run the landing: everything that already landed is recognised and counted, never duplicated, and only what was waiting can newly land.
The coverage report closes the run, and it always names what the import does not carry, by design: past orders and sales history (open orders arrive afterwards through hourly order intake once you switch it on; past sales remain readable through Reported mode, below), discounts and gift cards, collections, and the store's metafields and SEO fields. Your next steps after a clean import: work any exceptions, switch on Shopify order intake and choose the store's sales mode, then run an inventory push and compare the store's levels against BlossomERP's — they should agree.
Keeping Shopify as your processor — the sales modes. The sections above describe BlossomERP running the store's documents. If you are happy with Shopify exactly as it is — Shopify keeps your inventory, delivery and payments, and you do not want BlossomERP to take the documents over — you do not have to choose between that and seeing your store's sales in BlossomERP. Every connected store has a sales mode, set in the Shopify Sales Mode section of the company record's Integrations tab, and exactly one mode is live at a time:
- Managed — the behaviour described above: orders are received as BlossomERP documents and progress here. With no election on record, this is the mode whenever Shopify order intake is on.
- Reported — Shopify stays your processor. BlossomERP reads the store's orders, refunds and Shopify Payments payouts in, and once a day, after the day has closed, posts one summary journal for the whole day: debit the Shopify clearing account with the day's gross sales, credit revenue with the net, and credit tax payable with the day's tax — taken from Shopify's own tax lines, never estimated. Refunds post on the day they happen, as contra-revenue in that day's journal; a past day is never rewritten. The figures are labelled as reported by Shopify everywhere they appear — they are the store's numbers, kept apart from your governed sales figures, never added into them.
- Off — neither flow runs. With no election on record, this is the mode whenever order intake is off.
A mode change is dated and prospective: pick the day it takes effect from, and it never reaches back over a day that has already filed. In Reported mode you also declare your tax posture — whether you collect sales tax and remit it yourself (the journal credits the tax to your tax payable account), or a marketplace facilitator such as Shopify remits it (the tax then never enters your books, and the journal carries net-of-tax amounts only) — and the clearing, revenue, tax payable and fee accounts the daily journal uses. The posture you declare is frozen onto each day's filing, and the journal states it.
Two disciplines keep Reported mode honest. A day that cannot post properly does not post: if the exchange rate for the store's currency is missing, an account is not configured, the tax posture is not declared, or the day's figures cannot be pulled from Shopify, the day waits by name on Sales Order Management → Shopify Intake Exceptions (kind Daily summary) with the reason on the row — fix the cause and press Retry. A day with no sales is recorded as checked and posts no journal at all. And when Shopify Payments pays you out, the payout clears itself: debit your bank, debit the payout fees, credit the clearing account. Money that settles through other gateways (a PayPal checkout, for example) never passes through Shopify Payments, so it is not auto-cleared — it reconciles through ordinary bank reconciliation, and the day's filing says so.
If you also keep your stock in BlossomERP while Shopify sells it, the day is not complete with the revenue journal alone: the goods sold have to leave your inventory, and their cost has to land in your books, or your stock and your gross profit quietly overstate themselves every day. Reported mode does this too, through a channel mirror warehouse. Flag a warehouse as a channel mirror (Warehouses), keep it stocked with ordinary stock transfers from a real warehouse, and choose it as the Channel mirror warehouse (COGS relief) in the same Shopify Sales Mode section. Each day's filing then carries a second, companion journal: the day's sold stock is relieved out of the mirror at its moving-average cost — debit Cost of Goods Sold, credit Inventory — and a refund whose goods came back to the shelf puts them back into the mirror at the cost they originally left at. (A refund Shopify did not restock returns nothing.) The relief never touches a real warehouse: Shopify fulfilled the sale, so the mirror is what depletes. If a relief cannot be done honestly — no mirror chosen, the mirror short of what was sold, stock sitting in the mirror at no cost, a sold item with no matching product — the day still files its revenue, and the relief waits by name on the same Intake Exceptions worklist, with Retry once you have fixed the cause. Choosing the mirror later still completes the days that waited.
Each day's journal carries the day's store-currency amounts on its lines beside the functional amounts posted, at the rate stated on the filing, and the day's own Shopify summary — every order and refund, with the tax split — is attached to the journal as its evidence, in Documents. You can read the reported figures themselves in Explore and Ask under the External Sales (as reported by Shopify) view, kept deliberately separate from the Sales view: a store's figures live in exactly one of the two, by its mode.
Reported revenue is by customer, too. Every order BlossomERP reads in is resolved to one of your customers — the same resolution managed orders use: an existing customer matched by their store identity or email, or a new customer created on the spot — so revenue by customer reads in reported mode exactly as it does for your BlossomERP documents, refunds carrying the customer their order resolved to. An order a guest placed (Shopify lets a buyer check out with no account and no email) is not a gap in the attribution: it belongs to a standing customer called Shopify Anonymous, one per store, which you will see in your customer list and as its own line in revenue by customer. Orders read in before this attribution existed keep no customer and read as unattributed — named as such, never quietly assigned to somebody. And when you want the whole channel picture at once, the consolidated channel reading presents your managed channels (BlossomERP documents, governed) and your reported channels (as reported by Shopify, refunds shown against them) side by side, each broken down by channel and by customer, with one total that is exactly the two added together — the two are shown together, never merged: each store's figures still live in exactly one of the two, by its mode.
WooCommerce works the same way. A WordPress/WooCommerce store connects on the Company form (Integrations → WooCommerce) with its store URL and a Read/Write API key, and from there everything you have just read about Shopify has its WooCommerce counterpart: the same E-commerce tab on each product (with a Push to WooCommerce now button), catalogue and stock levels pushed out of BlossomERP (a product family pushes as one variable product with a variation per variant), and the same choice of sales modes in the WooCommerce Sales Mode section below the connection. In Managed mode, store orders are received as BlossomERP sales orders — with one difference in how the money reads, because WooCommerce has no separate "paid" marker: an order's status is its payment state. An order the store shows as processing or completed counts as paid (its money is receipted as a prepayment into the WooCommerce clearing bank account you set in the same section, and prepaid progression applies if you switch it on); pending counts as unpaid and lands as a plain draft; on-hold is genuinely ambiguous — WooCommerce uses it both for orders awaiting payment and for orders a person parked by hand — so an on-hold order is received as a draft and deliberately not progressed or receipted until the store settles it one way or the other, with a note explaining exactly that waiting on the Intake Exceptions worklist; an order cancelled, failed or refunded before it was ever received is not taken in at all, and the hourly pull names it in its output rather than skipping it quietly. Guest checkouts resolve just as they do for Shopify: by the buyer's email when the order carries one, and onto a standing Woocommerce Anonymous customer when it does not. A posted waybill on a WooCommerce order reports back when fulfilment sync is on: WooCommerce keeps no fulfilment record of its own, so the order is marked completed and the waybill, carrier and tracking travel as an order note — the store-side evidence of the delivery. In Reported mode the same daily duty files one summary journal per day (figures labelled as reported by WooCommerce), and the same channel mirror warehouse relieves the day's sold stock — with two honest differences from Shopify. First, payouts do not auto-clear: WooCommerce itself never pays you — your store's payment gateway settles outside it — so the clearing account bridges through ordinary bank reconciliation. Second, a refunded sale's stock is not put back automatically: WooCommerce does not record whether refunded goods were restocked, so BlossomERP will not guess — the refund's money reverses on its own day, and the mirror keeps the difference for you to correct by a stock count or transfer when the goods really are back on the shelf.
Amazon is reported-only. An Amazon marketplace account connects on the Company form (Integrations → Amazon Integration), but Amazon works differently from the two stores above, and BlossomERP is plain about the differences. First, the connection itself takes a short chain on Amazon's side, done once by whoever administers your Seller Central account: register as a developer in Seller Central, create a private SP-API app for your own account, and self-authorize it — Amazon then gives you an LWA client ID, a client secret and a refresh token, and those three go into the Amazon Integration section. (There is no public BlossomERP app to install; your app is yours.) Second, Amazon orders never become BlossomERP documents: there is no Managed mode for Amazon, and the Amazon Sales Mode section offers just Reported and Off. In Reported mode the same daily duty files one summary journal per day, figures labelled as reported by Amazon. Third, Amazon does not release buyer names or emails to apps without a special restricted-data grant, so reported Amazon sales belong to a standing customer called Amazon Anonymous — revenue by customer still adds up exactly; the names are simply Amazon's to withhold, not BlossomERP's to invent. Fourth, the stock relief follows how each line was fulfilled, which you set per product on its E-commerce tab: a line Fulfilled by Amazon (FBA) relieves out of the FBA mirror warehouse you elect in the Amazon Sales Mode section — a warehouse standing in BlossomERP for the stock physically sitting at Amazon, which you keep stocked with transfers; a merchant-fulfilled line relieves out of the product's own warehouse (its E-commerce tab override, or the Amazon default warehouse in the connection section). When a buyer returns an FBA item and Amazon puts it back on the saleable shelf, the return reinstates into the mirror at the cost the original sale relieved at. BlossomERP also compares the mirror against Amazon's own fulfillable quantities and names any difference — stock Amazon fulfilled outside BlossomERP's paperwork, a transfer not yet posted, a return Amazon re-shelved — and never adjusts the mirror on its own; the correction is yours to make, by transfer or count, once you know which side is right. Fifth, the money arrives as settlements, not daily payouts: Amazon keeps its fees and pays you the net roughly every fourteen days, so each settlement posts its own journal — the net into the settlement bank account you set in the Amazon Sales Mode section, the fees itemised onto the fee accounts (referral fees and FBA fulfilment/storage fees have separate accounts; leave the FBA one unset and FBA fees land on the referral account), and the clearing account credited with the gross. That means fees appear in your books when the settlement lands, not day by day — within a settlement period the daily figures carry revenue only. If a settlement's gross does not match what the period's daily summaries put through clearing — Amazon held or released a reserve, or applied an adjustment — the difference is named for you on the Intake Exceptions worklist, never quietly plugged. And as with the other channels, a day, a relief or a settlement that cannot post honestly waits there by name — no mirror elected, stock the warehouse does not hold, a fee with no account — with a Retry once you have fixed what it names. One more expectation to set plainly: the tax posture. In most US states Amazon is the marketplace facilitator and collects and remits the sales tax itself, so the form presents that posture as the expected case — the tax then never enters your books, and the daily journal carries net-of-tax amounts only. If you sell somewhere you remit the tax yourself, declare collects-and-remits instead, and the journal credits the tax from Amazon's own figures to your tax payable account.