# Validation Report: URS-049

**Title:** Update inventory from Uniphar ASN on delivered status
**Date:** 2026-04-23T03:43:11.182Z
**Duration:** 101.8s
**Overall Status:** ✅ PASS

## User Requirement

> The system shall update inventory based on ASN information, upon receipt of "delivered" status from Uniphar.

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

## Environment

- **Inbox URL:** http://localhost:64871
- **Database:** localhost:64872/cc_repinbox_dev

## Setup

Status: ✅ PASS

## Run Recording

This validation was captured as a single end-to-end screencast covering every step below.

[▶ Watch full run recording](videos/step-01-urs-049-uniphar-asn.webm)

## 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: Pre-ASN inventory — ✅ PASS

**What this step proves:**

Documents the inventory state at BOSS-001 before any ASN is processed. This baseline proves that inventory increases seen after Step 9 are caused by the ASN delivery, not by pre-existing stock. The full end-to-end screencast video (attached below) covers all steps from pre-ASN baseline through delivery confirmation and inventory materialization.

**Screenshots:**

![step 01 inventory before asn](screenshots/step-01-inventory-before-asn.png)

---

### 2. Step 3: Intake email list (ASN received) — ✅ PASS

**What this step proves:**

Confirms the Uniphar/secure-wms.com ASN email appears in the email intake log (/settings/email_intake) with a "processed" status. The email arrived via the same SES → Minio → Restate Import.processMail pipeline used in production — only the SES/SQS hops are replaced by a direct Minio upload in the test environment.

**Screenshots:**

![step 03 intake email list](screenshots/step-03-intake-email-list.png)

---

### 3. Step 4: Intake email detail (parsed ASN body) — ✅ PASS

**What this step proves:**

Drills into the ASN email detail page, showing the sender (notifications@secure-wms.com), subject, HTML table body, and the shipping_package + import_job entities the pipeline produced. Proves the parser correctly extracted account code, carrier, tracking number, and all three SKU/lot/quantity rows from the secure-wms.com HTML table format.

**Screenshots:**

![step 04 intake email detail](screenshots/step-04-intake-email-detail.png)

---

### 4. Step 5: Import job log (ASN parsed) — ✅ PASS

**What this step proves:**

Shows the shipping-notice import job log with the parsed SKU, LOT, and quantity rows from the ASN. Confirms the text-extraction import_config matched the URS-049 email rule and the import job completed with status=complete, packagesCreated=1, itemsAdded=3.

**Screenshots:**

![step 05 import job log](screenshots/step-05-import-job-log.png)

---

### 5. Step 6: Shipping package detail (pre-delivery) — ✅ PASS

**What this step proves:**

Captures the shipping_packages detail page before the delivery webhook fires. Confirms the package was routed to the ZuriMED BOSS-001 destination location with the correct tracking number and three shipping_package_items matching the ASN quantities.

**Screenshots:**

![step 06 shipping package pre delivery](screenshots/step-06-shipping-package-pre-delivery.png)

---

### 6. Step 8: Shipping package detail (delivered) — ✅ PASS

**What this step proves:**

After the PackageTracker.mockUpdate webhook sets status=delivered, the package detail page is re-captured to show the updated status. This is the trigger that causes the auto-add-inventory hook to fire, materializing inventory_items at BOSS-001.

**Screenshots:**

![step 08 shipping package delivered](screenshots/step-08-shipping-package-delivered.png)

---

### 7. Step 9: Inventory after ASN delivery (lots materialized) — ✅ PASS

**What this step proves:**

Documents the inventory state at BOSS-001 after the delivery event. The three ASN lots (SP019N1A, FL516SNA, ZM2025MR) now appear with quantities matching the ASN exactly, proving the requirement: "The system shall update inventory based on ASN information, upon receipt of delivered status from Uniphar."

**Screenshots:**

![step 09 inventory after delivery](screenshots/step-09-inventory-after-delivery.png)

---

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

### intake_emails row exists for the Uniphar ASN — ✅ PASS

**Assertion:** intake_emails from notifications@secure-wms.com matched the URS-049 rule and was stored.

```sql

    SELECT id, "from", "to", subject, matched_rule_id, received_at
      FROM intake_emails
     WHERE matched_rule_id = $1 AND received_at >= $2
     ORDER BY received_at DESC
     LIMIT 1
```

| id | from | to | subject | matched_rule_id | received_at |
| --- | --- | --- | --- | --- | --- |
| 019db86f-72c3-7098-875f-d279e302e02b | notifications@secure-wms.com | zurimed.asn@agent.getdeviceflow.com | requested transaction notification | 01950049-0049-7049-8049-000000000002 | 2026-04-23T03:43:24.877Z |

### import_jobs row completed for the Uniphar ASN — ✅ PASS

**Assertion:** import_jobs.status=complete with packagesCreated=1 and itemsAdded=3.

```sql

    SELECT id, status, info
      FROM import_jobs
     WHERE organization_id = $1 AND import_config_id = $2 AND created_at >= $3
     ORDER BY created_at DESC
     LIMIT 1
```

| id | status | info |
| --- | --- | --- |
| 019db86f-72d0-7aed-9965-990a53b4df5b | complete | `{"itemsAdded":3,"missingContacts":[],"packagesCreated":1,"missingLocations":[],"packagesReconciled":0,"backorderedItemsAdded":0,"unmatchedOrderNumbers":["URS049-PKG-A"],"fulfillmentOrdersLinked":0}` |

### shipping_packages row created for the Uniphar ASN — ✅ PASS

**Assertion:** A shipping_packages row with the ASN tracking number was created, routed to BOSS-001.

