1use chrono::{DateTime, Utc, NaiveDate};
2use serde::{Deserialize, Serialize};
3use sqlx::FromRow;
4use uuid::Uuid;
5use rust_decimal::Decimal;
6
7use super::JournalType;
8use super::JournalSource;
9use super::JournalStatus;
10use super::AuditMetadata;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
14#[serde(transparent)]
15pub struct JournalId(pub Uuid);
16
17impl JournalId {
18 pub fn new(id: Uuid) -> Self { Self(id) }
19 pub fn generate() -> Self { Self(Uuid::new_v4()) }
20 pub fn into_inner(self) -> Uuid { self.0 }
21}
22
23impl std::fmt::Display for JournalId {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 write!(f, "{}", self.0)
26 }
27}
28
29impl std::str::FromStr for JournalId {
30 type Err = uuid::Error;
31 fn from_str(s: &str) -> Result<Self, Self::Err> {
32 Ok(Self(Uuid::parse_str(s)?))
33 }
34}
35
36impl From<Uuid> for JournalId {
37 fn from(id: Uuid) -> Self { Self(id) }
38}
39
40impl From<JournalId> for Uuid {
41 fn from(id: JournalId) -> Self { id.0 }
42}
43
44impl AsRef<Uuid> for JournalId {
45 fn as_ref(&self) -> &Uuid { &self.0 }
46}
47
48impl std::ops::Deref for JournalId {
49 type Target = Uuid;
50 fn deref(&self) -> &Self::Target { &self.0 }
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
54pub struct Journal {
55 pub id: Uuid,
56 pub journal_number: String,
57 pub reference_number: Option<String>,
58 pub journal_type: JournalType,
59 pub branch_id: Option<Uuid>,
60 pub transaction_date: NaiveDate,
61 pub posting_date: Option<NaiveDate>,
62 pub fiscal_period_id: Option<Uuid>,
63 pub fiscal_year: Option<i32>,
64 pub fiscal_month: Option<i32>,
65 pub description: String,
66 pub notes: Option<String>,
67 pub currency: String,
68 pub total_debit: Decimal,
69 pub total_credit: Decimal,
70 pub line_count: i32,
71 pub source: JournalSource,
72 pub source_type: Option<String>,
73 pub source_id: Option<Uuid>,
74 pub source_reference: Option<String>,
75 pub is_reversed: bool,
76 pub reversed_by_id: Option<Uuid>,
77 pub reversed_at: Option<DateTime<Utc>>,
78 pub reversal_reason: Option<String>,
79 pub reverses_id: Option<Uuid>,
80 pub is_reversing: bool,
81 pub auto_reverse: bool,
82 pub auto_reverse_date: Option<NaiveDate>,
83 pub status: JournalStatus,
84 pub requires_approval: bool,
85 pub approval_threshold: Option<Decimal>,
86 pub submitted_at: Option<DateTime<Utc>>,
87 pub submitted_by: Option<Uuid>,
88 pub approved_at: Option<DateTime<Utc>>,
89 pub approved_by: Option<Uuid>,
90 pub rejected_at: Option<DateTime<Utc>>,
91 pub rejected_by: Option<Uuid>,
92 pub rejection_reason: Option<String>,
93 pub posted_at: Option<DateTime<Utc>>,
94 pub posted_by: Option<Uuid>,
95 pub is_voided: bool,
96 pub voided_at: Option<DateTime<Utc>>,
97 pub voided_by: Option<Uuid>,
98 pub void_reason: Option<String>,
99 pub attachments: serde_json::Value,
100 #[serde(default)]
101 #[sqlx(json)]
102 pub metadata: AuditMetadata,
103}
104
105impl Journal {
106 pub fn builder() -> JournalBuilder {
108 <JournalBuilder as Default>::default()
109 }
110
111 pub fn new(journal_number: String, journal_type: JournalType, transaction_date: NaiveDate, description: String, currency: String, total_debit: Decimal, total_credit: Decimal, line_count: i32, source: JournalSource, is_reversed: bool, is_reversing: bool, auto_reverse: bool, status: JournalStatus, requires_approval: bool, is_voided: bool, attachments: serde_json::Value) -> Self {
113 Self {
114 id: Uuid::new_v4(),
115 journal_number,
116 reference_number: None,
117 journal_type,
118 branch_id: None,
119 transaction_date,
120 posting_date: None,
121 fiscal_period_id: None,
122 fiscal_year: None,
123 fiscal_month: None,
124 description,
125 notes: None,
126 currency,
127 total_debit,
128 total_credit,
129 line_count,
130 source,
131 source_type: None,
132 source_id: None,
133 source_reference: None,
134 is_reversed,
135 reversed_by_id: None,
136 reversed_at: None,
137 reversal_reason: None,
138 reverses_id: None,
139 is_reversing,
140 auto_reverse,
141 auto_reverse_date: None,
142 status,
143 requires_approval,
144 approval_threshold: None,
145 submitted_at: None,
146 submitted_by: None,
147 approved_at: None,
148 approved_by: None,
149 rejected_at: None,
150 rejected_by: None,
151 rejection_reason: None,
152 posted_at: None,
153 posted_by: None,
154 is_voided,
155 voided_at: None,
156 voided_by: None,
157 void_reason: None,
158 attachments,
159 metadata: AuditMetadata::default(),
160 }
161 }
162
163 pub fn id(&self) -> &Uuid {
165 &self.id
166 }
167
168 pub fn typed_id(&self) -> JournalId {
170 JournalId(self.id)
171 }
172
173 pub fn created_at(&self) -> Option<&DateTime<Utc>> {
175 self.metadata.created_at.as_ref()
176 }
177
178 pub fn updated_at(&self) -> Option<&DateTime<Utc>> {
180 self.metadata.updated_at.as_ref()
181 }
182
183 pub fn is_deleted(&self) -> bool {
185 self.metadata.deleted_at.is_some()
186 }
187
188 pub fn is_active(&self) -> bool {
190 self.metadata.deleted_at.is_none()
191 }
192
193 pub fn deleted_at(&self) -> Option<&DateTime<Utc>> {
195 self.metadata.deleted_at.as_ref()
196 }
197
198 pub fn created_by(&self) -> Option<&Uuid> {
200 self.metadata.created_by.as_ref()
201 }
202
203 pub fn updated_by(&self) -> Option<&Uuid> {
205 self.metadata.updated_by.as_ref()
206 }
207
208 pub fn deleted_by(&self) -> Option<&Uuid> {
210 self.metadata.deleted_by.as_ref()
211 }
212
213 pub fn status(&self) -> &JournalStatus {
215 &self.status
216 }
217
218
219 pub fn with_reference_number(mut self, value: String) -> Self {
225 self.reference_number = Some(value);
226 self
227 }
228
229 pub fn with_branch_id(mut self, value: Uuid) -> Self {
231 self.branch_id = Some(value);
232 self
233 }
234
235 pub fn with_posting_date(mut self, value: NaiveDate) -> Self {
237 self.posting_date = Some(value);
238 self
239 }
240
241 pub fn with_fiscal_period_id(mut self, value: Uuid) -> Self {
243 self.fiscal_period_id = Some(value);
244 self
245 }
246
247 pub fn with_fiscal_year(mut self, value: i32) -> Self {
249 self.fiscal_year = Some(value);
250 self
251 }
252
253 pub fn with_fiscal_month(mut self, value: i32) -> Self {
255 self.fiscal_month = Some(value);
256 self
257 }
258
259 pub fn with_notes(mut self, value: String) -> Self {
261 self.notes = Some(value);
262 self
263 }
264
265 pub fn with_source_type(mut self, value: String) -> Self {
267 self.source_type = Some(value);
268 self
269 }
270
271 pub fn with_source_id(mut self, value: Uuid) -> Self {
273 self.source_id = Some(value);
274 self
275 }
276
277 pub fn with_source_reference(mut self, value: String) -> Self {
279 self.source_reference = Some(value);
280 self
281 }
282
283 pub fn with_reversed_by_id(mut self, value: Uuid) -> Self {
285 self.reversed_by_id = Some(value);
286 self
287 }
288
289 pub fn with_reversed_at(mut self, value: DateTime<Utc>) -> Self {
291 self.reversed_at = Some(value);
292 self
293 }
294
295 pub fn with_reversal_reason(mut self, value: String) -> Self {
297 self.reversal_reason = Some(value);
298 self
299 }
300
301 pub fn with_reverses_id(mut self, value: Uuid) -> Self {
303 self.reverses_id = Some(value);
304 self
305 }
306
307 pub fn with_auto_reverse_date(mut self, value: NaiveDate) -> Self {
309 self.auto_reverse_date = Some(value);
310 self
311 }
312
313 pub fn with_approval_threshold(mut self, value: Decimal) -> Self {
315 self.approval_threshold = Some(value);
316 self
317 }
318
319 pub fn with_submitted_at(mut self, value: DateTime<Utc>) -> Self {
321 self.submitted_at = Some(value);
322 self
323 }
324
325 pub fn with_submitted_by(mut self, value: Uuid) -> Self {
327 self.submitted_by = Some(value);
328 self
329 }
330
331 pub fn with_approved_at(mut self, value: DateTime<Utc>) -> Self {
333 self.approved_at = Some(value);
334 self
335 }
336
337 pub fn with_approved_by(mut self, value: Uuid) -> Self {
339 self.approved_by = Some(value);
340 self
341 }
342
343 pub fn with_rejected_at(mut self, value: DateTime<Utc>) -> Self {
345 self.rejected_at = Some(value);
346 self
347 }
348
349 pub fn with_rejected_by(mut self, value: Uuid) -> Self {
351 self.rejected_by = Some(value);
352 self
353 }
354
355 pub fn with_rejection_reason(mut self, value: String) -> Self {
357 self.rejection_reason = Some(value);
358 self
359 }
360
361 pub fn with_posted_at(mut self, value: DateTime<Utc>) -> Self {
363 self.posted_at = Some(value);
364 self
365 }
366
367 pub fn with_posted_by(mut self, value: Uuid) -> Self {
369 self.posted_by = Some(value);
370 self
371 }
372
373 pub fn with_voided_at(mut self, value: DateTime<Utc>) -> Self {
375 self.voided_at = Some(value);
376 self
377 }
378
379 pub fn with_voided_by(mut self, value: Uuid) -> Self {
381 self.voided_by = Some(value);
382 self
383 }
384
385 pub fn with_void_reason(mut self, value: String) -> Self {
387 self.void_reason = Some(value);
388 self
389 }
390
391 pub fn apply_patch(&mut self, fields: std::collections::HashMap<String, serde_json::Value>) {
397 for (key, value) in fields {
398 match key.as_str() {
399 "journal_number" => {
400 if let Ok(v) = serde_json::from_value(value) { self.journal_number = v; }
401 }
402 "reference_number" => {
403 if let Ok(v) = serde_json::from_value(value) { self.reference_number = v; }
404 }
405 "journal_type" => {
406 if let Ok(v) = serde_json::from_value(value) { self.journal_type = v; }
407 }
408 "branch_id" => {
409 if let Ok(v) = serde_json::from_value(value) { self.branch_id = v; }
410 }
411 "transaction_date" => {
412 if let Ok(v) = serde_json::from_value(value) { self.transaction_date = v; }
413 }
414 "posting_date" => {
415 if let Ok(v) = serde_json::from_value(value) { self.posting_date = v; }
416 }
417 "fiscal_period_id" => {
418 if let Ok(v) = serde_json::from_value(value) { self.fiscal_period_id = v; }
419 }
420 "fiscal_year" => {
421 if let Ok(v) = serde_json::from_value(value) { self.fiscal_year = v; }
422 }
423 "fiscal_month" => {
424 if let Ok(v) = serde_json::from_value(value) { self.fiscal_month = v; }
425 }
426 "description" => {
427 if let Ok(v) = serde_json::from_value(value) { self.description = v; }
428 }
429 "notes" => {
430 if let Ok(v) = serde_json::from_value(value) { self.notes = v; }
431 }
432 "currency" => {
433 if let Ok(v) = serde_json::from_value(value) { self.currency = v; }
434 }
435 "total_debit" => {
436 if let Ok(v) = serde_json::from_value(value) { self.total_debit = v; }
437 }
438 "total_credit" => {
439 if let Ok(v) = serde_json::from_value(value) { self.total_credit = v; }
440 }
441 "line_count" => {
442 if let Ok(v) = serde_json::from_value(value) { self.line_count = v; }
443 }
444 "source" => {
445 if let Ok(v) = serde_json::from_value(value) { self.source = v; }
446 }
447 "source_type" => {
448 if let Ok(v) = serde_json::from_value(value) { self.source_type = v; }
449 }
450 "source_id" => {
451 if let Ok(v) = serde_json::from_value(value) { self.source_id = v; }
452 }
453 "source_reference" => {
454 if let Ok(v) = serde_json::from_value(value) { self.source_reference = v; }
455 }
456 "is_reversed" => {
457 if let Ok(v) = serde_json::from_value(value) { self.is_reversed = v; }
458 }
459 "reversed_by_id" => {
460 if let Ok(v) = serde_json::from_value(value) { self.reversed_by_id = v; }
461 }
462 "reversed_at" => {
463 if let Ok(v) = serde_json::from_value(value) { self.reversed_at = v; }
464 }
465 "reversal_reason" => {
466 if let Ok(v) = serde_json::from_value(value) { self.reversal_reason = v; }
467 }
468 "reverses_id" => {
469 if let Ok(v) = serde_json::from_value(value) { self.reverses_id = v; }
470 }
471 "is_reversing" => {
472 if let Ok(v) = serde_json::from_value(value) { self.is_reversing = v; }
473 }
474 "auto_reverse" => {
475 if let Ok(v) = serde_json::from_value(value) { self.auto_reverse = v; }
476 }
477 "auto_reverse_date" => {
478 if let Ok(v) = serde_json::from_value(value) { self.auto_reverse_date = v; }
479 }
480 "status" => {
481 if let Ok(v) = serde_json::from_value(value) { self.status = v; }
482 }
483 "requires_approval" => {
484 if let Ok(v) = serde_json::from_value(value) { self.requires_approval = v; }
485 }
486 "approval_threshold" => {
487 if let Ok(v) = serde_json::from_value(value) { self.approval_threshold = v; }
488 }
489 "submitted_at" => {
490 if let Ok(v) = serde_json::from_value(value) { self.submitted_at = v; }
491 }
492 "submitted_by" => {
493 if let Ok(v) = serde_json::from_value(value) { self.submitted_by = v; }
494 }
495 "approved_at" => {
496 if let Ok(v) = serde_json::from_value(value) { self.approved_at = v; }
497 }
498 "approved_by" => {
499 if let Ok(v) = serde_json::from_value(value) { self.approved_by = v; }
500 }
501 "rejected_at" => {
502 if let Ok(v) = serde_json::from_value(value) { self.rejected_at = v; }
503 }
504 "rejected_by" => {
505 if let Ok(v) = serde_json::from_value(value) { self.rejected_by = v; }
506 }
507 "rejection_reason" => {
508 if let Ok(v) = serde_json::from_value(value) { self.rejection_reason = v; }
509 }
510 "posted_at" => {
511 if let Ok(v) = serde_json::from_value(value) { self.posted_at = v; }
512 }
513 "posted_by" => {
514 if let Ok(v) = serde_json::from_value(value) { self.posted_by = v; }
515 }
516 "is_voided" => {
517 if let Ok(v) = serde_json::from_value(value) { self.is_voided = v; }
518 }
519 "voided_at" => {
520 if let Ok(v) = serde_json::from_value(value) { self.voided_at = v; }
521 }
522 "voided_by" => {
523 if let Ok(v) = serde_json::from_value(value) { self.voided_by = v; }
524 }
525 "void_reason" => {
526 if let Ok(v) = serde_json::from_value(value) { self.void_reason = v; }
527 }
528 "attachments" => {
529 if let Ok(v) = serde_json::from_value(value) { self.attachments = v; }
530 }
531 _ => {} }
533 }
534 }
535
536}
537
538impl super::Entity for Journal {
539 type Id = Uuid;
540
541 fn entity_id(&self) -> &Self::Id {
542 &self.id
543 }
544
545 fn entity_type() -> &'static str {
546 "Journal"
547 }
548}
549
550impl backbone_core::PersistentEntity for Journal {
551 fn entity_id(&self) -> String {
552 self.id.to_string()
553 }
554 fn set_entity_id(&mut self, id: String) {
555 if let Ok(uuid) = uuid::Uuid::parse_str(&id) {
556 self.id = uuid;
557 }
558 }
559 fn created_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
560 self.metadata.created_at
561 }
562 fn set_created_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
563 self.metadata.created_at = Some(ts);
564 }
565 fn updated_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
566 self.metadata.updated_at
567 }
568 fn set_updated_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
569 self.metadata.updated_at = Some(ts);
570 }
571 fn deleted_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
572 self.metadata.deleted_at
573 }
574 fn set_deleted_at(&mut self, ts: Option<chrono::DateTime<chrono::Utc>>) {
575 self.metadata.deleted_at = ts;
576 }
577}
578
579impl backbone_orm::EntityRepoMeta for Journal {
580 fn column_types() -> std::collections::HashMap<String, String> {
581 let mut m = std::collections::HashMap::new();
582 m.insert("id".to_string(), "uuid".to_string());
583 m.insert("branch_id".to_string(), "uuid".to_string());
584 m.insert("fiscal_period_id".to_string(), "uuid".to_string());
585 m.insert("source_id".to_string(), "uuid".to_string());
586 m.insert("reversed_by_id".to_string(), "uuid".to_string());
587 m.insert("reverses_id".to_string(), "uuid".to_string());
588 m.insert("journal_type".to_string(), "journal_type".to_string());
589 m.insert("source".to_string(), "journal_source".to_string());
590 m.insert("status".to_string(), "journal_status".to_string());
591 m.insert("transaction_date".to_string(), "date".to_string());
592 m.insert("posting_date".to_string(), "date".to_string());
593 m.insert("reversed_at".to_string(), "timestamptz".to_string());
594 m.insert("auto_reverse_date".to_string(), "date".to_string());
595 m.insert("submitted_at".to_string(), "timestamptz".to_string());
596 m.insert("approved_at".to_string(), "timestamptz".to_string());
597 m.insert("rejected_at".to_string(), "timestamptz".to_string());
598 m.insert("posted_at".to_string(), "timestamptz".to_string());
599 m.insert("voided_at".to_string(), "timestamptz".to_string());
600 m
601 }
602 fn search_fields() -> &'static [&'static str] {
603 &["journal_number", "description", "currency"]
604 }
605 fn relations() -> &'static [(&'static str, &'static str, &'static str)] {
606 &[("fiscalPeriod", "fiscal_periods", "fiscalPeriodId"), ("reversedBy", "journals", "reversedById"), ("reverses", "journals", "reversesId")]
607 }
608}
609
610#[derive(Debug, Clone, Default)]
615pub struct JournalBuilder {
616 journal_number: Option<String>,
617 reference_number: Option<String>,
618 journal_type: Option<JournalType>,
619 branch_id: Option<Uuid>,
620 transaction_date: Option<NaiveDate>,
621 posting_date: Option<NaiveDate>,
622 fiscal_period_id: Option<Uuid>,
623 fiscal_year: Option<i32>,
624 fiscal_month: Option<i32>,
625 description: Option<String>,
626 notes: Option<String>,
627 currency: Option<String>,
628 total_debit: Option<Decimal>,
629 total_credit: Option<Decimal>,
630 line_count: Option<i32>,
631 source: Option<JournalSource>,
632 source_type: Option<String>,
633 source_id: Option<Uuid>,
634 source_reference: Option<String>,
635 is_reversed: Option<bool>,
636 reversed_by_id: Option<Uuid>,
637 reversed_at: Option<DateTime<Utc>>,
638 reversal_reason: Option<String>,
639 reverses_id: Option<Uuid>,
640 is_reversing: Option<bool>,
641 auto_reverse: Option<bool>,
642 auto_reverse_date: Option<NaiveDate>,
643 status: Option<JournalStatus>,
644 requires_approval: Option<bool>,
645 approval_threshold: Option<Decimal>,
646 submitted_at: Option<DateTime<Utc>>,
647 submitted_by: Option<Uuid>,
648 approved_at: Option<DateTime<Utc>>,
649 approved_by: Option<Uuid>,
650 rejected_at: Option<DateTime<Utc>>,
651 rejected_by: Option<Uuid>,
652 rejection_reason: Option<String>,
653 posted_at: Option<DateTime<Utc>>,
654 posted_by: Option<Uuid>,
655 is_voided: Option<bool>,
656 voided_at: Option<DateTime<Utc>>,
657 voided_by: Option<Uuid>,
658 void_reason: Option<String>,
659 attachments: Option<serde_json::Value>,
660}
661
662impl JournalBuilder {
663 pub fn journal_number(mut self, value: String) -> Self {
665 self.journal_number = Some(value);
666 self
667 }
668
669 pub fn reference_number(mut self, value: String) -> Self {
671 self.reference_number = Some(value);
672 self
673 }
674
675 pub fn journal_type(mut self, value: JournalType) -> Self {
677 self.journal_type = Some(value);
678 self
679 }
680
681 pub fn branch_id(mut self, value: Uuid) -> Self {
683 self.branch_id = Some(value);
684 self
685 }
686
687 pub fn transaction_date(mut self, value: NaiveDate) -> Self {
689 self.transaction_date = Some(value);
690 self
691 }
692
693 pub fn posting_date(mut self, value: NaiveDate) -> Self {
695 self.posting_date = Some(value);
696 self
697 }
698
699 pub fn fiscal_period_id(mut self, value: Uuid) -> Self {
701 self.fiscal_period_id = Some(value);
702 self
703 }
704
705 pub fn fiscal_year(mut self, value: i32) -> Self {
707 self.fiscal_year = Some(value);
708 self
709 }
710
711 pub fn fiscal_month(mut self, value: i32) -> Self {
713 self.fiscal_month = Some(value);
714 self
715 }
716
717 pub fn description(mut self, value: String) -> Self {
719 self.description = Some(value);
720 self
721 }
722
723 pub fn notes(mut self, value: String) -> Self {
725 self.notes = Some(value);
726 self
727 }
728
729 pub fn currency(mut self, value: String) -> Self {
731 self.currency = Some(value);
732 self
733 }
734
735 pub fn total_debit(mut self, value: Decimal) -> Self {
737 self.total_debit = Some(value);
738 self
739 }
740
741 pub fn total_credit(mut self, value: Decimal) -> Self {
743 self.total_credit = Some(value);
744 self
745 }
746
747 pub fn line_count(mut self, value: i32) -> Self {
749 self.line_count = Some(value);
750 self
751 }
752
753 pub fn source(mut self, value: JournalSource) -> Self {
755 self.source = Some(value);
756 self
757 }
758
759 pub fn source_type(mut self, value: String) -> Self {
761 self.source_type = Some(value);
762 self
763 }
764
765 pub fn source_id(mut self, value: Uuid) -> Self {
767 self.source_id = Some(value);
768 self
769 }
770
771 pub fn source_reference(mut self, value: String) -> Self {
773 self.source_reference = Some(value);
774 self
775 }
776
777 pub fn is_reversed(mut self, value: bool) -> Self {
779 self.is_reversed = Some(value);
780 self
781 }
782
783 pub fn reversed_by_id(mut self, value: Uuid) -> Self {
785 self.reversed_by_id = Some(value);
786 self
787 }
788
789 pub fn reversed_at(mut self, value: DateTime<Utc>) -> Self {
791 self.reversed_at = Some(value);
792 self
793 }
794
795 pub fn reversal_reason(mut self, value: String) -> Self {
797 self.reversal_reason = Some(value);
798 self
799 }
800
801 pub fn reverses_id(mut self, value: Uuid) -> Self {
803 self.reverses_id = Some(value);
804 self
805 }
806
807 pub fn is_reversing(mut self, value: bool) -> Self {
809 self.is_reversing = Some(value);
810 self
811 }
812
813 pub fn auto_reverse(mut self, value: bool) -> Self {
815 self.auto_reverse = Some(value);
816 self
817 }
818
819 pub fn auto_reverse_date(mut self, value: NaiveDate) -> Self {
821 self.auto_reverse_date = Some(value);
822 self
823 }
824
825 pub fn status(mut self, value: JournalStatus) -> Self {
827 self.status = Some(value);
828 self
829 }
830
831 pub fn requires_approval(mut self, value: bool) -> Self {
833 self.requires_approval = Some(value);
834 self
835 }
836
837 pub fn approval_threshold(mut self, value: Decimal) -> Self {
839 self.approval_threshold = Some(value);
840 self
841 }
842
843 pub fn submitted_at(mut self, value: DateTime<Utc>) -> Self {
845 self.submitted_at = Some(value);
846 self
847 }
848
849 pub fn submitted_by(mut self, value: Uuid) -> Self {
851 self.submitted_by = Some(value);
852 self
853 }
854
855 pub fn approved_at(mut self, value: DateTime<Utc>) -> Self {
857 self.approved_at = Some(value);
858 self
859 }
860
861 pub fn approved_by(mut self, value: Uuid) -> Self {
863 self.approved_by = Some(value);
864 self
865 }
866
867 pub fn rejected_at(mut self, value: DateTime<Utc>) -> Self {
869 self.rejected_at = Some(value);
870 self
871 }
872
873 pub fn rejected_by(mut self, value: Uuid) -> Self {
875 self.rejected_by = Some(value);
876 self
877 }
878
879 pub fn rejection_reason(mut self, value: String) -> Self {
881 self.rejection_reason = Some(value);
882 self
883 }
884
885 pub fn posted_at(mut self, value: DateTime<Utc>) -> Self {
887 self.posted_at = Some(value);
888 self
889 }
890
891 pub fn posted_by(mut self, value: Uuid) -> Self {
893 self.posted_by = Some(value);
894 self
895 }
896
897 pub fn is_voided(mut self, value: bool) -> Self {
899 self.is_voided = Some(value);
900 self
901 }
902
903 pub fn voided_at(mut self, value: DateTime<Utc>) -> Self {
905 self.voided_at = Some(value);
906 self
907 }
908
909 pub fn voided_by(mut self, value: Uuid) -> Self {
911 self.voided_by = Some(value);
912 self
913 }
914
915 pub fn void_reason(mut self, value: String) -> Self {
917 self.void_reason = Some(value);
918 self
919 }
920
921 pub fn attachments(mut self, value: serde_json::Value) -> Self {
923 self.attachments = Some(value);
924 self
925 }
926
927 pub fn build(self) -> Result<Journal, String> {
931 let journal_number = self.journal_number.ok_or_else(|| "journal_number is required".to_string())?;
932 let transaction_date = self.transaction_date.ok_or_else(|| "transaction_date is required".to_string())?;
933 let description = self.description.ok_or_else(|| "description is required".to_string())?;
934
935 Ok(Journal {
936 id: Uuid::new_v4(),
937 journal_number,
938 reference_number: self.reference_number,
939 journal_type: self.journal_type.unwrap_or_default(),
940 branch_id: self.branch_id,
941 transaction_date,
942 posting_date: self.posting_date,
943 fiscal_period_id: self.fiscal_period_id,
944 fiscal_year: self.fiscal_year,
945 fiscal_month: self.fiscal_month,
946 description,
947 notes: self.notes,
948 currency: self.currency.unwrap_or("IDR".to_string()),
949 total_debit: self.total_debit.unwrap_or(Decimal::from(0)),
950 total_credit: self.total_credit.unwrap_or(Decimal::from(0)),
951 line_count: self.line_count.unwrap_or(0),
952 source: self.source.unwrap_or_default(),
953 source_type: self.source_type,
954 source_id: self.source_id,
955 source_reference: self.source_reference,
956 is_reversed: self.is_reversed.unwrap_or(false),
957 reversed_by_id: self.reversed_by_id,
958 reversed_at: self.reversed_at,
959 reversal_reason: self.reversal_reason,
960 reverses_id: self.reverses_id,
961 is_reversing: self.is_reversing.unwrap_or(false),
962 auto_reverse: self.auto_reverse.unwrap_or(false),
963 auto_reverse_date: self.auto_reverse_date,
964 status: self.status.unwrap_or_default(),
965 requires_approval: self.requires_approval.unwrap_or(false),
966 approval_threshold: self.approval_threshold,
967 submitted_at: self.submitted_at,
968 submitted_by: self.submitted_by,
969 approved_at: self.approved_at,
970 approved_by: self.approved_by,
971 rejected_at: self.rejected_at,
972 rejected_by: self.rejected_by,
973 rejection_reason: self.rejection_reason,
974 posted_at: self.posted_at,
975 posted_by: self.posted_by,
976 is_voided: self.is_voided.unwrap_or(false),
977 voided_at: self.voided_at,
978 voided_by: self.voided_by,
979 void_reason: self.void_reason,
980 attachments: self.attachments.unwrap_or(serde_json::json!([])),
981 metadata: AuditMetadata::default(),
982 })
983 }
984}