Vai al contenuto

Task

La task è il cuore di GesTask. Sta in uno stato di una bacheca, può avere più assegnatari, sotto-task, checklist, commenti, allegati, relazioni, promemoria, ore lavorate e una regola di ricorrenza.

Il modello Task

Campo Significato
list_id Lo stato in cui si trova: è la fase del lavoro.
status Solo todo o done: dice se la task è conclusa, non in che fase è.
task_number Numero progressivo nella bacheca; con la sigla forma la chiave (getTaskKey(), es. OPS-142).
priority low, normal, high, urgent.
starts_at, ends_at Inizio e fine. ends_at è anche la scadenza; l'API la espone pure come due_at, in sola lettura.
completed_at Quando è stata conclusa.
percent_done Avanzamento 0–100, calcolato dallo stato.
estimated_minutes Stima, fino a 60000 minuti.
parent_id Task principale, se è una sotto-task.
assigned_to Vecchio assegnatario singolo, ancora letto dai filtri; gli assegnatari veri sono in task_assignees.
is_template La task possiede una regola di ricorrenza (non c'entra con i modelli di bacheca).
recurrence_id Sulle task generate: la regola che le ha create.

Le parti della task:

Modello Tabella Note
TaskAssignee task_assignees Assegnatari; solo membri del team (TaskAssigneeService).
TaskTag task_tags Etichette globali o della stessa bacheca.
TaskComment, TaskCommentMention task_comments, task_comment_mentions Un livello di risposte; menzioni di utenti, team, bacheche e task.
TaskAttachment task_attachments File in storage/ (vedi Sicurezza).
TaskChecklistItem task_checklist_items Voci da spuntare, con chi e quando.
TaskRelation task_relations relates_to, blocks, blocked_by, duplicates, duplicated_by.
TaskReminder task_reminders Promemoria a data fissa o relativi a inizio/fine.
TaskWorkLog task_work_logs Ore lavorate: 1–1440 minuti per giorno, con nota; source è manual o timer.
TaskTimerSession task_timer_sessions Tratti del conteggio automatico delle ore (vedi Conteggio ore).
TaskHistory task_history Cronologia delle modifiche.
TaskRecurrence task_recurrence Regola di ricorrenza.
TaskUnreadStatus task_unread_statuses Una riga = task non letta da quell'utente.
Subscription subscriptions Iscrizione agli aggiornamenti della task.

Task, allegati, relazioni e promemoria vanno nel cestino; commenti, voci di checklist, ore e cronologia si eliminano davvero.

Behavior

I behavior di Task girano in quest'ordine, e insieme fanno sì che basti salvare la task perché tutto il resto si aggiorni:

Behavior Quando Cosa fa
UuidBehavior prima della validazione assegna lo uuid
TaskNumberBehavior prima dell'inserimento prende il prossimo numero della bacheca; se fallisce la task resta senza numero, non fallisce
TaskStatusSyncBehavior cambio di stato (list_id) entrando in uno stato conclusivo la task diventa done; uscendone verso uno di lavoro torna todo; fra due stati di lavoro non cambia
TaskHistoryBehavior modifica, cestino, ripristino scrive una riga di cronologia per ogni campo seguito
TaskTimerBehavior inserimento, cambio di stato, cestino avvia o ferma il conteggio delle ore (vedi Conteggio ore)
TaskProgressBehavior cambio di stato o di status ricalcola percent_done
RecurringFollowUpBehavior la task diventa done se fa parte di una ricorrenza, genera subito la successiva
TaskWebhookBehavior spostamento, completamento, cestino, ripristino invia task.moved, task.completed, task.deleted, task.restored

task.created e task.updated non partono dai behavior ma da chi crea o modifica (TaskWorkflow, API, link pubblici), perché solo lui sa quando la modifica è completa.

Aggiornamenti in blocco

Gli updateAll di BoardListService e TaskProgressService non passano dai behavior: non scrivono cronologia e non inviano webhook. Il cestino di una bacheca e lo stato che smette di conteggiare fermano il conteggio delle ore a mano, con TaskTimerService::stopForTasks().

Avanzamento

TaskProgressService::syncTask:

  • task conclusa, o in uno stato conclusivo: 100;
  • stato escluso dall'avanzamento: resta il valore attuale;
  • altrimenti la posizione dello stato fra quelli non esclusi: round(100 * indice / (numero - 1)).

Sotto-task e checklist hanno un loro conteggio (subtaskProgress(), checklistProgress()) ma non toccano percent_done.

Cronologia

TaskHistoryBehavior segue titolo, descrizione, stato, status, priorità, assegnatario, date, avanzamento, stima, task principale, completamento e cestino. I valori si salvano già leggibili (nome dello stato, nome utente, durata formattata, descrizione tagliata a 500 caratteri). La creazione non si registra. Le voci con field = timer le scrive TaskTimerService: avvio, pausa, ripresa e stop del conteggio delle ore, con i minuti conteggiati.

Conteggio ore

Uno stato con counts_time conteggia le ore delle task che contiene (TaskTimerService, agganciato agli spostamenti da TaskTimerBehavior):

  • entrare in uno stato che conteggia apre un tratto (task_timer_sessions); uscire verso uno che non conteggia lo chiude; fra due stati che conteggiano il tratto continua;
  • la pausa chiude il tratto e la ripresa ne apre un altro: lo stato si ricava, tratto aperto = in corso, stato che conteggia senza tratto aperto = in pausa;
  • alla chiusura WorkingTimeCalculator conta i minuti nell'orario di params['workingHours'] (default lun–ven 9–18) divisi per giorno, e per ogni assegnatario nasce un TaskWorkLog con source = timer;
  • timer/rollover alle 00:05 chiude e riapre i tratti aperti, così le ore di ieri entrano nei report anche per le task che restano in corso;
  • uno stato conclusivo non può conteggiare; una task ripristinata dal cestino o uno stato che inizia a conteggiare non avviano nulla: le task risultano in pausa.

Pausa e ripresa: TaskTimerController nel frontend e POST /v1/tasks/{uuid}/timer/pause|resume nell'API, con il permesso task.move.

Il flusso: TaskWorkflow

common/services/TaskWorkflow è l'ingresso unico per frontend e API.

Creazione

  1. Sceglie lo stato (quello d'ingresso se non indicato) e verifica che sia della bacheca.
  2. Tiene solo gli assegnatari che sono membri del team.
  3. In una transazione salva la task, gli assegnatari, lo stato "non letta" per i destinatari e la ricorrenza.
  4. Dopo il commit invia il webhook task.created e le notifiche.

Modifica

update() controlla che la task non sia bloccata, aggiorna i campi, gestisce il cambio di stato (stessa bacheca, permesso task.move) e di status (tramite TaskCompletionService), assegnatari e ricorrenza. Poi invia task.updated e notifica solo i nuovi assegnatari, lo spostamento o il cambio di stato e i cambi di data.

Completamento e riapertura

complete() imposta done e sposta la task nello stato conclusivo predefinito, se la bacheca ne ha uno. reopen() la riporta todo e, se era in uno stato conclusivo, nel primo stato di lavoro.

Spostamento nel Kanban

move() riceve stato di destinazione e ordine delle task: rifiuta spostamenti fra team diversi e task bloccate, riscrive le posizioni in una transazione e lascia ai behavior stato, avanzamento, cronologia, ricorrenza e webhook. La risposta AJAX restituisce il nuovo avanzamento.

Trasferimento in un'altra bacheca

transfer() porta la task, con le sue sotto-task, in un'altra bacheca, anche di un altro team: nella scheda task è il riquadro «Trasferisci», nell'API POST /v1/tasks/{uuid}/transfer; per più task insieme, l'azione di massa transfer di TaskBulkOperation, che chiama transfer() una task per volta e salta quelle bloccate e le sotto-task scelte senza la loro task. Serve task.move nel team di partenza e task.create in quello di arrivo.

  • Ogni task prende la sigla successiva della bacheca di arrivo (task_number da BoardKeyService); la vecchia non si riusa.
  • Va nella colonna indicata o in quella d'ingresso; stato e conteggio ore li aggiornano i behavior come per uno spostamento.
  • Restano commenti, checklist, allegati, ore e cronologia. Si tolgono le etichette della bacheca di partenza e, se il team cambia, gli assegnatari che non ne sono membri, le relazioni e i collegamenti alle riunioni.
  • Una sotto-task da sola non si trasferisce (segue la sua task principale), e nemmeno una task bloccata.
  • Webhook: task.deleted sulla bacheca di partenza, task.created su quella di arrivo; i follower ricevono una notifica di cambio stato.

TaskNumberBehavior assegna il numero solo alla creazione: il trasferimento è l'unico altro punto in cui cambia.

Duplicazione

duplicate() crea una copia della task, con le sue sotto-task, nella stessa bacheca o in un'altra, anche di un altro team; l'originale non cambia. Nella scheda task è il riquadro «Duplica», che accetta più bacheche insieme; nell'API POST /v1/tasks/{uuid}/duplicate; per più task insieme, l'azione di massa duplicate di TaskBulkOperation, con boards (fino a 20), che crea una copia per task e per bacheca e restituisce in applied le copie. Serve board.view nel team di partenza e task.create in ogni bacheca di arrivo.

  • Nella stessa bacheca la copia nasce nello stato dell'originale e le sotto-task nel loro; altrove tutte nella colonna indicata o in quella d'ingresso. Lo stato done lo decide il behavior, come per ogni task.
  • Si copiano titolo, descrizione, priorità, date, stima, voci di checklist (da spuntare), gli assegnatari membri del team di arrivo e le etichette globali o della bacheca di arrivo.
  • Restano all'originale commenti, allegati, ore, cronologia, relazioni, promemoria, ricorrenza e collegamenti alle riunioni; la copia non è mai un modello.
  • Una sotto-task da sola non si duplica; una task bloccata sì, perché non viene toccata.
  • Webhook task.created per la copia e per ogni sotto-task copiata; la notifica di nuova task parte solo per la copia principale.

Eliminazione

delete() sposta la task nel cestino. Eliminare task è permesso solo al super amministratore e a chiunque nel proprio spazio personale (TrashPolicy); il permesso RBAC task.delete esiste ma non viene mai concesso.

Task bloccate

Una task è bloccata se ha una relazione blocked_by o se un'altra task ha una relazione blocks verso di lei (le task nel cestino non contano). Una task completata non blocca più: la relazione resta e torna a valere se la task viene riaperta. Con più task bloccanti il blocco cade solo quando sono tutte completate. Il blocco non è transitivo: conta solo lo stato delle task che bloccano direttamente. Ogni operazione di modifica passa da TaskAccess::ensureActionable(), che risponde 403 finché il blocco c'è. L'API espone is_blocked.

Sotto-task e checklist

  • Le sotto-task hanno un solo livello: crearne una sotto una sotto-task la aggancia alla task principale. attachSubtask accetta solo task della stessa bacheca, senza figli propri.
  • Una voce di checklist si può promuovere a sotto-task (TaskParts::promoteChecklistItem).

common/services/TaskParts gestisce tutte le parti (etichette, iscrizioni, checklist, relazioni, promemoria, ore, allegati, commenti) e controlla per prima cosa che la task non sia bloccata.

Commenti e menzioni

TaskCommentPublisher salva le menzioni, invia il webhook comment.created e notifica: le persone e i team menzionati ricevono comment_mention, assegnatari e iscritti non menzionati ricevono task_comment. TaskCommentMentionService riconosce al massimo 20 menzioni per commento e solo di entità visibili a chi scrive.

Il corpo è Markdown: renderBody toglie i riferimenti #task[…] e #board[…], passa il resto a TaskMarkdown::comment() (flavor gfm-comment, dove un a capo semplice resta un a capo, e HtmlPurifier come per le descrizioni) e rimette al loro posto i collegamenti. Notifiche, email e risultati di ricerca mostrano il testo così come è scritto.

Ricorrenze

Una task con una regola di ricorrenza (is_template = 1) genera istanze.

  • Regola (TaskRecurrence): frequenza giornaliera, settimanale, mensile o annuale, intervallo 1–99, giorni della settimana, giorno del mese, ora, scostamento della scadenza, copia di assegnatari ed etichette, fine per data o per numero di occorrenze.
  • Calcolo (RecurringTaskSchedule): prossima occorrenza, descrizione in italiano, esaurimento della regola.
  • Generazione (RecurringTaskGenerator):
    • php yii task/generate-recurring, ogni 15 minuti, genera le occorrenze scadute;
    • si tiene una sola istanza aperta per volta: finché l'ultima non è conclusa non se ne crea un'altra;
    • le occorrenze arretrate di oltre 30 giorni si saltano;
    • l'istanza nasce nel primo stato di lavoro, con inizio uguale all'occorrenza e fine spostata di due_offset_days (o della durata della task modello);
    • concludere un'istanza genera subito la successiva (RecurringFollowUpBehavior);
    • "Genera ora" dalla scheda forza un'istanza se non ce n'è una aperta.

Letta e non letta

Una task nuova o modificata diventa "non letta" per i destinatari: tutto il team se non ha assegnatari, altrimenti autore, assegnatari e iscritti. Aprirne la scheda la segna letta.