use chrono::{DateTime, NaiveDate, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use utoipa::ToSchema;
use super::{Address, ContactPoint, Gender, Identifier, IdentityDocument, EmergencyContact};
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct Worker {
pub id: Uuid,
pub identifiers: Vec<Identifier>,
pub active: bool,
pub name: HumanName,
pub additional_names: Vec<HumanName>,
pub telecom: Vec<ContactPoint>,
pub gender: Gender,
#[serde(default)]
pub worker_type: Option<WorkerType>,
pub birth_date: Option<NaiveDate>,
#[serde(default)]
pub tax_id: Option<String>,
#[serde(default)]
pub documents: Vec<IdentityDocument>,
#[serde(default)]
pub emergency_contacts: Vec<EmergencyContact>,
pub deceased: bool,
pub deceased_datetime: Option<DateTime<Utc>>,
pub addresses: Vec<Address>,
pub marital_status: Option<String>,
pub multiple_birth: Option<bool>,
pub photo: Vec<String>,
pub managing_organization: Option<Uuid>,
pub links: Vec<WorkerLink>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct HumanName {
pub use_type: Option<NameUse>,
pub family: String,
pub given: Vec<String>,
pub prefix: Vec<String>,
pub suffix: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "lowercase")]
pub enum NameUse {
Usual,
Official,
Temp,
Nickname,
Anonymous,
Old,
Maiden,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum WorkerType {
Doctor,
Nurse,
Carer,
Staff,
Employee,
Manager,
Supervisor,
Consultant,
Other,
}
impl std::fmt::Display for WorkerType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
WorkerType::Doctor => write!(f, "doctor"),
WorkerType::Nurse => write!(f, "nurse"),
WorkerType::Carer => write!(f, "carer"),
WorkerType::Staff => write!(f, "staff"),
WorkerType::Employee => write!(f, "employee"),
WorkerType::Manager => write!(f, "manager"),
WorkerType::Supervisor => write!(f, "supervisor"),
WorkerType::Consultant => write!(f, "consultant"),
WorkerType::Other => write!(f, "other"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct WorkerLink {
pub other_worker_id: Uuid,
pub link_type: LinkType,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "lowercase")]
pub enum LinkType {
ReplacedBy,
Replaces,
Refer,
Seealso,
}
impl Worker {
pub fn new(name: HumanName, gender: Gender) -> Self {
let now = Utc::now();
Self {
id: Uuid::new_v4(),
identifiers: Vec::new(),
active: true,
name,
additional_names: Vec::new(),
telecom: Vec::new(),
gender,
worker_type: None,
birth_date: None,
tax_id: None,
documents: Vec::new(),
emergency_contacts: Vec::new(),
deceased: false,
deceased_datetime: None,
addresses: Vec::new(),
marital_status: None,
multiple_birth: None,
photo: Vec::new(),
managing_organization: None,
links: Vec::new(),
created_at: now,
updated_at: now,
}
}
pub fn full_name(&self) -> String {
let given = self.name.given.join(" ");
format!("{} {}", given, self.name.family)
}
pub fn effective_tax_id(&self) -> Option<&str> {
if let Some(ref tid) = self.tax_id {
return Some(tid.as_str());
}
self.identifiers.iter()
.find(|id| id.identifier_type == super::IdentifierType::TAX)
.map(|id| id.value.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::models::Gender;
#[test]
fn test_worker_new_defaults() {
let name = HumanName {
use_type: None,
family: "Doe".into(),
given: vec!["Jane".into()],
prefix: vec![],
suffix: vec![],
};
let worker = Worker::new(name, Gender::Female);
assert!(worker.active);
assert!(!worker.deceased);
assert_eq!(worker.gender, Gender::Female);
assert_eq!(worker.name.family, "Doe");
assert_eq!(worker.name.given, vec!["Jane".to_string()]);
assert!(worker.identifiers.is_empty());
assert!(worker.addresses.is_empty());
assert!(worker.telecom.is_empty());
assert!(worker.documents.is_empty());
assert!(worker.emergency_contacts.is_empty());
assert!(worker.links.is_empty());
assert!(worker.worker_type.is_none());
assert!(worker.birth_date.is_none());
assert!(worker.tax_id.is_none());
assert!(worker.marital_status.is_none());
assert!(worker.managing_organization.is_none());
}
#[test]
fn test_worker_serialization_roundtrip() {
let name = HumanName {
use_type: Some(NameUse::Official),
family: "Smith".into(),
given: vec!["John".into(), "Michael".into()],
prefix: vec!["Dr.".into()],
suffix: vec!["Jr.".into()],
};
let mut worker = Worker::new(name, Gender::Male);
worker.birth_date = Some(chrono::NaiveDate::from_ymd_opt(1985, 3, 20).unwrap());
worker.tax_id = Some("123-45-6789".into());
let json = serde_json::to_string(&worker).expect("Serialization should succeed");
let deserialized: Worker = serde_json::from_str(&json).expect("Deserialization should succeed");
assert_eq!(deserialized.name.family, "Smith");
assert_eq!(deserialized.name.given.len(), 2);
assert_eq!(deserialized.gender, Gender::Male);
assert_eq!(deserialized.tax_id.as_deref(), Some("123-45-6789"));
assert_eq!(deserialized.birth_date, worker.birth_date);
}
#[test]
fn test_human_name_display() {
let name = HumanName {
use_type: None,
family: "Garcia".into(),
given: vec!["Maria".into(), "Elena".into()],
prefix: vec![],
suffix: vec![],
};
let worker = Worker::new(name, Gender::Female);
let full = worker.full_name();
assert_eq!(full, "Maria Elena Garcia");
}
#[test]
fn test_worker_type_variants() {
let types = vec![
WorkerType::Doctor,
WorkerType::Nurse,
WorkerType::Carer,
WorkerType::Staff,
WorkerType::Employee,
WorkerType::Manager,
WorkerType::Supervisor,
WorkerType::Consultant,
WorkerType::Other,
];
for wt in types {
let json = serde_json::to_string(&wt).expect("WorkerType serialization");
let deser: WorkerType = serde_json::from_str(&json).expect("WorkerType deserialization");
assert_eq!(deser, wt);
}
}
#[test]
fn test_worker_with_worker_type() {
let name = HumanName {
use_type: None,
family: "Chen".into(),
given: vec!["Wei".into()],
prefix: vec!["Dr.".into()],
suffix: vec![],
};
let mut worker = Worker::new(name, Gender::Male);
worker.worker_type = Some(WorkerType::Doctor);
assert_eq!(worker.worker_type, Some(WorkerType::Doctor));
assert_eq!(worker.worker_type.as_ref().unwrap().to_string(), "doctor");
let json = serde_json::to_string(&worker).expect("Serialization");
let deser: Worker = serde_json::from_str(&json).expect("Deserialization");
assert_eq!(deser.worker_type, Some(WorkerType::Doctor));
}
#[test]
fn test_gender_variants() {
let genders = vec![Gender::Male, Gender::Female, Gender::Other, Gender::Unknown];
for g in genders {
let json = serde_json::to_string(&g).expect("Gender serialization");
let deser: Gender = serde_json::from_str(&json).expect("Gender deserialization");
assert_eq!(deser, g);
}
}
}