Skip to content

Cannabis Dispensary

Seed-to-sale grams that balance, with the ID, limit and COA checks before the sale.

The short answer

Cannabis Dispensary is an Odoo app for licensed dispensaries that need the custodial answer, not just the commercial one. Every gram is accounted for as dispensed, destroyed, on hand or quarantined, and the till refuses a sale when the ID was not checked, the batch has no passing lab result, or the day's running total would cross the legal cap.

Request this module

Last updated

What it is for

An inspector does not ask what you sold. They ask where every gram went. A dispensary can have a perfectly reconciled cash drawer and still fail an audit, because the question is custodial rather than commercial. Every gram that came through the door was dispensed, destroyed, left on the shelf, or quarantined pending a lab result, and those have to add back up to intake with evidence attached to each. Point-of-sale software answers the commercial question. The custodial one gets answered by a spreadsheet maintained after the fact, which is the artefact nobody trusts when it matters — that, and a budtender holding an ID check and a running daily total in their head, with a queue in front of them.

Platform
Odoo
Category
Regulated records
Works with
Odoo 18 · Odoo 19
Technical name
cannabis_dispensary
Status
Direct from webbox
Pricing
Paid

What is inside it

  1. 01

    The seed-to-sale ledger

    Intake is typed once. Dispensed, destroyed, on hand and quarantined are computed from the sales log and the destruction register, so the shelf figure follows from the events rather than from anyone's correction.

  2. 02

    What the till refuses

    A ticket can be created, edited and cancelled freely. Completing it is the guarded step: no unchecked ID, no batch without a passing COA, no sale that puts the customer over the day's cap.

  3. 03

    The daily limit

    The check sums every completed sale that customer made that day and adds the ticket being rung. Day boundaries resolve in the dispensary's own timezone rather than off raw UTC dates.

  4. 04

    The board

    Opening the app lands on a compliance board of twelve tiles: quarantined grams, saleable stock, limit and ID-check rates, tickets held at the till, expiring medical cards, with five charts underneath.

  5. 05

    Chain of custody

    Destruction is a two-step record carrying a quantity, a reason, a method and a named witness. A draft entry is not yet a destroyed gram, and entries with no witness get their own tile.

What it looks like in practice

  1. A customer splits one purchase across two visits on the same afternoon

    Someone buys at 16:00 and comes back at 18:00 local time. At UTC−7 those two timestamps land on different UTC days, so a naive daily total drops the earlier one.

    The guard sums every completed sale that customer made inside the dispensary's own day, adds the ticket being rung, and refuses completion with the message that the sale would put the customer at 31.5 g today, over the 28.0 g legal daily limit. The verdict is stored per ticket and coloured in the list, so who went over and when is a filter you can run.

    Default daily limit
    28.0 g

    A default you can change.

    The daily limitWhat the till refuses

  2. The batch that failed its lab screen is missing from the shelf list

    The batch list totals 4,440 g while intake across the estate was 4,920 g. One archived batch of 480 g failed its screen and was destroyed in full.

    The dashboard ledger adds the archived batch back in, so dispensed, destroyed, on hand and quarantined sum to intake. A dashboard test asserts that the sum holds, which means a change breaking the balance fails the build instead of shipping quietly.

    Intake
    4,920 g
    Dispensed
    150 g
    Destroyed
    559.5 g
    On hand
    3,160.5 g
    Quarantined
    1,050 g

    Figures from the module's own demo data, on a fresh install. Install with demo data and open Cannabis then Dashboard.

    The seed-to-sale ledgerThe board

  3. A destruction entry sits with the witness field still blank

    Seven rows in the destruction register add to 563 g in the footer, and the board reports 559.5 g destroyed.

    The 3.5 g at the top is still a draft. Mark Destroyed is the only route to the second state, so those grams stay outside the destroyed total until somebody presses it, and rows with no witness named show in red where an audit stops.

    Register footer
    563 g
    Reported destroyed
    559.5 g
    Still in draft
    3.5 g

    Figures from the module's own demo data, on a fresh install. Install with demo data and open Inventory then Waste / Destruction.

    Chain of custodyThe board

What it deliberately does not do

No state track-and-trace filing
There is no Metrc, BioTrack or Leaf API integration. The ledger here is the internal record you report from.
Not a payment terminal or cash-drawer POS
It logs the dispense and the compliance decision. Money stays Odoo's job.
No lab portal feed
A COA is a flag plus the attached certificate, set by whoever reads the lab report.
Cultivation is out of scope
No plant tagging, propagation, room or harvest planning, and no delivery manifests or driver routing. It starts at intake.
It does not decide your legal limit
The daily limit is a field on the ticket, defaulting to 28.0 g. Set it to whatever your jurisdiction requires.

Questions

Can I install this on Odoo Online?
No. Odoo Online cannot install modules that contain Python, so this runs on Odoo.sh or a self-hosted instance. Check the versions listed above for the series and edition it is verified against.
Does it file to Metrc or BioTrack?
No. There is no state track-and-trace integration of any kind, and nothing is transmitted to Metrc, BioTrack or Leaf. The ledger here is the internal record you report from, so a licence that requires automated filing still needs the state system alongside it.
Where is the legal daily limit set?
It is a field on the sales ticket, defaulting to 28.0 g, and the refusal message quotes whatever that field says. A different cap is a configuration change rather than a code change, and the running total behind it is summed in the dispensary's own timezone.
Who can see the app once it is installed?
Odoo administrators are granted Manager on install, so the menus appear the moment installation finishes. User is the floor: creating and working the day's records. Manager adds configuration and the full compliance surface, and every model carries explicit access rules.

Have something to build?

Tell us the problem. We'll come back with a plan, a price, and who'd actually build it.

  • Free scoping call
  • Reply within 1 business day
  • No lock-in