Counter sales against the medicine catalogue, with the stock movement and the money recorded as one thing rather than two.

Each sale carries its line items, so what was handed over is on the record.
The stock movement is part of the sale
Dispensing takes the quantity out of the catalogue inside the same database transaction that writes the sale. Three things follow from that, and they are the reason it is built this way:
- A sale that fails changes nothing. No half-written receipt, no stock missing against a transaction that never completed.
- Reversing a sale puts the stock back. Deleting without returning the quantity is how stock figures quietly become fiction.
- Two people dispensing the last few units at the same moment cannot both succeed, because the stock read holds a lock until the sale is written.
Line items, not a total
Each medicine on a sale is its own row with its quantity, unit price and line total. The name and price are copied onto the line, so a price change next month does not rewrite what somebody paid last month.
Prescribed against dispensed
Because prescriptions and sales are both rows, the two can be compared. The drug usage report puts what was prescribed next to what was actually handed over across a date range.

The gap between the two columns is the part worth reading.
A drug prescribed often and never dispensed here is usually a stock problem, or a script being filled somewhere else. Either way it is worth knowing before the next order goes in.
See it rather than read about it. Dispensing is covered in the user guide.
The user guide walks the whole system in the order you would meet it, and the screenshots page has every screen. The source is on GitHub under the GPL v3.