Skip to content

Bugfix: Validation consumption#225

Open
giovanny07 wants to merge 9 commits into
pluginsGLPI:mainfrom
giovanny07:bugfix/validation-consumption
Open

Bugfix: Validation consumption#225
giovanny07 wants to merge 9 commits into
pluginsGLPI:mainfrom
giovanny07:bugfix/validation-consumption

Conversation

@giovanny07

Copy link
Copy Markdown

Checklist before requesting a review

  • I have performed a self-review of my code.
  • I have added tests (when available) that prove my fix is effective or that my feature works.
  • I have updated the CHANGELOG with a short functional description of the fix or new feature.

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 -l
  • git diff --check
  • Manual UI validation of voucher selection and quantity behavior in the ticket form

@stonebuzz stonebuzz requested review from Rom1-B and stonebuzz April 27, 2026 07:41
Comment thread front/ticket.form.php Outdated
ERROR,
);
Html::back();
if (!Session::haveRight('ticket', UPDATE) && !Session::haveRight(Entity::$rightname, UPDATE)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread inc/ticket.class.php Outdated
Comment on lines +268 to +308
$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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread CHANGELOG.md Outdated

- 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- 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.

Comment thread front/ticket.form.php

use Glpi\Exception\Http\BadRequestHttpException;

Session::haveRight("ticket", UPDATE);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Session::haveRight("ticket", UPDATE);
Session::checkRight(\Ticket::class, UPDATE);

Comment thread front/ticket.form.php
Comment on lines -37 to -50
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();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove these checks?

@stonebuzz

Copy link
Copy Markdown
Contributor

Hi @giovanny07

can you rebase ?

@giovanny07 giovanny07 force-pushed the bugfix/validation-consumption branch from 373344a to 9f28a7f Compare May 11, 2026 16:04
@giovanny07

Copy link
Copy Markdown
Author

Hi @stonebuzz, rebased on top of latest main.

@stonebuzz

Copy link
Copy Markdown
Contributor

please fix CI

@Herafia

Herafia commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Hi @giovanny07 could you resolve the requests and the CI ?

@giovanny07

Copy link
Copy Markdown
Author

Hi team!

Sorry for the delay, I was on vacation. I've fixed it now.

Regards

@stonebuzz

Copy link
Copy Markdown
Contributor

Let me check tomorrow =)

@stonebuzz stonebuzz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Comment thread inc/ticket.class.php Outdated
&& !in_array($ticket->fields['status'], array_merge(Ticket::getSolvedStatusArray(), Ticket::getClosedStatusArray()));
}

private static function checkInput(array $input, ?self $current_credit = null): array|false

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private static function checkInput(array $input, ?self $current_credit = null): array|false
private static function 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.

Comment thread inc/ticket.class.php
Comment on lines +247 to +252
$ticket = new Ticket();
if (
!$ticket->getFromDB($ticket_id)
|| !$ticket->can($ticket_id, READ)
|| !self::canUpdateCreditsForTicket($ticket)
) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$ticket = new Ticket();
if (
!$ticket->getFromDB($ticket_id)
|| !$ticket->can($ticket_id, READ)
|| !self::canUpdateCreditsForTicket($ticket)
) {
$ticket = new Ticket();
if (
!$ticket->getFromDB($ticket_id)
|| !$ticket->can($ticket_id, READ)
|| (!$skip_status_check && !self::canUpdateCreditsForTicket($ticket))
) {

Comment thread front/ticket.form.php Outdated
use Glpi\Exception\Http\BadRequestHttpException;

Session::haveRight("ticket", UPDATE);
Session::checkRight(Ticket::class, UPDATE);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Session::checkRight(Ticket::class, UPDATE);
Session::checkRight(\Ticket::class, UPDATE);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix here

Comment thread inc/ticket.class.php Outdated
}

$input = $validated_input + $input;
$input['users_id'] = (int) Session::getLoginUserID();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$input['users_id'] = (int) Session::getLoginUserID();

ALready set in front/ticket.form.php line 42

@giovanny07

Copy link
Copy Markdown
Author

Hi @stonebuzz, thanks for the detailed review.

Addressed in the latest pushed commit:

  • Added a skip_status_check parameter to PluginCreditTicket::checkInput() and use it only from consumeVoucher(). This keeps
    the normal status/editability checks for manual add/update paths, while allowing the ITILSolution hook path to consume credits after GLPI has already moved the ticket to solved.
  • Removed the redundant users_id assignment from prepareInputForAdd(); it remains set by the front controller / caller input.
  • Added PHPUnit coverage for consumeVoucher() on an already-solved ticket through the solution-hook path.
  • Added PHPUnit coverage for prepareInputForAdd() rejecting vouchers outside their validity window: one with begin_date in
    the future and one with end_date in the past.

On the Session::checkRight() suggestion: I kept Ticket::class without the leading slash because Rector failed the CI when the
code used \Ticket::class and explicitly rewrote it to Ticket::class.

Local validation done:

  • find inc front ajax tests -name '*.php' -print0 | xargs -0 -n1 php -l
  • git diff --check

I could not run PHPUnit locally because this checkout does not include the GLPI core vendor/; the plugin CI should now run the
tests because phpunit.xml is present.

Comment thread front/ticket.form.php Outdated
use Glpi\Exception\Http\BadRequestHttpException;

Session::haveRight("ticket", UPDATE);
Session::checkRight(Ticket::class, UPDATE);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix here

@stonebuzz stonebuzz requested review from Rom1-B and stonebuzz June 23, 2026 13:46

@stonebuzz stonebuzz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants