Skip to content

Credit consumption is not visible/editable when editing a task, and is not removed when deleting a task #260

Description

@hloresilv-ticgal

Code of Conduct

  • I agree to follow this project's Code of Conduct

Is there an existing issue for this?

  • I have searched the existing issues

GLPI Version

10.0.27

Plugin version

1.14.3

Bug description

There is a lifecycle desynchronization between GLPI tasks (TicketTask) and credit consumptions (glpi_plugin_credit_consumptions).

Currently:

  1. When editing an existing task, the credit consumption fields are neither displayed nor editable.
  2. When deleting/purging a task, the credit consumption record is not removed, leaving the voucher balance permanently deducted and creating an orphaned consumption record on the ticket.

Relevant log output

Page URL

No response

Steps To reproduce

Part 1: Task Editing Issue

  1. Ensure the entity has an active credit voucher with available balance (e.g., 10 units).
  2. Go to any ticket and add a new Task (TicketTask).
  3. In the task form, select the credit voucher and specify an amount to consume (e.g., 2 units). Save the task.
  4. Verify under the ticket's Credit tab that 2 units were debited.
  5. In the ticket timeline, click Edit (pencil icon) on the newly created task.
  6. Observation: The credit consumption fields are completely missing. There is no information showing which voucher was used or how many units were consumed, nor is there any way to adjust the consumed amount.

Part 2: Task Deletion Issue

  1. From the timeline, Delete (and purge) the task created in Part 1.
  2. Check the ticket's Credit tab and the entity's voucher balance.
  3. Observation: The consumption record of 2 units still exists. The voucher balance remains reduced (still 8 units remaining instead of restoring to 10), leaving an orphaned consumption linked only to the ticket.

Your GLPI setup information

Information about system installation and configuration
GLPI 10.0.27 ( => /var/www/html)
Installation mode: TARBALL
Current language:en_GB

Server
 
Operating system: Linux 905caf0d6215 6.14.0-1020-oem #​20-Ubuntu SMP PREEMPT_DYNAMIC Mon Jan 19 08:17:52 UTC 2026 x86_64
PHP 8.2.34 apache2handler (Core, PDO, Phar, Reflection, SPL, SimpleXML, Zend OPcache, apache2handler, apcu, bcmath, bz2, ctype,
curl, date, dom, exif, fileinfo, filter, gd, hash, iconv, intl, json, ldap, libxml, mbstring, mysqli, mysqlnd, openssl, pcre,
pdo_mysql, pdo_sqlite, posix, random, session, sodium, sqlite3, standard, tokenizer, xdebug, xml, xmlreader, xmlwriter, zip,
zlib)
Setup: disable_functions="" max_execution_time="30" max_input_vars="1000" memory_limit="128M" post_max_size="8M"
session.cookie_secure="0" session.cookie_httponly="" session.cookie_samesite="" session.save_handler="files"
upload_max_filesize="2M"
Software: Apache/2.4.68 (Debian) (Apache/2.4.68 (Debian) Server at localhost Port 8080
)
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/154.0.0.0 Safari/537.36
Server Software: mariadb.org binary distribution
Server Version: 10.11.18-MariaDB-ubu2204
Server SQL Mode: STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
Parameters: glpi_user@db/glpidb
Host info: db via TCP/IP

PHP version (8.2.34) is supported.
Sessions configuration is OK.
Allocated memory is sufficient.
mysqli extension is installed.
Following extensions are installed: dom, fileinfo, filter, libxml, json, simplexml, xmlreader, xmlwriter.
curl extension is installed.
gd extension is installed.
intl extension is installed.
zlib extension is installed.
The constant SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES is present.
Database engine version (10.11.18) is supported.
No files from previous GLPI version detected.
The log file has been created successfully.
Write access to /var/www/html/files/_cache has been validated.
Write access to /var/www/html/files/_cron has been validated.
Write access to /var/www/html/files has been validated.
Write access to /var/www/html/files/_dumps has been validated.
Write access to /var/www/html/files/_graphs has been validated.
Write access to /var/www/html/files/_lock has been validated.
Write access to /var/www/html/files/_pictures has been validated.
Write access to /var/www/html/files/_plugins has been validated.
Write access to /var/www/html/files/_rss has been validated.
Write access to /var/www/html/files/_sessions has been validated.
Write access to /var/www/html/files/_tmp has been validated.
Write access to /var/www/html/files/_uploads has been validated.