```sql

    SELECT id, tracking_number, carrier, destination_location_id
      FROM shipping_packages
     WHERE organization_id = $1 AND created_at >= $2 AND tracking_number = $3
```

| id | tracking_number | carrier | destination_location_id |
| --- | --- | --- | --- |
| 019db86f-72ff-7c7b-8ad8-9baeabb256b6 | 391915791211 | fedex | d0b29b2c-cef4-41b4-86d6-32efb93f92e7 |

### shipping_package_items has 3 rows with correct quantities — ✅ PASS

**Assertion:** Each ASN SKU line produced a shipping_package_items row with the expected quantity.

```sql

    SELECT spi.id, spi.quantity, spi.info
      FROM shipping_package_items spi
      JOIN shipping_packages sp ON sp.id = spi.package_id
     WHERE sp.organization_id = $1 AND sp.created_at >= $2
       AND sp.tracking_number = $3
     ORDER BY spi.source_file_row ASC NULLS LAST, spi.id ASC
```

| id | quantity | info |
| --- | --- | --- |
| 019db86f-7303-704c-93d3-75b218d5a36f | 3 | `{"lot":"LOT-URS049-SP-A1","sku":"SP019N1A"}` |
| 019db86f-7303-704c-93d3-75b3eddad9d3 | 5 | `{"lot":"LOT-URS049-FL-B2","sku":"FL516SNA"}` |
| 019db86f-7303-704c-93d3-75b4f880420b | 2 | `{"lot":"LOT-URS049-MR-C3","sku":"ZM2025MR"}` |

### shipping_package was delivered — ✅ PASS

**Assertion:** shipping_packages.status=delivered after PackageTracker.mockUpdate.

```sql

    SELECT id, status, delivered_at, destination_location_id
      FROM shipping_packages
     WHERE organization_id = $1 AND created_at >= $2 AND tracking_number = $3
```

| id | status | delivered_at | destination_location_id |
| --- | --- | --- | --- |
| 019db86f-72ff-7c7b-8ad8-9baeabb256b6 | delivered | 2026-04-23T03:43:42.546Z | d0b29b2c-cef4-41b4-86d6-32efb93f92e7 |

### inventory_items materialized for each ASN line — ✅ PASS

**Assertion:** One inventory_items row per ASN (productId, lot) at BOSS-001 with quantity_ready matching the ASN.

```sql

    SELECT ii.id, ii.product_id, ii.lot, ii.quantity_ready, ii.real_world_location_id
      FROM inventory_items ii
     WHERE ii.organization_id = $1
       AND ii.product_id = ANY($2)
       AND NOT (ii.id = ANY($3))
```

| id | product_id | lot | quantity_ready | real_world_location_id |
| --- | --- | --- | --- | --- |
| 019db870-a4ca-7ecd-a77a-ab2e31540896 | 01989ca2-6a54-7834-8376-06a0251bacd8 | LOT-URS049-SP-A1 | 3 | d0b29b2c-cef4-41b4-86d6-32efb93f92e7 |
| 019db870-a4ca-7ecd-a77a-ab2f15776a03 | 01989ca1-f2f8-7ab7-8269-7179342797cc | LOT-URS049-FL-B2 | 5 | d0b29b2c-cef4-41b4-86d6-32efb93f92e7 |
| 019db870-a4ca-7ecd-a77a-ab30d34af581 | 01984270-64c1-76da-aa82-eaf3f0a8326b | LOT-URS049-MR-C3 | 2 | d0b29b2c-cef4-41b4-86d6-32efb93f92e7 |

### inventory_history rows recorded for the ASN delivery — ✅ PASS

**Assertion:** 3 inventory_history rows written during the run window for the ASN SKUs.

```sql

    SELECT ih.id, ih.item_id, ih.quantity, ih.status, ih.info
      FROM inventory_history ih
      JOIN inventory_items ii ON ii.id = ih.item_id
     WHERE ih.organization_id = $1
       AND ih.created_at >= $2
       AND ii.product_id = ANY($3)
```

| id | item_id | quantity | status | info |
| --- | --- | --- | --- | --- |
| 019db870-a4d5-7819-9faa-8cb6673f14f4 | 019db870-a4ca-7ecd-a77a-ab2e31540896 | 3 | ready | `{"action":"added","reason":"auto_added_package","packageId":"019db86f-72ff-7c7b-8ad8-9baeabb256b6"}` |
| 019db870-a4d5-7819-9faa-8cb73a2ae320 | 019db870-a4ca-7ecd-a77a-ab2f15776a03 | 5 | ready | `{"action":"added","reason":"auto_added_package","packageId":"019db86f-72ff-7c7b-8ad8-9baeabb256b6"}` |
| 019db870-a4d5-7819-9faa-8cb84f198f34 | 019db870-a4ca-7ecd-a77a-ab30d34af581 | 2 | ready | `{"action":"added","reason":"auto_added_package","packageId":"019db86f-72ff-7c7b-8ad8-9baeabb256b6"}` |

## 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:43:09.253Z

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

2 event(s) captured:

| Time | Type | Action | User | Org | Object ID | Performed | Reason |
|------|------|--------|------|-----|-----------|-----------|--------|
| 2026-04-23 03:43:18Z | user_log | user:login | alex.admin@zurimed.com | ZuriMED | — | — |  |
| 2026-04-23 03:44:43Z | decision | enable_team_tracked_inventory_routing | — | ZuriMED | 019db86f-72ff-7c7b-8ad8-9baeabb256b6 | yes | Package not linked to direct or dropship order |
