DEV Community

Mohit
Mohit

Posted on

An Hour-by-Hour ERP Cutover Playbook for Canadian Distributors

Cutover is the only part of an ERP project with no room to renegotiate. Configuration slips by a week and nobody outside the project notices. Cutover slips by four hours and a distribution business cannot ship on Monday.

This is the plan I would hand a Canadian distributor moving from a legacy system to Odoo over a single weekend — roughly 60 hours, Friday evening to Monday morning. It assumes a warehouse that does not run Saturdays, one legal entity, and a production instance already provisioned. Adjust the arithmetic if any of that is different for you.

Rehearse it twice, on real data

The single strongest predictor of a clean cutover is how many times you have already done it.

Two full rehearsals on a copy of production data, timed with a clock, is the minimum. Not "we tested the import" — the entire sequence, in order, with the same people running the same commands. The first rehearsal finds broken steps. The second finds the real durations, which will be longer than your estimates.

On Odoo.sh this is what staging branches are for. Staging builds get a neutralized copy of the production database, and per the branch documentation they are explicitly not treated as persistent storage — outgoing mail and scheduled actions are disabled so a rehearsal cannot email your customers. If you are self-hosting, do the same thing manually before anything else runs:

UPDATE ir_cron SET active = false;
UPDATE ir_mail_server SET active = false;
UPDATE fetchmail_server SET active = false;
UPDATE base_automation SET active = false;
Enter fullscreen mode Exit fullscreen mode

Forgetting that last one is how a rehearsal fires 4,000 order-confirmation emails from a test database. I have watched it happen.

Agree the rollback trigger before Friday

Write down, in advance, the condition under which you abandon the cutover and reopen the legacy system on Monday. Then write down the latest clock time at which that decision can still be executed.

For a weekend cutover the decision point is usually Sunday around midday. Past that, there is not enough runway to restore the legacy system, re-key the weekend's work and open on time. So the trigger is a time, not a feeling: if opening inventory is not reconciled and opening balances are not posted by 12:00 Sunday, we roll back.

The reason to write it down is that nobody makes this call well at 2am on Sunday with sunk cost in the room. Pre-committing removes the argument.

The freeze schedule

Freezes are the part clients push back on hardest, and the part that most reliably determines whether the weekend works.

Data Freeze from Why
Master data (products, partners, UoM, pricelists) Wednesday 17:00 Extracts have to be stable while you clean and load them
Purchase orders Thursday 17:00 Open POs become the migrated backlog; a PO raised Friday afternoon exists in neither system
Sales orders Friday 12:00 Anything after this is keyed into Odoo on Monday
Goods receipts / shipments Friday 15:00 Warehouse needs clear time before the physical count
Legacy system, all write access Friday 18:00 This is the extract point

Two days of master-data freeze sounds excessive until you have watched a vendor record created on Thursday afternoon fail to appear in Monday's system, and the AP clerk conclude the migration lost data.

Give the sales team a written procedure for the frozen window — where to record orders taken Friday afternoon, and who keys them in Monday. A shared spreadsheet with a named owner is fine. Nothing is not fine.

Friday 18:00 to Monday 06:00

Window Step Owner
Fri 18:00–18:30 Legacy write access revoked. Final full backup taken and checksummed. DBA
Fri 18:30–20:00 Final extracts pulled: master data deltas since Wednesday, open SOs, open POs, AR/AP aging, trial balance, on-hand stock by location. Data lead
Fri 20:00–23:00 Physical count of high-value and fast-moving SKUs. Count sheets on paper, entered later. Warehouse
Sat 08:00–11:00 Master data delta load. Row counts reconciled against extract. Data lead
Sat 11:00–14:00 Opening inventory loaded as stock.quant records. Reconciled to count sheets, not to the legacy report. Data lead + Warehouse
Sat 14:00–18:00 Open SO and PO backlog loaded. Spot-check 20 documents against legacy printouts by hand. Ops lead
Sat 18:00–21:00 Opening balances posted: trial balance as a journal entry, AR and AP as open items per invoice. Finance
Sun 08:00–11:00 ir.sequence values set. Tax and fiscal positions verified against real invoices. Warehouse routes and operation types checked. Finance + Ops
Sun 11:00–12:00 Reconciliation review. Go / rollback decision. Steering group
Sun 12:00–15:00 Crons, mail servers and automation rules re-enabled. Integrations repointed. Smoke tests. Tech lead
Sun 15:00–18:00 Three end-to-end business transactions: quote to invoice, PO to receipt to bill, pick to pack to ship. Ops + Finance
Sun 18:00–20:00 User access enabled. Comms sent. Floor-walker roster confirmed. Project manager
Mon 06:00 First shift. Two floor-walkers per shift for the first week. Everyone

