feat: show crossareal in event planning
This commit is contained in:
parent
18875a9e0c
commit
760897b301
@ -14,12 +14,15 @@ SELECT
|
|||||||
user_.areaId,
|
user_.areaId,
|
||||||
user_.locked,
|
user_.locked,
|
||||||
user_.lastLogin,
|
user_.lastLogin,
|
||||||
user_.receiveNotifications
|
user_.receiveNotifications,
|
||||||
|
area.name AS areaName
|
||||||
FROM availability
|
FROM availability
|
||||||
JOIN
|
JOIN
|
||||||
user_ ON availability.userId = user_.id
|
user_ ON availability.userId = user_.id
|
||||||
|
JOIN
|
||||||
|
area ON user_.areaId = area.id
|
||||||
WHERE
|
WHERE
|
||||||
availability.starttimestamp::date = $1
|
availability.starttimestamp::date = $1
|
||||||
AND user_.areaId = $2
|
AND (user_.areaId = $2 OR availability.crossAreal = true)
|
||||||
AND availability.startTimestamp <= $3
|
AND availability.startTimestamp <= $3
|
||||||
AND availability.endTimestamp >= $4;
|
AND availability.endTimestamp >= $4;
|
||||||
|
@ -18,7 +18,7 @@ impl Availability {
|
|||||||
pub async fn create(
|
pub async fn create(
|
||||||
pool: &PgPool,
|
pool: &PgPool,
|
||||||
user_id: i32,
|
user_id: i32,
|
||||||
changeset: AvailabilityChangeset,
|
changeset: &AvailabilityChangeset,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
query_file!(
|
query_file!(
|
||||||
"sql/availability/create.sql",
|
"sql/availability/create.sql",
|
||||||
@ -75,7 +75,7 @@ impl Availability {
|
|||||||
Ok(availabilities)
|
Ok(availabilities)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// loads availabilities for the area and the same day as the start date and which fully lie inside the daterange
|
/// loads availabilities for the area or ones that are cross-area and the same day as the start date and which fully lie inside the daterange
|
||||||
pub async fn read_all_by_daterange_and_area_including_user_for_event_planning(
|
pub async fn read_all_by_daterange_and_area_including_user_for_event_planning(
|
||||||
pool: &PgPool,
|
pool: &PgPool,
|
||||||
date_range: (NaiveDateTime, NaiveDateTime),
|
date_range: (NaiveDateTime, NaiveDateTime),
|
||||||
@ -105,7 +105,10 @@ impl Availability {
|
|||||||
role: r.role,
|
role: r.role,
|
||||||
function: r.function.clone(),
|
function: r.function.clone(),
|
||||||
area_id: r.areaid,
|
area_id: r.areaid,
|
||||||
area: None,
|
area: Some(Area {
|
||||||
|
id: r.areaid,
|
||||||
|
name: r.areaname.clone(),
|
||||||
|
}),
|
||||||
locked: r.locked,
|
locked: r.locked,
|
||||||
last_login: r.lastlogin,
|
last_login: r.lastlogin,
|
||||||
receive_notifications: r.receivenotifications,
|
receive_notifications: r.receivenotifications,
|
||||||
|
@ -15,8 +15,6 @@ snapshot_kind: text
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Max Mustermann</td>
|
<td>Max Mustermann</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -0,0 +1,210 @@
|
|||||||
|
---
|
||||||
|
source: web/src/endpoints/events/get_plan.rs
|
||||||
|
expression: body
|
||||||
|
snapshot_kind: text
|
||||||
|
---
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="title">Eventplanung</h1>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<h5 class="title is-5">Allgemeines</h5>
|
||||||
|
|
||||||
|
<div class="fixed-grid has-1-cols-mobile">
|
||||||
|
<div class="grid content">
|
||||||
|
<div class="cell is-col-span-2">
|
||||||
|
<p><b>Name:</b> Große Veranstaltung</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cell">
|
||||||
|
<p><b>Datum:</b> Mittwoch, 01.01.2025</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cell">
|
||||||
|
<p><b>Uhrzeit:</b> 12:00 Uhr - 01.01.2025 22:00 Uhr</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cell is-col-span-2">
|
||||||
|
<p><b>Veranstaltungsort:</b> Location</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cell">
|
||||||
|
<p><b>Wachhabender:</b> FF</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cell">
|
||||||
|
<p><b>Führungsassistent benötigt:</b> ja</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cell is-col-span-2">
|
||||||
|
<p><b>Anzahl der Posten:</b> 5</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cell is-col-span-2">
|
||||||
|
<p><b>Anzugsordnung:</b> Tuchuniform</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cell is-col-span-2">
|
||||||
|
<p><b>Anmerkungen:</b> </p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<h5 class="title is-5">Einteilung Personal</h5>
|
||||||
|
|
||||||
|
|
||||||
|
<table class="table is-fullwidth">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Funktion</th>
|
||||||
|
<th>Zeitraum</th>
|
||||||
|
<th>Kommentar</th>
|
||||||
|
<th>Planung</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Max Mustermann</td>
|
||||||
|
<td>
|
||||||
|
<div class="tags"><span class="tag is-primary is-light">Posten</span></div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
12:00 bis 01.01.2025 22:00
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
Kommentar
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="dropdown">
|
||||||
|
<div class="dropdown-trigger"
|
||||||
|
_="on click[.dropdown does not contain target] from elsewhere
|
||||||
|
or keyup[key is 'Escape'] from elsewhere
|
||||||
|
or click[parentElement of me does not match .is-active]
|
||||||
|
remove .is-active from .dropdown end
|
||||||
|
on click toggle .is-active on the parentElement of me">
|
||||||
|
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu">
|
||||||
|
|
||||||
|
<span>als Posten geplant</span>
|
||||||
|
<svg class="icon">
|
||||||
|
<use href="/static/feather-sprite.svg#edit-2" />
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown-menu" id="dropdown-menu" role="menu">
|
||||||
|
<div class="dropdown-content" hx-target="closest table" hx-swap="outerHTML">
|
||||||
|
|
||||||
|
<a class="dropdown-item"
|
||||||
|
hx-post="/assignments/new?event=1&availability=1&function=1" disabled>
|
||||||
|
als Posten planen</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<hr class="dropdown-divider" />
|
||||||
|
<a class="dropdown-item"
|
||||||
|
hx-delete="/assignments/delete?event=1&availability=1"
|
||||||
|
class="button is-small">entplanen</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Rudi Tester (Fremdbereich Süd)</td>
|
||||||
|
<td>
|
||||||
|
<div class="tags"><span class="tag is-primary is-light">Posten</span></div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
12:00 bis 01.01.2025 22:00
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
Kommentar
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="dropdown">
|
||||||
|
<div class="dropdown-trigger"
|
||||||
|
_="on click[.dropdown does not contain target] from elsewhere
|
||||||
|
or keyup[key is 'Escape'] from elsewhere
|
||||||
|
or click[parentElement of me does not match .is-active]
|
||||||
|
remove .is-active from .dropdown end
|
||||||
|
on click toggle .is-active on the parentElement of me">
|
||||||
|
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu">
|
||||||
|
|
||||||
|
<span>Planen</span>
|
||||||
|
<svg class="icon">
|
||||||
|
<use href="/static/feather-sprite.svg#chevron-down" />
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown-menu" id="dropdown-menu" role="menu">
|
||||||
|
<div class="dropdown-content" hx-target="closest table" hx-swap="outerHTML">
|
||||||
|
|
||||||
|
<a class="dropdown-item"
|
||||||
|
hx-post="/assignments/new?event=1&availability=2&function=1" >
|
||||||
|
als Posten planen</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<h5 class="title is-5">Einteilung Fahrzeuge</h5>
|
||||||
|
<div id="vehicle-plan">
|
||||||
|
<div class="field is-grouped is-grouped-multiline">
|
||||||
|
|
||||||
|
<div class="control">
|
||||||
|
<div class="tags has-addons">
|
||||||
|
<span class="tag is-link"> 11.49.1 - HLF FF Ost</span>
|
||||||
|
<button class="tag is-delete" hx-delete="/vehicleassignments/delete?event=1&vehicle=1"
|
||||||
|
hx-target="#vehicle-plan" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Fahrzeug hinzufügen</label>
|
||||||
|
<div class="control">
|
||||||
|
<div class="select">
|
||||||
|
<select name="vehicle" hx-post="/vehicleassignments/new?event=1" hx-include="this"
|
||||||
|
hx-target="#vehicle-plan">
|
||||||
|
<option selected></option>
|
||||||
|
|
||||||
|
<option value="2">11.19.1 - MTW FF Ost</option>
|
||||||
|
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control">
|
||||||
|
<a class="button is-link is-light" hx-boost="true" href="/calendar?date=2025-01-01">
|
||||||
|
<svg class="icon">
|
||||||
|
<use href="/static/feather-sprite.svg#arrow-left" />
|
||||||
|
</svg>
|
||||||
|
<span>Zurück</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
@ -98,7 +98,7 @@ mod tests {
|
|||||||
),
|
),
|
||||||
comment: None,
|
comment: None,
|
||||||
};
|
};
|
||||||
Availability::create(pool, 1, new_availability).await?;
|
Availability::create(pool, 1, &new_availability).await?;
|
||||||
|
|
||||||
let new_assignment = AssignmentChangeset {
|
let new_assignment = AssignmentChangeset {
|
||||||
function: Function::Posten,
|
function: Function::Posten,
|
||||||
|
@ -117,7 +117,7 @@ mod tests {
|
|||||||
Availability::create(
|
Availability::create(
|
||||||
pool,
|
pool,
|
||||||
1,
|
1,
|
||||||
AvailabilityChangeset {
|
&AvailabilityChangeset {
|
||||||
time: (start, end),
|
time: (start, end),
|
||||||
comment: None,
|
comment: None,
|
||||||
},
|
},
|
||||||
@ -335,7 +335,7 @@ mod tests {
|
|||||||
Availability::create(
|
Availability::create(
|
||||||
&context.db_pool,
|
&context.db_pool,
|
||||||
1,
|
1,
|
||||||
AvailabilityChangeset {
|
&AvailabilityChangeset {
|
||||||
time: (start, end),
|
time: (start, end),
|
||||||
comment: None,
|
comment: None,
|
||||||
},
|
},
|
||||||
@ -346,7 +346,7 @@ mod tests {
|
|||||||
Availability::create(
|
Availability::create(
|
||||||
&context.db_pool,
|
&context.db_pool,
|
||||||
2,
|
2,
|
||||||
AvailabilityChangeset {
|
&AvailabilityChangeset {
|
||||||
time: (start, end),
|
time: (start, end),
|
||||||
comment: None,
|
comment: None,
|
||||||
},
|
},
|
||||||
|
@ -34,21 +34,23 @@ mod tests {
|
|||||||
use brass_macros::db_test;
|
use brass_macros::db_test;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use fake::{Fake, Faker};
|
use fake::{Fake, Faker};
|
||||||
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::utils::test_helper::{
|
use crate::utils::test_helper::{
|
||||||
create_test_login_user, test_delete, DbTestContext, NaiveDateTimeExt, RequestConfig,
|
create_test_login_user, test_delete, DbTestContext, NaiveDateTimeExt, RequestConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[db_test]
|
async fn arrange(pool: &PgPool, availability_id: i32, area_id: i32) -> Result<(), sqlx::Error> {
|
||||||
async fn deletes_when_availability_is_from_user_itself(context: &DbTestContext) {
|
Area::create(pool, "Süd").await?;
|
||||||
let app = context.app().await;
|
let mut changeset: UserChangeset = Faker.fake();
|
||||||
let config = RequestConfig::new("/availability/delete/1");
|
changeset.area_id = area_id;
|
||||||
create_test_login_user(&context.db_pool, &config).await;
|
|
||||||
|
User::create(pool, &changeset).await?;
|
||||||
|
|
||||||
Availability::create(
|
Availability::create(
|
||||||
&context.db_pool,
|
pool,
|
||||||
1,
|
availability_id,
|
||||||
AvailabilityChangeset {
|
&AvailabilityChangeset {
|
||||||
time: (
|
time: (
|
||||||
NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(),
|
NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(),
|
||||||
NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(),
|
NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(),
|
||||||
@ -56,8 +58,18 @@ mod tests {
|
|||||||
comment: None,
|
comment: None,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[db_test]
|
||||||
|
async fn deletes_when_availability_is_from_user_itself(context: &DbTestContext) {
|
||||||
|
let app = context.app().await;
|
||||||
|
let config = RequestConfig::new("/availability/delete/1");
|
||||||
|
create_test_login_user(&context.db_pool, &config).await;
|
||||||
|
|
||||||
|
arrange(&context.db_pool, 1, 1).await.unwrap();
|
||||||
|
|
||||||
let response = test_delete(&app, &config).await;
|
let response = test_delete(&app, &config).await;
|
||||||
assert_eq!(StatusCode::OK, response.status());
|
assert_eq!(StatusCode::OK, response.status());
|
||||||
@ -73,22 +85,7 @@ mod tests {
|
|||||||
let app = context.app().await;
|
let app = context.app().await;
|
||||||
let config = RequestConfig::new("/availability/delete/1").with_role(Role::AreaManager);
|
let config = RequestConfig::new("/availability/delete/1").with_role(Role::AreaManager);
|
||||||
create_test_login_user(&context.db_pool, &config).await;
|
create_test_login_user(&context.db_pool, &config).await;
|
||||||
|
arrange(&context.db_pool, 2, 1).await.unwrap();
|
||||||
User::create(&context.db_pool, &Faker.fake()).await.unwrap();
|
|
||||||
|
|
||||||
Availability::create(
|
|
||||||
&context.db_pool,
|
|
||||||
2,
|
|
||||||
AvailabilityChangeset {
|
|
||||||
time: (
|
|
||||||
NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(),
|
|
||||||
NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(),
|
|
||||||
),
|
|
||||||
comment: None,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let response = test_delete(&app, &config).await;
|
let response = test_delete(&app, &config).await;
|
||||||
assert_eq!(StatusCode::OK, response.status());
|
assert_eq!(StatusCode::OK, response.status());
|
||||||
@ -104,26 +101,7 @@ mod tests {
|
|||||||
let app = context.app().await;
|
let app = context.app().await;
|
||||||
let config = RequestConfig::new("/availability/delete/1").with_role(Role::AreaManager);
|
let config = RequestConfig::new("/availability/delete/1").with_role(Role::AreaManager);
|
||||||
create_test_login_user(&context.db_pool, &config).await;
|
create_test_login_user(&context.db_pool, &config).await;
|
||||||
|
arrange(&context.db_pool, 2, 2).await.unwrap();
|
||||||
Area::create(&context.db_pool, "Süd").await.unwrap();
|
|
||||||
let mut changeset: UserChangeset = Faker.fake();
|
|
||||||
changeset.area_id = 2;
|
|
||||||
|
|
||||||
User::create(&context.db_pool, &changeset).await.unwrap();
|
|
||||||
|
|
||||||
Availability::create(
|
|
||||||
&context.db_pool,
|
|
||||||
2,
|
|
||||||
AvailabilityChangeset {
|
|
||||||
time: (
|
|
||||||
NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(),
|
|
||||||
NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(),
|
|
||||||
),
|
|
||||||
comment: None,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let response = test_delete(&app, &config).await;
|
let response = test_delete(&app, &config).await;
|
||||||
assert_eq!(StatusCode::UNAUTHORIZED, response.status());
|
assert_eq!(StatusCode::UNAUTHORIZED, response.status());
|
||||||
@ -139,22 +117,7 @@ mod tests {
|
|||||||
let app = context.app().await;
|
let app = context.app().await;
|
||||||
let config = RequestConfig::new("/availability/delete/1").with_role(Role::Admin);
|
let config = RequestConfig::new("/availability/delete/1").with_role(Role::Admin);
|
||||||
create_test_login_user(&context.db_pool, &config).await;
|
create_test_login_user(&context.db_pool, &config).await;
|
||||||
|
arrange(&context.db_pool, 2, 1).await.unwrap();
|
||||||
User::create(&context.db_pool, &Faker.fake()).await.unwrap();
|
|
||||||
|
|
||||||
Availability::create(
|
|
||||||
&context.db_pool,
|
|
||||||
2,
|
|
||||||
AvailabilityChangeset {
|
|
||||||
time: (
|
|
||||||
NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(),
|
|
||||||
NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(),
|
|
||||||
),
|
|
||||||
comment: None,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let response = test_delete(&app, &config).await;
|
let response = test_delete(&app, &config).await;
|
||||||
assert_eq!(StatusCode::OK, response.status());
|
assert_eq!(StatusCode::OK, response.status());
|
||||||
|
@ -66,7 +66,7 @@ pub async fn post(
|
|||||||
|
|
||||||
Availability::update(pool.get_ref(), a.id, changeset).await?;
|
Availability::update(pool.get_ref(), a.id, changeset).await?;
|
||||||
} else {
|
} else {
|
||||||
Availability::create(pool.get_ref(), user_for_availability, changeset).await?;
|
Availability::create(pool.get_ref(), user_for_availability, &changeset).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let url = utils::get_return_url_for_date(&form.startdate);
|
let url = utils::get_return_url_for_date(&form.startdate);
|
||||||
|
@ -106,7 +106,7 @@ mod tests {
|
|||||||
Availability::create(
|
Availability::create(
|
||||||
&context.db_pool,
|
&context.db_pool,
|
||||||
2,
|
2,
|
||||||
AvailabilityChangeset {
|
&AvailabilityChangeset {
|
||||||
time: (
|
time: (
|
||||||
NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(),
|
NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(),
|
||||||
NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(),
|
NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(),
|
||||||
@ -169,7 +169,7 @@ mod tests {
|
|||||||
Availability::create(
|
Availability::create(
|
||||||
&context.db_pool,
|
&context.db_pool,
|
||||||
2,
|
2,
|
||||||
AvailabilityChangeset {
|
&AvailabilityChangeset {
|
||||||
time: (
|
time: (
|
||||||
NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(),
|
NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(),
|
||||||
NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(),
|
NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(),
|
||||||
@ -216,7 +216,7 @@ mod tests {
|
|||||||
Availability::create(
|
Availability::create(
|
||||||
&context.db_pool,
|
&context.db_pool,
|
||||||
1,
|
1,
|
||||||
AvailabilityChangeset {
|
&AvailabilityChangeset {
|
||||||
time: (
|
time: (
|
||||||
NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(),
|
NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(),
|
||||||
NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(),
|
NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(),
|
||||||
@ -267,7 +267,7 @@ mod tests {
|
|||||||
Availability::create(
|
Availability::create(
|
||||||
&context.db_pool,
|
&context.db_pool,
|
||||||
2,
|
2,
|
||||||
AvailabilityChangeset {
|
&AvailabilityChangeset {
|
||||||
time: (
|
time: (
|
||||||
NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(),
|
NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 10, 0, 0).unwrap(),
|
||||||
NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(),
|
NaiveDateTime::from_ymd_and_hms(2025, 02, 01, 10, 0, 0).unwrap(),
|
||||||
|
@ -18,7 +18,12 @@ use crate::{
|
|||||||
use brass_db::models::{Availability, AvailabilityAssignmentState, Event, Role, User, Vehicle};
|
use brass_db::models::{Availability, AvailabilityAssignmentState, Event, Role, User, Vehicle};
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "events/plan.html")]
|
#[cfg_attr(not(test), template(path = "events/plan.html"))]
|
||||||
|
#[cfg_attr(
|
||||||
|
test,
|
||||||
|
template(path = "events/plan.html", block = "content"),
|
||||||
|
allow(dead_code)
|
||||||
|
)]
|
||||||
pub struct PlanEventTemplate {
|
pub struct PlanEventTemplate {
|
||||||
user: User,
|
user: User,
|
||||||
event: Event,
|
event: Event,
|
||||||
@ -72,3 +77,132 @@ pub async fn get(
|
|||||||
|
|
||||||
Ok(template.to_response()?)
|
Ok(template.to_response()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use actix_http::StatusCode;
|
||||||
|
use brass_macros::db_test;
|
||||||
|
use chrono::NaiveDateTime;
|
||||||
|
use fake::{Fake, Faker};
|
||||||
|
use sqlx::PgPool;
|
||||||
|
|
||||||
|
use crate::utils::test_helper::{
|
||||||
|
assert_snapshot, create_test_login_user, test_get, DbTestContext, NaiveDateTimeExt,
|
||||||
|
RequestConfig, ServiceResponseExt,
|
||||||
|
};
|
||||||
|
use brass_db::models::{
|
||||||
|
Area, Assignment, AssignmentChangeset, Availability, AvailabilityChangeset, Event,
|
||||||
|
EventChangeset, Function, Location, Role, User, UserChangeset, Vehicle, VehicleAssignment,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[db_test]
|
||||||
|
async fn generates_template_for_admin(context: &DbTestContext) {
|
||||||
|
let app = context.app().await;
|
||||||
|
let config = RequestConfig::new("/events/1/plan").with_role(Role::Admin);
|
||||||
|
arrange(&context.db_pool).await.unwrap();
|
||||||
|
create_test_login_user(&context.db_pool, &config).await;
|
||||||
|
|
||||||
|
let (status, body) = test_get(app, &config).await.into_status_and_body().await;
|
||||||
|
|
||||||
|
assert_eq!(StatusCode::OK, status);
|
||||||
|
assert_snapshot!(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[db_test]
|
||||||
|
async fn returns_ok_for_area_manager_of_same_area(context: &DbTestContext) {
|
||||||
|
let app = context.app().await;
|
||||||
|
let config = RequestConfig::new("/events/1/plan").with_role(Role::AreaManager);
|
||||||
|
arrange(&context.db_pool).await.unwrap();
|
||||||
|
create_test_login_user(&context.db_pool, &config).await;
|
||||||
|
|
||||||
|
let response = test_get(app, &config).await;
|
||||||
|
|
||||||
|
assert_eq!(StatusCode::OK, response.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[db_test]
|
||||||
|
async fn returns_unauthorized_for_area_manager_of_different_area(context: &DbTestContext) {
|
||||||
|
let app = context.app().await;
|
||||||
|
let config = RequestConfig::new("/events/1/plan")
|
||||||
|
.with_role(Role::AreaManager)
|
||||||
|
.with_user_area(2);
|
||||||
|
arrange(&context.db_pool).await.unwrap();
|
||||||
|
create_test_login_user(&context.db_pool, &config).await;
|
||||||
|
|
||||||
|
let response = test_get(app, &config).await;
|
||||||
|
|
||||||
|
assert_eq!(StatusCode::UNAUTHORIZED, response.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[db_test]
|
||||||
|
async fn returns_unauthorized_for_user(context: &DbTestContext) {
|
||||||
|
let app = context.app().await;
|
||||||
|
let config = RequestConfig::new("/events/1/plan");
|
||||||
|
arrange(&context.db_pool).await.unwrap();
|
||||||
|
create_test_login_user(&context.db_pool, &config).await;
|
||||||
|
|
||||||
|
let response = test_get(app, &config).await;
|
||||||
|
|
||||||
|
assert_eq!(StatusCode::UNAUTHORIZED, response.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[db_test]
|
||||||
|
async fn returns_not_found_when_event_doesnt_exist(context: &DbTestContext) {
|
||||||
|
let app = context.app().await;
|
||||||
|
let config = RequestConfig::new("/events/1/plan").with_role(Role::AreaManager);
|
||||||
|
create_test_login_user(&context.db_pool, &config).await;
|
||||||
|
|
||||||
|
let response = test_get(app, &config).await;
|
||||||
|
|
||||||
|
assert_eq!(StatusCode::NOT_FOUND, response.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn arrange(pool: &PgPool) -> anyhow::Result<()> {
|
||||||
|
Location::create(pool, "Location", 1).await?;
|
||||||
|
Area::create(pool, "Süd").await.unwrap();
|
||||||
|
|
||||||
|
let mut user_changeset: UserChangeset = Faker.fake();
|
||||||
|
user_changeset.name = "Max Mustermann".to_string();
|
||||||
|
User::create(pool, &user_changeset).await?;
|
||||||
|
|
||||||
|
let mut other_area_user: UserChangeset = Faker.fake();
|
||||||
|
other_area_user.area_id = 2;
|
||||||
|
other_area_user.name = "Rudi Tester".to_string();
|
||||||
|
User::create(pool, &other_area_user).await?;
|
||||||
|
|
||||||
|
let start = NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 12, 0, 0).unwrap();
|
||||||
|
let end = NaiveDateTime::from_ymd_and_hms(2025, 01, 01, 22, 0, 0).unwrap();
|
||||||
|
|
||||||
|
let mut new_event = EventChangeset::create_for_test(start, end);
|
||||||
|
new_event.name = "Große Veranstaltung".to_string();
|
||||||
|
Event::create(pool, new_event).await?;
|
||||||
|
|
||||||
|
let new_availability = AvailabilityChangeset {
|
||||||
|
time: (start, end),
|
||||||
|
comment: Some("Kommentar".to_string()),
|
||||||
|
};
|
||||||
|
Availability::create(pool, 1, &new_availability).await?;
|
||||||
|
Availability::create(pool, 2, &new_availability).await?;
|
||||||
|
Availability::update_cross_areal(pool, 2, true)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let new_assignment = AssignmentChangeset {
|
||||||
|
function: Function::Posten,
|
||||||
|
time: (start, end),
|
||||||
|
};
|
||||||
|
Assignment::create(pool, 1, 1, new_assignment).await?;
|
||||||
|
|
||||||
|
Vehicle::create(pool, "11.49.1", "HLF FF Ost")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
Vehicle::create(pool, "11.19.1", "MTW FF Ost")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
VehicleAssignment::create(pool, 1, 1, start, end)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
<p><b>Führungsassistent benötigt:</b> {% if event.fuehrungsassistent_required %}ja{% else %}nein{% endif %}
|
<p><b>Führungsassistent benötigt:</b> {% if event.fuehrungsassistent_required %}ja{% else %}nein{% endif -%}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -19,10 +19,11 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for (availability, status) in availabilities %}
|
{% for (availability, status) in availabilities -%}
|
||||||
{% let u = availability.user.as_ref().unwrap() %}
|
{%- let u = availability.user.as_ref().unwrap() -%}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ u.name }}</td>
|
<td>{{ u.name }}{% if u.area_id != event.location.as_ref().unwrap().area_id %} (Fremdbereich {{ u.area.as_ref().unwrap().name
|
||||||
|
}}){% endif %}</td>
|
||||||
<td>
|
<td>
|
||||||
{{ u.function|show_tree|safe }}
|
{{ u.function|show_tree|safe }}
|
||||||
</td>
|
</td>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user