1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use sqlx::FromRow;
4use uuid::Uuid;
5use rust_decimal::Decimal;
6
7use super::PartyType;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[serde(transparent)]
12pub struct JournalLineId(pub Uuid);
13
14impl JournalLineId {
15 pub fn new(id: Uuid) -> Self { Self(id) }
16 pub fn generate() -> Self { Self(Uuid::new_v4()) }
17 pub fn into_inner(self) -> Uuid { self.0 }
18}
19
20impl std::fmt::Display for JournalLineId {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 write!(f, "{}", self.0)
23 }
24}
25
26impl std::str::FromStr for JournalLineId {
27 type Err = uuid::Error;
28 fn from_str(s: &str) -> Result<Self, Self::Err> {
29 Ok(Self(Uuid::parse_str(s)?))
30 }
31}
32
33impl From<Uuid> for JournalLineId {
34 fn from(id: Uuid) -> Self { Self(id) }
35}
36
37impl From<JournalLineId> for Uuid {
38 fn from(id: JournalLineId) -> Self { id.0 }
39}
40
41impl AsRef<Uuid> for JournalLineId {
42 fn as_ref(&self) -> &Uuid { &self.0 }
43}
44
45impl std::ops::Deref for JournalLineId {
46 type Target = Uuid;
47 fn deref(&self) -> &Self::Target { &self.0 }
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
51pub struct JournalLine {
52 pub id: Uuid,
53 pub journal_id: Uuid,
54 pub branch_id: Option<Uuid>,
55 pub party_type: Option<PartyType>,
56 pub party_id: Option<Uuid>,
57 pub line_number: i32,
58 pub account_id: Uuid,
59 pub account_number: String,
60 pub account_name: String,
61 pub debit_amount: Decimal,
62 pub credit_amount: Decimal,
63 pub currency: String,
64 pub exchange_rate: Decimal,
65 pub base_debit_amount: Decimal,
66 pub base_credit_amount: Decimal,
67 pub description: Option<String>,
68 pub cost_center_id: Option<Uuid>,
69 pub project_id: Option<Uuid>,
70 pub department_id: Option<Uuid>,
71 pub dimensions: Option<serde_json::Value>,
72 pub source_type: Option<String>,
73 pub source_id: Option<Uuid>,
74 pub source_reference: Option<String>,
75 pub is_tax_line: bool,
76 pub tax_rate: Option<Decimal>,
77 pub tax_base_amount: Option<Decimal>,
78 pub related_line_id: Option<Uuid>,
79 pub has_quantity: bool,
80 pub quantity: Option<Decimal>,
81 pub unit: Option<String>,
82 pub unit_price: Option<Decimal>,
83 pub is_reconciled: bool,
84 pub reconciliation_id: Option<Uuid>,
85 pub reconciled_at: Option<DateTime<Utc>>,
86 pub full_reconcile_id: Option<Uuid>,
87 pub is_posted: bool,
88 pub ledger_id: Option<Uuid>,
89 pub posted_at: Option<DateTime<Utc>>,
90 pub tags: serde_json::Value,
91 pub data: serde_json::Value,
92}
93
94impl JournalLine {
95 pub fn builder() -> JournalLineBuilder {
97 <JournalLineBuilder as Default>::default()
98 }
99
100 pub fn new(journal_id: Uuid, line_number: i32, account_id: Uuid, account_number: String, account_name: String, debit_amount: Decimal, credit_amount: Decimal, currency: String, exchange_rate: Decimal, base_debit_amount: Decimal, base_credit_amount: Decimal, is_tax_line: bool, has_quantity: bool, is_reconciled: bool, is_posted: bool, tags: serde_json::Value, data: serde_json::Value) -> Self {
102 Self {
103 id: Uuid::new_v4(),
104 journal_id,
105 branch_id: None,
106 party_type: None,
107 party_id: None,
108 line_number,
109 account_id,
110 account_number,
111 account_name,
112 debit_amount,
113 credit_amount,
114 currency,
115 exchange_rate,
116 base_debit_amount,
117 base_credit_amount,
118 description: None,
119 cost_center_id: None,
120 project_id: None,
121 department_id: None,
122 dimensions: None,
123 source_type: None,
124 source_id: None,
125 source_reference: None,
126 is_tax_line,
127 tax_rate: None,
128 tax_base_amount: None,
129 related_line_id: None,
130 has_quantity,
131 quantity: None,
132 unit: None,
133 unit_price: None,
134 is_reconciled,
135 reconciliation_id: None,
136 reconciled_at: None,
137 full_reconcile_id: None,
138 is_posted,
139 ledger_id: None,
140 posted_at: None,
141 tags,
142 data,
143 }
144 }
145
146 pub fn id(&self) -> &Uuid {
148 &self.id
149 }
150
151 pub fn typed_id(&self) -> JournalLineId {
153 JournalLineId(self.id)
154 }
155
156
157 pub fn with_branch_id(mut self, value: Uuid) -> Self {
163 self.branch_id = Some(value);
164 self
165 }
166
167 pub fn with_party_type(mut self, value: PartyType) -> Self {
169 self.party_type = Some(value);
170 self
171 }
172
173 pub fn with_party_id(mut self, value: Uuid) -> Self {
175 self.party_id = Some(value);
176 self
177 }
178
179 pub fn with_description(mut self, value: String) -> Self {
181 self.description = Some(value);
182 self
183 }
184
185 pub fn with_cost_center_id(mut self, value: Uuid) -> Self {
187 self.cost_center_id = Some(value);
188 self
189 }
190
191 pub fn with_project_id(mut self, value: Uuid) -> Self {
193 self.project_id = Some(value);
194 self
195 }
196
197 pub fn with_department_id(mut self, value: Uuid) -> Self {
199 self.department_id = Some(value);
200 self
201 }
202
203 pub fn with_dimensions(mut self, value: serde_json::Value) -> Self {
205 self.dimensions = Some(value);
206 self
207 }
208
209 pub fn with_source_type(mut self, value: String) -> Self {
211 self.source_type = Some(value);
212 self
213 }
214
215 pub fn with_source_id(mut self, value: Uuid) -> Self {
217 self.source_id = Some(value);
218 self
219 }
220
221 pub fn with_source_reference(mut self, value: String) -> Self {
223 self.source_reference = Some(value);
224 self
225 }
226
227 pub fn with_tax_rate(mut self, value: Decimal) -> Self {
229 self.tax_rate = Some(value);
230 self
231 }
232
233 pub fn with_tax_base_amount(mut self, value: Decimal) -> Self {
235 self.tax_base_amount = Some(value);
236 self
237 }
238
239 pub fn with_related_line_id(mut self, value: Uuid) -> Self {
241 self.related_line_id = Some(value);
242 self
243 }
244
245 pub fn with_quantity(mut self, value: Decimal) -> Self {
247 self.quantity = Some(value);
248 self
249 }
250
251 pub fn with_unit(mut self, value: String) -> Self {
253 self.unit = Some(value);
254 self
255 }
256
257 pub fn with_unit_price(mut self, value: Decimal) -> Self {
259 self.unit_price = Some(value);
260 self
261 }
262
263 pub fn with_reconciliation_id(mut self, value: Uuid) -> Self {
265 self.reconciliation_id = Some(value);
266 self
267 }
268
269 pub fn with_reconciled_at(mut self, value: DateTime<Utc>) -> Self {
271 self.reconciled_at = Some(value);
272 self
273 }
274
275 pub fn with_full_reconcile_id(mut self, value: Uuid) -> Self {
277 self.full_reconcile_id = Some(value);
278 self
279 }
280
281 pub fn with_ledger_id(mut self, value: Uuid) -> Self {
283 self.ledger_id = Some(value);
284 self
285 }
286
287 pub fn with_posted_at(mut self, value: DateTime<Utc>) -> Self {
289 self.posted_at = Some(value);
290 self
291 }
292
293 pub fn apply_patch(&mut self, fields: std::collections::HashMap<String, serde_json::Value>) {
299 for (key, value) in fields {
300 match key.as_str() {
301 "journal_id" => {
302 if let Ok(v) = serde_json::from_value(value) { self.journal_id = v; }
303 }
304 "branch_id" => {
305 if let Ok(v) = serde_json::from_value(value) { self.branch_id = v; }
306 }
307 "party_type" => {
308 if let Ok(v) = serde_json::from_value(value) { self.party_type = v; }
309 }
310 "party_id" => {
311 if let Ok(v) = serde_json::from_value(value) { self.party_id = v; }
312 }
313 "line_number" => {
314 if let Ok(v) = serde_json::from_value(value) { self.line_number = v; }
315 }
316 "account_id" => {
317 if let Ok(v) = serde_json::from_value(value) { self.account_id = v; }
318 }
319 "account_number" => {
320 if let Ok(v) = serde_json::from_value(value) { self.account_number = v; }
321 }
322 "account_name" => {
323 if let Ok(v) = serde_json::from_value(value) { self.account_name = v; }
324 }
325 "debit_amount" => {
326 if let Ok(v) = serde_json::from_value(value) { self.debit_amount = v; }
327 }
328 "credit_amount" => {
329 if let Ok(v) = serde_json::from_value(value) { self.credit_amount = v; }
330 }
331 "currency" => {
332 if let Ok(v) = serde_json::from_value(value) { self.currency = v; }
333 }
334 "exchange_rate" => {
335 if let Ok(v) = serde_json::from_value(value) { self.exchange_rate = v; }
336 }
337 "base_debit_amount" => {
338 if let Ok(v) = serde_json::from_value(value) { self.base_debit_amount = v; }
339 }
340 "base_credit_amount" => {
341 if let Ok(v) = serde_json::from_value(value) { self.base_credit_amount = v; }
342 }
343 "description" => {
344 if let Ok(v) = serde_json::from_value(value) { self.description = v; }
345 }
346 "cost_center_id" => {
347 if let Ok(v) = serde_json::from_value(value) { self.cost_center_id = v; }
348 }
349 "project_id" => {
350 if let Ok(v) = serde_json::from_value(value) { self.project_id = v; }
351 }
352 "department_id" => {
353 if let Ok(v) = serde_json::from_value(value) { self.department_id = v; }
354 }
355 "dimensions" => {
356 if let Ok(v) = serde_json::from_value(value) { self.dimensions = v; }
357 }
358 "source_type" => {
359 if let Ok(v) = serde_json::from_value(value) { self.source_type = v; }
360 }
361 "source_id" => {
362 if let Ok(v) = serde_json::from_value(value) { self.source_id = v; }
363 }
364 "source_reference" => {
365 if let Ok(v) = serde_json::from_value(value) { self.source_reference = v; }
366 }
367 "is_tax_line" => {
368 if let Ok(v) = serde_json::from_value(value) { self.is_tax_line = v; }
369 }
370 "tax_rate" => {
371 if let Ok(v) = serde_json::from_value(value) { self.tax_rate = v; }
372 }
373 "tax_base_amount" => {
374 if let Ok(v) = serde_json::from_value(value) { self.tax_base_amount = v; }
375 }
376 "related_line_id" => {
377 if let Ok(v) = serde_json::from_value(value) { self.related_line_id = v; }
378 }
379 "has_quantity" => {
380 if let Ok(v) = serde_json::from_value(value) { self.has_quantity = v; }
381 }
382 "quantity" => {
383 if let Ok(v) = serde_json::from_value(value) { self.quantity = v; }
384 }
385 "unit" => {
386 if let Ok(v) = serde_json::from_value(value) { self.unit = v; }
387 }
388 "unit_price" => {
389 if let Ok(v) = serde_json::from_value(value) { self.unit_price = v; }
390 }
391 "is_reconciled" => {
392 if let Ok(v) = serde_json::from_value(value) { self.is_reconciled = v; }
393 }
394 "reconciliation_id" => {
395 if let Ok(v) = serde_json::from_value(value) { self.reconciliation_id = v; }
396 }
397 "reconciled_at" => {
398 if let Ok(v) = serde_json::from_value(value) { self.reconciled_at = v; }
399 }
400 "full_reconcile_id" => {
401 if let Ok(v) = serde_json::from_value(value) { self.full_reconcile_id = v; }
402 }
403 "is_posted" => {
404 if let Ok(v) = serde_json::from_value(value) { self.is_posted = v; }
405 }
406 "ledger_id" => {
407 if let Ok(v) = serde_json::from_value(value) { self.ledger_id = v; }
408 }
409 "posted_at" => {
410 if let Ok(v) = serde_json::from_value(value) { self.posted_at = v; }
411 }
412 "tags" => {
413 if let Ok(v) = serde_json::from_value(value) { self.tags = v; }
414 }
415 "data" => {
416 if let Ok(v) = serde_json::from_value(value) { self.data = v; }
417 }
418 _ => {} }
420 }
421 }
422
423}
424
425impl super::Entity for JournalLine {
426 type Id = Uuid;
427
428 fn entity_id(&self) -> &Self::Id {
429 &self.id
430 }
431
432 fn entity_type() -> &'static str {
433 "JournalLine"
434 }
435}
436
437impl backbone_core::PersistentEntity for JournalLine {
438 fn entity_id(&self) -> String {
439 self.id.to_string()
440 }
441 fn set_entity_id(&mut self, id: String) {
442 if let Ok(uuid) = uuid::Uuid::parse_str(&id) {
443 self.id = uuid;
444 }
445 }
446 fn created_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
447 None
448 }
449 fn set_created_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
450 let _ = ts;
451 }
452 fn updated_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
453 None
454 }
455 fn set_updated_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
456 let _ = ts;
457 }
458 fn deleted_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
459 None
460 }
461 fn set_deleted_at(&mut self, ts: Option<chrono::DateTime<chrono::Utc>>) {
462 let _ = ts;
463 }
464}
465
466impl backbone_orm::EntityRepoMeta for JournalLine {
467 fn column_types() -> std::collections::HashMap<String, String> {
468 let mut m = std::collections::HashMap::new();
469 m.insert("id".to_string(), "uuid".to_string());
470 m.insert("journal_id".to_string(), "uuid".to_string());
471 m.insert("branch_id".to_string(), "uuid".to_string());
472 m.insert("party_id".to_string(), "uuid".to_string());
473 m.insert("account_id".to_string(), "uuid".to_string());
474 m.insert("cost_center_id".to_string(), "uuid".to_string());
475 m.insert("project_id".to_string(), "uuid".to_string());
476 m.insert("department_id".to_string(), "uuid".to_string());
477 m.insert("source_id".to_string(), "uuid".to_string());
478 m.insert("related_line_id".to_string(), "uuid".to_string());
479 m.insert("reconciliation_id".to_string(), "uuid".to_string());
480 m.insert("full_reconcile_id".to_string(), "uuid".to_string());
481 m.insert("ledger_id".to_string(), "uuid".to_string());
482 m.insert("party_type".to_string(), "party_type".to_string());
483 m.insert("reconciled_at".to_string(), "timestamptz".to_string());
484 m.insert("posted_at".to_string(), "timestamptz".to_string());
485 m
486 }
487 fn search_fields() -> &'static [&'static str] {
488 &["account_number", "account_name", "currency"]
489 }
490 fn relations() -> &'static [(&'static str, &'static str, &'static str)] {
491 &[("journal", "journals", "journalId"), ("account", "accounts", "accountId"), ("relatedLine", "journal_lines", "relatedLineId"), ("reconciliation", "reconciliations", "reconciliationId"), ("fullReconcile", "full_reconciles", "fullReconcileId"), ("ledger", "ledgers", "ledgerId"), ("costCenter", "cost_centers", "costCenterId")]
492 }
493}
494
495#[derive(Debug, Clone, Default)]
500pub struct JournalLineBuilder {
501 journal_id: Option<Uuid>,
502 branch_id: Option<Uuid>,
503 party_type: Option<PartyType>,
504 party_id: Option<Uuid>,
505 line_number: Option<i32>,
506 account_id: Option<Uuid>,
507 account_number: Option<String>,
508 account_name: Option<String>,
509 debit_amount: Option<Decimal>,
510 credit_amount: Option<Decimal>,
511 currency: Option<String>,
512 exchange_rate: Option<Decimal>,
513 base_debit_amount: Option<Decimal>,
514 base_credit_amount: Option<Decimal>,
515 description: Option<String>,
516 cost_center_id: Option<Uuid>,
517 project_id: Option<Uuid>,
518 department_id: Option<Uuid>,
519 dimensions: Option<serde_json::Value>,
520 source_type: Option<String>,
521 source_id: Option<Uuid>,
522 source_reference: Option<String>,
523 is_tax_line: Option<bool>,
524 tax_rate: Option<Decimal>,
525 tax_base_amount: Option<Decimal>,
526 related_line_id: Option<Uuid>,
527 has_quantity: Option<bool>,
528 quantity: Option<Decimal>,
529 unit: Option<String>,
530 unit_price: Option<Decimal>,
531 is_reconciled: Option<bool>,
532 reconciliation_id: Option<Uuid>,
533 reconciled_at: Option<DateTime<Utc>>,
534 full_reconcile_id: Option<Uuid>,
535 is_posted: Option<bool>,
536 ledger_id: Option<Uuid>,
537 posted_at: Option<DateTime<Utc>>,
538 tags: Option<serde_json::Value>,
539 data: Option<serde_json::Value>,
540}
541
542impl JournalLineBuilder {
543 pub fn journal_id(mut self, value: Uuid) -> Self {
545 self.journal_id = Some(value);
546 self
547 }
548
549 pub fn branch_id(mut self, value: Uuid) -> Self {
551 self.branch_id = Some(value);
552 self
553 }
554
555 pub fn party_type(mut self, value: PartyType) -> Self {
557 self.party_type = Some(value);
558 self
559 }
560
561 pub fn party_id(mut self, value: Uuid) -> Self {
563 self.party_id = Some(value);
564 self
565 }
566
567 pub fn line_number(mut self, value: i32) -> Self {
569 self.line_number = Some(value);
570 self
571 }
572
573 pub fn account_id(mut self, value: Uuid) -> Self {
575 self.account_id = Some(value);
576 self
577 }
578
579 pub fn account_number(mut self, value: String) -> Self {
581 self.account_number = Some(value);
582 self
583 }
584
585 pub fn account_name(mut self, value: String) -> Self {
587 self.account_name = Some(value);
588 self
589 }
590
591 pub fn debit_amount(mut self, value: Decimal) -> Self {
593 self.debit_amount = Some(value);
594 self
595 }
596
597 pub fn credit_amount(mut self, value: Decimal) -> Self {
599 self.credit_amount = Some(value);
600 self
601 }
602
603 pub fn currency(mut self, value: String) -> Self {
605 self.currency = Some(value);
606 self
607 }
608
609 pub fn exchange_rate(mut self, value: Decimal) -> Self {
611 self.exchange_rate = Some(value);
612 self
613 }
614
615 pub fn base_debit_amount(mut self, value: Decimal) -> Self {
617 self.base_debit_amount = Some(value);
618 self
619 }
620
621 pub fn base_credit_amount(mut self, value: Decimal) -> Self {
623 self.base_credit_amount = Some(value);
624 self
625 }
626
627 pub fn description(mut self, value: String) -> Self {
629 self.description = Some(value);
630 self
631 }
632
633 pub fn cost_center_id(mut self, value: Uuid) -> Self {
635 self.cost_center_id = Some(value);
636 self
637 }
638
639 pub fn project_id(mut self, value: Uuid) -> Self {
641 self.project_id = Some(value);
642 self
643 }
644
645 pub fn department_id(mut self, value: Uuid) -> Self {
647 self.department_id = Some(value);
648 self
649 }
650
651 pub fn dimensions(mut self, value: serde_json::Value) -> Self {
653 self.dimensions = Some(value);
654 self
655 }
656
657 pub fn source_type(mut self, value: String) -> Self {
659 self.source_type = Some(value);
660 self
661 }
662
663 pub fn source_id(mut self, value: Uuid) -> Self {
665 self.source_id = Some(value);
666 self
667 }
668
669 pub fn source_reference(mut self, value: String) -> Self {
671 self.source_reference = Some(value);
672 self
673 }
674
675 pub fn is_tax_line(mut self, value: bool) -> Self {
677 self.is_tax_line = Some(value);
678 self
679 }
680
681 pub fn tax_rate(mut self, value: Decimal) -> Self {
683 self.tax_rate = Some(value);
684 self
685 }
686
687 pub fn tax_base_amount(mut self, value: Decimal) -> Self {
689 self.tax_base_amount = Some(value);
690 self
691 }
692
693 pub fn related_line_id(mut self, value: Uuid) -> Self {
695 self.related_line_id = Some(value);
696 self
697 }
698
699 pub fn has_quantity(mut self, value: bool) -> Self {
701 self.has_quantity = Some(value);
702 self
703 }
704
705 pub fn quantity(mut self, value: Decimal) -> Self {
707 self.quantity = Some(value);
708 self
709 }
710
711 pub fn unit(mut self, value: String) -> Self {
713 self.unit = Some(value);
714 self
715 }
716
717 pub fn unit_price(mut self, value: Decimal) -> Self {
719 self.unit_price = Some(value);
720 self
721 }
722
723 pub fn is_reconciled(mut self, value: bool) -> Self {
725 self.is_reconciled = Some(value);
726 self
727 }
728
729 pub fn reconciliation_id(mut self, value: Uuid) -> Self {
731 self.reconciliation_id = Some(value);
732 self
733 }
734
735 pub fn reconciled_at(mut self, value: DateTime<Utc>) -> Self {
737 self.reconciled_at = Some(value);
738 self
739 }
740
741 pub fn full_reconcile_id(mut self, value: Uuid) -> Self {
743 self.full_reconcile_id = Some(value);
744 self
745 }
746
747 pub fn is_posted(mut self, value: bool) -> Self {
749 self.is_posted = Some(value);
750 self
751 }
752
753 pub fn ledger_id(mut self, value: Uuid) -> Self {
755 self.ledger_id = Some(value);
756 self
757 }
758
759 pub fn posted_at(mut self, value: DateTime<Utc>) -> Self {
761 self.posted_at = Some(value);
762 self
763 }
764
765 pub fn tags(mut self, value: serde_json::Value) -> Self {
767 self.tags = Some(value);
768 self
769 }
770
771 pub fn data(mut self, value: serde_json::Value) -> Self {
773 self.data = Some(value);
774 self
775 }
776
777 pub fn build(self) -> Result<JournalLine, String> {
781 let journal_id = self.journal_id.ok_or_else(|| "journal_id is required".to_string())?;
782 let line_number = self.line_number.ok_or_else(|| "line_number is required".to_string())?;
783 let account_id = self.account_id.ok_or_else(|| "account_id is required".to_string())?;
784 let account_number = self.account_number.ok_or_else(|| "account_number is required".to_string())?;
785 let account_name = self.account_name.ok_or_else(|| "account_name is required".to_string())?;
786
787 Ok(JournalLine {
788 id: Uuid::new_v4(),
789 journal_id,
790 branch_id: self.branch_id,
791 party_type: self.party_type,
792 party_id: self.party_id,
793 line_number,
794 account_id,
795 account_number,
796 account_name,
797 debit_amount: self.debit_amount.unwrap_or(Decimal::from(0)),
798 credit_amount: self.credit_amount.unwrap_or(Decimal::from(0)),
799 currency: self.currency.unwrap_or("IDR".to_string()),
800 exchange_rate: self.exchange_rate.unwrap_or(Decimal::from(1)),
801 base_debit_amount: self.base_debit_amount.unwrap_or(Decimal::from(0)),
802 base_credit_amount: self.base_credit_amount.unwrap_or(Decimal::from(0)),
803 description: self.description,
804 cost_center_id: self.cost_center_id,
805 project_id: self.project_id,
806 department_id: self.department_id,
807 dimensions: self.dimensions,
808 source_type: self.source_type,
809 source_id: self.source_id,
810 source_reference: self.source_reference,
811 is_tax_line: self.is_tax_line.unwrap_or(false),
812 tax_rate: self.tax_rate,
813 tax_base_amount: self.tax_base_amount,
814 related_line_id: self.related_line_id,
815 has_quantity: self.has_quantity.unwrap_or(false),
816 quantity: self.quantity,
817 unit: self.unit,
818 unit_price: self.unit_price,
819 is_reconciled: self.is_reconciled.unwrap_or(false),
820 reconciliation_id: self.reconciliation_id,
821 reconciled_at: self.reconciled_at,
822 full_reconcile_id: self.full_reconcile_id,
823 is_posted: self.is_posted.unwrap_or(false),
824 ledger_id: self.ledger_id,
825 posted_at: self.posted_at,
826 tags: self.tags.unwrap_or(serde_json::json!([])),
827 data: self.data.unwrap_or(serde_json::json!({})),
828 })
829 }
830}