01
The problem
Customers forget the service and the MOT, and the garage only remembers when they turn up. The record keeps the history and derives the next dates from it, with nobody typing them in.
02
The model
The vehicle stores its first registration date, mileage and jobs. The last service is the most recent job marked as a service.
| Table | What it holds |
|---|---|
| vehicles | Plate, model, fuel, customer, mileage and first registration date. |
| jobs | Each job: date, mileage, description, amount and whether it counts as a service. |
| reminders | When the last reminder went out, so it isn't repeated. |
03
The date rules
Service: one year or 15,000 km after the last one, whichever comes first. Portuguese MOT for cars: at 4 years, then every 2 years until 8, then every year.
ts
// Revisão: um ano ou 15 000 km depois da última, o que vier primeiro.
export function nextService(v) {
const last = lastService(v) ?? { date: v.firstReg, km: 0 };
const dueKm = last.km + 15_000;
const date = v.km >= dueKm ? today() : addYears(last.date, 1);
return { date, km: dueKm };
}04
What I'd do differently in production
Reminders would go out by text through a provider, with a send log and a limit so the customer isn't bothered twice in the same week.
ts
// IPO de ligeiros: aos 4 anos, de 2 em 2 até aos 8, depois todos os anos.
export function nextIpo(v) {
const due = [4, 6, 8, ...range(9, 40)].map(y => addYears(v.firstReg, y));
const last = lastIpo(v);
return due.find(d => !last || d > last.date);
}