Uh oh!
There was an error while loading. Please reload this page.
Bugfix: Validation consumption - #225
Conversation
| ERROR, | ||
| ); | ||
| Html::back(); | ||
| if (!Session::haveRight('ticket', UPDATE) && !Session::haveRight(Entity::$rightname, UPDATE)) { |
There was a problem hiding this comment.
Why add a validation check when modifying an entity?
A technician updating a ticket (including data coming from the credit plugin) does not necessarily have the permissions required to update the associated entity.
| $ticket_id = filter_var( | ||
| $input['tickets_id'] ?? null, | ||
| FILTER_VALIDATE_INT, | ||
| ['options' => ['min_range' => 1]], | ||
| ); | ||
| if ($ticket_id === false) { | ||
| Session::addMessageAfterRedirect( | ||
| __s('Ticket is mandatory.', 'credit'), | ||
| true, | ||
| ERROR, | ||
| ); | ||
| return false; | ||
| } | ||
| $credit_id = filter_var( | ||
| $input['plugin_credit_entities_id'] ?? null, | ||
| FILTER_VALIDATE_INT, | ||
| ['options' => ['min_range' => 1]], | ||
| ); | ||
| if ($credit_id === false) { | ||
| Session::addMessageAfterRedirect( | ||
| __s('Credit voucher entity must be selected.', 'credit'), | ||
| true, | ||
| ERROR, | ||
| ); | ||
| return false; | ||
| } | ||
| $consumed = filter_var( | ||
| $input['consumed'] ?? null, | ||
| FILTER_VALIDATE_INT, | ||
| ['options' => ['min_range' => 1]], | ||
| ); | ||
| if ($consumed === false) { | ||
| Session::addMessageAfterRedirect( | ||
| __s('Credit voucher quantity must be greater than 0.', 'credit'), | ||
| true, | ||
| ERROR, | ||
| ); | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Redundant code with prepareInputForUpdate
This logic duplicates functionality already present in prepareInputForUpdate.
It could be refactored into a dedicated checkInput function, or merged into getValidatedConsumptionInput, which should be renamed accordingly to better reflect its broader responsibility.
| - Improve consumed credits modal readability and open related tickets in a new tab | ||
| - Show credit vouchers list in entity tab for read-only users while keeping add/config forms restricted to editable contexts. | ||
| - Centralize credit consumption validation, prevent invalid voucher selections outside the validity window, and fix the ticket tab quantity field behavior. |
There was a problem hiding this comment.
| -Centralize credit consumption validation, prevent invalid voucher selections outside the validity window, and fix the ticket tab quantity field behavior. | |
| -Improve credit validation and voucher handling. |
Uh oh!
There was an error while loading. Please reload this page.
| if ($_REQUEST['plugin_credit_entities_id'] == 0) { | ||
| Session::addMessageAfterRedirect( | ||
| __s('Credit voucher entity must be selected.', 'credit'), | ||
| true, | ||
| ERROR, | ||
| ); | ||
| Html::back(); | ||
| } elseif ($_REQUEST['plugin_credit_quantity'] == 0) { | ||
| Session::addMessageAfterRedirect( | ||
| __s('Credit voucher quantity must be greater than 0.', 'credit'), | ||
| true, | ||
| ERROR, | ||
| ); | ||
| Html::back(); |
stonebuzz
commented
May 5, 2026
Hi @giovanny07 can you rebase ? |
373344a to
9f28a7fComparegiovanny07
commented
May 11, 2026
Hi @stonebuzz, rebased on top of latest |
stonebuzz
commented
May 12, 2026
please fix CI |
Herafia
commented
Jun 10, 2026
Hi @giovanny07 could you resolve the requests and the CI ? |
giovanny07
commented
Jun 22, 2026
Hi team! Sorry for the delay, I was on vacation. I've fixed it now. Regards |
stonebuzz
commented
Jun 22, 2026
Let me check tomorrow =) |
stonebuzz
left a comment
There was a problem hiding this comment.
The test checkbox is unchecked, and given that checkInput centralizes logic that was previously scattered and untested, it would be worth adding at least:
- a test for consumeVoucher on an already-solved ticket (this is the exact regression this PR introduces)
- a test for prepareInputForAdd with an out-of-window voucher (begin_date in the future, end_date in the past)
| && !in_array($ticket->fields['status'], array_merge(Ticket::getSolvedStatusArray(), Ticket::getClosedStatusArray())); | ||
| } | ||
| private static function checkInput(array $input, ?self $current_credit = null): array|false |
There was a problem hiding this comment.
| privatestaticfunction checkInput(array$input, ?self$current_credit = null): array|false | |
| privatestaticfunction checkInput(array$input, ?self$current_credit = null, bool$skip_status_check = false): array|false |
canUpdateCreditsForTicket returns false for non-entity-admins when the ticket is already solved or closed. consumeVoucher is registered on the item_add hook (setup.php line 74-76). For ITILSolution, GLPI sets the ticket status to "solved" inside ITILSolution::add(), before any item_add hooks fire.
| $ticket = new Ticket(); | ||
| if ( | ||
| !$ticket->getFromDB($ticket_id) | ||
| || !$ticket->can($ticket_id, READ) | ||
| || !self::canUpdateCreditsForTicket($ticket) | ||
| ) { |
There was a problem hiding this comment.
| $ticket = newTicket(); | |
| if ( | |
| !$ticket->getFromDB($ticket_id) | |
| || !$ticket->can($ticket_id, READ) | |
| || !self::canUpdateCreditsForTicket($ticket) | |
| ) { | |
| $ticket = newTicket(); | |
| if ( | |
| !$ticket->getFromDB($ticket_id) | |
| || !$ticket->can($ticket_id, READ) | |
| || (!$skip_status_check && !self::canUpdateCreditsForTicket($ticket)) | |
| ) { |
| use Glpi\Exception\Http\BadRequestHttpException; | ||
| Session::haveRight("ticket", UPDATE); | ||
| Session::checkRight(Ticket::class, UPDATE); |
There was a problem hiding this comment.
| Session::checkRight(Ticket::class, UPDATE); | |
| Session::checkRight(\Ticket::class, UPDATE); |
| } | ||
| $input = $validated_input + $input; | ||
| $input['users_id'] = (int) Session::getLoginUserID(); |
There was a problem hiding this comment.
| $input['users_id'] = (int) Session::getLoginUserID(); |
ALready set in front/ticket.form.php line 42
giovanny07
commented
Jun 23, 2026
Hi @stonebuzz, thanks for the detailed review. Addressed in the latest pushed commit:
On the Local validation done:
I could not run PHPUnit locally because this checkout does not include the GLPI core |
| use Glpi\Exception\Http\BadRequestHttpException; | ||
| Session::haveRight("ticket", UPDATE); | ||
| Session::checkRight(Ticket::class, UPDATE); |
Uh oh!
There was an error while loading. Please reload this page.
| 'users_id' => Session::getLoginUserID(), | ||
| ])); | ||
| } | ||
| } |
There was a problem hiding this comment.
No test covers the overconsumption paths in checkInput (lines 287-298 of inc/ticket.class.php): when overconsumption_allowed = 1, a WARNING should be emitted but the add should succeed; when overconsumption_allowed = 0, the add should fail. Can you add a test for each branch?
The deduction logic in checkInput (lines 278-282): when updating an existing credit entry for the same voucher, $quantity_consumed is reduced by the current record's consumed value so the user is not double-charged. This path has no test. Can you add a test for prepareInputForUpdate where the updated quantity stays within the adjusted remaining budget?
giovanny07
commented
Jun 30, 2026
Those checks weren't dropped, they were centralized into
The goal was to have one source of truth for validation shared by the manual form and the hook-driven path (followup/task/solution), instead of duplicating the entity/quantity checks in the front controller. |
a21d0c3 to
941f09dComparestonebuzz
commented
Jul 1, 2026
can you rebase to solve conflicts ? |
Thanks! you're rigth Co-authored-by: Romain B. <8530352+Rom1-B@users.noreply.github.com>
Covers the two branches flagged by Rom1-B: WARNING+success when overconsumption_allowed=1, ERROR+failure when disabled, and the prepareInputForUpdate path where an existing record's consumption is deducted before re-checking the remaining budget.
941f09d to
5172f13Compare
Checklist before requesting a review
Description
This PR centralizes credit consumption validation so both the manual ticket form and the hook-driven followup/task/solution flow
use the same server-side rules. It also prevents selecting vouchers outside their valid begin/end date window and fixes the
ticket-tab JavaScript that broke the quantity field when selecting a voucher.
Validation performed:
find inc front ajax -name '*.php' -print0 | xargs -0 -n1 php -lgit diff --check