backbone_inventory/presentation/dto/
bin_dto.rs1use 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#[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#[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#[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 pub fn has_changes(&self) -> bool {
91 self.item_id.is_some() || self.warehouse_id.is_some()
92 }
93}
94
95#[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#[derive(Debug, Clone, Serialize)]
129#[cfg_attr(feature = "openapi", derive(ToSchema))]
130#[serde(rename_all = "camelCase")]
131pub struct BinListResponseDto {
132 pub items: Vec<BinResponseDto>,
134 pub total: u64,
136 pub page: u32,
138 pub per_page: u32,
140 pub total_pages: u32,
142 pub has_next: bool,
144 pub has_prev: bool,
146}
147
148impl BinListResponseDto {
149 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#[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
180impl 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