feat(gamification): clear auto_completed on uncomplete
uncomplete_task now clears auto_completed alongside done / done_at on both the target row and the cascaded-parent reopen. Keeps the flag accurate so a later re-completion via a different path is counted correctly by the Phase 8 daily-count query. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -487,7 +487,7 @@ pub async fn uncomplete_task(pool: &SqlitePool, id: &str) -> Result<()> {
|
||||
.await
|
||||
.map_err(|e| KonError::StorageError(format!("Begin transaction failed: {e}")))?;
|
||||
|
||||
sqlx::query("UPDATE tasks SET done = 0, done_at = NULL WHERE id = ?")
|
||||
sqlx::query("UPDATE tasks SET done = 0, done_at = NULL, auto_completed = 0 WHERE id = ?")
|
||||
.bind(id)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
@@ -507,11 +507,14 @@ pub async fn uncomplete_task(pool: &SqlitePool, id: &str) -> Result<()> {
|
||||
.flatten();
|
||||
|
||||
if let Some(pid) = parent_id {
|
||||
sqlx::query("UPDATE tasks SET done = 0, done_at = NULL WHERE id = ? AND done = 1")
|
||||
.bind(&pid)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.map_err(|e| KonError::StorageError(format!("Reopen parent failed: {e}")))?;
|
||||
sqlx::query(
|
||||
"UPDATE tasks SET done = 0, done_at = NULL, auto_completed = 0 \
|
||||
WHERE id = ? AND done = 1",
|
||||
)
|
||||
.bind(&pid)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.map_err(|e| KonError::StorageError(format!("Reopen parent failed: {e}")))?;
|
||||
}
|
||||
|
||||
tx.commit()
|
||||
@@ -2077,4 +2080,48 @@ mod tests {
|
||||
assert_eq!(s1_auto, 0, "subtask completion is manual");
|
||||
assert_eq!(s2_auto, 0, "subtask completion is manual");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn uncomplete_clears_auto_completed_on_parent() {
|
||||
let pool = test_pool().await;
|
||||
|
||||
insert_task(
|
||||
&pool,
|
||||
"parent",
|
||||
"Parent task",
|
||||
"inbox",
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
insert_subtask(&pool, "s1", "Only step", "parent")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Cascade closes the parent with auto_completed = 1.
|
||||
complete_subtask_and_check_parent(&pool, "s1")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Uncompleting the subtask reopens the parent (existing invariant)
|
||||
// and must also clear auto_completed on the parent so a later
|
||||
// manual re-completion is counted cleanly.
|
||||
uncomplete_task(&pool, "s1").await.unwrap();
|
||||
|
||||
let parent_done: i64 = sqlx::query_scalar("SELECT done FROM tasks WHERE id = 'parent'")
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
let parent_auto: i64 =
|
||||
sqlx::query_scalar("SELECT auto_completed FROM tasks WHERE id = 'parent'")
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(parent_done, 0, "parent reopens when child is uncompleted");
|
||||
assert_eq!(parent_auto, 0, "auto_completed must clear on reopen");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user