1use chrono::{DateTime, Utc, NaiveDate};
2use serde::{Deserialize, Serialize};
3use sqlx::FromRow;
4use uuid::Uuid;
5use rust_decimal::Decimal;
6
7use super::StatementType;
8use super::StatementStatus;
9use super::AuditMetadata;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13#[serde(transparent)]
14pub struct FinancialStatementId(pub Uuid);
15
16impl FinancialStatementId {
17 pub fn new(id: Uuid) -> Self { Self(id) }
18 pub fn generate() -> Self { Self(Uuid::new_v4()) }
19 pub fn into_inner(self) -> Uuid { self.0 }
20}
21
22impl std::fmt::Display for FinancialStatementId {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 write!(f, "{}", self.0)
25 }
26}
27
28impl std::str::FromStr for FinancialStatementId {
29 type Err = uuid::Error;
30 fn from_str(s: &str) -> Result<Self, Self::Err> {
31 Ok(Self(Uuid::parse_str(s)?))
32 }
33}
34
35impl From<Uuid> for FinancialStatementId {
36 fn from(id: Uuid) -> Self { Self(id) }
37}
38
39impl From<FinancialStatementId> for Uuid {
40 fn from(id: FinancialStatementId) -> Self { id.0 }
41}
42
43impl AsRef<Uuid> for FinancialStatementId {
44 fn as_ref(&self) -> &Uuid { &self.0 }
45}
46
47impl std::ops::Deref for FinancialStatementId {
48 type Target = Uuid;
49 fn deref(&self) -> &Self::Target { &self.0 }
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
53pub struct FinancialStatement {
54 pub id: Uuid,
55 pub statement_number: String,
56 pub statement_type: StatementType,
57 pub name: String,
58 pub fiscal_period_id: Option<Uuid>,
59 pub fiscal_year: i32,
60 pub fiscal_month: Option<i32>,
61 pub as_of_date: NaiveDate,
62 pub period_start: Option<NaiveDate>,
63 pub period_end: Option<NaiveDate>,
64 pub is_comparative: bool,
65 pub comparative_period_id: Option<Uuid>,
66 pub comparative_as_of_date: Option<NaiveDate>,
67 pub status: StatementStatus,
68 pub total_current_assets: Option<Decimal>,
69 pub total_non_current_assets: Option<Decimal>,
70 pub total_assets: Option<Decimal>,
71 pub total_current_liabilities: Option<Decimal>,
72 pub total_non_current_liabilities: Option<Decimal>,
73 pub total_liabilities: Option<Decimal>,
74 pub total_equity: Option<Decimal>,
75 pub balance_check: bool,
76 pub balance_difference: Option<Decimal>,
77 pub total_revenue: Option<Decimal>,
78 pub total_cogs: Option<Decimal>,
79 pub gross_profit: Option<Decimal>,
80 pub gross_profit_margin: Option<Decimal>,
81 pub total_operating_expenses: Option<Decimal>,
82 pub operating_income: Option<Decimal>,
83 pub operating_margin: Option<Decimal>,
84 pub total_other_income: Option<Decimal>,
85 pub total_other_expenses: Option<Decimal>,
86 pub income_before_tax: Option<Decimal>,
87 pub tax_expense: Option<Decimal>,
88 pub net_income: Option<Decimal>,
89 pub net_profit_margin: Option<Decimal>,
90 pub cash_from_operations: Option<Decimal>,
91 pub cash_from_investing: Option<Decimal>,
92 pub cash_from_financing: Option<Decimal>,
93 pub net_cash_change: Option<Decimal>,
94 pub beginning_cash: Option<Decimal>,
95 pub ending_cash: Option<Decimal>,
96 pub total_debits: Option<Decimal>,
97 pub total_credits: Option<Decimal>,
98 pub trial_balance_check: bool,
99 pub line_items: serde_json::Value,
100 pub comparative_data: serde_json::Value,
101 pub variance_data: serde_json::Value,
102 pub notes: serde_json::Value,
103 pub management_notes: Option<String>,
104 pub generated_at: Option<DateTime<Utc>>,
105 pub generated_by: Option<Uuid>,
106 pub generation_parameters: serde_json::Value,
107 pub reviewed_at: Option<DateTime<Utc>>,
108 pub reviewed_by: Option<Uuid>,
109 pub approved_at: Option<DateTime<Utc>>,
110 pub approved_by: Option<Uuid>,
111 pub published_at: Option<DateTime<Utc>>,
112 pub pdf_url: Option<String>,
113 pub excel_url: Option<String>,
114 pub currency: String,
115 #[serde(default)]
116 #[sqlx(json)]
117 pub metadata: AuditMetadata,
118}
119
120impl FinancialStatement {
121 pub fn builder() -> FinancialStatementBuilder {
123 <FinancialStatementBuilder as Default>::default()
124 }
125
126 pub fn new(statement_number: String, statement_type: StatementType, name: String, fiscal_year: i32, as_of_date: NaiveDate, is_comparative: bool, status: StatementStatus, balance_check: bool, trial_balance_check: bool, line_items: serde_json::Value, comparative_data: serde_json::Value, variance_data: serde_json::Value, notes: serde_json::Value, generation_parameters: serde_json::Value, currency: String) -> Self {
128 Self {
129 id: Uuid::new_v4(),
130 statement_number,
131 statement_type,
132 name,
133 fiscal_period_id: None,
134 fiscal_year,
135 fiscal_month: None,
136 as_of_date,
137 period_start: None,
138 period_end: None,
139 is_comparative,
140 comparative_period_id: None,
141 comparative_as_of_date: None,
142 status,
143 total_current_assets: None,
144 total_non_current_assets: None,
145 total_assets: None,
146 total_current_liabilities: None,
147 total_non_current_liabilities: None,
148 total_liabilities: None,
149 total_equity: None,
150 balance_check,
151 balance_difference: None,
152 total_revenue: None,
153 total_cogs: None,
154 gross_profit: None,
155 gross_profit_margin: None,
156 total_operating_expenses: None,
157 operating_income: None,
158 operating_margin: None,
159 total_other_income: None,
160 total_other_expenses: None,
161 income_before_tax: None,
162 tax_expense: None,
163 net_income: None,
164 net_profit_margin: None,
165 cash_from_operations: None,
166 cash_from_investing: None,
167 cash_from_financing: None,
168 net_cash_change: None,
169 beginning_cash: None,
170 ending_cash: None,
171 total_debits: None,
172 total_credits: None,
173 trial_balance_check,
174 line_items,
175 comparative_data,
176 variance_data,
177 notes,
178 management_notes: None,
179 generated_at: None,
180 generated_by: None,
181 generation_parameters,
182 reviewed_at: None,
183 reviewed_by: None,
184 approved_at: None,
185 approved_by: None,
186 published_at: None,
187 pdf_url: None,
188 excel_url: None,
189 currency,
190 metadata: AuditMetadata::default(),
191 }
192 }
193
194 pub fn id(&self) -> &Uuid {
196 &self.id
197 }
198
199 pub fn typed_id(&self) -> FinancialStatementId {
201 FinancialStatementId(self.id)
202 }
203
204 pub fn created_at(&self) -> Option<&DateTime<Utc>> {
206 self.metadata.created_at.as_ref()
207 }
208
209 pub fn updated_at(&self) -> Option<&DateTime<Utc>> {
211 self.metadata.updated_at.as_ref()
212 }
213
214 pub fn is_deleted(&self) -> bool {
216 self.metadata.deleted_at.is_some()
217 }
218
219 pub fn is_active(&self) -> bool {
221 self.metadata.deleted_at.is_none()
222 }
223
224 pub fn deleted_at(&self) -> Option<&DateTime<Utc>> {
226 self.metadata.deleted_at.as_ref()
227 }
228
229 pub fn created_by(&self) -> Option<&Uuid> {
231 self.metadata.created_by.as_ref()
232 }
233
234 pub fn updated_by(&self) -> Option<&Uuid> {
236 self.metadata.updated_by.as_ref()
237 }
238
239 pub fn deleted_by(&self) -> Option<&Uuid> {
241 self.metadata.deleted_by.as_ref()
242 }
243
244 pub fn status(&self) -> &StatementStatus {
246 &self.status
247 }
248
249
250 pub fn with_fiscal_period_id(mut self, value: Uuid) -> Self {
256 self.fiscal_period_id = Some(value);
257 self
258 }
259
260 pub fn with_fiscal_month(mut self, value: i32) -> Self {
262 self.fiscal_month = Some(value);
263 self
264 }
265
266 pub fn with_period_start(mut self, value: NaiveDate) -> Self {
268 self.period_start = Some(value);
269 self
270 }
271
272 pub fn with_period_end(mut self, value: NaiveDate) -> Self {
274 self.period_end = Some(value);
275 self
276 }
277
278 pub fn with_comparative_period_id(mut self, value: Uuid) -> Self {
280 self.comparative_period_id = Some(value);
281 self
282 }
283
284 pub fn with_comparative_as_of_date(mut self, value: NaiveDate) -> Self {
286 self.comparative_as_of_date = Some(value);
287 self
288 }
289
290 pub fn with_total_current_assets(mut self, value: Decimal) -> Self {
292 self.total_current_assets = Some(value);
293 self
294 }
295
296 pub fn with_total_non_current_assets(mut self, value: Decimal) -> Self {
298 self.total_non_current_assets = Some(value);
299 self
300 }
301
302 pub fn with_total_assets(mut self, value: Decimal) -> Self {
304 self.total_assets = Some(value);
305 self
306 }
307
308 pub fn with_total_current_liabilities(mut self, value: Decimal) -> Self {
310 self.total_current_liabilities = Some(value);
311 self
312 }
313
314 pub fn with_total_non_current_liabilities(mut self, value: Decimal) -> Self {
316 self.total_non_current_liabilities = Some(value);
317 self
318 }
319
320 pub fn with_total_liabilities(mut self, value: Decimal) -> Self {
322 self.total_liabilities = Some(value);
323 self
324 }
325
326 pub fn with_total_equity(mut self, value: Decimal) -> Self {
328 self.total_equity = Some(value);
329 self
330 }
331
332 pub fn with_balance_difference(mut self, value: Decimal) -> Self {
334 self.balance_difference = Some(value);
335 self
336 }
337
338 pub fn with_total_revenue(mut self, value: Decimal) -> Self {
340 self.total_revenue = Some(value);
341 self
342 }
343
344 pub fn with_total_cogs(mut self, value: Decimal) -> Self {
346 self.total_cogs = Some(value);
347 self
348 }
349
350 pub fn with_gross_profit(mut self, value: Decimal) -> Self {
352 self.gross_profit = Some(value);
353 self
354 }
355
356 pub fn with_gross_profit_margin(mut self, value: Decimal) -> Self {
358 self.gross_profit_margin = Some(value);
359 self
360 }
361
362 pub fn with_total_operating_expenses(mut self, value: Decimal) -> Self {
364 self.total_operating_expenses = Some(value);
365 self
366 }
367
368 pub fn with_operating_income(mut self, value: Decimal) -> Self {
370 self.operating_income = Some(value);
371 self
372 }
373
374 pub fn with_operating_margin(mut self, value: Decimal) -> Self {
376 self.operating_margin = Some(value);
377 self
378 }
379
380 pub fn with_total_other_income(mut self, value: Decimal) -> Self {
382 self.total_other_income = Some(value);
383 self
384 }
385
386 pub fn with_total_other_expenses(mut self, value: Decimal) -> Self {
388 self.total_other_expenses = Some(value);
389 self
390 }
391
392 pub fn with_income_before_tax(mut self, value: Decimal) -> Self {
394 self.income_before_tax = Some(value);
395 self
396 }
397
398 pub fn with_tax_expense(mut self, value: Decimal) -> Self {
400 self.tax_expense = Some(value);
401 self
402 }
403
404 pub fn with_net_income(mut self, value: Decimal) -> Self {
406 self.net_income = Some(value);
407 self
408 }
409
410 pub fn with_net_profit_margin(mut self, value: Decimal) -> Self {
412 self.net_profit_margin = Some(value);
413 self
414 }
415
416 pub fn with_cash_from_operations(mut self, value: Decimal) -> Self {
418 self.cash_from_operations = Some(value);
419 self
420 }
421
422 pub fn with_cash_from_investing(mut self, value: Decimal) -> Self {
424 self.cash_from_investing = Some(value);
425 self
426 }
427
428 pub fn with_cash_from_financing(mut self, value: Decimal) -> Self {
430 self.cash_from_financing = Some(value);
431 self
432 }
433
434 pub fn with_net_cash_change(mut self, value: Decimal) -> Self {
436 self.net_cash_change = Some(value);
437 self
438 }
439
440 pub fn with_beginning_cash(mut self, value: Decimal) -> Self {
442 self.beginning_cash = Some(value);
443 self
444 }
445
446 pub fn with_ending_cash(mut self, value: Decimal) -> Self {
448 self.ending_cash = Some(value);
449 self
450 }
451
452 pub fn with_total_debits(mut self, value: Decimal) -> Self {
454 self.total_debits = Some(value);
455 self
456 }
457
458 pub fn with_total_credits(mut self, value: Decimal) -> Self {
460 self.total_credits = Some(value);
461 self
462 }
463
464 pub fn with_management_notes(mut self, value: String) -> Self {
466 self.management_notes = Some(value);
467 self
468 }
469
470 pub fn with_generated_at(mut self, value: DateTime<Utc>) -> Self {
472 self.generated_at = Some(value);
473 self
474 }
475
476 pub fn with_generated_by(mut self, value: Uuid) -> Self {
478 self.generated_by = Some(value);
479 self
480 }
481
482 pub fn with_reviewed_at(mut self, value: DateTime<Utc>) -> Self {
484 self.reviewed_at = Some(value);
485 self
486 }
487
488 pub fn with_reviewed_by(mut self, value: Uuid) -> Self {
490 self.reviewed_by = Some(value);
491 self
492 }
493
494 pub fn with_approved_at(mut self, value: DateTime<Utc>) -> Self {
496 self.approved_at = Some(value);
497 self
498 }
499
500 pub fn with_approved_by(mut self, value: Uuid) -> Self {
502 self.approved_by = Some(value);
503 self
504 }
505
506 pub fn with_published_at(mut self, value: DateTime<Utc>) -> Self {
508 self.published_at = Some(value);
509 self
510 }
511
512 pub fn with_pdf_url(mut self, value: String) -> Self {
514 self.pdf_url = Some(value);
515 self
516 }
517
518 pub fn with_excel_url(mut self, value: String) -> Self {
520 self.excel_url = Some(value);
521 self
522 }
523
524 pub fn apply_patch(&mut self, fields: std::collections::HashMap<String, serde_json::Value>) {
530 for (key, value) in fields {
531 match key.as_str() {
532 "statement_number" => {
533 if let Ok(v) = serde_json::from_value(value) { self.statement_number = v; }
534 }
535 "statement_type" => {
536 if let Ok(v) = serde_json::from_value(value) { self.statement_type = v; }
537 }
538 "name" => {
539 if let Ok(v) = serde_json::from_value(value) { self.name = v; }
540 }
541 "fiscal_period_id" => {
542 if let Ok(v) = serde_json::from_value(value) { self.fiscal_period_id = v; }
543 }
544 "fiscal_year" => {
545 if let Ok(v) = serde_json::from_value(value) { self.fiscal_year = v; }
546 }
547 "fiscal_month" => {
548 if let Ok(v) = serde_json::from_value(value) { self.fiscal_month = v; }
549 }
550 "as_of_date" => {
551 if let Ok(v) = serde_json::from_value(value) { self.as_of_date = v; }
552 }
553 "period_start" => {
554 if let Ok(v) = serde_json::from_value(value) { self.period_start = v; }
555 }
556 "period_end" => {
557 if let Ok(v) = serde_json::from_value(value) { self.period_end = v; }
558 }
559 "is_comparative" => {
560 if let Ok(v) = serde_json::from_value(value) { self.is_comparative = v; }
561 }
562 "comparative_period_id" => {
563 if let Ok(v) = serde_json::from_value(value) { self.comparative_period_id = v; }
564 }
565 "comparative_as_of_date" => {
566 if let Ok(v) = serde_json::from_value(value) { self.comparative_as_of_date = v; }
567 }
568 "status" => {
569 if let Ok(v) = serde_json::from_value(value) { self.status = v; }
570 }
571 "total_current_assets" => {
572 if let Ok(v) = serde_json::from_value(value) { self.total_current_assets = v; }
573 }
574 "total_non_current_assets" => {
575 if let Ok(v) = serde_json::from_value(value) { self.total_non_current_assets = v; }
576 }
577 "total_assets" => {
578 if let Ok(v) = serde_json::from_value(value) { self.total_assets = v; }
579 }
580 "total_current_liabilities" => {
581 if let Ok(v) = serde_json::from_value(value) { self.total_current_liabilities = v; }
582 }
583 "total_non_current_liabilities" => {
584 if let Ok(v) = serde_json::from_value(value) { self.total_non_current_liabilities = v; }
585 }
586 "total_liabilities" => {
587 if let Ok(v) = serde_json::from_value(value) { self.total_liabilities = v; }
588 }
589 "total_equity" => {
590 if let Ok(v) = serde_json::from_value(value) { self.total_equity = v; }
591 }
592 "balance_check" => {
593 if let Ok(v) = serde_json::from_value(value) { self.balance_check = v; }
594 }
595 "balance_difference" => {
596 if let Ok(v) = serde_json::from_value(value) { self.balance_difference = v; }
597 }
598 "total_revenue" => {
599 if let Ok(v) = serde_json::from_value(value) { self.total_revenue = v; }
600 }
601 "total_cogs" => {
602 if let Ok(v) = serde_json::from_value(value) { self.total_cogs = v; }
603 }
604 "gross_profit" => {
605 if let Ok(v) = serde_json::from_value(value) { self.gross_profit = v; }
606 }
607 "gross_profit_margin" => {
608 if let Ok(v) = serde_json::from_value(value) { self.gross_profit_margin = v; }
609 }
610 "total_operating_expenses" => {
611 if let Ok(v) = serde_json::from_value(value) { self.total_operating_expenses = v; }
612 }
613 "operating_income" => {
614 if let Ok(v) = serde_json::from_value(value) { self.operating_income = v; }
615 }
616 "operating_margin" => {
617 if let Ok(v) = serde_json::from_value(value) { self.operating_margin = v; }
618 }
619 "total_other_income" => {
620 if let Ok(v) = serde_json::from_value(value) { self.total_other_income = v; }
621 }
622 "total_other_expenses" => {
623 if let Ok(v) = serde_json::from_value(value) { self.total_other_expenses = v; }
624 }
625 "income_before_tax" => {
626 if let Ok(v) = serde_json::from_value(value) { self.income_before_tax = v; }
627 }
628 "tax_expense" => {
629 if let Ok(v) = serde_json::from_value(value) { self.tax_expense = v; }
630 }
631 "net_income" => {
632 if let Ok(v) = serde_json::from_value(value) { self.net_income = v; }
633 }
634 "net_profit_margin" => {
635 if let Ok(v) = serde_json::from_value(value) { self.net_profit_margin = v; }
636 }
637 "cash_from_operations" => {
638 if let Ok(v) = serde_json::from_value(value) { self.cash_from_operations = v; }
639 }
640 "cash_from_investing" => {
641 if let Ok(v) = serde_json::from_value(value) { self.cash_from_investing = v; }
642 }
643 "cash_from_financing" => {
644 if let Ok(v) = serde_json::from_value(value) { self.cash_from_financing = v; }
645 }
646 "net_cash_change" => {
647 if let Ok(v) = serde_json::from_value(value) { self.net_cash_change = v; }
648 }
649 "beginning_cash" => {
650 if let Ok(v) = serde_json::from_value(value) { self.beginning_cash = v; }
651 }
652 "ending_cash" => {
653 if let Ok(v) = serde_json::from_value(value) { self.ending_cash = v; }
654 }
655 "total_debits" => {
656 if let Ok(v) = serde_json::from_value(value) { self.total_debits = v; }
657 }
658 "total_credits" => {
659 if let Ok(v) = serde_json::from_value(value) { self.total_credits = v; }
660 }
661 "trial_balance_check" => {
662 if let Ok(v) = serde_json::from_value(value) { self.trial_balance_check = v; }
663 }
664 "line_items" => {
665 if let Ok(v) = serde_json::from_value(value) { self.line_items = v; }
666 }
667 "comparative_data" => {
668 if let Ok(v) = serde_json::from_value(value) { self.comparative_data = v; }
669 }
670 "variance_data" => {
671 if let Ok(v) = serde_json::from_value(value) { self.variance_data = v; }
672 }
673 "notes" => {
674 if let Ok(v) = serde_json::from_value(value) { self.notes = v; }
675 }
676 "management_notes" => {
677 if let Ok(v) = serde_json::from_value(value) { self.management_notes = v; }
678 }
679 "generated_at" => {
680 if let Ok(v) = serde_json::from_value(value) { self.generated_at = v; }
681 }
682 "generated_by" => {
683 if let Ok(v) = serde_json::from_value(value) { self.generated_by = v; }
684 }
685 "generation_parameters" => {
686 if let Ok(v) = serde_json::from_value(value) { self.generation_parameters = v; }
687 }
688 "reviewed_at" => {
689 if let Ok(v) = serde_json::from_value(value) { self.reviewed_at = v; }
690 }
691 "reviewed_by" => {
692 if let Ok(v) = serde_json::from_value(value) { self.reviewed_by = v; }
693 }
694 "approved_at" => {
695 if let Ok(v) = serde_json::from_value(value) { self.approved_at = v; }
696 }
697 "approved_by" => {
698 if let Ok(v) = serde_json::from_value(value) { self.approved_by = v; }
699 }
700 "published_at" => {
701 if let Ok(v) = serde_json::from_value(value) { self.published_at = v; }
702 }
703 "pdf_url" => {
704 if let Ok(v) = serde_json::from_value(value) { self.pdf_url = v; }
705 }
706 "excel_url" => {
707 if let Ok(v) = serde_json::from_value(value) { self.excel_url = v; }
708 }
709 "currency" => {
710 if let Ok(v) = serde_json::from_value(value) { self.currency = v; }
711 }
712 _ => {} }
714 }
715 }
716
717}
718
719impl super::Entity for FinancialStatement {
720 type Id = Uuid;
721
722 fn entity_id(&self) -> &Self::Id {
723 &self.id
724 }
725
726 fn entity_type() -> &'static str {
727 "FinancialStatement"
728 }
729}
730
731impl backbone_core::PersistentEntity for FinancialStatement {
732 fn entity_id(&self) -> String {
733 self.id.to_string()
734 }
735 fn set_entity_id(&mut self, id: String) {
736 if let Ok(uuid) = uuid::Uuid::parse_str(&id) {
737 self.id = uuid;
738 }
739 }
740 fn created_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
741 self.metadata.created_at
742 }
743 fn set_created_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
744 self.metadata.created_at = Some(ts);
745 }
746 fn updated_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
747 self.metadata.updated_at
748 }
749 fn set_updated_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
750 self.metadata.updated_at = Some(ts);
751 }
752 fn deleted_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
753 self.metadata.deleted_at
754 }
755 fn set_deleted_at(&mut self, ts: Option<chrono::DateTime<chrono::Utc>>) {
756 self.metadata.deleted_at = ts;
757 }
758}
759
760impl backbone_orm::EntityRepoMeta for FinancialStatement {
761 fn column_types() -> std::collections::HashMap<String, String> {
762 let mut m = std::collections::HashMap::new();
763 m.insert("id".to_string(), "uuid".to_string());
764 m.insert("fiscal_period_id".to_string(), "uuid".to_string());
765 m.insert("comparative_period_id".to_string(), "uuid".to_string());
766 m.insert("statement_type".to_string(), "statement_type".to_string());
767 m.insert("status".to_string(), "statement_status".to_string());
768 m.insert("as_of_date".to_string(), "date".to_string());
769 m.insert("period_start".to_string(), "date".to_string());
770 m.insert("period_end".to_string(), "date".to_string());
771 m.insert("comparative_as_of_date".to_string(), "date".to_string());
772 m.insert("generated_at".to_string(), "timestamptz".to_string());
773 m.insert("reviewed_at".to_string(), "timestamptz".to_string());
774 m.insert("approved_at".to_string(), "timestamptz".to_string());
775 m.insert("published_at".to_string(), "timestamptz".to_string());
776 m
777 }
778 fn search_fields() -> &'static [&'static str] {
779 &["statement_number", "name", "currency"]
780 }
781 fn relations() -> &'static [(&'static str, &'static str, &'static str)] {
782 &[("fiscalPeriod", "fiscal_periods", "fiscalPeriodId"), ("comparativePeriod", "fiscal_periods", "comparativePeriodId")]
783 }
784}
785
786#[derive(Debug, Clone, Default)]
791pub struct FinancialStatementBuilder {
792 statement_number: Option<String>,
793 statement_type: Option<StatementType>,
794 name: Option<String>,
795 fiscal_period_id: Option<Uuid>,
796 fiscal_year: Option<i32>,
797 fiscal_month: Option<i32>,
798 as_of_date: Option<NaiveDate>,
799 period_start: Option<NaiveDate>,
800 period_end: Option<NaiveDate>,
801 is_comparative: Option<bool>,
802 comparative_period_id: Option<Uuid>,
803 comparative_as_of_date: Option<NaiveDate>,
804 status: Option<StatementStatus>,
805 total_current_assets: Option<Decimal>,
806 total_non_current_assets: Option<Decimal>,
807 total_assets: Option<Decimal>,
808 total_current_liabilities: Option<Decimal>,
809 total_non_current_liabilities: Option<Decimal>,
810 total_liabilities: Option<Decimal>,
811 total_equity: Option<Decimal>,
812 balance_check: Option<bool>,
813 balance_difference: Option<Decimal>,
814 total_revenue: Option<Decimal>,
815 total_cogs: Option<Decimal>,
816 gross_profit: Option<Decimal>,
817 gross_profit_margin: Option<Decimal>,
818 total_operating_expenses: Option<Decimal>,
819 operating_income: Option<Decimal>,
820 operating_margin: Option<Decimal>,
821 total_other_income: Option<Decimal>,
822 total_other_expenses: Option<Decimal>,
823 income_before_tax: Option<Decimal>,
824 tax_expense: Option<Decimal>,
825 net_income: Option<Decimal>,
826 net_profit_margin: Option<Decimal>,
827 cash_from_operations: Option<Decimal>,
828 cash_from_investing: Option<Decimal>,
829 cash_from_financing: Option<Decimal>,
830 net_cash_change: Option<Decimal>,
831 beginning_cash: Option<Decimal>,
832 ending_cash: Option<Decimal>,
833 total_debits: Option<Decimal>,
834 total_credits: Option<Decimal>,
835 trial_balance_check: Option<bool>,
836 line_items: Option<serde_json::Value>,
837 comparative_data: Option<serde_json::Value>,
838 variance_data: Option<serde_json::Value>,
839 notes: Option<serde_json::Value>,
840 management_notes: Option<String>,
841 generated_at: Option<DateTime<Utc>>,
842 generated_by: Option<Uuid>,
843 generation_parameters: Option<serde_json::Value>,
844 reviewed_at: Option<DateTime<Utc>>,
845 reviewed_by: Option<Uuid>,
846 approved_at: Option<DateTime<Utc>>,
847 approved_by: Option<Uuid>,
848 published_at: Option<DateTime<Utc>>,
849 pdf_url: Option<String>,
850 excel_url: Option<String>,
851 currency: Option<String>,
852}
853
854impl FinancialStatementBuilder {
855 pub fn statement_number(mut self, value: String) -> Self {
857 self.statement_number = Some(value);
858 self
859 }
860
861 pub fn statement_type(mut self, value: StatementType) -> Self {
863 self.statement_type = Some(value);
864 self
865 }
866
867 pub fn name(mut self, value: String) -> Self {
869 self.name = Some(value);
870 self
871 }
872
873 pub fn fiscal_period_id(mut self, value: Uuid) -> Self {
875 self.fiscal_period_id = Some(value);
876 self
877 }
878
879 pub fn fiscal_year(mut self, value: i32) -> Self {
881 self.fiscal_year = Some(value);
882 self
883 }
884
885 pub fn fiscal_month(mut self, value: i32) -> Self {
887 self.fiscal_month = Some(value);
888 self
889 }
890
891 pub fn as_of_date(mut self, value: NaiveDate) -> Self {
893 self.as_of_date = Some(value);
894 self
895 }
896
897 pub fn period_start(mut self, value: NaiveDate) -> Self {
899 self.period_start = Some(value);
900 self
901 }
902
903 pub fn period_end(mut self, value: NaiveDate) -> Self {
905 self.period_end = Some(value);
906 self
907 }
908
909 pub fn is_comparative(mut self, value: bool) -> Self {
911 self.is_comparative = Some(value);
912 self
913 }
914
915 pub fn comparative_period_id(mut self, value: Uuid) -> Self {
917 self.comparative_period_id = Some(value);
918 self
919 }
920
921 pub fn comparative_as_of_date(mut self, value: NaiveDate) -> Self {
923 self.comparative_as_of_date = Some(value);
924 self
925 }
926
927 pub fn status(mut self, value: StatementStatus) -> Self {
929 self.status = Some(value);
930 self
931 }
932
933 pub fn total_current_assets(mut self, value: Decimal) -> Self {
935 self.total_current_assets = Some(value);
936 self
937 }
938
939 pub fn total_non_current_assets(mut self, value: Decimal) -> Self {
941 self.total_non_current_assets = Some(value);
942 self
943 }
944
945 pub fn total_assets(mut self, value: Decimal) -> Self {
947 self.total_assets = Some(value);
948 self
949 }
950
951 pub fn total_current_liabilities(mut self, value: Decimal) -> Self {
953 self.total_current_liabilities = Some(value);
954 self
955 }
956
957 pub fn total_non_current_liabilities(mut self, value: Decimal) -> Self {
959 self.total_non_current_liabilities = Some(value);
960 self
961 }
962
963 pub fn total_liabilities(mut self, value: Decimal) -> Self {
965 self.total_liabilities = Some(value);
966 self
967 }
968
969 pub fn total_equity(mut self, value: Decimal) -> Self {
971 self.total_equity = Some(value);
972 self
973 }
974
975 pub fn balance_check(mut self, value: bool) -> Self {
977 self.balance_check = Some(value);
978 self
979 }
980
981 pub fn balance_difference(mut self, value: Decimal) -> Self {
983 self.balance_difference = Some(value);
984 self
985 }
986
987 pub fn total_revenue(mut self, value: Decimal) -> Self {
989 self.total_revenue = Some(value);
990 self
991 }
992
993 pub fn total_cogs(mut self, value: Decimal) -> Self {
995 self.total_cogs = Some(value);
996 self
997 }
998
999 pub fn gross_profit(mut self, value: Decimal) -> Self {
1001 self.gross_profit = Some(value);
1002 self
1003 }
1004
1005 pub fn gross_profit_margin(mut self, value: Decimal) -> Self {
1007 self.gross_profit_margin = Some(value);
1008 self
1009 }
1010
1011 pub fn total_operating_expenses(mut self, value: Decimal) -> Self {
1013 self.total_operating_expenses = Some(value);
1014 self
1015 }
1016
1017 pub fn operating_income(mut self, value: Decimal) -> Self {
1019 self.operating_income = Some(value);
1020 self
1021 }
1022
1023 pub fn operating_margin(mut self, value: Decimal) -> Self {
1025 self.operating_margin = Some(value);
1026 self
1027 }
1028
1029 pub fn total_other_income(mut self, value: Decimal) -> Self {
1031 self.total_other_income = Some(value);
1032 self
1033 }
1034
1035 pub fn total_other_expenses(mut self, value: Decimal) -> Self {
1037 self.total_other_expenses = Some(value);
1038 self
1039 }
1040
1041 pub fn income_before_tax(mut self, value: Decimal) -> Self {
1043 self.income_before_tax = Some(value);
1044 self
1045 }
1046
1047 pub fn tax_expense(mut self, value: Decimal) -> Self {
1049 self.tax_expense = Some(value);
1050 self
1051 }
1052
1053 pub fn net_income(mut self, value: Decimal) -> Self {
1055 self.net_income = Some(value);
1056 self
1057 }
1058
1059 pub fn net_profit_margin(mut self, value: Decimal) -> Self {
1061 self.net_profit_margin = Some(value);
1062 self
1063 }
1064
1065 pub fn cash_from_operations(mut self, value: Decimal) -> Self {
1067 self.cash_from_operations = Some(value);
1068 self
1069 }
1070
1071 pub fn cash_from_investing(mut self, value: Decimal) -> Self {
1073 self.cash_from_investing = Some(value);
1074 self
1075 }
1076
1077 pub fn cash_from_financing(mut self, value: Decimal) -> Self {
1079 self.cash_from_financing = Some(value);
1080 self
1081 }
1082
1083 pub fn net_cash_change(mut self, value: Decimal) -> Self {
1085 self.net_cash_change = Some(value);
1086 self
1087 }
1088
1089 pub fn beginning_cash(mut self, value: Decimal) -> Self {
1091 self.beginning_cash = Some(value);
1092 self
1093 }
1094
1095 pub fn ending_cash(mut self, value: Decimal) -> Self {
1097 self.ending_cash = Some(value);
1098 self
1099 }
1100
1101 pub fn total_debits(mut self, value: Decimal) -> Self {
1103 self.total_debits = Some(value);
1104 self
1105 }
1106
1107 pub fn total_credits(mut self, value: Decimal) -> Self {
1109 self.total_credits = Some(value);
1110 self
1111 }
1112
1113 pub fn trial_balance_check(mut self, value: bool) -> Self {
1115 self.trial_balance_check = Some(value);
1116 self
1117 }
1118
1119 pub fn line_items(mut self, value: serde_json::Value) -> Self {
1121 self.line_items = Some(value);
1122 self
1123 }
1124
1125 pub fn comparative_data(mut self, value: serde_json::Value) -> Self {
1127 self.comparative_data = Some(value);
1128 self
1129 }
1130
1131 pub fn variance_data(mut self, value: serde_json::Value) -> Self {
1133 self.variance_data = Some(value);
1134 self
1135 }
1136
1137 pub fn notes(mut self, value: serde_json::Value) -> Self {
1139 self.notes = Some(value);
1140 self
1141 }
1142
1143 pub fn management_notes(mut self, value: String) -> Self {
1145 self.management_notes = Some(value);
1146 self
1147 }
1148
1149 pub fn generated_at(mut self, value: DateTime<Utc>) -> Self {
1151 self.generated_at = Some(value);
1152 self
1153 }
1154
1155 pub fn generated_by(mut self, value: Uuid) -> Self {
1157 self.generated_by = Some(value);
1158 self
1159 }
1160
1161 pub fn generation_parameters(mut self, value: serde_json::Value) -> Self {
1163 self.generation_parameters = Some(value);
1164 self
1165 }
1166
1167 pub fn reviewed_at(mut self, value: DateTime<Utc>) -> Self {
1169 self.reviewed_at = Some(value);
1170 self
1171 }
1172
1173 pub fn reviewed_by(mut self, value: Uuid) -> Self {
1175 self.reviewed_by = Some(value);
1176 self
1177 }
1178
1179 pub fn approved_at(mut self, value: DateTime<Utc>) -> Self {
1181 self.approved_at = Some(value);
1182 self
1183 }
1184
1185 pub fn approved_by(mut self, value: Uuid) -> Self {
1187 self.approved_by = Some(value);
1188 self
1189 }
1190
1191 pub fn published_at(mut self, value: DateTime<Utc>) -> Self {
1193 self.published_at = Some(value);
1194 self
1195 }
1196
1197 pub fn pdf_url(mut self, value: String) -> Self {
1199 self.pdf_url = Some(value);
1200 self
1201 }
1202
1203 pub fn excel_url(mut self, value: String) -> Self {
1205 self.excel_url = Some(value);
1206 self
1207 }
1208
1209 pub fn currency(mut self, value: String) -> Self {
1211 self.currency = Some(value);
1212 self
1213 }
1214
1215 pub fn build(self) -> Result<FinancialStatement, String> {
1219 let statement_number = self.statement_number.ok_or_else(|| "statement_number is required".to_string())?;
1220 let statement_type = self.statement_type.ok_or_else(|| "statement_type is required".to_string())?;
1221 let name = self.name.ok_or_else(|| "name is required".to_string())?;
1222 let fiscal_year = self.fiscal_year.ok_or_else(|| "fiscal_year is required".to_string())?;
1223 let as_of_date = self.as_of_date.ok_or_else(|| "as_of_date is required".to_string())?;
1224
1225 Ok(FinancialStatement {
1226 id: Uuid::new_v4(),
1227 statement_number,
1228 statement_type,
1229 name,
1230 fiscal_period_id: self.fiscal_period_id,
1231 fiscal_year,
1232 fiscal_month: self.fiscal_month,
1233 as_of_date,
1234 period_start: self.period_start,
1235 period_end: self.period_end,
1236 is_comparative: self.is_comparative.unwrap_or(false),
1237 comparative_period_id: self.comparative_period_id,
1238 comparative_as_of_date: self.comparative_as_of_date,
1239 status: self.status.unwrap_or_default(),
1240 total_current_assets: self.total_current_assets,
1241 total_non_current_assets: self.total_non_current_assets,
1242 total_assets: self.total_assets,
1243 total_current_liabilities: self.total_current_liabilities,
1244 total_non_current_liabilities: self.total_non_current_liabilities,
1245 total_liabilities: self.total_liabilities,
1246 total_equity: self.total_equity,
1247 balance_check: self.balance_check.unwrap_or(false),
1248 balance_difference: self.balance_difference,
1249 total_revenue: self.total_revenue,
1250 total_cogs: self.total_cogs,
1251 gross_profit: self.gross_profit,
1252 gross_profit_margin: self.gross_profit_margin,
1253 total_operating_expenses: self.total_operating_expenses,
1254 operating_income: self.operating_income,
1255 operating_margin: self.operating_margin,
1256 total_other_income: self.total_other_income,
1257 total_other_expenses: self.total_other_expenses,
1258 income_before_tax: self.income_before_tax,
1259 tax_expense: self.tax_expense,
1260 net_income: self.net_income,
1261 net_profit_margin: self.net_profit_margin,
1262 cash_from_operations: self.cash_from_operations,
1263 cash_from_investing: self.cash_from_investing,
1264 cash_from_financing: self.cash_from_financing,
1265 net_cash_change: self.net_cash_change,
1266 beginning_cash: self.beginning_cash,
1267 ending_cash: self.ending_cash,
1268 total_debits: self.total_debits,
1269 total_credits: self.total_credits,
1270 trial_balance_check: self.trial_balance_check.unwrap_or(false),
1271 line_items: self.line_items.unwrap_or(serde_json::json!([])),
1272 comparative_data: self.comparative_data.unwrap_or(serde_json::json!({})),
1273 variance_data: self.variance_data.unwrap_or(serde_json::json!({})),
1274 notes: self.notes.unwrap_or(serde_json::json!([])),
1275 management_notes: self.management_notes,
1276 generated_at: self.generated_at,
1277 generated_by: self.generated_by,
1278 generation_parameters: self.generation_parameters.unwrap_or(serde_json::json!({})),
1279 reviewed_at: self.reviewed_at,
1280 reviewed_by: self.reviewed_by,
1281 approved_at: self.approved_at,
1282 approved_by: self.approved_by,
1283 published_at: self.published_at,
1284 pdf_url: self.pdf_url,
1285 excel_url: self.excel_url,
1286 currency: self.currency.unwrap_or("IDR".to_string()),
1287 metadata: AuditMetadata::default(),
1288 })
1289 }
1290}