"1,247 sold." Two words and a number, sitting under the price — and for a shopper who has never heard of your store, it answers the only question that actually matters: has anyone else trusted this enough to hand over their money? A total-sold counter takes a fact your database already knows and puts it where the buying decision happens. This post is specifically about that number: the cumulative, all-time count of units sold, shown as a static badge on the product page. Not the live "12 people viewing right now" ticker, not the "someone in Munich just bought this" popup — those are different signals with their own jobs, and we link to them below. The all-time counter has one purpose: turn your sales history into proof.
Last updated: June 2026.
What "total products sold" actually means in PrestaShop — and where the number lives

Before you display a sold count, you need to know which number you're showing, because PrestaShop stores several that look similar and mean different things. Getting this wrong is the single most common implementation mistake we see.
| What you want | Where it lives | What it really is |
|---|---|---|
| Units actually sold | ps_order_detail.product_quantity, summed for that product across valid orders | The honest all-time count — every line item ever ordered. |
| Stock on hand | ps_stock_available.quantity (and the legacy ps_product.quantity mirror) | What's left to sell — not what sold. Showing this as "sold" is wrong. |
| "Sales" column in Stats | ps_product_sale (maintained by the ProductSale class as orders are validated) | A cached aggregate PrestaShop maintains for the Best-selling-products dashboard. |
So what does that mean for you? The genuine total-sold figure is a sum over ps_order_detail, joined to ps_orders so you only count orders in a valid state (paid, shipped, delivered) and exclude carts that were abandoned, cancelled, or error-state. A counter that quietly includes cancelled and refunded orders is inflating itself — and the day a customer notices is the day the number stops building trust and starts costing it.
The honest version of that query looks like this — summing line-item quantities, but only across orders PrestaShop has flagged as valid:
SELECT SUM(od.product_quantity) AS units_sold
FROM ps_order_detail od
JOIN ps_orders o ON o.id_order = od.id_order
WHERE od.product_id = :id_product
AND o.valid = 1;
The o.valid flag is the gate that matters: PrestaShop sets it to 1 only when an order reaches a state its merchant marked as "logable" (paid, shipped, delivered), so joining on it is what excludes the abandoned, cancelled and error-state orders. Run that with a GROUP BY on every product page, though, and it becomes the performance problem the post warns about — which is why the cached table below exists.
PrestaShop already exposes a cleaner shortcut: the ps_product_sale table, kept up to date by PrestaShop's ProductSale class whenever an order is validated (the same data behind Stats → Best-selling products in the back office). Reading the cached quantity column there is far lighter than summing order details on every page load — which matters, because the product page is one of the most-hit URLs on your store and you do not want a heavy GROUP BY firing for every visitor.
Why a sold counter works — and where the line is
Social proof works because people look to others when they're deciding under uncertainty. In a physical shop you can see other customers picking things up; online, that signal vanishes unless you put it back. A sold counter is the most quantitative way to do it — a single number that says "this is a path other people have already walked." It tends to do the most work on mid-priced, considered purchases: expensive enough that the buyer wants reassurance, cheap enough that they won't read a dozen reviews first. On impulse-priced items the customer barely pauses; on high-ticket items they research regardless of your badge.
The honest boundary: a sold counter is one signal in a stack, not a magic number. It validates popularity, but it says nothing about whether the product is good — that's the job of product reviews, which carry qualitative weight a raw count can't. The two are complementary: "238 sold · 4.7 stars · 41 reviews" is far stronger than any one of them alone, because it answers both "is this popular?" and "were they happy?" If your store leans on third-party review widgets, it's worth understanding why those alone aren't enough for the trust signal a sold counter pairs with.
When the number helps you — and when to hide it
The thing most guides skip: a sold counter is not unconditionally good. The same badge that converts on one product actively damages another. A counter that's smart about when it shows is worth more than one that blindly prints whatever the database holds.
- Show it once it clears a threshold. "2 sold" reads worse than no counter at all — it whispers "nobody wants this." Set a minimum (we usually start merchants around 25–50 units) and suppress the badge below it. A product earns its counter; it isn't born with one.
- New store, no reviews? This is where it earns its keep. With zero reviews and no brand recognition, even "47 sold" tells a first-time visitor that real people bought and presumably received their orders. It's proof the store works, not just the product.
- Competitive catalogue? When a shopper is comparing the same item across three tabs, the listing showing "3,400 sold" has an edge over the two showing nothing — the count becomes a tiebreaker they didn't know they were looking for.
- Luxury or exclusivity positioning? Turn it off. If the brand story is "limited edition, handcrafted, few of these exist," a five-figure sold count flatly contradicts it. You can't be exclusive and mass-market on the same page.
- Digital products and themes? Be careful. A high count can read as "everyone already has this," which is a buying objection rather than reassurance for design assets that are supposed to make a store distinctive.
How to present the number so it reads as honest
The exact same sales history can build trust or trigger suspicion depending entirely on how you format it. A few rules that hold up across stores:
- Round the big numbers, keep the small ones exact. "247 sold" is credible because it's specific; "12,847 sold" looks suspiciously precise and invites the thought "did they make that up?" Above a few thousand, "12K+ sold" or "10,000+ sold" reads as more honest, not less.
- Consider a time window. "124 sold this month" signals ongoing demand, which is more persuasive than "5,000 sold" accumulated silently over four years. A recent window also rescues older products whose all-time number is modest but whose current momentum is real. In PrestaShop terms that's a date-bounded sum over ps_order_detail joined to the order date — heavier than the cached table, so cache the result.
- Put it in the decision zone. The counter belongs next to the price and the add-to-cart button, where the customer is actually deciding — not buried at the foot of the page or hidden in a tab. On the product template that's the displayProductActions / displayProductPriceBlock hook region; a counter rendered low in displayFooterProduct wastes most of its psychological pull. (For where every element on the page should sit and why, see product page anatomy.)
The all-time counter vs. the live ticker — don't confuse the two
This is the distinction that decides which tool you actually need. A total-sold counter is a stable, cumulative fact — it changes slowly, it's safe to cache, and its message is "many people, over time, chose this." That's this post's subject. It is a different animal from the real-time signals in the same family:
- Live sales and view stats — "9 people bought this today / 23 viewing now" — are about momentum and concurrency, recalculated constantly. That's a separate decision covered in showing live sales and views.
- Recent-purchase popups — the toast that slides in saying "Anna in Kraków just ordered this" — are live sales notifications and sales popups, which manufacture immediacy rather than accumulated weight.
So what's the practical takeaway? The all-time counter and the live ticker answer different objections. "Is this a safe choice?" is answered by accumulated volume; "is this happening now?" is answered by live data. Many stores run both — but reach for the static counter when you want quiet, durable reassurance that doesn't depend on a busy traffic moment to look convincing.
Getting it onto a PrestaShop product page
PrestaShop has no native "total sold" badge on the front office, so the number has to be put there. Three routes, in rising order of effort:
| Route | Effort | Survives upgrades? | Best when… |
|---|---|---|---|
| Template tweak — read the sales figure in a child theme override of product.tpl | Medium (needs a developer) | Fragile — theme edits can break on upgrade and won't handle thresholds or formatting | You have one-off needs and in-house dev time. |
| A hook-based module — renders the counter via a display hook, no theme surgery | Low — install and configure from the back office | Yes — no core or theme files are forked | You want thresholds, formatting and caching handled, and the badge to survive the next 1.7 → 8 → 9 update. |
| Custom development | High | Depends on how it's built | You have very specific logic (per-category rules, A/B variants) and a developer to own it. |
The module route exists precisely because the requirements that make a sold counter safe — exclude cancelled orders, hide low counts, round large ones, read the cached ps_product_sale table instead of summing order details on every load — are fiddly to get right by hand and easy to get wrong. A module built for this hooks into the product page with a single install, keeps the heavy query off the hot path, and is configured entirely from your back office rather than a developer invoice. If a counter is part of a wider plan to make your product pages convert, our product page design and conversion guide maps how it fits alongside the other elements.
A note on performance — because this is where home-grown counters go wrong
The naïve version of a sold counter runs a SELECT SUM(product_quantity) … GROUP BY over ps_order_detail on every single product-page view. On a store with a deep order history and decent traffic, that's a self-inflicted slowdown on your most important page. Two defences, in order of preference: read the pre-aggregated ps_product_sale table (PrestaShop already maintains it through the ProductSale class as orders validate, so the cost is paid once in the background, not per visitor); and if you must compute a custom window, cache the result per product with a sensible TTL so the database is hit occasionally, not constantly. A counter that drags your page-load time down is doing the opposite of helping you sell — slow pages lose the very customers the badge was meant to reassure.
Where the sold counter sits in your wider social-proof stack
A sold counter is one tile in a larger trust picture, and it works hardest when the tiles around it pull the same direction. The quantitative count pairs naturally with the qualitative signals: star ratings and reviews answer "were people happy?"; trust badges answer "is checkout safe?"; and the live signals — sales and views or a recent-purchase popup — answer "is this happening now?" The all-time sold counter's distinct contribution is durable, accumulated weight: proof that the path you're inviting a hesitant shopper down has already been walked, thousands of times, by people who paid and came back for nothing more than the product they wanted.
Get the source number right (real sales, valid orders only), show it only when it flatters the product, format it so it reads as honest, place it where the decision happens, and keep the query off your page-load critical path. Do that, and a fact your database has been quietly recording since your first order starts earning its keep at the exact moment a customer is deciding whether to trust you.
FAQ
Which PrestaShop number is the real "units sold" figure?
It's the sum of product_quantity in ps_order_detail across valid orders — not the stock figure. Stock on hand lives in ps_stock_available.quantity and tells you what's left to sell, which is the opposite of what sold; showing it as "sold" is the most common mistake. PrestaShop also keeps a cached aggregate in ps_product_sale, maintained by its ProductSale class as orders validate, and that's the lighter number to read on a busy product page.
Does the sold counter include cancelled or refunded orders?
It shouldn't, and that's the whole point of joining on o.valid = 1. Without that filter you'd be counting abandoned carts and cancelled orders as sales, inflating the badge. An inflated counter doesn't just exaggerate — the moment a customer senses the number is invented, it flips from a trust signal into a reason to doubt everything else on the page.
Won't a sold counter slow my product pages down?
It will if you compute it the naïve way — a SUM ... GROUP BY over the order history on every single page view, on what is already one of your busiest URLs. Two defences, in order: read the pre-aggregated ps_product_sale table that PrestaShop already maintains in the background, or, if you need a custom time-window figure, compute it once and cache the result per product with a sensible TTL so the database is hit occasionally rather than on every visit.
Should every product show its sold count?
No — a counter is only an asset above a threshold. "2 sold" reads worse than no badge at all, so set a minimum (often 25–50 units) and suppress it below that. Turn it off entirely for luxury or limited-edition positioning, where a high count contradicts the exclusivity story, and be cautious on digital products, where "everyone already has this" can read as an objection rather than reassurance.
Should I show an all-time total or a recent window?
It depends on what you're trying to signal. An all-time total ("5,000 sold") shows durable, accumulated trust; a window ("124 sold this month") shows ongoing demand and can rescue an older product whose lifetime number is modest but whose current momentum is real. The window is heavier to compute because it's a date-bounded sum rather than a cached lookup, so cache the result. Many stores show the framing that flatters the specific product.
Comments
No comments yet. Be the first!
Be the first to ask a question or share useful feedback.
Leave a comment
Share a question, an installation detail, or feedback that could help another reader.