- Reject duplicate site codes per client (case-insensitive); site code is immutable once set
- Delete tombstones the site and requires the DeleteSites permission (Admin, Scheduler)
- GET /api/locations/{id}/open-work-orders returns the open count and ids
- PATCH /api/locations/{id}/contact-info saves contacts and notes from the work-order Site dialog
- Add nullable Locations.Notes, used as the site-level POC notes fallback
Stop client writes from changing Locations.AccountId, make the SH-221 migration discoverable, and thread the board-create CancellationToken through lookup and persistence.
SH-44 and SH-82 both left "blocks, or requires explicit confirmation" to
be decided with the team, and the implementation took the blocking
branch. SH-254 settles it the other way: the approved design offers
"Deactivate anyway" beside the list of open work orders.
Deactivation with open work orders is now permitted, but only when the
caller says it has shown them: ConfirmOpenWorkOrders on the update DTO
and a confirmOpenWorkOrders query parameter on the delete route. Absent
the flag the existing guard still throws, so nothing deactivates by
accident and no caller loses the check by omission.
confirmOpenWorkOrders is a required parameter on DeleteVendorAsync
rather than an optional one, so every call site states its intent.
Stamp WorkOrder.AccountId on all create paths and filter board/list/search/detail by server-derived account claims so scoped callers cannot cross accounts.
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor(api): enforce service and data-service boundaries
* refactor(api): complete feature service boundaries
* refactor(identity): enforce service and data boundaries
* refactor(vendors): enforce service and data boundaries
* refactor(workorders): enforce service and data boundaries
* refactor(backend): enforce architecture and optimize dispatch
* style(backend): format changed architecture files
* fix(architecture): address backend review follow-ups
* fix(backend): sanitize exception disclosure in changed API endpoints
Replace raw exception-message disclosure (ex.Message) returned to API
callers with a stable sanitized public message plus correlated structured
internal logging, across the endpoints changed in this PR.
- Add SanitizedErrors helper: logs the original exception at Error with a
generated correlation id and returns a stable public message referencing
it so support can trace without exposing internals.
- Inject ILogger<T> into the 14 changed controllers and route every
ex.Message/dbex.Message disclosure through the helper, preserving status
codes, response shapes, and business data (e.g. OpenWorkOrders).
- Leave FluentValidation (vex.Errors) and existing fixed-message catches
untouched; out-of-scope controllers (Account/Contact/Employee/Asset/
PMSchedule) are unchanged.
- Add focused tests proving internal exception text is not returned and
that Error logging carrying the original exception is invoked.
* fix(architecture): abstract job run state access
* style: format board update service
* test: use collection assertion idiom
* Align EntityFrameworkCore.SqlServer and Tools to 8.0.8
* Add calendar/events backend API
Cherry-picked from main-backup (19994ef); scratch notes file removed.
* Require authentication on CalendarController
Security review found [Authorize] commented out, leaving all 6 calendar
endpoints anonymous. Enforce auth to match the API convention (17/23
controllers).
* Add CalendarController unit tests (xUnit + EF InMemory)
- DispatchSignoff model (DispatchId, SignoffType, Name, Signature base64, SignatureMethod, SignedAt)
- AddDispatchSignoff endpoint — one per type per dispatch, validates no duplicate
- GetDispatchById includes signoffs in response
- Migration for DispatchSignoffs table
- DispatchWorkOrder junction table for 1:N dispatch-to-WO relationship
- Make Dispatch.WorkOrderId nullable (backward compat)
- Add AcceptToken and AcknowledgedAt to Dispatch model
- Dispatch_DTO accepts WorkOrderIds array
- DispatchToVendor creates junction rows, email lists all WOs in table
- GetDispatches queries both junction table and direct FK
- GetDispatchById includes workOrders list from junction table
- Migration with DispatchWorkOrder table
- TaskListTemplate and TaskListTemplateItem models
- TaskListTemplateController: list, get by ID, create with items, update (replace items), soft delete
- Migration for new tables
- Add DispatchNumber and CompletedDate to Dispatch model
- Add DispatchId to Comments for per-dispatch vendor threads
- GetDispatchById endpoint with vendor communication thread
- UpdateDispatch endpoint for status, NTE, dates, description
- AddDispatchComment endpoint — saves comment + sends email to vendor with sender name
- BackfillDispatchNumbers endpoint for existing dispatches
- SyncVendorReplies endpoint — pulls from DynamoDB VendorReplies table
- Fix reply-to address to include dispatch number
- Include sender name in dispatch and comment emails
- Add ScheduledDate field to Dispatch model and DTO
- Save scheduled date when dispatching to vendor
- Include ScheduledDate in dispatch response projections
- Create Vendor model with company info, trade specialties, active flag
- Create Dispatch model (doubles as Vendor PO) with PO number, NTE, status, reply-to address
- VendorController: CRUD, paginated list, dropdown endpoint with trade filtering
- DispatchToVendor endpoint: multi-vendor dispatch, auto-generated PO numbers (VPO-00001),
HTML email with full WO details via SendGrid, reply-to wo-{number}@int.seahaven.com
- GetDispatches endpoint for listing dispatches by WO
- Include dispatches in GetWorkorderById response
- SendMessage.SendDispatchEmail with reply-to support
- Audit log entry for each dispatch
- Create DropdownOption model with Category, Value, ParentValue for Trade/SubTrade/Problem
- Add DropdownOptionsController with CRUD + ByCategory endpoint with parent filtering
- Add Problem, Trade, SubTrade, VendorNTE, ScheduledDate, CompletedDate, Source to WorkOrder
- Update EditWorkorder_DTO and GetWorkorderById with new fields
- Audit log tracks changes to all new fields
- Seed default Trades (10), SubTrades (20), and Problems (11) on startup
- Add CommentType field to Comments (customer, vendor, internal)
- Add Status field to EditWorkorder_DTO
- Create WorkOrderAuditLog model tracking field-level changes
- Log all field changes on work order edit and status change
- Include commentType and auditLog in GetWorkorderById response
- Set CommentType=customer on synced comments from DynamoDB
- Add BackfillCommentTypes endpoint for existing data
- Frontend sends accountId as empty string when not set
- Change DTO AccountId to string with GetAccountId() parser
- Prevents JSON deserialization error on empty/non-numeric values
- Extend Locations model with Address, City, State, ZipCode, Phone, Contact, ContactEmail, Status, AccountId
- Update Location DTOs to match new fields
- Create LocationController with paged list, get by ID, create, update, delete
- Support both REST-style routes (/api/Location/{id}) and legacy query routes (/api/Location/DeleteLocation?id=)
- Add migration for new Location columns