Dev to 4.0.11
[usit-rt.git] / docs / customizing / lifecycles.pod
1 =head1 Ticket Lifecycles
2
3 By default, RT comes with ticket statuses that work for many types
4 of workflows: new, open, stalled, resolved, rejected, and deleted.
5 But there can be any number of workflows where these status values
6 don't completely fit. RT allows you to add new custom status values and
7 define their behavior with a feature called Lifecycles.
8
9 This guide demonstrates lifecycles using an order fulfillment
10 system as a real-world example. You can find full lifecycles
11 documentation in L<RT_Config/Lifecycles>.
12
13 As with all RT custom configuration, if you are customizing the RT
14 lifecycle, make your changes in your C<RT_SiteConfig.pm> file, not
15 directly in C<RT_Config.pm>. If you are adding a new lifecycle, you can
16 add a new entry with:
17
18     Set(%Lifecycles, my_new_lifecycle => { ... } );
19
20 The detailed configuration options are discussed below. Once you add it
21 and restart the server, the new lifecycle will be available on the
22 queue configuration page.
23
24 If you want to modify the default lifecycle, you can copy it from
25 C<RT_Config.pm>, paste it into C<RT_SiteConfig.pm> and make your
26 changes.
27
28 =head1 Order Processing Example
29
30 To show how you might use custom lifecycles, we're going to configure
31 an RT lifecycle to process orders of some sort. In our order example,
32 each ticket in the queue is considered a separate order and the orders
33 have the following statuses:
34
35 =over
36
37 =item pending
38
39 The order just came in untouched, pending purchase validation
40
41 =item processing
42
43 The order is being looked at for transaction processing
44
45 =item delivery
46
47 The order is out for delivery
48
49 =item delivered
50
51 The order was successfully delivered to its destination
52
53 =item refunded
54
55 The order was delivered but subsequently refunded
56
57 =item declined
58
59 There was an error in the process validation and the order was denied purchase
60
61 =back
62
63 In this particular example, the only status an order can start with is
64 'pending.'  When a process coordinator chooses to take this order, it
65 goes into processing. The order can then either be delivered or denied
66 processing. Once denied, the lifecycle for that order ends. If it is
67 delivered, the order can still be refunded.
68
69 The following sections walk through each part of the configuration.
70 You can find the full configuration at the end in case you want to
71 see the exact syntax or use it to experiment with.
72
73 =head2 Defining Status Values
74
75 Every queue has a lifecycle assigned to it. Without changing any
76 configuration, you are given two lifecycles to choose from: "default"
77 and "approvals." The approvals lifecycle is used by the internal
78 approvals queue, and should not be changed or used by other queues. Do
79 not modify the approvals lifecycle unless you fully understand how RT
80 approvals work.
81
82 =for html <img alt="Lifecycle choices" src="../images/lifecycle-choices.png">
83
84 =for :text [Lifecycle choices F<docs/images/lifecycle-choices.png>]
85
86 =for :man [Lifecycle choices F<docs/images/lifecycle-choices.png>]
87
88 In RT 4.0, the C<@ActiveStatus> and C<@InactiveStatus> configurations
89 which were previously available are gone. The logic defined by those
90 options is now a subset of RT's lifecycle features, as described here.
91
92 A ticket naturally has three states: initial (I<new>), active (I<open> and
93 I<stalled>), and inactive (I<resolved>, I<rejected>, and I<deleted>). These
94 default settings look like this in the C<RT_Config.pm> file:
95
96     default => {
97         initial         => [ 'new' ],
98         active          => [ 'open', 'stalled' ],
99         inactive        => [ 'resolved', 'rejected', 'deleted' ],
100
101 The initial state is the default starting place for new tickets, although
102 you can create tickets with other statuses. Initial is generally used
103 to acknowledge that a request has been made, but not yet acted on. RT
104 sets the Started date on a ticket when it is moved out of the initial state.
105
106 Active tickets are currently being worked on, inactive tickets have reached
107 some final state. By default, inactive tickets don't show up in search
108 results. The AutoOpen action sets a ticket's status to the first active
109 status. You can find more details in L<RT_Config/"Lifecycle definitions">.
110
111 Now we want to set up some statuses appropriate for order fulfillment,
112 so we create a new top-level key called C<orders> and add our new status
113 values.
114
115     Set( %Lifecycles, orders => {
116              initial  => [ 'pending' ],
117              active   => [ 'processing', 'delivery' ],
118              inactive => [ 'delivered', 'returned', 'declined', 'deleted' ],
119              # ...,
120     });
121
122 We still use the initial, active and inactive categories, but we are
123 able to define status values that are appropriate for the workflow
124 we want to create. This should make the system more intuitive for users.
125
126 =head2 Transitions
127
128 The typical lifecycle follows the path initial -> active -> inactive.
129 Obviously the path of a ticket can get more complicated than this, which
130 is where transitions come into play.
131
132 Transitions manage the flow of a ticket from status to status. This
133 section of the configuration has keys, which are the current status,
134 and values that define which other statuses the ticket can transition
135 to. Here are the transitions we define for our order process.
136
137     Set( %Lifecycles, orders => {
138         # ...,
139         transitions => {
140             ''          => [qw(pending processing declined)],
141             pending     => [qw(processing declined deleted)],
142             processing  => [qw(pending declined delivery delivered deleted)],
143             delivery    => [qw(pending delivered returned deleted)],
144             delivered   => [qw(pending returned deleted)],
145             returned    => [qw(pending delivery deleted)],
146             deleted     => [qw(pending processing delivered delivery returned)],
147         },
148         # ...,
149     });
150
151 If a ticket is in the delivered status, it doesn't make sense for it to
152 transition to processing or declined since the customer already has the
153 order. However, it can transition to returned since they could send it back.
154 The configuration above defines this for RT.
155
156 The C<''> entry defines the valid statuses when a ticket is created.
157
158 Deleted is a special status in RT that allows you to remove a ticket from
159 active use. You may need to do this if a ticket is created by mistake, or
160 a duplicate is created. Once deleted, a ticket will never show up in search
161 results. As you can see, the system will allow you to
162 transition to deleted from any status.
163
164 =head2 Rights and Access Control
165
166 Your workflow may have several people working on tickets at different
167 steps, and for some you may want to make sure only certain users
168 can perform certain actions. For example, the company may have a rule
169 that only the quality assurance team is allowed to approve (or decline)
170 an order for delivery.
171
172 You can apply labels to transitions and assign rights to them to allow
173 you to apply this sort of access control. This is done with a rights
174 entry:
175
176     Set( %Lifecycles, orders => {
177         # ...,
178         rights => {
179             '* -> declined' => 'DeclineOrder',
180             '* -> delivery' => 'ApproveOrder',
181         },
182         # ...,
183     });
184
185 This configuration tells RT to require the right DeclineOrder for a
186 transition from any status (C<*>) to C<declined>. The ApproveOrder
187 right is similar, but for C<delivery>. These rights take the place of
188 the standard ModifyTicket right, not in addition to it, so keep that
189 in mind when creating  and assigning new rights.
190
191 Once these rights are configured and loaded (by restarting the web
192 server), they can be assigned in the web UI to groups, queues, and users.
193 The rights show up on the rights pages in a Status tab alongside the
194 standard RT rights tabs.
195
196 =for html <img alt="Lifecycle group rights" src="../images/global-lifecycle-group-rights.png">
197
198 =for :text [Lifecycle group rights F<docs/images/global-lifecycle-group-rights.png>]
199
200 =for :man [Lifecycle group rights F<docs/images/global-lifecycle-group-rights.png>]
201
202 After a status transition right is granted, users with the right will see
203 the status in the drop-down, and possibly any related actions (see
204 L</Actions>).
205
206 =head2 Default Status
207
208 There are interfaces to RT from which it isn't possible to define a status,
209 like sending an email to create a ticket, but tickets
210 require a status. To handle these cases, you can set
211 default status values for RT to use when the user doesn't explicitly set
212 a value.
213
214 Looking at the defaults section in the standard RT configuration,
215 you can see the events for which you can define a default status.
216 For example, 'on_create' => 'new' automatically gives newly created tickets
217 a C<new> status when the requestor doesn't supply a status. We can do the same
218 for our process.
219
220     Set( %Lifecycles, orders => {
221         defaults => {
222             on_create => 'pending',
223         },
224         # ...,
225     });
226
227 Only a small number of defaults are needed because in practice there are
228 relatively few cases where a ticket will find itself without a status or
229 in an ambiguous state.
230
231 =head2 Actions
232
233 To customize how transitions are presented in RT, lifecycles have an
234 C<actions> section where you can customize how an action (e.g. changing
235 status from new -> open) looks and functions. You can customize the action's
236 label, which is how it appears to users, and the type of update, either comment
237 or reply. As an example, in the default RT configuration the action
238 "new -> open" has the default label "Open it" and an update value of C<Respond>.
239
240 Using the lifecycles configuration, you can change the label to anything you
241 like. You can set the update option to C<Comment> or C<Respond>, which tells RT
242 to process the action as a comment (not sent to requestors) or a reply (sent
243 to requestors).
244
245 This part of the lifecycles configuration replaces the previous
246 C<$ResolveDefaultUpdateType> configuration value. To mimic that option, set
247 the update type to C<Comment> for all transitions to C<resolved>.
248
249 Here is an example of a change we might make for our order process:
250
251     Set( %Lifecycles, orders => {
252         # ...,
253         actions => [
254             'pending -> processing' => {
255                 label  => 'Open For Processing',
256                 update => 'Comment',
257             },
258             'pending -> declined' => {
259                 label  => 'Decline',
260                 update => 'Respond',
261             },
262             # ...
263         ],
264         # ...
265     });
266
267 Alternatively, supplying no update type results in a "quick"
268 action that changes the status immediately without going through the
269 ticket update page.  RT's default "Delete" action is a "quick" action,
270 for example:
271
272     # from the RT "default" lifecycle
273     'new -> deleted'   => {
274         label  => 'Delete',
275     },
276
277 If the transition has an associated right, it must be granted for a user to
278 see the action. For example, if we give a group the DeclineOrder right as
279 shown in the earlier example, members of that group will see a Decline option
280 in their Actions menu if a ticket has a pending status. The
281 L</"Full Configuration"> at the end shows other action entries that
282 make the Decline option available in more cases.
283
284 =for html <img alt="Action menu decline" src="../images/action-decline.png">
285
286 =for :text [Action menu decline F<docs/images/action-decline.png>]
287
288 =for :man [Action menu decline F<docs/images/action-decline.png>]
289
290 =head2 Mapping Between Queues
291
292 As we've demonstrated, each queue can have its own custom lifecycle, but
293 in RT you sometimes want to move a ticket from one queue to another.
294 A ticket will have a status in a given queue, but that status may not
295 exist in another queue you want to move the ticket to, or it may exist
296 but mean something different. To allow tickets to move between queues with
297 different lifecycles, RT needs to know how to set the status appropriately.
298
299 The lifecycle configuration has a C<__maps__> entry to allow you to
300 specify the mappings you want between different queues. Sometimes statuses
301 between queues don't or can't match perfectly, but if you need to move
302 tickets between those queues, it's important that you provide a complete
303 mapping, defining the most sensible mapping you can.
304
305 If you don't provide a mapping, users will see an error when they try to
306 move a ticket between queues with different lifecycles but no mapping.
307
308     Set( %Lifecycles, orders => {
309         # ...,
310         __maps__ => {
311             'default -> orders' => {
312                 'new'  => 'pending',
313                 'open' => 'processing',
314                 # ...,
315             },
316             'orders -> default' => {
317                 'pending'    => 'new',
318                 'processing' => 'open',
319                 # ...,
320             },
321             # ...,
322         },
323         # ...,
324     });
325
326 In the example above, we first define mappings between the default queue and
327 our new orders queue. The second block defines the reverse for tickets that
328 might be moved from the orders queue to a queue that uses the default lifecycle.
329
330 =head2 Full Configuration
331
332 Here is the full configuration if you want to add it to your RT instance
333 to experiment.
334
335     Set(%Lifecycles,
336
337         # 'orders' shows up as a lifecycle choice when you create a new
338         # queue or modify an existing one
339         orders => {
340             # All the appropriate order statuses
341             initial         => [ 'pending' ],
342             active          => [ 'processing', 'delivery' ],
343             inactive        => [ 'delivered', 'returned', 'declined' ],
344
345             # Default order statuses for certain actions
346             defaults => {
347                 on_create => 'pending',
348             },
349
350             # Status change restrictions
351             transitions => {
352                 ''          => [qw(pending processing declined)],
353                 pending     => [qw(processing declined deleted)],
354                 processing  => [qw(pending declined delivery delivered deleted)],
355                 delivery    => [qw(pending delivered returned deleted)],
356                 delivered   => [qw(pending returned deleted)],
357                 returned    => [qw(pending delivery deleted)],
358                 deleted     => [qw(pending processing delivered delivery returned)],
359             },
360
361             # Rights for different actions
362             rights => {
363
364                 # These rights are in the default lifecycle
365                 '* -> deleted'  => 'DeleteTicket',
366                 '* -> *'        => 'ModifyTicket',
367
368                 # Maybe we want to create rights to keep QA rigid
369                 '* -> declined' => 'DeclineOrder',
370                 '* -> delivery' => 'ApproveOrder',
371             },
372
373             # Actions for the web UI
374             actions => [
375                 'pending -> processing' => {
376                     label  => 'Open For Processing',
377                     update => 'Comment',
378                 },
379                 'pending -> delivered' => {
380                     label  => 'Mark as being delivered',
381                     update => 'Comment',
382                 },
383                 'pending -> declined' => {
384                     label  => 'Decline',
385                     update => 'Respond',
386                 },
387                 'pending -> deleted' => {
388                     label  => 'Delete',
389                 },
390                 'processing -> declined' => {
391                     label  => 'Decline',
392                     update => 'Respond',
393                 },
394                 'processing -> delivery' => {
395                     label  => 'Out for delivery',
396                     update => 'Comment',
397                 },
398                 'delivery -> delivered' => {
399                     label  => 'Mark as delivered',
400                     update => 'Comment',
401                 },
402                 'delivery -> returned' => {
403                     label  => 'Returned to Manufacturer',
404                     update => 'Respond',
405                 },
406                 'delivered -> returned' => {
407                     label  => 'Returned to Manufacturer',
408                     update => 'Respond',
409                 },
410                 'returned -> delivery' => {
411                     label  => 'Re-deliver Order',
412                     update => 'Respond',
413                 },
414                 'deleted -> pending' => {
415                     label  => 'Undelete',
416                     update => 'Respond',
417                 },
418             ],
419         },
420
421         # Status mapping different different lifecycles
422         __maps__ => {
423             'default -> orders' => {
424                 'new'      => 'pending',
425                 'open'     => 'processing',
426                 'stalled'  => 'processing',
427                 'resolved' => 'delivered',
428                 'rejected' => 'declined',
429                 'deleted'  => 'deleted',
430             },
431             'orders -> default' => {
432                 'pending'    => 'new',
433                 'processing' => 'open',
434                 'delivered'  => 'resolved',
435                 'returned'   => 'open', # closest matching we have in 'default'
436                 'declined'   => 'rejected',
437                 'deleted'    => 'deleted',
438             },
439         },
440     );
441
442 Here is an example history of a ticket following this lifecycle:
443
444 =for html <img alt="Lifecycle history" src="../images/order-history-example.png">
445
446 =for :text [Lifecycle history F<docs/images/order-history-example.png>]
447
448 =for :man [Lifecycle history F<docs/images/order-history-example.png>]