# Backward Compatibility Cleanup Plan

**Date:** 2026-02-14
**Status:** Proposed
**Motivation:** The app is not live yet. All backward compatibility shims should be removed to build the clean target state.

---

## Inventory: 28 instances found, grouped into 7 cleanup tasks

### Task 1: Remove legacy `location` field from Inventory model (HIGH)

**Files:**
- `app/modules/inventory/models/inventory.py:33` — legacy `location = Column(String)` field
- `app/modules/inventory/services/inventory_service.py:90,158` — writes `location=location` alongside new `bin_location`

**What exists:** The old single `location` field (e.g., `"SA-10-02"`) is kept in parallel with the new `warehouse` + `bin_location` split. Every inventory write populates both.

**Cleanup:**
1. Remove `location` column from `Inventory` model
2. Remove `location=location` from both create paths in `inventory_service.py`
3. Search all reads of `.location` and migrate to `.bin_location` or `.warehouse`
4. Update `__repr__` method
5. Create Alembic migration to drop the column (data already in `bin_location`)

---

### Task 2: Remove dashboard backward-compat helpers + legacy stat fields (HIGH)

**Files:**
- `app/modules/core/routes/api/admin_dashboard.py:73-107,146,149` — helper functions `_extract_metric_value()`, `_widget_list_item_to_dict()`, `_extract_widget_items()` that transform new aggregator data to old response format
- `app/modules/analytics/services/stats_service.py:294-299,494-498` — duplicate legacy fields (`total_stores`, `active_stores`, `total_imports`, `completed_imports`, etc.) alongside new schema-compatible fields

**What exists:** The dashboard endpoint transforms the new provider-based aggregator output into an old flat response. Stats service returns both old and new field names.

**Cleanup:**
1. Remove the three `_extract_*` helper functions from `admin_dashboard.py`
2. Return aggregator data directly from endpoints
3. Remove legacy field names from `get_store_statistics()` and `get_import_statistics()` in `stats_service.py`
4. Keep only the schema-compatible field names (`total`, `verified`, `pending`, etc.)
5. Update frontend to consume the new field names

---

### Task 3: Remove re-export shims and aliases (MEDIUM)

**Files:**
- `app/core/frontend_detector.py:200-207` — `get_frontend_type()` wrapper around `FrontendDetector.detect()`
- `app/modules/task_base.py:169-170` — `DatabaseTask = ModuleTask` alias
- `app/modules/cms/routes/api/admin.py:26-27` — `router = admin_router` alias
- `app/modules/cms/routes/store.py` — entire file re-exports `store_router` from `routes/api/store.py`
- `app/modules/payments/routes/__init__.py` — re-exports `admin_router`, `store_router` from `routes/api/`
- `app/modules/analytics/schemas/stats.py:18-34,154-169` — re-exports core dashboard schemas
- `app/modules/billing/services/subscription_service.py:33` — re-exports `SubscriptionNotFoundException`
- `app/modules/tenancy/models/admin.py:60-61` — comment about `AdminNotification` re-export via `models/database/__init__.py`
- `app/modules/dev_tools/models/test_run.py:7` — re-export comment
- `app/modules/dev_tools/models/architecture_scan.py:7` — re-export comment
- `app/modules/dev_tools/tasks/test_runner.py:7` — re-export comment
- `app/modules/dev_tools/tasks/code_quality.py:7` — re-export comment
- `app/modules/dev_tools/schemas/__init__.py:6` — re-export comment

**What exists:** A collection of aliases, wrapper functions, and re-export modules that allow old import paths to keep working after code was moved or renamed.

**Cleanup (per item):**

| Item | Action |
|------|--------|
| `get_frontend_type()` | Delete function. Update callers to `FrontendDetector.detect()` |
| `DatabaseTask = ModuleTask` | Delete alias. Rename all `DatabaseTask` imports to `ModuleTask` |
| `router = admin_router` (CMS) | Delete alias. Update imports to use `admin_router` |
| `cms/routes/store.py` | Delete file. Update `cms/definition.py` to import from `routes/api/store` |
| `payments/routes/__init__.py` | Clear re-exports. Update `payments/definition.py` to import from `routes/api/` |
| `analytics/schemas/stats.py` re-exports | Remove re-export block. Update all imports to `core.schemas.dashboard` |
| `SubscriptionNotFoundException` re-export | Remove import. Update callers to import from `billing.exceptions` |
| `AdminNotification` re-export | Find and remove from `models/database/__init__.py`. Update imports to `messaging.models.admin_notification` |
| dev_tools comments | Remove backward compat comments. Verify no legacy import paths exist |

---

### Task 4: Remove module-enabling JSON fallback (MEDIUM)

**Files:**
- `app/modules/service.py:140-174` — falls back from `PlatformModule` junction table to `Platform.settings["enabled_modules"]` JSON, then to "enable all modules"

