Product
Catalog product. Identified externally by `code` (per-negocio business code, e.g. "REM-001"). There is NO numeric `id` field on the wire — every endpoint that addresses a product takes its `code` in the URL. Prices live ON THE OPTIONS, never on the product itself — there is no :price field on Product. Every product is exposed as a set of options (≥ 1). `images` is the unified image surface: a CONCATENATION of producto-uploaded images and every option's currently-attached file. Options reference an entry in this list via `image_id`. Today the same logical image attached to two options appears as two entries (clones with identical urls, distinct eids); post-migration shared images dedup to a single entry. Consumers treat `images` as the canonical list either way — to add an image, POST it via /products/{code}/images, then PUT a product with the desired option's `image_id` set to the returned id.
Ejemplo
Campo
| Campo | Tipo | Flags | Notas |
|---|---|---|---|
code | string | Per-negocio product code (the external identifier). Optional on POST — if omitted, the server autogenerates one ("AUTO-NNNNNN") and echoes it back. Unique within the negocio. | |
name | string | requerido | |
description | string | Short description (one paragraph). | |
details | string | Long-form details (care instructions, materials, multiple paragraphs OK). | |
visible | boolean | Storefront visibility. | |
stock | integer | Product-level stock-count. Distinct from option stock. | |
categories | string[] | Array of category ids; all must belong to this negocio. | |
images | Image[] | sólo lectura | Concatenation of producto-uploaded images + every option's currently-attached file. Add via POST /products/{code}/images, remove via DELETE /products/{code}/images/{image-id}; option assignment is done via the option DTO's image_id field. |
options | Option[] | requerido | Required on create (≥ 1). On update, omit to keep the existing set; an empty [] is rejected. |
