# Story 1.11: Municipality Operational Addresses Status: done ## Story As a a client services staff member, I want to store and update municipality operational mailing and delivery addresses, so that election services reference current address information without depending on legacy records. ## Acceptance Criteria 1. **Given** a municipality profile exists **When** a client services user adds an address **Then** they can specify mailing or delivery address type, and the record is saved to the extension address table linked to the municipality profile 2. **Given** an address is updated **When** saved **Then** the previous address is preserved in history, the new address is marked as current, and the change is logged with actor and timestamp 3. **Given** an address record is saved **When** retrieved **Then** it includes address type, street, city, state, zip, and effective date fields 4. **Given** a municipality profile is viewed **When** addresses are displayed **Then** mailing and delivery addresses are shown with clear type labels and current/historical status ## Tasks / Subtasks - [ ] Implement story behavior in aligned backend/frontend modules (AC: #1) - [ ] Add or update API/service/UI components required by the story scope - [ ] Keep legacy Access entities read-only and route writes to extension-layer structures - [ ] Cover acceptance criteria #2 in implementation and tests (AC: #2) - [ ] Add validation/error handling and UX state updates as needed - [ ] Cover acceptance criteria #3 in implementation and tests (AC: #3) - [ ] Add validation/error handling and UX state updates as needed - [ ] Cover acceptance criteria #4 in implementation and tests (AC: #4) - [ ] Add validation/error handling and UX state updates as needed - [ ] Validate and document completion evidence - [ ] Verify build/tests for touched modules - [ ] Capture changed files and any migration/config implications ### Review Findings **Patch** - [x] [Review][Patch] AC2: UpdateAddressAsync must insert a new record for the updated address and mark the old one IsCurrent=false — do not mutate existing row in-place [MunicipalityAddressService.cs:55-78] - [x] [Review][Patch] AC2: Implement audit log entry on create/update using existing audit infrastructure — capture actor identity and timestamp [MunicipalityAddressService.cs] - [x] [Review][Patch] Convert DeleteAddressAsync to soft-delete — set IsCurrent=false rather than removing the row [MunicipalityAddressService.cs:81-88] - [x] [Review][Patch] Route attribute string literals missing quotes — compilation failure [MunicipalityAddressesController.cs:8,18,25,57] - [x] [Review][Patch] UpdateAddress decorated with [HttpPost] instead of [HttpPut("{id}")] — conflicts with CreateAddress, id cannot bind [MunicipalityAddressesController.cs:42] - [x] [Review][Patch] GetAddresses and GetAddress share ambiguous route template shape — AmbiguousMatchException at runtime [MunicipalityAddressesController.cs:18,25] - [x] [Review][Patch] Interface declares non-nullable return types where null is returned — NullReferenceException risk for callers [IMunicipalityAddressService.cs:10-12] - [x] [Review][Patch] ApplicationDbContext is not defined anywhere in the project — service will fail to compile and register [MunicipalityAddressService.cs:9] - [x] [Review][Patch] IsCurrent logic ignores address type — adding a Delivery address incorrectly marks the current Mailing address as not-current [MunicipalityAddressService.cs:32,59] - [x] [Review][Patch] No transaction around IsCurrent read-modify-write — concurrent requests can produce multiple IsCurrent=true rows per address type [MunicipalityAddressService.cs:30-48,55-73] - [x] [Review][Patch] No [Authorize] attribute on controller — RBAC requirement from Dev Notes not enforced [MunicipalityAddressesController.cs:8] - [x] [Review][Patch] CreatedAt/UpdatedAt default to object construction time rather than save time [MunicipalityAddress.cs:38-40] - [x] [Review][Patch] Microsoft.EntityFrameworkCore unnecessarily imported in interface — leaks infrastructure dependency into abstraction [IMunicipalityAddressService.cs:3] - [x] [Review][Patch] [ForeignKey] placed on navigation property instead of scalar FK — string-based reference breaks silently on rename [MunicipalityAddress.cs:42] **Deferred** - [x] [Review][Defer] State field is free-text with no format validation [MunicipalityAddress.cs:25] — deferred, may support non-US addresses - [x] [Review][Defer] MunicipalityId existence not validated before insert — surfaces as DbUpdateException [MunicipalityAddressService.cs:46] — deferred, EF FK constraint handles at DB level ## Dev Notes - Follow Epic 1 architecture constraints: ASP.NET Core + React separation, RBAC-aware patterns, and immutable legacy tables. - Reuse shared component and workflow patterns defined in UX and architecture docs; avoid parallel custom implementations. - Keep changes scoped to this story; do not pull forward Epic 2+ features. ### Project Structure Notes - Backend: `Campaign_Tracker.Server/` - Frontend: `campaign-tracker-client/` - Story artifacts: `_bmad-output/implementation-artifacts/` ### References - Story source: `_bmad-output/planning-artifacts/epics.md` (Epic 1 / Story 1.11) - Architecture constraints: `_bmad-output/planning-artifacts/architecture.md` - UX patterns: `_bmad-output/planning-artifacts/ux-design-specification.md` ## Dev Agent Record ### Agent Model Used GPT-5 Codex ### Debug Log References - Story generated from epic source and architecture/UX planning artifacts. ### Completion Notes List - Story context created and marked ready-for-dev. ### File List - `_bmad-output/implementation-artifacts/1-11-municipality-operational-addresses.md`