YS FluentCart Order Statuses

FluentCart × 自訂訂單流程:下生產、排定出貨等自訂狀態,付款後不被覆寫,狀態變更自動寄信,從 More Action 直接變更訂單狀態,附流程報表與防呆。

v0.7.0 Fluent Cart fluentcart 免費下載

FluentCart 內建的訂單狀態只有處理中、保留中、已完成、已取消這幾種,但代購、客製生產、預購型的店家需要的是「已付款 → 下生產 → 排定出貨 → 已出貨」這種自己的流程。這支外掛讓你在後台新增自訂的訂單狀態與出貨狀態——名稱、顏色、說明、付款條件都可設定——FluentCart 的訂單列表(每個自訂狀態都有已存檢視)、「變更出貨狀態」對話框(英文介面為 Change Shipping Status)與顧客的訂單頁都會直接吃到,FluentCart Pro 的進階篩選也會列出自訂訂單狀態。完全不動 FluentCart 核心,全部透過公開的 filter 與 REST 完成。

付款後不會被 FluentCart 覆寫

FluentCart 在收到款項時會把訂單狀態改寫成「處理中」,這段是核心寫死的、沒有 filter。外掛會在付款的同一個流程裡把你的自訂狀態寫回去,並在訂單的活動紀錄留下一行說明;每個狀態都可以在「進階」裡選擇「保留此狀態」或「交由 FluentCart 改成處理中」(限定「僅未付款訂單」的狀態一律交給 FluentCart),也有一個總開關。營收報表看的是付款狀態,所以自訂訂單狀態完全不影響營收數字。

狀態變更自動寄信給顧客

每一個自訂狀態都可以開啟電子郵件通知——顧客一封、管理員副本一封,各自獨立開關。通知直接註冊進 FluentCart 自己的「Email 通知」設定頁:用 FluentCart 的開關、主旨欄、預覽功能與寄件者設定,郵件外框、頁首頁尾也和 FluentCart 內建信件一致;標題與內文段落可以直接在同一個編輯頁填寫,支援 {{order.invoice_no}}、{{order.customer.full_name}} 等 FluentCart 短碼。全部預設關閉,開了才會寄。

訂單狀態在 More Action 裡變更

FluentCart 的後台本身沒有可以自由選擇訂單狀態的控制項——不論訂單是否付款,More Action 裡都只有「標記為完成」、「退回處理中」、「取消訂單」這類固定按鈕(1.6.0 與 1.6.3 實測)。外掛在同一個 More Action 選單最上面加上「變更訂單狀態」(Change Order Status),打開的「更新訂單狀態」(Update Order Status)對話框操作方式和 FluentCart 的「變更出貨狀態」對話框一樣,但只會依流程順序列出這筆訂單可以移動到的狀態;出貨狀態照舊在 FluentCart 的「變更出貨狀態」裡選,自訂的出貨狀態會直接列在裡面。寫入走 FluentCart 自己的訂單 API,活動紀錄、狀態變更事件與品項履行都和手動改狀態完全一樣。

「訂單流程」卡片:看進度、一鍵推進

訂單頁側欄的「訂單流程」(Order workflow)卡片同時顯示訂單狀態與出貨狀態:目前狀態、流程第幾步、不能變更時的原因,以及一鍵推進的「下一步」(Next step)按鈕,下方再提示其他狀態要到 More Action 的哪個選項變更。萬一這個頁面無法從 More Action 使用「變更訂單狀態」(例如日後 FluentCart 改版),卡片會自動改為直接列出可選的訂單狀態;已取消的訂單在 FluentCart 裡沒有「變更出貨狀態」,卡片也會直接列出可改的出貨狀態。

兩種流程樣板,一鍵建立

