What it does
A vehicle document (RF15, US18) is a file in the Organization's private storage filed among one car's documents with a kind (registration, title, insurance, tax, purchase_invoice, loan_agreement, warranty, inspection, receipt, other), a title and an optional due date. The bytes travel by the template's direct upload (Documents and uploads): uploads.prepare, one signed PUT or S3 multipart above 16 MiB, uploads.complete. There is no second storage path.
- Filing checks that the car and the uploaded file belong to the Active Organization. The client generates the id, so a replay answers the first filing (RNF04).
- A due date is a
reminder(basisdate, titled like the document,reminder.document_id), written in the same transaction. Changing the date moves it and reopens it if it was done; clearing the date or deleting the document deletes it. These reminders are alerts, not services:reminder.list(M-Manutencao, the M-Veiculo next service) leaves them out. - A document is mutable (kind, title, due date) and deletable, unless it is the receipt of an expense or a service (
receiptOf): that record keeps its proof (CONFLICT). Deleting a document leaves the uploaded file in Files. - Opening one asks
vehicleDocument.url, a presigned GET of its key that expires after 300 seconds (RNF05).
| Procedure | Failures |
|---|---|
vehicleDocument.list { vehicleId, limit ≤ 100 } (newest first) | INPUT_VALIDATION, NOT_FOUND (car) |
vehicleDocument.create { id, vehicleId, fileId, kind, title, dueOn? } | INPUT_VALIDATION, NOT_FOUND (car, file), CONFLICT (id taken) |
vehicleDocument.update { id, kind?, title?, dueOn? } (dueOn: null clears) | INPUT_VALIDATION, NOT_FOUND |
vehicleDocument.delete { id } | NOT_FOUND, CONFLICT (a receipt) |
vehicleDocument.url { id } | NOT_FOUND |
All are for Members of the Organization; a non-Member gets FORBIDDEN.
In the app
M-Documentos (features/vehicle-document/vehicle-document.screen.tsx, route /documents?vehicleId=) opens from M-Veiculo's Documents pill and segment. The segmented All | Garage | Insurance | Taxes filters the two-column grid (Garage is every kind but insurance and tax; one column at the largest text sizes). The status line and the chip's tint come from vehicle-document.model.ts: expired or due within 30 days is warning, a registration or inspection in force is "valid" in positive, another due date "expires …", and without one a title is "on file", a tax "paid", the rest "private" (text2). A card opens a sheet: a photo shows from its signed link, a PDF opens in the in-app browser, then edit or delete (confirmed). "Add document" and ⋯ open the form (vehicle-document.form.screen.tsx): a photo from the library or a PDF from Files (expo-document-picker), the type, the name (the type's by default) and the due date. The file uploads once per pick, so a retry after a failed filing reuses it. autofolio://dev/M-Documentos opens the seeded M3's six documents.
Data and isolation
vehicle_document and reminder have row-level security by Active Organization, and composite foreign keys keep a document on its car and its file in its Organization; uploaded_file keys are CHECKed under orgs/<organizationId>/. The tests prove another Organization gets NOT_FOUND on every procedure and that every signed key is under the caller's prefix (RNF06). No audit_log event is written; titles and file names never go to logs.