# Validation Report: URS-063

**Title:** Distributor Onboarding Workflow with Roles
**Date:** 2026-04-23T03:45:07.127Z
**Duration:** 91.4s
**Overall Status:** ✅ PASS

## User Requirement

> The system shall support onboarding and role assignment of distributors.

*Source: `User_Requirement_Specifications_ZuriMED_DeviceFlow.xlsx` — the run below proves the system meets this requirement.*

## Environment

- **Inbox URL:** http://localhost:65472
- **Database:** localhost:65473/cc_repinbox_dev

## Setup

Status: ✅ PASS

## Test Steps

Each step below corresponds to one Playwright test that ran sequentially. Screenshots and video recordings provide visual evidence of the UI behaviour.

### 1. Step 1: Login as manufacturer admin — ✅ PASS

**What this step proves:**

Proves that Mark (ZuriMED manufacturer admin) can authenticate successfully. Establishes the administrative context required to manage distributor relationships and member invitations in all subsequent steps. This step also opens the single browser context re-used by steps 2–11 so the audit log records exactly one `user:login` event for the manufacturer session (not one per step).

**Screenshots:**

![step 01 manufacturer logged in](screenshots/step-01-manufacturer-logged-in.png)

**Video recording:**

[▶ Watch step recording](videos/step-01-login.webm)

---

### 2. Step 2: Navigate to Distributor Agencies — ✅ PASS

**What this step proves:**

Navigates to the Organization Relationships settings page where distributor agency relationships are managed. Confirms the page is accessible to manufacturer admins and displays the correct heading.

**Screenshots:**

![step 02 org relationships page](screenshots/step-02-org-relationships-page.png)

**Video recording:**

[▶ Watch step recording](videos/step-02-navigate-distributor-agencies.webm)

---

### 3. Step 3: Existing distributors — ✅ PASS

**What this step proves:**

Verifies that existing downstream distributor relationships (StellarTech Medical Solutions) are displayed in the Downstream Organizations section, and that the "Create New Distributor" action is available to the admin.

**Screenshots:**

![step 03 existing distributors](screenshots/step-03-existing-distributors.png)

![step 03 create button visible](screenshots/step-03-create-button-visible.png)

**Video recording:**

[▶ Watch step recording](videos/step-03-existing-distributors.webm)

---

### 4. Step 4: Create distributor — ✅ PASS

**What this step proves:**

Fills out and submits the new distributor form with an organization name, invite email, invitee name, the "Add user as a representative" option, and the Rep ID and Trunk ID inputs. Confirms the system creates the distributor relationship and redirects back to the org-relationships page on success. The submitted Rep ID / Trunk ID values are asserted in DB validation to prove they were persisted.

**Audit events generated by this step:**

*(Evidence scoped to step execution window: 2026-04-23T03:45:28.772Z → 2026-04-23T03:45:36.082Z)*

| Time | Type | Action | User | Org | Performed |
|------|------|--------|------|-----|-----------|
| 2026-04-23 03:45:34Z | user_log | rep_onboarding_request_approved | test-distributor@example.com | New Medical Supplies Inc. | — |
| 2026-04-23 03:45:34Z | transactional_email | rep_created | — | New Medical Supplies Inc. | — |
| 2026-04-23 03:45:35Z | transactional_email | verify_email | — | ZuriMED | — |

**Emails triggered by this step:**

*(Evidence matched by declared name — step timing not available or no events fell in window)*

**Email 1: Representative Account Approved - ZuriMED**

Template: `Representative_Account_Approved_-_ZuriMED`

![Representative Account Approved - ZuriMED](screenshots/emails/2026-04-23T03-45-33-957Z-Representative_Account_Approved_-_ZuriMED.png)

**Email 2: Verify your DeviceFlow account**

Template: `Verify_your_DeviceFlow_account`

![Verify your DeviceFlow account](screenshots/emails/2026-04-23T03-45-35-487Z-Verify_your_DeviceFlow_account.png)

