Splitting off transactions as new tickets

We had a request from one of our service desk folk recently, asking if there was a simple way they could take an email correspondence transaction for an existing ticket and turn it into a new ticket. This came about because some end users were asking for more than one thing at the same time, and if different teams had to deal with each request it either had to be done serially with tickets bouncing back and forth between different teams’ queues, or the service desk had to raise new tickets themselves.

So I put my hacking slippers on and in case this is useful to others, here’s what I’ve got so far. My solution is in two parts: a callback on the /Elements/ShowTransactionPage and a new local Mason page called ``/Ticket/TransactionToTicket’.

Here’s the callback, which I’ve put into /opt/rt5/local/html/Callbacks/SplitOffTransaction/Elements/ShowTransaction/Default on our RT5 setup:

$Transaction => undef
$Actions => undef
$Object => undef

if($Transaction->IsInbound() &&
   ($Transaction->Type() eq 'Create' || $Transaction->Type() eq 'Correspond')
   ) {
  push @$Actions, {
    path => '/Ticket/TransactionToTicket.html?id='.$Transaction->id,
    title => 'Split Off',

This is pretty simple - it just looks for the sort of transactions we want (Create or Correspond on an RT::Ticket type object) and then sneaks in an extra new action menu item to point to the new Mason page with the title “Split Off”.

The Mason page lives in /opt/rt5/local/html/Ticket/TransactionToTicket.html and is a bit longer:

<& /Elements/Header, 
    Title => $title,
    LinkRel => \%link_rel &>
<& /Elements/Tabs &>

<h1>Handling Transaction <% $Transaction->id %> in queue <% $Queue->Name() %></h1>

my $title;
my %link_rel;

# Check that we have a valid transaction to play with
unless ($ARGS{'id'}) {
    Abort('No transaction ID specified');
my $Transaction = RT::Transaction->new( $session{'CurrentUser'} );
if($Transaction->ObjectType ne "RT::Ticket" ||
   !($Transaction->Type() eq 'Create' || $Transaction->Type() eq 'Correspond') 
  ) {
    Abort('Transaction '.$ARGS{'id'}.' is not a valid ticket correspondance transaction');

# Get the old original ticket that this transaction came from, and the
# queue that ticket was in.
my $OldTicket = RT::Ticket->new( $session{'CurrentUser'} );
my $Queue = $OldTicket->QueueObj();

# Check that the user can create tickets in that queue.
unless ( $Queue->CurrentUserHasRight('CreateTicket') ) {
  Abort('You have no permission to create tickets in this queue.', Code => HTTP::Status::HTTP_FORBIDDEN);

# Work out who the requestor should be. First try the From for the first
# attachment in the transaction, but if that doesn't work fall back on the
# requestor(s) of the original old ticket.
my $oldRequestors = $Transaction->Attachments->First->Addresses->{'From'};
if(!@{$oldRequestors}) {
  @{$oldRequestors} = split(/,/, $OldTicket->RequestorAddresses);

# Make a shiny new ticket with the contents of the original transaction
my $NewTicket = RT::Ticket->new( $session{'CurrentUser'} );
my($ticketId, $newTransObj, $errorMessage) = $NewTicket->Create(
  Queue => $Queue,
  Requestor => $oldRequestors,
  Subject => $Transaction->Subject() || "Split off from " . $OldTicket->Subject() ? $OldTicket->Subject : $OldTicket->id,
  MIMEObj => $Transaction->Attachments->First->ContentAsMIME(),
  Parents => $OldTicket->id,
  Owner => $OldTicket->OwnerObj,
if(!$ticketId) {
    Abort('Could not create a new ticket for transaction '.$ARGS{'id'}. 
          ': '. $errorMessage);

# Redirect the user to look at the new ticket.

Still pretty obvious hopefully - its takes the ID of the transaction we want to split off, check it looks OK, gets the original old ticket object and associated queue, checks that the current user can create tickets and if they can makes a new one with first attachment of the transaction as its MIME object. For a requestor it tries to get the From address from the transaction, but if that fails it reverts the requestor(s) of the original old ticket the transaction came from. I also try to link the old ticket as the parent of the new ticket.

Finally if the ticket has been created OK, it redirects the user to the normal RT /Ticket/Display.html page so they can check and interact with it. Note that the page output at the top is a bit of a debugging placeholder - if all goes well the user never sees that because once the ticket is successfully created we redirect to the display page.

I hope this quick hack is of use to someone (and cue someone now telling me that there’s already an RT config option to turn this feature on in the existing code… :slight_smile: )


This sounds like it will be useful in our organisation.

We are still on RT 4.2.16 at the moment (1.3TB database, lots of 24/7 users, 90+ local extensions, heavily customised UI - upgrading is a bit daunting). At first glance what you’ve posted looks like it would pretty much just work on 4.2.16 - before I try it out, do you think there are likely to be any obvious gotchas?

You probably want to check /opt/rt5/share/html/Element/ShowTransaction in your distribution to check that it has an un-named callback just before it processes the @actions array, as that’s what the Callback part hooks into. If it does, I can’t see why it wouldn’t work as I don’t think I’ve used any API calls that have changed that much in the move from 4.x → 5.x.

There is such a callback. Come to think of it, I’ve used it before, in an extension to add comment pinning. Thanks!

Heh, just as your reply arrived I’d finished hunting through BP’s github for the 4.2.16 release to confirm it had the callback! Looks like you’re good to give it a try in that case.

Having discussed it with our service desk team lead, who liked the idea, I think that if I were to implement this in our organisation, I would change it a little.

Instead of creating a new ticket immediately, I would make clicking “Split Off” open up the ticket creation form, pre-populated as appropriate, so that the operator gets a chance to review / edit the contents (and change their mind, in case of mis-clicks).

The ticket creation page would be modified with a banner to show that this is a “split off” operation rather than a plain “new ticket” operation.

Also I’d want to add some permissions checks to the transaction callback so that we only show the action if the operator has permission to comment on this ticket and to create tickets in the same queue.

Not wanting to steal your idea - would you be happy for me to implement what’s described here, as an open-source extension named something like “RT::Extension::NewTicketFromCorrespondence”, or should I just build it specifically for my organisation at some point, when and if they approve the development time for it?

Go for it - this was an idea from our service desk folk so the more the merrier!

Trying adding this to the Callback routine:

  push @$Actions, {
    path => '/Ticket/Create.html?Subject='.$m->interp->apply_escapes( $Object->\
Subject, 'h') . '&Queue=' . $m->interp->apply_escapes( $Object->QueueObj->Name(\
), 'h' ) . '&QuoteTransaction=' . $Transaction->id . '&Requestors=' . $m->inter\
p->apply_escapes( $Object->RequestorAddresses, 'h'),
    title => 'Split Off and Check',

It should give you an extra “Split off and Check” button that takes you to ticket creation with the transaction quoted as the initial content, ready for the service desk folk to play with.

1 Like

Oh, and as for the banner, you could use the “FormStart” callback in Ticket/Create.html to check if you’ve been passed a new extra argument such as “TxnCreateBanner” and then display that text at the top of the form. Should mean you can do all this using the untouched distro version of the Ticket/Create.html.

I flipping love RT’s callbacks. :+1:

First draft: ivarch.com: RT::Extension::NewTicketFromCorrespondence

I’ve done a quick smoke-test on RT 4.2.16, 4.4.4, and 5.0.1. Seems OK.

The CPAN page probably doesn’t exist yet.

Might need to remove the “IsInbound” check since that just checks if the transaction creator matches the ticket’s current requestor, which might not fit all use cases.

And on RT 5 it would be nice to give the action its own icon instead of showing the text, but I’m not sure how to do that or whether it should be done at all.


Looks good - a definite improvement over my original hack!