Notes & Item Lifecycle Dates¶
Date: 2026-06-13
Problem / motivation¶
Users need a lightweight, free-form way to attach contextual remarks to records in the system. The first consumer is the Item catalog, but the note should not be item-specific — it should be a generic, polymorphic resource that can hang off any entity in the future (clients, suppliers, orders, …) without a schema change. Separately, the Item catalog needs to track two lifecycle dates that the business already records on paper: when an item was manufactured and when it expires.
In scope¶
- A new top-level
notesresource (model → repo → service → graphql → RBAC → migration → tests). - Notes link polymorphically to any entity via
(entity_type, entity_id), whereentity_typeis the existingSourceTypeenum (the same enumfile_entitiesuses). For this feature the frontend only wires notes to items (SourceType.ITEMS), but nothing in the model restricts it. - CRUD over notes: create, update, list-for-entity, get-by-id, delete.
- Two new nullable columns on
items:manufacture_dateandexpiration_date.
Out of scope¶
- Wiring notes to entities other than items (clients/suppliers/orders/etc.) — the model supports it, but no frontend contract is defined here.
- Per-lot / per-serial expiration tracking. The two dates live on the item row itself, not on inventory lots.
- Mentions, attachments, rich text, threading, or note reactions.
Data model changes¶
- New table
notes(migrationadd_notes_table): idUUID PKcontentText, not nullentity_typeSmallInteger (IntEnum(SourceType)), not nullentity_idUUID, not nullcreated_by_idUUID FK →users.id, not nullcreated_attimestamptz, not null, defaultnow()- Composite index
notes_entity_type_entity_id_idxon(entity_type, entity_id)— the only query pattern is "list notes for this entity". itemstable gains two nullabledatecolumns (migrationadd_dates_to_items):manufacture_date— date the item was manufactured.expiration_date— date the item expires.
Migration chain tip at authoring time: add_parent_id_to_exp_categories.
add_notes_table → down_revision = add_parent_id_to_exp_categories;
add_dates_to_items → down_revision = add_notes_table.
GraphQL surface¶
Queries:
notesForEntity(entityType: SourceType!, entityId: UUID!): [Note!]!note(id: UUID!): Note
Mutations:
createNote(note: NoteInput!): Note!updateNote(note: NoteInput!): Note!deleteNote(id: UUID!): Boolean!
Types:
input NoteInput { id, content, entityType, entityId }— shared create/update input following the quotes/ordersto_orm_model()convention.type Note { id, content, entityType, entityId, createdById, createdAt }
RBAC¶
- New
Path.NOTESandResource.NOTESenum members inapp/graphql/rbac/models/models.py. - Mutations are guarded by
PathPermissionAccess(Path.NOTES). - Backfill:
Path.NOTESadded toscripts/backfill_new_path_permissions.pyNEW_PATHSso existing tenants get aPathPermission(can_view=True)row.
Background tasks / cron¶
None.
Frontend contract¶
- The item detail screen renders a notes panel: call
notesForEntity(entityType: ITEMS, entityId: <itemId>), andcreateNote/updateNote/deleteNotewithNoteInput.entityType = ITEMSandentityId = <itemId>. ItemInputaccepts optionalmanufactureDateandexpirationDate(ISODate).Item/ItemLiteexpose both fields (nullable). The items landing page (ItemLandingPage) also returnsmanufactureDateandexpirationDateso they can be shown/filtered in the list view.
Open questions¶
None — defaults chosen: notes are tenant-visible, editable and deletable by any
user with the NOTES path; created_by_id is retained for audit only.
What shipped¶
- New
notesmodule:Notemodel,NoteRepository,NoteService,NoteInput/NoteGraphQL types, andNoteQueries/NoteMutations(auto-discovered into the schema). Files under app/graphql/notes/. Noteregistered in load_models.py.Path.NOTES+Resource.NOTESadded to rbac/models/models.py;NOTESadded to the medical tenant path list in tenant_type.py (commercial already grants all non-health paths);Path.NOTESappended toNEW_PATHSin backfill_new_path_permissions.py.items.manufacture_dateanditems.expiration_datecolumns added to theItemmodel,ItemInput.to_orm_model, andItemLiteResponse(so bothItemandItemLiteexpose them).- Migrations:
add_notes_tablethenadd_dates_to_items. - Tests under tests/graphql/notes/: repository (real-DB CRUD) + service (mapping/delegation).
Future additions¶
- Wire notes to more entity types — the model is already polymorphic; only
the item frontend is wired. Adding clients/suppliers/orders is frontend-only
(pass a different
SourceType). Deferred until product asks for it. - Per-lot expiration tracking — the two dates currently live on the item row. If lot/serial tracking needs its own expiry, that belongs on the inventory lot, not the item. Deferred; no current demand.