BambooForge Labs

Pharmacy Management & Retail POS

Pharmacy retail on Odoo Community stock: dispense by batch with expiry checked, refuse what may not be handed over, controlled drugs register.

Buy on the Odoo Apps StoreOpen the live demoIndustries€149Community & Enterprise

Available for Odoo 16.0, Odoo 17.0, Odoo 18.0, Odoo 19.0, Odoo 20.0. Technical name bambooforge_pharmacy.

Odoo 16.0Odoo 17.0Odoo 18.0Odoo 19.0Odoo 20.0
Screens and walkthrough for
Transcript of the walkthrough (Odoo 20)

Pharmacy Management & Retail POS. Pharmacy retail on Odoo Community stock: dispense by batch with expiry checked, refuse what may not be handed over, controlled drugs register. Said before you buy, because it is the half that decides whether the app fits: In the next few minutes: what it is, then medicines are products with rules, then dispensing checks before it hands over, then the controlled drugs register, then medicine that comes back. Then what this app deliberately does not do, so you can tell before you buy whether it fits.

Everything lives under Pharmacy then Dispensing then Stock then Controlled drugs register. Odoo Community 16.0, 17.0, 18.0 or 19.0 with stock, product_expiry and account. No Enterprise module. What it is.

A retail pharmacy on Odoo Community, built around the three things that get a pharmacy fined or sued: the batch, the expiry, and the register. Medicines are products with rules. Active ingredient (what a pharmacist substitutes on), strength, form, ATC code, prescription-only, controlled schedule, maximum days supply, cold chain, and the counselling note the pharmacist has to say out loud. A controlled item that is not lot-tracked is refused outright - a register that cannot name the batch is not a register.

Dispensing checks before it hands over An expired batch is refused, with the expiry date in the message, read in the shop's own timezone - a box printed "30/09" is saleable all day on the 30th, wherever the shop is. A lot-tracked medicine cannot leave without naming the box that left the shelf; otherwise a recall cannot be answered. Prescription-only needs the prescriber on the record. A controlled item needs the prescriber's licence number and a checked ID before anything is handed over.

More days than the medicine allows is refused. The same controlled script cannot be dispensed twice: the prescription number and the prescriber's licence are compared with what has already been supplied. A dispensing record cannot be rewritten once handed over: it is what an inspector reads. Nor can its items, nor its state - the buttons are the only way through, on the server and not merely in the form.

Only a pharmacist may hand over anything with a schedule on it, Schedule II to V, and that is checked on the server rather than by hiding a button. The controlled drugs register. Written automatically, one entry per controlled item, with the batch, the quantity, a running balance, who supplied it, to whom and on whose authority. Append-only: entries cannot be edited or deleted - and they cannot be typed either.

No role holds create rights on the register; the counter writes it, and the running balance is derived from the line above rather than accepted from whoever is asking. A return writes a receipt line, and a destruction writes its own entry with the witness who watched it. A pharmacy going live already has stock in the cabinet, so the book can be opened from what is on the shelf: one opening correction per controlled medicine, equal to the quantity really in the company's custody, quarantine included. It shows the numbers before they are signed for, it refuses to run twice, and a medicine that already has entries is left alone.

Without it the balance starts at zero and the first hand-over prints a negative number that can never be edited away. Medicine that comes back. A return is recorded through a wizard that asks the questions a return has to answer: why it came back, how much of it (one of three boxes is a return), and where it goes. It never goes back on the saleable shelf - it goes into a quarantine location the module creates, out of the availability check and out of the FEFO suggestion - or it is destroyed in front of a named witness, which writes both a stock scrap and a register entry.

A posted invoice is credited with a linked, posted, reconciled credit note; a draft one is cancelled outright. Stock, expiry and money Dispensing creates the delivery with the right lot and takes it out of stock; the customer invoice follows the handover, never the other way round. Lots carry days to expiry and a shelf-life state, and the suggested batch is the one expiring first (FEFO) - a pharmacy that picks the newest box writes off stock every quarter. Expired, quarantined and other companies' batches are never suggested.

A daily job raises what is about to go out of date while it can still be returned to the wholesaler, and addresses the work to a named, active pharmacy manager for that company - never to OdooBot. Roles. The counter prepares a dispensing and sells over-the-counter items. Only a pharmacist can hand over a controlled drug.

