# `PhoenixKitCatalogue.Catalogue.Duplication`
[🔗](https://github.com/BeamLabEU/phoenix_kit_catalogue/blob/v0.19.1/lib/phoenix_kit_catalogue/catalogue/duplication.ex#L1)

Copies of items and categories — the "Duplicate" bulk action.

An item copy is the row plus everything the item form edits alongside
it: multilang `data` (translations, custom fields, featured-image
pointer), attribute-set attachments, the attribute-group assignment,
the CURRENT supplier rows, catalogue rules, and its files folder. Files
are not re-uploaded: the copy gets its own Storage folder with a
`FolderLink` to each of the source's files, so removing one from the
copy never touches the original. Supplier rows get a FRESH comment
thread — a discount promised on the original stays on the original.
Comments and activity history are not copied.

A category copy is the category row (same treatment) plus its whole
subtree: every active child category and every non-trashed item, each
keeping its own name and position. Only the top-level copy is renamed
("Alpha (copy)") and slotted right after its source.

Everything for one copy runs in one transaction; the bulk functions
run one transaction per source so a refused row does not undo the
others, and emit ONE batch event per touched catalogue afterwards.

Public surface is re-exported from `PhoenixKitCatalogue.Catalogue`.

# `bulk_result`

```elixir
@type bulk_result() ::
  {:ok, %{created: non_neg_integer(), errors: [{Ecto.UUID.t(), term()}]}}
```

# `bulk_duplicate_categories`

```elixir
@spec bulk_duplicate_categories(
  [Ecto.UUID.t()],
  keyword()
) :: bulk_result()
```

Copies several categories (each with its subtree); one batch event per catalogue.

# `bulk_duplicate_items`

```elixir
@spec bulk_duplicate_items(
  [Ecto.UUID.t()],
  keyword()
) :: bulk_result()
```

Copies several items; one transaction each, one `:item` batch event per
catalogue. Pass `catalogue_uuid:` to refuse items outside that catalogue
(`:wrong_catalogue_scope`) — the uuids are client-captured.

# `duplicate_category`

```elixir
@spec duplicate_category(
  PhoenixKitCatalogue.Schemas.Category.t(),
  keyword()
) ::
  {:ok,
   %{
     category: PhoenixKitCatalogue.Schemas.Category.t(),
     categories: non_neg_integer(),
     items: non_neg_integer()
   }}
  | {:error, term()}
```

Copies one category with its subtree. Options as `duplicate_item/2`
(`:parent_uuid` instead of `:category_uuid`). Returns the new category and
how many descendant categories and items came along.

# `duplicate_item`

```elixir
@spec duplicate_item(
  PhoenixKitCatalogue.Schemas.Item.t(),
  keyword()
) :: {:ok, PhoenixKitCatalogue.Schemas.Item.t()} | {:error, term()}
```

Copies one item. Options:

  * `:category_uuid` — put the copy in this category (`nil` = uncategorized)
    instead of the source's; the catalogue follows the category.
  * `:suffix` — append " (copy)" to the name (default `true`).
  * `:keep_position` — keep the source's position number verbatim instead of
    slotting the copy right after the source (default `false`; used for
    subtree copies where the whole sibling set moves together).
  * `:actor_uuid`, `:mode`, `:broadcast` — as elsewhere in the context.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
