After reading this guide you will be able to bring the app's stock figures up to date with the ledger the pharmacy
actually keeps, read how current they now are, and know what refreshing does — and does not — change.
Who does this? The Responsible Pharmacist, the Dispensing Technician and the Business Manager can
refresh the positions. The Counter Assistant can read every stock screen but cannot refresh them (the request is
refused with 403 Forbidden). Refreshing needs the app to reach ERPNext; if it cannot, you get a red message and
nothing is changed.
The pharmacy app does not hold the stock ledger — ERPNext does. The screens staff read are built from a copy of
what ERPNext last reported: the stock overview's figures, the low-stock report (C2) and the expiry report (C3) all read
it. That copy is called the positions in these screens, and it is only ever as current as the last refresh. The
stock overview says so explicitly, with the line Last position refresh.
Nothing refreshes it for you. There is no nightly job and no automatic schedule: the figures move when somebody
presses the button. A pharmacy that has not refreshed since Friday is looking at Friday's stock, however much has been
dispensed, sold, delivered or written off since — which is why a delivery recorded yesterday (C1) or a wastage
recorded this morning (C4) will not appear in the figures until somebody refreshes.
Refreshing is safe to repeat. It reads ERPNext and updates the app's own copy; it posts nothing to the ledger
and changes no stock quantity anywhere. Unlike a stock adjustment, pressing it twice does no harm at all.

The stock overview straight after a refresh: the green banner reports how many positions ERPNext returned, the
figures above are rebuilt from them, and Last position refresh has moved to the time you pressed the button.
The refresh reads, for every active stock item, the batch positions ERPNext holds in the pharmacy's two pools —
its Dispensary warehouse and its Counter warehouse — and rebuilds the app's copy of them: the batch, the
expiry date and the quantity of every position ERPNext reports, and the removal of a cached one ERPNext does not.
| What you see | What it tells you |
|---|---|
| "Refreshed 7 stock position(s)." | ERPNext returned that many batch positions — its count, one per item, pool and batch. A position the app held that ERPNext did not report is removed in the same pass (see below), so after a successful refresh this figure and the app's own cached positions agree. |
| Last position refresh now reads a fresh date and time | When the figures were last taken from ERPNext. It reads never until the first refresh. |
| The figures above the button changed | Active items, Cached positions, Expired and Short-dated are all rebuilt from the positions just refreshed. |
Dates render as Django does, for example Sept. 21, 2026, 3:21 p.m.
The refresh also removes what the ledger no longer reports. It does not only add and update: for each item and pool
it has just read successfully, a cached position that ERPNext did not report is deleted. So a batch that has
been consumed, adjusted away or otherwise taken out of the ledger stops counting in the figures, and stops appearing on
the expiry report (C3), as soon as a refresh completes — rather than lingering at its last known quantity.
The careful part is when that deletion happens: only after ERPNext has been read without error, item by item and
pool by pool. A refresh that fails part-way, or cannot reach ERPNext at all, leaves everything it did not manage to
read exactly as it was — it never empties the cache. That is the safety in the button: the app's copy can be stale,
but a failed refresh cannot make it wrong.
Everything that reads the cached positions:
| Screen | What it reads from the cache |
|---|---|
| Stock overview (C2) | Active items, cached positions, expired and short-dated counts, and the low/zero stock panel |
| Low stock (C2) | The quantity each item is holding, checked against its alert level |
| Expiry report (C3) | Every position's batch, expiry date and quantity |
Two things that do not touch the cache, and so never depend on the refresh: a receipt (C1) and an
adjustment (C4) — both post straight to the ERPNext ledger. That is why the figures can disagree with what you have
just done until the positions are refreshed.
| What you see | What it means |
|---|---|
| "Could not refresh stock positions from ERPNext." | The refresh could not reach ERPNext, or ERPNext refused the request. Nothing has been removed or changed by the attempt — the figures are as they were, or whatever part of the refresh had already completed. Press the button again; if it keeps failing, tell your Business Manager. |
| "Refreshed 0 stock position(s)." | ERPNext answered and reported no positions at all for the pharmacy's items in its Dispensary and Counter warehouses. If you expect stock, this is worth raising — it usually means the items or the warehouses do not line up in ERPNext. |
| The count is lower than a number you have seen elsewhere | They count different things. This one is ERPNext's count of batch positions — one per item, pool and batch — while the expiry report lists the cached positions inside the warning band, which is the overview's Expired figure plus its Short-dated figure. |
| The figures still look wrong after a refresh | Then the cache is telling the truth about ERPNext, and the disagreement is with the shelf. That is a stock correction (C4), and worth telling whoever looks after stock. |
| Last position refresh still reads never | No refresh has completed successfully yet — the figures are showing nothing at all. |
| You tried to open the refresh directly in the address bar | The refresh is a button, not a page: its address on its own is refused, and you must go through the button on the stock overview. |
| A button is refused when you press it | Your role does not hold the permission. All four roles can read the stock screens; refreshing is open to three of them. |