A system can go live on schedule and still leave the finance team invoicing in a spreadsheet the following Monday. When that happens, the cause is usually upstream of the project: nobody walked through one complete, real transaction before the estimate was signed.
This post is a practical template for doing that walkthrough and turning it into something an estimate can rely on. It applies to ERP, CRM, Salesforce, Odoo or any business system that touches orders, invoices and payments.
Pick a messy transaction, not a clean one
Choose a recent sale that had at least one exception: a partial delivery, an off-list discount, a credit note, a split payment. Clean transactions confirm the demo. Messy ones show the scope.
Record every step in the same shape
Walk the transaction with the people who actually did it. For each step, capture the same fields. A plain YAML file works fine and is easy to diff later:
transaction: "Recent sale with partial delivery and a credit note"
steps:
- step: order
done_by: sales rep
recorded_in: customer email + spreadsheet
exceptions: [special price]
approved_by: sales manager
outside_system: [approval via chat message]
- step: credit_note
done_by: accounting
recorded_in: accounting system
exceptions: [partial return]
approved_by: finance
outside_system: [email thread]
- step: payment
done_by: treasury
recorded_in: bank + spreadsheet
exceptions: [paid in installments]
approved_by: null
outside_system: [reconciliation spreadsheet]
The values above are a format example, not a real case. The interesting fields are approved_by and outside_system. That is where unestimated work hides.
Classify every gap before anyone estimates hours
Each thing that doesn't map cleanly to the target system goes into one of four buckets:
| Bucket | Meaning | How to estimate |
|---|---|---|
| standard | The product does it out of the box | Near zero |
| configuration | Fields, picklists, validation rules, declarative automation, permissions | Hours, low risk |
| development | Custom code, UI components, integrations | Separate estimate, real risk |
| business decision | Nobody has defined the rule | Not hours. A meeting with someone who has authority |
The last bucket is the one that blows up projects. "Who approves a credit note above a certain amount?" is not a configuration task. If it gets mixed into the configuration estimate, it comes back later as a change request, a moved date, or a spreadsheet that survives go-live.
A quick way to make this visible is to count gaps per bucket per step:
from collections import Counter
gaps = [
("order", "business_decision", "no backup approver for off-list discounts"),
("order", "configuration", "discount approval threshold"),
("credit_note", "business_decision", "no written limit for credit notes"),
("payment", "development", "bank file import"),
]
by_bucket = Counter(bucket for _, bucket, _ in gaps)
print(by_bucket)
# Any business_decision > 0 means the estimate is not ready to sign.
Same caveat: the list is illustrative. The rule at the end is the point.
Treat every spreadsheet as a requirement
For each spreadsheet in outside_system, write down what it calculates, where its inputs come from and who makes decisions with it. Some become formula fields or reports. Others hide a business rule nobody wrote down. Either way, they belong in scope, not in a "phase two" nobody funds.
Who sits in the room
The people who do the work every day, plus someone with authority to settle disagreements between departments. Without the second one, business-decision gaps stay open and the consultants end up guessing.
Use the same trace after go-live
A few weeks after launch, rerun the walkthrough with the same transaction type. If outside_system is still full, the project is not done, whatever the timeline says. Going live is a date. Closing the month without people staying late to reconcile two sets of numbers is the result.
I wrote about the business side of this, in Spanish, on our blog: why an ERP implementation can go live on time and still fail.
Juan Manuel Garrido, founder of EGA Futura
Top comments (0)