2023-08-02 14:29:19 +02:00
use crate ::model ::{ boat ::Boat , user ::User } ;
use chrono ::NaiveDateTime ;
use rocket ::serde ::{ Deserialize , Serialize } ;
use rocket ::FromForm ;
use sqlx ::{ FromRow , SqlitePool } ;
2023-10-05 08:59:13 +02:00
use super ::log ::Log ;
2024-03-28 08:34:02 +01:00
use super ::notification ::Notification ;
use super ::role ::Role ;
2023-10-05 08:59:13 +02:00
2023-08-02 14:29:19 +02:00
#[ derive(FromRow, Debug, Serialize, Deserialize) ]
pub struct BoatDamage {
pub id : i64 ,
pub boat_id : i64 ,
pub desc : String ,
pub user_id_created : i64 ,
pub created_at : NaiveDateTime ,
pub user_id_fixed : Option < i64 > ,
pub fixed_at : Option < NaiveDateTime > ,
pub user_id_verified : Option < i64 > ,
pub verified_at : Option < NaiveDateTime > ,
pub lock_boat : bool ,
}
#[ derive(FromRow, Debug, Serialize, Deserialize) ]
pub struct BoatDamageWithDetails {
#[ serde(flatten) ]
boat_damage : BoatDamage ,
user_created : User ,
user_fixed : Option < User > ,
user_verified : Option < User > ,
boat : Boat ,
2023-10-23 22:26:33 +02:00
verified : bool ,
2023-08-02 14:29:19 +02:00
}
2023-10-05 08:59:13 +02:00
#[ derive(Debug) ]
2023-08-02 14:29:19 +02:00
pub struct BoatDamageToAdd < ' r > {
pub boat_id : i64 ,
pub desc : & ' r str ,
pub user_id_created : i32 ,
pub lock_boat : bool ,
}
2023-10-05 08:59:13 +02:00
#[ derive(FromForm, Debug) ]
2023-08-02 14:29:19 +02:00
pub struct BoatDamageFixed < ' r > {
pub desc : & ' r str ,
pub user_id_fixed : i32 ,
}
2023-10-05 08:59:13 +02:00
#[ derive(FromForm, Debug) ]
2023-08-02 14:29:19 +02:00
pub struct BoatDamageVerified < ' r > {
pub desc : & ' r str ,
pub user_id_verified : i32 ,
}
impl BoatDamage {
pub async fn find_by_id ( db : & SqlitePool , id : i32 ) -> Option < Self > {
sqlx ::query_as! (
Self ,
" SELECT id, boat_id, desc, user_id_created, created_at, user_id_fixed, fixed_at, user_id_verified, verified_at, lock_boat
FROM boat_damage
WHERE id like ? " ,
id
)
. fetch_one ( db )
. await
. ok ( )
}
pub async fn all ( db : & SqlitePool ) -> Vec < BoatDamageWithDetails > {
let boatdamages = sqlx ::query_as! (
BoatDamage ,
"
SELECT id , boat_id , desc , user_id_created , created_at , user_id_fixed , fixed_at , user_id_verified , verified_at , lock_boat
FROM boat_damage
2024-04-14 20:28:06 +02:00
WHERE (
verified_at IS NULL
OR verified_at > = datetime ( ' now ' , ' - 30 days ' )
)
2023-08-02 14:29:19 +02:00
ORDER BY created_at DESC
"
)
. fetch_all ( db )
. await
. unwrap ( ) ; //TODO: fixme
let mut res = Vec ::new ( ) ;
for boat_damage in boatdamages {
let user_fixed = match boat_damage . user_id_fixed {
Some ( id ) = > {
let user = User ::find_by_id ( db , id as i32 ) . await ;
Some ( user . unwrap ( ) )
}
None = > None ,
} ;
let user_verified = match boat_damage . user_id_verified {
Some ( id ) = > {
let user = User ::find_by_id ( db , id as i32 ) . await ;
Some ( user . unwrap ( ) )
}
None = > None ,
} ;
res . push ( BoatDamageWithDetails {
boat : Boat ::find_by_id ( db , boat_damage . boat_id as i32 )
. await
. unwrap ( ) ,
user_created : User ::find_by_id ( db , boat_damage . user_id_created as i32 )
. await
. unwrap ( ) ,
user_fixed ,
2023-10-23 22:26:33 +02:00
verified : user_verified . is_some ( ) ,
2023-08-02 14:29:19 +02:00
user_verified ,
boat_damage ,
2023-09-06 14:39:36 +02:00
} ) ;
2023-08-02 14:29:19 +02:00
}
res
}
pub async fn create ( db : & SqlitePool , boatdamage : BoatDamageToAdd < '_ > ) -> Result < ( ) , String > {
2023-10-05 08:59:13 +02:00
Log ::create ( db , format! ( " New boat damage: {boatdamage:?} " ) ) . await ;
2024-05-20 20:32:26 +02:00
let Some ( boat ) = Boat ::find_by_id ( db , boatdamage . boat_id as i32 ) . await else {
return Err ( " Boot gibt's ned " . into ( ) ) ;
} ;
let was_unusable_before = boat . is_locked ( db ) . await ;
2023-10-05 08:59:13 +02:00
2023-08-02 14:29:19 +02:00
sqlx ::query! (
" INSERT INTO boat_damage(boat_id, desc, user_id_created, lock_boat) VALUES (?,?,?, ?) " ,
boatdamage . boat_id ,
boatdamage . desc ,
boatdamage . user_id_created ,
boatdamage . lock_boat
)
. execute ( db )
. await
. map_err ( | e | e . to_string ( ) ) ? ;
2024-03-28 08:34:02 +01:00
2024-05-20 20:32:26 +02:00
if ! was_unusable_before & & boat . is_locked ( db ) . await {
let cox = Role ::find_by_name ( db , " cox " ) . await . unwrap ( ) ;
2024-05-21 19:41:00 +02:00
Notification ::create_for_role ( db , & cox , & format! ( " Liebe Steuerberechtigte, bitte beachten, dass {} bis auf weiteres aufgrund von Reparaturarbeiten gesperrt ist. " , boat . name ) , " Boot gesperrt " , None , None ) . await ;
2024-05-20 20:32:26 +02:00
}
2024-03-28 08:34:02 +01:00
let technicals =
User ::all_with_role ( db , & Role ::find_by_name ( db , " tech " ) . await . unwrap ( ) ) . await ;
for technical in technicals {
if technical . id as i32 ! = boatdamage . user_id_created {
Notification ::create (
db ,
& technical ,
& format! (
" {} hat einen neuen Bootschaden für Boot '{}' angelegt: {} " ,
User ::find_by_id ( db , boatdamage . user_id_created )
. await
. unwrap ( )
. name ,
2024-05-20 20:32:26 +02:00
boat . name ,
2024-03-28 08:34:02 +01:00
boatdamage . desc
) ,
" Neuer Bootsschaden angelegt " ,
None ,
2024-05-21 19:41:00 +02:00
None ,
2024-03-28 08:34:02 +01:00
)
. await ;
}
}
Notification ::create (
db ,
& User ::find_by_id ( db , boatdamage . user_id_created )
. await
. unwrap ( ) ,
& format! (
" Du hat einen neuen Bootschaden für Boot '{}' angelegt: {} " ,
Boat ::find_by_id ( db , boatdamage . boat_id as i32 )
. await
. unwrap ( )
. name ,
boatdamage . desc
) ,
" Neuer Bootsschaden angelegt " ,
None ,
2024-05-21 19:41:00 +02:00
None ,
2024-03-28 08:34:02 +01:00
)
. await ;
2023-08-02 14:29:19 +02:00
Ok ( ( ) )
}
2024-03-28 08:34:02 +01:00
pub async fn fixed (
& self ,
db : & SqlitePool ,
boat_damage : BoatDamageFixed < '_ > ,
) -> Result < ( ) , String > {
Log ::create ( db , format! ( " Fixed boat damage: {boat_damage:?} " ) ) . await ;
let boat = Boat ::find_by_id ( db , self . boat_id as i32 ) . await . unwrap ( ) ;
2023-10-05 08:59:13 +02:00
2023-08-02 14:29:19 +02:00
sqlx ::query! (
" UPDATE boat_damage SET desc=?, user_id_fixed=?, fixed_at=CURRENT_TIMESTAMP WHERE id=? " ,
2024-03-28 08:34:02 +01:00
boat_damage . desc ,
boat_damage . user_id_fixed ,
2023-08-02 14:29:19 +02:00
self . id
)
. execute ( db )
. await
. map_err ( | e | e . to_string ( ) ) ? ;
2024-03-28 08:34:02 +01:00
let user = User ::find_by_id ( db , boat_damage . user_id_fixed )
. await
. unwrap ( ) ;
2023-12-23 21:27:52 +01:00
if user . has_role ( db , " tech " ) . await {
2023-08-02 14:29:19 +02:00
return self
. verified (
db ,
BoatDamageVerified {
2024-03-28 08:34:02 +01:00
desc : boat_damage . desc ,
2023-08-02 14:29:19 +02:00
user_id_verified : user . id as i32 ,
} ,
)
. await ;
}
2024-03-28 08:34:02 +01:00
let technicals =
User ::all_with_role ( db , & Role ::find_by_name ( db , " tech " ) . await . unwrap ( ) ) . await ;
for technical in technicals {
if technical . id as i32 ! = boat_damage . user_id_fixed {
Notification ::create (
db ,
& technical ,
& format! (
" {} hat den Bootschaden '{}' beim Boot '{}' repariert. Könntest du das bei Gelegenheit verifizieren? " ,
User ::find_by_id ( db , boat_damage . user_id_fixed )
. await
. unwrap ( )
. name ,
boat_damage . desc ,
boat . name ,
) ,
" Bootsschaden repariert " ,
2024-05-21 19:41:00 +02:00
None , None
2024-03-28 08:34:02 +01:00
)
. await ;
}
}
2024-04-17 12:44:31 +02:00
if boat_damage . user_id_fixed ! = self . user_id_created as i32 {
let user_fixed = User ::find_by_id ( db , boat_damage . user_id_fixed )
2024-03-28 08:34:02 +01:00
. await
2024-04-17 12:44:31 +02:00
. unwrap ( ) ;
let user_created = User ::find_by_id ( db , self . user_id_created as i32 )
. await
. unwrap ( ) ;
// Boatdamage is also directly verified, if a tech has repaired it. We don't want to
// send 2 notifications.
if ! user_fixed . has_role ( db , " tech " ) . await {
Notification ::create (
db ,
& user_created ,
& format! (
" {} hat den von dir eingetragenen Bootschaden '{}' beim Boot '{}' repariert. Dieser muss nun noch von unseren Bootswarten bestätigt werden. " ,
user_fixed . name ,
boat_damage . desc , boat . name ,
) ,
" Bootsschaden repariert " ,
2024-05-21 19:41:00 +02:00
None , None
2024-04-17 12:44:31 +02:00
)
. await ;
}
}
2024-03-28 08:34:02 +01:00
2023-08-02 14:29:19 +02:00
Ok ( ( ) )
}
pub async fn verified (
& self ,
db : & SqlitePool ,
2024-05-20 20:32:26 +02:00
boat_form : BoatDamageVerified < '_ > ,
2023-08-02 14:29:19 +02:00
) -> Result < ( ) , String > {
2024-05-20 20:32:26 +02:00
if let Some ( verifier ) = User ::find_by_id ( db , boat_form . user_id_verified ) . await {
2023-12-23 21:27:52 +01:00
if ! verifier . has_role ( db , " tech " ) . await {
2024-05-20 20:32:26 +02:00
Log ::create ( db , format! ( " User {verifier:?} tried to verify boat {boat_form:?} . The user is no tech. Manually craftted request? " ) ) . await ;
2023-10-05 08:59:13 +02:00
return Err ( " You are not allowed to verify the boat! " . into ( ) ) ;
}
} else {
2024-05-20 20:32:26 +02:00
Log ::create ( db , format! ( " Someone tried to verify the boat {boat_form:?} with user_id= {} which does not exist. Manually craftted request? " , boat_form . user_id_verified ) ) . await ;
2023-10-05 08:59:13 +02:00
return Err ( " Could not find user " . into ( ) ) ;
}
2024-05-20 20:32:26 +02:00
let Some ( boat ) = Boat ::find_by_id ( db , self . boat_id as i32 ) . await else {
return Err ( " Boot gibt's ned " . into ( ) ) ;
} ;
let was_unusable_before = boat . is_locked ( db ) . await ;
Log ::create ( db , format! ( " Verified boat damage: {boat_form:?} " ) ) . await ;
2023-08-02 14:29:19 +02:00
sqlx ::query! (
" UPDATE boat_damage SET desc=?, user_id_verified=?, verified_at=CURRENT_TIMESTAMP WHERE id=? " ,
2024-05-20 20:32:26 +02:00
boat_form . desc ,
boat_form . user_id_verified ,
2023-08-02 14:29:19 +02:00
self . id
)
. execute ( db )
. await . map_err ( | e | e . to_string ( ) ) ? ;
2024-04-17 12:44:31 +02:00
2024-05-20 20:32:26 +02:00
if boat_form . user_id_verified ! = self . user_id_created as i32 {
let user_verified = User ::find_by_id ( db , boat_form . user_id_verified )
. await
. unwrap ( ) ;
2024-04-17 12:44:31 +02:00
let user_created = User ::find_by_id ( db , self . user_id_created as i32 )
. await
. unwrap ( ) ;
if user_verified . id = = self . user_id_fixed . unwrap ( ) {
Notification ::create (
db ,
& user_created ,
& format! (
" {} hat den von dir eingetragenen Bootschaden '{}' beim Boot '{}' repariert und verifiziert. " ,
user_verified . name ,
self . desc , boat . name ,
) ,
" Bootsschaden repariert & verifiziert " ,
None ,
2024-05-21 19:41:00 +02:00
None
2024-04-17 12:44:31 +02:00
)
. await ;
} else {
Notification ::create (
db ,
& user_created ,
& format! (
" {} hat verifiziert, dass der von dir eingetragenen Bootschaden '{}' beim Boot '{}' korrekt repariert wurde. " ,
user_verified . name ,
self . desc , boat . name ,
) ,
" Bootsschaden verifiziert " ,
None ,
2024-05-21 19:41:00 +02:00
None
2024-04-17 12:44:31 +02:00
) . await ;
}
}
2024-05-20 20:32:26 +02:00
if was_unusable_before & & ! boat . is_locked ( db ) . await {
let cox = Role ::find_by_name ( db , " cox " ) . await . unwrap ( ) ;
2024-05-21 19:41:00 +02:00
Notification ::create_for_role ( db , & cox , & format! ( " Liebe Steuerberechtigte, {} wurde repariert und freut sich ab sofort wieder gerudert zu werden :-) " , boat . name ) , " Boot repariert " , None , None ) . await ;
2024-05-20 20:32:26 +02:00
}
2023-08-02 14:29:19 +02:00
Ok ( ( ) )
}
}