Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

Purchase Order Data & Events

The Purchase Order drop-in uses the event bus to emit and listen to events for communication between drop-ins and external integrations.

Version: 1.0.0
EventDirectionDescription
order/dataEmitsEmitted when data is available or changes.
purchase-order/errorEmitsEmitted when an error occurs.
purchase-order/placedEmitsEmitted when an order is placed.
auth/permissionsListensFired by Auth (auth) when permissions are updated.
purchase-order/dataEmits and listensTriggered when data is available or changes.
purchase-order/refreshEmits and listensEmitted and consumed for internal and external communication.

The following sections provide detailed information about each event, including its direction, event payload, and usage examples.

Fired by Auth (auth) when permissions are updated.

{
admin?: boolean;
[key: string]: boolean | undefined;
}
import { events } from '@dropins/tools/event-bus.js';
events.on('auth/permissions', (payload) => {
console.log('auth/permissions event received:', payload);
// Add your custom logic here
});

Emitted when data is available or changes.

PurchaseOrderModel['quote']

See PurchaseOrderModel for full type definition.

  • When the Purchase Order Details page loads with a poRef parameter
  • After purchase-order/refresh event (emitted together with purchase-order/data)
import { events } from '@dropins/tools/event-bus.js';
events.on('order/data', (payload) => {
console.log('Purchase order data for Order containers:', payload.data);
// Initialize Order Drop-In containers with PO data
displayPurchaseOrderDetailsInOrderContainers(payload.data);
// Extract purchase order information
const { number, status, items } = payload.data;
});
  • Initialize Order Drop-In containers with purchase order data.
  • Display purchase order details on the PO Details page.
  • Sync PO data after refresh events.
  • Update UI when PO data loads.

Triggered when data is available or changes.

PurchaseOrderModel

See PurchaseOrderModel for full type definition.

  • After loading a purchase order
  • After approving a purchase order
  • After rejecting a purchase order
  • After canceling a purchase order
  • After adding comments to a purchase order
  • After updating purchase order status
import { events } from '@dropins/tools/event-bus.js';
events.on('purchase-order/data', (payload) => {
const po = payload.data;
console.log('Purchase order updated:', po.number, po.status);
// Update the UI to reflect current status
updatePurchaseOrderStatus(po.status);
// Show approval flow if needed
if (po.requiresApproval) {
displayApprovalFlow(po.approvalFlow);
}
});
  • Refresh the purchase order details view.
  • Update purchase order lists.
  • Display the approval flow progress.
  • Show status-specific actions.
  • Update cached purchase order data.

Emitted when an error occurs.

import { events } from '@dropins/tools/event-bus.js';
events.on('purchase-order/error', (payload) => {
console.log('purchase-order/error event received:', payload);
// Add your custom logic here
});

Emitted when an order is placed.

PurchaseOrderModel

See PurchaseOrderModel for full type definition.

  • After successfully calling placePurchaseOrder()
  • After converting a cart to a purchase order
import { events } from '@dropins/tools/event-bus.js';
events.on('purchase-order/placed', (payload) => {
const { number: purchaseOrderNumber, status } = payload.data;
const requiresApproval = status !== "APPROVED";
console.log(`Purchase order ${purchaseOrderNumber} placed with status: ${status}`);
// Show appropriate confirmation message
if (requiresApproval) {
showMessage('Purchase order submitted for approval');
redirectToApprovalStatus(purchaseOrderNumber);
} else {
showMessage('Purchase order placed successfully');
redirectToOrderConfirmation(purchaseOrderNumber);
}
// Track analytics
trackPurchaseOrderPlacement(purchaseOrderNumber, requiresApproval);
});

Example 2: Complete checkout workflow with notifications

