Added new views, joined rules and categories

This commit is contained in:
Manuel Forcén Muñoz 2024-04-24 23:20:37 +02:00
parent bbc61cfe45
commit 759f91a9a2
12 changed files with 469 additions and 83 deletions

View file

@ -1,4 +1,4 @@
use std::sync::Arc;
use std::{collections::HashMap, sync::Arc};
use axum::{
extract::{Path, Query, State},
@ -12,10 +12,7 @@ use sqlx::SqlitePool;
use tera::{Context, Tera};
use crate::users::UserToken;
use accounters::models::{
account::Account,
transaction::{Transaction, TxConflictResolutionMode},
};
use accounters::models::{account::Account, categories::Category, transaction::Transaction};
#[derive(Deserialize)]
pub struct AccountViewParams {
@ -51,6 +48,14 @@ pub async fn list(
);
}
let categories: HashMap<i32, String> = Category::list(db.as_ref())
.await
.unwrap()
.iter()
.map(|x| (x.category_id, x.name.clone()))
.collect();
ctx.insert("categories", &categories);
let n_entries = entries.unwrap_or(10).max(10);
let page = page.unwrap_or(0).max(0);
@ -146,7 +151,6 @@ pub async fn add_transactions_action(
&tx.date,
None,
(tx.amount * 100.0).round() as i32,
TxConflictResolutionMode::Nothing,
)
.await
{

View file

@ -0,0 +1,99 @@
use std::sync::Arc;
use accounters::models::{categories::Category, transaction::Transaction};
use axum::{
extract::{Path, State},
response::IntoResponse,
Form,
};
use chrono::{DateTime, Utc};
use hyper::{header, StatusCode};
use serde::{Deserialize, Deserializer};
use sqlx::SqlitePool;
use tera::Tera;
use crate::users::UserToken;
pub async fn view(
db: State<Arc<SqlitePool>>,
tmpl: State<Arc<Tera>>,
user: UserToken,
Path(id): Path<i32>,
) -> impl IntoResponse {
let tx = Transaction::get_by_id(db.as_ref(), id).await.unwrap();
let mut ctx = tera::Context::new();
ctx.insert("tx_id", &id);
ctx.insert("tx", &tx);
let categories = Category::list(db.as_ref()).await.unwrap();
ctx.insert("categories", &categories);
(
StatusCode::OK,
[(header::CONTENT_TYPE, "text/html;charset=utf-8")],
tmpl.render("transaction.html", &ctx).unwrap(),
)
}
fn deserialize_optional<'de, D>(data: D) -> Result<Option<i32>, D::Error>
where
D: Deserializer<'de>,
{
let str = String::deserialize(data)?;
if str.is_empty() {
Ok(None)
} else {
Ok(Some(str.parse().unwrap()))
}
}
#[derive(Deserialize, Debug)]
pub struct TxUpdateRequest {
description: String,
date: DateTime<Utc>,
amount: f32,
#[serde(deserialize_with = "deserialize_optional")]
category: Option<i32>,
}
pub async fn update(
db: State<Arc<SqlitePool>>,
tmpl: State<Arc<Tera>>,
user: UserToken,
Path(id): Path<i32>,
Form(req): Form<TxUpdateRequest>,
) -> impl IntoResponse {
let ret_str = format!("/transaction/{id}");
let mut tx = match Transaction::get_by_id(db.as_ref(), id).await {
Ok(tx) => tx,
Err(e) => {
return (
StatusCode::NOT_FOUND,
[(header::LOCATION, ret_str)],
format!("{e:?}"),
);
}
};
let amount = (req.amount * 100.0).round() as i32;
if tx.get_amount() != amount {
tx.set_amount(db.as_ref(), amount).await.unwrap();
}
if tx.get_description() != req.description {
tx.set_description(db.as_ref(), &req.description)
.await
.unwrap();
}
if tx.get_category() != req.category {
tx.set_category(db.as_ref(), req.category).await.unwrap();
}
(
StatusCode::MOVED_PERMANENTLY,
[(header::LOCATION, ret_str)],
String::new(),
)
}