# Validation Report: URS-071

**Title:** Distributor returns expiring, expired, or recalled stock to the manufacturer
**Date:** 2026-08-03T23:05:12.565Z
**Duration:** 28.2s
**Overall Status:** ✅ PASS

## User Requirement

> The system shall let a distributor return expiring, expired, or recalled stock to the manufacturer through a dedicated returns flow that records the return type, derives the return location from the selected lots, requires no sales account, and creates no replacement order; and shall record an audit decision when the replacement-order step is skipped.

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

## Environment

- **Inbox URL:** http://localhost:35551
- **Database:** localhost:46175/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 and open the expiry return flow — ✅ PASS

**What this step proves:**

A Corveta sales rep (Blair Bennett) logs in and opens the dedicated expiry/recall returns flow at /returns/new?type=expiry. The first step asks why the stock is coming back (Expiring or Recall) and which manufacturer it belongs to. This verifies the lean flow is reachable and gated by the rep's returns permissions and the `returns` feature flag.

**Screenshots:**

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

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

**Video recording:**

[▶ Watch step recording](videos/step-01-open-expiry-flow.webm)

---

### 2. Step 2: Select manufacturer and expiring lots — ✅ PASS

**What this step proves:**

The rep selects Vantis as the manufacturer (auto-selected when Corveta stocks a single manufacturer) and advances to the lot picker. The picker lists the distributor's own returnable inventory with expiration status badges; the seeded URS-071 lot renders as "expiring soon". The rep uses "Select all expiring" to choose it. This verifies the flow surfaces the distributor's own expiring stock and lets the rep pick the lots to return.

**Screenshots:**

![step 02 lot picker](screenshots/step-02-lot-picker.png)

![step 02 lots selected](screenshots/step-02-lots-selected.png)

**Video recording:**

[▶ Watch step recording](videos/step-02-select-lots.webm)

---

### 3. Step 3: Review and submit the expiry return — ✅ PASS

**What this step proves:**

The rep reviews the return — manufacturer, reason, and selected lots — adds notes, and submits. The submit path creates a return with return_type=expiry, derives the return location from the selected lots, attaches no sales account, and creates no replacement order. Because the replacement-order step is skipped, the service writes a decision audit row (create_replacement_order_on_return, performed=false, reason=return_type_not_incident). This is the primary URS-071 evidence that the lean return persists correctly and the skip is audited.

**Audit events generated by this step:**

*(Evidence scoped to step execution window: 2026-08-03T23:05:37.084Z → 2026-08-03T23:05:39.074Z)*

| Time | Type | Action | User | Org | Performed |
|------|------|--------|------|-----|-----------|
| 2026-08-03 23:05:38Z | decision | create_replacement_order_on_return | blair.bennett@corvetasurgical.com | Vantis | no |
| 2026-08-03 23:05:38Z | return_request | created | blair.bennett@corvetasurgical.com | Vantis | — |

**Screenshots:**

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

![step 03 submission success](screenshots/step-03-submission-success.png)

**Video recording:**