**Email 3: Verify your DeviceFlow account**

Template: `Verify_your_DeviceFlow_account`

![Verify your DeviceFlow account](screenshots/emails/2026-04-23T03-46-06-043Z-Verify_your_DeviceFlow_account.png)

**Screenshots:**

![step 04 create distributor form empty](screenshots/step-04-create-distributor-form-empty.png)

![step 04 form filled with ids](screenshots/step-04-form-filled-with-ids.png)

![step 04 distributor created](screenshots/step-04-distributor-created.png)

**Video recording:**

[▶ Watch step recording](videos/step-04-create-distributor.webm)

---

### 5. Step 5: Duplicate name rejected — ✅ PASS

**What this step proves:**

Re-submits the new-distributor form with the same organization name and asserts the server rejects it with the inline message "An organization with this name already exists". Proves the case-insensitive `ilike` duplicate-name guard in new-child.remote.ts is active end-to-end — not just at the form schema level.

**Screenshots:**

![step 05 duplicate form filled](screenshots/step-05-duplicate-form-filled.png)

![step 05 duplicate error visible](screenshots/step-05-duplicate-error-visible.png)

**Video recording:**

[▶ Watch step recording](videos/step-05-duplicate-name-guard.webm)

---

### 6. Step 6: New distributor in list — ✅ PASS

**What this step proves:**

After creation, the new distributor organization ("New Medical Supplies Inc.") appears alongside the existing StellarTech in the downstream organizations list, confirming the relationship was persisted correctly.

**Screenshots:**

![step 06 new distributor in list](screenshots/step-06-new-distributor-in-list.png)

**Video recording:**

[▶ Watch step recording](videos/step-06-verify-new-distributor.webm)

---

### 7. Step 7: Members page — ✅ PASS

**What this step proves:**

Navigates to the Members settings page and confirms it loads with an existing member table and the "Invite Member" button visible. Proves the admin has the manage_users permission required to send invitations.

**Screenshots:**

![step 07 members page](screenshots/step-07-members-page.png)

**Video recording:**

[▶ Watch step recording](videos/step-07-members-page.webm)

---

### 8. Step 8: Invite member — ✅ PASS

**What this step proves:**

Opens the Invite Member form, fills in the new member's name and email, and explicitly selects the "ZuriMED Admin" role from the role dropdown. Submits the invitation and verifies the form resets on success. The specific role picked here is asserted in DB check 8 so a silent regression (e.g. hardcoding the role on the server) is caught.

**Emails triggered by this step:**

*(Evidence matched by declared name — step timing not available or no events fell in window)*

**Email 1: Verify your DeviceFlow account**

Template: `Verify_your_DeviceFlow_account`

![Verify your DeviceFlow account](screenshots/emails/2026-04-23T03-45-35-487Z-Verify_your_DeviceFlow_account.png)

**Email 2: Verify your DeviceFlow account**

Template: `Verify_your_DeviceFlow_account`

![Verify your DeviceFlow account](screenshots/emails/2026-04-23T03-46-06-043Z-Verify_your_DeviceFlow_account.png)

**Screenshots:**

![step 08 invite form open](screenshots/step-08-invite-form-open.png)

![step 08 role assigned](screenshots/step-08-role-assigned.png)

![step 08 invite submitted](screenshots/step-08-invite-submitted.png)

**Video recording:**

[▶ Watch step recording](videos/step-08-invite-member.webm)

---

### 9. Step 9: Member in list — ✅ PASS

**What this step proves:**

After invitation, the new member ("Jane Doe") appears in the members list with their email address visible, confirming the invitation was recorded and the member is searchable from the admin interface.

**Screenshots:**

![step 09 member in list](screenshots/step-09-member-in-list.png)

**Video recording:**

[▶ Watch step recording](videos/step-09-verify-member.webm)

---

### 10. Step 10: Member details — ✅ PASS

**What this step proves:**

Opens the member detail page for the invited user and verifies the Roles section is present. Confirms that role assignments made during invitation are accessible and visible to administrators.

**Screenshots:**

![step 10 member details page](screenshots/step-10-member-details-page.png)

![step 10 member roles](screenshots/step-10-member-roles.png)

**Video recording:**

[▶ Watch step recording](videos/step-10-member-details.webm)

---

### 11. Step 11: Admin overview — ✅ PASS

**What this step proves:**

Final admin view showing both the full distributor list (StellarTech + new distributor) and the complete member roster. Provides end-to-end evidence that the entire distributor onboarding workflow — from agency creation through member invitation and role assignment — is operational.

**Screenshots:**

![step 11 all distributors](screenshots/step-11-all-distributors.png)

![step 11 all members](screenshots/step-11-all-members.png)

**Video recording:**

[▶ Watch step recording](videos/step-11-admin-overview.webm)

---

### 12. Step 12: Distributor first login — ✅ PASS

**What this step proves:**

Switches to the distributor invitee's perspective in a fresh browser context. Reads the verification email captured during step 4, extracts the `/verify-email?requestId=…&code=…` link, and navigates to it. The server auto-verifies the code and redirects through the password-reset flow, where the invitee sets an initial password and lands on the app home page. Proves the invitation link is functional from the recipient's point of view, not just from the admin's.

**Screenshots:**

![step 12 password form](screenshots/step-12-password-form.png)

![step 12 landing page](screenshots/step-12-landing-page.png)

**Video recording:**

[▶ Watch step recording](videos/step-12-distributor-first-login.webm)

---

## Database Validations

The following SQL queries ran against the application database after the Playwright scenarios completed. Each query asserts a specific condition that proves the feature under test persisted its data correctly.

### New distributor organization created — ✅ PASS

**Assertion:** Organization "New Medical Supplies Inc." should exist in the organizations table

```sql
SELECT id, name, created_at
      FROM organizations
      WHERE name = $1
```

| id | name | created_at |
| --- | --- | --- |
| 019db871-6ad0-78b9-88d0-1a0a769394fd | New Medical Supplies Inc. | 2026-04-23T03:45:33.904Z |

### Distributor relationship established — ✅ PASS

**Assertion:** A distributor relationship should exist between ZuriMED and New Medical Supplies Inc.

```sql
SELECT
        r.parent_organization_id,
        r.child_organization_id,
        r.relationship_type,
        p.name as parent_name,
        c.name as child_name
      FROM organization_relationships r
      JOIN organizations p ON p.id = r.parent_organization_id
      JOIN organizations c ON c.id = r.child_organization_id
      WHERE r.parent_organization_id = $1
        AND c.name = $2
```

| parent_organization_id | child_organization_id | relationship_type | parent_name | child_name |
| --- | --- | --- | --- | --- |
| a1b2c3d4-e5f6-7890-abcd-ef1234567890 | 019db871-6ad0-78b9-88d0-1a0a769394fd | distributor | ZuriMED | New Medical Supplies Inc. |

### Invited distributor user created — ✅ PASS

**Assertion:** User with email test-distributor@example.com should exist in the users table

```sql
SELECT id, email, name, created_at
      FROM users
      WHERE email = $1
```

| id | email | name | created_at |
| --- | --- | --- | --- |
| 019db871-6ace-7c25-ac42-eda8884f37d1 | test-distributor@example.com | John Smith | 2026-04-23T03:45:33.902Z |

### Distributor user has expected role assigned — ✅ PASS

**Assertion:** User test-distributor@example.com should have role "Administrator (preset)" in New Medical Supplies Inc. (hardcoded at new-child.remote.ts as globalOrgAdminRoleId)

```sql
SELECT
        ur.user_id,
        ur.role_id,
        ur.organization_id,
        r.name as role_name,
        u.email,
        o.name as org_name
      FROM user_roles ur
      JOIN users u ON u.id = ur.user_id
      JOIN roles r ON r.id = ur.role_id
      JOIN organizations o ON o.id = ur.organization_id
      WHERE u.email = $1
        AND o.name = $2
```

