
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.
Available for Odoo 16.0, Odoo 17.0, Odoo 18.0, Odoo 19.0, Odoo 20.0. Technical name bambooforge_pharmacy.
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.





Transcript of the walkthrough (Odoo 19)
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. 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. 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. 00:01:10.1000 --> 00:01:19.754 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.
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.





Transcript of the walkthrough (Odoo 18)
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. 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. 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.
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.





Transcript of the walkthrough (Odoo 17)
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. 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. 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. 00:01:10.1000 --> 00:01:19.754 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.
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.





Transcript of the walkthrough (Odoo 16)
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. 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. 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. 00:01:10.1000 --> 00:01:19.754 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.
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.





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.
- Live demo
- https://demo20.bambooforge.dev/odoo/action-bambooforge_pharmacy.action_bf_pharmacy_dispense
- 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
Copy bambooforge_pharmacy into your addons path and update the app list, or install the ZIP from the Odoo Apps Store.
Install Pharmacy Management & Retail POS.
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.