cala_ledger/account_set/
entity.rs1use 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#[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}