Customer bike
A specific bike owned by a specific customer. Surfaces on the customer profile, links to service tickets, persists across multiple visits.
Why this entity exists separately from customers
A customer can own multiple bikes; each bike has its own service history. Tracking bikes as first-class lets us:
- Show "this bike was here in 2022 for a chain replacement" on the next visit
- Surface relevant service intervals ("brake bleed due in 1000 km")
- Distinguish "Bob's gravel bike" from "Bob's mountain bike" when he drops one off
Table: customer_bikes
| Column | Type | Notes |
|---|---|---|
id | INTEGER PK | |
customer_id | INTEGER FK → customers | |
nickname | TEXT | Optional human label ("gravel bike") |
brand | TEXT | Specialized, Trek, Devinci, etc. |
model | TEXT | Model name |
model_year | INTEGER | Filter year 0001 sentinel from AIM |
frame_size | TEXT | "M", "54cm", "L/XL" |
serial_number | TEXT | PII; the bike's frame serial |
color | TEXT | |
wheel_size | TEXT | "29", "27.5", "700c", "650b" |
purchase_date | DATE | When the customer bought it (from us or elsewhere) |
purchased_from_us | INTEGER (0/1) | Sourced from a Helm sale vs externally owned |
notes | TEXT | Free text about the bike |
created_at, updated_at | TEXT |
How bikes get into the system
Three paths:
- Sale from this shop — at the point of sale of a new bike (category cat_pk=448 in AIM,
inventory_categories.is_bikein Helm), acustomer_bikesrow is automatically created and linked. - Drop-off form — when a customer drops off a bike for service, the operator picks the customer, then either selects an existing bike or adds a new one inline.
- Manual add — on the customer profile, the operator can "+ Add bike" with serial/model/year fields.
Behaviors
Service history
GET /api/customer-bikes/{id}/tickets returns all service tickets for this bike, with their statuses and dates. Used in:
- The customer profile's bike card hover state
- The drop-off form (shows previous tickets when this bike is selected)
- The ticket detail page (when the bike has history)
Delete
Blocked if the bike has any service_tickets not in picked_up or cancelled. Otherwise hard DELETE with audit log.
Edit
Editable fields in-situ on the customer profile: nickname, frame_size, color, notes. The serial number and brand/model are editable but flagged as audit-relevant ("are you sure? this is the identity of the bike").
Migrated from AIM
For Swicked: sales-derived bikes from AIM's scsasld (sales line items) where the line is in BIKES category (sccat.cat_pk = 448). The migration:
- Joins
scsasld→scsasrl(serial registry) onsld_serno→srl_sernoto get the frame serial - Joins to
custfor the customer - Joins to
pim(product image / product master) for brand + model - Creates
customer_bikesrows withpurchased_from_us = 1
Bikes purchased elsewhere are not in AIM data; they enter Helm via service drop-offs.
In-situ editing surface
On the customer profile, in the Bikes section, in edit mode:
- Each bike card: drag handle for reordering, × for remove
- Click any field to edit inline
-
- Add bike opens a quick form (brand, model, year, size, serial)
On the service drop-off form:
- Picking the customer reveals their bikes
- Picking a bike shows its history; selecting "New bike" reveals the inline add form
See also
- Customer entity
- Service ticket entity
- Service ticket lifecycle
- Migration from AIM