use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use super::error::TransferError;
use super::operator::ResponsibleOperator;
use super::status::TransferStatus;
use crate::domain::passport::PassportId;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub enum TransferReason {
Sale,
Return,
Remanufacturing,
Repurposing,
PreparationForReuse,
Import,
InsolvencySuccession,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransferRecord {
pub transfer_id: Uuid,
pub passport_id: PassportId,
pub from_operator: ResponsibleOperator,
pub to_operator: ResponsibleOperator,
pub reason: TransferReason,
pub from_signature: Option<String>,
pub to_signature: Option<String>,
pub initiated_at: DateTime<Utc>,
pub completed_at: Option<DateTime<Utc>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rejected_at: Option<DateTime<Utc>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cancelled_at: Option<DateTime<Utc>>,
pub notes: Option<String>,
}
impl TransferRecord {
#[must_use]
pub fn signing_payload(&self) -> serde_json::Value {
serde_json::json!({
"transferId": self.transfer_id,
"passportId": self.passport_id,
"fromOperator": self.from_operator,
"toOperator": self.to_operator,
"reason": self.reason,
"initiatedAt": self.initiated_at,
})
}
pub fn status(&self) -> TransferStatus {
if self.rejected_at.is_some() {
return TransferStatus::Rejected;
}
if self.cancelled_at.is_some() {
return TransferStatus::Cancelled;
}
match (&self.from_signature, &self.to_signature, &self.completed_at) {
(Some(_), Some(_), Some(_)) => TransferStatus::Completed,
(Some(_), Some(_), None) => TransferStatus::Accepted,
_ => TransferStatus::Initiated,
}
}
pub fn is_complete(&self) -> bool {
self.from_signature.is_some() && self.to_signature.is_some() && self.completed_at.is_some()
}
pub fn reject(&mut self) -> Result<(), TransferError> {
let s = self.status();
if s != TransferStatus::Initiated {
return Err(TransferError::InvalidState {
current: s,
action: "reject".into(),
});
}
self.rejected_at = Some(Utc::now());
Ok(())
}
pub fn cancel(&mut self) -> Result<(), TransferError> {
match self.status() {
TransferStatus::Initiated | TransferStatus::Accepted => {
self.cancelled_at = Some(Utc::now());
Ok(())
}
s => Err(TransferError::InvalidState {
current: s,
action: "cancel".into(),
}),
}
}
pub fn complete(&mut self) -> Result<(), TransferError> {
let s = self.status();
if s != TransferStatus::Accepted {
return Err(TransferError::InvalidState {
current: s,
action: "complete".into(),
});
}
self.completed_at = Some(Utc::now());
Ok(())
}
}