diff --git a/src/lib.rs b/src/lib.rs index 3a5636b..39e905d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -174,6 +174,7 @@ pub async fn run_server(db: Arc, port: u16, serve_docs: bool .routes(routes!(routes::bal::start_bal)) .routes(routes!(routes::bal::stop_bal)) .routes(routes!(routes::bal::get_bals)) + .routes(routes!(routes::bal::get_bal_accounting)) // Authentication .route_layer(middleware::from_fn_with_state(shared_state.clone(), routes::auth::auth_middleware)) .routes(routes!(routes::auth::auth)) diff --git a/src/routes/bal.rs b/src/routes/bal.rs index ba7aa1f..0fdce03 100644 --- a/src/routes/bal.rs +++ b/src/routes/bal.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; use axum::{extract::{Path, State}, Json}; use reqwest::{StatusCode}; @@ -6,7 +6,7 @@ use sea_orm::{ActiveModelTrait, ActiveValue::{NotSet, Set}, ColumnTrait, EntityT use serde::{Deserialize, Serialize}; use utoipa::IntoParams; -use crate::{entities::{bal::{self, BalState}, prelude::*}, routes::auth::Claims, AppState}; +use crate::{entities::{bal::{self, BalState}, book_instance, owner, prelude::*}, routes::auth::Claims, AppState}; #[derive(IntoParams)] @@ -280,3 +280,72 @@ pub async fn get_bals( } } +#[derive(Serialize, utoipa::ToSchema)] +pub struct OwnerAccountingData { + pub owed_money: f32, + pub owed_books: Vec +} + +#[derive(Serialize, utoipa::ToSchema)] +pub struct BalAccounting { + pub owner_map: HashMap, + pub total_collected_money: f32 +} + +#[axum::debug_handler] +#[utoipa::path( + get, + path = "/bal/{id}/accounting", + params(BalByIdParams), + security(("jwt" = [])), + responses( + (status = OK, body = BalAccounting, description = "Accounting data of the specified BAL"), + (status = NOT_FOUND, description = "No bal with this id exists in the database"), + (status = FORBIDDEN, description = "You don't own the specified bal"), + ), + summary = "Get accounting data for specified BAL", + description = "Get accounting data for specified BAL", + tag = "bal-api", +)] +pub async fn get_bal_accounting( + State(state): State>, + claims: Claims, + Path(id): Path +) -> (StatusCode, Json>) { + if let Ok(Some(bal)) = Bal::find_by_id(id).one(state.db_conn.as_ref()).await { + if !bal.user_id == claims.user_id { + (StatusCode::FORBIDDEN, Json(None)) + } else { + let mut accounting_data = BalAccounting { + owner_map: HashMap::new(), + total_collected_money: 0. + }; + + let bal_owners = Owner::find() + .filter(owner::Column::UserId.eq(claims.user_id)).all(state.db_conn.as_ref()).await.unwrap(); + for owner in bal_owners { + let owner_books = BookInstance::find() + .filter(book_instance::Column::OwnerId.eq(owner.id)) + .filter(book_instance::Column::BalId.eq(bal.id)) + .all(state.db_conn.as_ref()).await.unwrap(); + if owner_books.is_empty() { + continue; + } + let mut owner_accounting_data = OwnerAccountingData { owed_money: 0., owed_books: vec![] }; + for book_instance in owner_books { + match book_instance.sold_price { + Some(val) => owner_accounting_data.owed_money += val, + None => owner_accounting_data.owed_books.push(book_instance), + } + } + accounting_data.total_collected_money += owner_accounting_data.owed_money; + accounting_data.owner_map.insert(owner.id, owner_accounting_data); + } + + (StatusCode::OK, Json(Some(accounting_data))) + } + } else { + (StatusCode::NOT_FOUND, Json(None)) + } +} +