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
WorkingTimeCalculatorconta i minuti nell'orario diparams['workingHours'](default lun–ven 9–18) divisi per giorno, e per ogni assegnatario nasce unTaskWorkLogconsource = timer; timer/rolloveralle 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¶
- Sceglie lo stato (quello d'ingresso se non indicato) e verifica che sia della bacheca.
- Tiene solo gli assegnatari che sono membri del team.
- In una transazione salva la task, gli assegnatari, lo stato "non letta" per i destinatari e la ricorrenza.
- Dopo il commit invia il webhook
task.createde 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_numberdaBoardKeyService); 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.deletedsulla bacheca di partenza,task.createdsu 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
donelo 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.createdper 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.
attachSubtaskaccetta 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.