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

# Agrupar linhas de bundle de outro app

> Use setInternalProperties para dizer ao Aftersell Cart quais linhas pertencem ao mesmo bundle, para que renderizem como um único item em vez de várias linhas sem relação.

A maioria dos apps de bundle monta um bundle adicionando **cada componente como sua própria linha do carrinho** e depois vinculando-as com line item properties de design próprio. A Ajax API da Shopify entrega essas linhas ao carrinho sem nenhuma indicação de que elas pertencem umas às outras, então por padrão o drawer mostra um bundle de três partes como três itens sem relação, cada um com seu próprio preço e seletor de quantidade.

`setInternalProperties` é como você diz ao carrinho que elas são uma coisa só.

<div id="how-grouping-works">
  ## Como o agrupamento funciona
</div>

O carrinho agrupa linhas com base em duas **propriedades canônicas**. Ele não conhece os nomes de propriedade do seu app de bundle, então você traduz: leia o que quer que o app escreveu e carimbe o par canônico em cada linha com um [line transform](/pt/aftersell/cart/sdk-hooks#registerlinetransform).

| Propriedade                   | Obrigatória | Valor                                                       |
| ----------------------------- | ----------- | ----------------------------------------------------------- |
| `_aftersell_cart_bundle_id`   | Sim         | Um ID compartilhado. Toda linha com o mesmo ID é um bundle. |
| `_aftersell_cart_bundle_role` | Não         | Defina como `parent` na linha que o bundle deve exibir.     |

Elas passam por `setInternalProperties`, não pela Shopify. Elas são uma **camada só de renderização**: nunca chegam a `properties`, nunca são persistidas na Shopify e nunca aparecem no pedido.

<div id="step-1-find-out-what-your-app-writes">
  ## Passo 1: descubra o que seu app escreve
</div>

Cada app de bundle nomeia suas propriedades de forma diferente, então comece olhando um carrinho real. Adicione um bundle na sua loja e execute isto no console do navegador:

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

Você está procurando uma propriedade compartilhada entre as linhas do bundle. Geralmente é uma propriedade oculta (o nome começa com `_`) contendo um ID, uma referência ou o nome do bundle. Algo como `_bundle_id`, `_bundle_ref` ou `_parent_id` é típico. Anote a chave exata e se uma linha está marcada como o produto principal.

<div id="step-2-map-it-onto-the-canonical-properties">
  ## Passo 2: mapeie para as propriedades canônicas
</div>

Cole em **Cart settings → Custom script → Initialization**, substituindo os nomes de propriedade pelos que você encontrou:

```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',
  });
});
```

Essa é a integração inteira. Assim que duas ou mais linhas compartilham um ID, o carrinho as agrupa em um bundle.

<Note>
  Se seu app não marca um produto principal, deixe `_aftersell_cart_bundle_role` de fora por completo. O carrinho escolhe uma âncora para você.
</Note>

<div id="what-you-get">
  ## O que você ganha
</div>

Uma vez que as linhas estão agrupadas, a linha âncora carrega um [objeto `bundle`](/pt/aftersell/cart/sdk-cart-object#bundles) e o drawer renderiza o bundle como um único item:

* **Os filhos ficam aninhados sob a âncora** em vez de aparecer como linhas separadas.
* **A quantidade é atômica.** Alterar a quantidade do bundle escala todos os membros juntos, usando a proporção `perAnchorQty` de cada filho, então um bundle com dois de um componente mantém essa relação de dois para um.
* **A remoção é atômica.** Remover o bundle remove todas as linhas membro em uma única requisição, em vez de deixar componentes órfãos para trás.
* **Uma única linha de preço.** O que ela mostra segue a configuração de **preço do bundle** no bloco [Cart items](/pt/aftersell/cart/cart-items-block): o total de todos os membros, ou apenas o preço do produto principal.

<div id="how-the-anchor-is-chosen">
  ## Como a âncora é escolhida
</div>

A âncora é a linha que o bundle exibe. O carrinho a escolhe nesta ordem:

1. A linha com `_aftersell_cart_bundle_role` definido como `parent`.
2. Caso contrário, o membro de **maior preço**.
3. Caso contrário, o primeiro membro no carrinho.

O fallback por preço geralmente está certo, já que apps de bundle costumam colocar o desconto no produto principal. Defina o role explicitamente quando não estiver, por exemplo quando o produto principal é o item mais barato ou é grátis.

<div id="rules-worth-knowing">
  ## Regras que vale a pena conhecer
</div>

* **Um bundle precisa de pelo menos duas linhas.** Uma única linha com um ID de bundle é deixada em paz e renderiza normalmente.
* **Bundles nativos da Shopify já são tratados.** Linhas que a própria Shopify marca como componentizadas são ignoradas por este agrupamento e adaptadas automaticamente. Você só precisa disto para apps que adicionam linhas separadas.
* **O transform executa em toda renderização.** Mantenha-o leve e livre de efeitos colaterais. Não chame ações nem faça fetch de dentro dele.
* **A mesclagem é aditiva.** Suas propriedades se mesclam com quaisquer definidas por outro transform. Em um conflito genuíno pela mesma chave, o último transform registrado vence.
* **O agrupamento executa depois de ocultar e renomear**, e antes da ordenação. Então uma linha que você oculta com `setHidden` nunca vira parte de um bundle, e um [comparator](/pt/aftersell/cart/sdk-hooks#registerlinecomparator) vê a âncora, não os filhos.

<Warning>
  **Filhos agrupados saem de `state.items`.** Uma vez que as linhas são agrupadas em um bundle, só a âncora aparece em `getCart().items` e nos payloads de evento; os filhos vão para `anchor.bundle.children`. Eles também deixam de contar para `itemCount`.

  O **total do carrinho não é afetado**, porque os totais vêm direto da Shopify. O agrupamento muda a apresentação, nunca o que o comprador paga.
</Warning>

<div id="reading-a-bundle-back">
  ## Lendo um bundle de volta
</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);
    });
  });
});
```

Para agir sobre as linhas de um bundle, use `bundle.memberKeys`, que contém a `key` de cada membro, incluindo a âncora.

<div id="using-it-for-other-things">
  ## Usando para outras coisas
</div>

O agrupamento de bundle é para o que `setInternalProperties` foi construído, mas a camada é um canal geral para **dados só de renderização que você deriva de uma linha**. Qualquer coisa que você colocar lá é legível em `line.internalProperties` e em um [bloco Custom code](/pt/aftersell/cart/custom-code-blocks), sem tocar no carrinho real:

```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}` });
  }
});
```

Use-a quando o valor é **derivado** e só de exibição. Se o dado precisa sobreviver até o pedido, ele pertence a uma line item property real, definida com um input `properties[...]` oculto no formulário do produto para que chegue ao Shopify seja quem for que realize a adição.

<div id="where-to-go-next">
  ## Para onde ir a seguir
</div>

* **[`registerLineTransform`](/pt/aftersell/cart/sdk-hooks#registerlinetransform)**: o hook pelo qual isto executa.
* **[Cart object](/pt/aftersell/cart/sdk-cart-object#bundles)**: o formato de `bundle` e seus filhos.
* **[Cart items block](/pt/aftersell/cart/cart-items-block)**: a configuração de preço do bundle.
