Skip to content

Commit 032a61c

Browse files
Fix solve ticket massive action (#19606)
* Fix solve ticket massive action * Fix undefined twig vars
1 parent dd358db commit 032a61c

File tree

4 files changed

+127
-25
lines changed

4 files changed

+127
-25
lines changed

src/Change_Ticket.php

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -166,35 +166,35 @@ public static function processMassiveActionsForOneItemtype(
166166
}
167167
return;
168168
case 'solveticket':
169+
if (!$item instanceof Ticket) {
170+
throw new InvalidArgumentException();
171+
}
172+
169173
$input = $ma->getInput();
170-
$ticket = new Ticket();
171174
foreach ($ids as $id) {
172175
if ($item->can($id, READ)) {
173-
if (
174-
$ticket->getFromDB($item->fields['tickets_id'])
175-
&& $ticket->canSolve()
176-
) {
176+
if ($item->canSolve()) {
177177
$solution = new ITILSolution();
178178
$added = $solution->add([
179-
'itemtype' => $ticket::class,
180-
'items_id' => $ticket->getID(),
181-
'solutiontypes_id' => $input['solutiontypes_id'],
182-
'content' => $input['content'],
179+
'itemtype' => $item::class,
180+
'items_id' => $item->getID(),
181+
'solutiontypes_id' => $input['solutiontypes_id'],
182+
'content' => $input['content'],
183183
]);
184184

185185
if ($added) {
186186
$ma->itemDone($item::class, $id, MassiveAction::ACTION_OK);
187187
} else {
188188
$ma->itemDone($item::class, $id, MassiveAction::ACTION_KO);
189-
$ma->addMessage($ticket->getErrorMessage(ERROR_ON_ACTION));
189+
$ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION));
190190
}
191191
} else {
192192
$ma->itemDone($item::class, $id, MassiveAction::ACTION_NORIGHT);
193-
$ma->addMessage($ticket->getErrorMessage(ERROR_RIGHT));
193+
$ma->addMessage($item->getErrorMessage(ERROR_RIGHT));
194194
}
195195
} else {
196196
$ma->itemDone($item::class, $id, MassiveAction::ACTION_NORIGHT);
197-
$ma->addMessage($ticket->getErrorMessage(ERROR_RIGHT));
197+
$ma->addMessage($item->getErrorMessage(ERROR_RIGHT));
198198
}
199199
}
200200
return;

src/Problem_Ticket.php

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -151,35 +151,35 @@ public static function processMassiveActionsForOneItemtype(
151151
return;
152152

153153
case 'solveticket':
154+
if (!$item instanceof Ticket) {
155+
throw new InvalidArgumentException();
156+
}
157+
154158
$input = $ma->getInput();
155-
$ticket = new Ticket();
156159
foreach ($ids as $id) {
157160
if ($item->can($id, READ)) {
158-
if (
159-
$ticket->getFromDB($item->fields['tickets_id'])
160-
&& $ticket->canSolve()
161-
) {
161+
if ($item->canSolve()) {
162162
$solution = new ITILSolution();
163163
$added = $solution->add([
164-
'itemtype' => $ticket::class,
165-
'items_id' => $ticket->getID(),
166-
'solutiontypes_id' => $input['solutiontypes_id'],
167-
'content' => $input['content'],
164+
'itemtype' => $item::class,
165+
'items_id' => $item->getID(),
166+
'solutiontypes_id' => $input['solutiontypes_id'],
167+
'content' => $input['content'],
168168
]);
169169

170170
if ($added) {
171171
$ma->itemDone($item::class, $id, MassiveAction::ACTION_OK);
172172
} else {
173173
$ma->itemDone($item::class, $id, MassiveAction::ACTION_KO);
174-
$ma->addMessage($ticket->getErrorMessage(ERROR_ON_ACTION));
174+
$ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION));
175175
}
176176
} else {
177177
$ma->itemDone($item::class, $id, MassiveAction::ACTION_NORIGHT);
178-
$ma->addMessage($ticket->getErrorMessage(ERROR_RIGHT));
178+
$ma->addMessage($item->getErrorMessage(ERROR_RIGHT));
179179
}
180180
} else {
181181
$ma->itemDone($item::class, $id, MassiveAction::ACTION_NORIGHT);
182-
$ma->addMessage($ticket->getErrorMessage(ERROR_RIGHT));
182+
$ma->addMessage($item->getErrorMessage(ERROR_RIGHT));
183183
}
184184
}
185185
return;

