The Vendors directory renders Location from City/State only, but the roster
form keeps its City/State inputs hidden, so vendors captured through the UI
store their location in the free-text Address field and the column falls back
to an em dash while the detail drawer shows the value.
Resolve location through a single rule -- structured City/State when present,
otherwise the free-text Address -- and apply it consistently so the column,
search, the location facet and the location filter cannot disagree.
- Directory rows fall back to the linked company's Address when the technician
has none, matching the Email precedence already established for these rows.
- BuildLocationLabelBody, which backs both the filter and the facet label,
falls back to Address when City/State are blank.
- Directory search matches Address so a row is findable by what it displays.
- The locations facet offers address-only companies as filter options.
* 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