Skip to content

FIX / PDF export for assignable asset groups#63

Open
Mary-Clb wants to merge 12 commits into
mainfrom
fix/pdf-assignable-groups-export
Open

FIX / PDF export for assignable asset groups#63
Mary-Clb wants to merge 12 commits into
mainfrom
fix/pdf-assignable-groups-export

Conversation

@Mary-Clb

@Mary-Clb Mary-Clb commented Apr 29, 2026

Copy link
Copy Markdown
  • 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.
  • This change requires a documentation update.

Description

  • It fixes !43643 & !45124
  • This PR fixes PDF exports for assignable GLPI assets when group fields are loaded as arrays of IDs. The PDF plugin was still assuming that groups_id and groups_id_tech were always scalar values, while GLPI now loads these fields from glpi_groups_items as arrays for assignable items. As a result, group-related fields could be empty or incorrectly rendered in generated PDFs.
    The classes updated are the ones impacted beacause they correspond to assets implementing the AssignableItem feature.

Screenshots :

Before fix :

Capture d’écran du 2026-04-29 11-09-40 Capture d’écran du 2026-04-29 11-10-18

After fix :

Capture d’écran du 2026-04-29 11-10-45

@Mary-Clb Mary-Clb self-assigned this Apr 29, 2026
@Mary-Clb Mary-Clb added the bug Something isn't working label Apr 29, 2026
@Mary-Clb
Mary-Clb requested review from Rom1-B and stonebuzz April 29, 2026 12:04
@Rom1-B

Rom1-B commented Apr 29, 2026

Copy link
Copy Markdown

CHANGELOG please (always update it)

Comment thread inc/common.class.php Outdated

@stonebuzz stonebuzz left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Better 🙂

However, we now have a significant amount of duplicated code. We should refactor and consolidate these methods into a single unified implementation.

@Mary-Clb

Mary-Clb commented May 4, 2026

Copy link
Copy Markdown
Author

@stonebuzz can I send it to the client ?

Comment thread inc/common.class.php Outdated
Comment thread inc/common.class.php
Comment on lines +46 to +49
return implode(', ', array_filter(array_map(
static fn($group_id) => Toolbox::stripTags(Dropdown::getDropdownName('glpi_groups', $group_id)),
$group_ids,
)));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

There’s something I’m not fully grasping in this PR.

In GLPI, an object can be assigned to a group using a one-to-one relationship (i.e., the object contains a groups_id or groups_id_tech column, for example in glpi_itilcategories).

Objects can also be assigned to multiple groups through an intermediate relation table, glpi_groups_items (cf: glpi_computers).

These objects rely on that relation table when they use the AssignableItem trait.

Here (unless I’m mistaken), we are only handling the first case.

@Mary-Clb

Copy link
Copy Markdown
Author

following @stonebuzz comment :

getGroupName() now explicitly distinguishes the two group relationship cases you raised:

  • Many-to-many ($item instanceof AssignableItemInterface, e.g. Computer, Monitor, Printer...) : queries glpi_groups_items directly, filtered by type (normal/tech), instead of implicitly relying on the conversion already done internally by the AssignableItem trait.
  • One-to-one (direct groups_id/groups_id_tech column, e.g. ITILCategory) : unchanged.

@Mary-Clb
Mary-Clb requested a review from Rom1-B July 21, 2026 13:15

@Rom1-B Rom1-B left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

There are no tests to validate the handling of the groups_id/groups_id_tech variables, whether they are an array or a scalar value. This plugin does not yet include a PHPUnit configuration. Could you please add it?

Comment thread inc/common.class.php
Comment on lines +46 to +62
if ($item instanceof AssignableItemInterface) {
// Many-to-many relation, stored in the glpi_groups_items pivot table
global $DB;
$it = $DB->request([
'SELECT' => 'groups_id',
'FROM' => Group_Item::getTable(),
'WHERE' => [
'itemtype' => $item::class,
'items_id' => $item->getID(),
'type' => $group_type,
],
]);
$group_ids = array_column(iterator_to_array($it), 'groups_id');
} else {
// One-to-one relation, direct column on the item (e.g. glpi_itilcategories)
$group_ids = (array) ($item->fields[$field] ?? 0);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The AssignableItemInterface branch re-queries glpi_groups_items directly, but this is redundant: AssignableItem::post_getFromDB() (src/Glpi/Features/AssignableItem.php) already calls loadGroupFields() on every getFromDB(), which populates $item->fields['groups_id'] / ['groups_id_tech'] as arrays of group ids straight from glpi_groups_items. Every caller of getGroupName() here reaches it through PluginPdfCommon::addHeader()$this->obj->getFromDB($ID), so that array is always already loaded by the time this runs.

The else branch's (array) ($item->fields[$field] ?? 0) already covers both shapes uniformly (an array cast is a no-op on an array, and wraps a scalar id in a single-element array), which is exactly the simplification you suggested and had applied in commit 1e1b4f5 before it was reverted by 8231424 ("handle many-to-many groups"). Dropping the instanceof branch removes an extra DB query per group field per item and directly resolves stonebuzz's "significant amount of duplicated code" comment, since it also brings this function in line with what AssignableItem already guarantees instead of adding a parallel lookup.

Comment thread inc/computer.class.php
Comment on lines 88 to +99
$pdf->displayLine(
'<b><i>' . sprintf(
__('%1$s: %2$s'),
__('Group') . '</i></b>',
Dropdown::getDropdownName(
'glpi_groups',
$computer->fields['groups_id'],
),
self::getGroupName($computer),
),
'<b><i>' . sprintf(
__('%1$s: %2$s'),
__('Group in charge of the hardware') . '</i></b>',
self::getGroupName($computer, Group_Item::GROUP_TYPE_TECH),
),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This introduces a duplicate "Group in charge of the hardware" field on the Computer PDF. PluginPdfCommon::mainLine($pdf, $computer, 'group-model') (called at inc/computer.class.php:67, unchanged by this PR) already renders "Group in charge of the hardware" paired with "Model" via the group-model case in common.class.php:538-553. The line added here renders the same tech-group value again, paired with "Group", where the pre-PR code only paired "Group" with "UUID". The result is that the tech group appears twice in the generated PDF, and "UUID" is pushed into an extra half-empty row.

Suggested change
);
$pdf->displayLine(
'<b><i>' . sprintf(
__('%1$s: %2$s'),
__('Group') . '</i></b>',
self::getGroupName($computer),
),
'<b><i>' . sprintf(
__('%1$s: %2$s'),
__('UUID') . '</i></b>',
$computer->fields['uuid'],
),
);

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants