Files
orion/docs/proposals/hosting-site-creation-fix.md
Samir Boulahtit 8a70259445
Some checks failed
CI / ruff (push) Successful in 15s
CI / pytest (push) Has been cancelled
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
fix(tenancy): use absolute URL in team invitation email link
Email clients need absolute URLs to make links clickable. The
acceptance_link was a relative path (/store/invitation/accept?token=...)
which rendered as plain text. Now prepends the platform domain with
the correct protocol.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 17:39:46 +02:00

3.4 KiB

Hosting Site Creation — Require Merchant or Prospect

Problem

hosted_site_service.create() (lines 118-131) auto-creates a fake "HostWizard System" Merchant with no owner_user_id as a placeholder to satisfy the Store's merchant_id FK. This is wrong on two levels:

  • Crashes because Merchant.owner_user_id is nullable=False
  • Conceptually wrong — POC sites should belong to a real entity

Current Flow

create()  → invents a system merchant (broken)
             → creates Store under it
             → creates HostedSite

create_from_prospect()  → reads prospect data
                         → calls create() (same broken path)

accept_proposal()  → creates real merchant
                    → reassigns store to it

Proposed Design: Require merchant_id OR prospect_id

Schema (HostedSiteCreate)

  • Add merchant_id: int | None and prospect_id: int | None
  • Add model_validator: at least one must be provided
  • Remove prospect_id from the separate URL path endpoint — unify into one create endpoint

Service (create)

  • If merchant_id provided: validate merchant exists, create Store under it directly
  • If prospect_id provided (no merchant): auto-create a merchant from prospect data using merchant_service.create_merchant_with_owner() (same as accept_proposal does today), then create Store under it
  • If both provided: use the existing merchant, link the prospect
  • Remove the system merchant creation entirely (lines 118-131)

create_from_prospect method

Can be removed — its logic merges into create via the prospect_id field.

accept_proposal

Simplify — merchant already exists at this point, so it only needs to handle subscription creation and prospect status update. The store reassignment is no longer needed.

Files to Change

File Change
hosting/schemas/hosted_site.py Add merchant_id, prospect_id fields + validator
hosting/services/hosted_site_service.py Rewrite create() to use provided merchant or create from prospect. Remove create_from_prospect(). Simplify accept_proposal().
hosting/routes/api/admin_sites.py Remove /from-prospect/{prospect_id} endpoint. The main POST /sites handles both paths now.
hosting/templates/hosting/admin/site-new.html Add merchant selector (dropdown/autocomplete) and prospect selector. Validate one is chosen.
hosting/tests/conftest.py Update hosted_site fixture to pass merchant_id
hosting/tests/unit/test_hosted_site_service.py Update all create calls, remove TestHostedSiteFromProspect class (merge into main tests), add validation tests for the "at least one required" rule

Template Changes Needed

The form currently has a business name + contact fields + a prospect ID input at the bottom. It needs:

  1. A merchant selector (autocomplete dropdown searching existing merchants)
  2. A prospect selector (autocomplete dropdown searching existing prospects)
  3. Validation: at least one must be selected
  4. When prospect is selected: auto-fill business_name, contact fields from prospect data
  5. When merchant is selected: auto-fill business_name from merchant name

What Gets Deleted

  • The entire "HostWizard System" merchant pattern (lines 118-131 in service)
  • The POST /sites/from-prospect/{prospect_id} endpoint
  • The create_from_prospect() service method
  • The prospect ID number input in the template (replaced by proper selector)