You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
5.2 KiB
Rust

use leptos::*;
use validator::Validate;
use crate::backend::data::{ApiResponse, CrReservation, Reservation, PublicFormData};
use crate::components::data_form::ForValidation;
use cfg_if::cfg_if;
use chrono::{NaiveDate, NaiveTime};
cfg_if! { if #[cfg(feature = "ssr")] {
use sqlx::{Postgres, Transaction};
use sqlx::query_as;
use sqlx::Error;
use uuid::Uuid;
use std::ops::DerefMut;
use std::str::FromStr;
use crate::backend::data::ReservationSum;
use crate::backend::get_pool;
async fn find_sum_by_uuid(uuid: &Uuid, tx: &mut Transaction<'_, Postgres>) -> Result<ReservationSum, Error> {
let reservation = query_as::<_, ReservationSum>("SELECT * FROM reservation_sum WHERE uuid = $1")
.bind(uuid)
.fetch_one(tx.deref_mut())
.await?;
Ok(reservation)
}
async fn reservations_for_day(day: &NaiveDate) -> Result<Vec<Reservation>, ServerFnError> {
let pool = get_pool().await?;
let reservations = query_as::<_, Reservation>("SELECT * FROM reservation JOIN reservation_sum on reservation.summary=reservation_sum.id WHERE reservation_sum.date=$1")
.bind(day)
.fetch_all(&pool)
.await;
if let Err(e) = reservations {
if matches!(e, Error::RowNotFound) {
Ok(vec![])
} else {
Err(e.into())
}
} else {
Ok(reservations?)
}
}
}}
#[server]
pub async fn get_public_form_data(day: NaiveDate) -> Result<ApiResponse<Vec<PublicFormData>>, ServerFnError> {
use crate::backend::opening_hours::hours_for_day;
use crate::backend::property::get_props;
use chrono::Datelike;
let hours = hours_for_day(day.weekday()).await?;
let props = get_props(Some("active = true".to_string())).await?;
let reservations = reservations_for_day(&day).await?;
Ok(ApiResponse::Data(props.into_iter().map(|p| PublicFormData {
property: p,
hours: hours.clone(),
reservations: reservations.clone()
}).collect::<Vec<_>>()))
}
pub fn is_reserved(reservations: &Vec<Reservation>, time: &NaiveTime, property: i32) -> bool {
for r in reservations {
if r.property == property && &r.from <= time && time < &r.to {
return true
}
}
false
}
#[server]
pub async fn create_reservation(reservation: CrReservation) -> Result<ApiResponse<NaiveDate>, ServerFnError> {
use crate::backend::get_pool;
use crate::backend::customer::find_customer_by_email;
use crate::backend::customer::sync_customer_data;
use crate::backend::customer::create_customer;
use crate::backend::property::get_prop_by_id;
use crate::backend::data::{TmCheck, ReservationState, Reservations};
use std::collections::HashMap;
use crate::error::AppError;
use chrono::Local;
use sqlx::query;
use rust_decimal::Decimal;
let slots = reservation.slots().iter().fold(HashMap::new(), |mut map, s| {
let slot_str = s.split("|").collect::<Vec<_>>();
map.entry(i32::from_str(slot_str.get(1).unwrap_or(&"")).unwrap_or(0))
.and_modify(|slot: &mut Vec<Result<TmCheck, AppError>>| slot.push(TmCheck::from_str(slot_str.get(0).unwrap_or(&""))))
.or_insert(vec![TmCheck::from_str(slot_str.get(0).unwrap_or(&""))]);
map
});
let res_for_day = reservations_for_day(&reservation.date()).await?;
let mut reservations = Reservations::new();
let mut price = Decimal::from(0);
for sl in slots {
let mut checks = sl.1.clone();
checks.sort();
let property = get_prop_by_id(sl.0).await?;
for c in checks {
reservations.add_slot(&c.clone()?, sl.0);
price = price + property.price;
if is_reserved(&res_for_day, &c?.from, sl.0) {
return Ok(ApiResponse::Error("Slot and time already booked".to_string()))
}
}
}
let pool = get_pool().await?;
let mut tx = pool.begin().await?;
let customer = if let Some(c) = find_customer_by_email(reservation.email(), &mut tx).await {
sync_customer_data(&c, reservation.full_name(), reservation.phone(), &mut tx).await?
} else {
create_customer(reservation.full_name(), reservation.email(), reservation.phone(), &mut tx).await?
};
let res_uuid = Uuid::new_v4();
query("INSERT INTO reservation_sum(uuid, date, customer, price, state, note, date_create) VALUES($1, $2, $3, $4, $5, $6, $7)")
.bind(res_uuid)
.bind(reservation.date())
.bind(customer.id())
.bind(price)
.bind(ReservationState::New)
.bind(reservation.note())
.bind(Local::now().date_naive())
.execute(tx.deref_mut())
.await?;
let sum = find_sum_by_uuid(&res_uuid, &mut tx).await?;
for r in reservations.reservations() {
query(r#"INSERT INTO reservation("from", "to", property, summary) VALUES($1, $2, $3, $4)"#)
.bind(r.from)
.bind(r.to)
.bind(r.property)
.bind(sum.id())
.execute(tx.deref_mut())
.await?;
}
tx.commit().await?;
Ok(ApiResponse::Data(reservation.date()))
}
impl ForValidation for CreateReservation {
fn entity(&self) -> &dyn Validate {
&self.reservation
}
}