use chrono::NaiveDate;
use serde_json::Value as Json;
use sqlx::{PgConnection, Row};
use uuid::Uuid;
pub struct ActivityRow {
pub id: Uuid,
pub res_model: String,
pub res_id: Uuid,
pub activity_type_id: Option<Uuid>,
pub summary: Option<String>,
pub note: Option<String>,
pub user_id: Uuid,
pub date_deadline: NaiveDate,
pub active: bool,
pub chained_next_activity: Option<Json>,
}
pub struct ActivityTypeRow {
pub id: Uuid,
pub chaining_type: String,
pub delay_count: i32,
pub delay_unit: String,
pub delay_from: String,
pub default_user_id: Option<Uuid>,
pub active: bool,
}
pub struct PlanTemplateRow {
pub id: Uuid,
pub activity_type_id: Uuid,
pub summary: Option<String>,
pub note: Option<String>,
pub delay_count: Option<i32>,
pub delay_unit: Option<String>,
pub user_id: Option<Uuid>,
}
pub struct NewActivityRow<'a> {
pub id: Uuid,
pub res_model: &'a str,
pub res_id: Uuid,
pub activity_type_id: Option<Uuid>,
pub summary: Option<&'a str>,
pub note: Option<&'a str>,
pub date_deadline: NaiveDate,
pub user_id: Uuid,
pub requested_user_id: Option<Uuid>,
pub state: &'a str,
pub chained_next_activity: Option<&'a Json>,
}
pub struct ActivityRepository;
impl ActivityRepository {
pub fn new() -> Self {
Self
}
}
impl Default for ActivityRepository {
fn default() -> Self {
Self::new()
}
}
impl ActivityRepository {
pub async fn insert_activity(
conn: &mut PgConnection,
a: &NewActivityRow<'_>,
) -> Result<Uuid, sqlx::Error> {
sqlx::query_scalar::<_, Uuid>(
r#"INSERT INTO messaging.mail_activities
(id, res_model, res_id, activity_type_id, summary, note, date_deadline,
user_id, requested_user_id, state, chained_next_activity)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10::mail_activity_state,$11)
RETURNING id"#,
)
.bind(a.id)
.bind(a.res_model)
.bind(a.res_id)
.bind(a.activity_type_id)
.bind(a.summary)
.bind(a.note)
.bind(a.date_deadline)
.bind(a.user_id)
.bind(a.requested_user_id)
.bind(a.state)
.bind(a.chained_next_activity)
.fetch_one(&mut *conn)
.await
}
pub async fn find_activity(
conn: &mut PgConnection,
id: Uuid,
) -> Result<Option<ActivityRow>, sqlx::Error> {
let row = sqlx::query(
r#"SELECT id, res_model, res_id, activity_type_id, summary, note, user_id,
date_deadline, active, chained_next_activity
FROM messaging.mail_activities WHERE id = $1"#,
)
.bind(id)
.fetch_optional(&mut *conn)
.await?;
Ok(row.map(|r| ActivityRow {
id: r.get("id"),
res_model: r.get("res_model"),
res_id: r.get("res_id"),
activity_type_id: r.get("activity_type_id"),
summary: r.get("summary"),
note: r.get("note"),
user_id: r.get("user_id"),
date_deadline: r.get("date_deadline"),
active: r.get("active"),
chained_next_activity: r.get("chained_next_activity"),
}))
}
pub async fn find_activity_type(
conn: &mut PgConnection,
id: Uuid,
) -> Result<Option<ActivityTypeRow>, sqlx::Error> {
let row = sqlx::query(
r#"SELECT id, chaining_type::text AS chaining_type, delay_count,
delay_unit::text AS delay_unit, delay_from::text AS delay_from,
default_user_id, active
FROM messaging.mail_activity_types WHERE id = $1"#,
)
.bind(id)
.fetch_optional(&mut *conn)
.await?;
Ok(row.map(|r| ActivityTypeRow {
id: r.get("id"),
chaining_type: r.get("chaining_type"),
delay_count: r.get("delay_count"),
delay_unit: r.get("delay_unit"),
delay_from: r.get("delay_from"),
default_user_id: r.get("default_user_id"),
active: r.get("active"),
}))
}
pub async fn archive_done(conn: &mut PgConnection, id: Uuid) -> Result<bool, sqlx::Error> {
let updated = sqlx::query_scalar::<_, Uuid>(
r#"UPDATE messaging.mail_activities
SET active = FALSE, state = 'done'::mail_activity_state
WHERE id = $1 AND active = TRUE
RETURNING id"#,
)
.bind(id)
.fetch_optional(&mut *conn)
.await?;
Ok(updated.is_some())
}
pub async fn project_state(
conn: &mut PgConnection,
id: Uuid,
today: NaiveDate,
) -> Result<(), sqlx::Error> {
sqlx::query(
r#"UPDATE messaging.mail_activities
SET state = CASE
WHEN date_deadline < $2 THEN 'overdue'::mail_activity_state
WHEN date_deadline = $2 THEN 'today'::mail_activity_state
ELSE 'planned'::mail_activity_state
END
WHERE id = $1 AND active = TRUE"#,
)
.bind(id)
.bind(today)
.execute(&mut *conn)
.await?;
Ok(())
}
pub async fn list_plan_templates(
conn: &mut PgConnection,
plan_id: Uuid,
) -> Result<Vec<PlanTemplateRow>, sqlx::Error> {
let rows = sqlx::query(
r#"SELECT id, activity_type_id, summary, note,
delay_count, delay_unit::text AS delay_unit, user_id
FROM messaging.mail_activity_plan_templates
WHERE plan_id = $1 AND (metadata->>'deleted_at') IS NULL
ORDER BY id"#,
)
.bind(plan_id)
.fetch_all(&mut *conn)
.await?;
Ok(rows
.iter()
.map(|r| PlanTemplateRow {
id: r.get("id"),
activity_type_id: r.get("activity_type_id"),
summary: r.get("summary"),
note: r.get("note"),
delay_count: r.get("delay_count"),
delay_unit: r.get("delay_unit"),
user_id: r.get("user_id"),
})
.collect())
}
}