Web server root directory configuration is not safe as it permits access to non-public files. See installation documentation for more details.
The following directories should be placed outside "/var/www/html":
‣ "/var/www/html/files" ("GLPI_VAR_DIR")
You can ignore this suggestion if your web server root directory is "/var/www/html/public".
PHP directive "session.cookie_httponly" should be set to "on" to prevent client-side script to access cookie values.
OS and PHP are relying on 64 bits integers.
exif extension is installed.
ldap extension is installed.
openssl extension is installed.
Following extensions are installed: bz2, Phar, zip.
Zend OPcache extension is installed.
Following extensions are installed: ctype, iconv, mbstring, sodium.
Write access to /var/www/html/marketplace has been validated.
Timezones seems loaded in database.

GLPI constants
 
GLPI_ROOT: "/var/www/html"
GLPI_CONFIG_DIR: "/var/www/html/config"
GLPI_VAR_DIR: "/var/www/html/files"
GLPI_MARKETPLACE_DIR: "/var/www/html/marketplace"
GLPI_USE_CSRF_CHECK: "1"
GLPI_CSRF_EXPIRES: "7200"
GLPI_CSRF_MAX_TOKENS: "100"
GLPI_USE_IDOR_CHECK: "1"
GLPI_IDOR_EXPIRES: "7200"
GLPI_ALLOW_IFRAME_IN_RICH_TEXT: false
GLPI_SERVERSIDE_URL_ALLOWLIST: ["^\n (http|https|feed):// # protocol\n (\n (?:\n (?:xn--[a-z0-9-]++\.)*+xn--[a-z0-9-]++ # a domain name using punycode\n |\n (?:[\pL\pN\pS\pM\-\]++\.)+[\pL\pN\pM]++ # a multi-level domain name\n |\n [a-z0-9\-\]++ # a single-level domain name\n )\.?\n | # or\n \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} # an IP address\n | # or\n \[\n (?:(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){6})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:::(?:(?:(?:[0-9a-f]{1,4})):){5})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){4})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,1}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){3})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,2}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){2})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,3}(?:(?:[0-9a-f]{1,4})))?::(?:(?:[0-9a-f]{1,4})):)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,4}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,5}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,6}(?:(?:[0-9a-f]{1,4})))?::))))\n \] # an IPv6 address\n )\n (?:/ (?:[\pL\pN\pS\pM\-._\!$&'()+,;=:@]|%[0-9A-Fa-f]{2}) )* # a path\n (?:\? (?:[\pL\pN\-._\~!$&'\\+,;=:@/?]|%[0-9A-Fa-f]{2}) )? # a query (optional)\n $~ixuD"]
GLPI_DISALLOWED_UPLOADS_PATTERN: "/\.(php\d*|phar)$/i"
GLPI_TELEMETRY_URI: "https://telemetry.glpi-project.org"
GLPI_INSTALL_MODE: "TARBALL"
GLPI_NETWORK_MAIL: "glpi@teclib.com"
GLPI_NETWORK_SERVICES: "https://services.glpi-network.com"
GLPI_MARKETPLACE_ALLOW_OVERRIDE: true
GLPI_MARKETPLACE_MANUAL_DOWNLOADS: true
GLPI_USER_AGENT_EXTRA_COMMENTS: ""
GLPI_DISABLE_ONLY_FULL_GROUP_BY_SQL_MODE: "1"
GLPI_AJAX_DASHBOARD: "1"
GLPI_CALDAV_IMPORT_STATE: 0
GLPI_DEMO_MODE: "0"
GLPI_CENTRAL_WARNINGS: "1"
GLPI_TEXT_MAXSIZE: "4000"
GLPI_DOC_DIR: "/var/www/html/files"
GLPI_CACHE_DIR: "/var/www/html/files/_cache"
GLPI_CRON_DIR: "/var/www/html/files/_cron"
GLPI_DUMP_DIR: "/var/www/html/files/_dumps"
GLPI_GRAPH_DIR: "/var/www/html/files/_graphs"
GLPI_LOCAL_I18N_DIR: "/var/www/html/files/_locales"
GLPI_LOCK_DIR: "/var/www/html/files/_lock"
GLPI_LOG_DIR: "/var/www/html/files/_log"
GLPI_PICTURE_DIR: "/var/www/html/files/_pictures"
GLPI_PLUGIN_DOC_DIR: "/var/www/html/files/_plugins"
GLPI_RSS_DIR: "/var/www/html/files/_rss"
GLPI_SESSION_DIR: "/var/www/html/files/_sessions"
GLPI_TMP_DIR: "/var/www/html/files/_tmp"
GLPI_UPLOAD_DIR: "/var/www/html/files/_uploads"
GLPI_INVENTORY_DIR: "/var/www/html/files/_inventories"
GLPI_NETWORK_API_URL: "https://services.glpi-network.com/api"
GLPI_NETWORK_REGISTRATION_API_URL: "https://services.glpi-network.com/api/registration/"
GLPI_MARKETPLACE_PLUGINS_API_URI: "https://services.glpi-network.com/api/marketplace/"
GLPI_I18N_DIR: "/var/www/html/locales"
GLPI_VERSION: "10.0.27"
GLPI_SCHEMA_VERSION: "10.0.27"
GLPI_MARKETPLACE_PRERELEASES: false
GLPI_MIN_PHP: "7.4.0"
GLPI_MAX_PHP: "8.6.0"
GLPI_YEAR: "2026"

