Skip to content
User Manual · Chapter 0 — Your company: setup, preferences, and room to grow

0.12 Moving from another product — the Migration Runner

Most companies do not start empty. They arrive with years of customers, products, invoices and balances inside another product — QuickBooks, Xero, Zoho Books, ERPNext, or a cupboard of spreadsheets. The Migration Runner (System Setup → Data Migration) exists for that move. Where the import wizard (Chapter 16) takes one tidy spreadsheet at a time, the runner takes the whole export folder at once — masters, transactions, everything — and works through it in the right order, unattended. There is no file-size ceiling: a folder with a hundred thousand invoice lines is a normal run. (An existing Shopify store has its own door of the same kind — the store import of §2.14, which pulls the store itself across once it is linked.)

What you feed it. Export from the old product first, using whatever that product offers, and put the files in one folder (or keep the single ZIP some products give you). The runner recognises what it is looking at by the contents of the files, not their names — with one exception: when two kinds of list are indistinguishable by content alone (a simple code-and-name list could be units of measure, product categories, or several other lists), the file's name settles which one it is, so give such files their natural names (product_categories.csv, uoms.csv) when you can:

  • QuickBooks Desktop — IIF exports and list/report CSVs. The company file itself (.qbb, .qbw) cannot be read — restore it in QuickBooks, export, and migrate the exports. Attachments live in a separate Attach folder; the runner counts them but does not import them.
  • QuickBooks Online — the Settings → Export data ZIP: report workbooks and list exports. The plain CSV exports work too — the Customer and Vendor Contact Lists and the Trial Balance report; those files open with the report's own headings above the columns, and the runner finds the real column row underneath. QuickBooks Online has no invoices export, so invoice history cannot be reconstructed; your balances arrive through the trial balance instead, and the coverage report says exactly that. The Journal and General Ledger reports are recognised and named, but not imported as documents. Include the Account List export (from the Chart of Accounts page) when you can: that is what brings your chart of accounts across, and the trial balance's own accounts need it — without it, accounts that exist only in your QuickBooks have nowhere to land.
  • Xero — the per-type CSV exports (contacts, chart of accounts, items, invoices, bills, trial balance). Invoices and bills arrive as posted documents (see below).
  • Zoho Books — the Data Backup, one ZIP of per-module CSVs. Invoices, bills and customer payments arrive as posted documents; the single Contacts file is split into customers and vendors by each contact's type; credit notes, quotes and purchase orders are named in the coverage report but are not imported as documents. Note: Zoho's Documents and Audit Trail backups are separate, incremental downloads — a folder built from those may hold only a slice.
  • ERPNext — per-DocType CSVs from its Data Export tool, or the Data Import templates for Sales Invoice and Purchase Invoice — both shapes are read. A bench backup (.sql.gz) cannot be read — restore it into ERPNext and use Data Export.
  • Sage 50, Wave, FreshBooks — recognised and explained, not yet supported: the runner quarantines those files with the reason and the workaround rather than guessing.
  • Anything in the shape of BlossomERP's own import templates (the Generic profile) works too.

The coverage report comes first. Before anything is imported, the run shows you what the folder holds against what a complete migration from that product should hold: what was found, what is missing, what could not be read and why, and the warnings that matter — for example that one product's inventory costing (average cost) will not match this one's exactly, or that some documents in the export (credit notes, journals) are carried by the opening balances rather than imported as documents, because BlossomERP does not import those as documents yet. Nothing in the folder passes unnoticed, and nothing is silently dropped.

