use async_trait::async_trait;
use chrono::{Datelike, NaiveDate};
use rust_decimal::Decimal;
use sqlx::PgPool;
use uuid::Uuid;
#[derive(Debug, Clone)]
pub struct ApprovedOvertime {
pub approval_id: Uuid,
pub stretches: Vec<(NaiveDate, Decimal)>,
}
#[async_trait]
pub trait ApprovedTimesheetInputs: Send + Sync {
async fn approved_overtime(
&self,
employee_id: Uuid,
from: NaiveDate,
to: NaiveDate,
) -> Result<Option<ApprovedOvertime>, sqlx::Error>;
}
pub struct PoolApprovedTimesheet {
pool: PgPool,
}
impl PoolApprovedTimesheet {
pub fn new(pool: PgPool) -> Self {
Self { pool }
}
}
#[async_trait]
impl ApprovedTimesheetInputs for PoolApprovedTimesheet {
async fn approved_overtime(
&self,
employee_id: Uuid,
from: NaiveDate,
to: NaiveDate,
) -> Result<Option<ApprovedOvertime>, sqlx::Error> {
let approval = backbone_orm::company_scope::fetch_optional_scalar_scoped::<Uuid>(
&self.pool,
sqlx::query_scalar(
r#"SELECT a.id
FROM timesheet.timesheet_approvals a
WHERE a.employee_id = $1
AND ((a.year = $2 AND a.month >= $3) OR (a.year = $4 AND a.month <= $5))
AND a.status = 'approved'
ORDER BY a.year DESC, a.month DESC
LIMIT 1"#,
)
.bind(employee_id)
.bind(from.year())
.bind(from.month() as i32)
.bind(to.year())
.bind(to.month() as i32),
)
.await?;
let Some(approval_id) = approval else {
return Ok(None);
};
let stretches: Vec<(NaiveDate, Decimal)> = backbone_orm::company_scope::fetch_all_scoped(
&self.pool,
sqlx::query_as(
r#"SELECT t.date, LEAST(SUM(t.unit_amount), o.authed)
FROM timesheet.timesheets t
JOIN LATERAL (
SELECT COALESCE(SUM(r.hours_planned), 0) AS authed
FROM attendance.overtime_requests r
WHERE r.employee_id = t.employee_id
AND r.date = t.date
AND r.status = 'approved'
AND (r.metadata->>'deleted_at') IS NULL
) o ON true
WHERE t.employee_id = $1
AND t.date BETWEEN $2 AND $3
AND t.entry_type = 'overtime'
AND (t.metadata->>'deleted_at') IS NULL
GROUP BY t.date, o.authed
HAVING LEAST(SUM(t.unit_amount), o.authed) > 0
ORDER BY t.date"#,
)
.bind(employee_id)
.bind(from)
.bind(to),
)
.await?;
Ok(Some(ApprovedOvertime { approval_id, stretches }))
}
}