RoundTrip Enhancements
Purpose
This is the parent issue for all enhancement ideas captured during development and beta testing. Every enhancement idea gets filed as a child of this issue with the Enhancement label.
Triage process
When an idea comes in:
- Capture it immediately — file a child issue with just a title and one-line description. Don't overthink it. Low priority by default.
- Evaluate it using the three questions below.
- Promote or park it — bump priority and assign to a milestone, or leave it in the backlog for the next review pass.
Three evaluation questions
| Question | What to look for |
|---|---|
| User value — Does it fix a real pain point? | Would a dispatcher or technician notice it missing? Would it save time or reduce confusion? |
| Effort — How long would it take? | S = hours, M = 1-2 days, L = 3-5 days, XL = needs a spike |
| Timing — When does it need to happen? | Pre-beta blocker / During beta (feedback-driven) / Post-beta (nice to have) |
Priority guide
| Linear Priority | Meaning |
|---|---|
| Urgent | Blocks beta or causes user confusion — do it now |
| High | Meaningfully improves UX — schedule in next milestone |
| Normal | Worth doing — add to backlog for next available slot |
| Low | Nice to have — park it, revisit at milestone planning |
Review cadence
- Daily: capture ideas as they come, don't evaluate yet
- End of each milestone: review all Low/Normal enhancements, promote or close
- Post-beta feedback: elevate anything users actually ask for
FEATURE: CSV import for Clients and Inventory — reduce onboarding friction for new tenants
Overview
New tenants coming from another system or spreadsheet will have existing client and inventory data they don't want to re-enter manually. CSV import significantly reduces onboarding friction and time-to-value.
Scope
Client Import
- CSV columns: FirstName, LastName, Email, Phone, IsCommercial, Street, City, State, PostalCode, Country
- Validates required fields, skips/reports duplicates
- Creates Client + primary ClientAddress per row
- Returns import summary (X imported, Y skipped, Z errors)
Inventory Import
- CSV columns: Name, SKU, Description, UnitCost, Currency, QuantityOnHand, ReorderPoint, Category
- Validates required fields
- Returns import summary
UX
- Upload CSV button on Clients list page and Inventory list page
- Preview first 5 rows before confirming import
- Download error report if any rows failed
- Download CSV template so users know the expected format
Technical Notes
- Backend: new
ImportClientsCommandandImportInventoryItemsCommandhandlers - Parse CSV server-side (CsvHelper library)
- Process in batches to avoid timeouts on large files
- TenantAdmin only
FEATURE: Quotes — complete domain + build full quote workflow (post-beta)
Overview
The Quote aggregate exists in the domain and is partially implemented, but the feature is incomplete and has no frontend at all. Quotes allow a dispatcher to send a cost estimate to a client before work begins. Once approved, the quote converts to an invoice.
Current state
Done:
Quoteaggregate root withCreate()andApprove()methodsQuoteLineItementity withCreate()factory- Basic domain structure in place
Not done (marked TODO in Quote.cs):
AddLineItem(),RemoveLineItem(),RecalculateTotals()domain methodsSend(),Decline(),Expire(),ConvertToInvoice()status transitions- No command/query handlers
- No API endpoints
- No frontend pages or modals
PRD status
Quotes are not listed as a Must Have in the PRD MVP scope. They are a logical extension of the billing workflow but can be deferred past initial beta.
Recommended approach
Defer to post-beta (v1.1). Complete the domain methods and build the full feature once:
- The core invoice workflow is working
- At least one beta tenant has validated the need for pre-work estimates
Quotes require client-facing communication (email delivery of the quote) which also depends on SendGrid (M12), making them doubly dependent on work not yet done.
When ready to build — scope
- Complete
Quotedomain:AddLineItem,RemoveLineItem,RecalculateTotals,Send,Decline,Expire,ConvertToInvoice - Command/query handlers for all operations
- API endpoints:
POST /v1/quotes,GET /v1/quotes,GET /v1/quotes/{id},POST /v1/quotes/{id}/send,POST /v1/quotes/{id}/approve,POST /v1/quotes/{id}/convert - Frontend: Quote list page, quote detail page, create quote modal (accessible from ticket detail and client detail)
ENHANCEMENT: Admin Functionality
Create api and frontend admin functionaliuty (TenantAdmin)
ENHANCEMENT: Allow create ticket to choose the service date
Should default to the current date
ENHANCEMENT: Auto-generate Labour line item on invoice from ticket timestamps
Enhancement
When generating an invoice from a completed ticket, automatically calculate and add a Labour line item based on the ticket's StartedAt and CompletedAt timestamps.
Business rules to define before building
These need decisions before implementation:
Labour rate
- Option A: Flat hourly rate per tenant (set in tenant settings)
- Option B: Rate varies by ticket priority (Urgent/High = premium rate, Normal/Low = standard)
- Option C: Rate varies by technician (senior tech = higher rate)
- Option D: After-hours multiplier (evenings/weekends = 1.5x or 2x)
Time rounding
- Exact minutes (e.g. 1h 23m = 1.38 hours)?
- Round to nearest 15 minutes?
- Round up to nearest 30 minutes (most common in trades)?
- Minimum charge (e.g. 1 hour minimum)?
Edge cases
- Ticket has no
StartedAt(technician never pressed Start) — skip auto-generation or useAssignedAt? - Ticket has multiple hold periods — deduct OnHold time or charge total elapsed?
- Labour line item already exists (manually added) — skip auto-generation?
Suggested approach
- Add a
TenantSettingstable withStandardHourlyRate,UrgentHourlyRate,AfterHoursMultiplier,MinimumChargeMinutes,TimeRoundingMinutes - In
GenerateInvoiceHandler, after auto-populating Part line items, calculate labour time and add a Labour line item ifStartedAtandCompletedAtexist - Show the calculated hours and rate in the invoice before finalising — dispatcher can adjust if needed
ENHANCEMENT: Bulk import inventory items via CSV (TenantAdmin)
Idea
Allow a TenantAdmin to import inventory items in bulk via CSV upload rather than adding them one at a time through the UI.
User value
High friction point for new tenant onboarding — a business joining RoundTrip likely has an existing parts list in a spreadsheet. Entering 50–200 inventory items one by one is a significant barrier to getting value from the app quickly.
Suggested approach
- TenantAdmin uploads a CSV with columns: SKU, Name, Category, Unit of Measure, Unit Cost, Initial Quantity, Reorder Threshold
- System validates each row and shows a preview with any errors highlighted before committing
- On confirm, calls
POST /v1/inventoryfor each valid row (or a bulk endpoint if one is added to the API) - Summary shown on completion: X items imported, Y skipped with reasons
Effort estimate
M–L — frontend CSV parsing + validation UI is moderate effort. API may need a bulk import endpoint to avoid N individual POST calls.
Timing
Post-beta — not blocking, but valuable for tenant onboarding once real customers start signing up.
ENHANCEMENT: Filter tickets
I think it would be good if we could do an advanced filter.
for example: filter all tickets that are not closed. or maybe a combination filter. That way a user could choose exactly what they want to see. Also to tie with the Service date enhancement allow filtering by day.
ENHANCEMENT: Google Maps Places Autocomplete for address entry — replace free-text fields
Overview
Replace free-text address fields with Google Maps Places Autocomplete. User starts typing and selects from a dropdown of verified addresses — guaranteed correct formatting and geocoding every time.
Problem
Nominatim (current geocoding provider) fails on abbreviated street names (e.g. "S First St" vs "South First Street"). Users entering abbreviated addresses won't have their clients geocoded and won't appear on the dispatch board map.
Solution
Google Maps Places Autocomplete on the Add Client and Add Address forms:
- User types a partial address
- Dropdown shows matching real addresses from Google Maps
- User selects — all fields auto-populate (street, city, state, postal code, country)
- Guaranteed to geocode correctly since the address comes from Google
Technical Notes
- Requires Google Maps JavaScript API key with Places library enabled
- ~28,500 free sessions/month — essentially free at current scale
- Add
VITE_GOOGLE_MAPS_API_KEYto frontend env vars - Replace address input fields in
AddClientModalandAddAddressModalwith Places Autocomplete component - On selection, parse the address components and populate individual fields
Cost
Free tier covers ~28,500 sessions/month. At $0.017 per session beyond that, costs only become meaningful at significant scale.
ENHANCEMENT: Pre-populate tax rate on invoice generation based on service address location
Enhancement
Pre-populate the tax rate field in the Generate Invoice modal based on the ticket's service address location, rather than requiring the dispatcher to enter it manually every time.
Options
Option A — Simple lookup table (recommended for beta)
Store a StateTaxRates table with state → default tax rate mappings. When generating an invoice, look up the rate from the ticket's AddressState. Dispatcher can still override.
Pros: Simple, no external dependency, no cost. Cons: Tax rates vary by county/city — state-level is approximate.
Option B — Third-party tax API (post-beta)
Use TaxJar or Avalara to look up the exact rate by address. Accurate to city/county level.
Pros: Accurate. Cons: API cost (~$19-99/month), external dependency, adds latency to invoice generation.
Option C — Tenant-configured default
Let the TenantAdmin set a default tax rate in tenant settings. Simple, no lookup needed.
Pros: Simplest to build. Cons: Doesn't account for jobs in different states/jurisdictions.
Recommended approach
For beta: Option C — tenant-configured default rate in tenant settings. Fast to build, works for single-state businesses (which most beta users will be).
Post-beta: Add Option A (state lookup table) once multi-state tenants come on board.
Post-revenue: Consider Option B (TaxJar) only if tenants explicitly need county/city accuracy.
F-05: AI-powered route optimization — deferred pending beta signal (future M13)
Overview
AI-powered daily route optimization for dispatchers. Uses n8n workflow automation + Ollama (local LLM) to suggest an optimized job sequence for each technician's day, minimizing drive time across their assigned tickets.
Why this matters (BRD context)
This was identified as a key differentiator in the BRD — route optimization at SMB pricing, with local LLM inference meaning tenant data never leaves the infrastructure. Competitors at this price point don't offer it.
Decision: Defer until post-beta signal
Do not start this until at least one real beta tenant has been using the app for several weeks and explicitly identifies manual routing as a pain point.
Route optimization is a productivity multiplier — it adds value only after the core workflow (tickets, dispatch, technician mobile) is running smoothly and tenants have enough daily jobs to make routing non-trivial (typically 5+ jobs per technician per day).
Trigger condition to start M13
- A beta tenant asks for better routing unprompted, OR
- A beta tenant has 3+ technicians each running 5+ jobs/day, OR
- Core bugs (M10) and multi-tenant hardening (M11) are fully resolved and no higher-priority feedback exists
Planned approach (from Ollama evaluation doc)
-
n8n workflow triggered by dispatcher ("Optimize Today's Routes")
-
Ollama local LLM for route sequencing — tenant data never leaves infrastructure
-
OpenRouteService for road distance/time matrix
-
Output: suggested ordered job sequence per technician shown on dispatch board
-
Dispatcher can accept, modify, or ignore
Marketing angle
Route optimization is a strong post-launch marketing hook — "AI-powered route optimization coming soon" gives early adopters a reason to get in now and stay. The research is already done (Ollama eval doc), the architecture is planned, and it's a differentiator no competitor at this price point offers.
Use it in:
- Landing page / waitlist copy: "RoundTrip includes AI-powered route optimization — your techs spend more time on jobs, less time driving"
- Beta onboarding: let early tenants know it's on the roadmap so they're invested in the product's future
- Social / launch: announcing it as a shipped feature after beta will generate genuine excitement
The key is the local LLM angle — tenant data never leaves your infrastructure. That's a meaningful data privacy story for trades businesses who are skeptical of cloud AI.
ENHANCEMENT: Reports Page
future enhancements backlog (date pickers, CSV/PDF export, filters, comparison periods)
Overview
Reporting enhancements to make the M16 dashboard more feature-rich. These are post-beta improvements that can be marketed as ongoing platform value.
Potential Enhancements
- Date range picker UI on all report pages (currently hardcoded to last 12 months)
- CSV export button on Ticket Volume and Technician Performance reports
- PDF export via Hangfire async job (ReportJobs infrastructure already built in TRA-260
- Technician Performance — filter by individual technician dropdown
- Ticket Volume — filter by status and technician dropdowns
- Revenue Dashboard — compare current period vs previous period
- Invoice Aging — click through to individual invoice detail
- Charts — tooltips, legends, and axis labels polish
- Mobile responsive layout for report pages
- Scheduled email delivery of reports (weekly/monthly digest)
- Custom date range presets (This Month, Last Quarter, YTD)
Notes
ReportJobs infrastructure is already built and ready for CSV/PDF export implementation. The async generation pattern (Hangfire → blob storage → download link) is fully wired — just needs the report-specific generators implementing per acceptance criteria.
FEATURE: Edit and remove user/technician profile — update name, role, phone, and permanent removal
Overview
There is currently no way to edit or remove a user or technician's profile after they have been invited. This is needed for:
- Updating phone number on a Technician record
- Changing a user's role
- Updating name or email if entered incorrectly at invite time
- Removing test users or users who have left the company
Scope
Edit User
- Edit user modal on Team page — update FirstName, LastName, Role
- Edit technician profile — update Phone, Status
- TenantAdmin only
Remove User
- Remove user button on Team page
- Deletes TenantUser record and corresponding Technician record if applicable
- Deletes Entra External ID account via Graph API
- Requires confirmation dialog —
"This will permanently remove access for {name}. This cannot be undone." - TenantAdmin only — cannot remove yourself or another TenantAdmin
Notes
- Technician records are auto-created at invite time with Phone = null. Until edit is built, admins cannot add a phone number for a technician after inviting them.
- Remove user must call Graph API to delete the Entra account, otherwise re-inviting the same email will fail with a duplicate user error.
- Deactivate (existing) vs Remove (new) — deactivate keeps the account but blocks login, remove permanently deletes everything. Both should be available.
R-01 Assignment Notes
Assignment notes functionality not possible
R-02 Filter functionality
In Technician PWA there is no filter functionality
R-05 File Attachment
No file attachment feature build
R-06 Record Part by Photo
This has not been implemented
R-10: Filter service history by date range
Not Implemented
R-11 Assign an item to multiple storage locations
Not Implemented
R-12 View low stock items on the dashboard
Not implement
R-13 Mark a low stock item as On Order
Not Implemented
R-17 Filter the ticket list on the dispatch board
Not implemented