Here is the part of PrestaShop that quietly runs your whole back office and almost nobody sets up on purpose: the order status. Every order is sitting in exactly one status at any moment, and that single value decides what the customer sees in their account, whether stock has been deducted, whether an invoice exists, and which email just landed in their inbox. Leave the defaults untouched and a busy shop limps along on guesswork. Shape the statuses around how you actually fulfil orders, and the same back office starts telling your team what to do next, and telling your customers what's happening before they email to ask. This guide is about getting there on PrestaShop specifically: what every default status really triggers, how to build your own in Shop Parameters → Statuses, what lives in the ps_order_state table underneath, and how to automate transitions so the people on your team stop being a database trigger with a pulse.
Last updated: June 2026.
What an order status actually is in PrestaShop
An order status in PrestaShop is not just a label. Each status is a row in the ps_order_state table (with its translatable name in ps_order_state_lang), and that row carries a set of boolean flags that tell the core what to do when an order enters this state: generate an invoice or not, mark the order paid, mark it shipped, expose the delivery PDF, send an email. (Stock deduction is not one of these flags. PrestaShop reduces available stock when the order is validated/created, not when a status flag fires; more on that below.) The order's current status lives in ps_orders.current_state, and on top of that PrestaShop writes a new line to ps_order_history every time the status changes, so the order also keeps a full, timestamped audit trail of everywhere it has been. The "current status" you see in the admin is the value in current_state, with ps_order_history recording how it got there.
The practical upshot for a store owner: the status list is where your operational logic lives. Get the flags right once and PrestaShop enforces them on every order forever. No member of staff has to remember "we generate the invoice when payment clears, not before." Get them wrong, and you can quietly generate invoices for orders nobody has paid for. We come back to that landmine below.
The default order statuses, and what each one really triggers
PrestaShop ships with a predefined set of statuses. Most merchants treat them as decoration; in fact each one fires real actions in the database the instant an order lands on it. Here is what's actually happening under the hood.
Awaiting check payment
Applied when a customer chooses check payment at checkout. The order exists but no money has arrived. The order is validated at placement, so available stock is handled then according to your stock settings, not separately "reserved" by this status. No invoice is generated, and the order waits here until you manually confirm the cheque has cleared. Nothing moves on its own, that's by design.
Awaiting bank wire payment
The bank-transfer equivalent. The customer is shown your IBAN/BIC and an order reference, available stock is handled at order validation per your stock settings, and the order parks here until you mark it paid, usually one to three business days later when the transfer lands. This is the status most worth automating away (more on that in the automation section), because confirming wires by hand is pure repetitive admin.
Payment accepted
This is the pivotal one. When an order reaches Payment accepted, PrestaShop:
- Generates the invoice (if the status carries the invoice flag. It does by default);
- Marks the order as paid in the database;
- Sends the status email to the customer.
Note what this status does not do: it doesn't deduct stock. Available stock is reduced when the order is created/validated at checkout, which happens even for orders sitting in Awaiting payment, so the inventory move has usually already occurred before the order ever reaches Payment accepted.
For gateway payments, PayPal, Stripe, Mollie, the payment module sets this status automatically the moment the gateway confirms the charge. You don't touch it. That automatic, code-driven transition is exactly the behaviour you want to replicate for your manual payment methods.
Processing in progress / Preparation in progress
An optional staging status that says "the warehouse is picking and packing." It fires no automatic actions. Its entire value is human coordination. Some shops skip it; high-volume shops find it indispensable for telling a packer's queue apart from an unpaid-orders queue at a glance.
Shipped
Applied when the parcel leaves you. It triggers a shipping-confirmation email (including the tracking number, if one is present) and makes the delivery slip available. The sequence matters: enter the tracking number in the order's Carriers section before you flip the status to Shipped, or the email goes out without the tracking link and you get the "where's my parcel?" message anyway.
Delivered
Marks that the customer has the goods. Set manually or by a carrier-API integration. In the EU it's the moment that starts the 14-day distance-selling return window, so accurate delivered dates aren't cosmetic; they're the clock your returns policy runs on.
Canceled
Cancelling an order can re-increase available stock for the order's products (through PrestaShop's normal stock-management behaviour, since the stock was already decremented at order placement), and the customer gets a cancellation email. Two things it does not do automatically: refund the money (you process that separately), and delete any invoice that was already generated. If an invoice exists, issue a credit slip rather than pretending the invoice never happened. Your accountant will thank you.
Refunded
Applied once a refund is processed. A credit slip isn't created just by applying this status label, it's generated through the refund workflow (the Standard refund / Partial refund / Return products panel) when you tick the option to generate one. Whether the items are returned to sellable stock likewise depends on how you handled the refund in that panel.
Payment error
Set when a payment attempt fails, declined card, insufficient funds, a failed 3-D Secure step. No stock is deducted, no invoice is generated. Whether the customer hears about it depends entirely on the payment module.
On backorder (paid / not paid)
For shops that accept orders they can't ship yet. The order is captured but fulfilment waits for restock. The "paid" and "not paid" variants let you keep the payment state honest while the goods are en route to you.
Reading the statuses in the database
You'll never need to touch SQL to run a shop, but knowing the shape of the data makes the admin screens make sense, and it's what any automation hangs off. The three tables that matter:
| Table | What it holds |
|---|---|
| ps_order_state | One row per status, with the behaviour flags (paid, shipped, invoice, send_email, logable, delivery, color, etc.). |
| ps_order_state_lang | The translated status name and the template name of the email that fires, per language. |
| ps_orders | The order itself, including current_state, the order's current status value. |
| ps_order_history | One row every time an order changes status, the timestamped trail of how the order reached its current state. |
Because each status change appends a new ps_order_history row (alongside updating current_state on the order), the history is never a destructive overwrite, it's an audit trail. That's why you can always see where an order has been, and why a careless bulk SQL update on order states is dangerous: write straight to the tables and you bypass every flag, email and stock movement the core would normally fire. The supported way to move an order in code is through the order object, not raw UPDATEs. We get into the right hooks for that in the developer section.
Creating custom order statuses

