> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aftersell.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Regrouper les lignes de bundle d'une autre application

> Utilisez setInternalProperties pour indiquer au panier Aftersell quelles lignes appartiennent au même bundle, afin qu'elles s'affichent comme un seul article au lieu de plusieurs lignes sans lien.

La plupart des applications de bundle construisent un bundle en ajoutant **chaque composant comme sa propre ligne de panier**, puis en les liant avec des propriétés de ligne de leur propre conception. L'API Ajax de Shopify transmet ces lignes au panier sans aucune indication qu'elles vont ensemble, donc par défaut le tiroir affiche un bundle en trois parties comme trois articles sans lien, chacun avec son propre prix et son propre sélecteur de quantité.

`setInternalProperties` est la façon d'indiquer au panier qu'elles ne font qu'un.

<div id="how-grouping-works">
  ## Comment fonctionne le regroupement
</div>

Le panier regroupe les lignes sur deux **propriétés canoniques**. Il ne connaît pas les noms de propriétés de votre application de bundle, donc vous traduisez : lisez ce que l'application a écrit, et apposez la paire canonique sur chaque ligne avec une [transformation de ligne](/fr/aftersell/cart/sdk-hooks#registerlinetransform).

| Propriété                     | Requise | Valeur                                                                         |
| ----------------------------- | ------- | ------------------------------------------------------------------------------ |
| `_aftersell_cart_bundle_id`   | Oui     | Un ID partagé. Chaque ligne portant le même ID forme un bundle.                |
| `_aftersell_cart_bundle_role` | Non     | Définissez-la à `parent` sur la ligne sous laquelle le bundle doit s'afficher. |

Elles passent par `setInternalProperties`, pas par Shopify. C'est une **surcouche d'affichage uniquement** : elles n'atteignent jamais `properties`, ne sont jamais persistées vers Shopify et n'apparaissent jamais sur la commande.

<div id="step-1-find-out-what-your-app-writes">
  ## Étape 1 : découvrez ce que votre application écrit
</div>

Chaque application de bundle nomme ses propriétés différemment, commencez donc par examiner un vrai panier. Ajoutez un bundle sur votre boutique, puis exécutez ceci dans la console du navigateur :

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.actions.getCart().items.forEach((line) => {
  console.log(line.title, line.properties);
});
```

Vous cherchez une propriété partagée entre les lignes du bundle. C'est généralement une propriété masquée (le nom commence par `_`) contenant un ID, une référence ou le nom du bundle. Quelque chose comme `_bundle_id`, `_bundle_ref` ou `_parent_id` est typique. Notez la clé exacte, et si une ligne est marquée comme produit principal.

<div id="step-2-map-it-onto-the-canonical-properties">
  ## Étape 2 : mappez-la sur les propriétés canoniques
</div>

Collez dans **Cart settings → Custom script → Initialization**, en remplaçant les noms de propriétés par ceux que vous avez trouvés :

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerLineTransform((line) => {
  const props = line.properties;
  if (!props) return;

  const bundleId = props._bundle_id;
  if (!bundleId) return;

  line.setInternalProperties({
    _aftersell_cart_bundle_id: bundleId,
    // Mark the main product so the bundle renders under it.
    _aftersell_cart_bundle_role: props._bundle_role === 'main' ? 'parent' : 'child',
  });
});
```

C'est toute l'intégration. Dès que deux lignes ou plus partagent un ID, le panier les regroupe en un seul bundle.

<Note>
  Si votre application ne marque pas de produit principal, omettez complètement `_aftersell_cart_bundle_role`. Le panier choisit une ancre pour vous.
</Note>

<div id="what-you-get">
  ## Ce que vous obtenez
</div>

Une fois les lignes regroupées, la ligne d'ancrage porte un [objet `bundle`](/fr/aftersell/cart/sdk-cart-object#bundles) et le tiroir affiche le bundle comme un seul article :

* **Les enfants s'imbriquent sous l'ancre** au lieu d'apparaître comme des lignes séparées.
* **La quantité est atomique.** Changer la quantité du bundle met à l'échelle chaque membre ensemble, en utilisant le ratio `perAnchorQty` de chaque enfant, de sorte qu'un bundle avec deux exemplaires d'un composant conserve cette relation de deux pour un.
* **La suppression est atomique.** Supprimer le bundle supprime chaque ligne membre en une seule requête, plutôt que de laisser des composants orphelins.
* **Une seule ligne de prix.** Ce qu'elle affiche suit le paramètre **bundle price** du bloc [Cart items](/fr/aftersell/cart/cart-items-block) : le total de tous les membres, ou le prix du produit principal seul.

