History
Changelog
What landed in the repo, grouped by date. Newest first. Add a block here whenever you ship a feature, fix, or contract change.
Clinic invoices from sales orders
- Issue one client invoice from one open sales order. The order becomes
invoicedand locks; lines, discounts, bill-to, ship-to, and notes freeze under the Location’s Legal Entity (INV-000123). - New Clinic → Invoices nav and
invoices.read/invoices.manageRBAC. Settings → Billing and SaaSbilling.*/BillingInvoiceare unchanged. - Receptionist can issue; veterinarian and organization manager are read-only for clinic invoices.
Related: Appointment to invoice, Billing, RBAC. Full contract: docs/functional/clinic-invoices.md
Profile photos: this device or a QR code from any phone
- The Take photo button on Client and Patient forms is now a menu: browse this computer (camera on a phone or tablet), or show a QR code. Any phone scans it and takes or picks a photo without signing in; the photo appears on the form right away and is attached on save, including on new records.
- The QR link expires in 15 minutes and works once. The public intake page keeps the plain camera button.
- This is now the standard approach for profiles, documents, and captures. Documents (vaccine certificates, PDFs) and captures (SOAP, observation, sample photos) will reuse the same flow.
- Dev: new
PhotoUploadSession(photo_upload_sessions),PhotoUploadChannel,POST /app/photo_upload_sessions, public/photo_uploads/:token, andPhotoAttachable#photo_upload_token. Unclaimed sessions are purged hourly after a day.
Related: Hands-off uploads, Patients. Full contract: docs/architecture/hands-off-uploads.md
Whiteboard: visit and status timers
- Every live card shows two timers: the clock counts the whole visit since arrival (warn 60 min, alert 120); the status icon counts time in the current status (warn 15, alert 30 for Checked in and Waiting; other statuses never warn).
- New
visits.status_changed_at, stamped on every status change (not on room → room). A migration backfills it from the last move,ready_to_bill_at, orcompleted_at. - Dev:
Visit#visit_minutes/#visit_leveland#status_minutes/#status_levelreplacewait_since,minutes_waiting, andwait_level. The Pipeline Waiting column icon (hourglass) now renders.
Related: Visits. Full contract: docs/functional/visits.md
Whiteboard: “Seen · waiting” renamed to “Waiting”
- Visit status
awaitingis nowwaiting(label “Waiting” / “En espera”). Statuses:checked_in→in_room⇄waiting→ready_to_bill→completed. - Floor waiting room groups are Arrived and Waiting. A migration renames existing
awaitingvisits. - Dev: the place check is now
Visit#in_waiting_room?(place:waiting_room);Visit#waiting?is the status.Whiteboard#waiting_room_visitslists the whole waiting room;#waiting_visitsonly thewaitingstatus. The waiting room column useswb-stage-waiting_room.
Related: Visits. Full contract: docs/functional/visits.md
Whiteboard: “In treatment” status removed
- Visit statuses are now
checked_in→in_room⇄awaiting→ready_to_bill→completed. Any room move (first rooming or back from Seen · waiting) setsin_room. - Pipeline loses the In treatment column. Posting
in_treatmentis rejected. - A migration converts existing
in_treatmentvisits toin_room(orawaitingif they have no room).
Related: Visits. Full contract: docs/functional/visits.md
Whiteboard: “Seen · waiting” status and time in Waiting
- New visit status
awaiting(Seen · waiting): seen and back in Waiting.in_treatmentnow always means care underway in a room. Dragging a roomed visit to Waiting setsawaiting; dragging it back to a room setsin_treatment. A migration converts existingin_treatmentvisits in Waiting. - Floor splits the waiting room into Arrived and Seen, waiting groups. Pipeline gains a Seen · waiting column; dropping a Waiting card on In treatment asks for a room.
- New
visits.place_changed_at: in Waiting the card timer counts time since the last move (warn 15 min, alert 30). The Schedule shows Seen · waiting with the caution tone.
Related: Visits. Full contract: docs/functional/visits.md
Wiki: appointment to invoice flow
- New page walking through appointment → visit → sales order → invoice: which documents become charges, how checkout picks the order, what keeps it in sync, and what is still intended.
Related: Appointment to invoice
Flowboard is now Whiteboard, with a List view
- The board moved to
/app/whiteboard(Spanish: Pizarra)./app/flowboardredirects. The plan feature key is nowwhiteboardand the completed-column preference iswhiteboard_completed_hidden; a migration renames both. - New List view (
?view=list): every visit at the Location in the standard table, newest first, searchable by code, patient, or client.
Related: Visits. Full contract: docs/functional/visits.md
Visit page from the flowboard
- The patient name on a flowboard card opens the visit (
/app/visits/:id,visits.read): patient, client, status, room, staff, arrival, and linked appointment. - Lists the visit’s documents: SOAP note, diagnostic orders, and treatment plans (
patients.read), plus checkout sales orders (checkout.read), and the change history.
Related: Visits. Full contract: docs/functional/visits.md
Checkout: sales orders with split and merge
- New Clinic → Checkout (
/app/checkout) and a Checkout button on ready-to-bill flowboard cards. An order collects the visit’s approved treatment plan lines (plus a price adjustment when the plan price was overridden), billable lab works, and catalog lines added at the desk, at snapshotted prices. - Split selected lines into a new order (optionally another payer) and merge open orders at the same Location. Line and order discounts (% or fixed); split and merge keep the combined total to the cent. Void releases the charges.
- New location-scoped actions
checkout.read/checkout.manageand acheckoutplan feature (enabled wherever Flowboard is). Invoices, payments, and stock deduction come next.
Related: Visits, RBAC, Billing. Full contract: docs/functional/checkout.md
Platform icons: Phosphor to Lucide
- The Rails UI icon font is Lucide (
lucide-static0.546.0) instead of Phosphor. - Views keep the same
icon_tagnames. Classes are nowlucide icon-…, notph/ph-fill. - Lucide has no fill weight and no horse glyph (
horsedraws a paw print).
Related: Architecture → Stack. Full guide: docs/architecture/icons.md
Mobile live boards: Action Cable, then HTTP reload
- Future Schedule and Flowboard on iOS and Android subscribe to the existing
ClinicBoardChannelrefresh and reload over HTTP. - The phone authenticates the socket with its API bearer token. Push is not the board sync.
Related: Architecture → Live boards
SaaS billing: plans, credits, upgrades and downgrades
- Settings → Billing: subscribe with Stripe PaymentIntents, card on file, invoices (TaskPaws / Version Dos LLC footer), credit balance and auto-recharge below 10%.
- Plan upgrades charge a prorated delta immediately; downgrades schedule to the next renewal (blocked when over target caps). Cancel pending downgrade from Billing.
- Admin past/upcoming transactions; org plan changes on subscribed tenants use the same
Billing::PlanChangepath.
Related: SaaS billing, Product, Architecture
In-form validation instead of browser bubbles
- Required fields no longer show the native “Please fill out this field” tooltip. A branded summary lists what is missing, with jump chips and inline hints that clear as you type.
- Copy names the field (“Email is needed to continue”) and admission uses chart-specific wording for patient name and parent contact details.
Related: Architecture, Patients
Richer patient admission
- Dog / Cat / Other species shortcuts, shared breed catalog with super-admin CRUD, optional hair type/color, spayed/neutered, and general notes.
- A patient can have more than one parent. Search existing parents by name, email, or phone. Optional parent address is geocoded when possible.
- Organization setting for metric vs imperial units, used for patient weight. Form actions sit on the right: cancel, then accept.
Related: Patients
SOAP notes and patient history
- One structured SOAP note per visit (S/O/A/P jsonb). Lazy-created on open; partial notes allowed.
- Patient chart shows a visit/appointment history timeline (checked-in bookings deduped with their visit).
- Flowboard cards link to SOAP. Write requires
soap.manage(vet / location admin / org admin); read viapatients.read.
Related: SOAP notes, Patients, Visits, RBAC
Visits and flowboard
- Location Floor / Pipeline board at
/app/flowboard. Check-in from the schedule, walk-in New Visit, place/status moves, narrow undo check-in. - Visit statuses separate from appointment calendar statuses. Waiting room name editable on the Location ficha.
Related: Visits, Appointments, Architecture
SuperUser impersonation
- From
/admin/users, a SuperUser can open/appas a chosen staff User. The SuperUser session stays; no staffUserSessionis created and login stats do not change. - A banner on
/appstops impersonation and returns to the users directory. Signing out of/appwhile impersonating does the same.
Related: Staff access, Architecture, RBAC
Use master values clears sellable
- On a catalog item, Use master values is no longer nested in the override save form, so it deletes the Location exception instead of resubmitting the current sellable override.
- Clearing an override returns that Location to the master price and sellable flag.
Related: Catalog and pricing
Catalog follows workspace location
- In a Location workspace the catalog lists only items visible at that site. Other Location names, grants, and the all-sites override table stay hidden — including for Organization administrators.
- The Organization workspace still shows the master list, every exclusive item, and all Location overrides and grants.
Related: Catalog and pricing
Users & Roles: resend invitation
- Pending invites show days since the last send and how many times the invite was issued. Organization administrators can resend without deleting the User.
- Resend rotates the invitation token and increments
invitation_sent_count. Invite email delivery is still not built.
Related: Staff access, Architecture, RBAC
Users & Roles: pending days, withdraw, disable staff
- Pending invites show how many days they have been waiting. Organization administrators can withdraw a pending invitation without deleting the User.
- Organization administrators can disable or enable a Staff membership (
disabled_at). The User login is unchanged. The last enabled Organization administrator cannot be disabled.
Related: Staff access, Architecture, RBAC
Master catalog and per-Location inventory
- Organization master catalog at
/app/catalogwith inherit-or-override prices, locks, three stock-control modes, and location-exclusive items with promote-to-master. - On-hand stock at
/app/inventoryis per Location. Quantity items adjust a count; batch items require a lot. Services with no control are omitted.
Related: Catalog, Inventory, Architecture, Roadmap
Staff JSON API mirrors the /app workspace
- Bearer session plus home, billing placeholder, organization settings, legal entities, locations, staff invite, and role assign/revoke at
/api/v1. - Optional
X-Organization-Id/X-Location-Idheaders; same Staff authorization as the web workspace.
Related: Architecture, Roadmap
Docs and wiki catch up to shipped tenancy
- Root README, architecture, roadmap, and functional contracts now match the running product (settings CRUD, trial, locale switchers, header org name).
Related: Product, Architecture, Roadmap
Settings hierarchy map and location address display
- Hierarchy tab shows the Organization plus each Location with its Legal Entity name, and counts for locations, entities, and staff.
- Locations table shows
address_summary(joined address fields) or a blank-address label.
Related: Organization, Product
Staff invite (alta, no email yet)
- Organization administrators invite users from Settings → Users & Roles (
POST /app/staffs). - New emails create a
Userwith a random password; existing emails reuse the login. TheStaffis pending until accepted. - Pending membership grants no organization access. Invite email delivery is not built.
Related: Product, Architecture, Staff access
Organization settings in the staff workspace
- Settings at
/app/settings: organization hierarchy, legal entities, locations, and users & roles. - Organization administrators CRUD legal entities and locations. Location administrators edit their site fields and assign location-scoped roles.
- Team (
/app/staffs) redirects to Settings → Users & Roles. Roles remain per location, not a single role per user.
Related: Product, Architecture, Organization, Staff access
Free-trial tracking
- Organizations get
trial_ends_onfromPlatformSetting.free_trial_days(default 14) at provision. - The
/appheader shows remaining days while on trial. After expiry, a persistent banner links to/app/billing(card collection not built). No lockout. - SuperUsers set the default length at
/admin/settingsand remaining days per Organization at/admin/organizations.
Related: Product, Architecture
Workspace header and language switchers
- Home greets the User by first name. The header shows the Organization name.
- Marketing nav,
/appprofile menu, and Admin account menu switch English/Spanish viaGET /locale/:locale.
Related: Product
SuperUser CRUD in Admin
- Platform operators create, edit, and delete SuperUsers at
/admin/super_users. - The last SuperUser and the signed-in operator cannot be deleted.
- The first operator is still seeded from credentials.
Related: Product, Architecture
Platform RBAC catalog and tenant assignment
- SuperUsers CRUD global roles and actions at
/admin/rolesand/admin/actions. - Organization and Location administrators assign or revoke those roles on Staff at
/app/staffs. - Default roles: Organization administrator, Organization manager, Location administrator, receptionist, veterinarian.
- Location grants are location-scoped assignments. Founder register assigns org-wide Organization administrator.
Related: RBAC, Staff access, Architecture
Staff workspace shell
- Signed-in
/appuses the Admin sidebar/header/footer pattern with the marketing teal palette (not Admin purple). - Sidebar placeholders: schedule, patients, inventory, billing, messages, reports, settings.
- Collapse control shrinks the rail to icons; state is stored in
localStorage.
Related: Product, Architecture
Founder tenant provision
- Persisted
Organization,LegalEntity,Location, andStaff. /registercollects clinic name and callsOrganization.provision!(placeholders + Stafforganization_admin).- New social visitors finish on
/register. Existing Users do not get a new tenant. Userhas noorganization_id; membership isStaff.- Admin Organizations directory lists real tenants.
/appshows the organization name.
Related: Organization, Staff access, Architecture
Admin console (list and inspect)
- Dashboard stats filter Overview tables (staff, active 7d, super admins, social logins).
- Overview / Activity / Compliance tabs; Activity lists recent sign-ins.
- Sidebar: Users, Organizations (empty until tenants exist), Settings.
- Audit log at
/admin/auditsshowsField: old value ---> new value. - Admin account menu switches English/Spanish.
Related: Product, Architecture, Roadmap
Local wiki
- Added
wiki/: HTML knowledge base for local reading (product, contracts, architecture, roadmap, setup, changelog).
Production logging and Apple sign-in
- Production logs to file; log level adjusted for operations.
- Apple PEM handling in OAuth settings; OmniAuth Apple provider config.
- Test: Apple social login must not assign email to first name.
- Credential key definitions expanded (no values in git).
Related: Architecture, Setup, Product
Staff and SuperUser auth
- Authlogic
UserandSuperUserwith separate sessions. - Routes:
/login,/register,/app,/admin/login,/admin. - Google, Apple, and Facebook for staff via OmniAuth;
SocialIdentitypersists tokens. - Docs updated for tenancy: Organization, Legal Entity, Location (not built yet).
- Landing assets moved local (hero, features, avatars).
Related: Product, Architecture, Organization
PostgreSQL, i18n, marketing site
- Database switched from SQLite to PostgreSQL; credentials-backed connection for all environments.
- Locale from
Accept-Language(endefault,es). - Landing, privacy, and terms copy; documentation of current vs intended product.
Repository start
- Initial monorepo.