cli

Command-line interface for Radroots
git clone https://radroots.dev/git/cli.git
Log | Files | Refs | README | LICENSE

commit ae793686fc4136d3432ca0b496c24f7cdf035e49
parent 6b51c5964ee79ab4e016f6194eb7da7814c27765
Author: triesap <tyson@radroots.org>
Date:   Thu, 30 Apr 2026 08:38:02 +0000

order: expose status revision state

Diffstat:
Msrc/domain/runtime.rs | 21+++++++++++++++++++++
Msrc/runtime/order.rs | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 91 insertions(+), 2 deletions(-)

diff --git a/src/domain/runtime.rs b/src/domain/runtime.rs @@ -1658,6 +1658,8 @@ pub struct OrderStatusView { #[serde(skip_serializing_if = "Option::is_none")] pub last_event_id: Option<String>, #[serde(skip_serializing_if = "Option::is_none")] + pub revision: Option<OrderStatusRevisionView>, + #[serde(skip_serializing_if = "Option::is_none")] pub inventory: Option<OrderInventoryView>, #[serde(skip_serializing_if = "Option::is_none")] pub fulfillment: Option<OrderStatusFulfillmentView>, @@ -1684,6 +1686,25 @@ pub struct OrderStatusView { } #[derive(Debug, Clone, Serialize)] +pub struct OrderStatusRevisionView { + pub state: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub revision_id: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub proposal_event_id: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub decision_event_id: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub root_event_id: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub prev_event_id: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub agreement_event_id: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option<String>, +} + +#[derive(Debug, Clone, Serialize)] pub struct OrderStatusFulfillmentView { pub state: String, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/src/runtime/order.rs b/src/runtime/order.rs @@ -74,8 +74,8 @@ use crate::domain::runtime::{ OrderInventoryView, OrderIssueView, OrderListView, OrderNewView, OrderReceiptView, OrderRevisionDecisionView, OrderRevisionProposalView, OrderStatusFulfillmentView, OrderStatusLifecycleCancellationView, OrderStatusLifecycleReceiptView, - OrderStatusLifecycleView, OrderStatusView, OrderSubmitView, OrderSummaryView, OrderWatchView, - RelayFailureView, + OrderStatusLifecycleView, OrderStatusRevisionView, OrderStatusView, OrderSubmitView, + OrderSummaryView, OrderWatchView, RelayFailureView, }; use crate::runtime::RuntimeError; use crate::runtime::accounts; @@ -1497,6 +1497,7 @@ pub fn status( seller_pubkey: None, economics: None, last_event_id: None, + revision: None, inventory: None, fulfillment: None, lifecycle: None, @@ -1533,6 +1534,7 @@ pub fn status( seller_pubkey: None, economics: None, last_event_id: None, + revision: None, inventory: None, fulfillment: None, lifecycle: None, @@ -1707,6 +1709,8 @@ fn order_status_reduction_from_receipt_with_context( }); let order_id = context.order_id; + let revision_proposal_records = revision_proposals.clone(); + let revision_decision_records = revision_decisions.clone(); let fulfillment_records = fulfillments.clone(); let cancellation_records = cancellations.clone(); let receipt_records = receipts.clone(); @@ -1828,6 +1832,12 @@ fn order_status_reduction_from_receipt_with_context( }), reducer_issues.as_slice(), ); + let revision = order_status_revision_view( + projection.last_event_id.as_deref(), + projection.agreement_event_id.as_deref(), + &revision_proposal_records, + &revision_decision_records, + ); let view = OrderStatusView { state, @@ -1842,6 +1852,7 @@ fn order_status_reduction_from_receipt_with_context( seller_pubkey: projection.seller_pubkey, economics: projection.economics, last_event_id: projection.last_event_id, + revision, inventory, fulfillment, lifecycle: Some(lifecycle), @@ -2683,6 +2694,63 @@ fn order_status_lifecycle_view( } } +fn order_status_revision_view( + last_event_id: Option<&str>, + agreement_event_id: Option<&str>, + proposals: &[RadrootsActiveOrderRevisionProposalRecord], + decisions: &[RadrootsActiveOrderRevisionDecisionRecord], +) -> Option<OrderStatusRevisionView> { + if let Some(proposal) = last_event_id + .and_then(|event_id| proposals.iter().find(|record| record.event_id == event_id)) + { + return Some(OrderStatusRevisionView { + state: "pending".to_owned(), + revision_id: Some(proposal.payload.revision_id.clone()), + proposal_event_id: Some(proposal.event_id.clone()), + decision_event_id: None, + root_event_id: Some(proposal.root_event_id.clone()), + prev_event_id: Some(proposal.prev_event_id.clone()), + agreement_event_id: None, + reason: Some(proposal.payload.reason.clone()), + }); + } + + if let Some(decision) = last_event_id + .and_then(|event_id| decisions.iter().find(|record| record.event_id == event_id)) + { + return Some(order_status_revision_view_from_decision( + decision, + agreement_event_id, + )); + } + + agreement_event_id + .and_then(|event_id| decisions.iter().find(|record| record.event_id == event_id)) + .map(|decision| order_status_revision_view_from_decision(decision, agreement_event_id)) +} + +fn order_status_revision_view_from_decision( + decision: &RadrootsActiveOrderRevisionDecisionRecord, + agreement_event_id: Option<&str>, +) -> OrderStatusRevisionView { + let (state, reason) = match &decision.payload.decision { + RadrootsTradeOrderRevisionDecision::Accepted => ("accepted", None), + RadrootsTradeOrderRevisionDecision::Declined { reason } => { + ("declined", Some(reason.clone())) + } + }; + OrderStatusRevisionView { + state: state.to_owned(), + revision_id: Some(decision.payload.revision_id.clone()), + proposal_event_id: Some(decision.prev_event_id.clone()), + decision_event_id: Some(decision.event_id.clone()), + root_event_id: Some(decision.root_event_id.clone()), + prev_event_id: Some(decision.prev_event_id.clone()), + agreement_event_id: agreement_event_id.map(str::to_owned), + reason, + } +} + fn order_status_lifecycle_phase( status: &RadrootsActiveOrderStatus, fulfillment_status: Option<RadrootsActiveTradeFulfillmentState>,