| Flag | IconColumn (toggle) ★ | bool | stored: deals.flag — clickable per row; surfaces the deal in the weekly-review "Flagged" section | — |
| Company / Contact | TextColumn::url() / TextColumn; row → detail | FK + string | stored: deals.customer_id (nullable pre-conversion), deals.contact_name. Row links to crm-deal-detail.html | customers.id |
| Deal | TextColumn | string | stored: deals.title | — |
| Source | TextColumn badge; SelectFilter | enum | stored: deals.source — Alibaba / Referral / Exhibition / Website / WhatsApp-social | — |
| Producing entity | TextColumn badge | FK | stored: deals.factory_id — CRM is visible to sales across granted factories, deals pin the producer early | factories.id |
| Stage | SelectColumn badge; tab filter | enum | stored: lead → quoted → sampling → ready_to_convert → won / lost. Sampling mirrors the linked rnd_request 5-status; Won gets "Create SO" prefilled | — |
| Trial # chip | Badge beside the Stage badge (Sampling rows only) | DERIVED | DERIVED — no new column: max(trial_no) on rnd_progress_log via the deal's linked rnd_request; amber at 3–4 trials, red at 5+ | rnd_progress_log |
| Est. value | TextColumn::numeric()->summarize(Sum) | money | stored: deals.est_value | — |
| People (PIC) | Chips (primary + collaborators) | pivot | stored: deal_pics (deal_id, user_id, is_primary) — one is_primary owner (shown in list) + N collaborators | users.id |
| Next action / Last activity | TextColumn; diary relation manager on detail | string + date / relation | stored: deals.next_action, deals.next_action_date; last activity derived from deal_activities (append-only) | deal_activities |
| Lost reason | Select + Textarea (on detail, stage=lost) | enum + text | stored: deals.lost_reason (Price / Lead time / MOQ too high / Went with competitor / No budget-timing / Ghosted / Spec mismatch), deals.lost_note — feeds the Objections breakdown | — |
| Activity diary | RelationManager (append-only) on detail | child table | stored: deal_activities (type enum Call / WhatsApp / Meeting / Email / Quote sent / Sample sent / Note, note, at, by_user_id, next_follow_up_date) — insert-only | deals.id / users.id |
| Weekly review | Widget / panel below the list | derived | derived: Moved this week / Gone quiet (no diary contact >7d) / Flagged (deals.flag) / Stuck too long (days-in-stage) / Won-Lost this week + Objections breakdown over deals.lost_reason | deals / deal_activities |
| New deal (CreateAction slideOver) | Filament CreateAction, slideOver | form → insert | create writes deals(customer_id nullable, contact_name, title, factory_id, stage default lead, est_value, source) + deal_pics rows (primary + collaborators) + first deal_activities note row in one transaction | deals / deal_pics / deal_activities |