feat: add project budgets and rounding override columns
This commit is contained in:
@@ -25,6 +25,9 @@ pub struct Project {
|
||||
pub hourly_rate: f64,
|
||||
pub color: String,
|
||||
pub archived: bool,
|
||||
pub budget_hours: Option<f64>,
|
||||
pub budget_amount: Option<f64>,
|
||||
pub rounding_override: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@@ -115,7 +118,9 @@ pub fn delete_client(state: State<AppState>, id: i64) -> Result<(), String> {
|
||||
#[tauri::command]
|
||||
pub fn get_projects(state: State<AppState>) -> Result<Vec<Project>, String> {
|
||||
let conn = state.db.lock().map_err(|e| e.to_string())?;
|
||||
let mut stmt = conn.prepare("SELECT id, client_id, name, hourly_rate, color, archived FROM projects ORDER BY name").map_err(|e| e.to_string())?;
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT id, client_id, name, hourly_rate, color, archived, budget_hours, budget_amount, rounding_override FROM projects ORDER BY name"
|
||||
).map_err(|e| e.to_string())?;
|
||||
let projects = stmt.query_map([], |row| {
|
||||
Ok(Project {
|
||||
id: Some(row.get(0)?),
|
||||
@@ -124,6 +129,9 @@ pub fn get_projects(state: State<AppState>) -> Result<Vec<Project>, String> {
|
||||
hourly_rate: row.get(3)?,
|
||||
color: row.get(4)?,
|
||||
archived: row.get::<_, i32>(5)? != 0,
|
||||
budget_hours: row.get(6)?,
|
||||
budget_amount: row.get(7)?,
|
||||
rounding_override: row.get(8)?,
|
||||
})
|
||||
}).map_err(|e| e.to_string())?;
|
||||
projects.collect::<Result<Vec<_>, _>>().map_err(|e| e.to_string())
|
||||
@@ -133,8 +141,8 @@ pub fn get_projects(state: State<AppState>) -> Result<Vec<Project>, String> {
|
||||
pub fn create_project(state: State<AppState>, project: Project) -> Result<i64, String> {
|
||||
let conn = state.db.lock().map_err(|e| e.to_string())?;
|
||||
conn.execute(
|
||||
"INSERT INTO projects (client_id, name, hourly_rate, color, archived) VALUES (?1, ?2, ?3, ?4, ?5)",
|
||||
params![project.client_id, project.name, project.hourly_rate, project.color, project.archived as i32],
|
||||
"INSERT INTO projects (client_id, name, hourly_rate, color, archived, budget_hours, budget_amount, rounding_override) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
|
||||
params![project.client_id, project.name, project.hourly_rate, project.color, project.archived as i32, project.budget_hours, project.budget_amount, project.rounding_override],
|
||||
).map_err(|e| e.to_string())?;
|
||||
Ok(conn.last_insert_rowid())
|
||||
}
|
||||
@@ -143,8 +151,8 @@ pub fn create_project(state: State<AppState>, project: Project) -> Result<i64, S
|
||||
pub fn update_project(state: State<AppState>, project: Project) -> Result<(), String> {
|
||||
let conn = state.db.lock().map_err(|e| e.to_string())?;
|
||||
conn.execute(
|
||||
"UPDATE projects SET client_id = ?1, name = ?2, hourly_rate = ?3, color = ?4, archived = ?5 WHERE id = ?6",
|
||||
params![project.client_id, project.name, project.hourly_rate, project.color, project.archived as i32, project.id],
|
||||
"UPDATE projects SET client_id = ?1, name = ?2, hourly_rate = ?3, color = ?4, archived = ?5, budget_hours = ?6, budget_amount = ?7, rounding_override = ?8 WHERE id = ?9",
|
||||
params![project.client_id, project.name, project.hourly_rate, project.color, project.archived as i32, project.budget_hours, project.budget_amount, project.rounding_override, project.id],
|
||||
).map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -416,7 +424,7 @@ pub fn export_data(state: State<AppState>) -> Result<serde_json::Value, String>
|
||||
};
|
||||
|
||||
let projects = {
|
||||
let mut stmt = conn.prepare("SELECT id, client_id, name, hourly_rate, color, archived FROM projects").map_err(|e| e.to_string())?;
|
||||
let mut stmt = conn.prepare("SELECT id, client_id, name, hourly_rate, color, archived, budget_hours, budget_amount, rounding_override FROM projects").map_err(|e| e.to_string())?;
|
||||
let rows: Vec<serde_json::Value> = stmt.query_map([], |row| {
|
||||
Ok(serde_json::json!({
|
||||
"id": row.get::<_, i64>(0)?,
|
||||
@@ -424,7 +432,10 @@ pub fn export_data(state: State<AppState>) -> Result<serde_json::Value, String>
|
||||
"name": row.get::<_, String>(2)?,
|
||||
"hourly_rate": row.get::<_, f64>(3)?,
|
||||
"color": row.get::<_, String>(4)?,
|
||||
"archived": row.get::<_, i32>(5)? != 0
|
||||
"archived": row.get::<_, i32>(5)? != 0,
|
||||
"budget_hours": row.get::<_, Option<f64>>(6)?,
|
||||
"budget_amount": row.get::<_, Option<f64>>(7)?,
|
||||
"rounding_override": row.get::<_, Option<i32>>(8)?
|
||||
}))
|
||||
}).map_err(|e| e.to_string())?.collect::<Result<Vec<_>, _>>().map_err(|e| e.to_string())?;
|
||||
rows
|
||||
@@ -637,3 +648,32 @@ pub fn set_entry_tags(state: State<AppState>, entry_id: i64, tag_ids: Vec<i64>)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_project_budget_status(state: State<AppState>, project_id: i64) -> Result<serde_json::Value, String> {
|
||||
let conn = state.db.lock().map_err(|e| e.to_string())?;
|
||||
|
||||
let total_seconds: i64 = conn.query_row(
|
||||
"SELECT COALESCE(SUM(duration), 0) FROM time_entries WHERE project_id = ?1",
|
||||
params![project_id],
|
||||
|row| row.get(0),
|
||||
).map_err(|e| e.to_string())?;
|
||||
|
||||
let project_row: (Option<f64>, Option<f64>, f64) = conn.query_row(
|
||||
"SELECT budget_hours, budget_amount, hourly_rate FROM projects WHERE id = ?1",
|
||||
params![project_id],
|
||||
|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),
|
||||
).map_err(|e| e.to_string())?;
|
||||
|
||||
let hours_used = total_seconds as f64 / 3600.0;
|
||||
let amount_used = hours_used * project_row.2;
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"hours_used": hours_used,
|
||||
"amount_used": amount_used,
|
||||
"budget_hours": project_row.0,
|
||||
"budget_amount": project_row.1,
|
||||
"percent_hours": project_row.0.map(|b| if b > 0.0 { (hours_used / b) * 100.0 } else { 0.0 }),
|
||||
"percent_amount": project_row.1.map(|b| if b > 0.0 { (amount_used / b) * 100.0 } else { 0.0 })
|
||||
}))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user