koprogo_api/application/dto/
expense_dto.rs

1use crate::domain::entities::{ApprovalStatus, ExpenseCategory, PaymentStatus};
2use serde::{Deserialize, Serialize};
3use validator::Validate;
4
5// ========== Legacy DTOs (backward compatibility) ==========
6
7#[derive(Debug, Deserialize, Validate, Clone)]
8pub struct CreateExpenseDto {
9    pub organization_id: String,
10    pub building_id: String,
11    pub category: ExpenseCategory,
12
13    #[validate(length(min = 1))]
14    pub description: String,
15
16    #[validate(range(min = 0.01))]
17    pub amount: f64,
18
19    pub expense_date: String,
20    pub supplier: Option<String>,
21    pub invoice_number: Option<String>,
22
23    /// Optional Belgian PCMN account code (e.g., "604001" for electricity)
24    /// Must reference an existing account in the organization's chart of accounts
25    #[validate(length(max = 40))]
26    pub account_code: Option<String>,
27}
28
29#[derive(Debug, Serialize)]
30pub struct ExpenseResponseDto {
31    pub id: String,
32    pub building_id: String,
33    pub category: ExpenseCategory,
34    pub description: String,
35    pub amount: f64,
36    pub expense_date: String,
37    pub payment_status: PaymentStatus,
38    pub approval_status: ApprovalStatus,
39    pub supplier: Option<String>,
40    pub invoice_number: Option<String>,
41    /// Belgian PCMN account code if linked to chart of accounts
42    pub account_code: Option<String>,
43}
44
45// ========== New Invoice DTOs (with VAT & Workflow) ==========
46
47/// Créer une facture brouillon avec gestion TVA
48#[derive(Debug, Deserialize, Validate, Clone)]
49pub struct CreateInvoiceDraftDto {
50    pub organization_id: String,
51    pub building_id: String,
52    pub category: ExpenseCategory,
53
54    #[validate(length(min = 1))]
55    pub description: String,
56
57    #[validate(range(min = 0.01))]
58    pub amount_excl_vat: f64, // Montant HT
59
60    #[validate(range(min = 0.0, max = 100.0))]
61    pub vat_rate: f64, // Taux TVA (21.0 pour 21%)
62
63    pub invoice_date: String,     // ISO 8601
64    pub due_date: Option<String>, // ISO 8601
65    pub supplier: Option<String>,
66    pub invoice_number: Option<String>,
67}
68
69/// Modifier une facture brouillon ou rejetée
70#[derive(Debug, Deserialize, Validate, Clone)]
71pub struct UpdateInvoiceDraftDto {
72    #[validate(length(min = 1))]
73    pub description: Option<String>,
74
75    pub category: Option<ExpenseCategory>,
76
77    #[validate(range(min = 0.01))]
78    pub amount_excl_vat: Option<f64>,
79
80    #[validate(range(min = 0.0, max = 100.0))]
81    pub vat_rate: Option<f64>,
82
83    pub invoice_date: Option<String>,
84    pub due_date: Option<String>,
85    pub supplier: Option<String>,
86    pub invoice_number: Option<String>,
87}
88
89/// Soumettre une facture pour validation (Draft → PendingApproval)
90#[derive(Debug, Deserialize, Clone)]
91pub struct SubmitForApprovalDto {
92    // Empty body, action via PUT /invoices/:id/submit
93}
94
95/// Approuver une facture (PendingApproval → Approved)
96#[derive(Debug, Deserialize, Clone)]
97pub struct ApproveInvoiceDto {
98    pub approved_by_user_id: String, // User ID du syndic/admin
99}
100
101/// Rejeter une facture avec raison (PendingApproval → Rejected)
102#[derive(Debug, Deserialize, Validate, Clone)]
103pub struct RejectInvoiceDto {
104    pub rejected_by_user_id: String,
105
106    #[validate(length(min = 1))]
107    pub rejection_reason: String,
108}
109
110/// Créer une ligne de facture
111#[derive(Debug, Deserialize, Validate, Clone)]
112pub struct CreateInvoiceLineItemDto {
113    pub expense_id: String,
114
115    #[validate(length(min = 1))]
116    pub description: String,
117
118    #[validate(range(min = 0.01))]
119    pub quantity: f64,
120
121    #[validate(range(min = 0.0))]
122    pub unit_price: f64,
123
124    #[validate(range(min = 0.0, max = 100.0))]
125    pub vat_rate: f64,
126}
127
128// ========== Response DTOs ==========
129
130/// Response enrichie avec tous les champs invoice/workflow
131#[derive(Debug, Serialize)]
132pub struct InvoiceResponseDto {
133    pub id: String,
134    pub organization_id: String,
135    pub building_id: String,
136    pub category: ExpenseCategory,
137    pub description: String,
138
139    // Montants
140    pub amount: f64, // TTC (backward compatibility)
141    pub amount_excl_vat: Option<f64>,
142    pub vat_rate: Option<f64>,
143    pub vat_amount: Option<f64>,
144    pub amount_incl_vat: Option<f64>,
145
146    // Dates
147    pub expense_date: String,
148    pub invoice_date: Option<String>,
149    pub due_date: Option<String>,
150    pub paid_date: Option<String>,
151
152    // Workflow
153    pub approval_status: ApprovalStatus,
154    pub submitted_at: Option<String>,
155    pub approved_by: Option<String>,
156    pub approved_at: Option<String>,
157    pub rejection_reason: Option<String>,
158
159    // Payment
160    pub payment_status: PaymentStatus,
161    pub supplier: Option<String>,
162    pub invoice_number: Option<String>,
163
164    pub created_at: String,
165    pub updated_at: String,
166}
167
168/// Response pour une ligne de facture
169#[derive(Debug, Serialize)]
170pub struct InvoiceLineItemResponseDto {
171    pub id: String,
172    pub expense_id: String,
173    pub description: String,
174    pub quantity: f64,
175    pub unit_price: f64,
176    pub amount_excl_vat: f64,
177    pub vat_rate: f64,
178    pub vat_amount: f64,
179    pub amount_incl_vat: f64,
180    pub created_at: String,
181}
182
183/// Response pour une répartition de charge
184#[derive(Debug, Serialize)]
185pub struct ChargeDistributionResponseDto {
186    pub id: String,
187    pub expense_id: String,
188    pub unit_id: String,
189    pub owner_id: String,
190    pub quota_percentage: f64, // Sera converti en pourcentage côté client (0.25 → 25%)
191    pub amount_due: f64,
192    pub created_at: String,
193}
194
195/// Liste des factures en attente d'approbation (pour syndics)
196#[derive(Debug, Serialize)]
197pub struct PendingInvoicesListDto {
198    pub invoices: Vec<InvoiceResponseDto>,
199    pub count: usize,
200}