**What exists:** Three-tier fallback for determining which modules are enabled:
1. `PlatformModule` junction table (new, auditable)
2. `Platform.settings["enabled_modules"]` JSON field (old)
3. Enable all modules if nothing configured (oldest)

**Cleanup:**
1. Remove the JSON fallback path (lines ~166-174)
2. Remove the "enable all" default
3. Require explicit `PlatformModule` records
4. Seed scripts should create `PlatformModule` entries for all platforms
5. Remove `_migrate_json_to_junction_table()` helper if no longer needed
6. Update `app/modules/service.py` docstring

---

### Task 5: Remove marketplace title/description compat + menu legacy format (MEDIUM)

**Files:**
- `app/modules/marketplace/services/marketplace_product_service.py:116-118` — strips `title`/`description` from product data dict before DB insert (old fields moved to translations table)
- `app/modules/core/services/menu_service.py:263` — converts new menu structure to legacy format via `menu_to_legacy_format()`

**Cleanup:**
1. **Marketplace:** Remove `product_dict.pop("title", None)` and `product_dict.pop("description", None)`. Remove these fields from `MarketplaceProductCreate`/`MarketplaceProductUpdate` schemas if present.
2. **Menu:** Remove `menu_to_legacy_format()` call. Return new menu structure directly. Update templates to consume the new format.

---

### Task 6: Clean up billing convenience methods (LOW)

**Files:**
- `app/modules/billing/services/subscription_service.py:164` — `get_subscription_for_store()` resolves store → merchant → platform → subscription
- `app/modules/billing/services/feature_service.py:223` — `has_feature_for_store()` resolves store → merchant/platform → feature check

**What exists:** Convenience methods that hide the merchant/platform hierarchy from store-level callers.

**Cleanup:**
1. Find all callers of `get_subscription_for_store()` and `has_feature_for_store()`
2. Refactor callers to resolve the merchant/platform explicitly
3. Remove the convenience methods
4. Or: rename them to `resolve_subscription_for_store()` and drop the "backward compat" comment — these may be genuinely useful abstractions even in the target state

**Note:** These might actually be worth keeping as they provide a clean API. The decision is whether the store→merchant→platform resolution is truly "backward compat" or just a useful service abstraction. If kept, just remove the "backward compatibility" comments.

---

### Task 7: Clean up test fixtures and test comments (LOW)

**Files:**
- `tests/fixtures/auth_fixtures.py:52,133` — `is_super_admin=True` on test_admin fixtures "for backward compatibility"
- `tests/integration/middleware/test_merchant_domain_flow.py:10,98` — test verifying subdomain routing "backward compatibility"
- `app/modules/tenancy/schemas/store.py:209` — language field defaults "for backward compatibility"
- `app/modules/tenancy/routes/api/admin_stores.py:213` — deprecated store-level ownership transfer endpoint
- `app/modules/registry.py:67-79` — `__getattr__` lazy module variables
- `alembic/versions_backup/` — migration references (leave as-is, it's history)

**Cleanup:**

| Item | Action |
|------|--------|
| Auth fixtures | Remove "backward compat" comment. Decide if `test_admin` should be super_admin or not based on what tests actually need |
| Merchant domain test | Remove "backward compat" from docstring. This is just testing subdomain routing — a current feature, not a compat shim |
| Store schema language defaults | Remove "backward compat" comment. These are just sensible defaults |
| Admin stores endpoint | Remove deprecated ownership transfer endpoint entirely if merchant-level endpoint exists |
| Registry `__getattr__` | Either migrate all callers to function calls, or keep the pattern and remove the "backward compat" comment (lazy loading is a valid pattern) |
| Alembic backup | Leave as-is — migration history should not be modified |

---

## Execution Order

```
Task 1 (inventory location)     ████████████  HIGH   — DB column removal, needs migration
Task 2 (dashboard/stats)        ████████████  HIGH   — API response shape change
Task 3 (re-export shims)        ████████      MEDIUM — mechanical find-and-replace
Task 4 (module-enabling JSON)   ████████      MEDIUM — seed script update needed
Task 5 (marketplace/menu)       ████████      MEDIUM — schema + template changes
Task 6 (billing convenience)    ████          LOW    — may decide to keep
Task 7 (tests/comments)         ████          LOW    — mostly comment removal
```

Tasks 1-2 are the most impactful (actual dead code/fields). Tasks 3-5 are mechanical cleanup. Tasks 6-7 are mostly comment/naming hygiene.

---

## Verification

After each task:
```bash
python -c "from main import app; print('OK')"
python -m pytest tests/ app/modules/*/tests/ -v --timeout=60
python scripts/validate/validate_architecture.py -v
```

After all tasks:
```bash
grep -rn "backward.compat\|backwards.compat" --include="*.py" | grep -v alembic
# Should return 0 results (alembic history excluded)
```
