feat: validation with garde
This commit is contained in:
parent
9060905483
commit
290c7bcc43
29
.sqlx/query-2a7e94e6d4fcaa3afc2755ee236d116547656c3418174463aacadc69d57cbf63.json
generated
Normal file
29
.sqlx/query-2a7e94e6d4fcaa3afc2755ee236d116547656c3418174463aacadc69d57cbf63.json
generated
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE assignment SET function = $1, startTime = $2, endTime = $3 WHERE eventId = $4 AND availabillityId = $5;",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
{
|
||||
"Custom": {
|
||||
"name": "function",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"posten",
|
||||
"fuehrungsassistent",
|
||||
"wachhabender"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Time",
|
||||
"Time",
|
||||
"Int4",
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "2a7e94e6d4fcaa3afc2755ee236d116547656c3418174463aacadc69d57cbf63"
|
||||
}
|
22
.sqlx/query-2d516ada804b4e3306e2a444602b303cf579a1c2d9c445d68f7b17319dc43007.json
generated
Normal file
22
.sqlx/query-2d516ada804b4e3306e2a444602b303cf579a1c2d9c445d68f7b17319dc43007.json
generated
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS(SELECT * FROM location WHERE id= $1);",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "2d516ada804b4e3306e2a444602b303cf579a1c2d9c445d68f7b17319dc43007"
|
||||
}
|
24
.sqlx/query-5e66fa920534af67e80c538be4fa5dbae6ac53427e697be5c71d36a16ea01154.json
generated
Normal file
24
.sqlx/query-5e66fa920534af67e80c538be4fa5dbae6ac53427e697be5c71d36a16ea01154.json
generated
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE event SET date = $1, startTime = $2, endTime = $3, name = $4, locationId = $5, voluntaryWachhabender = $6, voluntaryFuehrungsassistent = $7, amountOfPosten = $8, clothing = $9, note = $10 WHERE id = $11;\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Date",
|
||||
"Time",
|
||||
"Time",
|
||||
"Text",
|
||||
"Int4",
|
||||
"Bool",
|
||||
"Bool",
|
||||
"Int2",
|
||||
"Text",
|
||||
"Text",
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "5e66fa920534af67e80c538be4fa5dbae6ac53427e697be5c71d36a16ea01154"
|
||||
}
|
227
Cargo.lock
generated
227
Cargo.lock
generated
@ -655,6 +655,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.3"
|
||||
@ -764,10 +773,11 @@ dependencies = [
|
||||
"dotenv",
|
||||
"fake",
|
||||
"futures-util",
|
||||
"garde",
|
||||
"insta",
|
||||
"lettre",
|
||||
"pico-args",
|
||||
"quick-xml",
|
||||
"quick-xml 0.37.2",
|
||||
"rand",
|
||||
"regex",
|
||||
"rinja",
|
||||
@ -775,7 +785,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"static-files",
|
||||
"thiserror",
|
||||
"thiserror 2.0.9",
|
||||
"zxcvbn",
|
||||
]
|
||||
|
||||
@ -833,6 +843,25 @@ dependencies = [
|
||||
"bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "card-validate"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "655fa70596e2a38372c0c0c4449ec0166ad9cc43d91558bbecc1a6f38bf9eb91"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.7"
|
||||
@ -941,6 +970,21 @@ version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "compact_str"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32"
|
||||
dependencies = [
|
||||
"castaway",
|
||||
"cfg-if",
|
||||
"itoa",
|
||||
"rustversion",
|
||||
"ryu",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.5.0"
|
||||
@ -1350,7 +1394,7 @@ checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
"regex-syntax 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1537,6 +1581,37 @@ dependencies = [
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "garde"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dbf10452e3dbf51033a5035a05762b2653c43bf84d46e96f15bc93beedd426d"
|
||||
dependencies = [
|
||||
"card-validate",
|
||||
"compact_str",
|
||||
"garde_derive",
|
||||
"idna",
|
||||
"once_cell",
|
||||
"phonenumber",
|
||||
"regex",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"unicode-segmentation",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "garde_derive"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccfdbc9c39fad7991686e229c55cf71565eafe73dcb2cf38ddf1d4aa3ca7e176"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn 2.0.95",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
@ -1972,6 +2047,15 @@ version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
@ -2137,6 +2221,15 @@ dependencies = [
|
||||
"value-bag",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lru-cache"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
version = "0.10.6"
|
||||
@ -2291,6 +2384,12 @@ version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "oncemutex"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d11de466f4a3006fe8a5e7ec84e93b79c70cb992ae0aa0eb631ad2df8abfe2"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.1"
|
||||
@ -2417,6 +2516,27 @@ version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "phonenumber"
|
||||
version = "0.3.6+8.13.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11756237b57b8cc5e97dc8b1e70ea436324d30e7075de63b14fd15073a8f692a"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"either",
|
||||
"fnv",
|
||||
"itertools 0.12.1",
|
||||
"lazy_static",
|
||||
"nom",
|
||||
"quick-xml 0.31.0",
|
||||
"regex",
|
||||
"regex-cache",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"strum",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.5.0"
|
||||
@ -2549,6 +2669,15 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.37.2"
|
||||
@ -2622,7 +2751,7 @@ dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
"regex-syntax 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2633,7 +2762,19 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
"regex-syntax 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-cache"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f7b62d69743b8b94f353b6b7c3deb4c5582828328bcb8d5fedf214373808793"
|
||||
dependencies = [
|
||||
"lru-cache",
|
||||
"oncemutex",
|
||||
"regex",
|
||||
"regex-syntax 0.6.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2642,6 +2783,12 @@ version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
@ -2812,6 +2959,12 @@ dependencies = [
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
@ -3060,7 +3213,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"thiserror 2.0.9",
|
||||
"tracing",
|
||||
"url",
|
||||
"webpki-roots",
|
||||
@ -3143,7 +3296,7 @@ dependencies = [
|
||||
"smallvec",
|
||||
"sqlx-core",
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
"thiserror 2.0.9",
|
||||
"tracing",
|
||||
"whoami",
|
||||
]
|
||||
@ -3181,7 +3334,7 @@ dependencies = [
|
||||
"smallvec",
|
||||
"sqlx-core",
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
"thiserror 2.0.9",
|
||||
"tracing",
|
||||
"whoami",
|
||||
]
|
||||
@ -3240,6 +3393,12 @@ dependencies = [
|
||||
"path-slash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "stringprep"
|
||||
version = "0.1.5"
|
||||
@ -3257,6 +3416,28 @@ version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.26.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.95",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
@ -3310,13 +3491,33 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
"thiserror-impl 2.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3487,6 +3688,12 @@ version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
@ -4008,7 +4215,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"derive_builder",
|
||||
"fancy-regex",
|
||||
"itertools",
|
||||
"itertools 0.13.0",
|
||||
"lazy_static",
|
||||
"regex",
|
||||
"time",
|
||||
|
@ -33,6 +33,7 @@ brass-macros = { path = "../macros" }
|
||||
brass-config = { path = "../config" }
|
||||
actix-http = "3.9.0"
|
||||
rinja = "0.3.5"
|
||||
garde = { version = "0.21.0", features = ["full"] }
|
||||
|
||||
[build-dependencies]
|
||||
built = "0.7.4"
|
||||
|
@ -1,11 +1,15 @@
|
||||
use actix_web::{web, HttpResponse, Responder};
|
||||
use garde::Validate;
|
||||
use rinja::Template;
|
||||
use serde::Deserialize;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::{
|
||||
endpoints::assignment::PlanEventPersonalTablePartialTemplate,
|
||||
models::{Assignment, Availabillity, Event, Function, Role, User},
|
||||
models::{
|
||||
Assignment, AssignmentChangeset, AssignmentContext, Availabillity, Event, Function, Role,
|
||||
User,
|
||||
},
|
||||
utils::{
|
||||
event_planning_template::{
|
||||
generate_availabillity_assignment_list, generate_status_whether_staff_is_required,
|
||||
@ -40,55 +44,43 @@ pub async fn post(
|
||||
return Err(ApplicationError::Unauthorized);
|
||||
}
|
||||
|
||||
let Some(availability) =
|
||||
let Some(availabillity) =
|
||||
Availabillity::read_by_id_including_user(pool.get_ref(), query.availabillity).await?
|
||||
else {
|
||||
return Ok(HttpResponse::NotFound().finish());
|
||||
};
|
||||
|
||||
let availability_user_not_in_event_location_area =
|
||||
availability.user.as_ref().unwrap().area_id != event.location.as_ref().unwrap().area_id;
|
||||
availabillity.user.as_ref().unwrap().area_id != event.location.as_ref().unwrap().area_id;
|
||||
|
||||
let existing_assignments_for_availabillity =
|
||||
Assignment::read_all_by_availabillity(pool.get_ref(), availability.id).await?;
|
||||
let has_start_time_during_event =
|
||||
|a: &Assignment| a.start_time >= event.start_time && a.start_time <= event.end_time;
|
||||
let has_end_time_during_event =
|
||||
|a: &Assignment| a.end_time >= event.start_time && a.end_time <= event.end_time;
|
||||
|
||||
let availability_already_assigned = existing_assignments_for_availabillity
|
||||
.iter()
|
||||
.any(|a| has_start_time_during_event(a) || has_end_time_during_event(a));
|
||||
|
||||
let function = Function::try_from(query.function)?;
|
||||
let user_not_qualified_for_assigned_function =
|
||||
availability.user.as_ref().unwrap().function < function;
|
||||
|
||||
let a = Assignment::count_by_event_and_function(pool.get_ref(), event.id, function).await?;
|
||||
let event_already_has_enough_assignments_for_function = match function {
|
||||
Function::Posten => a >= event.amount_of_posten as i64,
|
||||
Function::Fuehrungsassistent => event.voluntary_fuehrungsassistent && a >= 1,
|
||||
Function::Wachhabender => event.voluntary_wachhabender && a >= 1,
|
||||
};
|
||||
|
||||
if availability_user_not_in_event_location_area
|
||||
|| availability_already_assigned
|
||||
|| user_not_qualified_for_assigned_function
|
||||
|| event_already_has_enough_assignments_for_function
|
||||
{
|
||||
// TODO: Fehlermeldung verbessern
|
||||
return Ok(HttpResponse::BadRequest().body(format!("{availability_user_not_in_event_location_area} {availability_already_assigned} {user_not_qualified_for_assigned_function} {event_already_has_enough_assignments_for_function}")));
|
||||
if availability_user_not_in_event_location_area {
|
||||
return Ok(HttpResponse::BadRequest()
|
||||
.body("availability user is not in the same area as event location"));
|
||||
}
|
||||
|
||||
Assignment::create(
|
||||
pool.get_ref(),
|
||||
event.id,
|
||||
availability.id,
|
||||
let function = Function::try_from(query.function)?;
|
||||
|
||||
let changeset = AssignmentChangeset {
|
||||
function,
|
||||
event.start_time,
|
||||
event.end_time,
|
||||
)
|
||||
.await?;
|
||||
time: (event.start_time, event.end_time),
|
||||
};
|
||||
|
||||
let assignments_for_event = Assignment::read_all_by_event(pool.get_ref(), event.id).await?;
|
||||
let assignments_for_availabillity =
|
||||
Assignment::read_all_by_availabillity(pool.get_ref(), availabillity.id).await?;
|
||||
let context = AssignmentContext {
|
||||
event: event.clone(),
|
||||
availabillity: availabillity.clone(),
|
||||
user_function: availabillity.user.as_ref().unwrap().function,
|
||||
assignments_for_event,
|
||||
assignments_for_availabillity,
|
||||
};
|
||||
|
||||
if let Err(e) = changeset.validate_with(&context) {
|
||||
return Ok(HttpResponse::BadRequest().body(e.to_string()));
|
||||
};
|
||||
|
||||
Assignment::create(pool.get_ref(), event.id, availabillity.id, changeset).await?;
|
||||
|
||||
let availabillities = generate_availabillity_assignment_list(pool.get_ref(), &event).await?;
|
||||
|
||||
|
@ -86,21 +86,24 @@ async fn produces_template(context: &DbTestContext) {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Event::create(
|
||||
&context.db_pool,
|
||||
&NaiveDate::parse_from_str("2025-01-01", "%F").unwrap(),
|
||||
&NaiveTime::parse_from_str("08:00", "%R").unwrap(),
|
||||
&NaiveTime::parse_from_str("10:00", "%R").unwrap(),
|
||||
&"Vorstellung".to_string(),
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
2,
|
||||
&"Tuchuniform".to_string(),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let changeset = crate::models::EventChangeset {
|
||||
date: NaiveDate::parse_from_str("2025-01-01", "%F").unwrap(),
|
||||
time: (
|
||||
NaiveTime::parse_from_str("08:00", "%R").unwrap(),
|
||||
NaiveTime::parse_from_str("10:00", "%R").unwrap(),
|
||||
),
|
||||
name: "Vorstellung".to_string(),
|
||||
location_id: 1,
|
||||
voluntary_fuehrungsassistent: false,
|
||||
voluntary_wachhabender: false,
|
||||
amount_of_posten: 2,
|
||||
clothing: "Tuchuniform".to_string(),
|
||||
note: None,
|
||||
};
|
||||
|
||||
Event::create(&context.db_pool, changeset)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let app = context.app().await;
|
||||
let config = RequestConfig {
|
||||
|
@ -8,6 +8,7 @@ pub mod get_edit;
|
||||
pub mod get_new;
|
||||
pub mod get_plan;
|
||||
pub mod post_new;
|
||||
pub mod post_edit;
|
||||
|
||||
#[derive(Template)]
|
||||
#[cfg_attr(not(test), template(path = "events/new_or_edit.html"))]
|
||||
|
187
web/src/endpoints/events/post_edit.rs
Normal file
187
web/src/endpoints/events/post_edit.rs
Normal file
@ -0,0 +1,187 @@
|
||||
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
||||
use chrono::{NaiveDate, NaiveTime};
|
||||
use garde::Validate;
|
||||
use serde::Deserialize;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::{
|
||||
endpoints::IdPath,
|
||||
models::{
|
||||
Assignment, AssignmentChangeset, Availabillity, Event, EventChangeset, EventContext,
|
||||
Function, Location, Role, User,
|
||||
},
|
||||
utils::{self, ApplicationError},
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EditEventForm {
|
||||
name: String,
|
||||
date: NaiveDate,
|
||||
from: NaiveTime,
|
||||
till: NaiveTime,
|
||||
location: i32,
|
||||
voluntarywachhabender: Option<bool>,
|
||||
voluntaryfuehrungsassistent: Option<bool>,
|
||||
amount: i16,
|
||||
clothing: String,
|
||||
note: Option<String>,
|
||||
}
|
||||
|
||||
#[actix_web::post("/events/{id}/edit")]
|
||||
pub async fn post(
|
||||
user: web::ReqData<User>,
|
||||
pool: web::Data<PgPool>,
|
||||
form: web::Form<EditEventForm>,
|
||||
path: web::Path<IdPath>,
|
||||
) -> Result<impl Responder, ApplicationError> {
|
||||
if user.role != Role::Admin && user.role != Role::AreaManager {
|
||||
return Err(ApplicationError::Unauthorized);
|
||||
}
|
||||
|
||||
let Some(event) = Event::read_by_id_including_location(pool.get_ref(), path.id).await? else {
|
||||
return Ok(HttpResponse::NotFound().finish());
|
||||
};
|
||||
|
||||
if event.location_id != form.location {
|
||||
let Some(location) = Location::read_by_id(pool.get_ref(), form.location).await? else {
|
||||
return Ok(HttpResponse::BadRequest().body("Location does not exist"));
|
||||
};
|
||||
|
||||
if user.role != Role::Admin && user.area_id != location.area_id {
|
||||
return Ok(HttpResponse::BadRequest().body("Can't use location outside of your area"));
|
||||
}
|
||||
|
||||
if event.location.as_ref().unwrap().area_id != location.area_id {
|
||||
return Ok(HttpResponse::BadRequest()
|
||||
.body("Can't change to a location outside of previous location area"));
|
||||
}
|
||||
}
|
||||
|
||||
let changeset = EventChangeset {
|
||||
date: form.date,
|
||||
amount_of_posten: form.amount,
|
||||
clothing: form.clothing.clone(),
|
||||
location_id: form.location,
|
||||
time: (form.from, form.till),
|
||||
name: form.name.clone(),
|
||||
note: form
|
||||
.note
|
||||
.clone()
|
||||
.and_then(|n| if n.len() != 0 { Some(n) } else { None }),
|
||||
voluntary_wachhabender: form.voluntarywachhabender.unwrap_or(false),
|
||||
voluntary_fuehrungsassistent: form.voluntaryfuehrungsassistent.unwrap_or(false),
|
||||
};
|
||||
|
||||
let assignments_for_event = Assignment::read_all_by_event(pool.get_ref(), event.id).await?;
|
||||
|
||||
let mut common_time = (
|
||||
NaiveTime::parse_from_str("00:00", "%R").unwrap(),
|
||||
NaiveTime::parse_from_str("23:59", "%R").unwrap(),
|
||||
);
|
||||
for assignment in &assignments_for_event {
|
||||
let availability = Availabillity::read_by_id(pool.get_ref(), assignment.availabillity_id)
|
||||
.await?
|
||||
.unwrap();
|
||||
let all_assignments =
|
||||
Assignment::read_all_by_availabillity(pool.get_ref(), assignment.availabillity_id)
|
||||
.await?;
|
||||
|
||||
if all_assignments.len() == 1 {
|
||||
if availability.start_time.is_some() && availability.end_time.is_some() {
|
||||
let start = availability.start_time.unwrap();
|
||||
let end = availability.end_time.unwrap();
|
||||
|
||||
if start > common_time.0 {
|
||||
common_time.0 = start;
|
||||
}
|
||||
|
||||
if end < common_time.1 {
|
||||
common_time.1 = end;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let mut slots = if availability.start_time.is_some() && availability.end_time.is_some()
|
||||
{
|
||||
vec![(
|
||||
availability.start_time.as_ref().unwrap().clone(),
|
||||
availability.end_time.as_ref().unwrap().clone(),
|
||||
)]
|
||||
} else {
|
||||
vec![(
|
||||
NaiveTime::parse_from_str("00:00", "%R").unwrap(),
|
||||
NaiveTime::parse_from_str("23:59", "%R").unwrap(),
|
||||
)]
|
||||
};
|
||||
for a in all_assignments
|
||||
.iter()
|
||||
.filter(|x| x.event_id != assignment.event_id)
|
||||
{
|
||||
let (fit, rest) = slots
|
||||
.into_iter()
|
||||
.partition(|s| s.0 >= a.start_time && s.1 <= a.end_time);
|
||||
slots = rest;
|
||||
let fit = fit.first().unwrap();
|
||||
|
||||
if fit.0 != a.start_time {
|
||||
slots.push((fit.0, a.start_time));
|
||||
}
|
||||
|
||||
if fit.1 != a.end_time {
|
||||
slots.push((a.end_time, fit.1));
|
||||
}
|
||||
}
|
||||
|
||||
let slot = slots
|
||||
.into_iter()
|
||||
.find(|s| s.0 >= assignment.start_time && s.1 <= assignment.end_time)
|
||||
.unwrap();
|
||||
|
||||
if slot.0 > common_time.0 {
|
||||
common_time.0 = slot.0;
|
||||
}
|
||||
|
||||
if slot.1 < common_time.1 {
|
||||
common_time.1 = slot.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let context = Some(EventContext {
|
||||
date_in_db: event.date,
|
||||
common_min_max_available_time: common_time,
|
||||
// safe as max amount of posten can be only 100
|
||||
amount_of_assigned_posten: assignments_for_event
|
||||
.iter()
|
||||
.filter(|a| a.function == Function::Posten)
|
||||
.count() as i16,
|
||||
wachhabender_assigned: assignments_for_event
|
||||
.iter()
|
||||
.any(|a| a.function == Function::Wachhabender),
|
||||
fuehrungsassistent_assigned: assignments_for_event
|
||||
.iter()
|
||||
.any(|a| a.function == Function::Fuehrungsassistent),
|
||||
});
|
||||
|
||||
if let Err(e) = changeset.validate_with(&context) {
|
||||
return Ok(HttpResponse::BadRequest().body(e.to_string()));
|
||||
};
|
||||
|
||||
if event.start_time != changeset.time.0 || event.end_time != changeset.time.1 {
|
||||
for a in assignments_for_event {
|
||||
let c = AssignmentChangeset {
|
||||
function: a.function,
|
||||
time: changeset.time.clone(),
|
||||
};
|
||||
Assignment::update(pool.get_ref(), a.event_id, a.availabillity_id, c).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Event::update(pool.get_ref(), event.id, changeset).await?;
|
||||
|
||||
let url = utils::get_return_url_for_date(&form.date);
|
||||
println!("redirecto to {url}");
|
||||
Ok(HttpResponse::Found()
|
||||
.insert_header((LOCATION, url.clone()))
|
||||
.insert_header(("HX-LOCATION", url))
|
||||
.finish())
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
||||
use chrono::{NaiveDate, NaiveTime};
|
||||
use garde::Validate;
|
||||
use serde::Deserialize;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::{
|
||||
models::{Event, Role, User},
|
||||
models::{Event, EventChangeset, Location, Role, User},
|
||||
utils::{self, ApplicationError},
|
||||
};
|
||||
|
||||
@ -32,24 +33,34 @@ pub async fn post(
|
||||
return Err(ApplicationError::Unauthorized);
|
||||
}
|
||||
|
||||
println!("{:?}", form.note);
|
||||
let Some(location) = Location::read_by_id(pool.get_ref(), form.location).await? else {
|
||||
return Ok(HttpResponse::BadRequest().body("Location does not exist"));
|
||||
};
|
||||
|
||||
Event::create(
|
||||
pool.get_ref(),
|
||||
&form.date,
|
||||
&form.from,
|
||||
&form.till,
|
||||
&form.name,
|
||||
form.location,
|
||||
form.voluntarywachhabender.unwrap_or(false),
|
||||
form.voluntaryfuehrungsassistent.unwrap_or(false),
|
||||
form.amount,
|
||||
&form.clothing,
|
||||
form.note
|
||||
.as_ref()
|
||||
if user.role != Role::Admin && user.area_id != location.area_id {
|
||||
return Ok(HttpResponse::BadRequest().body("Can't use location outside of your area"));
|
||||
}
|
||||
|
||||
let changeset = EventChangeset {
|
||||
date: form.date,
|
||||
amount_of_posten: form.amount,
|
||||
clothing: form.clothing.clone(),
|
||||
location_id: form.location,
|
||||
time: (form.from, form.till),
|
||||
name: form.name.clone(),
|
||||
note: form
|
||||
.note
|
||||
.clone()
|
||||
.and_then(|n| if n.len() != 0 { Some(n) } else { None }),
|
||||
)
|
||||
.await?;
|
||||
voluntary_wachhabender: form.voluntarywachhabender.unwrap_or(false),
|
||||
voluntary_fuehrungsassistent: form.voluntaryfuehrungsassistent.unwrap_or(false),
|
||||
};
|
||||
|
||||
if let Err(e) = changeset.validate_with(&None) {
|
||||
return Ok(HttpResponse::BadRequest().body(e.to_string()));
|
||||
};
|
||||
|
||||
Event::create(pool.get_ref(), changeset).await?;
|
||||
|
||||
let url = utils::get_return_url_for_date(&form.date);
|
||||
println!("redirecto to {url}");
|
||||
|
@ -7,9 +7,9 @@ mod assignment;
|
||||
mod availability;
|
||||
mod events;
|
||||
mod export;
|
||||
mod imprint;
|
||||
mod location;
|
||||
pub mod user;
|
||||
mod imprint;
|
||||
mod vehicle;
|
||||
mod vehicle_assignment;
|
||||
|
||||
@ -60,6 +60,7 @@ pub fn init(cfg: &mut ServiceConfig) {
|
||||
cfg.service(events::post_new::post);
|
||||
cfg.service(events::get_plan::get);
|
||||
cfg.service(events::get_edit::get);
|
||||
cfg.service(events::post_edit::post);
|
||||
|
||||
cfg.service(assignment::post_new::post);
|
||||
cfg.service(assignment::delete::delete);
|
||||
|
@ -1,7 +1,7 @@
|
||||
use chrono::NaiveTime;
|
||||
use sqlx::{query, PgPool};
|
||||
|
||||
use super::{Function, Result};
|
||||
use super::{assignment_changeset::AssignmentChangeset, Function, Result};
|
||||
|
||||
pub struct Assignment {
|
||||
pub event_id: i32,
|
||||
@ -16,9 +16,7 @@ impl Assignment {
|
||||
pool: &PgPool,
|
||||
event_id: i32,
|
||||
availabillity_id: i32,
|
||||
function: Function,
|
||||
start_time: NaiveTime,
|
||||
end_time: NaiveTime,
|
||||
changeset: AssignmentChangeset
|
||||
) -> Result<()> {
|
||||
query!(
|
||||
r##"
|
||||
@ -27,9 +25,9 @@ impl Assignment {
|
||||
"##,
|
||||
event_id,
|
||||
availabillity_id,
|
||||
function as Function,
|
||||
start_time,
|
||||
end_time
|
||||
changeset.function as Function,
|
||||
changeset.time.0,
|
||||
changeset.time.1
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
@ -154,6 +152,17 @@ impl Assignment {
|
||||
Ok(assignemnet)
|
||||
}
|
||||
|
||||
pub async fn update(
|
||||
pool: &PgPool,
|
||||
event_id: i32,
|
||||
availabillity_id: i32,
|
||||
changeset: AssignmentChangeset,
|
||||
) -> Result<()> {
|
||||
query!("UPDATE assignment SET function = $1, startTime = $2, endTime = $3 WHERE eventId = $4 AND availabillityId = $5;", changeset.function as Function, changeset.time.0, changeset.time.1, event_id, availabillity_id).execute(pool).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete(pool: &PgPool, event_id: i32, availabillity_id: i32) -> Result<()> {
|
||||
query!("DELETE FROM assignment WHERE assignment.eventId = $1 AND assignment.availabillityId = $2;",
|
||||
event_id,
|
||||
|
119
web/src/models/assignment_changeset.rs
Normal file
119
web/src/models/assignment_changeset.rs
Normal file
@ -0,0 +1,119 @@
|
||||
use chrono::NaiveTime;
|
||||
use garde::Validate;
|
||||
|
||||
use super::{start_time_lies_before_end_time, Assignment, Availabillity, Event, Function};
|
||||
|
||||
#[derive(Validate)]
|
||||
#[garde(allow_unvalidated)]
|
||||
#[garde(context(AssignmentContext as ctx))]
|
||||
/// check before: event and availabillity and must exist and user of availabillity is in event location
|
||||
pub struct AssignmentChangeset {
|
||||
#[garde(
|
||||
custom(user_of_availability_has_function),
|
||||
custom(event_has_free_slot_for_function)
|
||||
)]
|
||||
pub function: Function,
|
||||
#[garde(
|
||||
custom(available_time_fits),
|
||||
custom(start_time_lies_before_end_time),
|
||||
custom(availabillity_not_already_assigned)
|
||||
)]
|
||||
pub time: (NaiveTime, NaiveTime),
|
||||
}
|
||||
|
||||
pub struct AssignmentContext {
|
||||
pub event: Event,
|
||||
pub availabillity: Availabillity,
|
||||
pub user_function: Function,
|
||||
pub assignments_for_event: Vec<Assignment>,
|
||||
pub assignments_for_availabillity: Vec<Assignment>,
|
||||
}
|
||||
|
||||
fn available_time_fits(
|
||||
value: &(NaiveTime, NaiveTime),
|
||||
context: &AssignmentContext,
|
||||
) -> garde::Result {
|
||||
if context.availabillity.start_time.is_some() && context.availabillity.end_time.is_some() {
|
||||
let start = context.availabillity.start_time.as_ref().unwrap();
|
||||
let end = context.availabillity.end_time.as_ref().unwrap();
|
||||
|
||||
if value.0 < *start || value.1 > *end {
|
||||
return Err(garde::Error::new(
|
||||
"time not made available can't be assigned",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn user_of_availability_has_function(
|
||||
value: &Function,
|
||||
context: &AssignmentContext,
|
||||
) -> garde::Result {
|
||||
if *value > context.user_function {
|
||||
return Err(garde::Error::new(
|
||||
"user has not the required function for this assignment",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn event_has_free_slot_for_function(
|
||||
_value: &Function,
|
||||
context: &AssignmentContext,
|
||||
) -> garde::Result {
|
||||
let list: Vec<&Assignment> = context
|
||||
.assignments_for_event
|
||||
.iter()
|
||||
.filter(|a| {
|
||||
a.availabillity_id != context.availabillity.id && a.event_id != context.event.id
|
||||
})
|
||||
.collect();
|
||||
|
||||
let a = list
|
||||
.iter()
|
||||
.filter(|a| a.function == Function::Posten)
|
||||
.count();
|
||||
if match context.user_function {
|
||||
Function::Posten => a >= context.event.amount_of_posten as usize,
|
||||
Function::Fuehrungsassistent => context.event.voluntary_fuehrungsassistent && a >= 1,
|
||||
Function::Wachhabender => context.event.voluntary_wachhabender && a >= 1,
|
||||
} {
|
||||
return Err(garde::Error::new(
|
||||
"event already has enough assignments for this function",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn availabillity_not_already_assigned(
|
||||
value: &(NaiveTime, NaiveTime),
|
||||
context: &AssignmentContext,
|
||||
) -> garde::Result {
|
||||
let list: Vec<&Assignment> = context
|
||||
.assignments_for_availabillity
|
||||
.iter()
|
||||
.filter(|a| {
|
||||
a.availabillity_id != context.availabillity.id && a.event_id != context.event.id
|
||||
})
|
||||
.collect();
|
||||
|
||||
let has_start_time_during_assignment =
|
||||
|a: &Assignment| a.start_time >= value.0 && a.start_time <= value.1;
|
||||
let has_end_time_during_assignment =
|
||||
|a: &Assignment| a.end_time >= value.0 && a.end_time <= value.1;
|
||||
|
||||
if list
|
||||
.iter()
|
||||
.any(|a| has_start_time_during_assignment(a) || has_end_time_during_assignment(a))
|
||||
{
|
||||
return Err(garde::Error::new(
|
||||
"availabillity is already assigned for that time",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
use chrono::{NaiveDate, NaiveTime};
|
||||
use sqlx::{query, PgPool};
|
||||
|
||||
use super::{Location, Result};
|
||||
use super::{event_changeset::EventChangeset, Location, Result};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Event {
|
||||
pub id: i32,
|
||||
pub date: NaiveDate,
|
||||
@ -21,24 +21,12 @@ pub struct Event {
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub async fn create(
|
||||
pool: &PgPool,
|
||||
date: &NaiveDate,
|
||||
start_time: &NaiveTime,
|
||||
end_time: &NaiveTime,
|
||||
name: &String,
|
||||
location_id: i32,
|
||||
voluntary_wachhabender: bool,
|
||||
voluntary_fuehrungsassistent: bool,
|
||||
amount_of_posten: i16,
|
||||
clothing: &String,
|
||||
note: Option<&String>,
|
||||
) -> Result<()> {
|
||||
pub async fn create(pool: &PgPool, changeset: EventChangeset) -> Result<()> {
|
||||
query!(r#"
|
||||
INSERT INTO event (date, startTime, endTime, name, locationId, voluntaryWachhabender, voluntaryFuehrungsassistent, amountOfPosten, clothing, note)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
|
||||
"#,
|
||||
date, start_time, end_time, name, location_id, voluntary_wachhabender, voluntary_fuehrungsassistent, amount_of_posten, clothing, note).execute(pool).await?;
|
||||
changeset.date, changeset.time.0, changeset.time.1,changeset.name, changeset.location_id, changeset.voluntary_wachhabender, changeset.voluntary_fuehrungsassistent, changeset.amount_of_posten, changeset.clothing, changeset.note).execute(pool).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -157,4 +145,17 @@ impl Event {
|
||||
|
||||
Ok(event)
|
||||
}
|
||||
|
||||
pub async fn update(pool: &PgPool, id: i32, changeset: EventChangeset) -> Result<()> {
|
||||
query!(r#"
|
||||
UPDATE event SET date = $1, startTime = $2, endTime = $3, name = $4, locationId = $5, voluntaryWachhabender = $6, voluntaryFuehrungsassistent = $7, amountOfPosten = $8, clothing = $9, note = $10 WHERE id = $11;
|
||||
"#,
|
||||
changeset.date,
|
||||
changeset.time.0,
|
||||
changeset.time.1,changeset.name, changeset.location_id, changeset.voluntary_wachhabender, changeset.voluntary_fuehrungsassistent, changeset.amount_of_posten, changeset.clothing, changeset.note, id)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
97
web/src/models/event_changeset.rs
Normal file
97
web/src/models/event_changeset.rs
Normal file
@ -0,0 +1,97 @@
|
||||
use chrono::NaiveDate;
|
||||
use chrono::NaiveTime;
|
||||
use garde::Validate;
|
||||
|
||||
use super::start_time_lies_before_end_time;
|
||||
|
||||
#[derive(Validate)]
|
||||
#[garde(allow_unvalidated)]
|
||||
#[garde(context(Option<EventContext> as ctx))]
|
||||
pub struct EventChangeset {
|
||||
#[garde(custom(date_unchanged_if_edit))]
|
||||
pub date: NaiveDate,
|
||||
#[garde(
|
||||
custom(start_time_lies_before_end_time),
|
||||
custom(time_can_be_extended_if_edit)
|
||||
)]
|
||||
pub time: (NaiveTime, NaiveTime),
|
||||
pub name: String,
|
||||
/// check before: must exist and user can create event for this location
|
||||
pub location_id: i32,
|
||||
#[garde(custom(can_unset_wachhabender))]
|
||||
pub voluntary_wachhabender: bool,
|
||||
#[garde(custom(can_unset_fuehrungsassistent))]
|
||||
pub voluntary_fuehrungsassistent: bool,
|
||||
#[garde(range(min = ctx.as_ref().and_then(|c: &EventContext| Some(c.amount_of_assigned_posten)).unwrap_or(1), max = 100))]
|
||||
pub amount_of_posten: i16,
|
||||
pub clothing: String,
|
||||
pub note: Option<String>,
|
||||
}
|
||||
|
||||
pub struct EventContext {
|
||||
pub date_in_db: NaiveDate,
|
||||
pub common_min_max_available_time: (NaiveTime, NaiveTime),
|
||||
pub wachhabender_assigned: bool,
|
||||
pub fuehrungsassistent_assigned: bool,
|
||||
pub amount_of_assigned_posten: i16,
|
||||
}
|
||||
|
||||
fn date_unchanged_if_edit(value: &NaiveDate, context: &Option<EventContext>) -> garde::Result {
|
||||
if context.as_ref().is_some_and(|c| c.date_in_db != *value) {
|
||||
return Err(garde::Error::new("event date can't be changed"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn time_can_be_extended_if_edit(
|
||||
value: &(NaiveTime, NaiveTime),
|
||||
context: &Option<EventContext>,
|
||||
) -> garde::Result {
|
||||
if let Some(context) = context {
|
||||
let old_start_time = context.common_min_max_available_time.0;
|
||||
let new_start_time = value.0;
|
||||
let old_end_time = context.common_min_max_available_time.1;
|
||||
let new_end_time = value.1;
|
||||
|
||||
if new_start_time < old_start_time {
|
||||
return Err(garde::Error::new(
|
||||
"starttime lies outside of available time for assigned people",
|
||||
));
|
||||
}
|
||||
|
||||
if new_end_time > old_end_time {
|
||||
return Err(garde::Error::new(
|
||||
"endtime lies ouside of available time for assigned people",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn can_unset_fuehrungsassistent(value: &bool, context: &Option<EventContext>) -> garde::Result {
|
||||
if context
|
||||
.as_ref()
|
||||
.is_some_and(|c| !*value && c.fuehrungsassistent_assigned)
|
||||
{
|
||||
return Err(garde::Error::new(
|
||||
"fuehrungsassistent can't be set to not by ff, because a person is already assigned",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn can_unset_wachhabender(value: &bool, context: &Option<EventContext>) -> garde::Result {
|
||||
if context
|
||||
.as_ref()
|
||||
.is_some_and(|c| !*value && c.wachhabender_assigned)
|
||||
{
|
||||
return Err(garde::Error::new(
|
||||
"wachhabender can't be set to not by ff, because a person is already assigned",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -4,7 +4,7 @@ use super::Area;
|
||||
|
||||
use super::Result;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Location {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
@ -138,4 +138,12 @@ impl Location {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn exists(pool: &PgPool, id: i32) -> Result<bool> {
|
||||
let b = query!("SELECT EXISTS(SELECT * FROM location WHERE id= $1);", id)
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
Ok(b.exists.is_some_and(|e| e))
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,43 @@
|
||||
mod area;
|
||||
mod assignement;
|
||||
mod assignment_changeset;
|
||||
mod availabillity;
|
||||
mod event;
|
||||
mod event_changeset;
|
||||
mod function;
|
||||
mod location;
|
||||
mod password_reset;
|
||||
mod registration;
|
||||
mod role;
|
||||
mod user;
|
||||
mod vehicle;
|
||||
mod password_reset;
|
||||
mod registration;
|
||||
mod vehicle_assignement;
|
||||
|
||||
pub use area::Area;
|
||||
pub use assignement::Assignment;
|
||||
pub use assignment_changeset::{AssignmentChangeset, AssignmentContext};
|
||||
pub use availabillity::{Availabillity, AvailabillityAssignmentState};
|
||||
use chrono::NaiveTime;
|
||||
pub use event::Event;
|
||||
pub use event_changeset::{EventChangeset, EventContext};
|
||||
pub use function::Function;
|
||||
pub use location::Location;
|
||||
pub use password_reset::{NoneToken, PasswordReset, Token};
|
||||
pub use registration::Registration;
|
||||
pub use role::Role;
|
||||
pub use user::User;
|
||||
pub use assignement::Assignment;
|
||||
pub use password_reset::{PasswordReset, Token, NoneToken};
|
||||
pub use registration::Registration;
|
||||
pub use vehicle::Vehicle;
|
||||
pub use vehicle_assignement::VehicleAssignement;
|
||||
|
||||
type Result<T> = std::result::Result<T, sqlx::Error>;
|
||||
|
||||
fn start_time_lies_before_end_time<T>(
|
||||
value: &(NaiveTime, NaiveTime),
|
||||
_context: &T,
|
||||
) -> garde::Result {
|
||||
if value.0 >= value.1 {
|
||||
return Err(garde::Error::new("endtime can't lie before starttime"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user