In this article
- Start with the commercial boundary
- Worked example: the approval request that was not a requirement
- Step 1: Identify the Odoo environment and delivery method
- Step 2: Write the workflow screen by screen and role by role
- Worked example: fiscal invoicing across two entities
- Step 3: Specify integrations as contracts, not aspirations
- Step 4: Scope country and tax localisation by legal entity
- Step 5: Define acceptance tests before development starts
- Step 6: Price change properly and protect the go-live date
- Frequently Asked Questions
- Can an Odoo developer give a fixed quote from a feature list?
- Should we use Odoo Studio or a Python custom module?
- What must an invoice specification include for Zambia, Zimbabwe, or Kenya?
- Who should approve the Odoo specification internally?
A finance director asks for an Odoo change, the developer replies with a range, and the range is wide enough to be useless for budget approval. Usually, the missing item is not technical skill. It is a bounded specification that says exactly what the business needs, where the work stops, and how acceptance will be measured.
To brief Odoo developer teams for a fixed quote, turn the request into a testable scope before anyone estimates hours. Odoo customisation and custom module development should begin with the process, the users, the Odoo environment, and the local compliance obligations. A fixed quote is credible only when those decisions are visible to both parties.
At Serpa, we treat this as an ERP governance exercise as much as a development exercise. The specification gives your CFO a budget boundary, your operations team an auditable operating model, and the developer a basis for delivery.
Start with the commercial boundary
A fixed quote depends on a bounded specification. A feature list such as “add approval workflows, integrate the bank, and make invoices compliant” is not enough. It leaves the developer to make assumptions about the number of entities, users, rules, integrations, reports, and exceptions.
Write a one-page commercial brief before documenting the workflow. It should answer the following questions.
| Specification item | What to state | Why it affects the fixed quote | | Business problem | The measurable operational or financial problem being addressed | Developers need to distinguish a new process from a cosmetic screen change. | | Users and legal entities | Named roles, user groups, countries, and companies | Multi-entity permissions and country-specific rules increase scope. | | Odoo environment | Odoo version, Enterprise or Community edition, hosting model, and installed third-party modules | Compatibility and deployment work depend on the exact environment. | | Work type | Configuration, Odoo Studio, importable data module, or Python custom module | Each option has different delivery, maintenance, and hosting requirements. | | In scope | Named workflows, screens, reports, data, integrations, and training | This creates an estimate that can be tested at handover. | | Out of scope | Work explicitly excluded from the quote | Exclusions prevent assumptions becoming unpaid development. | | Target date | Required go-live date and dependencies owned by the client | A date without access, data, and decision owners is not a delivery plan. | The most important judgement call is hosting. Odoo Online cannot install Python custom modules, according to Odoo 19.0 documentation. If the requested solution requires Python code, the specification must identify a hosting option that supports custom modules before a fixed quote can be valid.
If your requirement can be met by configuration or Odoo Studio, do not commission a Python module merely because a developer can build one. Custom code introduces upgrade, testing, deployment, and support obligations. Use a Python custom module when the process, controls, integration, or user experience cannot be reliably delivered through the standard product and permitted configuration.
Worked example: the approval request that was not a requirement
Take a distributor with twelve staff and a US$40,000 monthly payroll. Its finance manager asks for “purchase approval in Odoo” because urgent orders are being raised without visibility. The first version of the brief names one approval screen, but does not say who approves when the finance manager is on leave, whether stock replenishment follows a different route, or what happens after a rejection.
A fixed quote at that stage would price guesses. The better specification identifies the request trigger, the cost-centre owner, the approval threshold, the substitute approver, the rejection reason, and the purchase order status at each point. The distributor should also state whether the approval applies to every legal entity or only one company. What it would do differently is document the exception path before asking for an estimate, because exceptions are where approval workflows become expensive.
Step 1: Identify the Odoo environment and delivery method
State the Odoo version and edition at the top of the document. Also state where Odoo is hosted, which modules are already installed, and whether another implementation partner has modified the database. These are fixed-quote dependencies, not administrative details.
Then classify the work.
1. Configuration: Use standard Odoo settings, existing fields, access rights, and approved application options.
2. Odoo Studio: Create or adapt fields, views, and automations within the Studio capabilities available in the selected environment.
3. Importable data module: Deliver records, templates, rules, or reference data as a module that can be installed and updated.
4. Python custom module: Build code for new models, business logic, integrations, reports, security, controllers, or web assets.
Odoo 19.0 developer documentation describes custom modules as potentially including models, views, data files, controllers, reports, security, and web assets. That is why “build a module” is not a complete scope. Name the components required and the business reason for each.
Also document source-code ownership, repository access, deployment responsibility, warranty terms, support arrangements, and upgrade responsibility. These items are commonly omitted because they do not appear on a user screen. They become material when the business upgrades Odoo or changes implementation partners.
Step 2: Write the workflow screen by screen and role by role
A developer should be able to build a test case from each workflow step. For every screen or process stage, record the trigger, inputs, validations, approvals, outputs, exceptions, access rights, notifications, and reports.
Use this practical template:
| Workflow field | Example of an acceptable specification | | Trigger | A warehouse supervisor creates a stock adjustment after a cycle count variance is confirmed. | | Inputs | Product, location, counted quantity, variance reason, attachment, and cost centre. | | Validation | A variance reason is mandatory when the counted quantity differs from the system quantity. | | Approval | The operations head approves adjustments above the defined internal threshold. | | Access rights | Supervisors can create drafts. Finance can view approved adjustments. Only authorised users can post. | | Notification | Send an Odoo activity to the approver when a request is submitted. | | Exception | If the approver rejects the adjustment, return it to draft with a mandatory rejection reason. | | Output | Create an auditable stock movement and include the variance in the monthly operations report. | The mistake we see most often is documenting the happy path only. “Manager approves” does not answer whether any manager can approve, whether the requester can approve their own transaction, whether approval can be delegated, or whether a rejected record can be edited and resubmitted. Those rules determine security, workflow states, and test effort.
Worked example: fiscal invoicing across two entities
Consider an illustrative group with a Zambian trading entity and a Zimbabwean distribution entity. The group asks for “one invoice customisation” because its sales teams use similar invoice layouts. That request is too broad because the fiscalisation path is country-specific and the legal entities have separate compliance obligations.
In Zambia, VAT taxpayers were required to use ZRA Smart Invoice from 1 July 2024, with standard penalties applying from 1 October 2024. ERP integrations use a Certified Invoicing System through the VSDC interface. In Zimbabwe, fiscal tax invoices must be issued from a device interfaced with ZIMRA FDMS, and an invalid invoice cannot support VAT input-tax or income-tax expenditure claims.
The group should scope two compliance workstreams, even if the visual layout is shared. It should name the legal entity, regulator, invoicing event, certified system or fiscal device path, failure handling, and audit output for each country. What it would do differently is avoid treating “tax localisation” as a standard invoicing setting, because a generic invoice workflow does not prove fiscal compliance.
Step 3: Specify integrations as contracts, not aspirations
“Integrate with X” cannot support a fixed quote. The developer needs the integration contract, including information that often sits with IT, finance, and the external provider.
For every integration, specify:
● The named external system and the internal business owner.
● Available API documentation and sandbox access.
● Authentication method and who supplies credentials.
● Data fields, field ownership, and transformation rules.
● Direction of data flow, such as Odoo to the external system or both directions.
● Frequency, including real-time, scheduled, or manual transfer.
● Error handling, retry rules, alerts, and reconciliation process.
● Third-party licence, transaction, certification, or support fees and who pays them.
A finance system integration is not complete when records appear in both systems. It is complete when exceptions can be identified, retried, reconciled, and audited. Require the quote to state who owns failed transactions after go-live.
Step 4: Scope country and tax localisation by legal entity
For African groups, tax localisation must be stated separately for each country and legal entity. A request for “African tax compliance” is not a deliverable.
Use regulator names and operating facts in the specification:
● Zambia: State whether invoices fall within ZRA Smart Invoice requirements and whether the scope includes the Certified Invoicing System and VSDC interface.
● Zimbabwe: State whether the entity must issue fiscal tax invoices through a device interfaced with ZIMRA FDMS. This affects invoice generation and the treatment of failed fiscal submissions.
● Kenya: State the taxpayer category and confirm the implementation position for KRA eTIMS. KRA guidance requires persons engaged in business to onboard eTIMS and issue electronic tax invoices, but the client position should be verified before design is approved.
● South Africa: From 1 April 2026, SARS increased compulsory VAT registration from R1 million to R2.3 million and voluntary registration from R50,000 to R120,000. The specification should state whether those thresholds affect tax configuration, workflows, and invoice requirements for the entity concerned.
FIRS-specific e-invoicing requirements were not confirmed from a primary source. Verify them with the relevant Nigerian authority and the client’s tax advisers before putting any FIRS requirement into a fixed quote.
Step 5: Define acceptance tests before development starts
Acceptance criteria are the point at which scope becomes auditable. For each requirement, define the expected result, the person who signs it off, and representative test data.
Odoo 19.0 documentation recommends testing custom modules on both an empty target-version database and an upgraded database. The test plan should cover views, reports, automated actions, workflows, and computed fields. This matters because a module can work in isolation while failing against upgraded data or an existing configuration.
A useful acceptance criterion reads like this: “When a purchasing officer submits a request with the required fields and an attached supplier quotation, the designated approver receives an Odoo activity. After approval, the system creates a purchase order. A user without the purchasing role cannot approve or confirm the order.”
An unusable criterion reads: “The workflow should work correctly.” It cannot be demonstrated, signed off, or defended later.
Specify who supplies test data, whether personal data must be masked, and which historical records will be migrated. Migration scope should name the date range, record types, attachments, opening balances, data-cleansing owner, reconciliation method, and sign-off owner. “Migrate historical data” is not a fixed-quote requirement.
Step 6: Price change properly and protect the go-live date
A fixed quote should include a change-control process. Once the specification is accepted, any request that changes the workflow, integration, report, data migration, legal entity, access rule, or acceptance test should be assessed for price and timing before work begins.
Do not allow “small changes” to bypass this control. A small field can alter permissions, reports, integration payloads, and test cases. The issue is not whether the request sounds minor. The issue is whether it changes the agreed deliverable.
Your quote should separate implementation deliverables from client dependencies. Typical client dependencies include decision-maker availability, access to environments, integration credentials, test data, tax adviser confirmation, user acceptance testing, and training attendance. A target go-live date remains a target until each dependency has an owner and due date.
Frequently Asked Questions
Can an Odoo developer give a fixed quote from a feature list?
Only where the feature list already defines workflows, exceptions, roles, environment, integrations, acceptance tests, and exclusions. Otherwise, a range or paid discovery phase is more honest because the developer is pricing assumptions.
Should we use Odoo Studio or a Python custom module?
Use configuration or Studio where they meet the requirement without compromising controls or maintainability. Use a Python custom module where you need logic, integration, security, reporting, or interface behaviour that the available standard tools cannot reliably provide. Confirm hosting first because Odoo Online cannot install Python custom modules.
What must an invoice specification include for Zambia, Zimbabwe, or Kenya?
Name the country, legal entity, regulator, fiscalisation or e-invoicing method, failure process, and audit output. Zambia requires ZRA Smart Invoice through the applicable Certified Invoicing System and VSDC path. Zimbabwe requires the applicable ZIMRA FDMS-connected fiscal device process. Kenya requirements should be confirmed against the client’s KRA eTIMS position.
Who should approve the Odoo specification internally?
The process owner should approve workflow accuracy, finance should approve control and reporting requirements, IT should approve hosting and integration dependencies, and the executive sponsor should approve budget, exclusions, and the go-live decision. One person rarely owns all four decisions.
A fixed quote is not obtained by asking a developer to be more precise. It is obtained by giving the developer a specification that can be built, tested, accepted, and supported. Request a Consultation with Serpa to scope your Odoo customisation and custom module development requirements.