# Task ID: 30

**Title:** Implement Number Update Command

**Status:** pending

**Dependencies:** 19, 22

**Priority:** low

**Description:** Add update command for changing phone number location or network site without reassignment.

**Details:**

Extend NumberService and CLI:
```python
# number_service.py
async def update_number(
    self,
    phone_number: str,
    location_id: str | None = None,
    network_site_id: str | None = None,
) -> None:
    """Update number location (synchronous operation)."""
    body = {"telephoneNumber": format_for_update(phone_number)}
    if location_id:
        body["locationId"] = location_id
    if network_site_id:
        body["networkSiteId"] = network_site_id
    await self._graph_client.post("/updateNumber", body)

# numbers.py CLI
@numbers_app.command()
def update(
    ctx: typer.Context,
    number: str = typer.Argument(...),
    location: Optional[str] = typer.Option(None, "--location"),
    network_site: Optional[str] = typer.Option(None, "--network-site"),
    dry_run: bool = typer.Option(False, "--dry-run"),
):
    ...
```

Note: updateNumber uses E.164 format with + prefix (different from assign/unassign).

**Test Strategy:**

Unit tests: number format conversion for update endpoint, location validation, dry-run preview, success/error handling.

---

## Implementation Notes

### Session: 2026-01-30 07:45 - Agent: claude-sonnet-4-5-20250929

**Status:** Implemented

#### What Was Done
- Added `update_number()` method to `NumberService` in `src/teams_phone/services/number_service.py:419-495`
- Added `update` CLI command to `src/teams_phone/cli/numbers.py:932-1113`
- Added `_update_to_json_dict()` helper function for JSON output formatting
- Created 11 unit tests in `tests/unit/test_number_service.py::TestNumberServiceUpdateNumber`
- Created 13 CLI tests in `tests/unit/test_cli_numbers.py::TestNumbersUpdateCommand`

#### Key Decisions Made
- **Synchronous operation**: Unlike assign/unassign (which return 202 and require polling), updateNumber returns 200 OK immediately. No polling or progress spinner needed.
- **Empty string to clear**: Following API contract, empty string `""` is used to clear locationId or networkSiteId fields.
- **Validation before API call**: Number must be assigned (USER_ASSIGNED status) before update can proceed.
- **Location validation**: If `--location` is provided, it's validated through LocationService before the update.
- **E.164 format with + prefix**: Uses `format_for_update()` which returns `+12065551234` format (different from assign/unassign which use digits-only).

#### Patterns Discovered
- Reused `_run_async()` helper for async service execution (see `numbers.py:50-78`)
- Followed same CLI pattern as `unassign` command for dry-run, confirmation, force flags
- Validation errors use exit code 5 (ValidationError)

#### Open Items for Next Agent
- None - task is fully implemented

#### Gotchas and Warnings
- **E.164 vs digits-only**: The updateNumber API requires E.164 format with `+` prefix. This is different from assignNumber/unassignNumber which require digits-only.
- **Synchronous vs async**: updateNumber is synchronous (200 OK), not async (202 Accepted). Do not add polling logic.
- **Number must be assigned**: CLI validates the number is in USER_ASSIGNED status before allowing update.

---
