Month-end in one click: connecting Odoo purchases to POS sales
Back to Blog
Automation

Month-end in one click: connecting Odoo purchases to POS sales

Mohtashim HusnainAugust 20, 20266 min read

## The problem: two systems, no single answer

Mealwheelz runs its restaurants on two systems that never talk to each other. Sales live in QuickBill POS. Purchases live in Odoo. Both are fine at what they do. Neither gives you a profit and loss statement.

So every month the finance team did what most restaurant groups do. They exported reports from both sides, pasted them into spreadsheets, and reconciled by hand. It took days. There was no food-cost view at all, because the numbers needed to compute one sat in two different databases. If an owner asked "what did we actually make last week?", the honest answer was "give us until Friday".

We were asked to fix that without replacing either system.

Why no API keys

The obvious approach is to ask both vendors for API access, collect keys, and build integrations on top. We did not do that, for two practical reasons.

First, key management is a burden for a small finance team. Keys expire, get rotated, get pasted into the wrong place. Second, one of the systems did not expose the data we needed through a clean public API anyway.

Instead we built web-login adapters. The module signs in the same way a person does, with the client's own credentials, and then talks to each system through the interfaces it already exposes to a logged-in user:

  • For Odoo, that is JSON-RPC, the same protocol the Odoo web client uses.
  • For QuickBill, the adapter authenticates through the web login and reads sales, items and settlements from there.

No new accounts, no keys to store, no vendor tickets. If a staff member's password changes, they update it once in the module and everything keeps working.

The Postgres cache and the reports

Hitting two live systems every time someone opens a report is slow and fragile. So the module does not do that. Instead, a sync step pulls sales, items and purchases from both sources and writes them into a Postgres cache that we control.

That cache is what the reports read from. Because the data is in one database with one schema, we can do things that were impossible before:

  • Filter sales and purchases by branch, date range, category or supplier in a single query.
  • Compare revenue against the purchases that produced it, period by period.
  • Drill from a monthly total down to the individual bills and purchase orders behind it.

The reports are built in Next.js, so they run in the browser with no installs, and the finance team can open them from anywhere. Filters are just controls on the page. Change one and the numbers update.

GST and food cost, handled once

Two pieces of month-end work used to eat the most time, so we made them permanent parts of the system rather than monthly chores.

**GST.** Sales tax in the client's jurisdiction is 13%. The module computes GST on synced sales automatically, so the tax figures fall out of the same data as everything else. There is no separate GST spreadsheet to maintain, and no risk of the tax report disagreeing with the sales report because they were built from different exports.

**Food cost.** This is the number restaurant owners care about most and the one they almost never have. We added a recipe layer: each menu item maps to the ingredients it uses, and those ingredients map to the purchase lines coming in from Odoo. With that mapping in place, food cost is computed continuously rather than estimated once a quarter. When a supplier raises prices, you see the effect on margin the next time you sync, not at the end of the quarter.

What changed for finance

The short version: days of reconciliation became one click.

Month-end now looks like this. Someone presses "Sync". The module logs into Odoo and QuickBill, pulls the period, updates the cache, and the reports are current. The P&L, GST figures and food-cost view are all there, built from the same data, with filters instead of formulas.

A few other things changed along the way:

  • Numbers are consistent. Every report reads from the same cache, so revenue on one page always matches revenue on another.
  • Questions get answered the same day. "What was food cost at one branch last month?" is a filter, not a project.
  • The spreadsheets are gone. Not archived or simplified. Gone.

What we would tell another restaurant group

You probably do not need to replace your POS or your accounting system. What you need is a layer that reads from both, stores the result somewhere you control, and gives your team reports they can filter themselves. Web-login adapters make that possible even when vendors are slow to hand out API access, and a Postgres cache makes it fast.

If your month-end still involves exporting two systems into one spreadsheet, this is a solved problem.

Want this for your business? Message us on WhatsApp +92 320 0496154.

odooposaccountingnext.js