Purchase Order Events
The Purchase Order drop-in emits 5 events to the event bus, allowing you to track and respond to purchase order state changes, order placement, and permission updates.
Version: 1.0.0-beta1
Available events
| Event Name | Description |
|---|---|
auth/permissions | Emitted when user permissions are updated or refreshed |
order/data | Emitted when order data associated with a purchase order changes |
purchase-order/data | Emitted when purchase order data is loaded or updated |
purchase-order/placed | Emitted when a purchase order is successfully placed |
purchase-order/refresh | Emitted to trigger a refresh of purchase order data |
Event details
auth/permissions
Emitted when user permissions related to purchase orders are updated or need to be refreshed.
Event payload
{ eventType: 'auth/permissions', data: { permissions: string[] // Array of permission codes }}When triggered
- After user logs in
- After company role changes
- After approval rule permissions are modified
- After company context switches
Example
import { events } from '@dropins/tools/event-bus.js';
events.on('auth/permissions', (payload) => { console.log('Permissions updated:', payload.data.permissions);
// Check for specific permissions const canCreatePO = payload.data.permissions.includes('Magento_PurchaseOrder::create'); const canApprovePO = payload.data.permissions.includes('Magento_PurchaseOrder::approve');
// Update UI based on permissions updatePurchaseOrderActions(canCreatePO, canApprovePO);});Usage scenarios
- Show/hide purchase order action buttons
- Enable/disable approval workflow features
- Update navigation menu items
- Conditionally render UI components
order/data
Emitted when standard order data associated with a purchase order changes or is loaded.
Event payload
{ eventType: 'order/data', data: OrderModel // Order data object}When triggered
- After a purchase order is converted to an order
- When loading order confirmation details
- After order status updates
Example
import { events } from '@dropins/tools/event-bus.js';
events.on('order/data', (payload) => { console.log('Order data loaded:', payload.data);
// Display order confirmation showOrderConfirmation(payload.data);
// Track conversion trackPurchaseOrderConversion(payload.data.orderNumber);});Usage scenarios
- Display order confirmation after PO placement
- Update order tracking information
- Sync order data with external systems
- Trigger post-purchase workflows
purchase-order/data
Emitted when purchase order data is loaded, updated, or changes state.
Event payload
{ eventType: 'purchase-order/data', data: PurchaseOrderModel // Purchase order data}When triggered
- 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
Example
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); }});Usage scenarios
- Refresh purchase order details view
- Update purchase order lists
- Display approval flow progress
- Show status-specific actions
- Update cached purchase order data
purchase-order/placed
Emitted when a purchase order is successfully placed from the cart.
Event payload
{ eventType: 'purchase-order/placed', data: { purchaseOrderNumber: string, requiresApproval: boolean, status: string }}When triggered
- After successfully calling
placePurchaseOrder() - After converting a cart to a purchase order
Example 1: Basic purchase order placement
import { events } from '@dropins/tools/event-bus.js';
events.on('purchase-order/placed', (payload) => { const { purchaseOrderNumber, requiresApproval, status } = payload.data;
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
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 { purchaseOrderNumber, requiresApproval, status } = payload.data;
// 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
import { events } from '@dropins/tools/event-bus.js';
// Dashboard for tracking all purchase ordersclass 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 { purchaseOrderNumber, requiresApproval, status } = payload.data;
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 dashboardconst dashboard = new PurchaseOrderDashboard();Usage scenarios
- Display success confirmation with order details
- Redirect to appropriate success page (approval vs. 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
Emitted to signal that purchase order data should be refreshed from the server.
Event payload
{ eventType: 'purchase-order/refresh', data: { purchaseOrderId?: string // Optional specific PO to refresh }}When triggered
- After approval rule changes
- After permission updates
- On user request (manual refresh)
- After significant state changes
Example
import { events } from '@dropins/tools/event-bus.js';
// Emit refresh eventevents.emit('purchase-order/refresh', { purchaseOrderId: 'PO123456'});
// Listen for refresh requestsevents.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(); }});Usage scenarios
- Force reload after external updates
- Implement pull-to-refresh functionality
- Sync data after background changes
- Refresh after approval rule modifications
Listening to events
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 eventsevents.on('purchase-order/placed', handlePurchaseOrderPlaced);events.on('purchase-order/data', handlePurchaseOrderData);events.on('purchase-order/refresh', handleRefreshRequest);
// Listen to permission changesevents.on('auth/permissions', handlePermissionUpdate);
// Remove listeners when doneevents.off('purchase-order/placed', handlePurchaseOrderPlaced);Related documentation
- Functions - API functions that emit these events
- Containers - UI components that respond to events
- Event bus documentation - Learn more about the event system