Section titled “Example 2: Complete checkout workflow with notifications”
import { events } from '@dropins/tools/event-bus.js';
import { placePurchaseOrder } from '@dropins/storefront-purchase-order/api.js';
async function completePurchaseOrderCheckout(cartId) {
try {
// Show checkout processing
showCheckoutModal('Processing your purchase order...');
// Place the purchase order
await placePurchaseOrder(cartId);
// Listen for successful placement
events.once('purchase-order/placed', async (payload) => {
const { number: purchaseOrderNumber, status } = payload.data;
const requiresApproval = status !== "APPROVED";
// Close processing modal
hideCheckoutModal();
// Clear cart UI
clearCartDisplay();
// Show success modal with details
if (requiresApproval) {
showSuccessModal({
title: 'Purchase Order Submitted',
message: `Your purchase order #${purchaseOrderNumber} has been submitted for approval.`,
details: [
`Status: Pending Approval`,
`You will be notified when it's reviewed.`,
`Track your order in the Purchase Orders section.`
],
primaryAction: {
label: 'View Purchase Order',
onClick: () => window.location.href = `/purchase-orders/${purchaseOrderNumber}`
},
secondaryAction: {
label: 'Continue Shopping',
onClick: () => window.location.href = '/products'
}
});
// Send notification email
await sendNotification({
type: 'purchase-order-submitted',
purchaseOrderNumber,
approvers: payload.data.approvers
});
} else {
showSuccessModal({
title: 'Order Placed Successfully',
message: `Your purchase order #${purchaseOrderNumber} has been placed.`,
details: [
`Status: ${status}`,
`You will receive a confirmation email shortly.`
],
primaryAction: {
label: 'View Order',
onClick: () => window.location.href = `/orders/${purchaseOrderNumber}`
}
});
}
// Track conversion
trackConversion({
type: 'purchase-order',
orderNumber: purchaseOrderNumber,
requiresApproval,
value: payload.data.total,
currency: payload.data.currency
});
// Update user's PO history count
incrementPurchaseOrderCount();
});
} catch (error) {
hideCheckoutModal();
showErrorModal({
title: 'Failed to Place Purchase Order',
message: error.message || 'An error occurred while processing your order.',
action: {
label: 'Try Again',
onClick: () => completePurchaseOrderCheckout(cartId)
}
});
console.error('Purchase order placement error:', error);
}
}

Example 3: Multi-approval workflow dashboard

Section titled “Example 3: Multi-approval workflow dashboard”
import { events } from '@dropins/tools/event-bus.js';
// Dashboard for tracking all purchase orders
class PurchaseOrderDashboard {
constructor() {
this.pendingOrders = [];
this.completedOrders = [];
// Listen for new purchase orders
events.on('purchase-order/placed', this.handleNewOrder.bind(this));
events.on('purchase-order/data', this.handleOrderUpdate.bind(this));
this.init();
}
async init() {
await this.loadExistingOrders();
this.render();
}
handleNewOrder(payload) {
const { number: purchaseOrderNumber, status } = payload.data;
const requiresApproval = status !== "APPROVED";
const order = {
number: purchaseOrderNumber,
status: status,
requiresApproval,
placedAt: new Date(),
...payload.data
};
if (requiresApproval) {
this.pendingOrders.unshift(order);
// Show real-time notification
this.showNotificationBanner({
type: 'info',
message: `New PO #${purchaseOrderNumber} awaiting approval`,
action: () => this.viewOrder(purchaseOrderNumber)
});
// Play notification sound
this.playNotificationSound();
// Update pending count badge
this.updatePendingBadge(this.pendingOrders.length);
} else {
this.completedOrders.unshift(order);
}
// Refresh dashboard display
this.render();
}
handleOrderUpdate(payload) {
const updatedOrder = payload.data;
// Remove from pending if approved/rejected
if (['approved', 'rejected', 'canceled'].includes(updatedOrder.status)) {
this.pendingOrders = this.pendingOrders.filter(
o => o.number !== updatedOrder.number
);
this.completedOrders.unshift(updatedOrder);
this.updatePendingBadge(this.pendingOrders.length);
}
this.render();
}
render() {
document.querySelector('#pending-orders').innerHTML =
this.renderOrderList(this.pendingOrders, 'pending');
document.querySelector('#completed-orders').innerHTML =
this.renderOrderList(this.completedOrders, 'completed');
}
renderOrderList(orders, type) {
if (orders.length === 0) {
return `<div class="empty-state">No ${type} purchase orders</div>`;
}
return orders.map(order => `
<div class="order-card" data-order="${order.number}">
<div class="order-header">
<span class="order-number">#${order.number}</span>
<span class="order-status status-${order.status}">${order.status}</span>
</div>
<div class="order-details">
<span>Total: ${order.total}</span>
<span>Date: ${formatDate(order.placedAt)}</span>
</div>
</div>
`).join('');
}
showNotificationBanner(options) {
// Show slide-in notification
const banner = document.createElement('div');
banner.className = `notification-banner notification-${options.type}`;
banner.innerHTML = `
<span>${options.message}</span>
<button onclick="this.parentElement.remove()">×</button>
`;
if (options.action) {
banner.onclick = options.action;
}
document.body.appendChild(banner);
setTimeout(() => banner.remove(), 5000);
}
playNotificationSound() {
const audio = new Audio('/sounds/notification.mp3');
audio.play().catch(() => {});
}
updatePendingBadge(count) {
const badge = document.querySelector('.pending-count-badge');
if (badge) {
badge.textContent = count;
badge.style.display = count > 0 ? 'block' : 'none';
}
}
viewOrder(orderNumber) {
window.location.href = `/purchase-orders/${orderNumber}`;
}
async loadExistingOrders() {
// Load existing orders from API
const { pendingOrders, completedOrders } = await fetchPurchaseOrders();
this.pendingOrders = pendingOrders;
this.completedOrders = completedOrders;
}
}
// Initialize dashboard
const dashboard = new PurchaseOrderDashboard();
  • Display success confirmation with order details.
  • Redirect to the success page (approval or direct order).
  • Clear shopping cart after successful placement.
  • Send email notifications to approvers.
  • Track analytics and conversion events.
  • Update purchase order history and counts.
  • Show real-time notifications for new orders.
  • Update dashboard widgets and badges.
  • Trigger approval workflow notifications.
  • Log purchase order creation for audit.
  • Update budget tracking systems.
  • Sync with ERP/accounting systems.