「訂單狀態」分頁的「建立標準流程」與「工具」分頁的「建立出貨流程(出貨狀態軸)」,都能一鍵建立「下生產 → 排定出貨 → 已出貨」的流程。我們推薦把流程放在出貨狀態,因為 FluentCart 永遠不會自動改寫出貨狀態,而且自訂的出貨狀態會直接出現在 FluentCart 自己的「變更出貨狀態」對話框。樣板不會改動 FluentCart 內建狀態的名稱,也不會覆寫你已經設定好的內容,套用兩次也不會重複。需要的話可以在「工具」分頁開啟「嚴格流程」,讓訂單狀態一次只能前進或後退一步(只管訂單狀態這條流程,出貨狀態不受限;改成已完成、已取消或保留中則隨時可以)。

防呆與紀錄:改設定、改狀態都不怕手滑

還有訂單在用的狀態,儲存時不能移除,畫面會直接說明是哪個狀態、還有幾筆訂單;已儲存的狀態也不能再改代碼,要停用還有訂單在用的狀態時也會先提醒。「搬移訂單」會先確認筆數與去向、不寄信,也不能直接搬到「已完成」、「已取消」這類終點狀態,每筆搬移都會留下紀錄。若有訂單停在已不存在的狀態,設定頁最上方會提醒,並可到「工具」分頁直接搬移。匯入前會先列出會變更的內容,匯入後還能「復原上一次匯入」;兩個人同時編輯設定時,畫面已過時的一方儲存會被擋下並提示重新載入,不會蓋掉對方的修改。訂單頁改成「已完成」或「已取消」一律先確認,「已完成」只出現在已付款訂單;改成「已取消」時,也會像 FluentCart 的「取消訂單」一樣把庫存加回去。

流程報表與卡單清單

「訂單狀態報表」分頁把整條流程攤開來看:各狀態的訂單數與金額(分已付款/未付款)、流程漏斗、每個步驟的平均與最長停留時間、以及在同一步卡超過 N 天的訂單清單,每張表都能下載 CSV(Excel 直接開中文不亂碼)。可選每日摘要信。訂單列表多了每個自訂狀態的已存檢視,訂單頁的「狀態歷程」把兩軸合併在同一條時間軸上,最新的變更排在最前面。

相容性與更新

FluentCart 1.6.0 起可用(1.6.0 與 1.6.3 都實測通過),PHP 7.4 以上,在 PHP 8.4、8.5 上也不會產生棄用警告。設定可整份匯出/匯入 JSON。停用外掛不會改動任何訂單資料。已內建 YS Plugin Hub 更新機制,安裝後可在 WordPress 外掛頁直接收到新版通知並一鍵更新。介面提供英文與繁體中文。

功能特色

自訂狀態與一鍵流程樣板

名稱、顏色、說明、排序與付款條件都可設定;一鍵建立「下生產 → 排定出貨 → 已出貨」,可放在訂單狀態或出貨狀態。

付款後保留自訂狀態

FluentCart 收款時會改寫成「處理中」,外掛自動寫回並在活動紀錄留下說明。

狀態變更 email 通知

每個狀態可開啟顧客通知與管理員副本,直接在 FluentCart 的「Email 通知」設定頁編輯主旨、標題與內文。

More Action 變更訂單狀態

訂單頁 More Action 多了「變更訂單狀態」,只列這筆訂單能去的狀態;側欄「訂單流程」卡片顯示目前步驟,一鍵「下一步」。

防呆、留紀錄、可復原

使用中的狀態不會被誤刪;搬移訂單先確認並留紀錄;匯入先看差異、可復原;兩人同時編輯不互相覆蓋。

流程報表與卡單清單

各狀態訂單數與金額、漏斗、停留時間、卡住的訂單,每張表可下載 CSV,可選每日摘要信。

螢幕截圖

更新日誌(最新版)

The order page's *Order workflow* card stops repeating *More Action*: it shows

where the order stands and its next step, and an order status is chosen in one

place. Nothing about an existing configuration or the e-mails changes. The

plugin is also quiet on PHP 8.4 and 8.5, and Move orders is tested past its

first batch.

For shop staff

  • The Order workflow card is shorter. For the order status and the

shipping status it shows the status the order is on, which step of the

workflow that is, why nothing can be changed when nothing can (a canceled

order, an order with nothing to ship), and the Next step → button. The