Libraries
 
htmlawed/htmlawed version 1.2.14 in (/var/www/html/lib)
phpmailer/phpmailer version 6.8.0 in (/var/www/html/vendor/phpmailer/phpmailer/src)
simplepie/simplepie version 1.5.8 in (/var/www/html/vendor/simplepie/simplepie/library)
tecnickcom/tcpdf version 6.8.0 in (/var/www/html/vendor/tecnickcom/tcpdf)
michelf/php-markdown in (/var/www/html/vendor/michelf/php-markdown/Michelf)
true/punycode in (/var/www/html/vendor/true/punycode/src)
iamcal/lib_autolink in (/var/www/html/vendor/iamcal/lib_autolink)
sabre/dav in (/var/www/html/vendor/sabre/dav/lib/DAV)
sabre/http in (/var/www/html/vendor/sabre/http/lib)
sabre/uri in (/var/www/html/vendor/sabre/uri/lib)
sabre/vobject in (/var/www/html/vendor/sabre/vobject/lib)
laminas/laminas-i18n in (/var/www/html/vendor/laminas/laminas-i18n/src)
laminas/laminas-servicemanager in (/var/www/html/vendor/laminas/laminas-servicemanager/src)
monolog/monolog in (/var/www/html/vendor/monolog/monolog/src/Monolog)
sebastian/diff in (/var/www/html/vendor/sebastian/diff/src)
donatj/phpuseragentparser in (/var/www/html/vendor/donatj/phpuseragentparser/src/UserAgent)
elvanto/litemoji in (/var/www/html/vendor/elvanto/litemoji/src)
symfony/console in (/var/www/html/vendor/symfony/console)
scssphp/scssphp in (/var/www/html/vendor/scssphp/scssphp/src)
laminas/laminas-mail in (/var/www/html/vendor/laminas/laminas-mail/src/Protocol)
laminas/laminas-mime in (/var/www/html/vendor/laminas/laminas-mime/src)
rlanvin/php-rrule in (/var/www/html/vendor/rlanvin/php-rrule/src)
ramsey/uuid in (/var/www/html/vendor/ramsey/uuid/src)
psr/log in (/var/www/html/vendor/psr/log/Psr/Log)
psr/simple-cache in (/var/www/html/vendor/psr/simple-cache/src)
psr/cache in (/var/www/html/vendor/psr/cache/src)
league/csv in (/var/www/html/vendor/league/csv/src)
mexitek/phpcolors in (/var/www/html/vendor/mexitek/phpcolors/src/Mexitek/PHPColors)
guzzlehttp/guzzle in (/var/www/html/vendor/guzzlehttp/guzzle/src)
guzzlehttp/psr7 in (/var/www/html/vendor/guzzlehttp/psr7/src)
glpi-project/inventory_format in (/var/www/html/vendor/glpi-project/inventory_format/lib/php)
wapmorgan/unified-archive in (/var/www/html/vendor/wapmorgan/unified-archive/src)
paragonie/sodium_compat in (/var/www/html/vendor/paragonie/sodium_compat/src)
symfony/cache in (/var/www/html/vendor/symfony/cache)
html2text/html2text in (/var/www/html/vendor/html2text/html2text/src)
symfony/css-selector in (/var/www/html/vendor/symfony/css-selector)
symfony/dom-crawler in (/var/www/html/vendor/symfony/dom-crawler)
twig/twig in (/var/www/html/vendor/twig/twig/src)
twig/string-extra in (/var/www/html/vendor/twig/string-extra)
symfony/polyfill-ctype not found
symfony/polyfill-iconv not found
symfony/polyfill-mbstring not found
symfony/polyfill-php80 not found
symfony/polyfill-php81 not found
symfony/polyfill-php82 in (/var/www/html/vendor/symfony/polyfill-php82)
league/oauth2-client in (/var/www/html/vendor/league/oauth2-client/src/Provider)
league/oauth2-google in (/var/www/html/vendor/league/oauth2-google/src/Provider)
thenetworg/oauth2-azure in (/var/www/html/vendor/thenetworg/oauth2-azure/src/Provider)
benmorel/weakmap-polyfill in (/var/www/html/ajax)

