koprogo_api/application/dto/
document_dto.rs

1use crate::domain::entities::{Document, DocumentType};
2use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6/// Response DTO for Document
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct DocumentResponse {
9    pub id: Uuid,
10    pub building_id: Uuid,
11    pub document_type: DocumentType,
12    pub title: String,
13    pub description: Option<String>,
14    pub file_path: String,
15    pub file_size: i64,
16    pub file_size_mb: f64,
17    pub mime_type: String,
18    pub uploaded_by: Uuid,
19    pub related_meeting_id: Option<Uuid>,
20    pub related_expense_id: Option<Uuid>,
21    pub created_at: DateTime<Utc>,
22    pub updated_at: DateTime<Utc>,
23}
24
25impl From<Document> for DocumentResponse {
26    fn from(doc: Document) -> Self {
27        let file_size_mb = doc.file_size_mb();
28        Self {
29            id: doc.id,
30            building_id: doc.building_id,
31            document_type: doc.document_type,
32            title: doc.title,
33            description: doc.description,
34            file_path: doc.file_path,
35            file_size: doc.file_size,
36            file_size_mb,
37            mime_type: doc.mime_type,
38            uploaded_by: doc.uploaded_by,
39            related_meeting_id: doc.related_meeting_id,
40            related_expense_id: doc.related_expense_id,
41            created_at: doc.created_at,
42            updated_at: doc.updated_at,
43        }
44    }
45}
46
47/// Request to upload a document
48/// Note: File upload is handled via multipart/form-data, this is for metadata
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct UploadDocumentRequest {
51    pub organization_id: Uuid,
52    pub building_id: Uuid,
53    pub document_type: DocumentType,
54    pub title: String,
55    pub description: Option<String>,
56    pub uploaded_by: Uuid,
57    pub related_meeting_id: Option<Uuid>,
58    pub related_expense_id: Option<Uuid>,
59}
60
61/// Request to link document to meeting
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct LinkDocumentToMeetingRequest {
64    pub meeting_id: Uuid,
65}
66
67/// Request to link document to expense
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct LinkDocumentToExpenseRequest {
70    pub expense_id: Uuid,
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn test_document_response_from_entity() {
79        let org_id = Uuid::new_v4();
80        let building_id = Uuid::new_v4();
81        let uploader_id = Uuid::new_v4();
82
83        let document = Document::new(
84            org_id,
85            building_id,
86            DocumentType::Invoice,
87            "Test Invoice".to_string(),
88            Some("Test description".to_string()),
89            "/documents/test.pdf".to_string(),
90            1048576, // 1 MB
91            "application/pdf".to_string(),
92            uploader_id,
93        )
94        .unwrap();
95
96        let response = DocumentResponse::from(document.clone());
97
98        assert_eq!(response.id, document.id);
99        assert_eq!(response.title, "Test Invoice");
100        assert_eq!(response.file_size, 1048576);
101        assert_eq!(response.file_size_mb, 1.0);
102    }
103
104    #[test]
105    fn test_upload_request_serialization() {
106        let org_id = Uuid::new_v4();
107        let building_id = Uuid::new_v4();
108        let uploader_id = Uuid::new_v4();
109
110        let request = UploadDocumentRequest {
111            organization_id: org_id,
112            building_id,
113            document_type: DocumentType::Contract,
114            title: "Contrat de syndic".to_string(),
115            description: Some("Contrat annuel".to_string()),
116            uploaded_by: uploader_id,
117            related_meeting_id: None,
118            related_expense_id: None,
119        };
120
121        let json = serde_json::to_string(&request).unwrap();
122        assert!(json.contains("Contrat de syndic"));
123
124        let deserialized: UploadDocumentRequest = serde_json::from_str(&json).unwrap();
125        assert_eq!(deserialized.title, request.title);
126    }
127}