*Move to…* list and the *Change* button are gone from both lines, and each

line says where they went: "Other statuses: More Action → Change Order

Status" (or "… → Change Shipping Status").

  • Any other status is chosen from More Action. *More Action → Change Order

Status* (added by this plugin in 0.6) for the order status, FluentCart's own

*More Action → Change Shipping Status* for the shipping status. FluentCart's

shipping dialog lists every shipping status it displays, including the

order's current one and a custom one whose *Selectable in the admin* is off;

choosing either is refused by FluentCart and changes nothing.

  • A canceled order keeps its shipping list on the card. FluentCart offers no

*Change Shipping Status* on a canceled order, although the shipping status can

still change (for example to *Delivered* after a return), so there the card

lists the shipping moves on screen, under a sentence saying why.

  • The card keeps a way back for the order status. Its order-status list

comes back, under a sentence saying why, when the *Change Order Status* entry

cannot be used on the page: four seconds after the card appears there is no

More Action trigger the entry can be added through, or the page is not this

order's own view; the More Action menu opens but the entry cannot be added to

it; or the entry's dialog cannot load the order because of a network failure

or a server error — and then the dialog's message points to the list. When

the dialog is refused because the page has been open too long (its session

has expired), it says to reload the page instead, and shows no list that

would be refused the same way. The rules are the same as before: *Completed*

and *Canceled* ask first, *Completed* only on a paid order, and a cancel

returns the items to stock.

  • After a change from the card, a page that stays where it is — FluentCart asks

"Leave site?" when the order has unsaved edits, and the answer was *Stay* —

brings the card up to date and lets its buttons be used again, instead of

leaving them greyed out.

  • On a server running PHP 8.4 or newer that logs deprecation notices (for

example with `WP_DEBUG` on), the plugin no longer writes "Implicitly marking

parameter … as nullable is deprecated" lines to the PHP error log.

Changed (technical)

  • `OrderWidget::renderChanger()` — the widget's content and the `html` of

`GET orders/{id}/state` — draws, per axis, the head (badge, `standing` note,

step), then either the lock sentence, "There is nowhere for this order to move

on this axis.", or the Next step button in its own

`<p class="ys-fct-changer-row">`, then `<p class="ys-fct-changer-hint"

data-ys-changer-hint="">` naming the More Action entry. When the order

axis has targets it also carries `<div class="ys-fct-changer-fallback" data-ys-changer-fallback="order" hidden>`: a note, the

`data-ys-changer-select="order"` select with the same options and

`data-ys-confirm` flags as before, and the `data-ys-changer-go="order"` button.

The shipping axis has a select and a Change button only on a canceled order