SQL replicas
 
Not active

Notifications
 
Way of sending emails: PHP

Plugins list
 
credit Name: Credit vouchers Version: 1.14.3 State: Enabled
Install Method: Manual

Anything else?

No response

Activity

  1. hloresilv-ticgal commented on Oct 1, 2026

    @hloresilv-ticgal
    Author

    When a task is recorded, the plugin generates a separate record in its consumption table (glpi_plugin_credit_consumptions) linked to the ticket and the voucher. However, the plugin does not implement delete hooks. Consequently, when the task is deleted in GLPI, the consumption record becomes ‘orphaned’ and the voucher balance remains reduced.

    The plugin only injects the voucher selection and consumable units fields into the creation form (when the item is new). It does not implement support in the edit (update) rendering hooks for tasks (TicketTask), so it does not pre-populate or display previous consumption, nor does it provide fields to amend it.

    Before editing, just after creating the ticket, we confirm credit is active:

    Image

    After editing:

    Image

    After deleting the task, credit doesn't dissapear:

    Image Image

    Credit for the test were created and activated beforehand with the following options:

    Image Image

    and the task was created by consuming the right voucher:

    Image
  2. stonebuzz commented on Oct 2, 2026

    @stonebuzz
    Contributor

    Hi @hloresilv-ticgal

    Deleting a task does not necessarily mean that the associated credits should be refunded. In some cases, the credits may have already been consumed and should therefore remain deducted.

    I would suggest adding an option to explicitly define whether deleting a task should trigger a credit refund, allowing this behavior to be configured according to the intended use case.

  3. github-actions commented on Oct 2, 2026

    @github-actions

    This issue has been closed as we only track bugs here.

    You can open a topic to discuss with community about this enhancement on suggestion website.
    You can also contact GLPI editor team directly if you are willing to sponsor this feature.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions