Skip to main content

backbone_inventory/presentation/dto/
delivery_note_dto.rs

1//! DeliveryNote DTOs (Data Transfer Objects)
2//!
3//! Generated by metaphor-schema. Do not edit manually.
4//!
5//! DTOs provide a clean separation between domain entities and API
6//! representations, with OpenAPI documentation support.
7
8use 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::DeliveryNote;
18use crate::domain::entity::AuditMetadata;
19use crate::domain::entity::DocStatus;
20use crate::domain::entity::GlPostingState;
21
22// =============================================================================
23// Create DTO
24// =============================================================================
25
26/// Request DTO for creating a new DeliveryNote
27///
28/// Used in POST requests to create new entities.
29/// Excludes auto-generated fields (id, created_at, updated_at, deleted_at).
30#[derive(Debug, Clone, Deserialize)]
31#[cfg_attr(feature = "openapi", derive(ToSchema))]
32#[serde(rename_all = "camelCase")]
33pub struct CreateDeliveryNoteDto {
34    #[cfg_attr(feature = "openapi", schema(example = "example"))]
35    #[serde(alias = "delivery_number")]
36    pub delivery_number: String,
37    #[serde(default, skip_serializing_if = "Option::is_none", alias = "branch_id")]
38    pub branch_id: Option<Uuid>,
39    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
40    #[serde(alias = "customer_id")]
41    pub customer_id: Uuid,
42    #[serde(default, skip_serializing_if = "Option::is_none", alias = "source_so_id")]
43    pub source_so_id: Option<Uuid>,
44    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
45    #[serde(alias = "warehouse_id")]
46    pub warehouse_id: Uuid,
47    #[cfg_attr(feature = "openapi", schema(example = "2024-01-01"))]
48    #[serde(alias = "posting_date")]
49    pub posting_date: NaiveDate,
50    #[serde(alias = "total_cogs")]
51    pub total_cogs: Decimal,
52    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
53    #[serde(alias = "cogs_account_id")]
54    pub cogs_account_id: Uuid,
55    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
56    #[serde(alias = "inventory_account_id")]
57    pub inventory_account_id: Uuid,
58    #[serde(default, skip_serializing_if = "Option::is_none", alias = "transfer_id")]
59    pub transfer_id: Option<Uuid>,
60    pub status: DocStatus,
61    #[serde(alias = "posting_state")]
62    pub posting_state: GlPostingState,
63    #[serde(default, skip_serializing_if = "Option::is_none", alias = "journal_id")]
64    pub journal_id: Option<Uuid>,
65    #[serde(default, skip_serializing_if = "Option::is_none", alias = "accounting_post_id")]
66    pub accounting_post_id: Option<Uuid>,
67    #[serde(default, skip_serializing_if = "Option::is_none", alias = "posted_at")]
68    pub posted_at: Option<DateTime<Utc>>,
69    #[serde(default, skip_serializing_if = "Option::is_none")]
70    pub notes: Option<String>,
71}
72
73// =============================================================================
74// Update DTO
75// =============================================================================
76
77/// Request DTO for full update of a DeliveryNote
78///
79/// Used in PUT requests for full entity replacement.
80/// All fields are required (except auto-generated ones).
81#[derive(Debug, Clone, Deserialize)]
82#[cfg_attr(feature = "openapi", derive(ToSchema))]
83#[serde(rename_all = "camelCase")]
84pub struct UpdateDeliveryNoteDto {
85    #[cfg_attr(feature = "openapi", schema(example = "example"))]
86    #[serde(alias = "delivery_number")]
87    pub delivery_number: String,
88    #[serde(default, skip_serializing_if = "Option::is_none", alias = "branch_id")]
89    pub branch_id: Option<Uuid>,
90    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
91    #[serde(alias = "customer_id")]
92    pub customer_id: Uuid,
93    #[serde(default, skip_serializing_if = "Option::is_none", alias = "source_so_id")]
94    pub source_so_id: Option<Uuid>,
95    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
96    #[serde(alias = "warehouse_id")]
97    pub warehouse_id: Uuid,
98    #[cfg_attr(feature = "openapi", schema(example = "2024-01-01"))]
99    #[serde(alias = "posting_date")]
100    pub posting_date: NaiveDate,
101    #[serde(alias = "total_cogs")]
102    pub total_cogs: Decimal,
103    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
104    #[serde(alias = "cogs_account_id")]
105    pub cogs_account_id: Uuid,
106    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
107    #[serde(alias = "inventory_account_id")]
108    pub inventory_account_id: Uuid,
109    #[serde(default, skip_serializing_if = "Option::is_none", alias = "transfer_id")]
110    pub transfer_id: Option<Uuid>,
111    pub status: DocStatus,
112    #[serde(alias = "posting_state")]
113    pub posting_state: GlPostingState,
114    #[serde(default, skip_serializing_if = "Option::is_none", alias = "journal_id")]
115    pub journal_id: Option<Uuid>,
116    #[serde(default, skip_serializing_if = "Option::is_none", alias = "accounting_post_id")]
117    pub accounting_post_id: Option<Uuid>,
118    #[serde(default, skip_serializing_if = "Option::is_none", alias = "posted_at")]
119    pub posted_at: Option<DateTime<Utc>>,
120    #[serde(default, skip_serializing_if = "Option::is_none")]
121    pub notes: Option<String>,
122}
123
124// =============================================================================
125// Patch DTO
126// =============================================================================
127
128/// Request DTO for partial update of a DeliveryNote
129///
130/// Used in PATCH requests for partial updates.
131/// All fields are optional - only provided fields will be updated.
132#[derive(Debug, Clone, Default, Deserialize)]
133#[cfg_attr(feature = "openapi", derive(ToSchema))]
134#[serde(rename_all = "camelCase")]
135pub struct PatchDeliveryNoteDto {
136    #[cfg_attr(feature = "openapi", schema(example = "example"))]
137    #[serde(skip_serializing_if = "Option::is_none", alias = "delivery_number")]
138    pub delivery_number: Option<String>,
139    #[serde(skip_serializing_if = "Option::is_none", alias = "branch_id")]
140    pub branch_id: Option<Uuid>,
141    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
142    #[serde(skip_serializing_if = "Option::is_none", alias = "customer_id")]
143    pub customer_id: Option<Uuid>,
144    #[serde(skip_serializing_if = "Option::is_none", alias = "source_so_id")]
145    pub source_so_id: Option<Uuid>,
146    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
147    #[serde(skip_serializing_if = "Option::is_none", alias = "warehouse_id")]
148    pub warehouse_id: Option<Uuid>,
149    #[cfg_attr(feature = "openapi", schema(example = "2024-01-01"))]
150    #[serde(skip_serializing_if = "Option::is_none", alias = "posting_date")]
151    pub posting_date: Option<NaiveDate>,
152    #[serde(skip_serializing_if = "Option::is_none", alias = "total_cogs")]
153    pub total_cogs: Option<Decimal>,
154    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
155    #[serde(skip_serializing_if = "Option::is_none", alias = "cogs_account_id")]
156    pub cogs_account_id: Option<Uuid>,
157    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
158    #[serde(skip_serializing_if = "Option::is_none", alias = "inventory_account_id")]
159    pub inventory_account_id: Option<Uuid>,
160    #[serde(skip_serializing_if = "Option::is_none", alias = "transfer_id")]
161    pub transfer_id: Option<Uuid>,
162    #[serde(skip_serializing_if = "Option::is_none")]
163    pub status: Option<DocStatus>,
164    #[serde(skip_serializing_if = "Option::is_none", alias = "posting_state")]
165    pub posting_state: Option<GlPostingState>,
166    #[serde(skip_serializing_if = "Option::is_none", alias = "journal_id")]
167    pub journal_id: Option<Uuid>,
168    #[serde(skip_serializing_if = "Option::is_none", alias = "accounting_post_id")]
169    pub accounting_post_id: Option<Uuid>,
170    #[serde(skip_serializing_if = "Option::is_none", alias = "posted_at")]
171    pub posted_at: Option<DateTime<Utc>>,
172    #[serde(skip_serializing_if = "Option::is_none")]
173    pub notes: Option<String>,
174}
175
176impl PatchDeliveryNoteDto {
177    /// Check if any field is set
178    pub fn has_changes(&self) -> bool {
179        self.delivery_number.is_some() || self.branch_id.is_some() || self.customer_id.is_some() || self.source_so_id.is_some() || self.warehouse_id.is_some() || self.posting_date.is_some() || self.total_cogs.is_some() || self.cogs_account_id.is_some() || self.inventory_account_id.is_some() || self.transfer_id.is_some() || self.status.is_some() || self.posting_state.is_some() || self.journal_id.is_some() || self.accounting_post_id.is_some() || self.posted_at.is_some() || self.notes.is_some()
180    }
181}
182
183// =============================================================================
184// Response DTO
185// =============================================================================
186
187/// Response DTO for DeliveryNote entity
188///
189/// Used in API responses for single entity.
190/// Includes all fields including metadata.
191#[derive(Debug, Clone, Serialize)]
192#[cfg_attr(feature = "openapi", derive(ToSchema))]
193#[serde(rename_all = "camelCase")]
194pub struct DeliveryNoteResponseDto {
195    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
196    pub id: Uuid,
197    #[cfg_attr(feature = "openapi", schema(example = "example"))]
198    pub delivery_number: String,
199    pub branch_id: Option<Uuid>,
200    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
201    pub customer_id: Uuid,
202    pub source_so_id: Option<Uuid>,
203    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
204    pub warehouse_id: Uuid,
205    #[cfg_attr(feature = "openapi", schema(example = "2024-01-01"))]
206    pub posting_date: NaiveDate,
207    pub total_cogs: Decimal,
208    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
209    pub cogs_account_id: Uuid,
210    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
211    pub inventory_account_id: Uuid,
212    pub transfer_id: Option<Uuid>,
213    pub status: DocStatus,
214    pub posting_state: GlPostingState,
215    pub journal_id: Option<Uuid>,
216    pub accounting_post_id: Option<Uuid>,
217    pub posted_at: Option<DateTime<Utc>>,
218    pub notes: Option<String>,
219    pub metadata: AuditMetadata,
220}
221
222// =============================================================================
223// List Response DTO
224// =============================================================================
225
226/// Paginated list response for DeliveryNote entities
227///
228/// Used in API responses for list endpoints.
229/// Includes pagination metadata.
230#[derive(Debug, Clone, Serialize)]
231#[cfg_attr(feature = "openapi", derive(ToSchema))]
232#[serde(rename_all = "camelCase")]
233pub struct DeliveryNoteListResponseDto {
234    /// List of items
235    pub items: Vec<DeliveryNoteResponseDto>,
236    /// Total number of items (across all pages)
237    pub total: u64,
238    /// Current page number (1-indexed)
239    pub page: u32,
240    /// Number of items per page
241    pub per_page: u32,
242    /// Total number of pages
243    pub total_pages: u32,
244    /// Whether there is a next page
245    pub has_next: bool,
246    /// Whether there is a previous page
247    pub has_prev: bool,
248}
249
250impl DeliveryNoteListResponseDto {
251    /// Create a new list response from items and pagination info
252    pub fn new(items: Vec<DeliveryNoteResponseDto>, total: u64, page: u32, per_page: u32) -> Self {
253        let total_pages = if per_page > 0 {
254            ((total as f64) / (per_page as f64)).ceil() as u32
255        } else {
256            0
257        };
258        Self {
259            items,
260            total,
261            page,
262            per_page,
263            total_pages,
264            has_next: page < total_pages,
265            has_prev: page > 1,
266        }
267    }
268}
269
270/// Summary DTO for list views (compact version)
271#[derive(Debug, Clone, Serialize)]
272#[cfg_attr(feature = "openapi", derive(ToSchema))]
273#[serde(rename_all = "camelCase")]
274pub struct DeliveryNoteSummaryDto {
275    pub id: Uuid,
276    pub delivery_number: String,
277    pub branch_id: Option<Uuid>,
278    pub customer_id: Uuid,
279    pub created_at: Option<DateTime<Utc>>,
280}
281
282// =============================================================================
283// Conversions
284// =============================================================================
285
286impl From<DeliveryNote> for DeliveryNoteResponseDto {
287    fn from(entity: DeliveryNote) -> Self {
288        Self {
289            id: entity.id,
290            delivery_number: entity.delivery_number,
291            branch_id: entity.branch_id,
292            customer_id: entity.customer_id,
293            source_so_id: entity.source_so_id,
294            warehouse_id: entity.warehouse_id,
295            posting_date: entity.posting_date,
296            total_cogs: entity.total_cogs,
297            cogs_account_id: entity.cogs_account_id,
298            inventory_account_id: entity.inventory_account_id,
299            transfer_id: entity.transfer_id,
300            status: entity.status,
301            posting_state: entity.posting_state,
302            journal_id: entity.journal_id,
303            accounting_post_id: entity.accounting_post_id,
304            posted_at: entity.posted_at,
305            notes: entity.notes,
306            metadata: entity.metadata,
307        }
308    }
309}
310
311impl From<DeliveryNote> for DeliveryNoteSummaryDto {
312    fn from(entity: DeliveryNote) -> Self {
313        let created_at = backbone_core::PersistentEntity::created_at(&entity);
314        Self {
315            id: entity.id,
316            delivery_number: entity.delivery_number,
317            branch_id: entity.branch_id,
318            customer_id: entity.customer_id,
319            created_at,
320        }
321    }
322}
323
324impl From<CreateDeliveryNoteDto> for DeliveryNote {
325    fn from(dto: CreateDeliveryNoteDto) -> Self {
326        Self {
327            id: Uuid::new_v4(),
328            delivery_number: dto.delivery_number,
329            branch_id: dto.branch_id,
330            customer_id: dto.customer_id,
331            source_so_id: dto.source_so_id,
332            warehouse_id: dto.warehouse_id,
333            posting_date: dto.posting_date,
334            total_cogs: dto.total_cogs,
335            cogs_account_id: dto.cogs_account_id,
336            inventory_account_id: dto.inventory_account_id,
337            transfer_id: dto.transfer_id,
338            status: dto.status,
339            posting_state: dto.posting_state,
340            journal_id: dto.journal_id,
341            accounting_post_id: dto.accounting_post_id,
342            posted_at: dto.posted_at,
343            notes: dto.notes,
344            metadata: AuditMetadata::default(),
345        }
346    }
347}
348
349impl From<&DeliveryNote> for DeliveryNoteResponseDto {
350    fn from(entity: &DeliveryNote) -> Self {
351        Self {
352            id: entity.id.clone(),
353            delivery_number: entity.delivery_number.clone(),
354            branch_id: entity.branch_id.clone(),
355            customer_id: entity.customer_id.clone(),
356            source_so_id: entity.source_so_id.clone(),
357            warehouse_id: entity.warehouse_id.clone(),
358            posting_date: entity.posting_date.clone(),
359            total_cogs: entity.total_cogs.clone(),
360            cogs_account_id: entity.cogs_account_id.clone(),
361            inventory_account_id: entity.inventory_account_id.clone(),
362            transfer_id: entity.transfer_id.clone(),
363            status: entity.status.clone(),
364            posting_state: entity.posting_state.clone(),
365            journal_id: entity.journal_id.clone(),
366            accounting_post_id: entity.accounting_post_id.clone(),
367            posted_at: entity.posted_at.clone(),
368            notes: entity.notes.clone(),
369            metadata: entity.metadata.clone(),
370        }
371    }
372}
373
374impl backbone_core::FromCreateDto<CreateDeliveryNoteDto> for DeliveryNote {
375    fn from_create_dto(dto: CreateDeliveryNoteDto) -> backbone_core::ServiceResult<Self> {
376        Ok(DeliveryNote::from(dto))
377    }
378}
379
380impl backbone_core::ApplyUpdateDto<UpdateDeliveryNoteDto> for DeliveryNote {
381    fn apply_update(mut self, dto: UpdateDeliveryNoteDto) -> backbone_core::ServiceResult<Self> {
382        self.delivery_number = dto.delivery_number;
383        self.branch_id = dto.branch_id;
384        self.customer_id = dto.customer_id;
385        self.source_so_id = dto.source_so_id;
386        self.warehouse_id = dto.warehouse_id;
387        self.posting_date = dto.posting_date;
388        self.total_cogs = dto.total_cogs;
389        self.cogs_account_id = dto.cogs_account_id;
390        self.inventory_account_id = dto.inventory_account_id;
391        self.transfer_id = dto.transfer_id;
392        self.status = dto.status;
393        self.posting_state = dto.posting_state;
394        self.journal_id = dto.journal_id;
395        self.accounting_post_id = dto.accounting_post_id;
396        self.posted_at = dto.posted_at;
397        self.notes = dto.notes;
398        Ok(self)
399    }
400}
401
402// =============================================================================
403// Custom DTOs
404// =============================================================================
405
406// Add custom DTOs specific to DeliveryNote here.
407// This section will be preserved during regeneration.
408// >>> END CUSTOM DTOs
409