Go to Shop Parameters → Statuses (on PrestaShop 1.6 this lived under Orders → Statuses). Click Add new order status and you're configuring one of those ps_order_state rows from the UI. The fields that matter:
- Status name: what your team reads in the order list. "Waiting for supplier" beats "Status 7" every time. Clarity here removes a category of "what does this one mean again?" questions.
- Icon and colour: the visual cue in the order list. Pick a convention and hold it: green for good, orange for pending, red for problems. A merchant should be able to triage the order list by colour from across the room.
- Behavioural flags. This is the real engine:
- Consider the associated order as validated: counts the order toward your sales statistics.
- Allow a customer to download and view PDF versions of their invoices: controls invoice visibility in the customer's account.
- Set the order as paid: tells the rest of PrestaShop money has been received.
- Show delivery PDF / Set the order as shipped: marks the goods as gone and exposes the delivery slip.
- Generate invoice: creates the invoice PDF at this transition.
- Send an email to the customer when the order's status has changed: the trigger that turns a status into a customer notification.
- Email template: which message goes out when the email flag is on.
The "So what?" of those flags: they're the difference between a status that's just a colour and one that quietly does your bookkeeping. Set them deliberately and PrestaShop does the right thing on every future order without anyone supervising it.
Custom statuses we see most often
From working with PrestaShop shops day to day, these are the custom statuses that earn their place:
- Awaiting supplier: paid, but an item has to be ordered in. No customer email. It's an internal flag, not news the customer needs.
- Ready for pickup: for click-and-collect. Email on, telling the customer the address, the hours, and what to bring.
- Partially shipped: some items gone, some still coming. Email the customer what shipped and what's outstanding.
- Under review: high-value or fraud-flagged orders held for a manual look. No customer email.
- Awaiting customer response: you've contacted them (address fix, product question) and the order is paused on their reply.
- Return requested / Return received: the two halves of an RMA. The customer has asked, and later the parcel has landed back with you for inspection.
Email triggers for each status
When a status has its email flag on, PrestaShop sends the customer the template named against that status. The templates live in /mails/LANGUAGE_CODE/ as paired HTML and TXT files (the plain-text version is the fallback for clients that block HTML, so both have to exist).
Wiring up a custom status email
Turn on email for a custom status and you're on the hook for the template, or the send fails silently into the logs. To wire it properly:
- Create the matching HTML and TXT files in every language folder under /mails/, the template name has to line up with what the status expects.
- Reuse the variable placeholders the core already exposes: {firstname}, {lastname}, {order_name}, {id_order}, {shipping_number} for tracking, and the rest.
- Style the look once in Design → Email Theme rather than hand-editing every file.
If you'd rather not author templates at all, the genuinely common case is simply re-sending an order email a customer lost. PrestaShop can resend the existing confirmation straight from the order page. We walk through exactly where that button is in how to resend an order confirmation email.
Sending the right emails, not all of them
- Don't over-email. Customers don't want a message for every internal hop. "Preparation in progress" is for your team, not their inbox. Reserve emails for the moments they actually care about: payment confirmed, shipped (with tracking), delivered.
- Make each email actionable. The shipped email carries the tracking number and link; the ready-for-pickup email carries the address, hours and what to bring. An email with no next step just generates a reply.
- Set expectations. On a backorder, tell them the wait. A vague "your order status changed" with no detail is a support ticket waiting to be raised.
Automating status changes
Hand-managing statuses is fine at ten or twenty orders a day. Past that, every manual transition is time you're paying for and a chance to forget a step. The opportunities, in rough order of payoff:
Payment confirmation
Card gateways already automate this, PayPal, Stripe and Mollie push the order to Payment accepted on confirmation, with no human involved. The gap is bank transfers and cheques, where you're left with manual confirmation, a bank-feed integration that matches incoming transfers to orders by reference, or a scheduled check against a gateway webhook. Closing that gap is usually the single biggest time win in the whole order flow.
Shipping
A carrier-integration module (Colissimo, DHL, Mondial Relay, and the like) should flip the order to Shipped and write the tracking number the moment you print the label. If yours isn't doing that, the setting to enable it almost always exists in the module, it's just off by default.
Delivery confirmation
Carrier APIs can report delivery. A scheduled job that polls the API daily and moves confirmed parcels to Delivered keeps that EU return clock honest without anyone watching tracking pages. This is classic cron-job territory. Set it once, let it run while you sleep.
Rule-based workflow automation
For the "if this, then that" logic the core doesn't cover, if the payment method is bank transfer and the total is over 500 EUR, set Under review and ping the admin, you want rule-based status automation rather than custom code that breaks at the next upgrade. That's the kind of conditional workflow our team builds modules for at mypresta.rocks: define the conditions and actions from the back office, and PrestaShop routes every order down the right path on its own. The benefit isn't "automation" as a buzzword. It's that the bottleneck stops being a person who has to be at their desk.
Order status hooks for developers
If you're building the automation yourself, PrestaShop fires hooks around every status change so you never have to write to the tables directly:
- actionOrderStatusUpdate: fires as a status change is being applied. It's a notification-style hook, so treat it as the place to run your own validation and module logic around a transition rather than as a guaranteed, core-supported way to cleanly block one.
- actionOrderStatusPostUpdate: fires after the change is committed. The place for follow-up work like syncing the order to an ERP or a shipping platform.
- actionOrderHistoryAddAfter: fires after a new ps_order_history row is written, handy for your own logging and audit trails.
A typical pattern: on the move to Shipped, post a webhook to your fulfilment partner's API; on the move to Delivered, queue a review-request email for seven days later. Because you're moving the order through its object and listening on these hooks, every flag, email and stock movement the core owns still fires correctly. Which is exactly why you reach for hooks instead of a raw SQL UPDATE on ps_order_state.
Editing and correcting orders along the way
Statuses describe where an order is; just as often you need to change what's in it, fix a quantity, swap an address, apply a discount after the fact. PrestaShop lets you edit a placed order from its detail page, with some real limits once an invoice exists. The full walkthrough of what's safe to change and when is in how to edit an order after it has been placed, and the broader day-to-day of working the order list (filtering, bulk actions, statuses in context) is covered in our complete guide to managing orders in PrestaShop.
Handling partial shipments
Partial shipments are the hardest workflow PrestaShop's defaults handle. The core quietly assumes one order equals one shipment: native partial-shipment support is limited, and delivery slips are order-level documents, PrestaShop doesn't cleanly model item-level partial shipments out of the box. A workable manual approach looks like this:
- Create a Partially shipped custom status;
- Record the tracking number for the items going out now;
- When the rest is ready, ship it under a new tracking number;
- Only move to Shipped once everything has left.
The friction point: the default order detail page doesn't natively track which items went in which parcel, and generating a per-shipment delivery document for just the items in that parcel isn't a native operation. Shops that split orders regularly need a custom workflow or a module for true per-item shipment tracking and documents on top of the core, otherwise the order history tells you something shipped, but not what.
B2B order workflows
B2B fulfilment doesn't follow the B2C path, and the status list is where you encode the difference:
- Quote and approval: orders that need sign-off before payment, modelled as "Quote sent" → "Quote approved" → "Awaiting payment" → "Payment accepted".
- Purchase orders: Net 30/60/90 terms mean fulfilment and payment collection are two separate clocks; your statuses have to track both without conflating them.
- Partial shipments: multi-item B2B orders with mixed lead times lean on the partial-shipment setup above as a matter of routine, not exception.
- Minimum-order enforcement: orders under threshold can route through "Under review" before anyone picks them.
Reporting on your statuses
Because every transition is a timestamped row in ps_order_history, your status data is your operational reporting, for free, if you look at it:
- Time in each status: how long orders sit in Payment accepted before Shipped is, literally, your fulfilment speed.
- Status distribution: a swelling pile in "Awaiting supplier" is a supply problem showing up before your supplier admits it.
- Cancellation by stage: if orders die disproportionately at one status, that stage of your process has a leak.
- Full lifecycle: placement to delivery, tracked over time, tells you whether you're getting faster or quietly slower.
Common mistakes to avoid
- Too many statuses. Past a dozen or so, nobody remembers them and the list stops triaging anything. If your team can't recite them, you have too many.
- Unclear names. "Status A" and "Status B" tell nobody anything. Name them for what they mean.
- A custom status with email on but no template. That's the silent-failure case, the send dies in the logs and the customer hears nothing.
- Untrained staff. The cleanest workflow is useless if the team doesn't know when to apply each status. A one-page reference chart pays for itself.
- The invoice-flag landmine. Switch on Generate invoice for a status that isn't payment-confirmed and you'll start minting invoices for orders nobody has paid for, a real accounting and tax mess to unwind. Double-check that flag before you save.
A note on test orders
While you're building all this out, you'll place test orders to watch the statuses and emails fire, and every one of them pollutes your stats, your invoice sequence and your ps_order_history if you leave it. Clear them out before you go live; the safe way to remove them (and why you shouldn't just DELETE the rows) is in how to delete test orders in PrestaShop.
Frequently asked questions
Where do I add a custom order status in PrestaShop?
Go to Shop Parameters → Statuses, then click Add new order status. On PrestaShop 1.6 the same screen lived under Orders → Statuses. Each status you create is a row in the ps_order_state table, and the checkboxes on that form (paid, shipped, generate invoice, send email) are the behaviour flags the core fires whenever an order lands on the status.
Does changing an order to "Payment accepted" deduct stock?
No. Available stock is reduced when the order is validated/created at checkout, which happens even while the order sits in "Awaiting payment", not when the paid flag fires. By the time an order reaches Payment accepted, the inventory move has usually already occurred. The paid status generates the invoice, marks the order paid, and sends the status email; it doesn't touch stock.
Why did my custom status email never send?
Almost always a missing template. When you switch on the email flag for a status, PrestaShop looks for HTML and TXT files named to match it under /mails/LANGUAGE_CODE/ in every language folder. If those files don't exist, the send fails silently into the logs and the customer hears nothing. Create the paired HTML/TXT templates for each language before relying on the email.
Can I move an order's status from code without writing SQL?
Yes, and you should. Move the order through its order object so every flag, email and stock movement still fires, and listen on the hooks PrestaShop provides: actionOrderStatusUpdate (as the change is applied), actionOrderStatusPostUpdate (after commit, good for ERP/shipping sync), and actionOrderHistoryAddAfter (after the history row is written). A raw UPDATE on ps_order_state bypasses all of that and is how you end up with invoices and emails out of sync.
How do I avoid generating invoices for unpaid orders?
Watch the Generate invoice flag. If you switch it on for a status that isn't payment-confirmed, PrestaShop will mint invoices for orders nobody has paid for, a real accounting and tax mess to unwind. Keep invoice generation tied to your payment-confirmed status only, and double-check that checkbox before you save a new status.
Can I automate status changes for bank transfers and conditional rules?
Card gateways already push orders to Payment accepted automatically; the gap is bank transfers, cheques, and conditional logic like "if bank transfer and total over 500 EUR, set Under review." That kind of rule-based workflow is exactly what our order workflow automation module handles from the back office, so the bottleneck stops being a person who has to be at their desk to flip statuses.
A well-shaped order status workflow is the spine of a calm back office: the team knows what to do next, the customer hears the right thing at the right moment, and the accounting stays clean because the flags, not a tired human at 6pm, decide when an invoice exists and when stock moves. Plan the workflow before you wire it up, because once orders are flowing through your statuses, changing them mid-stream is far more disruptive than getting them right the first time. When you outgrow manual transitions, rule-based automation from mypresta.rocks takes the repetitive status-flipping off your team's plate so every order follows the same path from placement to delivery, every time, whether anyone's watching or not.