1use stateset_core::{
4 CreateReturn, CustomerId, OrderId, Result, Return, ReturnFilter, ReturnId, ReturnStatus,
5 UpdateReturn, Validate,
6};
7use stateset_db::Database;
8use stateset_observability::Metrics;
9use std::sync::Arc;
10
11#[cfg(feature = "events")]
12use crate::events::EventSystem;
13#[cfg(feature = "events")]
14use rust_decimal::Decimal;
15#[cfg(feature = "events")]
16use stateset_core::CommerceEvent;
17
18pub struct Returns {
20 db: Arc<dyn Database>,
21 metrics: Metrics,
22 #[cfg(feature = "events")]
23 event_system: Arc<EventSystem>,
24}
25
26impl std::fmt::Debug for Returns {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 f.debug_struct("Returns").finish_non_exhaustive()
29 }
30}
31
32impl Returns {
33 #[cfg(feature = "events")]
34 pub(crate) fn new(
35 db: Arc<dyn Database>,
36 event_system: Arc<EventSystem>,
37 metrics: Metrics,
38 ) -> Self {
39 Self { db, metrics, event_system }
40 }
41
42 #[cfg(not(feature = "events"))]
43 pub(crate) fn new(db: Arc<dyn Database>, metrics: Metrics) -> Self {
44 Self { db, metrics }
45 }
46
47 #[cfg(feature = "events")]
48 fn emit(&self, event: CommerceEvent) {
49 self.event_system.emit(event);
50 }
51
52 #[cfg(feature = "events")]
53 fn emit_status_change(&self, previous: &Return, updated: &Return) {
54 if previous.status != updated.status {
55 self.emit(CommerceEvent::ReturnStatusChanged {
56 return_id: updated.id,
57 from_status: previous.status,
58 to_status: updated.status,
59 timestamp: updated.updated_at,
60 });
61 }
62 }
63
64 pub fn create(&self, input: CreateReturn) -> Result<Return> {
84 input.validate()?;
87 let ret = self.db.returns().create(input)?;
88 self.metrics.record_return_requested(&ret.id.to_string());
89 #[cfg(feature = "events")]
90 {
91 self.emit(CommerceEvent::ReturnRequested {
92 return_id: ret.id,
93 order_id: ret.order_id,
94 customer_id: ret.customer_id,
95 reason: ret.reason,
96 item_count: ret.items.len(),
97 timestamp: ret.created_at,
98 });
99 }
100 Ok(ret)
101 }
102
103 pub fn get(&self, id: ReturnId) -> Result<Option<Return>> {
105 self.db.returns().get(id)
106 }
107
108 pub fn update(&self, id: ReturnId, input: UpdateReturn) -> Result<Return> {
110 #[cfg(feature = "events")]
111 let previous = self.db.returns().get(id)?;
112
113 let updated = self.db.returns().update(id, input)?;
114
115 #[cfg(feature = "events")]
116 if let Some(previous) = previous {
117 if previous.status != updated.status {
118 self.emit_status_change(&previous, &updated);
119 match updated.status {
120 ReturnStatus::Approved => {
121 self.emit(CommerceEvent::ReturnApproved {
122 return_id: updated.id,
123 order_id: updated.order_id,
124 timestamp: updated.updated_at,
125 });
126 }
127 ReturnStatus::Rejected => {
128 self.emit(CommerceEvent::ReturnRejected {
129 return_id: updated.id,
130 order_id: updated.order_id,
131 reason: updated.notes.clone().unwrap_or_default(),
132 timestamp: updated.updated_at,
133 });
134 }
135 ReturnStatus::Completed => {
136 let refund_amount = updated.refund_amount.unwrap_or(Decimal::ZERO);
137 self.emit(CommerceEvent::ReturnCompleted {
138 return_id: updated.id,
139 order_id: updated.order_id,
140 refund_amount,
141 timestamp: updated.updated_at,
142 });
143 if let (Some(amount), Some(method)) =
144 (updated.refund_amount, updated.refund_method.clone())
145 {
146 self.emit(CommerceEvent::RefundIssued {
147 return_id: updated.id,
148 order_id: updated.order_id,
149 amount,
150 method,
151 timestamp: updated.updated_at,
152 });
153 }
154 }
155 _ => {}
156 }
157 }
158 }
159
160 Ok(updated)
161 }
162
163 pub fn list(&self, filter: ReturnFilter) -> Result<Vec<Return>> {
165 self.db.returns().list(filter)
166 }
167
168 pub fn list_for_order(&self, order_id: OrderId) -> Result<Vec<Return>> {
170 self.db.returns().list(ReturnFilter { order_id: Some(order_id), ..Default::default() })
171 }
172
173 pub fn list_for_customer(&self, customer_id: CustomerId) -> Result<Vec<Return>> {
175 self.db
176 .returns()
177 .list(ReturnFilter { customer_id: Some(customer_id), ..Default::default() })
178 }
179
180 pub fn approve(&self, id: ReturnId) -> Result<Return> {
182 #[cfg(feature = "events")]
183 let previous = self.db.returns().get(id)?;
184
185 let ret = self.db.returns().approve(id)?;
186
187 #[cfg(feature = "events")]
188 {
189 if let Some(previous) = previous {
190 self.emit_status_change(&previous, &ret);
191 }
192 self.emit(CommerceEvent::ReturnApproved {
193 return_id: ret.id,
194 order_id: ret.order_id,
195 timestamp: ret.updated_at,
196 });
197 }
198
199 Ok(ret)
200 }
201
202 pub fn reject(&self, id: ReturnId, reason: &str) -> Result<Return> {
204 #[cfg(feature = "events")]
205 let previous = self.db.returns().get(id)?;
206
207 let ret = self.db.returns().reject(id, reason)?;
208
209 #[cfg(feature = "events")]
210 {
211 if let Some(previous) = previous {
212 self.emit_status_change(&previous, &ret);
213 }
214 self.emit(CommerceEvent::ReturnRejected {
215 return_id: ret.id,
216 order_id: ret.order_id,
217 reason: reason.to_string(),
218 timestamp: ret.updated_at,
219 });
220 }
221
222 Ok(ret)
223 }
224
225 pub fn mark_received(&self, id: ReturnId) -> Result<Return> {
227 self.update(id, UpdateReturn { status: Some(ReturnStatus::Received), ..Default::default() })
228 }
229
230 pub fn complete(&self, id: ReturnId) -> Result<Return> {
232 #[cfg(feature = "events")]
233 let previous = self.db.returns().get(id)?;
234
235 let ret = self.db.returns().complete(id)?;
236
237 #[cfg(feature = "events")]
238 {
239 if let Some(previous) = previous {
240 self.emit_status_change(&previous, &ret);
241 }
242 let refund_amount = ret.refund_amount.unwrap_or(Decimal::ZERO);
243 self.emit(CommerceEvent::ReturnCompleted {
244 return_id: ret.id,
245 order_id: ret.order_id,
246 refund_amount,
247 timestamp: ret.updated_at,
248 });
249 if let (Some(amount), Some(method)) = (ret.refund_amount, ret.refund_method.clone()) {
250 self.emit(CommerceEvent::RefundIssued {
251 return_id: ret.id,
252 order_id: ret.order_id,
253 amount,
254 method,
255 timestamp: ret.updated_at,
256 });
257 }
258 }
259
260 Ok(ret)
261 }
262
263 pub fn cancel(&self, id: ReturnId) -> Result<Return> {
265 #[cfg(feature = "events")]
266 let previous = self.db.returns().get(id)?;
267
268 let ret = self.db.returns().cancel(id)?;
269
270 #[cfg(feature = "events")]
271 if let Some(previous) = previous {
272 self.emit_status_change(&previous, &ret);
273 }
274
275 Ok(ret)
276 }
277
278 pub fn count(&self, filter: ReturnFilter) -> Result<u64> {
280 self.db.returns().count(filter)
281 }
282
283 pub fn add_tracking(&self, id: ReturnId, tracking_number: &str) -> Result<Return> {
285 self.update(
286 id,
287 UpdateReturn {
288 tracking_number: Some(tracking_number.to_string()),
289 status: Some(ReturnStatus::InTransit),
290 ..Default::default()
291 },
292 )
293 }
294
295 pub fn list_pending(&self) -> Result<Vec<Return>> {
297 self.db
298 .returns()
299 .list(ReturnFilter { status: Some(ReturnStatus::Requested), ..Default::default() })
300 }
301}