It is the number one request from any pharmacy owner: "look at my purchases and my sales and place the order — or propose it and I'll say yes." It can be done, it works very well, and half of what people picture is the wrong way round.
Why the model must not do the sums
A language model looking at a sales table will give you a number. The problem is it will give you a different one tomorrow from the same data, it will not tell you when it got it wrong, and it charges you every time. For a 200-line daily order that is expensive, slow and irreproducible — and when it fails you will not know why.
Replenishment is a solved problem and fits in four variables: mean demand, supplier lead time, demand variability and target cover. It is deterministic, you can check it in a spreadsheet, and it costs nothing.
| The arithmetic does this | The AI does this |
|---|---|
| How many units are missing | Why demand is spiking this month |
| Reorder point and safety stock | "This is seasonal — watch it in October" |
| Days of cover | Drafting the claim to the lab |
| What is overstocked or about to expire | Summarising the order in three lines |
| Which lines do not move | Proposing an alternative during a shortage |
What data it needs — and where the line is
Per line: current stock, sales per period (weekly is usually enough), what is already on order, the pack size, and the flags that change the decision: sale-or-return, shortage, controlled drug, short expiry. All of it comes out of your PMR or stock system.
Try it
Below is a sample catalogue with the situations that actually turn up in a pharmacy. Change the supplier lead time or the target cover and watch everything move. The data is fictional and the calculation runs in your browser.
What the agent never orders on its own
- Controlled drugs. Schedule 2 and 3 carry requisition and register obligations. Slipping them into an automatic order is not a stock problem: it is a legal one with your name on it.
- Lines in shortage. Ordering more does not make them arrive sooner. What helps is flagging it once and finding an alternative, not repeating the line every day.
- Sale-or-return and consignment stock. The supplier replaces it; ordering it means paying for something you already have.
- What does not move. With history and zero sales, the answer is not to replenish: it is to check whether it should go back. An agent that restocks what nobody buys is a machine for tying up cash.
- Anything behaving erratically. The number exists but it is not trustworthy, and the agent has to say so rather than hide it.
How approval works
You asked for "propose it and I'll say yes". That is exactly the right design: the agent proposes and a person approves. With one detail that decides whether the system is safe or only looks it:
It is the same rule the SEO gap agent on this site follows: it proposes and never publishes. The domain changes, the shape does not. A well-built agent takes the review from two hundred lines down to four; it does not remove it.
The suggestions the AI genuinely adds
Once the arithmetic has done its part, some things only surface by reading the whole picture — and there a model earns its place:
- "Hay fever lines have climbed three weeks running: the season has started earlier than last year."
- "These four lines are from the same supplier; grouping them clears the carriage-paid threshold."
- "This generic has not moved since the other manufacturer's version came in."
- "Of what is going short-dated, this one will shift on promotion and this one should go back now."
- "The line in shortage has two alternatives with the same composition — here is the note for the prescriber."
All of those are sentences, not quantities. That is the line.
How to actually build it
- Export from your stock system: stock, weekly sales, what is on order. Aggregated by product, without a single patient field.
- Calculate the proposal with arithmetic, not a model. This is the part that has to be boring and reproducible.
- Send the AI only the summary — lines, totals, what needs review — and ask for the commentary. Do not send the whole table: you are paying for tokens that add nothing.
- Show it for approval, including what is not being ordered.
- Keep what you approved. Without a history of proposals against real orders you cannot tell whether the agent is any good, and in three months you will not remember.
The formula, in full and with no AI
Before putting a model anywhere near this, one thing has to be clear: the quantity you order is arithmetic. There is nothing to reason about. And if the model calculates it, you have introduced the possibility of being wrong about the one thing that cannot be wrong.
function suggest(p, coverDays) {
const dailySales = p.monthly_sales / 30;
const leadTimeDays = p.supplier_days ?? 2;
// What is needed to cover the period + how long it takes to arrive
const target = dailySales * (coverDays + leadTimeDays);
// Minus what you have and what is already on its way
let units = target - p.stock - (p.in_transit || 0);
// And rounded to the pack size: if it comes in sixes, order sixes
units = Math.ceil(units / p.pack_size) * p.pack_size;
return Math.max(0, units);
}in_transit and
leadTimeDays. Without the first, every run re-orders what you ordered
yesterday and has not arrived — and that is how you accumulate three times the target stock with
nothing appearing to go wrong. Without the second, the order covers you up to exactly the day
you run out, which is one day late.
pack_size is what separates a theoretical list from an order you can
actually send. "Order 7 units" of something that comes in boxes of six is not an
instruction: it is a number somebody has to translate by hand, and once 40 lines need
translating by hand the agent has saved nothing.
The data you need, and what happens when each piece is missing
| Field | If you do not have it |
|---|---|
stock | There is no agent. It is the absolute minimum |
monthly_sales | You can fall back on the system's fixed minimum, but you order the same in August as in January |
in_transit | You double-order. The most expensive failure on the list |
pack_size | The list cannot be sent without hand-editing it |
expiry | You can over-order exactly what is going to expire unsold |
out_of_stock_flag | You order, over and over, something nobody is going to supply |
The six cases that break the formula
This is where it stops being arithmetic. What all of these have in common is that the number the formula produces is correct and the decision is bad:
-
The spike that does not repeat
A care home took 40 units on Tuesday. The monthly average shoots up and the agent orders for demand that does not exist. You catch it by comparing last month's sales with the three before: more than double, and the line gets flagged.
-
Seasonality
Cold and flu remedies in May. The annual average says they sell; reality says they will not until November. Without month-by-month history there is no way to see it, and with it the rule is simple: compare against the same month last year, not the average.
-
The supply shortage
You order, it does not arrive, and the next day you order again because stock is still low. The agent loops against the real world. Suggest 0, with the reason written, and keep the line visible so somebody can decide whether to find a substitute.
-
What expires before it sells
You have 20 units expiring in two months and you sell 4 a month. Ordering more is throwing money away twice. If you have expiry dates, this rule alone pays for the work of building the agent.
-
Promotions and new lines
A product you have just introduced has no history: the formula orders 0 and it sells out on day one. A product on promotion will sell more than its average says. Both cases need a human decision, and all the agent can do is flag them.
-
Controlled items
Controlled drugs and anything with its own paperwork. Always suggest 0, and not out of caution: that order simply is not placed this way.
The approval screen, which IS the product
Everything above ends up on a screen. And that screen is literally what decides whether the agent gets used: it can have the best calculation in the world and be worthless if approving costs more than placing the order by hand.
| It has to show | Why |
|---|---|
| The lines with quantities and the total | Approving without seeing the total is not approving |
| Where each quantity comes from | "12 units — 4 left, you sell 71/month, covers 14 days". Without this, by the third time it gets approved unread |
| The flagged lines, at the top and in another colour | They are the only ones needing your judgement. Mixed in with the 38 good ones, they do not get looked at |
| Being able to change a quantity without redoing everything | If correcting means starting over, the order gets placed by hand |
| An approve button that cannot be pressed while flagged lines are unresolved | It is the one place where a barrier is justified |
What the AI is actually for here
With the formula in code and the six flags in code, the fair question is what is left for the model. Three things are left, and they are the three that cannot be written as rules:
| The code does | The model does |
|---|---|
| Calculate each quantity | Explain an odd line in one sentence you can read at a glance |
| Flag the six cases | Investigate why something happened: "why did this run out?", chaining queries |
| Apply caps and pack sizes | Understand what you ask in plain language: "just do the fridge items, for two weeks" |
How to put it into service without risking anything
| Phase | What you do | How long |
|---|---|---|
| 1. Dry run | Generate the proposal and compare it with the order you were going to place | Two weeks |
| 2. Assisted | Start from the proposal and correct it. Write down what you correct | A month |
| 3. Approved | You review it and approve it unchanged nearly every time | Once the corrections drop off by themselves |
How to tell whether it is working
An ordering agent is judged on four numbers, and none of them is "how many orders it prepared". All four come from comparing what was approved with what happened afterwards:
| Number | What it says | What to do if it is bad |
|---|---|---|
| Lines you correct | How far the formula is from your judgement | Every repeated correction is a missing rule. Write it |
| Stockouts | Whether you are running short | Raise the cover days or the lead time |
| Stock expiring unsold | Whether you are overshooting | Lower the cover, and check the expiry rule |
| Minutes per order | Whether it actually saves time | If it does not fall, the problem is the approval screen, not the maths |
Before moving on
- I can say which part of the agent is arithmetic and which is AI.
- I can name five things it must not order on its own.
- I understand why "approve all" has to be able to disappear.
- I know what I export and what never leaves the stock system.
- I have a small category to start with in parallel.