use serde::{Deserialize, Serialize};
use super::{bool_from_int_default_false, PayrixId};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
#[serde(rename_all = "camelCase")]
pub struct Note {
pub id: PayrixId,
#[serde(default)]
pub created: Option<String>,
#[serde(default)]
pub modified: Option<String>,
#[serde(default)]
pub creator: Option<PayrixId>,
#[serde(default)]
pub modifier: Option<PayrixId>,
#[serde(default)]
pub login: Option<PayrixId>,
#[serde(default)]
pub hold: Option<PayrixId>,
#[serde(default)]
pub txn: Option<PayrixId>,
#[serde(default)]
pub terminal_txn: Option<PayrixId>,
#[serde(default)]
pub entity: Option<PayrixId>,
#[serde(default, rename = "type")]
pub note_type: Option<String>,
#[serde(default)]
pub note: Option<String>,
#[serde(default)]
pub data: Option<String>,
#[serde(default)]
pub pinned: Option<i32>,
#[serde(default)]
pub pinned_date: Option<String>,
#[serde(default, with = "bool_from_int_default_false")]
pub inactive: bool,
#[serde(default, with = "bool_from_int_default_false")]
pub frozen: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum NoteDocumentPurpose {
#[default]
General,
PersonalId,
CompanyId,
VoidCheck,
BankStatement,
BankLetter,
Contract,
TaxDocument,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum NoteDocumentFileType {
#[default]
Jpg,
Jpeg,
Gif,
Png,
Pdf,
Tif,
Tiff,
Txt,
Xml,
Asc,
Rtf,
Xls,
Xlsx,
Doc,
Docx,
Odt,
Ods,
Json,
Soap,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum NoteDocumentStatus {
#[default]
Created,
Processed,
Failed,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
#[serde(rename_all = "camelCase")]
pub struct NoteDocument {
pub id: PayrixId,
#[serde(default)]
pub created: Option<String>,
#[serde(default)]
pub modified: Option<String>,
#[serde(default)]
pub creator: Option<PayrixId>,
#[serde(default)]
pub modifier: Option<PayrixId>,
#[serde(default)]
pub note: Option<PayrixId>,
#[serde(default)]
pub custom: Option<String>,
#[serde(default, rename = "type")]
pub file_type: Option<String>,
#[serde(default)]
pub document_type: Option<NoteDocumentPurpose>,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub status: Option<NoteDocumentStatus>,
#[serde(default, with = "bool_from_int_default_false")]
pub inactive: bool,
#[serde(default, with = "bool_from_int_default_false")]
pub frozen: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn note_deserialize_full() {
let json = r#"{
"id": "t1_nte_12345678901234567890123",
"created": "2024-01-01 00:00:00.0000",
"modified": "2024-01-02 23:59:59.9999",
"creator": "t1_lgn_12345678901234567890123",
"modifier": "t1_lgn_12345678901234567890124",
"login": "t1_lgn_12345678901234567890125",
"hold": "t1_hld_12345678901234567890123",
"txn": "t1_txn_12345678901234567890123",
"terminalTxn": "t1_ttx_12345678901234567890123",
"entity": "t1_ent_12345678901234567890123",
"type": "note",
"note": "Account reviewed for compliance",
"data": "Additional details here",
"pinned": 1,
"pinnedDate": "2024-01-01 10:00:00",
"inactive": 0,
"frozen": 1
}"#;
let note: Note = serde_json::from_str(json).unwrap();
assert_eq!(note.id.as_str(), "t1_nte_12345678901234567890123");
assert_eq!(note.created, Some("2024-01-01 00:00:00.0000".to_string()));
assert_eq!(note.modified, Some("2024-01-02 23:59:59.9999".to_string()));
assert_eq!(
note.creator.as_ref().map(|c| c.as_str()),
Some("t1_lgn_12345678901234567890123")
);
assert_eq!(
note.modifier.as_ref().map(|m| m.as_str()),
Some("t1_lgn_12345678901234567890124")
);
assert_eq!(
note.login.as_ref().map(|l| l.as_str()),
Some("t1_lgn_12345678901234567890125")
);
assert_eq!(
note.hold.as_ref().map(|h| h.as_str()),
Some("t1_hld_12345678901234567890123")
);
assert_eq!(
note.txn.as_ref().map(|t| t.as_str()),
Some("t1_txn_12345678901234567890123")
);
assert_eq!(
note.terminal_txn.as_ref().map(|t| t.as_str()),
Some("t1_ttx_12345678901234567890123")
);
assert_eq!(
note.entity.as_ref().map(|e| e.as_str()),
Some("t1_ent_12345678901234567890123")
);
assert_eq!(note.note_type, Some("note".to_string()));
assert_eq!(
note.note,
Some("Account reviewed for compliance".to_string())
);
assert_eq!(note.data, Some("Additional details here".to_string()));
assert_eq!(note.pinned, Some(1));
assert_eq!(note.pinned_date, Some("2024-01-01 10:00:00".to_string()));
assert!(!note.inactive);
assert!(note.frozen);
}
#[test]
fn note_deserialize_minimal() {
let json = r#"{"id": "t1_nte_12345678901234567890123"}"#;
let note: Note = serde_json::from_str(json).unwrap();
assert_eq!(note.id.as_str(), "t1_nte_12345678901234567890123");
assert!(note.created.is_none());
assert!(note.modified.is_none());
assert!(note.creator.is_none());
assert!(note.modifier.is_none());
assert!(note.login.is_none());
assert!(note.hold.is_none());
assert!(note.txn.is_none());
assert!(note.terminal_txn.is_none());
assert!(note.entity.is_none());
assert!(note.note_type.is_none());
assert!(note.note.is_none());
assert!(note.data.is_none());
assert!(note.pinned.is_none());
assert!(note.pinned_date.is_none());
assert!(!note.inactive);
assert!(!note.frozen);
}
#[test]
fn note_various_types() {
let types = vec![
"note",
"release",
"review",
"reReview",
"riskApproved",
"riskPending",
"riskDenied",
];
for note_type in types {
let json = format!(
r#"{{"id": "t1_nte_12345678901234567890123", "type": "{}"}}"#,
note_type
);
let note: Note = serde_json::from_str(&json).unwrap();
assert_eq!(note.note_type, Some(note_type.to_string()));
}
}
#[test]
fn note_bool_from_int() {
let json = r#"{"id": "t1_nte_12345678901234567890123", "inactive": 1, "frozen": 0}"#;
let note: Note = serde_json::from_str(json).unwrap();
assert!(note.inactive);
assert!(!note.frozen);
}
#[test]
fn note_serialize_roundtrip() {
let json = r#"{
"id": "t1_nte_12345678901234567890123",
"entity": "t1_ent_12345678901234567890123",
"type": "note",
"note": "Test note"
}"#;
let note: Note = serde_json::from_str(json).unwrap();
let serialized = serde_json::to_string(¬e).unwrap();
let deserialized: Note = serde_json::from_str(&serialized).unwrap();
assert_eq!(note.id, deserialized.id);
assert_eq!(note.entity, deserialized.entity);
assert_eq!(note.note_type, deserialized.note_type);
assert_eq!(note.note, deserialized.note);
}
#[test]
fn note_document_deserialize_full() {
let json = r#"{
"id": "t1_ntd_12345678901234567890123",
"created": "2024-01-01 00:00:00.0000",
"modified": "2024-01-02 23:59:59.9999",
"creator": "t1_lgn_12345678901234567890123",
"modifier": "t1_lgn_12345678901234567890124",
"note": "t1_nte_12345678901234567890123",
"custom": "custom data",
"type": "png",
"documentType": "voidCheck",
"name": "VoidCheck.png",
"description": "Void check for bank verification",
"status": "processed",
"inactive": 0,
"frozen": 0
}"#;
let doc: NoteDocument = serde_json::from_str(json).unwrap();
assert_eq!(doc.id.as_str(), "t1_ntd_12345678901234567890123");
assert_eq!(doc.created, Some("2024-01-01 00:00:00.0000".to_string()));
assert_eq!(doc.modified, Some("2024-01-02 23:59:59.9999".to_string()));
assert_eq!(
doc.creator.as_ref().map(|c| c.as_str()),
Some("t1_lgn_12345678901234567890123")
);
assert_eq!(
doc.modifier.as_ref().map(|m| m.as_str()),
Some("t1_lgn_12345678901234567890124")
);
assert_eq!(
doc.note.as_ref().map(|n| n.as_str()),
Some("t1_nte_12345678901234567890123")
);
assert_eq!(doc.custom, Some("custom data".to_string()));
assert_eq!(doc.file_type, Some("png".to_string()));
assert_eq!(doc.document_type, Some(NoteDocumentPurpose::VoidCheck));
assert_eq!(doc.name, Some("VoidCheck.png".to_string()));
assert_eq!(
doc.description,
Some("Void check for bank verification".to_string())
);
assert_eq!(doc.status, Some(NoteDocumentStatus::Processed));
assert!(!doc.inactive);
assert!(!doc.frozen);
}
#[test]
fn note_document_deserialize_minimal() {
let json = r#"{"id": "t1_ntd_12345678901234567890123"}"#;
let doc: NoteDocument = serde_json::from_str(json).unwrap();
assert_eq!(doc.id.as_str(), "t1_ntd_12345678901234567890123");
assert!(doc.created.is_none());
assert!(doc.modified.is_none());
assert!(doc.creator.is_none());
assert!(doc.modifier.is_none());
assert!(doc.note.is_none());
assert!(doc.custom.is_none());
assert!(doc.file_type.is_none());
assert!(doc.document_type.is_none());
assert!(doc.name.is_none());
assert!(doc.description.is_none());
assert!(doc.status.is_none());
assert!(!doc.inactive);
assert!(!doc.frozen);
}
#[test]
fn note_document_various_file_types() {
let types = vec!["jpg", "jpeg", "gif", "png", "pdf", "tif", "tiff", "txt", "xml", "asc"];
for file_type in types {
let json = format!(
r#"{{"id": "t1_ntd_12345678901234567890123", "type": "{}"}}"#,
file_type
);
let doc: NoteDocument = serde_json::from_str(&json).unwrap();
assert_eq!(doc.file_type, Some(file_type.to_string()));
}
}
#[test]
fn note_document_purpose_enum() {
let purposes = vec![
("general", NoteDocumentPurpose::General),
("personalId", NoteDocumentPurpose::PersonalId),
("companyId", NoteDocumentPurpose::CompanyId),
("voidCheck", NoteDocumentPurpose::VoidCheck),
("bankStatement", NoteDocumentPurpose::BankStatement),
("bankLetter", NoteDocumentPurpose::BankLetter),
("contract", NoteDocumentPurpose::Contract),
("taxDocument", NoteDocumentPurpose::TaxDocument),
];
for (json_value, expected) in purposes {
let json = format!(
r#"{{"id": "t1_ntd_12345678901234567890123", "documentType": "{}"}}"#,
json_value
);
let doc: NoteDocument = serde_json::from_str(&json).unwrap();
assert_eq!(doc.document_type, Some(expected));
}
}
#[test]
fn note_document_status_enum() {
let statuses = vec![
("created", NoteDocumentStatus::Created),
("processed", NoteDocumentStatus::Processed),
("failed", NoteDocumentStatus::Failed),
];
for (json_value, expected) in statuses {
let json = format!(
r#"{{"id": "t1_ntd_12345678901234567890123", "status": "{}"}}"#,
json_value
);
let doc: NoteDocument = serde_json::from_str(&json).unwrap();
assert_eq!(doc.status, Some(expected));
}
}
#[test]
fn note_document_serialize_roundtrip() {
let json = r#"{
"id": "t1_ntd_12345678901234567890123",
"note": "t1_nte_12345678901234567890123",
"type": "pdf",
"documentType": "voidCheck",
"name": "check.pdf",
"status": "processed"
}"#;
let doc: NoteDocument = serde_json::from_str(json).unwrap();
let serialized = serde_json::to_string(&doc).unwrap();
let deserialized: NoteDocument = serde_json::from_str(&serialized).unwrap();
assert_eq!(doc.id, deserialized.id);
assert_eq!(doc.note, deserialized.note);
assert_eq!(doc.file_type, deserialized.file_type);
assert_eq!(doc.document_type, deserialized.document_type);
assert_eq!(doc.name, deserialized.name);
assert_eq!(doc.status, deserialized.status);
}
}