| user_id | role_id | organization_id | role_name | email | org_name |
| --- | --- | --- | --- | --- | --- |
| 019db871-6ace-7c25-ac42-eda8884f37d1 | 24ee9b94-92a6-4bd4-89e6-48d4f19cfdbe | 019db871-6ad0-78b9-88d0-1a0a769394fd | Administrator (preset) | test-distributor@example.com | New Medical Supplies Inc. |

### User is member of distributor org — ✅ PASS

**Assertion:** User test-distributor@example.com should be a member of New Medical Supplies Inc.

```sql
SELECT
        om.user_id,
        om.organization_id,
        om.active,
        u.email,
        o.name as org_name
      FROM organization_members om
      JOIN users u ON u.id = om.user_id
      JOIN organizations o ON o.id = om.organization_id
      WHERE u.email = $1
        AND o.name = $2
```

| user_id | organization_id | active | email | org_name |
| --- | --- | --- | --- | --- |
| 019db871-6ace-7c25-ac42-eda8884f37d1 | 019db871-6ad0-78b9-88d0-1a0a769394fd | true | test-distributor@example.com | New Medical Supplies Inc. |

### Representation relationship created — ✅ PASS

**Assertion:** A representation relationship should exist for test-distributor@example.com (created via "Add as representative" checkbox)

```sql
SELECT
        orr.id,
        orr.user_id,
        orr.approving_organization_id,
        orr.requesting_organization_id,
        orr.status,
        orr.representative_type,
        u.email,
        ao.name as approving_org,
        ro.name as requesting_org
      FROM organization_representation_relationships orr
      JOIN users u ON u.id = orr.user_id
      JOIN organizations ao ON ao.id = orr.approving_organization_id
      JOIN organizations ro ON ro.id = orr.requesting_organization_id
      WHERE u.email = $1
```

| id | user_id | approving_organization_id | requesting_organization_id | status | representative_type | email | approving_org | requesting_org |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 019db871-6af3-7bc1-83e2-9b20e8b3232e | 019db871-6ace-7c25-ac42-eda8884f37d1 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | 019db871-6ad0-78b9-88d0-1a0a769394fd | active | salesperson | test-distributor@example.com | ZuriMED | New Medical Supplies Inc. |

### Submitted Rep ID persisted for invited user — ✅ PASS

**Assertion:** Rep ID "TEST-REP-063" submitted through the new-distributor form should be stored in real_world_contact_ids for test-distributor@example.com scoped to New Medical Supplies Inc.

```sql
SELECT
        rwci.id,
        rwci.organization_id,
        rwci.contact_id,
        rwci.id_type,
        rwci.value,
        u.email,
        o.name as org_name
      FROM real_world_contact_ids rwci
      JOIN contacts c ON c.id = rwci.contact_id
      JOIN users u ON u.id = c.user_id
      JOIN organizations o ON o.id = rwci.organization_id
      WHERE u.email = $1
        AND o.name = $2
```

| id | organization_id | contact_id | id_type | value | email | org_name |
| --- | --- | --- | --- | --- | --- | --- |
| 019db871-6af1-7d68-94e0-a15bd635c0bd | 019db871-6ad0-78b9-88d0-1a0a769394fd | 019db871-6ad3-7057-bf8f-1361ee09c9dc | zurimed_rep_id | TEST-REP-063 | test-distributor@example.com | New Medical Supplies Inc. |

### Submitted Trunk ID persisted for invited user — ✅ PASS

**Assertion:** Trunk ID "TEST-TRUNK-063" submitted through the new-distributor form should be stored in real_world_location_ids scoped to the manufacturer org (ZuriMED)

