Skip to main content

backbone_inventory/presentation/dto/
bin_dto.rs

1//! Bin 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};
11use rust_decimal::Decimal;
12
13#[cfg(feature = "openapi")]
14#[cfg(feature = "openapi")]
15use utoipa::ToSchema;
16
17use crate::domain::entity::Bin;
18use crate::domain::entity::AuditMetadata;
19
20// =============================================================================
21// Create DTO
22// =============================================================================
23
24/// Request DTO for creating a new Bin
25///
26/// Used in POST requests to create new entities.
27/// Excludes auto-generated fields (id, created_at, updated_at, deleted_at).
28#[derive(Debug, Clone, Deserialize)]
29#[cfg_attr(feature = "openapi", derive(ToSchema))]
30#[serde(rename_all = "camelCase")]
31pub struct CreateBinDto {
32    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
33    #[serde(alias = "item_id")]
34    pub item_id: Uuid,
35    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
36    #[serde(alias = "warehouse_id")]
37    pub warehouse_id: Uuid,
38    #[serde(alias = "actual_qty")]
39    pub actual_qty: Decimal,
40    #[serde(alias = "reserved_qty")]
41    pub reserved_qty: Decimal,
42    #[serde(alias = "valuation_rate")]
43    pub valuation_rate: Decimal,
44    #[serde(alias = "stock_value")]
45    pub stock_value: Decimal,
46}
47
48// =============================================================================
49// Update DTO
50// =============================================================================
51
52/// Request DTO for full update of a Bin
53///
54/// Used in PUT requests for full entity replacement.
55/// All fields are required (except auto-generated ones).
56#[derive(Debug, Clone, Deserialize)]
57#[cfg_attr(feature = "openapi", derive(ToSchema))]
58#[serde(rename_all = "camelCase")]
59pub struct UpdateBinDto {
60    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
61    #[serde(alias = "item_id")]
62    pub item_id: Uuid,
63    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
64    #[serde(alias = "warehouse_id")]
65    pub warehouse_id: Uuid,
66}
67
68// =============================================================================
69// Patch DTO
70// =============================================================================
71
72/// Request DTO for partial update of a Bin
73///
74/// Used in PATCH requests for partial updates.
75/// All fields are optional - only provided fields will be updated.
76#[derive(Debug, Clone, Default, Deserialize)]
77#[cfg_attr(feature = "openapi", derive(ToSchema))]
78#[serde(rename_all = "camelCase")]
79pub struct PatchBinDto {
80    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
81    #[serde(skip_serializing_if = "Option::is_none", alias = "item_id")]
82    pub item_id: Option<Uuid>,
83    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
84    #[serde(skip_serializing_if = "Option::is_none", alias = "warehouse_id")]
85    pub warehouse_id: Option<Uuid>,
86}
87
88impl PatchBinDto {
89    /// Check if any field is set
90    pub fn has_changes(&self) -> bool {
91        self.item_id.is_some() || self.warehouse_id.is_some()
92    }
93}
94
95// =============================================================================
96// Response DTO
97// =============================================================================
98
99/// Response DTO for Bin entity
100///
101/// Used in API responses for single entity.
102/// Includes all fields including metadata.
103#[derive(Debug, Clone, Serialize)]
104#[cfg_attr(feature = "openapi", derive(ToSchema))]
105#[serde(rename_all = "camelCase")]
106pub struct BinResponseDto {
107    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
108    pub id: Uuid,
109    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
110    pub item_id: Uuid,
111    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
112    pub warehouse_id: Uuid,
113    pub actual_qty: Decimal,
114    pub reserved_qty: Decimal,
115    pub valuation_rate: Decimal,
116    pub stock_value: Decimal,
117    pub metadata: AuditMetadata,
118}
119
120// =============================================================================
121// List Response DTO
122// =============================================================================
123
124/// Paginated list response for Bin entities
125///
126/// Used in API responses for list endpoints.
127/// Includes pagination metadata.
128#[derive(Debug, Clone, Serialize)]
129#[cfg_attr(feature = "openapi", derive(ToSchema))]
130#[serde(rename_all = "camelCase")]
131pub struct BinListResponseDto {
132    /// List of items
133    pub items: Vec<BinResponseDto>,
134    /// Total number of items (across all pages)
135    pub total: u64,
136    /// Current page number (1-indexed)
137    pub page: u32,
138    /// Number of items per page
139    pub per_page: u32,
140    /// Total number of pages
141    pub total_pages: u32,
142    /// Whether there is a next page
143    pub has_next: bool,
144    /// Whether there is a previous page
145    pub has_prev: bool,
146}
147
148impl BinListResponseDto {
149    /// Create a new list response from items and pagination info
150    pub fn new(items: Vec<BinResponseDto>, total: u64, page: u32, per_page: u32) -> Self {
151        let total_pages = if per_page > 0 {
152            ((total as f64) / (per_page as f64)).ceil() as u32
153        } else {
154            0
155        };
156        Self {
157            items,
158            total,
159            page,
160            per_page,
161            total_pages,
162            has_next: page < total_pages,
163            has_prev: page > 1,
164        }
165    }
166}
167
168/// Summary DTO for list views (compact version)
169#[derive(Debug, Clone, Serialize)]
170#[cfg_attr(feature = "openapi", derive(ToSchema))]
171#[serde(rename_all = "camelCase")]
172pub struct BinSummaryDto {
173    pub id: Uuid,
174    pub item_id: Uuid,
175    pub warehouse_id: Uuid,
176    pub actual_qty: Decimal,
177    pub created_at: Option<DateTime<Utc>>,
178}
179
180// =============================================================================
181// Conversions
182// =============================================================================
183
184impl From<Bin> for BinResponseDto {
185    fn from(entity: Bin) -> Self {
186        Self {
187            id: entity.id,
188            item_id: entity.item_id,
189            warehouse_id: entity.warehouse_id,
190            actual_qty: entity.actual_qty,
191            reserved_qty: entity.reserved_qty,
192            valuation_rate: entity.valuation_rate,
193            stock_value: entity.stock_value,
194            metadata: entity.metadata,
195        }
196    }
197}
198
199impl From<Bin> for BinSummaryDto {
200    fn from(entity: Bin) -> Self {
201        let created_at = backbone_core::PersistentEntity::created_at(&entity);
202        Self {
203            id: entity.id,
204            item_id: entity.item_id,
205            warehouse_id: entity.warehouse_id,
206            actual_qty: entity.actual_qty,
207            created_at,
208        }
209    }
210}
211
212impl From<CreateBinDto> for Bin {
213    fn from(dto: CreateBinDto) -> Self {
214        Self {
215            id: Uuid::new_v4(),
216            item_id: dto.item_id,
217            warehouse_id: dto.warehouse_id,
218            actual_qty: dto.actual_qty,
219            reserved_qty: dto.reserved_qty,
220            valuation_rate: dto.valuation_rate,
221            stock_value: dto.stock_value,
222            metadata: AuditMetadata::default(),
223        }
224    }
225}
226
227impl From<&Bin> for BinResponseDto {
228    fn from(entity: &Bin) -> Self {
229        Self {
230            id: entity.id.clone(),
231            item_id: entity.item_id.clone(),
232            warehouse_id: entity.warehouse_id.clone(),
233            actual_qty: entity.actual_qty.clone(),
234            reserved_qty: entity.reserved_qty.clone(),
235            valuation_rate: entity.valuation_rate.clone(),
236            stock_value: entity.stock_value.clone(),
237            metadata: entity.metadata.clone(),
238        }
239    }
240}
241
242impl backbone_core::FromCreateDto<CreateBinDto> for Bin {
243    fn from_create_dto(dto: CreateBinDto) -> backbone_core::ServiceResult<Self> {
244        Ok(Bin::from(dto))
245    }
246}
247
248impl backbone_core::ApplyUpdateDto<UpdateBinDto> for Bin {
249    fn apply_update(mut self, dto: UpdateBinDto) -> backbone_core::ServiceResult<Self> {
250        self.item_id = dto.item_id;
251        self.warehouse_id = dto.warehouse_id;
252        Ok(self)
253    }
254}
255
256// =============================================================================
257// Custom DTOs
258// =============================================================================
259
260// Add custom DTOs specific to Bin here.
261// This section will be preserved during regeneration.
262// >>> END CUSTOM DTOs
263