feat: finish implementing assignment validation

This commit is contained in:
Max Hohlfeld 2025-07-02 10:14:40 +02:00
parent 512b061c7a
commit bdaf8ff20e
6 changed files with 29 additions and 23 deletions

View File

@ -1,10 +1,9 @@
use anyhow::Context;
use chrono::{Local, NaiveDateTime, Utc};
use chrono::Local;
use sqlx::migrate::Migrate;
use sqlx::{migrate::Migrator, Executor};
use std::fs::File;
use std::io::Write;
use std::time::SystemTime;
use std::{
collections::HashMap,
path::{Path, PathBuf},
@ -37,6 +36,7 @@ enum Command {
}
#[async_std::main]
#[allow(unused)]
async fn main() {
let cli = Cli::parse();
let config = load_config(&cli.environment).expect("Could not load config!");

View File

@ -3,6 +3,7 @@ use sqlx::{PgPool, query};
use super::{AssignmentChangeset, Function, Result};
#[derive(Debug)]
pub struct Assignment {
pub event_id: i32,
pub availability_id: i32,

View File

@ -31,7 +31,7 @@ impl<'a> AsyncValidate<'a> for AssignmentChangeset {
Availability::read_by_id_including_user(context.pool, context.availability_id).await?
else {
return Err(AsyncValidateError::new(
"Angegebener Verfügbarkeit des Nutzers existiert nicht!",
"Angegebener Verfügbarkeit des Nutzers existiert nicht.",
));
};
@ -39,7 +39,7 @@ impl<'a> AsyncValidate<'a> for AssignmentChangeset {
Event::read_by_id_including_location(context.pool, context.event_id).await?
else {
return Err(AsyncValidateError::new(
"Angegebenes Event existiert nicht!",
"Angegebene Veranstaltung existiert nicht.",
));
};
@ -65,7 +65,7 @@ fn availability_user_inside_event_area(
if user.area_id != location.area_id {
return Err(AsyncValidateError::new(
"Nutzer der Verfügbarkeit ist nicht im gleichen Bereich wie der Ort der Veranstaltung!",
"Nutzer der Verfügbarkeit ist nicht im gleichen Bereich wie der Ort der Veranstaltung.",
));
}
@ -78,7 +78,7 @@ fn available_time_fits(
) -> Result<(), AsyncValidateError> {
if value.0 < availability.start || value.1 > availability.end {
return Err(AsyncValidateError::new(
"time not made available can't be assigned",
"Die verfügbar gemachte Zeit passt nicht zu der zugewiesenen Zeit für die Veranstaltung.",
));
}
@ -93,7 +93,7 @@ fn user_of_availability_has_function(
if !user_function.contains(value) {
return Err(AsyncValidateError::new(
"user has not the required function for this assignment",
"Nutzer der Verfügbarkeit besitzt nicht die benötigte Funktion um für diese Position zugewiesen zu werden.",
));
}
@ -106,12 +106,15 @@ async fn event_has_free_slot_for_function(
event: &Event,
pool: &PgPool,
) -> Result<(), AsyncValidateError> {
debug!(?event, "event parameter");
let assignments_for_event: Vec<Assignment> = Assignment::read_all_by_event(pool, event.id)
.await?
.into_iter()
.filter(|a| a.availability_id != availability.id && a.event_id != event.id)
.filter(|a| a.availability_id != availability.id)
.collect();
debug!(?assignments_for_event, "existing assignments for event");
let assignments_with_function = assignments_for_event
.iter()
.filter(|a| a.function == *value)
@ -119,7 +122,7 @@ async fn event_has_free_slot_for_function(
debug!(
assignments_with_function,
"existing assignments for function"
"amount of existing assignments for function"
);
if match *value {
@ -130,7 +133,7 @@ async fn event_has_free_slot_for_function(
Function::Wachhabender => event.voluntary_wachhabender && assignments_with_function >= 1,
} {
return Err(AsyncValidateError::new(
"event already has enough assignments for this function",
"Veranstaltung hat bereits genug Zuweisungen für diese Funktion.",
));
}
@ -146,7 +149,7 @@ async fn availability_not_already_assigned(
let list: Vec<Assignment> = Assignment::read_all_by_availability(pool, availability.id)
.await?
.into_iter()
.filter(|a| a.availability_id != availability.id && a.event_id != event.id)
.filter(|a| a.event_id != event.id)
.collect();
let has_start_time_during_assignment = |a: &Assignment| a.start >= time.0 && a.start <= time.1;
@ -157,7 +160,7 @@ async fn availability_not_already_assigned(
.any(|a| has_start_time_during_assignment(a) || has_end_time_during_assignment(a))
{
return Err(AsyncValidateError::new(
"availability is already assigned for that time",
"Die Verfügbarkeit des Nutzers wurde bereits zu einer anderen Veranstaltung zugewiesen.",
));
}
@ -173,7 +176,9 @@ fn user_is_admin_or_area_manager_of_event_area(
user.role == Role::AreaManager && user.area_id == event.location.as_ref().unwrap().area_id;
if !user_is_admin && !user_is_area_manager_event_area {
return Err(AsyncValidateError::new("TODO: admin or areamanager"));
return Err(AsyncValidateError::new(
"Du verfügst nicht über die Berechtigung, Zuweisungen zu Veranstaltungen vorzunehmen.",
));
}
Ok(())

View File

@ -85,7 +85,6 @@ mod tests {
use chrono::NaiveDateTime;
use fake::{Fake, Faker};
use sqlx::PgPool;
use tracing::info;
use crate::utils::test_helper::{
assert_snapshot, test_post, DbTestContext, NaiveDateTimeExt, RequestConfig,
@ -248,10 +247,7 @@ mod tests {
assert_eq!(StatusCode::UNPROCESSABLE_ENTITY, response.status());
}
#[db_test]
fn fails_when_end_time_lies_before_start_time(context: &DbTestContext) {
assert!(false)
}
// fn fails_when_end_time_lies_before_start_time(context: &DbTestContext) -> not possible as event has the same constraint
#[db_test]
fn fails_when_availability_time_already_assigned(context: &DbTestContext) {
@ -262,6 +258,7 @@ mod tests {
arrange(&context.db_pool).await;
arrange_event(&context.db_pool, start, end, 1).await;
arrange_event(&context.db_pool, start, end, 1).await;
arrange_availability(&context.db_pool, start, end).await;
let assignment_changeset = AssignmentChangeset {
@ -273,7 +270,7 @@ mod tests {
.await
.unwrap();
let config = RequestConfig::new("/assignments/new?availability=1&function=1&event=1")
let config = RequestConfig::new("/assignments/new?availability=1&function=1&event=2")
.with_role(Role::Admin);
let response = test_post::<_, _, String>(&context.db_pool, app, &config, None).await;
@ -303,7 +300,6 @@ mod tests {
#[db_test]
fn fails_when_event_already_has_enough_assignments_for_function(context: &DbTestContext) {
let app = context.app().await;
info!("Hallo welt");
let start = NaiveDateTime::from_ymd_and_hms(2025, 01, 10, 10, 0, 0).unwrap();
let end = NaiveDateTime::from_ymd_and_hms(2025, 01, 10, 20, 0, 0).unwrap();

View File

@ -88,6 +88,7 @@ mod tests {
faker::{internet::en::SafeEmail, name::en::Name},
Fake, Faker,
};
use tracing::debug;
#[db_test]
async fn works_when_user_is_admin(context: &DbTestContext) {
@ -172,11 +173,14 @@ mod tests {
is_posten: None,
is_wachhabender: None,
is_fuehrungsassistent: Some(true),
area: Some(2),
area: Some(1),
};
let response = test_post(&context.db_pool, app, &config, Some(form)).await;
assert_eq!(StatusCode::FOUND, response.status());
let (status, body) = response.into_status_and_body().await;
debug!(body);
assert_eq!(StatusCode::FOUND, status);
let updated_user = User::read_by_id(&context.db_pool, 1)
.await

View File

@ -41,7 +41,7 @@ mod tests {
use crate::utils::test_helper::{
assert_snapshot, read_body, test_get, DbTestContext, RequestConfig, StatusCode,
};
use brass_db::models::{Role, User, Vehicle};
use brass_db::models::{Role, Vehicle};
use brass_macros::db_test;
#[db_test]