Skip to main content

backbone_inventory/presentation/http/
purchase_receipt_handler.rs

1//! PurchaseReceipt REST handlers
2//!
3//! Generated by metaphor-schema. Do not edit manually.
4//!
5//! Uses Axum and backbone-core's BackboneCrudHandler for all 12 CRUD endpoints.
6
7use std::collections::HashMap;
8use std::sync::Arc;
9
10use axum::Router;
11use serde::{Deserialize, Serialize};
12use uuid::Uuid;
13use chrono::{DateTime, Utc, NaiveDate};
14use rust_decimal::Decimal;
15
16// Backbone framework imports
17use backbone_core::http::BackboneCrudHandler;
18
19// Auth integration (optional)
20#[cfg(feature = "auth")]
21use backbone_auth::middleware::AuthContext;
22#[cfg(feature = "auth")]
23use backbone_auth::AuthMiddleware;
24
25// Domain imports
26use crate::domain::entity::*;
27use crate::application::service::{PurchaseReceiptService, ServiceError};
28
29// DTO imports
30use crate::presentation::dto::{CreatePurchaseReceiptDto, UpdatePurchaseReceiptDto, PatchPurchaseReceiptDto, PurchaseReceiptResponseDto};
31
32
33/// Application error type
34#[derive(Debug, thiserror::Error)]
35pub enum PurchaseReceiptError {
36    #[error("Not found: {0}")]
37    NotFound(String),
38    #[error("Validation error: {0}")]
39    Validation(String),
40    #[error("Database error: {0}")]
41    Database(String),
42    #[error("Internal error: {0}")]
43    Internal(String),
44}
45
46impl From<ServiceError> for PurchaseReceiptError {
47    fn from(err: ServiceError) -> Self {
48        match err {
49            ServiceError::NotFound => Self::NotFound(err.to_string()),
50            ServiceError::Validation(ref msg) => Self::Validation(msg.clone()),
51            ServiceError::AlreadyExists(ref msg) => Self::Validation(msg.clone()),
52            ServiceError::Repository(ref e) => Self::Database(e.to_string()),
53            ServiceError::Internal(ref msg) => Self::Internal(msg.clone()),
54            ServiceError::Violations(_) => Self::Validation(err.to_string()),
55        }
56    }
57}
58
59impl axum::response::IntoResponse for PurchaseReceiptError {
60    fn into_response(self) -> axum::response::Response {
61        use axum::http::StatusCode;
62        use axum::Json;
63
64        let (status, code) = match &self {
65            Self::NotFound(_) => (StatusCode::NOT_FOUND, "PURCHASERECEIPT_NOT_FOUND"),
66            Self::Validation(_) => (StatusCode::BAD_REQUEST, "PURCHASERECEIPT_VALIDATION_ERROR"),
67            Self::Database(_) => (StatusCode::INTERNAL_SERVER_ERROR, "PURCHASERECEIPT_DATABASE_ERROR"),
68            Self::Internal(_) => (StatusCode::INTERNAL_SERVER_ERROR, "PURCHASERECEIPT_INTERNAL_ERROR"),
69        };
70
71        let body = serde_json::json!({
72            "success": false,
73            "error": code,
74            "message": self.to_string(),
75        });
76
77        (status, Json(body)).into_response()
78    }
79}
80
81// =============================================================================
82// Route Configuration
83// =============================================================================
84
85/// The permissions this entity's generic routes require: `purchase_receipt:<action>` for `read`, `create`,
86/// `update`, `delete`, `restore` and `purge`, with any rename the schema's `requires:` declares.
87pub fn purchase_receipt_permissions() -> backbone_core::http::CrudPermissions {
88    backbone_core::http::CrudPermissions::new("purchase_receipt")
89        .in_module("inventory")
90}
91
92/// Create Axum router with all 16 Backbone endpoints for PurchaseReceipt.
93///
94/// # Routes
95///
96/// | Method | Path | Description |
97/// |--------|------|-------------|
98/// | GET | /purchase_receipts | List with pagination |
99/// | POST | /purchase_receipts | Create new |
100/// | GET | /purchase_receipts/:id | Get by ID |
101/// | PUT | /purchase_receipts/:id | Full update |
102/// | PATCH | /purchase_receipts/:id | Partial update |
103/// | DELETE | /purchase_receipts/:id | Soft delete |
104/// | POST | /purchase_receipts/bulk | Bulk create |
105/// | POST | /purchase_receipts/upsert | Upsert |
106/// | GET | /purchase_receipts/trash | List deleted |
107/// | POST | /purchase_receipts/:id/restore | Restore |
108/// | DELETE | /purchase_receipts/empty | Empty trash |
109/// | GET | /purchase_receipts/:id/deleted | Get deleted by ID |
110/// | DELETE | /purchase_receipts/trash/:id | Permanent delete from trash |
111/// | GET | /purchase_receipts/count | Count active entities |
112/// | GET | /purchase_receipts/trash/count | Count deleted entities |
113///
114/// # Example
115///
116/// ```text
117/// let service = Arc::new(PurchaseReceiptService::with_repository(repository));
118/// let router = create_purchase_receipt_routes(service);
119/// ```
120pub fn create_purchase_receipt_routes(service: Arc<PurchaseReceiptService>) -> Router {
121    BackboneCrudHandler::<PurchaseReceiptService, PurchaseReceipt, CreatePurchaseReceiptDto, UpdatePurchaseReceiptDto, PurchaseReceiptResponseDto>::routes_guarded(
122        service,
123        "/purchase_receipts",
124        purchase_receipt_permissions(),
125    )
126}
127
128/// Create Axum router with only the read (GET) endpoints for PurchaseReceipt.
129///
130/// Each route requires the entity's `read` permission, so it needs an authenticated caller.
131/// Mutations must be served separately via `create_purchase_receipt_write_routes`,
132/// typically wrapped in an auth middleware layer.
133pub fn create_purchase_receipt_read_routes(service: Arc<PurchaseReceiptService>) -> Router {
134    BackboneCrudHandler::<PurchaseReceiptService, PurchaseReceipt, CreatePurchaseReceiptDto, UpdatePurchaseReceiptDto, PurchaseReceiptResponseDto>::read_routes_guarded(
135        service,
136        "/purchase_receipts",
137        purchase_receipt_permissions(),
138    )
139}
140
141/// Create Axum router with only the write (mutation) endpoints for PurchaseReceipt.
142///
143/// These routes must NOT be publicly exposed. Wrap them with an auth
144/// middleware before nesting into the application router.
145///
146/// # This is unguarded generic CRUD, not a validated write path
147///
148/// These are plain create/update/patch/delete mutations over the entity row —
149/// they bypass all business invariants. If the module exposes a validated write
150/// service (e.g. a command router over its domain engine), serve THAT instead
151/// for any mutation that must respect domain rules.
152pub fn create_purchase_receipt_write_routes(service: Arc<PurchaseReceiptService>) -> Router {
153    BackboneCrudHandler::<PurchaseReceiptService, PurchaseReceipt, CreatePurchaseReceiptDto, UpdatePurchaseReceiptDto, PurchaseReceiptResponseDto>::write_routes_guarded(
154        service,
155        "/purchase_receipts",
156        purchase_receipt_permissions(),
157    )
158}
159
160/// Create authenticated routes with auth middleware.
161///
162/// Requires the `auth` feature flag. The `AuthMiddleware` implementation
163/// is responsible for extracting and validating tokens, then providing
164/// an `AuthContext` via request extensions.
165#[cfg(feature = "auth")]
166pub fn create_protected_purchase_receipt_routes<A: AuthMiddleware + Send + Sync + 'static>(
167    service: Arc<PurchaseReceiptService>,
168    auth: Arc<A>,
169) -> Router {
170    use axum::middleware;
171    use axum::response::IntoResponse;
172
173    let auth_layer = auth.clone();
174    create_purchase_receipt_routes(service)
175        .layer(middleware::from_fn(move |mut req: axum::extract::Request, next: axum::middleware::Next| {
176            let auth = auth_layer.clone();
177            async move {
178                let token = req.headers()
179                    .get(axum::http::header::AUTHORIZATION)
180                    .and_then(|h| h.to_str().ok())
181                    .and_then(|raw| raw.strip_prefix("Bearer ").or_else(|| raw.strip_prefix("bearer ")))
182                    .unwrap_or("");
183                match auth.authenticate(token).await {
184                    Ok(ctx) => {
185                        req.extensions_mut().insert(ctx);
186                        next.run(req).await
187                    }
188                    Err(_) => {
189                        (axum::http::StatusCode::UNAUTHORIZED,
190                         axum::Json(serde_json::json!({
191                             "success": false,
192                             "error": "unauthorized",
193                             "message": "Authentication required"
194                         }))
195                        ).into_response()
196                    }
197                }
198            }
199        }))
200}
201