Then the runner evaluates — before anything is imported. Under the coverage report, the run page shows an evaluation of every file: what it becomes in BlossomERP, how many rows it holds, and whether it is safe to run. For each file it checks the references the rows rely on — the customer on an order, the product on a line, the category on a product — and tells you how many of those codes are in the folder itself, how many already exist in your company, and how many are nowhere, naming examples of the missing ones. It also names the things the file assumes your company already has (a unit of measure such as EA, a warehouse), and counts any blank required values. Each file gets a verdict — ready, warning, or blocked, with the reason written out. While any file is blocked, Execute will refuse to start and tell you why; if you understand the blockers and want to proceed anyway, use Proceed despite blockers and tick the acknowledgement — it is recorded on the run with your name and the time, and the blocked rows will simply fail by name, ready for correction. You can re-run the evaluation at any time with Re-evaluate — on a big folder the rebuild takes a while, so it runs in the background: the page tells you it is queued or running, keeps showing the previous assessment until the new one lands, and refreshes itself when it does. Saving a changed mapping queues the same rebuild. If a rebuild ever fails, the reason is written on the page, not lost.

Wrong product? Change the source. If the runner identified the folder as the wrong product — it can happen when two products' exports look alike — open the run and use Change source: pick the right product (or Detect automatically) and the folder is re-scanned under it, with a fresh coverage report and evaluation. Your files, mode and cut-off date are kept. Change source is available only while nothing has been imported yet; once the first row has committed, the source is fixed, and migrating under a different product means starting a new run.

The evaluation also states plainly what cleaning the runner will do, and what it will not do. It tidies what is present: numbers with thousand separators are read as the number they show, codes are matched without worrying about capital letters or stray spaces, and dates are read in the usual formats (if a date could be read two ways — 05/06 as 5 June or May 6 — it is flagged and read the standard way, never swapped). It will never invent what is missing: a blank stays blank and fails by name, a date it cannot read stays exactly as written and fails by name, and no record is created just to satisfy a reference. Nothing is guessed.

Two modes, one cut-off date. You choose the day the old system stops and this one starts.

  • Opening balances — the lists (chart of accounts, customers, suppliers, products), an opening journal posted from the old system's trial balance at the cut-off, and open AR/AP items only.
  • Opening balances + history — the same, plus the actual documents for the 24 months before the cut-off; anything older is folded into the opening balances.

Invoices and receipts come across as real documents. Sales invoices in the folder import as posted invoices, and customer receipts import as posted receipts, each one settling the invoice it names. (The same two imports exist in the Chapter 16 wizard, for one file at a time.) A few rules keep them honest:

  • Every receipt names the bank or cash account the money landed in — by its chart account code, or by the code or name of a bank you have set up under Banking (the money then lands in that bank's own ledger account, which is where the old product's bank names point). If the name or code does not exist in either place, the evaluation blocks the file by name — money is never posted to a guessed account.
  • A payment method the system does not recognise (a local wallet name, say) is left blank on the receipt and named in the evaluation as a warning. No method is invented.
  • If a receipt pays more than its invoice still owes — it happens, when settlements are matched late — the receipt is capped at what was owed and the extra stays on that receipt as an unallocated credit on the customer's account, visible on the receipt's journal. The evaluation counts these in advance, and the run page names the adjustment on the receipt's row.
  • A receipt that names no invoice at all — money taken on account, a payment the old system never matched to an invoice — imports as an on-account receipt: posted, with the whole amount sitting unallocated on the customer's account, ready for you to apply to invoices later. The run page names it on the receipt's row. Naming an invoice that does not exist is a different thing — that still fails by name.
  • Foreign-currency invoices and receipts take their exchange rate from the source row when the export carries one, otherwise from the rate table for the document's date. If neither exists, the evaluation blocks by name — a rate is never invented.
  • In a history run, an invoice older than the window is skipped by name, and its receipts are skipped with it — a settlement never lands against an invoice that is not there.
  • Posted documents need an open accounting period for their date, and the runner creates the periods the run needs. The evaluation works out the months your documents span and lists, by name, the periods it will create — monthly periods in your company's usual pattern, created open before anything posts, so each document lands in the period of its own date. Periods that already stand open are used as they are. One exception is a hard stop: if a needed period already exists but is closed, the evaluation blocks by name — the runner never reopens a closed period. Reopen it yourself in setup and re-evaluate, or proceed despite blockers and the documents dated there will fail by name. And if a run holds no posting documents at all — masters and named files only — the evaluation says so plainly: no posting documents, so no periods are needed.

