use axum::extract::{Json, Path, Query, State}; use chrono::{NaiveDate, NaiveDateTime}; use sea_orm::{ prelude::*, DbBackend, FromQueryResult, ItemsAndPagesNumber, JoinType, Order, QueryOrder, QuerySelect, Statement, }; use serde::{Deserialize, Serialize}; use crate::db::paginate::{paginate_also_related, PaginatedResponse}; use crate::error::AppError; use crate::{model, AppState}; use super::{CompanySlug, Pagination}; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct TransactionCompany { pub id: i32, pub foreign_id: String, pub date_published: NaiveDate, pub date_executed: NaiveDate, pub person: String, pub exchange: String, pub nature: String, pub isin: Option, pub instrument: String, pub volume: i32, pub unit_price: f32, pub created_at_utc: NaiveDateTime, pub company: Option, } pub async fn get_all( state: State, Query(CompanySlug { company_slug }): Query, Query(Pagination { page, size }): Query, ) -> Result>, AppError> { let db = &state.db; let mut filters = vec![]; if let Some(c) = company_slug { filters.push(model::company::Column::Slug.eq(c)) } let res = paginate_also_related::< model::transaction::Entity, model::company::Entity, model::transaction::Model, model::company::Model, model::transaction::Column, >( db, page, size, Some(model::transaction::Column::DatePublished), Some(Order::Desc), Some(filters), ) .await?; let list = res .list .iter() .map(|t| TransactionCompany { id: t.0.id, foreign_id: t.0.foreign_id.to_owned(), date_published: t.0.date_published, date_executed: t.0.date_executed, person: t.0.person.to_owned(), exchange: t.0.exchange.to_owned(), nature: t.0.nature.to_owned(), isin: t.0.isin.clone(), instrument: t.0.instrument.to_owned(), volume: t.0.volume, unit_price: t.0.unit_price, created_at_utc: t.0.created_at_utc, company: t.1.to_owned(), }) .collect(); let res = PaginatedResponse { count: res.count, num_pages: res.num_pages, list, }; Ok(Json(res)) } #[derive(FromQueryResult, Serialize)] pub struct LatestTransaction { company_name: String, slug: String, nature: String, total: f32, } pub async fn get_latest_transactions( state: State, Query(pagination): Query, ) -> Result>, AppError> { let db = &state.db; let s = pagination.size.unwrap_or(20).min(50); let query_raw = "SELECT company.name as company_name, company.slug, transaction.nature, SUM(transaction.volume * transaction.unit_price) as total FROM transaction JOIN company ON transaction.company_id = company.id WHERE DATE(created_at_utc) IN (SELECT DATE(MAX(created_at_utc)) FROM transaction) GROUP BY company.name, transaction.nature ORDER BY transaction.nature, total DESC" .to_string(); let query = model::transaction::Entity::find() .from_raw_sql(Statement::from_string( DbBackend::MySql, query_raw.to_string(), )) .into_model::(); let pages = query.paginate(db, s); let ItemsAndPagesNumber { number_of_items: count, number_of_pages: num_pages, } = pages.num_items_and_pages().await?; let p = pagination.page.unwrap_or(0).min(num_pages); let list = pages.fetch_page(p).await?; let res = PaginatedResponse { count, num_pages, list, }; Ok(Json(res)) } pub async fn get_aggregated_transactions( state: State, pagination: Query, ) -> Result>, AppError> { let db = &state.db; let s = pagination.0.size.unwrap_or(20).min(50); let query = model::company::Entity::find() .select_only() .join( JoinType::InnerJoin, model::company::Relation::Transaction.def(), ) .column(model::company::Column::Id) .column(model::company::Column::Name) .column(model::company::Column::Slug) .column_as(model::transaction::Column::Id.count(), "transaction_count"); let pages = query .group_by(model::company::Column::Name) .order_by(model::transaction::Column::Id.count(), Order::Desc) .into_model::() .paginate(db, s); let ItemsAndPagesNumber { number_of_items: count, number_of_pages: num_pages, } = pages.num_items_and_pages().await?; let p = pagination.0.page.unwrap_or(0).min(num_pages); let list = pages.fetch_page(p).await?; let res = PaginatedResponse { count, num_pages, list, }; Ok(Json(res)) } #[derive(Serialize, FromQueryResult, Debug)] pub struct TransactionsAggregated { id: i32, name: String, slug: String, transaction_count: i32, }