```sql
SELECT
        rwli.id,
        rwli.organization_id,
        rwli.location_id,
        rwli.id_type,
        rwli.value,
        u.email,
        o.name as org_name
      FROM real_world_location_ids rwli
      JOIN real_world_locations rwl ON rwl.id = rwli.location_id
      JOIN contacts c ON c.default_location_id = rwl.id
      JOIN users u ON u.id = c.user_id
      JOIN organizations o ON o.id = rwli.organization_id
      WHERE u.email = $1
        AND o.id = $2
```

| id | organization_id | location_id | id_type | value | email | org_name |
| --- | --- | --- | --- | --- | --- | --- |
| 019db871-6af2-708c-9ab1-ae40de821be0 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | 019db871-6ae3-7fd5-a11e-994d6fb5b94b | TRUNK_LOCATION_ID | TEST-TRUNK-063 | test-distributor@example.com | ZuriMED |

### Invited member exists in ZuriMED org — ✅ PASS

**Assertion:** User jane.doe.test@zurimed.com should be a member of ZuriMED

```sql
SELECT
        u.id, u.email, u.name,
        om.active,
        o.name as org_name
      FROM users u
      JOIN organization_members om ON om.user_id = u.id
      JOIN organizations o ON o.id = om.organization_id
      WHERE u.email = $1
        AND om.organization_id = $2
```

| id | email | name | active | org_name |
| --- | --- | --- | --- | --- |
| 019db871-e1ce-7006-aaf8-b2cafb26bdf5 | jane.doe.test@zurimed.com | Jane Doe | true | ZuriMED |

### Invited member has the role selected in the form — ✅ PASS

**Assertion:** User jane.doe.test@zurimed.com should have role "ZuriMED Admin" in ZuriMED (this is the role the spec picks from the Invite Member dropdown)

```sql
SELECT
        ur.user_id,
        ur.role_id,
        r.name as role_name,
        u.email,
        o.name as org_name
      FROM user_roles ur
      JOIN users u ON u.id = ur.user_id
      JOIN roles r ON r.id = ur.role_id
      JOIN organizations o ON o.id = ur.organization_id
      WHERE u.email = $1
        AND ur.organization_id = $2
```

| user_id | role_id | role_name | email | org_name |
| --- | --- | --- | --- | --- |
| 019db871-e1ce-7006-aaf8-b2cafb26bdf5 | 39d0e1f2-a3b4-5678-3456-789012345678 | ZuriMED Admin | jane.doe.test@zurimed.com | ZuriMED |

### Distributor org has feature flags — ✅ PASS

**Assertion:** New Medical Supplies Inc. should have feature flags propagated from parent org

```sql
SELECT
        ff.id as flag_id,
        ff.value,
        o.name as org_name
      FROM feature_flags ff
      JOIN organizations o ON o.id = ff.organization_id
      WHERE o.name = $1
      ORDER BY ff.id
```

| flag_id | value | org_name |
| --- | --- | --- |
| billing | true | New Medical Supplies Inc. |
| home_screen_prototype | true | New Medical Supplies Inc. |
| inbox_checklist_items | true | New Medical Supplies Inc. |
| inbox_tickets | true | New Medical Supplies Inc. |
| inventory_snapshots | true | New Medical Supplies Inc. |
| order_requests | true | New Medical Supplies Inc. |
| org_type:distributor | true | New Medical Supplies Inc. |
| organization_representation | true | New Medical Supplies Inc. |
| sales_accounts | true | New Medical Supplies Inc. |
| workflow_visualization | true | New Medical Supplies Inc. |

### manufacturer_can_create_distributors flag enabled — ✅ PASS

**Assertion:** manufacturer_can_create_distributors feature flag should be enabled for ZuriMED

```sql
SELECT id, organization_id, value
      FROM feature_flags
      WHERE id = 'manufacturer_can_create_distributors'
        AND organization_id = $1
```

| id | organization_id | value |
| --- | --- | --- |
| manufacturer_can_create_distributors | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | true |

