v1.23.0: ship events and operations dashboard
"Ship events and operations dashboard" release. Two threads.
The admin sales-order edit surface now emits the outbound events a marketplace consumer needs -- a per-field salesorderedit.completed event, and ship.confirmed when an edit ships the order (carrier derived from the free-text ship method, tracking made optional for local pickups). And the dashboard gains an operations view: a per-PO Received tab and a Marketplace Health tab whose per-channel health bubbles are admin-defined in Settings rather than hardcoded.
Mobile. Zero mobile/ diffs on this release. The current mobile build (version 1.19.0, versionCode 9) remains current; no new APK for v1.23.0.
Added
- SO-edit outbound events (#404): editing a sales-order header via
PUT /api/admin/sales-orders/<so_id>emitssalesorderedit.completed/1carrying the per-field diff that landed (the same{field, old_value, new_value}set as the audit row), so marketplaces can sync header edits that previously produced no event. When the same edit transitions the order to SHIPPED it additionally firesship.confirmed/1viarecord_ship; the admin surface has no carrier field, socarrier_from_ship_methodderives one from the free-text ship method (defaulting to "Other"). A local-pickup ship (the ship method names a pickup) needs no tracking, soship.confirmed/1now permits an emptytracking_numbersarray (minItems 0, a backward-compatible widening). - Operations dashboard (#405): the dashboard becomes a tab shell adding a Received tab (per-PO receipts for a date range,
GET /api/v1/dashboard/received) and a Marketplace Health tab (GET /api/v1/dashboard/shipping-health) with per-channel Orders Received / Orders Shipped totals and a clickable need-to-ship-today bubble that opens the SO drill-down. The channel set is admin-defined: a Settings > Marketplace Health surface saves{origin, label}pairs to thedashboard_bubble_originsapp setting, matched verbatim against the free-textsales_orders.order_origin; with nothing configured the view surfaces no channels, never implicitly from the data. Shared range picker across the date-scoped tabs.