The manager configures and can delete a draft - and only the manager can set the control regime on a product: the schedule, prescription-only and the maximum days supply are manager-only fields with a tracked history, so re-importing the product master from a supplier file cannot quietly de-schedule morphine. How we make sure it works. Before every release, 121 automated checks run against this app on Odoo 16, 17, 18, 19 and 20. Among them: The same controlled script cannot be presented twice.

The watch does not raise the same batch twice. A drug with a register behind it cannot be de scheduled. A counter user cannot hand over a schedule four item. A counter user cannot hand over a schedule two item.

The complete list is in the source you receive. Questions people ask. Which Odoo versions does it run on? Odoo 16.0, 17.0, 18.0, 19.0, 20.0, on the series you buy it for; each series is its own listing.

Community or Enterprise? Both. It needs no Enterprise app. Support.

support@bambooforge.dev - answered within two business days. OPL-1; one purchase covers one production database plus its staging copies. The complete manual is at docs dot bambooforge dot dev, the demo database is linked from the store page, and support answers within twenty-four hours. Links are in the description.

Dispense - bambooforge_pharmacy
Dispense
Expiry - bambooforge_pharmacy
Expiry
Register movement - bambooforge_pharmacy
Register movement
Register open - bambooforge_pharmacy
Register open
Register - bambooforge_pharmacy
Register

Key facts

What it is
Pharmacy retail on Odoo Community stock: dispense by batch with expiry checked, refuse what may not be handed over, controlled drugs register.
For
Industries
Odoo versions
16.0, 17.0, 18.0, 19.0, 20.0 - Community and Enterprise
Runs on
Odoo.sh or Odoo you host yourself - not Odoo Online, which accepts no third-party apps
Price
149 EUR, one-time, on the Odoo Apps Store
Technical name
bambooforge_pharmacy
Not for
Said before you buy, because it is the half that decides whether the app fits:; Dispense a controlled item that is not lot-tracked, or the same controlled script twice.; Let a lot-tracked medicine leave without the box that left the shelf.; Rewrite a dispensing record once handed over - on the server, not merely in the form.
Manual
https://docs.bambooforge.dev/bambooforge_pharmacy/
Live demo
https://demo20.bambooforge.dev/odoo/action-bambooforge_pharmacy.action_bf_pharmacy_dispense
Store page
https://apps.odoo.com/apps/modules/20.0/bambooforge_pharmacy
Manual last updated
2026-10-15

Pharmacy Management & Retail POS

What it is

A retail pharmacy on Odoo Community, built around the three things that get a pharmacy fined or sued: the batch, the expiry, and the register.

Medicines are products with rules

Active ingredient (what a pharmacist substitutes on), strength, form, ATC code, prescription-only, controlled schedule, maximum days supply, cold chain, and the counselling note the pharmacist has to say out loud. A controlled item that is not lot-tracked is refused outright - a register that cannot name the batch is not a register.

Dispensing checks before it hands over

  • An expired batch is refused, with the expiry date in the message, read in the shop's own timezone - a box printed "30/09" is saleable all day on the 30th, wherever the shop is.

  • A lot-tracked medicine cannot leave without naming the box that left the shelf; otherwise a recall cannot be answered.

  • Prescription-only needs the prescriber on the record.

  • A controlled item needs the prescriber's licence number and a checked ID before anything is handed over.

  • More days than the medicine allows is refused.

  • The same controlled script cannot be dispensed twice: the prescription number and the prescriber's licence are compared with what has already been supplied.

  • A dispensing record cannot be rewritten once handed over: it is what an inspector reads. Nor can its items, nor its state - the buttons are the only way through, on the server and not merely in the form.

  • Only a pharmacist may hand over anything with a schedule on it, Schedule II to V, and that is checked on the server rather than by hiding a button.

The controlled drugs register

Written automatically, one entry per controlled item, with the batch, the quantity, a running balance, who supplied it, to whom and on whose authority. Append-only: entries cannot be edited or deleted - and they cannot be typed either. No role holds create rights on the register; the counter writes it, and the running balance is derived from the line above rather than accepted from whoever is asking. A return writes a receipt line, and a destruction writes its own entry with the witness who watched it.

