From f894bf325df19f451cd93efa77b7d2f452b1082f Mon Sep 17 00:00:00 2001 From: Simon Goller Date: Fri, 14 Jun 2024 09:53:57 +0200 Subject: [PATCH] Add endpoint to copy shift plan week --- rest/src/booking.rs | 39 +++++++++++++++++++++++++++++++-- rest/src/lib.rs | 14 ++++++++++++ service/src/booking.rs | 8 +++++++ service_impl/src/booking.rs | 43 +++++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 2 deletions(-) diff --git a/rest/src/booking.rs b/rest/src/booking.rs index 3e254b0..a44f07f 100644 --- a/rest/src/booking.rs +++ b/rest/src/booking.rs @@ -1,14 +1,15 @@ +use std::collections::HashMap; use std::sync::Arc; use axum::body::Body; -use axum::extract::Path; +use axum::extract::{Path, Query}; use axum::routing::{delete, get, post}; use axum::{extract::State, response::Response}; use axum::{Extension, Json, Router}; use rest_types::BookingTO; use uuid::Uuid; -use crate::{error_handler, Context, RestStateDef}; +use crate::{error_handler, Context, RestError, RestStateDef}; use service::booking::{Booking, BookingService}; pub fn generate_route() -> Router { @@ -18,6 +19,7 @@ pub fn generate_route() -> Router { .route("/:id", get(get_booking::)) .route("/", post(create_booking::)) .route("/:id", delete(delete_booking::)) + .route("/copy", post(copy_calendar_week::)) } pub async fn get_all_bookings( @@ -109,6 +111,39 @@ pub async fn create_booking( ) } +pub async fn copy_calendar_week( + rest_state: State, + Extension(context): Extension, + Query(params): Query>, +) -> Response { + error_handler( + (async { + let from_year = params + .get("from_year") + .ok_or_else(|| RestError::BadRequest("year missing".to_string()))? + .parse()?; + let from_week = params + .get("from_week") + .ok_or_else(|| RestError::BadRequest("week missing".to_string()))? + .parse()?; + let to_year = params + .get("to_year") + .ok_or_else(|| RestError::BadRequest("year missing".to_string()))? + .parse()?; + let to_week = params + .get("to_week") + .ok_or_else(|| RestError::BadRequest("week missing".to_string()))? + .parse()?; + rest_state + .booking_service() + .copy_week(from_week, from_year, to_week, to_year, context.into()) + .await?; + Ok(Response::builder().status(200).body(Body::empty()).unwrap()) + }) + .await, + ) +} + pub async fn delete_booking( rest_state: State, Extension(context): Extension, diff --git a/rest/src/lib.rs b/rest/src/lib.rs index 15bf403..5bf5e31 100644 --- a/rest/src/lib.rs +++ b/rest/src/lib.rs @@ -107,6 +107,12 @@ pub enum RestError { #[error("Inconsistent id. Got {0} in path but {1} in body")] InconsistentId(Uuid, Uuid), + + #[error("Bad request: {0}")] + BadRequest(String), + + #[error("Parse int error: {0}")] + ParseIntError(#[from] std::num::ParseIntError), } fn error_handler(result: Result) -> Response { @@ -119,6 +125,14 @@ fn error_handler(result: Result) -> Response { .status(400) .body(Body::new(err.to_string())) .unwrap(), + Err(err @ RestError::BadRequest(_)) => Response::builder() + .status(400) + .body(Body::new(err.to_string())) + .unwrap(), + Err(err @ RestError::ParseIntError(_)) => Response::builder() + .status(400) + .body(Body::new(err.to_string())) + .unwrap(), Err(RestError::ServiceError(service::ServiceError::Forbidden)) => { Response::builder().status(403).body(Body::empty()).unwrap() } diff --git a/service/src/booking.rs b/service/src/booking.rs index e27bf01..c5b024b 100644 --- a/service/src/booking.rs +++ b/service/src/booking.rs @@ -75,6 +75,14 @@ pub trait BookingService { booking: &Booking, context: Authentication, ) -> Result; + async fn copy_week( + &self, + from_calendar_week: u8, + from_year: u32, + to_calendar_week: u8, + to_year: u32, + context: Authentication, + ) -> Result<(), ServiceError>; async fn delete( &self, id: Uuid, diff --git a/service_impl/src/booking.rs b/service_impl/src/booking.rs index 3aab21a..5148cf7 100644 --- a/service_impl/src/booking.rs +++ b/service_impl/src/booking.rs @@ -233,6 +233,49 @@ where Ok(new_booking) } + async fn copy_week( + &self, + from_calendar_week: u8, + from_year: u32, + to_calendar_week: u8, + to_year: u32, + context: Authentication, + ) -> Result<(), ServiceError> { + self.permission_service + .check_permission("hr", context.clone()) + .await?; + let from_week = self + .get_for_week(from_calendar_week, from_year, Authentication::Full) + .await?; + let to_week = self + .get_for_week(to_calendar_week, to_year, Authentication::Full) + .await?; + + // Remove entries which are already in the destination week + let to_week_ids: Arc<[(Uuid, Uuid)]> = to_week + .iter() + .map(|b| (b.sales_person_id, b.slot_id)) + .collect(); + let from_week: Arc<[Booking]> = from_week + .iter() + .filter(|b| !to_week_ids.contains(&(b.sales_person_id, b.slot_id))) + .map(|b| { + let mut new_booking = b.clone(); + new_booking.id = Uuid::nil(); + new_booking.calendar_week = to_calendar_week as i32; + new_booking.year = to_year; + new_booking.created = None; + new_booking.version = Uuid::nil(); + new_booking + }) + .collect(); + + for booking in from_week.into_iter() { + self.create(booking, Authentication::Full).await?; + } + Ok(()) + } + async fn delete( &self, id: Uuid,