## Audit & Email Assertion Ledger

Per-declaration outcome of every `expectedAuditActions` and `expectedEmailTemplates` entry written into the orchestrator. Missing evidence here is a real test failure, not a soft warning.

### Audit Action Assertions

Each row asserts that a declared `expectedAuditActions` entry produced a matching row in `audit_events`. A ❌ flips overall status to FAIL — the declaration is real proof, not just an annotation.

| Step | Expected Audit Action | Found |
|------|-----------------------|-------|
| Step 4: Create distributor | `user_log:rep_onboarding_request_approved` | ✅ |

### Email Template Assertions

Each row asserts that a declared `expectedEmailTemplates` entry was matched (case-insensitive substring) by a captured email subject or template. A ❌ flips overall status to FAIL.

| Step | Expected Template | Found |
|------|-------------------|-------|
| Step 4: Create distributor | `Verify your DeviceFlow` | ✅ |
| Step 4: Create distributor | `Representative Account Approved` | ✅ |
| Step 8: Invite member | `Verify your DeviceFlow` | ✅ |

## Audit Log Events

Every row written to `audit_events` while this test was running (scoped to the demo organizations). Provides compliance evidence that user actions are traced end-to-end (URS-003).

**Capture window start:** 2026-04-23T03:45:05.195Z

<details><summary>Query used to capture events</summary>

```sql
SELECT
    ae.created_at,
    ae.event_type,
    ae.action,
    ae.user_id,
    u.email AS user_email,
    ae.organization_id,
    o.name AS organization_name,
    ae.object_id,
    ae.secondary_object_id,
    ae.payload,
    ae.route,
    ae.trace_id
  FROM audit_events ae
  LEFT JOIN users u ON u.id = ae.user_id
  LEFT JOIN organizations o ON o.id = ae.organization_id
  WHERE ae.created_at >= $1
    AND ae.organization_id = ANY($2::uuid[])
  ORDER BY ae.created_at ASC
```
</details>

5 event(s) captured:

| Time | Type | Action | User | Org | Object ID | Performed | Reason |
|------|------|--------|------|-----|-----------|-----------|--------|
| 2026-04-23 03:45:13Z | user_log | user:login | mark.manufacturer@zurimed.com | ZuriMED | — | — |  |
| 2026-04-23 03:45:34Z | user_log | rep_onboarding_request_approved | test-distributor@example.com | New Medical Supplies Inc. | 019db871-6af3-7bc1-83e2-9b20e8b3232e | — |  |
| 2026-04-23 03:45:34Z | transactional_email | rep_created | — | New Medical Supplies Inc. | 019db871-6af3-7bc1-83e2-9b20e8b3232e | — |  |
| 2026-04-23 03:45:35Z | transactional_email | verify_email | — | ZuriMED | 019db871-6ace-7c25-ac42-eda8884f37d1 | — |  |
| 2026-04-23 03:46:06Z | transactional_email | verify_email | — | ZuriMED | 019db871-e1ce-7006-aaf8-b2cafb26bdf5 | — |  |

## Email Evidence

3 notification email(s) were captured during this test run. Each email is rendered as a screenshot for compliance review.

### 1. Representative Account Approved - ZuriMED

**Template:** `Representative_Account_Approved_-_ZuriMED`

![Representative Account Approved - ZuriMED](screenshots/emails/2026-04-23T03-45-33-957Z-Representative_Account_Approved_-_ZuriMED.png)

### 2. Verify your DeviceFlow account

**Template:** `Verify_your_DeviceFlow_account`

![Verify your DeviceFlow account](screenshots/emails/2026-04-23T03-45-35-487Z-Verify_your_DeviceFlow_account.png)

### 3. Verify your DeviceFlow account

**Template:** `Verify_your_DeviceFlow_account`

![Verify your DeviceFlow account](screenshots/emails/2026-04-23T03-46-06-043Z-Verify_your_DeviceFlow_account.png)