Receipts, destructions and corrections go in through the same register. A wholesaler delivery writes itself the moment the transfer is validated - read off the locations the stock actually travelled between, so a return to the wholesaler on the same incoming operation type is booked out rather than in, and moving a box from the shelf to the cabinet is not counted as a second delivery. Everything else is recorded through a wizard that asks the questions the book has to answer: what happened, to which batch, how much, and - for a destruction - who watched it. Nothing goes round the register: the wizard calls the same elevated writer the counter does, and the balance is still derived under the same lock.

A pharmacy going live already has stock in the cabinet, so the book can be opened from what is on the shelf: one opening correction per controlled medicine, equal to the quantity really in the company's custody, quarantine included. It shows the numbers before they are signed for, it refuses to run twice, and a medicine that already has entries is left alone. Without it the balance starts at zero and the first hand-over prints a negative number that can never be edited away.

Who may write which line: a receipt is the pharmacist's, because it is a physical fact at goods-in and making it wait for the manager means either the book waits or a login is shared. A destruction and a correction are the manager's - they are the two entries that can make a discrepancy disappear.

Medicine that comes back

A return is recorded through a wizard that asks the questions a return has to answer: why it came back, how much of it (one of three boxes is a return), and where it goes. It never goes back on the saleable shelf - it goes into a quarantine location the module creates, out of the availability check and out of the FEFO suggestion - or it is destroyed in front of a named witness, which writes both a stock scrap and a register entry. A posted invoice is credited with a linked, posted, reconciled credit note; a draft one is cancelled outright.

Stock, expiry and money

  • Dispensing creates the delivery with the right lot and takes it out of stock; the customer invoice follows the handover, never the other way round.

  • Lots carry days to expiry and a shelf-life state, and the suggested batch is the one expiring first (FEFO) - a pharmacy that picks the newest box writes off stock every quarter. Expired, quarantined and other companies' batches are never suggested.

  • A daily job raises what is about to go out of date while it can still be returned to the wholesaler, and addresses the work to a named, active pharmacy manager for that company - never to OdooBot.

Roles

The counter prepares a dispensing and sells over-the-counter items. Only a pharmacist can hand over a controlled drug. The manager configures and can delete a draft - and only the manager can set the control regime on a product: the schedule, prescription-only and the maximum days supply are manager-only fields with a tracked history, so re-importing the product master from a supplier file cannot quietly de-schedule morphine.

Requirements

Odoo Community 16.0, 17.0, 18.0, 19.0 or 20.0 with stock, product_expiry and account. No Enterprise module.

How we make sure it works

Before every release, 121 automated checks run against this app on Odoo 16, 17, 18, 19 and 20. Among them:

  • The same controlled script cannot be presented twice.

  • The watch does not raise the same batch twice.

  • A drug with a register behind it cannot be de scheduled.

  • A counter user cannot hand over a schedule four item.

  • A counter user cannot hand over a schedule two item.

The complete list is in the source you receive.

What it does not do

Said before you buy, because it is the half that decides whether the app fits:

  • Dispense a controlled item that is not lot-tracked, or the same controlled script twice.

  • Let a lot-tracked medicine leave without the box that left the shelf.

  • Rewrite a dispensing record once handed over - on the server, not merely in the form.

Install

  1. Copy bambooforge_pharmacy into your addons path and update the app list, or install the ZIP from the Odoo Apps Store.

  2. Install Pharmacy Management & Retail POS.

  3. Open Pharmacy - its menus are Dispensing, Stock, Controlled drugs register.

Questions people ask

Which Odoo versions does it run on?

Odoo 16.0, 17.0, 18.0, 19.0, 20.0, on the series you buy it for; each series is its own listing.

Community or Enterprise?

Both. It needs no Enterprise app.

What does it need?

The standard Inventory, Invoicing, product_expiry apps. No extra Python package.

Where does it show up?

Under the Pharmacy menu (Dispensing, Stock, Controlled drugs register).

Does anything leave my server?

No. It makes no call outside your Odoo server: no telemetry, no licence check, no third-party service.

What happens if I uninstall it?

Odoo drops the app's own tables and fields; the standard records it worked on (contacts, orders, products) stay as they are.

Support

support@bambooforge.dev - answered within two business days. OPL-1; one purchase covers one production database plus its staging copies.