1use cli_core::eval::escape_str;
4use cli_core::forms::{
5 EditableTransaction, LogicalSplitInput, build_transaction_update_payload,
6 parse_editable_transaction,
7};
8
9use crate::app::App;
10use crate::form::Form;
11use crate::modal::Modal;
12use crate::route::RouteCtx;
13use crate::view::ViewId;
14
15impl App {
16 pub fn open_transaction_edit_async(&mut self, id: &str) {
20 if self.console_eval.is_none() {
21 self.status = "console not connected".to_string();
22 return;
23 }
24 let form = format!("(get-transaction-detail {})", escape_str(id));
25 if self
26 .dispatch_eval(ViewId::Transactions, RouteCtx::TransactionEdit, form)
27 .is_none()
28 {
29 self.status = "eval worker stopped".to_string();
30 }
31 }
32
33 pub(super) fn deliver_transaction_edit_reply(&mut self, wire: &str) {
35 match parse_editable_transaction(wire) {
36 Ok(et) => self.open_edit_form_from(&et),
37 Err(msg) => self.status = format!("can't edit this transaction: {msg}"),
38 }
39 }
40
41 fn open_edit_form_from(&mut self, et: &EditableTransaction) {
42 let mode = self.edit_mode;
43 self.overlays
44 .push(Modal::Form(Form::transaction_edit(mode, et)));
45 self.fetch_transaction_form_options();
46 }
47
48 pub fn submit_transaction_edit(
52 &mut self,
53 id: &str,
54 splits: &[LogicalSplitInput],
55 note: &str,
56 date: &str,
57 ) -> bool {
58 let form = match build_transaction_update_payload(id, splits, note, date) {
59 Ok(f) => f,
60 Err(e) => {
61 self.status = format!("transaction payload error: {e}");
62 return false;
63 }
64 };
65 if self.console_eval.is_none() {
66 self.status = "console not connected".to_string();
67 return false;
68 }
69 match self.dispatch_eval(
70 ViewId::Transactions,
71 RouteCtx::Mutation {
72 refresh: ViewId::Transactions,
73 },
74 form,
75 ) {
76 Some(_) => true,
77 None => {
78 self.status = "eval worker stopped".to_string();
79 false
80 }
81 }
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88 use crate::widgets::EditMode;
89 use sqlx::types::Uuid;
90
91 fn make_app() -> App {
92 App::new(Uuid::new_v4(), EditMode::Emacs)
93 }
94
95 #[test]
96 fn deliver_transaction_edit_reply_err_path_sets_status() {
97 let mut app = make_app();
98 app.deliver_transaction_edit_reply("garbage wire");
100 assert!(
101 app.status.starts_with("can't edit this transaction:"),
102 "error reply sets descriptive status: {}",
103 app.status
104 );
105 assert!(app.overlays.is_empty(), "no form opened on parse error");
106 }
107
108 #[test]
109 fn submit_transaction_edit_without_eval_sets_status() {
110 use cli_core::forms::LogicalSplitInput;
111 let splits = vec![LogicalSplitInput {
112 from: "from-uuid".to_string(),
113 to: "to-uuid".to_string(),
114 from_commodity: "comm-uuid".to_string(),
115 to_commodity: "comm-uuid".to_string(),
116 amount: "10".to_string(),
117 to_amount: None,
118 }];
119 let mut app = make_app();
120 let ok = app.submit_transaction_edit("tx-id", &splits, "note", "2024-01-01");
121 assert!(!ok, "no eval worker → returns false");
122 assert_eq!(app.status, "console not connected");
123 }
124}