A car rental business sent over its monthly business report as an Excel file.
That sounds normal. Many small operations close the month that way. Someone exports data, fixes a few lines, adds transfers, adds long rentals, checks totals, and emails the file when it feels finished.
The useful part was what happened after the file arrived.
We did not treat the spreadsheet as the source of truth. We treated it as a claim.
The source of truth was the Odoo back office, where the rental agreements live: contract number, pickup date, return date, customer, car, pricing, fuel, coverage, total amount, and state. The Excel was supposed to summarize that data. So the first job was simple: read the workbook, read the contracts from Odoo, and compare both sides line by line.
The result was uncomfortable in the best possible way.
The file had more than sixty contract rows across several months. Most contract numbers existed in Odoo, which was good. But many totals did not match the totals in the rental agreements. A couple of contract numbers did not match exactly. Some rows looked like reused references or manual corrections. A few date ranges did not agree with the contracts.
This is the kind of problem that can stay hidden for a long time because the spreadsheet looks final. It has sheet names, totals, sections, and a familiar format. It feels official.
But a formatted Excel file can still be wrong.
The report was a symptom
The first deliverable was a validation report.
I built a small cross-check against Odoo and generated a PDF for the client. The PDF did not try to accuse anyone of bad work. It listed the facts:
- how many rows were read from the spreadsheet
- how many rental agreements were found in Odoo
- which rows had amount differences
- which contract numbers were missing by exact match
- which examples needed manual review
That was useful, but it was still a reaction. It answered the question: "Does this report match the back office?"
The better question came next: "Why are we generating this report outside the back office at all?"
Once you ask that, the spreadsheet stops being a monthly artifact and becomes a product brief.
The feature almost wrote itself
The Excel already described the feature.
It had one page per month. It grouped normal rentals, transfers, long rentals, and summary totals. It had the information the business wanted to send and the shape they were used to reading.
So the plan became: build the monthly business report inside Odoo, using the contracts already stored there, and keep the output close enough to the Excel that the business does not need to relearn its own report.
The back office should have a menu like:
Vehicle Rental > Reports > Monthly Business Reports
From there, the user selects a month and generates the report.
The report becomes a monthly record, not a loose file. It has a state. Draft. Locked. Exported. If the month is still open, the report can be refreshed. When the month is closed, it can be locked so the exported PDF or XLSX stays tied to the numbers that were reviewed.
That lock is important. A live query is fine for dashboards. A monthly business report needs a snapshot. If someone changes a rental agreement two weeks later, the system should not silently rewrite the report that was already sent.
Build from the rental agreement
The current OK CarHire back office already has the right core model: vehicle.rental.agreement.
That is where the report should start.
The plan is not to invent a parallel accounting model. The report should read the agreements whose pickup or return dates fall inside the selected month, then classify each line into the same business buckets the spreadsheet uses.
For each line, the report needs enough fields to be auditable:
- rental agreement number
- customer
- vehicle
- pickup date
- return date
- duration
- rental type
- base amount
- extras
- full coverage
- fuel or full tank charge
- total amount
- validation status
The validation status is the difference between a pretty report and a useful report.
If a contract is cancelled, missing a vehicle, missing a customer, has a suspicious date range, or has a total that cannot be explained from its components, the report should say so before export. The user should not discover the issue after the email has been sent.
The latest code changed the plan
Before writing the final plan, I pulled the latest back office code.
That mattered. The system had just received work around mini-leasing and tier-based pricing. Those changes affect the monthly report directly because long rentals cannot be calculated the same way as tourist rentals.
In the current code, tourist and leasing contracts still use pricing rules from vehicle.rental.pricing. Mini-leasing uses pricing tiers on the vehicle model through vehicle.rental.pricing.tier.
That means the monthly report cannot multiply days by one daily rate and call it done.
For mini-leasing, it needs to respect:
- the vehicle model's tier-based mini-leasing pricing
- the full tank price on the vehicle model
- the current rule that full coverage is zero for mini-leasing unless the business later changes that policy
This is why reading the current code before planning matters. A plan written from the Excel alone would miss the pricing system that now exists in the back office.
The output should look familiar
I do not think the first version should try to redesign the business report.
The existing Excel has a format the business already understands. The first win is reliability, not visual novelty.
So the Odoo feature should export:
- a PDF for review and email
- an XLSX that keeps one page per month
- the same section names the team already expects
- clear totals per section and month
- a validation section when something needs attention
The difference is where the numbers come from.
Instead of a human rebuilding the month in Excel, Odoo creates the month from the rental agreements. Instead of manual checking after the fact, the validation happens before the report is locked. Instead of hunting for which contract caused a difference, each report line links back to the contract.
That is the quality jump.
Small companies need this more, not less
People often talk about automation as if it starts with a grand architecture.
In practice, many useful automations start with a messy attachment.
Someone sends a spreadsheet. The spreadsheet has a few wrong numbers. The wrong numbers point to repeated manual work. The manual work points to a missing screen in the system.
That is a good path to a feature because it starts from a real business habit.
The goal is not to shame the spreadsheet. The spreadsheet did its job for a while. It gave the company a way to close the month. But once the back office has the contracts and the pricing rules, the spreadsheet should stop being the place where truth is assembled.
The back office should produce the report.
Then the team can spend its time reviewing exceptions instead of copying totals.
What the development plan includes
The plan now has three layers.
First, the data layer:
vehicle.rental.monthly.reportvehicle.rental.monthly.report.line- snapshot totals
- month, state, company, currency, generated date, locked date
Second, the business layer:
- generate or refresh a month
- classify rentals by type
- apply the current pricing logic
- surface validation issues
- lock a reviewed report
Third, the output layer:
- Odoo tree and form views
- smart buttons back to rental agreements
- PDF export
- XLSX export
- access rights for operations and accounting users
The acceptance criteria are concrete. Generate February, March, April, May, or June 2026 from Odoo data. Confirm the number of lines. Confirm totals. Confirm that mismatches are visible. Confirm that locked reports do not change after contract edits. Confirm that mini-leasing uses the new tier logic.
That last part is where the plan becomes useful as engineering work instead of just documentation. A developer can implement it, test it, and know when it is done.
The real lesson
The interesting part of this work was not the PDF, the Excel parsing, or the Odoo XML-RPC call.
The interesting part was the move from reconciliation to product design.
One email arrived with a final-looking spreadsheet. A few checks showed that the final file was not reliable enough to close the month. That gave us the exact shape of the feature the back office needed.
Now the monthly report can become part of the rental system itself.
One page per month. Same business format. Fewer manual edits. More traceability. Better review before anything leaves the company.
That is a real improvement for a rental business because next month the team should not have to wonder whether the Excel matches the contracts.