| Request # | TextColumn (series RND-{yyyy}-{seq,5}) | string | stored: rnd_requests.code | — |
| Customer | TextColumn::url() → customer card | FK | stored: rnd_requests.customer_id — Sales creates the request from the customer conversation | customers.id |
| Product | TextColumn | string + enum | stored: product_name, product_type (Sachet / Capsule / Gummy / Drink / Tea) | — |
| Raw mats | TextColumn badge | enum | stored: raw_material_status — Existing (all in item master) / Requesting (waiting supplier samples) | — |
| Trial # | TextColumn (derived) | int | derived: max(progress_log.trial_no) — auto-increments on each log entry of type Trial. Green ✓ when Confirmed, amber at 3–4, red at 5+ | rnd_progress_log |
| Design | TextColumn dot + label (mini-badge) | enum | stored: design_track — design_status ((none) / Brief sent / Draft vN / Approved) + designer_user_id. Parallel side-track advanced by the designer concurrently — NOT a request status, never blocks the R&D flow | users.id |
| Deadline | TextColumn date + days chip | date | stored: rnd_requests.deadline — the ONLY priority signal (founder decision: no priority field). Green ≥ 14d, amber 1–13d, red OVERDUE | — |
| Status | TextColumn badge + Advance-stage Action | enum | enum simplified (founder decision): Requested → In development → Sample sent → Confirmed. Sample feedback FAIL loops back to In development (trial # increments). Cancelled = rejected / withdrawn — terminal, any point. Design is a parallel side-track (not a status); costing is a section within development (not a stage) | — |
| Last update | TextColumn (derived) | relation | derived: latest rnd_progress_log entry (text + date) — status changes are auto-logged, so this never goes stale | rnd_progress_log |
| PIC | SelectColumn → users | FK | stored: pic_user_id — R&D lead assigns; designer is a separate field on the detail | users.id |