|  |  | @ -246,7 +246,7 @@ export class TaskService { | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     const existing = this.dbInstance |  |  |  |     const existing = this.dbInstance | 
			
		
	
		
		
			
				
					
					|  |  |  |       .prepare(` |  |  |  |       .prepare(` | 
			
		
	
		
		
			
				
					
					|  |  |  |         SELECT id, description, due_date, completed, completed_at, display_code |  |  |  |         SELECT id, description, due_date, completed, completed_at, display_code, group_id | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         FROM tasks |  |  |  |         FROM tasks | 
			
		
	
		
		
			
				
					
					|  |  |  |         WHERE id = ? |  |  |  |         WHERE id = ? | 
			
		
	
		
		
			
				
					
					|  |  |  |       `)
 |  |  |  |       `)
 | 
			
		
	
	
		
		
			
				
					|  |  | @ -413,7 +413,7 @@ export class TaskService { | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   // Soltar tarea (unassign): idempotente
 |  |  |  |   // Soltar tarea (unassign): idempotente
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   static unassignTask(taskId: number, userId: string): { |  |  |  |   static unassignTask(taskId: number, userId: string): { | 
			
		
	
		
		
			
				
					
					|  |  |  |     status: 'unassigned' | 'not_assigned' | 'not_found' | 'completed'; |  |  |  |     status: 'unassigned' | 'not_assigned' | 'not_found' | 'completed' | 'forbidden_personal'; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     task?: { id: number; description: string; due_date: string | null; display_code: number | null }; |  |  |  |     task?: { id: number; description: string; due_date: string | null; display_code: number | null }; | 
			
		
	
		
		
			
				
					
					|  |  |  |     now_unassigned?: boolean; // true si tras soltar no quedan asignados
 |  |  |  |     now_unassigned?: boolean; // true si tras soltar no quedan asignados
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   } { |  |  |  |   } { | 
			
		
	
	
		
		
			
				
					|  |  | @ -446,6 +446,30 @@ export class TaskService { | 
			
		
	
		
		
			
				
					
					|  |  |  |       }; |  |  |  |       }; | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     // Regla: no permitir soltar si es tarea personal y el usuario es el único asignatario
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     try { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       const stats = this.dbInstance.prepare(` | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         SELECT COUNT(*) AS cnt, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                SUM(CASE WHEN user_id = ? THEN 1 ELSE 0 END) AS mine | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         FROM task_assignments | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         WHERE task_id = ? | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       `).get(ensuredUser, taskId) as any;
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       const cnt = Number(stats?.cnt || 0); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       const mine = Number(stats?.mine || 0) > 0; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       if ((existing.group_id == null || existing.group_id === null) && cnt === 1 && mine) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         return { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |           status: 'forbidden_personal', | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |           task: { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             id: Number(existing.id), | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             description: String(existing.description || ''), | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             due_date: existing.due_date ? String(existing.due_date) : null, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             display_code: existing.display_code != null ? Number(existing.display_code) : null, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |           }, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |           now_unassigned: false, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         }; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } catch {} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     const deleteStmt = this.dbInstance.prepare(` |  |  |  |     const deleteStmt = this.dbInstance.prepare(` | 
			
		
	
		
		
			
				
					
					|  |  |  |       DELETE FROM task_assignments |  |  |  |       DELETE FROM task_assignments | 
			
		
	
		
		
			
				
					
					|  |  |  |       WHERE task_id = ? AND user_id = ? |  |  |  |       WHERE task_id = ? AND user_id = ? | 
			
		
	
	
		
		
			
				
					|  |  | 
 |