Skip to main content

cala_ledger/account_set/
entity.rs

1use derive_builder::Builder;
2use es_entity::*;
3use serde::{Deserialize, Serialize};
4
5pub use cala_types::{
6    account_set::*, primitives::AccountSetId, velocity::VelocityContextAccountValues,
7};
8
9use crate::primitives::*;
10
11#[derive(EsEvent, Debug, Serialize, Deserialize)]
12#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
13#[serde(tag = "type", rename_all = "snake_case")]
14#[es_event(id = "AccountSetId", event_context = false)]
15pub enum AccountSetEvent {
16    Initialized {
17        values: AccountSetValues,
18    },
19    Updated {
20        values: AccountSetValues,
21        fields: Vec<String>,
22    },
23}
24
25#[derive(EsEntity, Builder)]
26#[builder(pattern = "owned", build_fn(error = "EntityHydrationError"))]
27pub struct AccountSet {
28    pub id: AccountSetId,
29    values: AccountSetValues,
30    events: EntityEvents<AccountSetEvent>,
31}
32
33impl AccountSet {
34    pub fn id(&self) -> AccountSetId {
35        self.values.id
36    }
37
38    pub fn values(&self) -> &AccountSetValues {
39        &self.values
40    }
41
42    pub fn update(&mut self, builder: impl Into<AccountSetUpdate>) -> es_entity::Idempotent<()> {
43        let AccountSetUpdateValues {
44            external_id,
45            name,
46            normal_balance_type,
47            description,
48            metadata,
49        } = builder
50            .into()
51            .build()
52            .expect("AccountSetUpdateValues always exist");
53        let mut updated_fields = Vec::new();
54
55        if let Some(name) = name {
56            if name != self.values().name {
57                self.values.name.clone_from(&name);
58                updated_fields.push("name".to_string());
59            }
60        }
61        if let Some(normal_balance_type) = normal_balance_type {
62            if normal_balance_type != self.values().normal_balance_type {
63                self.values
64                    .normal_balance_type
65                    .clone_from(&normal_balance_type);
66                updated_fields.push("normal_balance_type".to_string());
67            }
68        }
69        if external_id.is_some() && external_id != self.values().external_id {
70            self.values.external_id.clone_from(&external_id);
71            updated_fields.push("external_id".to_string());
72        }
73        if description.is_some() && description != self.values().description {
74            self.values.description.clone_from(&description);
75            updated_fields.push("description".to_string());
76        }
77        if let Some(metadata) = metadata {
78            if metadata != serde_json::Value::Null
79                && Some(&metadata) != self.values().metadata.as_ref()
80            {
81                self.values.metadata = Some(metadata);
82                updated_fields.push("metadata".to_string());
83            }
84        }
85
86        if updated_fields.is_empty() {
87            return es_entity::Idempotent::AlreadyApplied;
88        } else {
89            self.events.push(AccountSetEvent::Updated {
90                values: self.values.clone(),
91                fields: updated_fields,
92            });
93        }
94        es_entity::Idempotent::Executed(())
95    }
96
97    pub fn into_values(self) -> AccountSetValues {
98        self.values
99    }
100
101    pub fn created_at(&self) -> chrono::DateTime<chrono::Utc> {
102        self.events
103            .entity_first_persisted_at()
104            .expect("Entity not persisted")
105    }
106
107    pub fn modified_at(&self) -> chrono::DateTime<chrono::Utc> {
108        self.events
109            .entity_last_modified_at()
110            .expect("Entity not persisted")
111    }
112}
113
114#[derive(Debug, Builder, Default)]
115#[builder(name = "AccountSetUpdate", default)]
116pub struct AccountSetUpdateValues {
117    #[builder(setter(strip_option, into))]
118    pub external_id: Option<String>,
119    #[builder(setter(into, strip_option))]
120    pub name: Option<String>,
121    #[builder(setter(into, strip_option))]
122    pub normal_balance_type: Option<DebitOrCredit>,
123    #[builder(setter(into, strip_option))]
124    pub description: Option<String>,
125    #[builder(setter(custom))]
126    pub metadata: Option<serde_json::Value>,
127}
128
129impl AccountSetUpdate {
130    pub fn metadata<T: serde::Serialize>(
131        &mut self,
132        metadata: T,
133    ) -> Result<&mut Self, serde_json::Error> {
134        self.metadata = Some(Some(serde_json::to_value(metadata)?));
135        Ok(self)
136    }
137}
138
139impl From<(AccountSetValues, Vec<String>)> for AccountSetUpdate {
140    fn from((values, fields): (AccountSetValues, Vec<String>)) -> Self {
141        let mut builder = AccountSetUpdate::default();
142
143        for field in fields {
144            match field.as_str() {
145                "external_id" => {
146                    if let Some(ref ext_id) = values.external_id {
147                        builder.external_id(ext_id);
148                    }
149                }
150                "name" => {
151                    builder.name(values.name.clone());
152                }
153
154                "normal_balance_type" => {
155                    builder.normal_balance_type(values.normal_balance_type);
156                }
157
158                "description" => {
159                    if let Some(ref desc) = values.description {
160                        builder.description(desc);
161                    }
162                }
163
164                "metadata" => {
165                    if let Some(metadata) = values.metadata.clone() {
166                        builder
167                            .metadata(metadata)
168                            .expect("Failed to serialize metadata");
169                    }
170                }
171                _ => unreachable!("Unknown field: {}", field),
172            }
173        }
174        builder
175    }
176}
177
178impl TryFromEvents<AccountSetEvent> for AccountSet {
179    fn try_from_events(
180        events: EntityEvents<AccountSetEvent>,
181    ) -> Result<Self, EntityHydrationError> {
182        let mut builder = AccountSetBuilder::default();
183        for event in events.iter_all() {
184            match event {
185                AccountSetEvent::Initialized { values } => {
186                    builder = builder.id(values.id).values(values.clone());
187                }
188                AccountSetEvent::Updated { values, .. } => {
189                    builder = builder.values(values.clone());
190                }
191            }
192        }
193        builder.events(events).build()
194    }
195}
196
197/// Representation of a ***new*** ledger account set entity with required/optional properties and a builder.
198#[derive(Builder, Debug)]
199pub struct NewAccountSet {
200    #[builder(setter(into))]
201    pub id: AccountSetId,
202    #[builder(setter(into))]
203    pub(super) name: String,
204    #[builder(setter(strip_option, into), default)]
205    pub(super) external_id: Option<String>,
206    #[builder(setter(into))]
207    pub(super) journal_id: JournalId,
208    #[builder(default)]
209    pub(super) normal_balance_type: DebitOrCredit,
210    #[builder(default)]
211    pub(super) eventually_consistent: bool,
212    #[builder(setter(strip_option, into), default)]
213    pub(super) description: Option<String>,
214    #[builder(setter(custom), default)]
215    pub(super) metadata: Option<serde_json::Value>,
216}
217
218impl NewAccountSet {
219    pub fn builder() -> NewAccountSetBuilder {
220        NewAccountSetBuilder::default()
221    }
222
223    pub(super) fn context_values(&self) -> VelocityContextAccountValues {
224        VelocityContextAccountValues {
225            id: self.id.into(),
226            name: self.name.clone(),
227            normal_balance_type: self.normal_balance_type,
228            external_id: self.external_id.clone(),
229            metadata: self.metadata.clone(),
230        }
231    }
232}
233
234impl IntoEvents<AccountSetEvent> for NewAccountSet {
235    fn into_events(self) -> EntityEvents<AccountSetEvent> {
236        EntityEvents::init(
237            self.id,
238            [AccountSetEvent::Initialized {
239                values: AccountSetValues {
240                    id: self.id,
241                    version: 1,
242                    journal_id: self.journal_id,
243                    name: self.name,
244                    external_id: self.external_id,
245                    normal_balance_type: self.normal_balance_type,
246                    description: self.description,
247                    metadata: self.metadata,
248                },
249            }],
250        )
251    }
252}
253
254impl NewAccountSetBuilder {
255    pub fn metadata<T: serde::Serialize>(
256        &mut self,
257        metadata: T,
258    ) -> Result<&mut Self, serde_json::Error> {
259        self.metadata = Some(Some(serde_json::to_value(metadata)?));
260        Ok(self)
261    }
262}