Shows a green "Available, order by HH:MM, delivery {tomorrow / weekday}" box with a live countdown, in the product summary header (next to the price) and on the cart. Once the cutoff passes it flips to "Available, order by {tomorrow / weekday} HH:MM, delivery {day}". By default the box only appears on products that can actually be ordered (in stock, or backorder-enabled).
To keep load off the PrestaShop server, the delivery estimate is fetched client-side:
the visitor's browser calls the companion FastAPI service directly
(GET /api/v1/delivery/estimate, CORS-enabled). PrestaShop only outputs a tiny placeholder
- a small JS config — it does not call the API on every page render.
Compatible with PrestaShop 1.7.x and 8.x.
Example (Polish): ✓ Dostępny. Zamów do 14:00, jutro u Ciebie* ⟨01:59:59⟩
Product page (server) ── renders placeholder + window.scmou config (no API call)
Browser (countdown.js) ── GET {API}/api/v1/delivery/estimate ──► FastAPI (CORS)
└─ builds the text, shows the green box, ticks the countdown every second
└─ at zero (optional): refetch → roll over to the next order window
- Client call mode (default) — the browser talks to the API. Least load on PrestaShop. The API URL must be reachable from customers' browsers (a public URL).
- Server call mode (fallback) — the browser calls the module's own AJAX controller
(
controllers/front/ajax.php), which proxies to the API in PHP (with a short cache). Use this when the API cannot be exposed publicly.
- Ensure the FastAPI service is running and reachable (publicly, for client mode).
- Zip the
scmorderuntil/folder and upload it in Back Office → Modules → Upload a module (or copy it into<prestashop>/modules/and install from the list). - Open Configure and set at least the API base URL. Everything else has sensible defaults.
| Setting | Key | Default | Notes |
|---|---|---|---|
| API base URL | SCMOU_API_URL |
http://localhost:8000 |
Client mode: must be browser-reachable |
| Call mode | SCMOU_CALL_MODE |
client |
client (browser) or server (PHP proxy) |
| API key | SCMOU_API_KEY |
— | X-API-Key; in client mode it's exposed — prefer an open API |
| Placement (product) | SCMOU_PLACEMENT |
price_block |
price_block (summary header) or additional_info (under price) |
| Cutoff time | SCMOU_CUTOFF |
— | HH:MM; empty = API default |
| Delivery offset | SCMOU_DELIVERY_OFFSET |
— | working days ship→delivery; empty = API default |
| API message locale | SCMOU_LOCALE |
auto | '' (page language) / en / pl — only the API request's locale param; the on-screen wording comes from PrestaShop translations |
| Text — open (per language) | SCMOU_TPL_OPEN |
built-in | placeholders {cutoff} {shipwhen} {when} {delivery} |
| Text — closed (per language) | SCMOU_TPL_CLOSED |
built-in | placeholders {cutoff} {shipwhen} {when} {delivery} |
| Countdown label (per language) | SCMOU_LABEL_COUNTDOWN |
— | prefix before the timer, e.g. "still" |
Footnote * (per language) |
SCMOU_FOOTNOTE |
* on working days / * w dni robocze |
small note under the box; empty = hidden |
| Cache TTL (server mode) | SCMOU_CACHE_TTL |
120 |
seconds; 0 disables |
| API timeout (server mode) | SCMOU_TIMEOUT |
3 |
seconds |
| Show on product / cart | SCMOU_SHOW_PRODUCT / SCMOU_SHOW_CART |
on | |
| Only on available products | SCMOU_SHOW_ONLY_AVAILABLE |
on | Product page: hide the box on out-of-stock products (unless backorders are allowed) |
| Auto-refresh at zero | SCMOU_REFRESH |
on | refetch when the timer reaches zero |
| Update source | SCMOU_UPDATE_REPO |
SecCodeSmith/Order-Until-PrestaShop-module |
GitHub owner/repo the self-updater reads releases from |
| Update token | SCMOU_UPDATE_TOKEN |
— | optional GitHub token (private repo / rate limits) |
The wording is built in the browser from the API's structured fields, so you can fully customise it. Placeholders:
{cutoff}— the order cutoff time, e.g.14:00{when}—jutro/tomorrowif delivery is the next day, otherwise the weekday (Polish uses the correct accusative:we wtorek,w środę, …){delivery}— delivery date asDD.MM{shipwhen}—today/tomorrow/ weekday for the ship day
Built-in defaults:
- Open (before today's cutoff): EN
Available, order by {cutoff}, delivery {when}*· PLDostępny, zamów do {cutoff}, dostawa {when}*→ e.g. "Available, order by 15:00, delivery tomorrow"*. - Closed (after the cutoff — ships the next working day): EN
Available, order by {shipwhen} {cutoff}, delivery {when}*· PLDostępny, zamów {shipwhen} do {cutoff}, dostawa {when}*→ e.g. "Available, order by tomorrow 15:00, delivery on Wednesday"*.
Any placeholder the running script cannot fill is stripped rather than shown raw, so the
customer never sees a literal {token} — but if you edited a template or upgraded the module
and still see raw {shipwhen}/{when}, an old countdown.js is cached: clear the
PrestaShop cache and, under Advanced Parameters → Performance, the "Combine, Compress and
Cache (CCC)" JS cache (a version bump also busts the browser cache).
The widget shows a single line only. (A second detail line existed in earlier versions but was removed — all wording now lives in the first line.)
The widget is fully multi-language — the same page renders correct wording in each shop language:
- Sentence text: the per-language
SCMOU_TPL_OPEN/SCMOU_TPL_CLOSEDfields (Back Office, one value per language). Leave empty to use the translatable default. - Day/date phrases (
today,tomorrow, weekday names, theddays unit, default templates): these live in the module as$this->l()strings and are translated per language via PrestaShop's translation system. They are injected intowindow.scmou.labelsfor the current page language, so the JS is language-agnostic.- A Polish translation ships in
translations/pl.php(with correct grammar, e.g.we wtorek,w środę). - For any other language, translate them in Back Office → International → Translations
→ Installed modules translations (type: Modules, select the language), or drop a
translations/<iso>.phpfile.
- A Polish translation ships in
Example — same product, two languages:
/men/1-1-hummingbird-printed-t-shirt.html → "…delivery tomorrow*"
/pl/men/1-1-hummingbird-printed-t-shirt.html → "…jutro u Ciebie*"
- Product summary header (default): hook
displayProductPriceBlock— renders once at the top of the price block, right by the price. Gated to the product detail page so it does not leak into product-list miniatures, the add-to-cart modal or cart line items (that hook fires in all of those). - Under the price block: switch Placement to
additional_info(hookdisplayProductAdditionalInfo). - Cart: hook
displayReassurance— renders inside the order-summary column, next to the "Proceed to checkout" button. Gated to the cart controller and to a single render per page. - Anywhere:
WidgetInterface— drop{widget name='scmorderuntil'}into any template.
British Racing Green (#004225) box by default. Override views/css/countdown.css or target: .scmou-box
(.scmou-open / .scmou-closed), .scmou-check, .scmou-text, .scmou-badge
(the countdown pill). Dark-mode aware.
The module can update itself from its GitHub releases — no manual re-upload:
- Modules → SCM Order Until → Configure → "Module updates".
- Click Check for updates. It queries the latest release of
SecCodeSmith/Order-Until-PrestaShop-module(configurable viaSCMOU_UPDATE_REPO). - If a newer version exists, click Download & install — the module downloads the
release's
scmorderuntil.zip, verifies it, extracts it overmodules/scmorderuntil/, and reloads so PrestaShop runs the upgrade scripts. The DB upgrade finishes on that reload.
Safeguards: the asset URL must be on github.com under the configured repo's
releases/download/ path; the zip must contain scmorderuntil/scmorderuntil.php and no
entry may escape the scmorderuntil/ folder (Zip-Slip guard). Set SCMOU_UPDATE_TOKEN for a
private repo or to avoid GitHub's unauthenticated API rate limit. Requires PHP's zip
extension and write access to modules/.
scmorderuntil/
├─ scmorderuntil.php module (install, hooks, widget, config, server-proxy)
├─ config.xml
├─ logo.png module icon shown in the Back Office module list
├─ controllers/front/ajax.php same-origin proxy (server call mode only)
├─ views/templates/hook/countdown.tpl placeholder markup (no server-side API call)
├─ views/js/countdown.js client-side fetch + text builder + live countdown
├─ views/css/countdown.css British Racing Green box + countdown pill (light/dark)
├─ upgrade/upgrade-1.3.0.php hook migration for already-installed shops
├─ translations/pl.php Polish translations of the widget phrases
└─ index.php (+ per-folder stubs)
The module's own version is the single source of truth — $this->version in
scmorderuntil.php (mirrored in config.xml). On every push to main/master, CI
(.github/workflows/ci.yml, job tag-and-release):
- Reads that version and bumps the patch (
scripts/bump_version.py --write), writing the new value back intoscmorderuntil.php+config.xml. - Commits the bump back to the branch (
[skip ci], so it does not loop) and creates the matchingvX.Y.Zgit tag. - Packages
scmorderuntil.zipand publishes a GitHub Release with it attached.
So 1.3.0 in the module ships as v1.3.1. To cut a minor/major release, edit
$this->version in scmorderuntil.php (e.g. to 1.4.0) and push — the next run releases
v1.4.1. A collision guard fails the job if the computed tag already exists.
- If the API is unreachable, the box stays hidden (fails silent) — no broken UI.
- CORS: the API sets
Access-Control-Allow-Origin(configurable viaCORS_ALLOW_ORIGINS). For client mode, keep the API open or restrict it at the network layer rather than with a browser-exposed key. - All the working-day / holiday / cutoff logic lives in the FastAPI service; this module only presents the result.