In this piece
AI-generated Odoo modules: audit the business contract
A discount approval looks small: set a flag and record who approved it. In our deliberately flawed specimen, that code approves a cancelled quotation, trusts a caller’s invented approver and leaves partial changes after an injected failure. The ordinary demonstration still passes.
Audit an AI-generated Odoo module against an approved business contract and the exact installed Odoo version. Reproduce consequential failure cases, inspect final records and verify corrections independently. A passing local model helps define the checks; native installation, authorization, transaction and upgrade evidence still decide whether the real module can go live.
Research checked: 5 November 2026. We reviewed official Odoo 20 sources and ran thirteen synthetic approval-contract cases. Both Python specimens were written for this article; they are not sampled client code or a benchmark of an AI tool. No Odoo addon was installed, no native upgrade ran, and every proposed native acceptance check remains NOT_RUN.
What should the module audit decide?
The module audit should decide whether a specified customization has enough evidence to release, needs a bounded repair, or should be replaced by an approved simpler implementation. Start with what the business command is allowed to change. A code-quality score cannot supply a missing approval rule or decide which person has authority to approve it.
Ask the owner to demonstrate the requirement against the current configuration before expanding the custom code. If an existing supported workflow already meets it, compare that option with maintaining an override. This is a fit decision to verify on the installed version, not an assumption that every custom approval is unnecessary.
Then freeze the review scope: module commit, Odoo version and edition, dependency revisions, affected records and execution identities. Include the configuration that changes behavior. A review of a Git branch without its required settings can miss the exact path the business will use.
The general QA guide for AI-generated code owns the wider production-readiness checklist. Here, the unit of review is a deterministic Odoo customization. The separate agent business-state guide covers an agent’s complete task and tool choices.
What is the approval specimen supposed to do?
The specimen approves the current revision of a draft quotation only when the fixture actor has approval authority for its company and the requested discount falls within an invented delegation limit. It records the trusted actor once. It must reject stale or prohibited decisions without leaving a partial approval, including when a later item in a batch fails.
Our fictional quotation Q-17 belongs to company A, has revision 3 and starts with a 5.00% discount. User 7 may approve discounts from 0% through 10.00%. A 10.01% request needs another decision. These are example business rules, not Odoo defaults. The fields named approval and approved_revision belong to the Python specimen.
| Rule | Allowed outcome | Required failure behavior |
|---|---|---|
| Authority and company | The current approved actor acts within company A. | A missing role or unauthorized company leaves the record unchanged. |
| Revision and lifecycle | The reviewed revision is still current and the quotation remains draft. | A stale revision or cancelled quotation is rejected. |
| Delegated limit | 10.00% is included in the example’s limit. | 10.01% cannot receive this approval. |
| Actor evidence | The approval records trusted fixture user 7. | A caller-supplied label cannot substitute user 99. |
| Repeated selection | One approval and one audit entry per quotation revision. | A replay or duplicate selection creates no additional approval effect. |
| Batch failure | The agreed batch completes with its audit evidence. | A rejected item or injected audit failure leaves no partial batch changes. |
Approval here changes a review field. It does not confirm the quotation, create a delivery or post accounting. That boundary matters when a reviewer asks what “success” means. If the real module also performs a later business action, add that action’s authorization and final-state contract.
Settle disputed rules before generating the expected results. The business-rule conflict guide explains how to assign that decision. A reviewer cannot determine whether 10.00% is acceptable by inspecting a function that already accepts it.
Which defects does the annotated review expose?
The annotated review exposes missing business preconditions, an untrusted approver label and changes that survive an incomplete action. Repeated calls also append another audit effect. Each finding ties a specific statement to a failed contract case. This is more useful for a release decision than labeling the whole module “AI-generated” and assigning a general risk score.
The flawed specimen contains this sequence, shortened here to show its order:
quote["approval"] = "approved"
quote["approved_revision"] = expected_revisions[key]
quote["approved_by"] = claimed_approver or actor["uid"]
if key == fail_audit_for:
raise InjectedAuditFailure(key)
store["audits"].append({
"quote": key,
"revision": expected_revisions[key],
"actor": quote["approved_by"],
})Nothing in that sequence checks whether the actor may approve the current record. Odoo’s public-method guidance treats supplied records and parameters as untrusted; CRUD access checks do not establish every business precondition. Review the actual receiving method, not only its visible button.
| Finding | Business consequence in the fixture | Correction to verify |
|---|---|---|
| No current authority or record checks | An unauthorized actor or company receives approval. | Validate the trusted identity and the target record before mutation. |
| No revision, state or limit preconditions | A stale, cancelled or out-of-limit request is marked approved. | Compare the approved contract with the current record values. |
| Caller chooses the recorded approver | The audit attributes user 99 instead of user 7. | Derive attribution from the trusted execution identity. |
| Every selection appends an effect | A repeat or duplicated item creates two audit entries. | Recognize an already completed approval of that revision. |
| Mutation precedes failure without an atomic boundary | A failed audit write leaves an approved record or partial batch. | Prove the agreed all-or-nothing outcome on the real implementation. |
The corrected Python specimen validates the selection and applies changes to a staged copy before replacing the model state. That models the required outcome. It is not an implementation of Odoo transactions. The official coding guidelines explain framework transaction ownership and why unexplained manual commits require review; exceptional explicitly owned cursors need separate reasoning.
Download the annotated finding record for the mapping from each defect to its reproducer. The detailed Odoo 20 company-access migration tests own the version-specific permission transition. A generic old ACL checklist is insufficient context for that migration.
What changed in the thirteen contract tests?
The flawed specimen violates eleven of the thirteen fixed expectations. The corrected specimen meets all thirteen. Both pass ordinary approval and the exact limit, which explains why a successful demonstration can conceal the other defects. These counts describe deliberately constructed examples; they are not a defect rate for AI-generated Odoo code.
| Case | Flawed specimen | Corrected specimen |
|---|---|---|
| Ordinary 5.00% approval | Approved; one audit entry | Approved; one audit entry |
| Exact 10.00% boundary | Approved; one audit entry | Approved; one audit entry |
| 10.01% request | Incorrectly approved | Rejected; unchanged |
| Actor lacks approval authority | Incorrectly approved | Rejected; unchanged |
| Record belongs to unauthorized B | Incorrectly approved | Rejected; unchanged |
| Review names revision 3, record is revision 4 | Incorrectly approved against revision 3 | Rejected; unchanged |
| Quotation is cancelled | Approval flag still set | Rejected; unchanged |
| Same quotation selected twice in one request | Two audit entries | One audit entry |
| Same approval called twice | Two audit entries | One audit entry |
| Caller supplies approver 99 | Attributes user 99 | Attributes trusted user 7 |
| Audit failure after approval assignment | Approved flag remains; no audit entry | No changes |
| Second batch record belongs to unauthorized B | Both records approved | Neither record changed |
| Second batch audit write fails | Both approved; only one audit entry | Neither record changed |
Download the runnable approval-review specimen and full expected and observed states. Save the Python file and run:
python3 approval-review-model.pyThe script uses Python’s standard library, with decimal comparison for the invented percentage rule. It does not import Odoo or access a network. Its expected states are specified from the contract, independently of either approval function. The output includes errors and final approval fields, so an exception alone cannot make a partial write look acceptable.
The model does not test simultaneous native requests. A real method may need additional protection when two callers approve the same revision concurrently. The n8n recovery article examines the separate boundary for external order creation; this fixture’s repeated audit effect does not establish connector idempotency.
What must be verified in the real Odoo module?
Verify the real entry points and affected records under the actual build before applying the model’s conclusions to Odoo. Inspect custom overrides and their dependencies, then execute the approved business cases through the receiving methods. Confirm which identity performs each change. An isolated function can satisfy its contract while its integration into the application remains wrong.
| Area | Review question | Evidence required |
|---|---|---|
| Entry points | Can a button, API call or automation reach the same behavior? | Approved identity and final-state checks for every relevant path. |
| Dependencies | Does the manifest declare the modules this customization uses? | A reproducible installation from the declared dependency set. |
| Overrides | Why is each base behavior replaced or extended? | Regression cases showing required standard workflows still work. |
| Company and field behavior | Does the command use the intended record, company and value? | Native role/context evidence and representative field checks. |
| Failure and concurrency | What remains after interruption or simultaneous approvals? | Resulting records and audit effects under controlled failure schedules. |
| Existing data | What happens to quotations created before this module revision? | Approved upgrade expectations and observed records after rehearsal. |
The proposed native acceptance pack contains these gates and the contract cases. It starts with NOT_RUN and pending policy approval. Replace illustrative expectations with approved rules, record exact identities and revisions, and attach actual evidence before treating a row as passed.
Review each override with the standard behavior beside it. An unexplained replacement creates an obligation to discover what was removed. If the module only needs one extra decision check, ask whether the chosen extension is narrower than replacing the complete workflow. Verify that judgment with the affected standard cases.
How should installation and upgrade evidence be checked?
Check installation, upgrade and runtime behavior as separate gates. A clean installation can reveal missing dependencies, while an existing-data rehearsal addresses records and configuration created before the change. Neither proves that the right business tests ran. Bind the report to the exact module revision, target environment and selected cases.
Odoo’s test documentation requires test modules to be imported and explains how enabling and selection determine execution. Ask for the runner output identifying the intended tests. A green command with the relevant tests absent leaves their behavior unverified.
The customized-database upgrade guide separates empty-database installation from installation on upgraded data and runtime rehearsal. Our Python run supplies none of those native results.
For this approval customization, include an older quotation without the new approval fields populated, a previously approved quotation whose business content changed, and a record awaiting review when the upgrade began. Decide the target state before the rehearsal. A default value that makes an old record look approved is a business decision, not a convenient way to eliminate an error.
Retain a controlled recovery plan for the rehearsal. Reverting the code alone cannot establish that data changes have been reversed. The migration reconciliation guide covers the wider expected-versus-observed comparison.
What should the audit handover contain?
The audit handover should make every release decision traceable to an approved rule and reproducible evidence. For each blocker, record the affected revision, reproducer and business consequence, then attach the correction and independent retest. Keep unexecuted checks visible. A list of stylistic suggestions does not answer whether a quotation can receive an unauthorized approval.
Our specimen’s finding record distinguishes a confirmed local contract failure from a native question still requiring execution. Carry that distinction into the real audit. “The model rejects a stale approval” and “the deployed Odoo method rejects it under these identities” are different evidence statements.
Separate an implementation fix from policy approval. The reviewer may show that the function enforces 10.00%; the business owner must decide whether that is the intended delegation. If the rule changes during review, revise the expected outcomes and rerun the affected cases against the new contract.
The release QA checklist provides the broader launch context. The general AI-built software audit guide explains a wider audit engagement; this module review should preserve its narrower, testable scope.
When would we release, repair or remove the customization?
Release the customization only for the scope supported by approved native evidence. Repair demonstrated contract violations and retest their affected workflows. Consider removing an unnecessary customization when an approved supported alternative satisfies the requirement, with a plan for its existing data. A polished demonstration should not determine any of those decisions.
We would begin with one consequential command, its real entry point and a pair of allowed and denied cases. Add the failure cases that can leave the business with a wrong record. This gives the audit a concrete first decision before expanding into every file in the repository.
For Wavect’s software quality assurance service, bring the module commit, installed-version details and one business rule the owner can approve. The useful deliverable is a finding with a reproducible test and a clear native release gate, followed by evidence that the agreed correction works.
