use std::sync::Arc;
use axum::{
extract::{Path, State},
http::StatusCode,
response::IntoResponse,
routing::{post, put},
Json, Router,
};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::application::service::check_printing_service::{
AllocationAck, CheckPrintingError, CheckPrintingService, PrintedCheckAck,
RecordCheck, RegisterSequence, SequenceAck,
};
#[derive(Debug, Deserialize)]
pub struct RegisterSequenceBody {
pub bank_account_id: Uuid,
pub numbering_mode: String,
#[serde(default = "default_next")]
pub next_number: i64,
}
fn default_next() -> i64 {
1
}
#[derive(Debug, Deserialize)]
pub struct AllocateBody {
pub bank_account_id: Uuid,
#[serde(default = "default_count")]
pub count: i64,
}
fn default_count() -> i64 {
1
}
#[derive(Debug, Deserialize)]
pub struct RecordCheckBody {
pub bank_account_id: Uuid,
pub payment_id: Uuid,
#[serde(default)]
pub payment_number: Option<String>,
pub amount: Decimal,
#[serde(default)]
pub payee_name: Option<String>,
#[serde(default)]
pub check_number: Option<String>,
#[serde(default)]
pub actor: Option<Uuid>,
}
#[derive(Debug, Deserialize)]
pub struct VoidBody {
#[serde(default)]
pub actor: Option<Uuid>,
}
#[derive(Debug, Serialize)]
struct ErrorBody {
error: String,
message: String,
}
fn error_response(e: &CheckPrintingError) -> axum::response::Response {
let status = StatusCode::from_u16(e.http_status()).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
(
status,
Json(ErrorBody {
error: e.code().to_string(),
message: e.to_string(),
}),
)
.into_response()
}
async fn register_sequence(
State(service): State<Arc<CheckPrintingService>>,
Json(body): Json<RegisterSequenceBody>,
) -> impl IntoResponse {
let req = RegisterSequence {
bank_account_id: body.bank_account_id,
numbering_mode: body.numbering_mode,
next_number: body.next_number,
};
match service.register_sequence(req).await {
Ok(ack) => (StatusCode::OK, Json(SequenceAck {
sequence_id: ack.sequence_id,
bank_account_id: ack.bank_account_id,
numbering_mode: ack.numbering_mode,
next_number: ack.next_number,
}))
.into_response(),
Err(e) => error_response(&e),
}
}
async fn allocate(
State(service): State<Arc<CheckPrintingService>>,
Json(body): Json<AllocateBody>,
) -> impl IntoResponse {
match service
.allocate_check_numbers(body.bank_account_id, body.count)
.await
{
Ok(ack) => (StatusCode::OK, Json(AllocationAck {
first: ack.first,
last: ack.last,
numbers: ack.numbers,
}))
.into_response(),
Err(e) => error_response(&e),
}
}
async fn record_check(
State(service): State<Arc<CheckPrintingService>>,
Json(body): Json<RecordCheckBody>,
) -> impl IntoResponse {
let req = RecordCheck {
bank_account_id: body.bank_account_id,
payment_id: body.payment_id,
payment_number: body.payment_number,
amount: body.amount,
payee_name: body.payee_name,
check_number: body.check_number,
actor: body.actor,
};
match service.record_printed_check(req).await {
Ok(ack) => (StatusCode::CREATED, Json(PrintedCheckAck {
printed_check_id: ack.printed_check_id,
bank_account_id: ack.bank_account_id,
payment_id: ack.payment_id,
check_number: ack.check_number,
status: ack.status,
}))
.into_response(),
Err(e) => error_response(&e),
}
}
async fn void_check(
State(service): State<Arc<CheckPrintingService>>,
Path(printed_check_id): Path<Uuid>,
Json(body): Json<VoidBody>,
) -> impl IntoResponse {
match service
.void_printed_check(printed_check_id, body.actor)
.await
{
Ok(ack) => (StatusCode::OK, Json(PrintedCheckAck {
printed_check_id: ack.printed_check_id,
bank_account_id: ack.bank_account_id,
payment_id: ack.payment_id,
check_number: ack.check_number,
status: ack.status,
}))
.into_response(),
Err(e) => error_response(&e),
}
}
pub fn create_check_printing_routes(service: Arc<CheckPrintingService>) -> Router {
Router::new()
.route("/accounting/checks/sequences", put(register_sequence))
.route("/accounting/checks/allocate", post(allocate))
.route("/accounting/checks", post(record_check))
.route("/accounting/checks/:id/void", post(void_check))
.with_state(service)
}