[▶ Watch step recording](videos/step-03-submit.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.

### Expiry return created for Corveta → Vantis — ✅ PASS

**Assertion:** A URS-071 return with return_type=expiry and status=submitted should exist (marker: "URS-071 validation run").

```sql
SELECT id, return_number, status, return_type, sales_account_id,
      return_location_id, replacement_order_id, notes, created_at
    FROM returns
    WHERE sales_organization_id = $1
      AND manufacturer_organization_id = $2
      AND return_type = 'expiry'
      AND notes LIKE '%URS-071%'
      AND created_at > NOW() - INTERVAL '2 hours'
    ORDER BY created_at DESC
```

| id | return_number | status | return_type | sales_account_id | return_location_id | replacement_order_id | notes | created_at |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 019fc9e0-2997-7e03-b5c1-23a27310fb16 | RET-1 | submitted | expiry | NULL | a6e7f8a9-b0c1-2345-0123-456789012345 | NULL | URS-071 validation run — submitted at 2026-08-03T23:05:36.507Z | 2026-08-03T23:05:38.943Z |

### No replacement order created for the expiry return — ✅ PASS

**Assertion:** Expiry/recall returns never create a replacement order, so replacement_order_id must be NULL.

```sql
-- replacement_order_id from the return row above
```

| replacement_order_id |
| --- |
| NULL |

### Return location derived from lots and no sales account required — ✅ PASS

**Assertion:** The return location should be derived from the selected lots (return_location_id NOT NULL) and no account should be attached (sales_account_id NULL).

```sql
-- return_location_id and sales_account_id from the return row above
```

| return_location_id | sales_account_id |
| --- | --- |
| a6e7f8a9-b0c1-2345-0123-456789012345 | NULL |

### Return item carries the seeded lot, manufacturer, and location — ✅ PASS

**Assertion:** At least one return_items row should carry lot_number=URS071-SHV-001, manufacturer_organization_id=Vantis, and location_id=TRUNK-25 (the lot the picker selected).

```sql
SELECT ri.id, ri.product_id, ri.lot_number, ri.manufacturer_organization_id,
      ri.location_id, ri.return_item_number
    FROM return_items ri
    WHERE ri.return_id = $1
    ORDER BY ri.lot_number
```

| id | product_id | lot_number | manufacturer_organization_id | location_id | return_item_number |
| --- | --- | --- | --- | --- | --- |
| 019fc9e0-299b-75dc-982e-664efc11eecb | 01989c9e-fb9b-7f7e-a345-ee490ddfc1cc | URS071-SHV-001 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | a6e7f8a9-b0c1-2345-0123-456789012345 | RET-1-72200873-8 |
| 019fc9e0-299b-75dc-982e-665294c9f11d | 01989c9e-fb9b-7f7e-a345-ee490ddfc1cc | URS071-SHV-001 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | a6e7f8a9-b0c1-2345-0123-456789012345 | RET-1-72200873-12 |
| 019fc9e0-299b-75dc-982e-6651658e7302 | 01989c9e-fb9b-7f7e-a345-ee490ddfc1cc | URS071-SHV-001 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | a6e7f8a9-b0c1-2345-0123-456789012345 | RET-1-72200873-11 |
| 019fc9e0-299b-75dc-982e-6650b75279a8 | 01989c9e-fb9b-7f7e-a345-ee490ddfc1cc | URS071-SHV-001 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | a6e7f8a9-b0c1-2345-0123-456789012345 | RET-1-72200873-10 |
| 019fc9e0-299b-75dc-982e-664f6e00d9c5 | 01989c9e-fb9b-7f7e-a345-ee490ddfc1cc | URS071-SHV-001 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | a6e7f8a9-b0c1-2345-0123-456789012345 | RET-1-72200873-9 |
| 019fc9e0-299b-75dc-982e-665c5a717eb5 | 01989c9e-9ccf-7a73-8ff7-b60007f2de85 | VM-PMX-2418 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | b7f8a9b0-c1d2-3456-1234-567890123456 | RET-1-72200616-4 |
| 019fc9e0-299b-75dc-982e-6659ec7ce25b | 01989c9e-9ccf-7a73-8ff7-b60007f2de85 | VM-PMX-2418 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | b7f8a9b0-c1d2-3456-1234-567890123456 | RET-1-72200616-1 |
| 019fc9e0-299b-75dc-982e-665a01f54553 | 01989c9e-9ccf-7a73-8ff7-b60007f2de85 | VM-PMX-2418 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | b7f8a9b0-c1d2-3456-1234-567890123456 | RET-1-72200616-2 |
| 019fc9e0-299b-75dc-982e-665b9cdbac77 | 01989c9e-9ccf-7a73-8ff7-b60007f2de85 | VM-PMX-2418 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | b7f8a9b0-c1d2-3456-1234-567890123456 | RET-1-72200616-3 |
| 019fc9e0-299b-75dc-982e-664bbaa6eade | 01989c9e-fb9b-7f7e-a345-ee490ddfc1cc | VM-SHV-2390 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | b7f8a9b0-c1d2-3456-1234-567890123456 | RET-1-72200873-5 |
| 019fc9e0-299b-75dc-982e-664cc63a9eed | 01989c9e-fb9b-7f7e-a345-ee490ddfc1cc | VM-SHV-2390 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | b7f8a9b0-c1d2-3456-1234-567890123456 | RET-1-72200873-6 |
| 019fc9e0-299b-75dc-982e-664d867d9f44 | 01989c9e-fb9b-7f7e-a345-ee490ddfc1cc | VM-SHV-2390 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | b7f8a9b0-c1d2-3456-1234-567890123456 | RET-1-72200873-7 |
| 019fc9e0-299b-75dc-982e-664a849d2cd2 | 01989c9e-fb9b-7f7e-a345-ee490ddfc1cc | VM-SHV-2401 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | a6e7f8a9-b0c1-2345-0123-456789012345 | RET-1-72200873-4 |
| 019fc9e0-299b-75dc-982e-66498ea9104e | 01989c9e-fb9b-7f7e-a345-ee490ddfc1cc | VM-SHV-2401 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | a6e7f8a9-b0c1-2345-0123-456789012345 | RET-1-72200873-3 |
| 019fc9e0-299b-75dc-982e-66481e62b906 | 01989c9e-fb9b-7f7e-a345-ee490ddfc1cc | VM-SHV-2401 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | a6e7f8a9-b0c1-2345-0123-456789012345 | RET-1-72200873-2 |
| 019fc9e0-299b-75dc-982e-66478d45c65e | 01989c9e-fb9b-7f7e-a345-ee490ddfc1cc | VM-SHV-2401 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | a6e7f8a9-b0c1-2345-0123-456789012345 | RET-1-72200873-1 |
| 019fc9e0-299b-75dc-982e-6657cc237b06 | 01989c9e-fb9b-7f7e-a345-ee490ddfc1cc | VM-SHV-2415 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | a6e7f8a9-b0c1-2345-0123-456789012345 | RET-1-72200873-17 |
| 019fc9e0-299b-75dc-982e-6658dec5eb7b | 01989c9e-fb9b-7f7e-a345-ee490ddfc1cc | VM-SHV-2415 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | a6e7f8a9-b0c1-2345-0123-456789012345 | RET-1-72200873-18 |
| 019fc9e0-299b-75dc-982e-665399c70506 | 01989c9e-fb9b-7f7e-a345-ee490ddfc1cc | VM-SHV-2415 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | a6e7f8a9-b0c1-2345-0123-456789012345 | RET-1-72200873-13 |
| 019fc9e0-299b-75dc-982e-6655b399b619 | 01989c9e-fb9b-7f7e-a345-ee490ddfc1cc | VM-SHV-2415 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | a6e7f8a9-b0c1-2345-0123-456789012345 | RET-1-72200873-15 |
| 019fc9e0-299b-75dc-982e-6654deba9be4 | 01989c9e-fb9b-7f7e-a345-ee490ddfc1cc | VM-SHV-2415 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | a6e7f8a9-b0c1-2345-0123-456789012345 | RET-1-72200873-14 |
| 019fc9e0-299b-75dc-982e-665628b2bfb0 | 01989c9e-fb9b-7f7e-a345-ee490ddfc1cc | VM-SHV-2415 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | a6e7f8a9-b0c1-2345-0123-456789012345 | RET-1-72200873-16 |

### Replacement-order skip recorded as an audit decision — ✅ PASS

**Assertion:** Submitting an expiry return should write a decision audit_events row with action='create_replacement_order_on_return', performed=false, reason='return_type_not_incident'.

```sql
SELECT id, event_type, action, object_id, organization_id, payload, created_at
    FROM audit_events
    WHERE created_at >= $1
      AND event_type = 'decision'
      AND action = 'create_replacement_order_on_return'
      AND payload->>'performed' = 'false'
      AND payload->>'reason' = 'return_type_not_incident'
    ORDER BY created_at DESC
```

| id | event_type | action | object_id | organization_id | payload | created_at |
| --- | --- | --- | --- | --- | --- | --- |
| 019fc9e0-29a1-7639-ada5-3d164c6cb348 | decision | create_replacement_order_on_return | 019fc9e0-2997-7e03-b5c1-23a27310fb16 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | `{"reason":"return_type_not_incident","performed":false,"entityType":"return"}` | 2026-08-03T23:05:38.943Z |

## 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 3: Review and submit the expiry return | `decision:create_replacement_order_on_return` | ✅ |

## 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-08-03T23:05:10.689Z

<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>

7 event(s) captured:

| Time | Type | Action | User | Org | Object ID | Performed | Reason |
|------|------|--------|------|-----|-----------|-----------|--------|
| 2026-08-03 23:05:10Z | checklist | checklists.create | — | Corveta Surgical Group | 019fc9df-bbde-795b-b31d-9d52feabc8bb | — |  |
| 2026-08-03 23:05:10Z | decision | ensure_lot_expiration_checklist | — | Corveta Surgical Group | b2c3d4e5-f6a7-8901-bcde-f12345678901 | yes | created_1_reopened_0_already_open_1 |
| 2026-08-03 23:05:16Z | user_log | user:login | blair.bennett@corvetasurgical.com | Corveta Surgical Group | — | — |  |
| 2026-08-03 23:05:22Z | user_log | user:login | blair.bennett@corvetasurgical.com | Corveta Surgical Group | — | — |  |
| 2026-08-03 23:05:30Z | user_log | user:login | blair.bennett@corvetasurgical.com | Corveta Surgical Group | — | — |  |
| 2026-08-03 23:05:38Z | decision | create_replacement_order_on_return | blair.bennett@corvetasurgical.com | Vantis | 019fc9e0-2997-7e03-b5c1-23a27310fb16 | no | return_type_not_incident |
| 2026-08-03 23:05:38Z | return_request | created | blair.bennett@corvetasurgical.com | Vantis | 019fc9e0-2997-7e03-b5c1-23a27310fb16 | — |  |
