# TSHIRTORDER-1628 — Tryck (print) product: backend API notes for frontend

Backend is done and verified (migrations applied, manual test run against real service methods with a rolled-back DB transaction — 17/17 checks passed). This note describes exactly what changed in the API response/payload so the React frontend can build the "Tryck" product type + order auto-add behavior. Base path: `/api/v1` (Bearer token auth, same as every other endpoint).

## 1. Product — new field `productKind`

Endpoints: `GET/POST /api/v1/products/`, `GET/POST /api/v1/products/{id}`

- `productKind`: `string`, values `"normal"` | `"tryck"`.
- **Optional on create.** If you don't send it, it defaults to `"normal"` — nothing changes for existing/normal products, you don't need to touch any existing create/edit calls.
- To create or edit a **Tryck** product, you must send `productKind: "tryck"` explicitly. There's no auto-detection.
- Returned on every read (list/detail) so you know which layout to render — see the main issue doc (`tryck-print-product.md`, "Chi tiết theo mockup" section) for exactly which fields to hide/show when `productKind === "tryck"` (2 tabs: "Information" + "Connect products").

## 2. Product — connecting a Tryck product to normal products

Endpoints: same product create/update endpoints as above.

- `connectedProductIds`: `number[]` — array of **normal product ids** to link to this Tryck product. This is the payload for the "Connect products" tab.
- **Full replace, not incremental.** Every time you send this field, it replaces the entire connection list for that Tryck product. Always send the complete list of currently-selected normal product ids (not a diff).
- Only takes effect when the product being saved has `productKind === "tryck"`. Sending it on a normal product is a silent no-op (ignored server-side, nothing is written).
- Not required on every save — omit the field entirely (don't send the key at all) if you're not touching connections in that particular save (e.g. editing just the price).

Read side, only present in the **detail** response (`GET /api/v1/products/{id}`) when `productKind === "tryck"`:

```jsonc
{
  "productKind": "tryck",
  "connectedProductIds": [123, 456],
  "connectedProducts": [
    { "id": 123, "name": "Basic Tee", "sku": "BT-001" },
    { "id": 456, "name": "Hoodie",    "sku": "HD-002" }
  ]
}
```

Use `connectedProducts` to render the current selection in the picker; `connectedProductIds` is the raw id list if that's more convenient for your form state.

## 3. Product — color / size fields (unchanged field names, UI behavior changes)

- Field names are unchanged: `color` (string) and `size`/`sizeId` (existing fields).
- Per stakeholder decision, these become **free-text input with autocomplete suggestions** (not a fixed dropdown) when `productKind === "tryck"`.
- The autocomplete suggestion source (list of existing values to suggest from) is **not defined yet** — backend is waiting on sample data from the stakeholder. For now, build these as plain free-text inputs; the autocomplete data source will be a follow-up.

## 4. Orders — Tryck lines are fully automatic, no FE payload changes needed

This is the part that needs the least frontend work: **you don't send anything extra to make Tryck lines appear.**

- When you submit an order's `items` array (create `POST /api/v1/orders/` or update `POST /api/v1/orders/{id}`) containing a normal product that has one or more Tryck products connected to it, the backend automatically creates one extra order line **per connected Tryck product** and includes them in the response's `items` array. You don't add them yourself.
- If a Tryck product's `price` is `0`, it's skipped (not added) — backend rule, not something to validate on the frontend.
- **You do not need to keep re-sending auto-added Tryck lines in the `items` payload on every save.** The backend re-discovers and keeps them automatically as long as their parent normal-product line is still present in the payload. Just manage normal product lines as you already do.
- **Removing the normal line removes its Tryck line(s) automatically too** — if you drop a normal product's line from the `items` payload (i.e. simply don't include it in the next save), its auto-added Tryck line(s) are deleted server-side in the same call. No separate delete call needed.

### New fields on each returned order item (`GET /api/v1/orders/{id}`, and in create/update responses)

```jsonc
{
  "orderProductId": 5001,
  "productId": 789,
  "productKind": "tryck",                 // NEW — "normal" | "tryck"
  "tryckParentOrderProductId": 5000,        // NEW — null for normal lines / manually-added tryck lines.
                                             //        For an auto-added tryck line, this is the orderProductId
                                             //        of the normal line that triggered it.
  "quantity": 1,
  "price": 20,
  "priceKickback": 0,
  ...
}
```

Use `productKind === "tryck"` to render a Tryck line differently in the order UI (e.g. a badge, non-draggable, etc.), and `tryckParentOrderProductId` if you need to visually group it under its parent normal line.

**Caveat:** the backend does not currently block you from resending an auto-added Tryck line's `orderProductId` in the `items` payload with a different `quantity`/`price` — if you do, it's saved like any other line (the auto-sync logic only *creates/removes* lines, it doesn't re-validate one you explicitly resubmit). Per the stakeholder's decision, quantity is fixed at `1` and price/kickback shouldn't be user-editable for these lines, so the simplest safe approach is: **don't let the UI edit Tryck lines, and don't include them yourself in the `items` payload** — let the backend manage them entirely.

## 5. Product picker (order create/edit page)

- No change needed. Tryck products are **not excluded** from the normal product picker/search used when adding items to an order (confirmed with stakeholder) — a user can still manually add a Tryck product as its own line if needed (unrelated to the auto-add mechanism above; such a manually-added line has `tryckParentOrderProductId: null`).

## 6. Ecom (customer-facing webshop) — FYI only, not an API you consume

- Tryck products are excluded server-side from all `/ecom/{channelEcomId}/...` listing/search/detail pages. This is the separate server-rendered webshop, not the `/api/v1` admin API — no action needed on your side, just noting it for completeness.