(the order axis's `current` is `canceled`) with shipping targets: `<div class="ys-fct-changer-direct" data-ys-changer-direct="shipping">`, not hidden

and without a hint line, holding a note, `data-ys-changer-select="shipping"`

and `data-ys-changer-go="shipping"`. The widget's `subtitle` now describes the

card's role and where statuses are changed; FluentCart 1.6.0 and 1.6.3 hand it

to their card header, which does not display it.

  • `assets/admin/order-changer.js` reveals the fallback (removes `hidden`, and

hides that axis's `data-ys-changer-hint` line) when the More Action entry

cannot be used: 4 s after the card appears (checked every 250 ms) the page is

not the card's own `#/orders//view` route or has no

`.fct-order-bulk-action-modal .fct-more-option-wrap [aria-controls]`; the

menu lookup after activity on the trigger runs out (21 tries, 50 ms apart),

or keeps throwing, while the trigger says `aria-expanded="true"`; or the

dialog's `GET orders/{id}/state` fails without an HTTP status (network), with

a `5xx`, or answers without an order axis. A card is noticed by one

`MutationObserver` whose callback queues at most one `querySelectorAll` every

100 ms. Once revealed for an order, the fallback is revealed at once on every

card re-rendered for it until the page is left. The submit path is unchanged —

`submit()` → `changeStatus()` → `POST orders/{id}/change` — and so are the

confirmation, `confirmed: true` and the server's rules.

  • More activity on the More Action trigger while the menu lookup is running

restarts its count (it used to be ignored), so a click near the end of a

hover's lookup still finds a menu created late.

  • `fetchState()` rejects with the HTTP `status` on the Error. The dialog's

`loadFailed` sentence names the list it has just revealed; when there is none

to reveal (an order with no move, or no card on the page) the dialog shows the

server's reason or the new `notLoaded` sentence. On a `401` or `403` it shows

the new `reloadPage` sentence ("%s Reload the page and try again.", `%s` the

server's reason) and reveals nothing; on any other `4xx`, the server's reason.

  • After a successful change, `afterChange()` no longer gives up when the page

has fired `beforeunload`: 1.5 s on, a page still marked as leaving is looked

at again after `STAY_WAIT` (5 s), and if it is still there the cards are

re-rendered from `GET orders/{id}/state`, `leaving` is cleared and the card's

busy state (`busy`, `.is-busy`) is released; that second look never reloads

the page itself. A card re-rendered in place is released the same way.

Before, both left the card's buttons inert until a manual reload.

  • `assets/admin/order-changer.css`: `.ys-fct-changer [hidden] { display: none

!important; }`, so the attribute holds whatever display rule FluentCart's page

brings, and spacing for the fallback, the canceled order's shipping list and

the hint line.

  • Strings: new — the card's subtitle, the two "Other statuses: More Action → …"

lines, the fallback's note, the canceled order's shipping-list note,

`notLoaded`, `reloadPage`; changed — `loadFailed`; dropped — the old

subtitle. zh_TW updated and recompiled; FluentCart's *Change Shipping Status*

reads 「變更出貨狀態」 in every zh_TW string, as it already did on the settings

screen.

Fixed (technical)

  • The 22 optional array parameters that default to `null` now say so in their

type: `?array $settings = null` instead of `array $settings = null`, in

`Settings`, `Pipeline\Changer`, `Email\NotificationRegistry`,

`Support\Labels` and `Reports\DailySummary`. PHP 8.4 deprecated the implicit

form. Nullable types exist since PHP 7.1, so the minimum stays PHP 7.4; no

caller, argument or return value changes. Every shipped PHP file now lints

without a deprecation notice on PHP 8.5.

Tests

  • New unit group W (`tests/OrderCardTest.php`) renders the card from

hand-written states: nothing to choose from on screen, one fallback, on the

order axis, rendered hidden, with exactly the targets in order and the

confirmation flags; the hint line on each open axis; on a canceled physical

order, the shipping list on screen with no hint and no fallback, and none on a

canceled digital one; Next step on both; the no-next-step, nowhere-to-move and

locked lines; the notes; escaping of labels in the list and in the Next step

button's attributes. For the stylesheet and the script it only matches source

text: the `[hidden]` rule, and the lines that watch, reveal, handle the

dialog's statuses and release the card.

  • What the script does is run in a browser by the new

`tests/order-changer-browser.py` (Playwright and Chrome, 56 checks): a page

shaped like FluentCart's order view with the cards `OrderWidget` renders,

every REST answer scripted and the page's clock paused. The fallback stays

hidden while More Action works, also under a page rule setting `display` on

every `div`; it is shown after the wait with no trigger or on another route,

not before, and at once on a re-rendered card; an open menu that cannot take

the entry shows it, a hover does not, and a late menu still gets the entry;

the dialog's 401, 403, 404, 500, network failure and empty answer each give

the right sentence and reveal or not, a server reason holding `$&` included;

a page that stayed after `beforeunload`, or refreshed in place, gets a

working card back; and a canceled order's shipping list posts the shipping

axis. Each of 15 mutations

of the script — the wait's reveal removed, a reveal at once, the re-render

path, the initial scan, the menu-open reveal, the 401/403 and 4xx branches,

the release, the stay check and others — fails it.

  • `changer-scenarios` C5 and S6, `guard-scenarios` A1 and N restate the card

for 0.7 from real orders: no list or Change button on screen; the hint lines;

the order-axis list present, hidden, with the same targets and flags as the

dialog (no *Completed* on an unpaid order, *Canceled* and *Completed*

flagged); nothing on the shipping axis; Next step intact; the state route's

`html` byte for byte the order page's widget; a canceled order's card with its

lock sentence, no fallback, and its shipping moves on screen — exactly the

ones the route accepts and the state route reports. S6's escaping probe is

now an order status, since an open order's card lists no shipping moves.

  • `guard-scenarios` A4b puts 1001 orders on a status of their own and empties

it with `POST migrate`. It watches the statements the move sends — two full

batches of 500 and a partial third, each one `UPDATE` of the orders and one

history `INSERT` of the same size — then checks every order, not a sample:

the count the route reports, nothing left on the source, each order on the

target with `updated_at` refreshed, exactly one `migrate` history row and

one activity line per order, and no mail. The orders are copies of one

fixture order's row and are removed again once the section passes. All

suites run on FluentCart 1.6.0 and 1.6.3, on PHP 8.2 and 8.5.

常見問題

會改到 FluentCart 核心嗎?

不會。全部透過 FluentCart 公開的 filter(order_statuses、editable_order_statuses、shipping_statuses、email_notifications 等)與 REST 完成;More Action 裡的「變更訂單狀態」也是選單打開時才由外掛加入,更新 FluentCart 不會蓋掉任何東西。

為什麼付款後訂單狀態會變成「處理中」?

這是 FluentCart 核心的行為(寫死、沒有 filter)。外掛會在同一個流程裡把自訂狀態寫回去;每個狀態可設定「保留此狀態」或「交由 FluentCart 改成處理中」,限定「僅未付款訂單」的狀態則一律交給 FluentCart。

通知信在哪裡設定?免費版 FluentCart 能改內文嗎?

在 FluentCart → 設定(Settings)→ Email Configuration → Notifications(Email 通知),會多一組「自訂訂單狀態」。開關、主旨、預覽都是 FluentCart 自己的;標題與內文段落是外掛加在同一個編輯頁的欄位,免費版也能填。完整的區塊編輯器仍需 FluentCart Pro。

為什麼 FluentCart 原生後台不能直接選我的自訂訂單狀態?

FluentCart 後台本來就沒有自由選擇訂單狀態的控制項(1.6.0 與 1.6.3 實測),不論是否付款,More Action 裡只有「標記為完成」、「退回處理中」、「取消訂單」這類固定按鈕。所以外掛在同一個選單加上「變更訂單狀態」,只列出這筆訂單可以移動到的狀態。出貨狀態則不同:自訂的出貨狀態會直接出現在 FluentCart 自己的「變更出貨狀態」對話框裡。

流程該放在訂單狀態還是出貨狀態?

如果流程是從顧客付款之後才開始(生產、包裝、排單、出貨),建議放出貨狀態:FluentCart 永遠不會自動改寫它,而且內建的「變更出貨狀態」在每張未取消、需要出貨的實體訂單上都能用。狀態確實屬於訂單本身、或希望顧客在訂單狀態上看到時,再放訂單狀態。

停用或移除外掛會影響訂單嗎?

停用不會改動任何訂單資料,重新啟用後設定與歷史都還在。移除預設也保留設定與歷史表,除非在 wp-config.php 定義 YS_FCT_STATUS_REMOVE_DATA。

還有訂單在用的狀態,可以直接移除嗎?

不行。儲存時會被擋下,並說明是哪個狀態、還有幾筆訂單;請先用該狀態列上的「搬移訂單」把訂單搬走再移除。已儲存的狀態也不能改代碼,要換代碼請新增狀態再搬移訂單。若是以前就被移除、還留著訂單的狀態,設定頁最上方會提醒,「工具」分頁可以直接搬移。

匯入設定後發現不對,可以復原嗎?

可以。匯入只接受本外掛匯出的檔案,寫入前會先列出會新增、移除、變更哪些內容;匯入後「工具」分頁會出現「復原上一次匯入」,狀態、設定與郵件文字都會回到匯入之前。

0