From 7e58a99d68daf992f1b0d921f30d0bc4b1437b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joona=20K=C3=A4rkk=C3=A4inen?= Date: Thu, 1 Feb 2024 20:38:01 +0200 Subject: [PATCH] feat: allow listing all invoices --- README.md | 2 +- src/api/invoices.rs | 9 +++++++++ src/api/mod.rs | 1 + src/database/invoices.rs | 41 ++++++++++++++++++++++++++++++++++++++++ src/models.rs | 12 ++++++++---- 5 files changed, 60 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ea24d09..91ff09c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ The application is based on [axum](https://github.com/tokio-rs/axum): ## Features/TODO: * [x] create invoice + validation * [ ] create user + authentication -* [ ] list invoices +* [x] list invoices * [ ] edit invoice * [ ] ratelimits * [ ] generate pdf diff --git a/src/api/invoices.rs b/src/api/invoices.rs index b24cccb..b3f3ae2 100644 --- a/src/api/invoices.rs +++ b/src/api/invoices.rs @@ -146,3 +146,12 @@ pub async fn create( axum::Json(conn.create_invoice(multipart.data.clone()).await?), )) } + +pub async fn list_all( + mut conn: DatabaseConnection +) -> Result<(StatusCode, Json>), Error> { + Ok(( + StatusCode::OK, + axum::Json(conn.list_invoices().await?), + )) +} diff --git a/src/api/mod.rs b/src/api/mod.rs index 3eff668..d0d6905 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -11,6 +11,7 @@ pub fn app() -> Router { Router::new() .route("/health", get(health)) .route("/invoices", post(invoices::create)) + .route("/invoices", get(invoices::list_all)) .layer(TraceLayer::new_for_http()) .layer(DefaultBodyLimit::disable()) .layer(RequestBodyLimitLayer::new( diff --git a/src/database/invoices.rs b/src/database/invoices.rs index b1f75f8..0e225d5 100644 --- a/src/database/invoices.rs +++ b/src/database/invoices.rs @@ -103,4 +103,45 @@ impl DatabaseConnection { attachments, }) } + pub async fn list_invoices(&mut self) -> Result, Error> { + let (invoices, parties): (Vec, Vec) = { + use crate::schema::parties; + use crate::schema::invoices; + invoices::table + .inner_join(parties::table) + .select((Invoice::as_select(), Party::as_select())) + .load::<(Invoice, Party)>(&mut self.0) + .await? + .into_iter() + .unzip() + }; + let invoice_rows = InvoiceRow::belonging_to(&invoices) + .select(InvoiceRow::as_select()) + .load(&mut self.0) + .await? + .grouped_by(&invoices); + let attachments = Attachment::belonging_to(&invoices) + .select(Attachment::as_select()) + .load(&mut self.0) + .await? + .grouped_by(&invoices); + Ok(invoice_rows + .into_iter() + .zip(attachments) + .zip(invoices) + .zip(parties) + .map(|(((rows, attachments), invoice), party)| { + PopulatedInvoice { + id: invoice.id, + status: invoice.status, + creation_time: invoice.creation_time, + counter_party: party, + rows, + due_date: invoice.due_date, + attachments, + } + }) + .collect::>() + ) + } } diff --git a/src/models.rs b/src/models.rs index 639eeb6..8ce1ce7 100644 --- a/src/models.rs +++ b/src/models.rs @@ -3,6 +3,7 @@ use chrono::{DateTime, NaiveDate, Utc}; use garde::Validate; use serde_derive::{Deserialize, Serialize}; +use diesel::prelude::*; // NOTES: // This is implemented based on https://github.com/Tietokilta/laskugeneraattori/blob/main/backend/src/procountor.rs#L293 @@ -24,7 +25,7 @@ pub enum InvoiceStatus { } /// A party of the invoice -#[derive(Identifiable, Queryable, Clone, Debug, Serialize)] +#[derive(Identifiable, Queryable, Selectable, Clone, Debug, Serialize)] #[diesel(table_name = parties)] pub struct Party { pub id: i32, @@ -61,7 +62,8 @@ pub struct NewParty { } /// The invoice model as stored in the database -#[derive(Identifiable, Queryable, Clone, Debug)] +#[derive(Identifiable, Queryable, Selectable, Associations, Clone, Debug)] +#[diesel(belongs_to(Party, foreign_key = counter_party_id))] #[diesel(table_name = invoices)] pub struct Invoice { pub id: i32, @@ -81,7 +83,8 @@ pub struct NewInvoice { } /// A single row of an invoice -#[derive(Identifiable, Queryable, Clone, Debug, Serialize)] +#[derive(Identifiable, Queryable, Selectable, Associations, Clone, Debug, Serialize)] +#[diesel(belongs_to(Invoice))] #[diesel(table_name = invoice_rows)] pub struct InvoiceRow { #[serde(skip_serializing)] @@ -110,7 +113,8 @@ pub struct NewInvoiceRow { /// The metadata for an invoice attachment /// The file itself can be requested using its hash and filename /// => /somepath/{hash}/{filename} -#[derive(Identifiable, Queryable, Clone, Debug, Serialize)] +#[derive(Identifiable, Queryable, Selectable, Associations, Clone, Debug, Serialize)] +#[diesel(belongs_to(Invoice))] #[diesel(table_name = invoice_attachments)] pub struct Attachment { #[serde(skip_serializing)]