1use serde::{Deserialize, Serialize};
9use uuid::Uuid;
10use chrono::{DateTime, Utc, NaiveDate};
11use rust_decimal::Decimal;
12
13#[cfg(feature = "openapi")]
14#[cfg(feature = "openapi")]
15use utoipa::ToSchema;
16
17use crate::domain::entity::Journal;
18use crate::domain::entity::AuditMetadata;
19use crate::domain::entity::JournalSource;
20use crate::domain::entity::JournalStatus;
21use crate::domain::entity::JournalType;
22
23#[derive(Debug, Clone, Deserialize)]
32#[cfg_attr(feature = "openapi", derive(ToSchema))]
33#[serde(rename_all = "camelCase")]
34pub struct CreateJournalDto {
35 #[cfg_attr(feature = "openapi", schema(example = "example"))]
36 #[serde(alias = "journal_number")]
37 pub journal_number: String,
38 #[serde(default, skip_serializing_if = "Option::is_none", alias = "reference_number")]
39 pub reference_number: Option<String>,
40 #[serde(alias = "journal_type")]
41 pub journal_type: JournalType,
42 #[serde(default, skip_serializing_if = "Option::is_none", alias = "branch_id")]
43 pub branch_id: Option<Uuid>,
44 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01"))]
45 #[serde(alias = "transaction_date")]
46 pub transaction_date: NaiveDate,
47 #[serde(default, skip_serializing_if = "Option::is_none", alias = "posting_date")]
48 pub posting_date: Option<NaiveDate>,
49 #[serde(default, skip_serializing_if = "Option::is_none", alias = "fiscal_period_id")]
50 pub fiscal_period_id: Option<Uuid>,
51 #[serde(default, skip_serializing_if = "Option::is_none", alias = "fiscal_year")]
52 pub fiscal_year: Option<i32>,
53 #[serde(default, skip_serializing_if = "Option::is_none", alias = "fiscal_month")]
54 pub fiscal_month: Option<i32>,
55 #[cfg_attr(feature = "openapi", schema(example = "example"))]
56 pub description: String,
57 #[serde(default, skip_serializing_if = "Option::is_none")]
58 pub notes: Option<String>,
59 #[cfg_attr(feature = "openapi", schema(example = "example"))]
60 pub currency: String,
61 #[serde(alias = "total_debit")]
62 pub total_debit: Decimal,
63 #[serde(alias = "total_credit")]
64 pub total_credit: Decimal,
65 #[cfg_attr(feature = "openapi", schema(example = 42))]
66 #[serde(alias = "line_count")]
67 pub line_count: i32,
68 pub source: JournalSource,
69 #[serde(default, skip_serializing_if = "Option::is_none", alias = "source_type")]
70 pub source_type: Option<String>,
71 #[serde(default, skip_serializing_if = "Option::is_none", alias = "source_id")]
72 pub source_id: Option<Uuid>,
73 #[serde(default, skip_serializing_if = "Option::is_none", alias = "source_reference")]
74 pub source_reference: Option<String>,
75 #[cfg_attr(feature = "openapi", schema(example = true))]
76 #[serde(alias = "is_reversed")]
77 pub is_reversed: bool,
78 #[serde(default, skip_serializing_if = "Option::is_none", alias = "reversed_by_id")]
79 pub reversed_by_id: Option<Uuid>,
80 #[serde(default, skip_serializing_if = "Option::is_none", alias = "reversed_at")]
81 pub reversed_at: Option<DateTime<Utc>>,
82 #[serde(default, skip_serializing_if = "Option::is_none", alias = "reversal_reason")]
83 pub reversal_reason: Option<String>,
84 #[serde(default, skip_serializing_if = "Option::is_none", alias = "reverses_id")]
85 pub reverses_id: Option<Uuid>,
86 #[cfg_attr(feature = "openapi", schema(example = true))]
87 #[serde(alias = "is_reversing")]
88 pub is_reversing: bool,
89 #[cfg_attr(feature = "openapi", schema(example = true))]
90 #[serde(alias = "auto_reverse")]
91 pub auto_reverse: bool,
92 #[serde(default, skip_serializing_if = "Option::is_none", alias = "auto_reverse_date")]
93 pub auto_reverse_date: Option<NaiveDate>,
94 #[cfg_attr(feature = "openapi", schema(example = true))]
95 #[serde(alias = "requires_approval")]
96 pub requires_approval: bool,
97 #[serde(default, skip_serializing_if = "Option::is_none", alias = "approval_threshold")]
98 pub approval_threshold: Option<Decimal>,
99 #[serde(default, skip_serializing_if = "Option::is_none", alias = "submitted_at")]
100 pub submitted_at: Option<DateTime<Utc>>,
101 #[serde(default, skip_serializing_if = "Option::is_none", alias = "submitted_by")]
102 pub submitted_by: Option<Uuid>,
103 #[serde(default, skip_serializing_if = "Option::is_none", alias = "approved_at")]
104 pub approved_at: Option<DateTime<Utc>>,
105 #[serde(default, skip_serializing_if = "Option::is_none", alias = "approved_by")]
106 pub approved_by: Option<Uuid>,
107 #[serde(default, skip_serializing_if = "Option::is_none", alias = "rejected_at")]
108 pub rejected_at: Option<DateTime<Utc>>,
109 #[serde(default, skip_serializing_if = "Option::is_none", alias = "rejected_by")]
110 pub rejected_by: Option<Uuid>,
111 #[serde(default, skip_serializing_if = "Option::is_none", alias = "rejection_reason")]
112 pub rejection_reason: Option<String>,
113 #[serde(default, skip_serializing_if = "Option::is_none", alias = "posted_at")]
114 pub posted_at: Option<DateTime<Utc>>,
115 #[serde(default, skip_serializing_if = "Option::is_none", alias = "posted_by")]
116 pub posted_by: Option<Uuid>,
117 #[cfg_attr(feature = "openapi", schema(example = true))]
118 #[serde(alias = "is_voided")]
119 pub is_voided: bool,
120 #[serde(default, skip_serializing_if = "Option::is_none", alias = "voided_at")]
121 pub voided_at: Option<DateTime<Utc>>,
122 #[serde(default, skip_serializing_if = "Option::is_none", alias = "voided_by")]
123 pub voided_by: Option<Uuid>,
124 #[serde(default, skip_serializing_if = "Option::is_none", alias = "void_reason")]
125 pub void_reason: Option<String>,
126 pub attachments: serde_json::Value,
127}
128
129#[derive(Debug, Clone, Deserialize)]
138#[cfg_attr(feature = "openapi", derive(ToSchema))]
139#[serde(rename_all = "camelCase")]
140pub struct UpdateJournalDto {
141 #[cfg_attr(feature = "openapi", schema(example = "example"))]
142 #[serde(alias = "journal_number")]
143 pub journal_number: String,
144 #[serde(default, skip_serializing_if = "Option::is_none", alias = "reference_number")]
145 pub reference_number: Option<String>,
146 #[serde(alias = "journal_type")]
147 pub journal_type: JournalType,
148 #[serde(default, skip_serializing_if = "Option::is_none", alias = "branch_id")]
149 pub branch_id: Option<Uuid>,
150 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01"))]
151 #[serde(alias = "transaction_date")]
152 pub transaction_date: NaiveDate,
153 #[serde(default, skip_serializing_if = "Option::is_none", alias = "posting_date")]
154 pub posting_date: Option<NaiveDate>,
155 #[serde(default, skip_serializing_if = "Option::is_none", alias = "fiscal_period_id")]
156 pub fiscal_period_id: Option<Uuid>,
157 #[serde(default, skip_serializing_if = "Option::is_none", alias = "fiscal_year")]
158 pub fiscal_year: Option<i32>,
159 #[serde(default, skip_serializing_if = "Option::is_none", alias = "fiscal_month")]
160 pub fiscal_month: Option<i32>,
161 #[cfg_attr(feature = "openapi", schema(example = "example"))]
162 pub description: String,
163 #[serde(default, skip_serializing_if = "Option::is_none")]
164 pub notes: Option<String>,
165 #[cfg_attr(feature = "openapi", schema(example = "example"))]
166 pub currency: String,
167 #[serde(alias = "total_debit")]
168 pub total_debit: Decimal,
169 #[serde(alias = "total_credit")]
170 pub total_credit: Decimal,
171 #[cfg_attr(feature = "openapi", schema(example = 42))]
172 #[serde(alias = "line_count")]
173 pub line_count: i32,
174 pub source: JournalSource,
175 #[serde(default, skip_serializing_if = "Option::is_none", alias = "source_type")]
176 pub source_type: Option<String>,
177 #[serde(default, skip_serializing_if = "Option::is_none", alias = "source_id")]
178 pub source_id: Option<Uuid>,
179 #[serde(default, skip_serializing_if = "Option::is_none", alias = "source_reference")]
180 pub source_reference: Option<String>,
181 #[cfg_attr(feature = "openapi", schema(example = true))]
182 #[serde(alias = "is_reversed")]
183 pub is_reversed: bool,
184 #[serde(default, skip_serializing_if = "Option::is_none", alias = "reversed_by_id")]
185 pub reversed_by_id: Option<Uuid>,
186 #[serde(default, skip_serializing_if = "Option::is_none", alias = "reversed_at")]
187 pub reversed_at: Option<DateTime<Utc>>,
188 #[serde(default, skip_serializing_if = "Option::is_none", alias = "reversal_reason")]
189 pub reversal_reason: Option<String>,
190 #[serde(default, skip_serializing_if = "Option::is_none", alias = "reverses_id")]
191 pub reverses_id: Option<Uuid>,
192 #[cfg_attr(feature = "openapi", schema(example = true))]
193 #[serde(alias = "is_reversing")]
194 pub is_reversing: bool,
195 #[cfg_attr(feature = "openapi", schema(example = true))]
196 #[serde(alias = "auto_reverse")]
197 pub auto_reverse: bool,
198 #[serde(default, skip_serializing_if = "Option::is_none", alias = "auto_reverse_date")]
199 pub auto_reverse_date: Option<NaiveDate>,
200 pub status: JournalStatus,
201 #[cfg_attr(feature = "openapi", schema(example = true))]
202 #[serde(alias = "requires_approval")]
203 pub requires_approval: bool,
204 #[serde(default, skip_serializing_if = "Option::is_none", alias = "approval_threshold")]
205 pub approval_threshold: Option<Decimal>,
206 #[serde(default, skip_serializing_if = "Option::is_none", alias = "submitted_at")]
207 pub submitted_at: Option<DateTime<Utc>>,
208 #[serde(default, skip_serializing_if = "Option::is_none", alias = "submitted_by")]
209 pub submitted_by: Option<Uuid>,
210 #[serde(default, skip_serializing_if = "Option::is_none", alias = "approved_at")]
211 pub approved_at: Option<DateTime<Utc>>,
212 #[serde(default, skip_serializing_if = "Option::is_none", alias = "approved_by")]
213 pub approved_by: Option<Uuid>,
214 #[serde(default, skip_serializing_if = "Option::is_none", alias = "rejected_at")]
215 pub rejected_at: Option<DateTime<Utc>>,
216 #[serde(default, skip_serializing_if = "Option::is_none", alias = "rejected_by")]
217 pub rejected_by: Option<Uuid>,
218 #[serde(default, skip_serializing_if = "Option::is_none", alias = "rejection_reason")]
219 pub rejection_reason: Option<String>,
220 #[serde(default, skip_serializing_if = "Option::is_none", alias = "posted_at")]
221 pub posted_at: Option<DateTime<Utc>>,
222 #[serde(default, skip_serializing_if = "Option::is_none", alias = "posted_by")]
223 pub posted_by: Option<Uuid>,
224 #[cfg_attr(feature = "openapi", schema(example = true))]
225 #[serde(alias = "is_voided")]
226 pub is_voided: bool,
227 #[serde(default, skip_serializing_if = "Option::is_none", alias = "voided_at")]
228 pub voided_at: Option<DateTime<Utc>>,
229 #[serde(default, skip_serializing_if = "Option::is_none", alias = "voided_by")]
230 pub voided_by: Option<Uuid>,
231 #[serde(default, skip_serializing_if = "Option::is_none", alias = "void_reason")]
232 pub void_reason: Option<String>,
233 pub attachments: serde_json::Value,
234}
235
236#[derive(Debug, Clone, Default, Deserialize)]
245#[cfg_attr(feature = "openapi", derive(ToSchema))]
246#[serde(rename_all = "camelCase")]
247pub struct PatchJournalDto {
248 #[cfg_attr(feature = "openapi", schema(example = "example"))]
249 #[serde(skip_serializing_if = "Option::is_none", alias = "journal_number")]
250 pub journal_number: Option<String>,
251 #[serde(skip_serializing_if = "Option::is_none", alias = "reference_number")]
252 pub reference_number: Option<String>,
253 #[serde(skip_serializing_if = "Option::is_none", alias = "journal_type")]
254 pub journal_type: Option<JournalType>,
255 #[serde(skip_serializing_if = "Option::is_none", alias = "branch_id")]
256 pub branch_id: Option<Uuid>,
257 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01"))]
258 #[serde(skip_serializing_if = "Option::is_none", alias = "transaction_date")]
259 pub transaction_date: Option<NaiveDate>,
260 #[serde(skip_serializing_if = "Option::is_none", alias = "posting_date")]
261 pub posting_date: Option<NaiveDate>,
262 #[serde(skip_serializing_if = "Option::is_none", alias = "fiscal_period_id")]
263 pub fiscal_period_id: Option<Uuid>,
264 #[serde(skip_serializing_if = "Option::is_none", alias = "fiscal_year")]
265 pub fiscal_year: Option<i32>,
266 #[serde(skip_serializing_if = "Option::is_none", alias = "fiscal_month")]
267 pub fiscal_month: Option<i32>,
268 #[cfg_attr(feature = "openapi", schema(example = "example"))]
269 #[serde(skip_serializing_if = "Option::is_none")]
270 pub description: Option<String>,
271 #[serde(skip_serializing_if = "Option::is_none")]
272 pub notes: Option<String>,
273 #[cfg_attr(feature = "openapi", schema(example = "example"))]
274 #[serde(skip_serializing_if = "Option::is_none")]
275 pub currency: Option<String>,
276 #[serde(skip_serializing_if = "Option::is_none", alias = "total_debit")]
277 pub total_debit: Option<Decimal>,
278 #[serde(skip_serializing_if = "Option::is_none", alias = "total_credit")]
279 pub total_credit: Option<Decimal>,
280 #[cfg_attr(feature = "openapi", schema(example = 42))]
281 #[serde(skip_serializing_if = "Option::is_none", alias = "line_count")]
282 pub line_count: Option<i32>,
283 #[serde(skip_serializing_if = "Option::is_none")]
284 pub source: Option<JournalSource>,
285 #[serde(skip_serializing_if = "Option::is_none", alias = "source_type")]
286 pub source_type: Option<String>,
287 #[serde(skip_serializing_if = "Option::is_none", alias = "source_id")]
288 pub source_id: Option<Uuid>,
289 #[serde(skip_serializing_if = "Option::is_none", alias = "source_reference")]
290 pub source_reference: Option<String>,
291 #[cfg_attr(feature = "openapi", schema(example = true))]
292 #[serde(skip_serializing_if = "Option::is_none", alias = "is_reversed")]
293 pub is_reversed: Option<bool>,
294 #[serde(skip_serializing_if = "Option::is_none", alias = "reversed_by_id")]
295 pub reversed_by_id: Option<Uuid>,
296 #[serde(skip_serializing_if = "Option::is_none", alias = "reversed_at")]
297 pub reversed_at: Option<DateTime<Utc>>,
298 #[serde(skip_serializing_if = "Option::is_none", alias = "reversal_reason")]
299 pub reversal_reason: Option<String>,
300 #[serde(skip_serializing_if = "Option::is_none", alias = "reverses_id")]
301 pub reverses_id: Option<Uuid>,
302 #[cfg_attr(feature = "openapi", schema(example = true))]
303 #[serde(skip_serializing_if = "Option::is_none", alias = "is_reversing")]
304 pub is_reversing: Option<bool>,
305 #[cfg_attr(feature = "openapi", schema(example = true))]
306 #[serde(skip_serializing_if = "Option::is_none", alias = "auto_reverse")]
307 pub auto_reverse: Option<bool>,
308 #[serde(skip_serializing_if = "Option::is_none", alias = "auto_reverse_date")]
309 pub auto_reverse_date: Option<NaiveDate>,
310 #[serde(skip_serializing_if = "Option::is_none")]
311 pub status: Option<JournalStatus>,
312 #[cfg_attr(feature = "openapi", schema(example = true))]
313 #[serde(skip_serializing_if = "Option::is_none", alias = "requires_approval")]
314 pub requires_approval: Option<bool>,
315 #[serde(skip_serializing_if = "Option::is_none", alias = "approval_threshold")]
316 pub approval_threshold: Option<Decimal>,
317 #[serde(skip_serializing_if = "Option::is_none", alias = "submitted_at")]
318 pub submitted_at: Option<DateTime<Utc>>,
319 #[serde(skip_serializing_if = "Option::is_none", alias = "submitted_by")]
320 pub submitted_by: Option<Uuid>,
321 #[serde(skip_serializing_if = "Option::is_none", alias = "approved_at")]
322 pub approved_at: Option<DateTime<Utc>>,
323 #[serde(skip_serializing_if = "Option::is_none", alias = "approved_by")]
324 pub approved_by: Option<Uuid>,
325 #[serde(skip_serializing_if = "Option::is_none", alias = "rejected_at")]
326 pub rejected_at: Option<DateTime<Utc>>,
327 #[serde(skip_serializing_if = "Option::is_none", alias = "rejected_by")]
328 pub rejected_by: Option<Uuid>,
329 #[serde(skip_serializing_if = "Option::is_none", alias = "rejection_reason")]
330 pub rejection_reason: Option<String>,
331 #[serde(skip_serializing_if = "Option::is_none", alias = "posted_at")]
332 pub posted_at: Option<DateTime<Utc>>,
333 #[serde(skip_serializing_if = "Option::is_none", alias = "posted_by")]
334 pub posted_by: Option<Uuid>,
335 #[cfg_attr(feature = "openapi", schema(example = true))]
336 #[serde(skip_serializing_if = "Option::is_none", alias = "is_voided")]
337 pub is_voided: Option<bool>,
338 #[serde(skip_serializing_if = "Option::is_none", alias = "voided_at")]
339 pub voided_at: Option<DateTime<Utc>>,
340 #[serde(skip_serializing_if = "Option::is_none", alias = "voided_by")]
341 pub voided_by: Option<Uuid>,
342 #[serde(skip_serializing_if = "Option::is_none", alias = "void_reason")]
343 pub void_reason: Option<String>,
344 #[serde(skip_serializing_if = "Option::is_none")]
345 pub attachments: Option<serde_json::Value>,
346}
347
348impl PatchJournalDto {
349 pub fn has_changes(&self) -> bool {
351 self.journal_number.is_some() || self.reference_number.is_some() || self.journal_type.is_some() || self.branch_id.is_some() || self.transaction_date.is_some() || self.posting_date.is_some() || self.fiscal_period_id.is_some() || self.fiscal_year.is_some() || self.fiscal_month.is_some() || self.description.is_some() || self.notes.is_some() || self.currency.is_some() || self.total_debit.is_some() || self.total_credit.is_some() || self.line_count.is_some() || self.source.is_some() || self.source_type.is_some() || self.source_id.is_some() || self.source_reference.is_some() || self.is_reversed.is_some() || self.reversed_by_id.is_some() || self.reversed_at.is_some() || self.reversal_reason.is_some() || self.reverses_id.is_some() || self.is_reversing.is_some() || self.auto_reverse.is_some() || self.auto_reverse_date.is_some() || self.status.is_some() || self.requires_approval.is_some() || self.approval_threshold.is_some() || self.submitted_at.is_some() || self.submitted_by.is_some() || self.approved_at.is_some() || self.approved_by.is_some() || self.rejected_at.is_some() || self.rejected_by.is_some() || self.rejection_reason.is_some() || self.posted_at.is_some() || self.posted_by.is_some() || self.is_voided.is_some() || self.voided_at.is_some() || self.voided_by.is_some() || self.void_reason.is_some() || self.attachments.is_some()
352 }
353}
354
355#[derive(Debug, Clone, Serialize)]
364#[cfg_attr(feature = "openapi", derive(ToSchema))]
365#[serde(rename_all = "camelCase")]
366pub struct JournalResponseDto {
367 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
368 pub id: Uuid,
369 #[cfg_attr(feature = "openapi", schema(example = "example"))]
370 pub journal_number: String,
371 pub reference_number: Option<String>,
372 pub journal_type: JournalType,
373 pub branch_id: Option<Uuid>,
374 #[cfg_attr(feature = "openapi", schema(example = "2024-01-01"))]
375 pub transaction_date: NaiveDate,
376 pub posting_date: Option<NaiveDate>,
377 pub fiscal_period_id: Option<Uuid>,
378 pub fiscal_year: Option<i32>,
379 pub fiscal_month: Option<i32>,
380 #[cfg_attr(feature = "openapi", schema(example = "example"))]
381 pub description: String,
382 pub notes: Option<String>,
383 #[cfg_attr(feature = "openapi", schema(example = "example"))]
384 pub currency: String,
385 pub total_debit: Decimal,
386 pub total_credit: Decimal,
387 #[cfg_attr(feature = "openapi", schema(example = 42))]
388 pub line_count: i32,
389 pub source: JournalSource,
390 pub source_type: Option<String>,
391 pub source_id: Option<Uuid>,
392 pub source_reference: Option<String>,
393 #[cfg_attr(feature = "openapi", schema(example = true))]
394 pub is_reversed: bool,
395 pub reversed_by_id: Option<Uuid>,
396 pub reversed_at: Option<DateTime<Utc>>,
397 pub reversal_reason: Option<String>,
398 pub reverses_id: Option<Uuid>,
399 #[cfg_attr(feature = "openapi", schema(example = true))]
400 pub is_reversing: bool,
401 #[cfg_attr(feature = "openapi", schema(example = true))]
402 pub auto_reverse: bool,
403 pub auto_reverse_date: Option<NaiveDate>,
404 pub status: JournalStatus,
405 #[cfg_attr(feature = "openapi", schema(example = true))]
406 pub requires_approval: bool,
407 pub approval_threshold: Option<Decimal>,
408 pub submitted_at: Option<DateTime<Utc>>,
409 pub submitted_by: Option<Uuid>,
410 pub approved_at: Option<DateTime<Utc>>,
411 pub approved_by: Option<Uuid>,
412 pub rejected_at: Option<DateTime<Utc>>,
413 pub rejected_by: Option<Uuid>,
414 pub rejection_reason: Option<String>,
415 pub posted_at: Option<DateTime<Utc>>,
416 pub posted_by: Option<Uuid>,
417 #[cfg_attr(feature = "openapi", schema(example = true))]
418 pub is_voided: bool,
419 pub voided_at: Option<DateTime<Utc>>,
420 pub voided_by: Option<Uuid>,
421 pub void_reason: Option<String>,
422 pub attachments: serde_json::Value,
423 pub metadata: AuditMetadata,
424}
425
426#[derive(Debug, Clone, Serialize)]
435#[cfg_attr(feature = "openapi", derive(ToSchema))]
436#[serde(rename_all = "camelCase")]
437pub struct JournalListResponseDto {
438 pub items: Vec<JournalResponseDto>,
440 pub total: u64,
442 pub page: u32,
444 pub per_page: u32,
446 pub total_pages: u32,
448 pub has_next: bool,
450 pub has_prev: bool,
452}
453
454impl JournalListResponseDto {
455 pub fn new(items: Vec<JournalResponseDto>, total: u64, page: u32, per_page: u32) -> Self {
457 let total_pages = if per_page > 0 {
458 ((total as f64) / (per_page as f64)).ceil() as u32
459 } else {
460 0
461 };
462 Self {
463 items,
464 total,
465 page,
466 per_page,
467 total_pages,
468 has_next: page < total_pages,
469 has_prev: page > 1,
470 }
471 }
472}
473
474#[derive(Debug, Clone, Serialize)]
476#[cfg_attr(feature = "openapi", derive(ToSchema))]
477#[serde(rename_all = "camelCase")]
478pub struct JournalSummaryDto {
479 pub id: Uuid,
480 pub journal_number: String,
481 pub reference_number: Option<String>,
482 pub journal_type: JournalType,
483 pub created_at: Option<DateTime<Utc>>,
484}
485
486impl From<Journal> for JournalResponseDto {
491 fn from(entity: Journal) -> Self {
492 Self {
493 id: entity.id,
494 journal_number: entity.journal_number,
495 reference_number: entity.reference_number,
496 journal_type: entity.journal_type,
497 branch_id: entity.branch_id,
498 transaction_date: entity.transaction_date,
499 posting_date: entity.posting_date,
500 fiscal_period_id: entity.fiscal_period_id,
501 fiscal_year: entity.fiscal_year,
502 fiscal_month: entity.fiscal_month,
503 description: entity.description,
504 notes: entity.notes,
505 currency: entity.currency,
506 total_debit: entity.total_debit,
507 total_credit: entity.total_credit,
508 line_count: entity.line_count,
509 source: entity.source,
510 source_type: entity.source_type,
511 source_id: entity.source_id,
512 source_reference: entity.source_reference,
513 is_reversed: entity.is_reversed,
514 reversed_by_id: entity.reversed_by_id,
515 reversed_at: entity.reversed_at,
516 reversal_reason: entity.reversal_reason,
517 reverses_id: entity.reverses_id,
518 is_reversing: entity.is_reversing,
519 auto_reverse: entity.auto_reverse,
520 auto_reverse_date: entity.auto_reverse_date,
521 status: entity.status,
522 requires_approval: entity.requires_approval,
523 approval_threshold: entity.approval_threshold,
524 submitted_at: entity.submitted_at,
525 submitted_by: entity.submitted_by,
526 approved_at: entity.approved_at,
527 approved_by: entity.approved_by,
528 rejected_at: entity.rejected_at,
529 rejected_by: entity.rejected_by,
530 rejection_reason: entity.rejection_reason,
531 posted_at: entity.posted_at,
532 posted_by: entity.posted_by,
533 is_voided: entity.is_voided,
534 voided_at: entity.voided_at,
535 voided_by: entity.voided_by,
536 void_reason: entity.void_reason,
537 attachments: entity.attachments,
538 metadata: entity.metadata,
539 }
540 }
541}
542
543impl From<Journal> for JournalSummaryDto {
544 fn from(entity: Journal) -> Self {
545 let created_at = backbone_core::PersistentEntity::created_at(&entity);
546 Self {
547 id: entity.id,
548 journal_number: entity.journal_number,
549 reference_number: entity.reference_number,
550 journal_type: entity.journal_type,
551 created_at,
552 }
553 }
554}
555
556impl From<CreateJournalDto> for Journal {
557 fn from(dto: CreateJournalDto) -> Self {
558 Self {
559 id: Uuid::new_v4(),
560 journal_number: dto.journal_number,
561 reference_number: dto.reference_number,
562 journal_type: dto.journal_type,
563 branch_id: dto.branch_id,
564 transaction_date: dto.transaction_date,
565 posting_date: dto.posting_date,
566 fiscal_period_id: dto.fiscal_period_id,
567 fiscal_year: dto.fiscal_year,
568 fiscal_month: dto.fiscal_month,
569 description: dto.description,
570 notes: dto.notes,
571 currency: dto.currency,
572 total_debit: dto.total_debit,
573 total_credit: dto.total_credit,
574 line_count: dto.line_count,
575 source: dto.source,
576 source_type: dto.source_type,
577 source_id: dto.source_id,
578 source_reference: dto.source_reference,
579 is_reversed: dto.is_reversed,
580 reversed_by_id: dto.reversed_by_id,
581 reversed_at: dto.reversed_at,
582 reversal_reason: dto.reversal_reason,
583 reverses_id: dto.reverses_id,
584 is_reversing: dto.is_reversing,
585 auto_reverse: dto.auto_reverse,
586 auto_reverse_date: dto.auto_reverse_date,
587 status: JournalStatus::Draft,
588 requires_approval: dto.requires_approval,
589 approval_threshold: dto.approval_threshold,
590 submitted_at: dto.submitted_at,
591 submitted_by: dto.submitted_by,
592 approved_at: dto.approved_at,
593 approved_by: dto.approved_by,
594 rejected_at: dto.rejected_at,
595 rejected_by: dto.rejected_by,
596 rejection_reason: dto.rejection_reason,
597 posted_at: dto.posted_at,
598 posted_by: dto.posted_by,
599 is_voided: dto.is_voided,
600 voided_at: dto.voided_at,
601 voided_by: dto.voided_by,
602 void_reason: dto.void_reason,
603 attachments: dto.attachments,
604 metadata: AuditMetadata::default(),
605 }
606 }
607}
608
609impl From<&Journal> for JournalResponseDto {
610 fn from(entity: &Journal) -> Self {
611 Self {
612 id: entity.id.clone(),
613 journal_number: entity.journal_number.clone(),
614 reference_number: entity.reference_number.clone(),
615 journal_type: entity.journal_type.clone(),
616 branch_id: entity.branch_id.clone(),
617 transaction_date: entity.transaction_date.clone(),
618 posting_date: entity.posting_date.clone(),
619 fiscal_period_id: entity.fiscal_period_id.clone(),
620 fiscal_year: entity.fiscal_year.clone(),
621 fiscal_month: entity.fiscal_month.clone(),
622 description: entity.description.clone(),
623 notes: entity.notes.clone(),
624 currency: entity.currency.clone(),
625 total_debit: entity.total_debit.clone(),
626 total_credit: entity.total_credit.clone(),
627 line_count: entity.line_count.clone(),
628 source: entity.source.clone(),
629 source_type: entity.source_type.clone(),
630 source_id: entity.source_id.clone(),
631 source_reference: entity.source_reference.clone(),
632 is_reversed: entity.is_reversed.clone(),
633 reversed_by_id: entity.reversed_by_id.clone(),
634 reversed_at: entity.reversed_at.clone(),
635 reversal_reason: entity.reversal_reason.clone(),
636 reverses_id: entity.reverses_id.clone(),
637 is_reversing: entity.is_reversing.clone(),
638 auto_reverse: entity.auto_reverse.clone(),
639 auto_reverse_date: entity.auto_reverse_date.clone(),
640 status: entity.status.clone(),
641 requires_approval: entity.requires_approval.clone(),
642 approval_threshold: entity.approval_threshold.clone(),
643 submitted_at: entity.submitted_at.clone(),
644 submitted_by: entity.submitted_by.clone(),
645 approved_at: entity.approved_at.clone(),
646 approved_by: entity.approved_by.clone(),
647 rejected_at: entity.rejected_at.clone(),
648 rejected_by: entity.rejected_by.clone(),
649 rejection_reason: entity.rejection_reason.clone(),
650 posted_at: entity.posted_at.clone(),
651 posted_by: entity.posted_by.clone(),
652 is_voided: entity.is_voided.clone(),
653 voided_at: entity.voided_at.clone(),
654 voided_by: entity.voided_by.clone(),
655 void_reason: entity.void_reason.clone(),
656 attachments: entity.attachments.clone(),
657 metadata: entity.metadata.clone(),
658 }
659 }
660}
661
662impl backbone_core::FromCreateDto<CreateJournalDto> for Journal {
663 fn from_create_dto(dto: CreateJournalDto) -> backbone_core::ServiceResult<Self> {
664 Ok(Journal::from(dto))
665 }
666}
667
668impl backbone_core::ApplyUpdateDto<UpdateJournalDto> for Journal {
669 fn apply_update(mut self, dto: UpdateJournalDto) -> backbone_core::ServiceResult<Self> {
670 self.journal_number = dto.journal_number;
671 self.reference_number = dto.reference_number;
672 self.journal_type = dto.journal_type;
673 self.branch_id = dto.branch_id;
674 self.transaction_date = dto.transaction_date;
675 self.posting_date = dto.posting_date;
676 self.fiscal_period_id = dto.fiscal_period_id;
677 self.fiscal_year = dto.fiscal_year;
678 self.fiscal_month = dto.fiscal_month;
679 self.description = dto.description;
680 self.notes = dto.notes;
681 self.currency = dto.currency;
682 self.total_debit = dto.total_debit;
683 self.total_credit = dto.total_credit;
684 self.line_count = dto.line_count;
685 self.source = dto.source;
686 self.source_type = dto.source_type;
687 self.source_id = dto.source_id;
688 self.source_reference = dto.source_reference;
689 self.is_reversed = dto.is_reversed;
690 self.reversed_by_id = dto.reversed_by_id;
691 self.reversed_at = dto.reversed_at;
692 self.reversal_reason = dto.reversal_reason;
693 self.reverses_id = dto.reverses_id;
694 self.is_reversing = dto.is_reversing;
695 self.auto_reverse = dto.auto_reverse;
696 self.auto_reverse_date = dto.auto_reverse_date;
697 self.status = dto.status;
698 self.requires_approval = dto.requires_approval;
699 self.approval_threshold = dto.approval_threshold;
700 self.submitted_at = dto.submitted_at;
701 self.submitted_by = dto.submitted_by;
702 self.approved_at = dto.approved_at;
703 self.approved_by = dto.approved_by;
704 self.rejected_at = dto.rejected_at;
705 self.rejected_by = dto.rejected_by;
706 self.rejection_reason = dto.rejection_reason;
707 self.posted_at = dto.posted_at;
708 self.posted_by = dto.posted_by;
709 self.is_voided = dto.is_voided;
710 self.voided_at = dto.voided_at;
711 self.voided_by = dto.voided_by;
712 self.void_reason = dto.void_reason;
713 self.attachments = dto.attachments;
714 Ok(self)
715 }
716}
717
718