The gaps in that table are deliberate. Overnight Saturday is not scheduled, because a team that has been awake for 30 hours makes the mistakes that turn a cutover into an incident.

Sequence numbers are the step everyone forgets

ir.sequence controls invoice, order and picking numbering. If you leave it at the Odoo default, your first live customer invoice is INV/2026/00001 while your legacy system last issued 48,217.

Your accountant will not enjoy that, your customers will query it against their POs, and fixing it retroactively means renumbering posted journal entries — which you should not do.

Set the prefix and next number to continue the legacy series, on Sunday morning, and verify by creating and deleting one draft document of each type. Do the same for pickings, receipts and purchase orders. It takes twenty minutes and prevents a month of reconciliation questions.

Reconcile with counts, not with confidence

Every load gets a row count comparison, extract against database, before anyone declares it done:

SELECT 'partners', count(*) FROM res_partner WHERE active
UNION ALL SELECT 'products', count(*) FROM product_template WHERE active
UNION ALL SELECT 'open_moves', count(*) FROM account_move WHERE state = 'posted' AND payment_state != 'paid';
Enter fullscreen mode Exit fullscreen mode

Then three value checks that have to match the legacy system to the cent: AR total, AP total, and inventory valuation. If inventory valuation is off, find out whether it is a quantity problem or a costing-method problem before you touch anything — landed cost and average-cost differences between systems are common and are not data errors.

Getting the opening-balance and valuation treatment right is the part of a migration where experience actually shows, and where an implementation partner earns its fee; it is a substantial share of what Master Software Solutions spends its cutover weekends doing. The mechanics of extracting and reshaping legacy data are covered in more depth on their Odoo migration pages.

Do not decommission the legacy system

Keep it running, read-only, for at least a year.

Beyond the obvious reason — someone will need a 2023 invoice — there is a Canadian compliance one. The CRA requires business records to be kept for six years from the end of the last tax year they relate to, with some categories held indefinitely. If you migrated two years of transactional history and the legacy system holds seven, switching it off does not satisfy that. Either migrate the full history, or keep the old system available in a documented read-only state.

Backups: know what you actually have

Take your own backup before cutover. Do not assume the platform's schedule covers the moment you care about.

Odoo.sh keeps 14 full backups of each production instance for up to three months — daily for a week, weekly for a month, then monthly — and does not back up staging or development builds at all. That is a reasonable policy and it is not a cutover safety net. You want a manual dump immediately before the first live transaction, stored somewhere you control, with a restore you have actually tested.

Restoring is the half nobody rehearses. A backup you have never restored is a file, not a recovery plan.

Week one is part of cutover

The weekend gets the plan. Week one gets the problems.

Budget floor-walkers, not a help desk — someone physically standing where the work happens, because a picker will not raise a ticket, they will improvise a workaround and teach it to the next shift. Expect a productivity dip through week two and staff for it rather than being surprised by it. Keep a triage log with a daily 15-minute standup, and hold a single named person accountable for closing configuration issues within 48 hours. Whether that capacity comes from your own team or an external support and maintenance arrangement matters less than it being explicitly resourced before Monday, which is where most plans quietly leave a gap.

The honest limitation of a weekend cutover

It only works when the business genuinely stops. A distributor that ships six days a week, or runs a customer-facing e-commerce channel that cannot pause, does not have a real freeze window — and forcing one produces a backlog that takes three weeks to clear.

In that situation a phased cutover is better, even though it is more expensive and more complex: you run two systems in parallel for a defined period, with a written rule for which is authoritative for each transaction type. Nobody enjoys it. It beats a weekend plan that assumes a stoppage the business cannot deliver, and choosing between the two shapes is a conversation worth having with an ERP consulting team before the date is set rather than after.

The one artefact that matters

Build the runbook as a shared document with a checkbox and a timestamp field per step, and make one person responsible for ticking them in real time.

Not because the ticks matter. Because at 3pm Sunday, when someone asks whether opening balances were posted before or after the inventory load, the answer has to be a fact rather than a recollection.

Top comments (0)