The opening journal is checked in advance, too. If the folder carries a trial balance, the evaluation resolves every account it names against your company's chart of accounts plus the accounts the folder's own chart-of-accounts file will create. Any name that resolves nowhere blocks the trial balance file, listed by name, with the same words the run would once have failed with halfway through: import the chart of accounts first (or correct the references), then re-evaluate. Nothing is imported first and refused later.

Bills, supplier payments and opening stock come across too. Supplier bills import as posted purchase invoices, and payments to suppliers import as posted payments, each one settling the bill it names — the exact mirror of invoices and receipts, with the same honesty rules (the bank account the money left from is named by chart code or by a Banking bank's code or name, and blocks by name if it exists in neither place; an over-payment is capped at what the bill still owes and the extra stays as an unallocated amount on the supplier's account, named on the row; a supplier payment that names no bill at all imports on account, exactly like an unmatched receipt; a bill older than the history window is skipped by name and takes its payments down with it). Imported bills are matched against goods receipts not at all — they post on their own; if your company uses GRNI (goods received, not invoiced), the evaluation warns you of exactly that before anything runs. Opening stock imports as its own list — product, warehouse, quantity with its unit of measure, and the unit cost from the old system. That declared cost is the value your stock opens at: if a row carries no cost, the evaluation blocks it by name — the system never substitutes a standard cost or a zero — and a negative quantity fails by name, because opening stock cannot be negative. Lot numbers and expiry dates are carried where the old system has them; if your company's Advanced Inventory switch is off, the evaluation warns that the lots will be recorded but lot tracking itself stays off until the switch is on.

Customers and suppliers arrive able to post. Every customer needs a receivables control account and every supplier a payables one before their invoices and bills can post — but most products never export that setting. When the source names none, the runner gives the customer or supplier your company's control account (their category's default first, where the category carries one), and the run page notes the choice on their row. If your chart has no control account for that side at all, the row says so instead — their documents will then refuse to post until you set the account on the customer or supplier yourself.

Products arrive able to post, too. A product needs its ledger accounts — inventory, revenue, and cost of goods sold — before its invoices, bills and goods receipts can post, and most products never export them. When the source names none, the runner applies the product category's default accounts (the same assist the product screen gives when you pick a category), and the run page notes the choice on the product's row. A product whose accounts can be found in none of those places fails by name in the evaluation, with the missing account spelled out — it is never imported half-configured to fail quietly later, when its first invoice is posted. Set the account on the row, on the product, or as the category's default, and re-run.

Categories can carry their defaults with them. The product categories list itself (in a folder, or through the Chapter 16 import template) can name each category's defaults — the default unit, the default inventory, cost-of-goods, revenue and expense accounts, the default purchase and sales tax codes, and whether the category is vatable — so the mapping an implementer would otherwise type into every category by hand after the run can be done once, in the file. The accounts named must exist and must be of the right kind (an inventory default must be an asset account, and so on); a wrong one fails by name in the evaluation, naming the default that is wrong. A categories list that names no defaults behaves exactly as before, and re-running a list never removes a default the list does not mention — updating a category changes only what the file actually names.

Opening balances keep the subledger honest. There is one law here, and it matters: a control account and its subledger must agree. If your folder carries the open invoices, they are imported as posted opening invoices — a part-paid invoice opens with exactly what is still owed, because its pre-cut-off receipts come across too — and the opening journal then leaves the receivables control account out, so the balance is carried by the open items themselves, never counted twice. The close-out checks the open invoices' total against the receivables figure from the old system by name. The same law holds on the other side of the books: open supplier bills and their pre-cut-off payments come across as posted opening documents, the journal leaves the payables control account out, and the close-out checks the open bills' total against the payables figure by name. And it holds for stock: your opening stock list carries the Inventory balance at its declared quantities and costs, the journal leaves Inventory out, and the close-out checks the stock valuation against the Inventory figure by name. Opening documents are marked as such, and they cannot be reversed or re-posted like ordinary documents — their figures belong to the migration. If you would rather not import open items at all, tick Accounts-only run when you create the run: the declaration is recorded, no open items are imported, the control balances stand in the opening journal alone, and the close-out says so against each control account. One more naming rule: in a full run, if a control account's balance arrives with no open items behind it and nothing in the company pointing at it as that subledger's control account — the usual shape when the chart came across from the old product but its open items did not — the close-out names the account and the amount, so the balance never stands in the journal silently without its detail.