templates/components/itilobject/timeline/form_solution.html.twig

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
{% set nokb = params['nokb'] is defined or (params['nokb'] ?? false) == true %}
4242
{% set noform = params['noform'] is defined or (params['noform'] ?? false) == true %}
4343
{% set disabled = (candedit == false) %}
44+
{% set rand = rand|default(random()) %}
4445

4546
{% set content_field_id = 'solution_content_' ~ rand %}
4647

@@ -112,10 +113,11 @@
112113
'no_label': true,
113114
'enable_richtext': true,
114115
'enable_fileupload': true,
115-
'mention_options': mention_options,
116+
'mention_options': mention_options|default({}),
116117
'entities_id': item.fields['entities_id'],
117118
'rand': rand,
118119
'disabled': disabled,
120+
'aria_label': _n('Solution', 'Solutions', 1),
119121
}
120122
) }}
121123
</div>
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* ---------------------------------------------------------------------
3+
*
4+
* GLPI - Gestionnaire Libre de Parc Informatique
5+
*
6+
* http://glpi-project.org
7+
*
8+
* @copyright 2015-2025 Teclib' and contributors.
9+
* @licence https://www.gnu.org/licenses/gpl-3.0.html
10+
*
11+
* ---------------------------------------------------------------------
12+
*
13+
* LICENSE
14+
*
15+
* This file is part of GLPI.
16+
*
17+
* This program is free software: you can redistribute it and/or modify
18+
* it under the terms of the GNU General Public License as published by
19+
* the Free Software Foundation, either version 3 of the License, or
20+
* (at your option) any later version.
21+
*
22+
* This program is distributed in the hope that it will be useful,
23+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
24+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25+
* GNU General Public License for more details.
26+
*
27+
* You should have received a copy of the GNU General Public License
28+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
29+
*
30+
* ---------------------------------------------------------------------
31+
*/
32+
33+
describe("Massive actions on ITIL objects", () => {
34+
beforeEach(() => {
35+
cy.login();
36+
cy.changeProfile('Super-Admin');
37+
});
38+
39+
// List ITIL objects that have a "Ticket" tab from which they can resolve
40+
// linked tickets.
41+
const itil_types_than_can_solve_tickets = [
42+
{
43+
type: "Change",
44+
link_type: "Change_Ticket",
45+
fkey: "changes_id",
46+
tab: "Change_Ticket$1",
47+
url: "change.form.php",
48+
},
49+
{
50+
type: "Problem",
51+
link_type: "Problem_Ticket",
52+
fkey: "problems_id",
53+
tab: "Problem_Ticket$1",
54+
url: "problem.form.php",
55+
},
56+
];
57+
for (const itil_type of itil_types_than_can_solve_tickets) {
58+
it(`can solve linked tickets (${itil_type.type})`, () => {
59+
// Create a ITIL item with a linked ticket.
60+
cy.createWithAPI(itil_type.type, {
61+
'name': "My ITIL object",
62+
'content': "My ITIL object content",
63+
}).as('itil_id');
64+
cy.createWithAPI('Ticket', {
65+
'name': "My ticket",
66+
'content': "My ticket content",
67+
}).as('ticket_id');
68+
cy.getMany(["@itil_id", "@ticket_id"]).then(([itil_id, ticket_id]) => {
69+
cy.createWithAPI(itil_type.link_type, {
70+
[itil_type.fkey]: itil_id,
71+
'tickets_id': ticket_id,
72+
});
73+
});
74+
75+
// Go to the itil item on the "Tickets" tab.
76+
cy.get('@itil_id').then((itil_id) => {
77+
cy.visit(`/front/${itil_type.url}?id=${itil_id}&forcetab=${itil_type.tab}`);
78+
});
79+
80+
// Fill resolve form through massive actions.
81+
cy.findByRole('checkbox', {name: "Check all"}).check();
82+
cy.findByRole('button', {name: "Actions"}).click();
83+
cy.getDropdownByLabelText("Action").selectDropdownValue("Solve tickets");
84+
cy.findByLabelText('Solution').awaitTinyMCE().type('My solution');
85+
86+
// Submit action.
87+
cy.findByRole('button', {name: "Post"}).click();
88+
cy.findByRole('alert')
89+
.contains('Operation successful')
90+
.should('be.visible')
91+
;
92+
cy.get('@ticket_id').then((ticket_id) => {
93+
cy.getWithAPI('Ticket', ticket_id).then((ticket) => {
94+
expect(ticket.status).to.equal(5);
95+
});
96+
});
97+
});
98+
}
99+
100+
});

0 commit comments

Comments
 (0)