use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::{gen_code, HasId, HasName, HasReference, TMFEvent, TimePeriod};
use tmflib_derive::{HasId, HasName};
use crate::common::{
contact::ContactMedium,
event::{Event, EventPayload},
related_party::RelatedParty,
};
use super::{Characteristic, MOD_PATH};
const CLASS_PATH: &str = "organization";
const CODE_PREFIX: &str = "O-";
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum OrganizationStateType {
#[default]
Initialized,
Validated,
Closed,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OrganizationRef {
pub id: String,
pub href: String,
pub name: String,
}
impl From<Organization> for OrganizationRef {
fn from(value: Organization) -> Self {
OrganizationRef {
id: value.get_id(),
href: value.get_href(),
name: value.get_name(),
}
}
}
#[derive(Clone, Debug, Default, Deserialize, HasId, HasName, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Organization {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub href: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
is_head_office: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
is_legal_entity: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub organization_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub trading_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub exists_during: Option<TimePeriod>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<OrganizationStateType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub contact_medium: Option<Vec<ContactMedium>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub related_party: Option<Vec<RelatedParty>>,
#[serde(skip_serializing_if = "Option::is_none")]
party_characteristic: Option<Vec<Characteristic>>,
}
impl Organization {
pub fn new(name: impl Into<String>) -> Organization {
let mut org = Organization::create();
let name: String = name.into();
let name = name.trim();
org.name = Some(name.to_string());
org.status = Some(OrganizationStateType::default());
org.related_party = Some(vec![]);
org.generate_code(None);
org
}
pub fn generate_code(&mut self, offset: Option<u32>) {
let (code, _hash) = gen_code(
self.get_name(),
self.get_id(),
offset,
Some(CODE_PREFIX.to_string()),
None,
);
let characteristic = Characteristic {
name: String::from("code"),
name_type: String::from("String"),
value: code,
..Default::default()
};
self.replace_characteristic(characteristic);
}
pub fn replace_characteristic(
&mut self,
characteristic: Characteristic,
) -> Option<Characteristic> {
match self.party_characteristic.as_mut() {
Some(c) => {
let pos = c.iter().position(|c| c.name == characteristic.name);
match pos {
Some(u) => {
let old = c[u].clone();
c[u] = characteristic;
Some(old)
}
None => {
c.push(characteristic);
None
}
}
}
None => {
self.party_characteristic = Some(vec![characteristic]);
None
}
}
}
}
impl From<String> for Organization {
fn from(value: String) -> Self {
Organization::new(value)
}
}
impl HasReference for Organization {
type RefType = RelatedParty;
fn as_ref(&self) -> Option<Self::RefType> {
Some(RelatedParty::from(self))
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum OrganizationEventType {
OrganizationCreateEvent,
OrganizationAttributeValueChangeEvent,
OrganizationStateChangeEvent,
OrganizationDeleteEvent,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct OrganizationEvent {
pub organization: Organization,
}
impl TMFEvent<OrganizationEvent> for Organization {
fn event(&self) -> OrganizationEvent {
OrganizationEvent {
organization: self.clone(),
}
}
}
impl EventPayload<OrganizationEvent> for Organization {
type Subject = Organization;
type EventType = OrganizationEventType;
fn to_event(&self, event_type: Self::EventType) -> Event<OrganizationEvent, Self::EventType> {
let now = Utc::now();
let desc = format!(
"{:?} for {} [{}]",
event_type,
self.get_name(),
self.get_id()
);
let event_time = DateTime::from_timestamp(now.timestamp(), 0).unwrap();
Event {
correlation_id: None,
description: Some(desc),
domain: Some(Organization::get_class()),
event_id: Uuid::new_v4().to_string(),
field_path: None,
href: Some(self.get_href()),
id: Some(self.get_id()),
title: Some(self.get_name()),
event_time: event_time.to_string(),
priority: None,
time_occurred: Some(event_time.to_string()),
event_type,
event: self.event(),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_org_new_name() {
let org = Organization::new("AnOrganization");
assert_eq!(org.name, Some("AnOrganization".into()));
}
#[test]
fn test_org_new_state() {
let org = Organization::new("AnOrganization");
assert_eq!(org.status, Some(OrganizationStateType::default()));
}
}