<div id="how-the-anchor-is-chosen">
  ## Comment l'ancre est choisie
</div>

L'ancre est la ligne sous laquelle le bundle s'affiche. Le panier la choisit dans cet ordre :

1. La ligne avec `_aftersell_cart_bundle_role` définie à `parent`.
2. Sinon, le membre **au prix le plus élevé**.
3. Sinon, le premier membre dans le panier.

Le repli sur le prix est généralement correct, car les applications de bundle ont tendance à placer la réduction sur le produit principal. Définissez le rôle explicitement lorsque ce n'est pas le cas, par exemple lorsque le produit principal est l'article le moins cher ou est gratuit.

<div id="rules-worth-knowing">
  ## Règles à connaître
</div>

* **Un bundle a besoin d'au moins deux lignes.** Une ligne seule portant un ID de bundle est laissée telle quelle et s'affiche normalement.
* **Les bundles natifs Shopify sont déjà gérés.** Les lignes que Shopify lui-même marque comme componentisées sont ignorées par ce regroupement et adaptées automatiquement. Vous n'en avez besoin que pour les applications qui ajoutent des lignes séparées.
* **La transformation s'exécute à chaque rendu.** Gardez-la légère et sans effets de bord. N'appelez pas d'actions et ne faites pas de fetch depuis son intérieur.
* **La fusion est additive.** Vos propriétés fusionnent avec celles définies par une autre transformation. En cas de véritable conflit sur la même clé, la dernière transformation enregistrée l'emporte.
* **Le regroupement s'exécute après le masquage et le renommage**, et avant le tri. Ainsi, une ligne que vous masquez avec `setHidden` ne devient jamais partie d'un bundle, et un [comparateur](/fr/aftersell/cart/sdk-hooks#registerlinecomparator) voit l'ancre, pas les enfants.

<Warning>
  **Les enfants regroupés quittent `state.items`.** Une fois les lignes intégrées dans un bundle, seule l'ancre apparaît dans `getCart().items` et dans les charges utiles d'événements ; les enfants passent dans `anchor.bundle.children`. Ils cessent aussi de compter dans `itemCount`.

  Le **total du panier n'est pas affecté**, car les totaux viennent directement de Shopify. Le regroupement change la présentation, jamais ce que l'acheteur paie.
</Warning>

<div id="reading-a-bundle-back">
  ## Relire un bundle
</div>

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('cart_updated', (state) => {
  state.items.forEach((line) => {
    if (!line.bundle) return;
    console.log(line.title, 'is a bundle of', line.bundle.children.length, 'items:');
    line.bundle.children.forEach((child) => {
      console.log('  ', child.quantity, 'x', child.title);
    });
  });
});
```

Pour agir sur les lignes d'un bundle, utilisez `bundle.memberKeys`, qui contient la `key` de chaque membre, y compris l'ancre.

<div id="using-it-for-other-things">
  ## L'utiliser pour d'autres choses
</div>

Le regroupement de bundles est ce pour quoi `setInternalProperties` a été conçu, mais la surcouche est un canal général pour des **données d'affichage uniquement que vous dérivez d'une ligne**. Tout ce que vous y placez est lisible dans `line.internalProperties` et dans un [bloc Custom code](/fr/aftersell/cart/custom-code-blocks), sans toucher au vrai panier :

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerLineTransform((line) => {
  if (line.properties?._preorder_ship_date) {
    line.setInternalProperties({ _badge: `Ships ${line.properties._preorder_ship_date}` });
  }
});
```

Utilisez-le lorsque la valeur est **dérivée** et destinée uniquement à l'affichage. Si les données doivent survivre jusqu'à la commande, elles ont leur place dans une véritable propriété de ligne, définie avec une entrée cachée `properties[...]` sur le formulaire produit pour qu'elle parvienne à Shopify quel que soit celui qui effectue l'ajout.

<div id="where-to-go-next">
  ## Pour aller plus loin
</div>

* **[`registerLineTransform`](/fr/aftersell/cart/sdk-hooks#registerlinetransform)** : le hook par lequel tout cela passe.
* **[Objet cart](/fr/aftersell/cart/sdk-cart-object#bundles)** : la forme de `bundle` et de ses enfants.
* **[Bloc Cart items](/fr/aftersell/cart/cart-items-block)** : le paramètre de prix de bundle.
