backbone_catalog/presentation/dto/
attribute_dto.rs1use serde::{Deserialize, Serialize};
9use uuid::Uuid;
10use chrono::{DateTime, Utc};
11
12#[cfg(feature = "openapi")]
13#[cfg(feature = "openapi")]
14use utoipa::ToSchema;
15
16use crate::domain::entity::Attribute;
17use crate::domain::entity::AuditMetadata;
18use crate::domain::entity::AttributeType;
19use crate::domain::entity::CatalogStatus;
20
21#[derive(Debug, Clone, Deserialize)]
30#[cfg_attr(feature = "openapi", derive(ToSchema))]
31#[serde(rename_all = "camelCase")]
32pub struct CreateAttributeDto {
33 #[cfg_attr(feature = "openapi", schema(example = "example"))]
34 pub code: String,
35 #[cfg_attr(feature = "openapi", schema(example = "example"))]
36 pub name: String,
37 #[serde(alias = "attribute_type")]
38 pub attribute_type: AttributeType,
39 pub status: CatalogStatus,
40}
41
42#[derive(Debug, Clone, Deserialize)]
51#[cfg_attr(feature = "openapi", derive(ToSchema))]
52#[serde(rename_all = "camelCase")]
53pub struct UpdateAttributeDto {
54 #[cfg_attr(feature = "openapi", schema(example = "example"))]
55 pub code: String,
56 #[cfg_attr(feature = "openapi", schema(example = "example"))]
57 pub name: String,
58 #[serde(alias = "attribute_type")]
59 pub attribute_type: AttributeType,
60 pub status: CatalogStatus,
61}
62
63#[derive(Debug, Clone, Default, Deserialize)]
72#[cfg_attr(feature = "openapi", derive(ToSchema))]
73#[serde(rename_all = "camelCase")]
74pub struct PatchAttributeDto {
75 #[cfg_attr(feature = "openapi", schema(example = "example"))]
76 #[serde(skip_serializing_if = "Option::is_none")]
77 pub code: Option<String>,
78 #[cfg_attr(feature = "openapi", schema(example = "example"))]
79 #[serde(skip_serializing_if = "Option::is_none")]
80 pub name: Option<String>,
81 #[serde(skip_serializing_if = "Option::is_none", alias = "attribute_type")]
82 pub attribute_type: Option<AttributeType>,
83 #[serde(skip_serializing_if = "Option::is_none")]
84 pub status: Option<CatalogStatus>,
85}
86
87impl PatchAttributeDto {
88 pub fn has_changes(&self) -> bool {
90 self.code.is_some() || self.name.is_some() || self.attribute_type.is_some() || self.status.is_some()
91 }
92}
93
94#[derive(Debug, Clone, Serialize)]
103#[cfg_attr(feature = "openapi", derive(ToSchema))]
104#[serde(rename_all = "camelCase")]
105pub struct AttributeResponseDto {
106 #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
107 pub id: Uuid,
108 #[cfg_attr(feature = "openapi", schema(example = "example"))]
109 pub code: String,
110 #[cfg_attr(feature = "openapi", schema(example = "example"))]
111 pub name: String,
112 pub attribute_type: AttributeType,
113 pub status: CatalogStatus,
114 pub metadata: AuditMetadata,
115}
116
117#[derive(Debug, Clone, Serialize)]
126#[cfg_attr(feature = "openapi", derive(ToSchema))]
127#[serde(rename_all = "camelCase")]
128pub struct AttributeListResponseDto {
129 pub items: Vec<AttributeResponseDto>,
131 pub total: u64,
133 pub page: u32,
135 pub per_page: u32,
137 pub total_pages: u32,
139 pub has_next: bool,
141 pub has_prev: bool,
143}
144
145impl AttributeListResponseDto {
146 pub fn new(items: Vec<AttributeResponseDto>, total: u64, page: u32, per_page: u32) -> Self {
148 let total_pages = if per_page > 0 {
149 ((total as f64) / (per_page as f64)).ceil() as u32
150 } else {
151 0
152 };
153 Self {
154 items,
155 total,
156 page,
157 per_page,
158 total_pages,
159 has_next: page < total_pages,
160 has_prev: page > 1,
161 }
162 }
163}
164
165#[derive(Debug, Clone, Serialize)]
167#[cfg_attr(feature = "openapi", derive(ToSchema))]
168#[serde(rename_all = "camelCase")]
169pub struct AttributeSummaryDto {
170 pub id: Uuid,
171 pub code: String,
172 pub name: String,
173 pub attribute_type: AttributeType,
174 pub created_at: Option<DateTime<Utc>>,
175}
176
177impl From<Attribute> for AttributeResponseDto {
182 fn from(entity: Attribute) -> Self {
183 Self {
184 id: entity.id,
185 code: entity.code,
186 name: entity.name,
187 attribute_type: entity.attribute_type,
188 status: entity.status,
189 metadata: entity.metadata,
190 }
191 }
192}
193
194impl From<Attribute> for AttributeSummaryDto {
195 fn from(entity: Attribute) -> Self {
196 let created_at = backbone_core::PersistentEntity::created_at(&entity);
197 Self {
198 id: entity.id,
199 code: entity.code,
200 name: entity.name,
201 attribute_type: entity.attribute_type,
202 created_at,
203 }
204 }
205}
206
207impl From<CreateAttributeDto> for Attribute {
208 fn from(dto: CreateAttributeDto) -> Self {
209 Self {
210 id: Uuid::new_v4(),
211 code: dto.code,
212 name: dto.name,
213 attribute_type: dto.attribute_type,
214 status: dto.status,
215 metadata: AuditMetadata::default(),
216 }
217 }
218}
219
220impl From<&Attribute> for AttributeResponseDto {
221 fn from(entity: &Attribute) -> Self {
222 Self {
223 id: entity.id.clone(),
224 code: entity.code.clone(),
225 name: entity.name.clone(),
226 attribute_type: entity.attribute_type.clone(),
227 status: entity.status.clone(),
228 metadata: entity.metadata.clone(),
229 }
230 }
231}
232
233impl backbone_core::FromCreateDto<CreateAttributeDto> for Attribute {
234 fn from_create_dto(dto: CreateAttributeDto) -> backbone_core::ServiceResult<Self> {
235 Ok(Attribute::from(dto))
236 }
237}
238
239impl backbone_core::ApplyUpdateDto<UpdateAttributeDto> for Attribute {
240 fn apply_update(mut self, dto: UpdateAttributeDto) -> backbone_core::ServiceResult<Self> {
241 self.code = dto.code;
242 self.name = dto.name;
243 self.attribute_type = dto.attribute_type;
244 self.status = dto.status;
245 Ok(self)
246 }
247}
248
249