purchase-order/refresh (emits and listens)

Section titled “purchase-order/refresh (emits and listens)”

Emitted and consumed for internal and external communication.

Boolean
  • After approval rule changes
  • After permission updates
  • On user request (manual refresh)
  • After significant state changes
import { events } from '@dropins/tools/event-bus.js';
// Emit refresh event
events.emit('purchase-order/refresh', {
purchaseOrderId: 'PO123456'
});
// Listen for refresh requests
events.on('purchase-order/refresh', async (payload) => {
if (payload.data.purchaseOrderId) {
// Refresh specific purchase order
await refreshPurchaseOrder(payload.data.purchaseOrderId);
} else {
// Refresh all purchase orders
await refreshAllPurchaseOrders();
}
});
  • Force reload after external updates.
  • Implement pull-to-refresh functionality.
  • Sync data after background changes.
  • Refresh after approval rule modifications.

All Purchase Order events are emitted through the centralized event bus. Subscribe to events using the events.on() method:

import { events } from '@dropins/tools/event-bus.js';
// Listen to purchase order lifecycle events
events.on('purchase-order/placed', handlePurchaseOrderPlaced);
events.on('purchase-order/data', handlePurchaseOrderData);
events.on('purchase-order/refresh', handleRefreshRequest);
// Remove listeners when done
events.off('purchase-order/placed', handlePurchaseOrderPlaced);

The following data models are used in event payloads for this drop-in.

Used in: order/data, purchase-order/data, purchase-order/placed.

interface PurchaseOrderModel {
typename: string;
uid: string;
number: string;
status: string;
availableActions: string[];
approvalFlow:
| {
ruleName: string;
events: Array<{
message: string;
name: string;
role: string;
status: string;
updatedAt: string;
}>;
}[]
| [];
comments?: Array<{
uid: string;
createdAt: string;
author: {
firstname: string;
lastname: string;
email: string;
};
text: string;
}>;
createdAt: string;
updatedAt: string;
createdBy: {
firstname: string;
lastname: string;
email: string;
};
historyLog?: Array<{
activity: string;
createdAt: string;
message: string;
uid: string;
}>;
quote: QuoteProps | null;
order: {
orderNumber: string;
id: string;
};
}