# Ticket Organisation Hooks

https://dev.wisecp.com/es/ticket-organisation-hooks

The seven hooks over merging, splitting and moving tickets.

## Overview

The operations that reorganise tickets live here: merging, splitting, and changing the department, priority or linked service.

The first two cannot be undone. A merge **deletes** everything but the target; a split moves replies into a new ticket. That is what makes the gates valuable.

## Reference

### Stopping a ticket merge

gateticket.merge

`AdminTickets` the others are deleted

Runs before tickets are merged. A merge cannot be undone: **everything but the target is deleted**.

Parameters 2

$primary_idintThe target ticket; the others move into it.

$merge_idsarrayThe other tickets; the target is not in this list.

Return 1

string|null**A non-empty text blocks the operation** and is shown as the error. An empty return lets it carry on.

Listener PHP

```php
Hook::add('gate:ticket.merge', 10, function ($primary_id, $merge_ids) {
    // Everything but the target is DELETED: this cannot be undone.
    foreach ($merge_ids as $id)
        if (Acme::underLegalHold((int) $id)) return 'A ticket under legal hold cannot be merged.';

    return null;
});
```

### Following a merge

actionticket.merged

`AdminTickets` after the merge

Runs after the tickets are merged.

Parameters 2

$primary_idintThe target ticket.

$merged_idsarrayThe ids merged in and **deleted**. Those ids no longer exist: point them at the target in your own records.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:ticket.merged', 10, function ($primary_id, $merged_ids) {
    // Those ids are gone: point them at the target.
    foreach ($merged_ids as $id) Acme::redirectRef((int) $id, $primary_id);
});
```

### Stopping a ticket split

gateticket.split

`AdminTickets` an unverified list

Runs before some replies of a ticket are moved into a new one.

Parameters 2

$sourcearrayThe source ticket being split.

$reply_idsarrayThe ids of the replies to move. They are cleaned but **not yet checked against the database**: an id here may not really belong to that ticket.

Return 1

string|null**A non-empty text blocks the operation** and is shown as the error. An empty return lets it carry on.

Listener PHP

```php
Hook::add('gate:ticket.split', 10, function ($source, $reply_ids) {
    // The list has not been checked against the database yet.
    if (count($reply_ids) > 50) return 'At most 50 replies can move at once.';

    return null;
});
```

### Following a split

actionticket.split

`AdminTickets` a verified list

Runs after the new ticket is created and the replies have moved.

Parameters 3

$sourcearrayThe source ticket.

$new_ticketarrayThe newly created ticket.

$moved_reply_idsarrayThe ids that actually moved. Unlike in the gate this list is **verified**: anything asked for but not moved is absent.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:ticket.split', 10, function ($source, $new_ticket, $moved_reply_ids) {
    // This list is verified: what really moved.
    Acme::reindexSplit((int) ($new_ticket['id'] ?? 0), $moved_reply_ids);
});
```

### Following a department change

actionticket.department_changed

`AdminTickets` zero means none

Runs after a ticket moves to another department. The department decides who sees the ticket.

Parameters 3

$ticketarrayThe record before the change.

$oldDepartmentIdintThe previous department; a zero means none.

$newDepartmentIdintThe new department.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:ticket.department_changed', 10,
    function ($ticket, $oldDepartmentId, $newDepartmentId) {
        // The department decides who sees it.
        Acme::notifyDepartment($newDepartmentId, (int) ($ticket['id'] ?? 0));
    });
```

### Following a priority change

actionticket.priority_changed

`AdminTickets` four levels

Runs after the priority of a ticket changes.

Parameters 3

$ticketarrayThe record before the change.

$oldPriorityintThe previous level.

$newPriorityintThe new level.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:ticket.priority_changed', 10,
    function ($ticket, $oldPriority, $newPriority) {
        if ($newPriority > $oldPriority) Acme::escalate((int) ($ticket['id'] ?? 0));
    });
```

### Following the linked service changing

actionticket.service_changed

`AdminTickets` zero means unlinked

Runs after the service a ticket is linked to changes.

Parameters 3

$ticketarrayThe record before the change.

$oldServiceIdintThe previous service; a zero means it was not linked.

$newServiceIdintThe new service; a **zero means the link was removed**.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:ticket.service_changed', 10,
    function ($ticket, $oldServiceId, $newServiceId) {
        // Zero means the link was removed.
        if (!$newServiceId) return;

        Acme::attachServiceContext((int) ($ticket['id'] ?? 0), $newServiceId);
    });
```

## Pitfalls

> **A merge deletes the other tickets**
> 
> In a merge everything but the target is **deleted**, not merely moved. The ids in the event hook are records that no longer exist: point them at the target on your side, or you are left with broken references.

> **The list in the split gate is not verified yet**
> 
> The reply ids reaching the gate are cleaned but **not matched against the database**: the list may hold an id that does not belong to that ticket. To see what really moved, read the list in the event hook.

## Related Articles

- Support Ticket Hooks
- [Customer Account Hooks](https://dev.wisecp.com/en/customer-account-hooks)
- [How Hooks Work](https://dev.wisecp.com/en/how-hooks-work)