Mappings are confirmed once, then remembered. For files in the plain template shape, the run page shows which source column feeds each field. Adjust it if the proposal is wrong; the mapping is saved for your company and offered again on the next migration from the same product.

Rows can fail — singly. A row with a bad email or an unknown code fails on its own, with the reason written on it; its neighbours import regardless. Download the failed rows, fix the values in the spreadsheet, and re-upload — only those rows run again. One special case needs no spreadsheet at all: if rows failed only because something they reference had not been imported yet (order lines that arrived before their orders, say), import or create the missing records and choose Re-validate blocked rows — those rows are checked again as they stand, and import if they now pass. A run can be paused and resumed; it never imports the same record twice.

You can watch a run work. The run page refreshes itself while a run is executing. At the top, an overall progress bar shows the percentage of staged rows that have settled — committed, skipped or failed — with the percentage printed beside it. Below, the files table gives one row per file: what it becomes, its status, how many rows are staged, committed, skipped and in error, and that file's own percentage — and the counts always add up: every staged row ends committed, skipped or in error, never nowhere. A few rows are structure, not data — a chart file's account-class rows, for instance: account classes are shared reference data, the same for every company, so those rows are listed and then marked skipped, with the reason written on the row. A run whose rows have not all reached one of those end states cannot be marked complete — the refusal names the file and how many rows are still unsettled; the file the runner is working on right now is marked ◀ processing. The page also shows when the run's chain last did something ("Last chain activity"). If a run should be moving but nothing has happened for a while — most often because the background worker is not running — the page says so in plain words: "No worker is processing this run", with what to do about it. A run never sits silent and unexplained: if it fails, the failure reason is written at the top of the page. If one of the run's source files cannot be opened when its turn comes — it has gone missing from the server's storage — the run stops at once and names that file there; it does not sit at "running" with nothing happening.

The close-out proves the books. When the run finishes, it compares your trial balance at the cut-off, account by account, against the trial balance from the old system. If they differ, the differences are listed on the run page — and the run cannot be marked complete until either they are resolved, or you acknowledge them there (the acknowledgement is recorded with your name and the time). The same is true when a control account — receivables, payables or inventory — carries a balance in the old system but no open items were imported for it: the close-out names the account and the amount, and the Acknowledge close-out button in the page header covers that case too. If the folder had no trial balance, you can declare the balances account by account instead; the close-out then says plainly that it is checking your declared figures against what was posted, not against the old system. And if there was neither a trial balance in the folder nor any balances declared, the close-out says exactly that — there is nothing to verify this run against — and claims no match it never checked.

Your history lands in Analytics, in full, by itself. When you mark a run complete, Blossom automatically loads everything the run brought in — the whole migrated history, however far back it reaches — into Analytics for your company. Nothing is left for you to run by hand, and there is no waiting for the regular Analytics updates to catch up: those only ever bring in the recent past, so without this step an imported history would quietly never appear. The evaluation you read before executing says this will happen, and after completion the run page shows the refresh itself — queued, running, then done. If it cannot run on your deployment, the run page says so and says why, instead of leaving you to discover a half-empty Analytics and wonder what the migration lost. This is worth knowing if you are on a trial: migrate with your history, and the dashboards, cockpits and Explore open on your real past, not on an empty company.

Only company admins can run migrations — the permission is called run data-migrations — because a migration writes the company's opening position. Everyone else simply does not see the screens.

See also: 16.3 System Setup — the directory · the import wizard's single-file imports in Chapter 16.


Snapshot: portal-v0-2026-11-10