Filters and hooks
Add board columns, card labels, a step to Mark shipped, printable documents and screens, from PHP and JavaScript.
Packboard's hooks are WordPress filters and actions, plus a few JavaScript filters on the board and grid (through wp.hooks). Put PHP examples in a small custom plugin or your child theme's functions.php.
packboard_board_lanes
Adds a column before To pick for orders that wait outside the flow. Packboard doesn't know what the column is: your callbacks decide which orders it holds and what its one action does. Dragging a card from it into To pick runs the action too.
add_filter( 'packboard_board_lanes', function ( $lanes ) {
$lanes['awaiting-stock'] = array(
'title' => 'Awaiting stock',
'description' => 'On hold until the supplier delivers.', // optional
'orders' => fn() => wc_get_orders( array( 'status' => 'on-hold', 'limit' => -1 ) ),
'note' => fn( WC_Order $order ) => 'Since ' . $order->get_date_modified()->date_i18n( 'M j' ), // optional
'action' => 'Ready to pick',
'run' => function ( WC_Order $order ) {
$order->update_status( 'processing' );
return true; // true when the order moved on
},
);
return $lanes;
} );
A lane without a title, an action, or orders and run callbacks is ignored.
packboard_card_tags
Short labels on an order's card. Gets the list so far and the order; return strings (empty ones are dropped).
add_filter( 'packboard_card_tags', function ( $tags, $order ) {
if ( $order->get_meta( '_gift_wrap' ) ) {
$tags[] = 'Gift wrap';
}
return $tags;
}, 10, 2 );
packboard_card_sort
Where a card goes in its column. Return a sort key: cards with a key come first, ordered by key as text, then cards without one, oldest first.
// Express shipping first.
add_filter( 'packboard_card_sort', function ( $key, $order ) {
return $order->has_shipping_method( 'flat_rate:2' ) ? '0-express' : $key;
}, 10, 2 );
packboard_ship_order
An action that runs for each order Mark shipped is about to complete, before its status changes. Whatever you save on the order here is saved with it, and is already there when WooCommerce sends the "Completed order" email. Gets the order and the REST request, which carries any fields the ship dialog added (see packboard.shipDialog below).
Throw an exception to stop one order from shipping: it stays in Packed and the board reports it.
add_action( 'packboard_ship_order', function ( $order, $request ) {
$order->update_meta_data( '_shipped_by', wp_get_current_user()->display_name );
}, 10, 2 );
Packboard Pro uses this action to record carriers and tracking numbers.
packboard_print_documents
Adds a printable document. It's served on Packboard's print page, which has a Print button and Packboard's print stylesheet, at the print URL with &doc=<key>&ids=<order IDs>. styles are registered style handles printed after Packboard's.
add_filter( 'packboard_print_documents', function ( $documents ) {
$documents['labels'] = array(
'title' => 'Address labels',
'render' => function ( array $orders ) {
foreach ( $orders as $order ) {
echo '<div class="label">' . wp_kses_post( $order->get_formatted_shipping_address() ) . '</div>';
}
},
);
return $documents;
} );
Open it from the board or grid with window.packboardDom.openPrint( 'labels', ids ).
packboard_admin_pages and packboard_enqueue_page
packboard_admin_pages adds a screen under the WooCommerce menu (slug => title), next to Order Grid and Fulfillment. It renders an empty #packboard-root and loads Packboard's admin stylesheet and the packboard-dom script, which sets window.packboardDom (el() to build elements, openPrint()) and window.packboardConfig (order statuses, stage names, the order edit and print URLs).
packboard_enqueue_page fires on every Packboard screen with its slug, before the screen's own script, so a script you enqueue there can add wp.hooks filters before the board or grid first draws.
add_action( 'packboard_enqueue_page', function ( $slug ) {
if ( 'packboard-fulfillment' === $slug ) {
wp_enqueue_script( 'my-board', plugins_url( 'board.js', __FILE__ ), array( 'packboard-dom', 'wp-hooks', 'wp-i18n' ), '1.0', true );
}
} );
JavaScript filters
All four use wp.hooks.addFilter( name, namespace, callback ).
packboard.boardActions ( actions, columnKey ): the buttons over a stage column (to_pick, picking, packed). Each is { id, label, primary, run( ids ) }; run gets the selected order IDs. Packboard's ids are start-picking, packing-slips, mark-packed and mark-shipped.
wp.hooks.addFilter( 'packboard.boardActions', 'my-plugin', ( actions, column ) =>
'packed' === column
? [ { id: 'labels', label: 'Address labels', run: ( ids ) => window.packboardDom.openPrint( 'labels', ids ) }, ...actions ]
: actions
);
packboard.cardLinks ( nodes, order, columnKey ): elements added at the bottom of a card.
packboard.shipDialog ( step, orders ): what the Mark shipped dialog asks for. step is { fields, cell, data, description }: fields are elements above the order list, cell( order ) returns an element for that order's row (or null), data() returns extra fields to send, which reach packboard_ship_order as request parameters (they can't replace ids or stage), and description is the text under the list. Return a new object built on the one you get, so other filters still apply. Pressing Enter in a text field never submits the dialog, so barcode scanners are safe.
packboard.gridActions ( actions ): buttons for the selected rows on the order grid, each { id, label, run( ids ) }.
packboard_loaded
An action that fires once Packboard has loaded (on plugins_loaded, and only when WooCommerce is active). Hook an add-on's setup to it, so it only runs when Packboard is there. PACKBOARD_VERSION is defined from then on.
REST API
Everything the screens do goes through packboard/v1, for users who can edit orders:
| Route | Does |
|---|---|
GET /orders |
The grid: page, per_page (up to 200), status, search |
POST /orders/bulk |
ids and status: change the status of up to 500 orders |
GET /fulfillment |
The board: columns (to_pick, picking, packed), lanes and truncated |
POST /fulfillment/stage |
ids and stage (to_pick, picking, packed, shipped), plus any ship dialog fields |
POST /fulfillment/lane |
ids and lane: run a lane's action |
Each answers with the orders it changed (updated) and the IDs it couldn't (failed).