  1200→| Field | Value |
  1201→|-------|-------|
  1202→| handle | canvas-tote-bag |
  1203→| status | active |
  1204→| vendor | Acme Accessories |
  1205→| product_type | Accessories |
  1206→| tags | ["trending"] |
  1207→| description_html | "Heavy-duty canvas tote bag with reinforced handles. Spacious enough for daily essentials." |
  1208→| published_at | now |
  1209→
  1210→Collections: (none)
  1211→
  1212→Options: Color (Natural, Black)
  1213→
  1214→Variants (2 total): price_amount = 1999, weight_g = 300, inventory = 40 each (deny)
  1215→
  1216→---
  1217→
  1218→**Product 14: "Bucket Hat"**
  1219→
  1220→| Field | Value |
  1221→|-------|-------|
  1222→| handle | bucket-hat |
  1223→| status | active |
  1224→| vendor | Acme Accessories |
  1225→| product_type | Accessories |
  1226→| tags | ["new", "trending"] |
  1227→| description_html | "Lightweight bucket hat for sun protection. Packable design, washed cotton twill." |
  1228→| published_at | now |
  1229→
  1230→Collections: New Arrivals
  1231→
  1232→Options: Size (S/M, L/XL), Color (Beige, Black, Olive)
  1233→
  1234→Variants (6 total): price_amount = 2499, weight_g = 80, inventory = 22 each (deny)
  1235→
  1236→---
  1237→
  1238→**Product 15: "Unreleased Winter Jacket" -- DRAFT**
  1239→
  1240→| Field | Value |
  1241→|-------|-------|
  1242→| handle | unreleased-winter-jacket |
  1243→| status | **draft** |
  1244→| vendor | Acme Outerwear |
  1245→| product_type | Jackets |
  1246→| tags | ["limited"] |
  1247→| description_html | "Upcoming winter collection piece. Insulated puffer jacket with water-resistant shell." |
  1248→| published_at | **null** |
  1249→
  1250→Collections: (none - draft not assigned)
  1251→
  1252→Options: Size (S, M, L, XL)
  1253→
  1254→Variants (4 total): price_amount = 14999, weight_g = 900, inventory = **0 each** (deny)
  1255→
  1256→**Test purpose:** Verifies draft products do not appear on the storefront.
  1257→
  1258→---
  1259→
  1260→**Product 16: "Discontinued Raincoat" -- ARCHIVED**
  1261→
  1262→| Field | Value |
  1263→|-------|-------|
  1264→| handle | discontinued-raincoat |
  1265→| status | **archived** |
  1266→| vendor | Acme Outerwear |
  1267→| product_type | Jackets |
  1268→| tags | [] |
  1269→| description_html | "Lightweight waterproof raincoat. This product has been discontinued." |
  1270→| published_at | 6 months ago |
  1271→
  1272→Collections: (none)
  1273→
  1274→Options: Size (M, L)
  1275→
  1276→Variants (2 total): price_amount = 8999, weight_g = 400, inventory = 3 each (deny)
  1277→
  1278→**Test purpose:** Verifies archived products do not appear on the storefront.
  1279→
  1280→---
  1281→
  1282→**Product 17: "Limited Edition Sneakers" -- SOLD OUT (deny policy)**
  1283→
  1284→| Field | Value |
  1285→|-------|-------|
  1286→| handle | limited-edition-sneakers |
  1287→| status | active |
  1288→| vendor | Acme Sport |
  1289→| product_type | Shoes |
  1290→| tags | ["limited"] |
  1291→| description_html | "Limited edition collaboration sneakers. Once they are gone, they are gone." |
  1292→| published_at | now |
  1293→
  1294→Collections: (none)
  1295→
  1296→Options: Size (EU 40, EU 42, EU 44)
  1297→
  1298→Variants (3 total): price_amount = 15999, weight_g = 650, inventory = **0 each, policy = "deny"**
  1299→
  1300→**Test purpose:** Tests "sold out" display and add-to-cart prevention.
  1301→
  1302→---
  1303→
  1304→**Product 18: "Backorder Denim Jacket" -- OUT OF STOCK (continue policy)**
  1305→
  1306→| Field | Value |
  1307→|-------|-------|
  1308→| handle | backorder-denim-jacket |
  1309→| status | active |
  1310→| vendor | Acme Denim |
  1311→| product_type | Jackets |
  1312→| tags | ["popular"] |
  1313→| description_html | "Classic denim jacket. Currently on backorder - ships within 2-3 weeks." |
  1314→| published_at | now |
  1315→
  1316→Collections: (none)
  1317→
  1318→Options: Size (S, M, L, XL)
  1319→
  1320→Variants (4 total): price_amount = 9999, weight_g = 750, inventory = **0 each, policy = "continue"**
  1321→
  1322→**Test purpose:** Tests that items with "continue" inventory policy can be added to cart despite zero stock.
  1323→
  1324→---
  1325→
  1326→**Product 19: "Gift Card" -- DIGITAL (no shipping)**
  1327→
  1328→| Field | Value |
  1329→|-------|-------|
  1330→| handle | gift-card |
  1331→| status | active |
  1332→| vendor | Acme Fashion |
  1333→| product_type | Gift Cards |
  1334→| tags | ["popular"] |
  1335→| description_html | "Digital gift card delivered via email. The perfect gift when you are not sure what to choose." |
  1336→| published_at | now |
  1337→
  1338→Collections: (none)
  1339→
  1340→Options: Amount (25 EUR, 50 EUR, 100 EUR)
  1341→
  1342→Variants (3 total - each a different denomination):
  1343→
  1344→| SKU | Price (cents) | Weight | requires_shipping | Inventory |
  1345→|-----|---------------|--------|-------------------|-----------|
  1346→| ACME-GIFT-25 | 2500 | 0g | **0 (digital)** | 9999 (deny) |
  1347→| ACME-GIFT-50 | 5000 | 0g | **0 (digital)** | 9999 (deny) |
  1348→| ACME-GIFT-100 | 10000 | 0g | **0 (digital)** | 9999 (deny) |
  1349→
  1350→**Test purpose:** Tests digital product flow (no shipping step in checkout).
  1351→
  1352→---
  1353→
  1354→**Product 20: "Cashmere Overcoat" -- EXPENSIVE**
  1355→
  1356→| Field | Value |
  1357→|-------|-------|
  1358→| handle | cashmere-overcoat |
  1359→| status | active |
  1360→| vendor | Acme Premium |
  1361→| product_type | Jackets |
  1362→| tags | ["limited", "new"] |
  1363→| description_html | "Luxurious cashmere-blend overcoat. Impeccable tailoring with silk lining." |
  1364→| published_at | now |
  1365→
  1366→Collections: New Arrivals
  1367→
  1368→Options: Size (S, M, L), Color (Camel, Charcoal)
  1369→
  1370→Variants (6 total): price_amount = 49999 (499.99 EUR), weight_g = 1200, inventory = 3 each (deny)
  1371→
  1372→**Test purpose:** Tests high-value price display (499.99 EUR).
  1373→
  1374→---
  1375→
  1376→#### Product Summary Table (Acme Fashion)
  1377→
  1378→| # | Title | Handle | Status | Price (cents) | Compare At | Variants | Inventory | Special |
  1379→|---|-------|--------|--------|---------------|-----------|----------|-----------|---------|
  1380→| 1 | Classic Cotton T-Shirt | classic-cotton-t-shirt | active | 2499 | - | 12 | 15 ea | - |
  1381→| 2 | Premium Slim Fit Jeans | premium-slim-fit-jeans | active | 7999 | 9999 | 10 | 8 ea | On sale |
  1382→| 3 | Organic Hoodie | organic-hoodie | active | 5999 | - | 4 | 20 ea | - |
  1383→| 4 | Leather Belt | leather-belt | active | 3499 | - | 4 | 25 ea | - |
  1384→| 5 | Running Sneakers | running-sneakers | active | 11999 | - | 14 | 5 ea | Low stock |
  1385→| 6 | Graphic Print Tee | graphic-print-tee | active | 2999 | - | 4 | 18 ea | - |
  1386→| 7 | V-Neck Linen Tee | v-neck-linen-tee | active | 3499 | - | 9 | 12 ea | - |
  1387→| 8 | Striped Polo Shirt | striped-polo-shirt | active | 2799 | 3999 | 4 | 10 ea | On sale |
  1388→| 9 | Cargo Pants | cargo-pants | active | 5499 | - | 12 | 14 ea | - |
  1389→| 10 | Chino Shorts | chino-shorts | active | 3999 | - | 8 | 16 ea | - |
  1390→| 11 | Wide Leg Trousers | wide-leg-trousers | active | 4999 | 6999 | 3 | 7 ea | On sale |
  1391→| 12 | Wool Scarf | wool-scarf | active | 2999 | - | 3 | 30 ea | - |
  1392→| 13 | Canvas Tote Bag | canvas-tote-bag | active | 1999 | - | 2 | 40 ea | - |
  1393→| 14 | Bucket Hat | bucket-hat | active | 2499 | - | 6 | 22 ea | - |
  1394→| 15 | Unreleased Winter Jacket | unreleased-winter-jacket | draft | 14999 | - | 4 | 0 ea | Draft |
  1395→| 16 | Discontinued Raincoat | discontinued-raincoat | archived | 8999 | - | 2 | 3 ea | Archived |
  1396→| 17 | Limited Edition Sneakers | limited-edition-sneakers | active | 15999 | - | 3 | 0 ea | Sold out (deny) |
  1397→| 18 | Backorder Denim Jacket | backorder-denim-jacket | active | 9999 | - | 4 | 0 ea | Backorder (continue) |
  1398→| 19 | Gift Card | gift-card | active | 2500/5000/10000 | - | 3 | 9999 ea | Digital |
  1399→| 20 | Cashmere Overcoat | cashmere-overcoat | active | 49999 | - | 6 | 3 ea | Expensive |
  1400→
  1401→**Total variants across Acme Fashion:** 107
  1402→
  1403→---
  1404→
  1405→#### Acme Electronics - 5 Products (minimal seed for tenant isolation)
  1406→
  1407→These products exist for tenant isolation testing. Store 2 data must never appear in Store 1 queries.
  1408→
  1409→| # | Title | Handle | Vendor | Type | Options | Variants | Price (cents) | Weight | Inventory |
  1410→|---|-------|--------|--------|------|---------|----------|---------------|--------|-----------|
  1411→| E1 | Pro Laptop 15 | pro-laptop-15 | TechCorp | Laptops | Storage: 256GB, 512GB, 1TB | 3 | 99999, 119999, 149999 | 1800g | 10 ea |
  1412→| E2 | Wireless Headphones | wireless-headphones | AudioMax | Audio | Color: Black, Silver | 2 | 14999 | 250g | 25 ea |
  1413→| E3 | USB-C Cable 2m | usb-c-cable-2m | CablePro | Cables | (none - single default variant) | 1 | 1299 | 50g | 200 |
  1414→| E4 | Mechanical Keyboard | mechanical-keyboard | KeyTech | Peripherals | Switch Type: Red, Blue, Brown | 3 | 12999 | 1100g | 15 ea |
  1415→| E5 | Monitor Stand | monitor-stand | DeskGear | Accessories | (none - single default variant) | 1 | 4999 | 2500g | 30 |
  1416→
  1417→All active, published now, tags vary. Inventory policy = "deny" for all.
  1418→
  1419→**Note on product media:** No `ProductMedia` records are seeded. The storefront must gracefully handle products without images by displaying a placeholder (e.g., a neutral SVG placeholder with the store's brand color). E2E tests should not assert on specific image URLs.
  1420→
  1421→---
  1422→
  1423→### 3.11 DiscountSeeder
  1424→
  1425→**Acme Fashion - 5 discount codes:**
  1426→
  1427→| Code | Type | Value Type | value_amount | starts_at | ends_at | usage_limit | usage_count | rules_json | status |
  1428→|------|------|-----------|-------------|-----------|---------|-------------|-------------|-----------|--------|
  1429→| WELCOME10 | code | percent | 10 (=10%) | 2025-01-01 | 2027-12-31 | null | 3 | min_purchase_amount: 2000 (=20.00 EUR) | active |
  1430→| FLAT5 | code | fixed | 500 (=5.00 EUR) | 2025-01-01 | 2027-12-31 | null | 0 | (empty) | active |
  1431→| FREESHIP | code | free_shipping | 0 | 2025-01-01 | 2027-12-31 | null | 1 | (empty) | active |
  1432→| EXPIRED20 | code | percent | 20 (=20%) | 2024-01-01 | 2024-12-31 | null | 0 | (empty) | expired |
  1433→| MAXED | code | percent | 10 (=10%) | 2025-01-01 | 2027-12-31 | 5 | 5 | (empty) | active |
  1434→
  1435→**Behavioral expectations:**
  1436→- WELCOME10: Applies 10% off to orders with subtotal >= 20.00 EUR. Already used 3 times (has capacity remaining since no usage_limit).
  1437→- FLAT5: Applies flat 5.00 EUR discount. Never used.
  1438→- FREESHIP: Removes shipping cost. Used once.
  1439→- EXPIRED20: Expired - checkout must reject this code with "discount expired" error.
  1440→- MAXED: Usage limit of 5 reached - checkout must reject this code with "discount limit reached" error.
  1441→
  1442→---
  1443→
  1444→### 3.12 CustomerSeeder
  1445→
  1446→**Acme Fashion - 10 customers:**
  1447→
  1448→| # | Email | Name | Password (plaintext) | marketing_opt_in |
  1449→|---|-------|------|----------------------|------------------|
  1450→| 1 | customer@acme.test | John Doe | password | 1 |
  1451→| 2 | jane@example.com | Jane Smith | password | 0 |
  1452→| 3 | michael@example.com | Michael Brown | password | 1 |
  1453→| 4 | sarah@example.com | Sarah Wilson | password | 0 |
  1454→| 5 | david@example.com | David Lee | password | 1 |
  1455→| 6 | emma@example.com | Emma Garcia | password | 0 |
  1456→| 7 | james@example.com | James Taylor | password | 0 |
  1457→| 8 | lisa@example.com | Lisa Anderson | password | 1 |
  1458→| 9 | robert@example.com | Robert Martinez | password | 0 |
  1459→| 10 | anna@example.com | Anna Thomas | password | 1 |
  1460→
  1461→**Customer 1 (customer@acme.test) addresses:**
  1462→
  1463→| Label | is_default | Address Details |
  1464→|-------|-----------|-----------------|
  1465→| Home | 1 | John Doe, Hauptstrasse 1, 10115 Berlin, DE, phone: +49 30 12345678 |
  1466→| Work | 0 | John Doe, Acme Corp, Friedrichstrasse 100, 3rd Floor, 10117 Berlin, DE, phone: +49 30 87654321 |
  1467→
  1468→**Customer 2 (jane@example.com) addresses:**
  1469→
  1470→| Label | is_default | Address Details |
  1471→|-------|-----------|-----------------|
  1472→| Home | 1 | Jane Smith, Schillerstrasse 45, 80336 Munich, Bavaria (BY), DE |
  1473→
  1474→**Customers 3-10:** Each gets one default address with Faker-generated German addresses.
  1475→
  1476→**Acme Electronics - 2 customers (tenant isolation testing):**
  1477→
  1478→| # | Email | Name | Password |
  1479→|---|-------|------|----------|
  1480→| 1 | techfan@example.com | Tech Fan | password |
  1481→| 2 | gadgetlover@example.com | Gadget Lover | password |
  1482→
  1483→Each has one default address with Faker-generated data.
  1484→
  1485→---
  1486→
  1487→### 3.13 OrderSeeder
  1488→
  1489→**Acme Fashion - 15 orders:**
  1490→
  1491→All orders use the customer's default shipping address (unless noted). Order numbers follow the "#NNNN" format starting from #1001.
  1492→
  1493→---
  1494→
  1495→**Order #1001 - Awaiting fulfillment**
  1496→
  1497→| Field | Value |
  1498→|-------|-------|
  1499→| customer | customer@acme.test (John Doe) |
  1500→| status / financial / fulfillment | paid / paid / unfulfilled |
  1501→| placed_at | 2 days ago |
  1502→
  1503→Lines:
  1504→
  1505→| Product | Variant (option values) | Qty | Unit Price | Line Total |
  1506→|---------|------------------------|-----|-----------|-----------|
  1507→| Classic Cotton T-Shirt | S / White | 2 | 2499 | 4998 |
  1508→
  1509→Totals: subtotal 4998, discount 0, shipping 499, tax 798, total **5497**
  1510→
  1511→Payment: stripe, provider_payment_id "pi_test_order1001", status "captured", amount 5497
  1512→
  1513→Fulfillments: None.
  1514→
  1515→---
  1516→
  1517→**Order #1002 - Fully delivered**
  1518→
  1519→| Field | Value |
  1520→|-------|-------|
  1521→| customer | customer@acme.test (John Doe) |
  1522→| status / financial / fulfillment | fulfilled / paid / fulfilled |
  1523→| placed_at | 10 days ago |
  1524→
  1525→Lines:
  1526→
  1527→| Product | Variant | Qty | Unit Price | Line Total |
  1528→|---------|---------|-----|-----------|-----------|
  1529→| Organic Hoodie | M | 1 | 5999 | 5999 |
  1530→| Classic Cotton T-Shirt | L / Black | 1 | 2499 | 2499 |
  1531→
  1532→Totals: subtotal 8498, discount 0, shipping 499, tax 1357, total **8997**
  1533→
  1534→Payment: stripe, "pi_test_order1002", captured, 8997
  1535→
  1536→Fulfillment: status "delivered", tracking company "DHL", tracking number "DHL1234567890", shipped 8 days ago. Fulfillment lines cover all order lines at full quantity.
  1537→
  1538→---
  1539→
  1540→**Order #1003 - Partially fulfilled**
  1541→
  1542→| Field | Value |
  1543→|-------|-------|
  1544→| customer | jane@example.com (Jane Smith) |
  1545→| status / financial / fulfillment | paid / paid / partial |
  1546→| placed_at | 5 days ago |
  1547→
  1548→Lines:
  1549→
  1550→| Product | Variant | Qty | Unit Price | Line Total |
  1551→|---------|---------|-----|-----------|-----------|
  1552→| Premium Slim Fit Jeans | 32 / Blue | 1 | 7999 | 7999 |
  1553→| Leather Belt | L/XL / Brown | 1 | 3499 | 3499 |
  1554→
  1555→Totals: subtotal 11498, discount 0, shipping 499, tax 1836, total **11997**
  1556→
  1557→Payment: stripe, "pi_test_order1003", captured, 11997
  1558→
  1559→Fulfillment: status "shipped", DHL, "DHL9876543210", shipped 3 days ago. Fulfillment lines: **only the Jeans line (qty 1)**. The Belt is not yet shipped.
  1560→
  1561→---
  1562→
  1563→**Order #1004 - Cancelled with full refund**
  1564→
  1565→| Field | Value |
  1566→|-------|-------|
  1567→| customer | customer@acme.test (John Doe) |
  1568→| status / financial / fulfillment | cancelled / refunded / unfulfilled |
  1569→| placed_at | 15 days ago |
  1570→
  1571→Lines:
  1572→
  1573→| Product | Variant | Qty | Unit Price | Line Total |
  1574→|---------|---------|-----|-----------|-----------|
  1575→| Classic Cotton T-Shirt | M / Navy | 1 | 2499 | 2499 |
  1576→
  1577→Totals: subtotal 2499, discount 0, shipping 499, tax 399, total **2998**
  1578→
  1579→Payment: stripe, "pi_test_order1004", status "refunded", amount 2998
  1580→
  1581→Refund: amount 2998, reason "Customer requested cancellation", status "processed", provider_refund_id "re_test_order1004"
  1582→
  1583→---
  1584→
  1585→**Order #1005 - Payment pending**
  1586→
  1587→| Field | Value |
  1588→|-------|-------|
  1589→| customer | jane@example.com (Jane Smith) |
  1590→| status / financial / fulfillment | pending / pending / unfulfilled |
  1591→| placed_at | 2 hours ago |
  1592→
  1593→Lines:
  1594→
  1595→| Product | Variant | Qty | Unit Price | Line Total |
  1596→|---------|---------|-----|-----------|-----------|
  1597→| Leather Belt | S/M / Black | 1 | 3499 | 3499 |
  1598→
  1599→Totals: subtotal 3499, discount 0, shipping 499, tax 559, total **3998**
  1600→
  1601→Payment: stripe, "pi_test_order1005", status "requires_action", amount 3998
  1602→
  1603→---
  1604→
  1605→**Order #1006 - Standard paid order**
  1606→
  1607→| Field | Value |
  1608→|-------|-------|
  1609→| customer | michael@example.com (Michael Brown) |
  1610→| status / financial / fulfillment | paid / paid / unfulfilled |
  1611→| placed_at | 1 day ago |
  1612→
  1613→Lines:
  1614→
  1615→| Product | Variant | Qty | Unit Price | Line Total |
  1616→|---------|---------|-----|-----------|-----------|
  1617→| Running Sneakers | EU 42 / Black | 1 | 11999 | 11999 |
  1618→
  1619→Totals: subtotal 11999, discount 0, shipping 499, tax 1916, total **12498**
  1620→
  1621→Payment: stripe, "pi_test_order1006", captured, 12498
  1622→
  1623→---
  1624→
  1625→**Order #1007 - Multi-item delivered**
  1626→
  1627→| Field | Value |
  1628→|-------|-------|
  1629→| customer | sarah@example.com (Sarah Wilson) |
  1630→| status / financial / fulfillment | fulfilled / paid / fulfilled |
  1631→| placed_at | 20 days ago |
  1632→
  1633→Lines:
  1634→
  1635→| Product | Variant | Qty | Unit Price | Line Total |
  1636→|---------|---------|-----|-----------|-----------|
  1637→| V-Neck Linen Tee | M / Beige | 2 | 3499 | 6998 |
  1638→| Wool Scarf | Grey | 1 | 2999 | 2999 |
  1639→
  1640→Totals: subtotal 9997, discount 0, shipping 499, tax 1596, total **10496**
  1641→
  1642→Payment: stripe, captured, 10496
  1643→
  1644→Fulfillment: delivered, DHL, "DHL1112223334", shipped 18 days ago. All lines fulfilled.
  1645→
  1646→---
  1647→
  1648→**Order #1008 - Partial refund**
  1649→
  1650→| Field | Value |
  1651→|-------|-------|
  1652→| customer | david@example.com (David Lee) |
  1653→| status / financial / fulfillment | paid / partially_refunded / fulfilled |
  1654→| placed_at | 12 days ago |
  1655→
  1656→Lines:
  1657→
  1658→| Product | Variant | Qty | Unit Price | Line Total |
  1659→|---------|---------|-----|-----------|-----------|
  1660→| Cargo Pants | 32 / Khaki | 1 | 5499 | 5499 |
  1661→| Graphic Print Tee | L | 1 | 2999 | 2999 |
  1662→
  1663→Totals: subtotal 8498, discount 0, shipping 499, tax 1357, total **8997**
  1664→
  1665→Payment: stripe, captured, 8997
  1666→
  1667→Fulfillment: delivered, UPS, "UPS5556667778", shipped 10 days ago
  1668→
  1669→Refund (partial - for Graphic Print Tee only): amount 2999, reason "Item returned", status "processed", provider_refund_id "re_test_order1008"
  1670→
  1671→---
  1672→
  1673→**Order #1009 - Accessories order**
  1674→
  1675→| Field | Value |
  1676→|-------|-------|
  1677→| customer | emma@example.com (Emma Garcia) |
  1678→| status / financial / fulfillment | paid / paid / unfulfilled |
  1679→| placed_at | 3 days ago |
  1680→
  1681→Lines:
  1682→
  1683→| Product | Variant | Qty | Unit Price | Line Total |
  1684→|---------|---------|-----|-----------|-----------|
  1685→| Canvas Tote Bag | Natural | 1 | 1999 | 1999 |
  1686→| Bucket Hat | S/M / Black | 1 | 2499 | 2499 |
  1687→
  1688→Totals: subtotal 4498, discount 0, shipping 499, tax 718, total **4997**
  1689→
  1690→Payment: stripe, captured, 4997
  1691→
  1692→---
  1693→
  1694→**Order #1010 - High-value order**
  1695→
  1696→| Field | Value |
  1697→|-------|-------|
  1698→| customer | customer@acme.test (John Doe) |
  1699→| status / financial / fulfillment | paid / paid / unfulfilled |
  1700→| placed_at | 1 day ago |
  1701→
  1702→Lines:
  1703→
  1704→| Product | Variant | Qty | Unit Price | Line Total |
  1705→|---------|---------|-----|-----------|-----------|
  1706→| Cashmere Overcoat | M / Camel | 1 | 49999 | 49999 |
  1707→
  1708→Totals: subtotal 49999, discount 0, shipping 499, tax 7983, total **50498**
  1709→
  1710→Payment: stripe, captured, 50498
  1711→
  1712→---
  1713→
  1714→**Order #1011 - Single item delivered**
  1715→
  1716→| Field | Value |
  1717→|-------|-------|
  1718→| customer | james@example.com (James Taylor) |
  1719→| status / financial / fulfillment | paid / paid / fulfilled |
  1720→| placed_at | 25 days ago |
  1721→
  1722→Lines:
  1723→
  1724→| Product | Variant | Qty | Unit Price | Line Total |
  1725→|---------|---------|-----|-----------|-----------|
  1726→| Striped Polo Shirt | XL | 1 | 2799 | 2799 |
  1727→
  1728→Totals: subtotal 2799, discount 0, shipping 499, tax 447, total **3298**
  1729→
  1730→Payment: stripe, captured, 3298
  1731→
  1732→Fulfillment: delivered, FedEx, "FX9998887776", shipped 23 days ago
  1733→
  1734→---
  1735→
  1736→**Order #1012 - Multi-quantity order**
  1737→
  1738→| Field | Value |
  1739→|-------|-------|
  1740→| customer | lisa@example.com (Lisa Anderson) |
  1741→| status / financial / fulfillment | paid / paid / unfulfilled |
  1742→| placed_at | 4 days ago |
  1743→
  1744→Lines:
  1745→
  1746→| Product | Variant | Qty | Unit Price | Line Total |
  1747→|---------|---------|-----|-----------|-----------|
  1748→| Chino Shorts | 34 / Navy | 2 | 3999 | 7998 |
  1749→
  1750→Totals: subtotal 7998, discount 0, shipping 499, tax 1277, total **8497**
  1751→
  1752→Payment: stripe, captured, 8497
  1753→
  1754→---
  1755→
  1756→**Order #1013 - Multi-item order**
  1757→
  1758→| Field | Value |
  1759→|-------|-------|
  1760→| customer | robert@example.com (Robert Martinez) |
  1761→| status / financial / fulfillment | paid / paid / unfulfilled |
  1762→| placed_at | 1 day ago |
  1763→
  1764→Lines:
  1765→
  1766→| Product | Variant | Qty | Unit Price | Line Total |
  1767→|---------|---------|-----|-----------|-----------|
  1768→| Wide Leg Trousers | M | 1 | 4999 | 4999 |
  1769→| Wool Scarf | Burgundy | 1 | 2999 | 2999 |
  1770→
  1771→Totals: subtotal 7998, discount 0, shipping 499, tax 1277, total **8497**
  1772→
  1773→Payment: stripe, captured, 8497
  1774→
  1775→---
  1776→
  1777→**Order #1014 - Digital product order**
  1778→
  1779→| Field | Value |
  1780→|-------|-------|
  1781→| customer | anna@example.com (Anna Thomas) |
  1782→| status / financial / fulfillment | paid / paid / fulfilled |
  1783→| placed_at | 14 days ago |
  1784→
  1785→Lines:
  1786→
  1787→| Product | Variant | Qty | Unit Price | Line Total |
  1788→|---------|---------|-----|-----------|-----------|
  1789→| Gift Card | 50 EUR | 1 | 5000 | 5000 |
  1790→
  1791→Totals: subtotal 5000, discount 0, shipping **0** (digital product), tax 798, total **5000**
  1792→
  1793→Payment: stripe, captured, 5000
  1794→
  1795→Fulfillment: delivered (digital fulfillment), no tracking company, no tracking number, shipped_at = placed_at
  1796→
  1797→---
  1798→
  1799→**Order #1015 - Order with discount**

| Field | Value |
|-------|-------|
| handle | canvas-tote-bag |
| status | active |
| vendor | Acme Accessories |
| product_type | Accessories |
| tags | ["trending"] |
| description_html | "Heavy-duty canvas tote bag with reinforced handles. Spacious enough for daily essentials." |
| published_at | now |

Collections: (none)

Options: Color (Natural, Black)

Variants (2 total): price_amount = 1999, weight_g = 300, inventory = 40 each (deny)

---

**Product 14: "Bucket Hat"**

| Field | Value |
|-------|-------|
| handle | bucket-hat |
| status | active |
| vendor | Acme Accessories |
| product_type | Accessories |
| tags | ["new", "trending"] |
| description_html | "Lightweight bucket hat for sun protection. Packable design, washed cotton twill." |
| published_at | now |

Collections: New Arrivals

Options: Size (S/M, L/XL), Color (Beige, Black, Olive)

Variants (6 total): price_amount = 2499, weight_g = 80, inventory = 22 each (deny)

---

**Product 15: "Unreleased Winter Jacket" -- DRAFT**

| Field | Value |
|-------|-------|
| handle | unreleased-winter-jacket |
| status | **draft** |
| vendor | Acme Outerwear |
| product_type | Jackets |
| tags | ["limited"] |
| description_html | "Upcoming winter collection piece. Insulated puffer jacket with water-resistant shell." |
| published_at | **null** |

Collections: (none - draft not assigned)

Options: Size (S, M, L, XL)

Variants (4 total): price_amount = 14999, weight_g = 900, inventory = **0 each** (deny)

**Test purpose:** Verifies draft products do not appear on the storefront.

---

**Product 16: "Discontinued Raincoat" -- ARCHIVED**

| Field | Value |
|-------|-------|
| handle | discontinued-raincoat |
| status | **archived** |
| vendor | Acme Outerwear |
| product_type | Jackets |
| tags | [] |
| description_html | "Lightweight waterproof raincoat. This product has been discontinued." |
| published_at | 6 months ago |

Collections: (none)

Options: Size (M, L)

Variants (2 total): price_amount = 8999, weight_g = 400, inventory = 3 each (deny)

**Test purpose:** Verifies archived products do not appear on the storefront.

---

**Product 17: "Limited Edition Sneakers" -- SOLD OUT (deny policy)**

| Field | Value |
|-------|-------|
| handle | limited-edition-sneakers |
| status | active |
| vendor | Acme Sport |
| product_type | Shoes |
| tags | ["limited"] |
| description_html | "Limited edition collaboration sneakers. Once they are gone, they are gone." |
| published_at | now |

Collections: (none)

Options: Size (EU 40, EU 42, EU 44)

Variants (3 total): price_amount = 15999, weight_g = 650, inventory = **0 each, policy = "deny"**

**Test purpose:** Tests "sold out" display and add-to-cart prevention.

---

**Product 18: "Backorder Denim Jacket" -- OUT OF STOCK (continue policy)**

| Field | Value |
|-------|-------|
| handle | backorder-denim-jacket |
| status | active |
| vendor | Acme Denim |
| product_type | Jackets |
| tags | ["popular"] |
| description_html | "Classic denim jacket. Currently on backorder - ships within 2-3 weeks." |
| published_at | now |

Collections: (none)

Options: Size (S, M, L, XL)

Variants (4 total): price_amount = 9999, weight_g = 750, inventory = **0 each, policy = "continue"**

**Test purpose:** Tests that items with "continue" inventory policy can be added to cart despite zero stock.

---

**Product 19: "Gift Card" -- DIGITAL (no shipping)**

| Field | Value |
|-------|-------|
| handle | gift-card |
| status | active |
| vendor | Acme Fashion |
| product_type | Gift Cards |
| tags | ["popular"] |
| description_html | "Digital gift card delivered via email. The perfect gift when you are not sure what to choose." |
| published_at | now |

Collections: (none)

Options: Amount (25 EUR, 50 EUR, 100 EUR)

Variants (3 total - each a different denomination):

| SKU | Price (cents) | Weight | requires_shipping | Inventory |
|-----|---------------|--------|-------------------|-----------|
| ACME-GIFT-25 | 2500 | 0g | **0 (digital)** | 9999 (deny) |
| ACME-GIFT-50 | 5000 | 0g | **0 (digital)** | 9999 (deny) |
| ACME-GIFT-100 | 10000 | 0g | **0 (digital)** | 9999 (deny) |

**Test purpose:** Tests digital product flow (no shipping step in checkout).

---

**Product 20: "Cashmere Overcoat" -- EXPENSIVE**

| Field | Value |
|-------|-------|
| handle | cashmere-overcoat |
| status | active |
| vendor | Acme Premium |
| product_type | Jackets |
| tags | ["limited", "new"] |
| description_html | "Luxurious cashmere-blend overcoat. Impeccable tailoring with silk lining." |
| published_at | now |

Collections: New Arrivals

Options: Size (S, M, L), Color (Camel, Charcoal)

Variants (6 total): price_amount = 49999 (499.99 EUR), weight_g = 1200, inventory = 3 each (deny)

**Test purpose:** Tests high-value price display (499.99 EUR).

---

#### Product Summary Table (Acme Fashion)

| # | Title | Handle | Status | Price (cents) | Compare At | Variants | Inventory | Special |
|---|-------|--------|--------|---------------|-----------|----------|-----------|---------|
| 1 | Classic Cotton T-Shirt | classic-cotton-t-shirt | active | 2499 | - | 12 | 15 ea | - |
| 2 | Premium Slim Fit Jeans | premium-slim-fit-jeans | active | 7999 | 9999 | 10 | 8 ea | On sale |
| 3 | Organic Hoodie | organic-hoodie | active | 5999 | - | 4 | 20 ea | - |
| 4 | Leather Belt | leather-belt | active | 3499 | - | 4 | 25 ea | - |
| 5 | Running Sneakers | running-sneakers | active | 11999 | - | 14 | 5 ea | Low stock |
| 6 | Graphic Print Tee | graphic-print-tee | active | 2999 | - | 4 | 18 ea | - |
| 7 | V-Neck Linen Tee | v-neck-linen-tee | active | 3499 | - | 9 | 12 ea | - |
| 8 | Striped Polo Shirt | striped-polo-shirt | active | 2799 | 3999 | 4 | 10 ea | On sale |
| 9 | Cargo Pants | cargo-pants | active | 5499 | - | 12 | 14 ea | - |
| 10 | Chino Shorts | chino-shorts | active | 3999 | - | 8 | 16 ea | - |
| 11 | Wide Leg Trousers | wide-leg-trousers | active | 4999 | 6999 | 3 | 7 ea | On sale |
| 12 | Wool Scarf | wool-scarf | active | 2999 | - | 3 | 30 ea | - |
| 13 | Canvas Tote Bag | canvas-tote-bag | active | 1999 | - | 2 | 40 ea | - |
| 14 | Bucket Hat | bucket-hat | active | 2499 | - | 6 | 22 ea | - |
| 15 | Unreleased Winter Jacket | unreleased-winter-jacket | draft | 14999 | - | 4 | 0 ea | Draft |
| 16 | Discontinued Raincoat | discontinued-raincoat | archived | 8999 | - | 2 | 3 ea | Archived |
| 17 | Limited Edition Sneakers | limited-edition-sneakers | active | 15999 | - | 3 | 0 ea | Sold out (deny) |
| 18 | Backorder Denim Jacket | backorder-denim-jacket | active | 9999 | - | 4 | 0 ea | Backorder (continue) |
| 19 | Gift Card | gift-card | active | 2500/5000/10000 | - | 3 | 9999 ea | Digital |
| 20 | Cashmere Overcoat | cashmere-overcoat | active | 49999 | - | 6 | 3 ea | Expensive |

**Total variants across Acme Fashion:** 107

---

#### Acme Electronics - 5 Products (minimal seed for tenant isolation)

These products exist for tenant isolation testing. Store 2 data must never appear in Store 1 queries.

| # | Title | Handle | Vendor | Type | Options | Variants | Price (cents) | Weight | Inventory |
|---|-------|--------|--------|------|---------|----------|---------------|--------|-----------|
| E1 | Pro Laptop 15 | pro-laptop-15 | TechCorp | Laptops | Storage: 256GB, 512GB, 1TB | 3 | 99999, 119999, 149999 | 1800g | 10 ea |
| E2 | Wireless Headphones | wireless-headphones | AudioMax | Audio | Color: Black, Silver | 2 | 14999 | 250g | 25 ea |
| E3 | USB-C Cable 2m | usb-c-cable-2m | CablePro | Cables | (none - single default variant) | 1 | 1299 | 50g | 200 |
| E4 | Mechanical Keyboard | mechanical-keyboard | KeyTech | Peripherals | Switch Type: Red, Blue, Brown | 3 | 12999 | 1100g | 15 ea |
| E5 | Monitor Stand | monitor-stand | DeskGear | Accessories | (none - single default variant) | 1 | 4999 | 2500g | 30 |

All active, published now, tags vary. Inventory policy = "deny" for all.

**Note on product media:** No `ProductMedia` records are seeded. The storefront must gracefully handle products without images by displaying a placeholder (e.g., a neutral SVG placeholder with the store's brand color). E2E tests should not assert on specific image URLs.

---

### 3.11 DiscountSeeder

**Acme Fashion - 5 discount codes:**

| Code | Type | Value Type | value_amount | starts_at | ends_at | usage_limit | usage_count | rules_json | status |
|------|------|-----------|-------------|-----------|---------|-------------|-------------|-----------|--------|
| WELCOME10 | code | percent | 10 (=10%) | 2025-01-01 | 2027-12-31 | null | 3 | min_purchase_amount: 2000 (=20.00 EUR) | active |
| FLAT5 | code | fixed | 500 (=5.00 EUR) | 2025-01-01 | 2027-12-31 | null | 0 | (empty) | active |
| FREESHIP | code | free_shipping | 0 | 2025-01-01 | 2027-12-31 | null | 1 | (empty) | active |
| EXPIRED20 | code | percent | 20 (=20%) | 2024-01-01 | 2024-12-31 | null | 0 | (empty) | expired |
| MAXED | code | percent | 10 (=10%) | 2025-01-01 | 2027-12-31 | 5 | 5 | (empty) | active |

**Behavioral expectations:**
- WELCOME10: Applies 10% off to orders with subtotal >= 20.00 EUR. Already used 3 times (has capacity remaining since no usage_limit).
- FLAT5: Applies flat 5.00 EUR discount. Never used.
- FREESHIP: Removes shipping cost. Used once.
- EXPIRED20: Expired - checkout must reject this code with "discount expired" error.
- MAXED: Usage limit of 5 reached - checkout must reject this code with "discount limit reached" error.

---

### 3.12 CustomerSeeder

**Acme Fashion - 10 customers:**

| # | Email | Name | Password (plaintext) | marketing_opt_in |
|---|-------|------|----------------------|------------------|
| 1 | customer@acme.test | John Doe | password | 1 |
| 2 | jane@example.com | Jane Smith | password | 0 |
| 3 | michael@example.com | Michael Brown | password | 1 |
| 4 | sarah@example.com | Sarah Wilson | password | 0 |
| 5 | david@example.com | David Lee | password | 1 |
| 6 | emma@example.com | Emma Garcia | password | 0 |
| 7 | james@example.com | James Taylor | password | 0 |
| 8 | lisa@example.com | Lisa Anderson | password | 1 |
| 9 | robert@example.com | Robert Martinez | password | 0 |
| 10 | anna@example.com | Anna Thomas | password | 1 |

**Customer 1 (customer@acme.test) addresses:**

| Label | is_default | Address Details |
|-------|-----------|-----------------|
| Home | 1 | John Doe, Hauptstrasse 1, 10115 Berlin, DE, phone: +49 30 12345678 |
| Work | 0 | John Doe, Acme Corp, Friedrichstrasse 100, 3rd Floor, 10117 Berlin, DE, phone: +49 30 87654321 |

**Customer 2 (jane@example.com) addresses:**

| Label | is_default | Address Details |
|-------|-----------|-----------------|
| Home | 1 | Jane Smith, Schillerstrasse 45, 80336 Munich, Bavaria (BY), DE |

**Customers 3-10:** Each gets one default address with Faker-generated German addresses.

**Acme Electronics - 2 customers (tenant isolation testing):**

| # | Email | Name | Password |
|---|-------|------|----------|
| 1 | techfan@example.com | Tech Fan | password |
| 2 | gadgetlover@example.com | Gadget Lover | password |

Each has one default address with Faker-generated data.

---

### 3.13 OrderSeeder

**Acme Fashion - 15 orders:**

All orders use the customer's default shipping address (unless noted). Order numbers follow the "#NNNN" format starting from #1001.

---

**Order #1001 - Awaiting fulfillment**

| Field | Value |
|-------|-------|
| customer | customer@acme.test (John Doe) |
| status / financial / fulfillment | paid / paid / unfulfilled |
| placed_at | 2 days ago |

Lines:

| Product | Variant (option values) | Qty | Unit Price | Line Total |
|---------|------------------------|-----|-----------|-----------|
| Classic Cotton T-Shirt | S / White | 2 | 2499 | 4998 |

Totals: subtotal 4998, discount 0, shipping 499, tax 798, total **5497**

Payment: stripe, provider_payment_id "pi_test_order1001", status "captured", amount 5497

Fulfillments: None.

---

**Order #1002 - Fully delivered**

| Field | Value |
|-------|-------|
| customer | customer@acme.test (John Doe) |
| status / financial / fulfillment | fulfilled / paid / fulfilled |
| placed_at | 10 days ago |

Lines:

| Product | Variant | Qty | Unit Price | Line Total |
|---------|---------|-----|-----------|-----------|
| Organic Hoodie | M | 1 | 5999 | 5999 |
| Classic Cotton T-Shirt | L / Black | 1 | 2499 | 2499 |

Totals: subtotal 8498, discount 0, shipping 499, tax 1357, total **8997**

Payment: stripe, "pi_test_order1002", captured, 8997

Fulfillment: status "delivered", tracking company "DHL", tracking number "DHL1234567890", shipped 8 days ago. Fulfillment lines cover all order lines at full quantity.

---

**Order #1003 - Partially fulfilled**

| Field | Value |
|-------|-------|
| customer | jane@example.com (Jane Smith) |
| status / financial / fulfillment | paid / paid / partial |
| placed_at | 5 days ago |

Lines:

| Product | Variant | Qty | Unit Price | Line Total |
|---------|---------|-----|-----------|-----------|
| Premium Slim Fit Jeans | 32 / Blue | 1 | 7999 | 7999 |
| Leather Belt | L/XL / Brown | 1 | 3499 | 3499 |

Totals: subtotal 11498, discount 0, shipping 499, tax 1836, total **11997**

Payment: stripe, "pi_test_order1003", captured, 11997

Fulfillment: status "shipped", DHL, "DHL9876543210", shipped 3 days ago. Fulfillment lines: **only the Jeans line (qty 1)**. The Belt is not yet shipped.

---

**Order #1004 - Cancelled with full refund**

| Field | Value |
|-------|-------|
| customer | customer@acme.test (John Doe) |
| status / financial / fulfillment | cancelled / refunded / unfulfilled |
| placed_at | 15 days ago |

Lines:

| Product | Variant | Qty | Unit Price | Line Total |
|---------|---------|-----|-----------|-----------|
| Classic Cotton T-Shirt | M / Navy | 1 | 2499 | 2499 |

Totals: subtotal 2499, discount 0, shipping 499, tax 399, total **2998**

Payment: stripe, "pi_test_order1004", status "refunded", amount 2998

Refund: amount 2998, reason "Customer requested cancellation", status "processed", provider_refund_id "re_test_order1004"

---

**Order #1005 - Payment pending**

| Field | Value |
|-------|-------|
| customer | jane@example.com (Jane Smith) |
| status / financial / fulfillment | pending / pending / unfulfilled |
| placed_at | 2 hours ago |

Lines:

| Product | Variant | Qty | Unit Price | Line Total |
|---------|---------|-----|-----------|-----------|
| Leather Belt | S/M / Black | 1 | 3499 | 3499 |

Totals: subtotal 3499, discount 0, shipping 499, tax 559, total **3998**

Payment: stripe, "pi_test_order1005", status "requires_action", amount 3998

---

**Order #1006 - Standard paid order**

| Field | Value |
|-------|-------|
| customer | michael@example.com (Michael Brown) |
| status / financial / fulfillment | paid / paid / unfulfilled |
| placed_at | 1 day ago |

Lines:

| Product | Variant | Qty | Unit Price | Line Total |
|---------|---------|-----|-----------|-----------|
| Running Sneakers | EU 42 / Black | 1 | 11999 | 11999 |

Totals: subtotal 11999, discount 0, shipping 499, tax 1916, total **12498**

Payment: stripe, "pi_test_order1006", captured, 12498

---

**Order #1007 - Multi-item delivered**

| Field | Value |
|-------|-------|
| customer | sarah@example.com (Sarah Wilson) |
| status / financial / fulfillment | fulfilled / paid / fulfilled |
| placed_at | 20 days ago |

Lines:

| Product | Variant | Qty | Unit Price | Line Total |
|---------|---------|-----|-----------|-----------|
| V-Neck Linen Tee | M / Beige | 2 | 3499 | 6998 |
| Wool Scarf | Grey | 1 | 2999 | 2999 |

Totals: subtotal 9997, discount 0, shipping 499, tax 1596, total **10496**

Payment: stripe, captured, 10496

Fulfillment: delivered, DHL, "DHL1112223334", shipped 18 days ago. All lines fulfilled.

---

**Order #1008 - Partial refund**

| Field | Value |
|-------|-------|
| customer | david@example.com (David Lee) |
| status / financial / fulfillment | paid / partially_refunded / fulfilled |
| placed_at | 12 days ago |

Lines:

| Product | Variant | Qty | Unit Price | Line Total |
|---------|---------|-----|-----------|-----------|
| Cargo Pants | 32 / Khaki | 1 | 5499 | 5499 |
| Graphic Print Tee | L | 1 | 2999 | 2999 |

Totals: subtotal 8498, discount 0, shipping 499, tax 1357, total **8997**

Payment: stripe, captured, 8997

Fulfillment: delivered, UPS, "UPS5556667778", shipped 10 days ago

Refund (partial - for Graphic Print Tee only): amount 2999, reason "Item returned", status "processed", provider_refund_id "re_test_order1008"

---

**Order #1009 - Accessories order**

| Field | Value |
|-------|-------|
| customer | emma@example.com (Emma Garcia) |
| status / financial / fulfillment | paid / paid / unfulfilled |
| placed_at | 3 days ago |

Lines:

| Product | Variant | Qty | Unit Price | Line Total |
|---------|---------|-----|-----------|-----------|
| Canvas Tote Bag | Natural | 1 | 1999 | 1999 |
| Bucket Hat | S/M / Black | 1 | 2499 | 2499 |

Totals: subtotal 4498, discount 0, shipping 499, tax 718, total **4997**

Payment: stripe, captured, 4997

---

**Order #1010 - High-value order**

| Field | Value |
|-------|-------|
| customer | customer@acme.test (John Doe) |
| status / financial / fulfillment | paid / paid / unfulfilled |
| placed_at | 1 day ago |

Lines:

| Product | Variant | Qty | Unit Price | Line Total |
|---------|---------|-----|-----------|-----------|
| Cashmere Overcoat | M / Camel | 1 | 49999 | 49999 |

Totals: subtotal 49999, discount 0, shipping 499, tax 7983, total **50498**

Payment: stripe, captured, 50498

---

**Order #1011 - Single item delivered**

| Field | Value |
|-------|-------|
| customer | james@example.com (James Taylor) |
| status / financial / fulfillment | paid / paid / fulfilled |
| placed_at | 25 days ago |

Lines:

| Product | Variant | Qty | Unit Price | Line Total |
|---------|---------|-----|-----------|-----------|
| Striped Polo Shirt | XL | 1 | 2799 | 2799 |

Totals: subtotal 2799, discount 0, shipping 499, tax 447, total **3298**

Payment: stripe, captured, 3298

Fulfillment: delivered, FedEx, "FX9998887776", shipped 23 days ago

---

**Order #1012 - Multi-quantity order**

| Field | Value |
|-------|-------|
| customer | lisa@example.com (Lisa Anderson) |
| status / financial / fulfillment | paid / paid / unfulfilled |
| placed_at | 4 days ago |

Lines:

| Product | Variant | Qty | Unit Price | Line Total |
|---------|---------|-----|-----------|-----------|
| Chino Shorts | 34 / Navy | 2 | 3999 | 7998 |

Totals: subtotal 7998, discount 0, shipping 499, tax 1277, total **8497**

Payment: stripe, captured, 8497

---

**Order #1013 - Multi-item order**

| Field | Value |
|-------|-------|
| customer | robert@example.com (Robert Martinez) |
| status / financial / fulfillment | paid / paid / unfulfilled |
| placed_at | 1 day ago |

Lines:

| Product | Variant | Qty | Unit Price | Line Total |
|---------|---------|-----|-----------|-----------|
| Wide Leg Trousers | M | 1 | 4999 | 4999 |
| Wool Scarf | Burgundy | 1 | 2999 | 2999 |

Totals: subtotal 7998, discount 0, shipping 499, tax 1277, total **8497**

Payment: stripe, captured, 8497

---

**Order #1014 - Digital product order**

| Field | Value |
|-------|-------|
| customer | anna@example.com (Anna Thomas) |
| status / financial / fulfillment | paid / paid / fulfilled |
| placed_at | 14 days ago |

Lines:

| Product | Variant | Qty | Unit Price | Line Total |
|---------|---------|-----|-----------|-----------|
| Gift Card | 50 EUR | 1 | 5000 | 5000 |

Totals: subtotal 5000, discount 0, shipping **0** (digital product), tax 798, total **5000**

Payment: stripe, captured, 5000

Fulfillment: delivered (digital fulfillment), no tracking company, no tracking number, shipped_at = placed_at

---

**Order #1015 - Order with discount**


     1→# 08 - Playwright E2E Test Plan
     2→
     3→> Complete end-to-end browser test specification for the Shopify-clone platform using Pest v4 browser testing (Playwright-backed). All tests verify the entire application works through a real browser. This document defines requirements and specifications for the coding agent to implement.
     4→
     5→---
     6→
     7→## Test Suite Summary
     8→
     9→| Suite | File | Tests | Purpose | Dependencies |
    10→|-------|------|:-----:|---------|--------------|
    11→| 1 - Smoke Tests | `tests/Browser/SmokeTest.php` | 10 | Hit every major page, assert HTTP 200 and no JS errors | Store 1, Products, Collections, Pages |
    12→| 2 - Admin Authentication | `tests/Browser/Admin/AuthenticationTest.php` | 10 | Admin login, logout, invalid credentials, session access control | Admin user |
    13→| 3 - Admin Product Management | `tests/Browser/Admin/ProductManagementTest.php` | 7 | Product CRUD: listing, creating, editing, archiving, filtering | Admin user, Products (incl. #15 draft) |
    14→| 4 - Admin Order Management | `tests/Browser/Admin/OrderManagementTest.php` | 7 | Order listing, filtering, detail view, fulfillment, refund | Admin user, Orders (#1001), Customer |
    15→| 5 - Admin Discount Management | `tests/Browser/Admin/DiscountManagementTest.php` | 6 | Discount code listing, creation, editing | Admin user, Discounts |
    16→| 6 - Admin Settings | `tests/Browser/Admin/SettingsTest.php` | 7 | Store settings, shipping zones, tax config, domains | Admin user, Shipping zones, Store 1 |
    17→| 7 - Storefront Browsing | `tests/Browser/Storefront/BrowsingTest.php` | 15 | Home page, collections, product detail, variants, search, pages | Products, Collections, Pages, Products #15/#17/#18 |
    18→| 8 - Cart Flow | `tests/Browser/Storefront/CartTest.php` | 12 | Cart lifecycle: add, update qty, remove, discounts, totals | Products, Discounts (all codes) |
    19→| 9 - Checkout Flow | `tests/Browser/Storefront/CheckoutTest.php` | 8 | Full checkout: contact, address, shipping, payment, confirmation | Products, Shipping zones, Discounts |
    20→| 10 - Customer Account | `tests/Browser/Storefront/CustomerAccountTest.php` | 12 | Registration, login, order history, addresses, logout | Customer user, Orders (#1001/#1002/#1004) |
    21→| 11 - Inventory Enforcement | `tests/Browser/Storefront/InventoryTest.php` | 4 | Inventory policy enforcement: deny vs. continue | Products #17 (deny), #18 (continue), #1 |
    22→| 12 - Tenant Isolation | `tests/Browser/Storefront/TenantIsolationTest.php` | 5 | Multi-store data isolation verification | Store 1, Admin user, Customer user |
    23→| 13 - Responsive / Mobile | `tests/Browser/Storefront/ResponsiveTest.php` | 8 | Mobile and tablet viewport rendering and interaction | Products, Admin user |
    24→| 14 - Accessibility | `tests/Browser/Storefront/AccessibilityTest.php` | 11 | Heading hierarchy, ARIA labels, form labels, keyboard nav | Products, Store 1 |
    25→| 15 - Admin Collections | `tests/Browser/Admin/CollectionManagementTest.php` | 3 | Collection CRUD in admin | Admin user, Collections |
    26→| 16 - Admin Customers | `tests/Browser/Admin/CustomerManagementTest.php` | 3 | Customer listing and detail views | Admin user, Customer user, Orders |
    27→| 17 - Admin Pages | `tests/Browser/Admin/PageManagementTest.php` | 3 | CMS page management | Admin user, Pages |
    28→| 18 - Admin Analytics | `tests/Browser/Admin/AnalyticsTest.php` | 3 | Analytics dashboard rendering | Admin user, Orders |
    29→| **Total** | **18 files** | **134** | | |
    30→
    31→---
    32→
    33→## Test Infrastructure Requirements
    34→
    35→### Framework
    36→
    37→- **Test runner:** Pest v4 with browser testing capabilities (Playwright under the hood)
    38→- **Test traits:** Each browser test file must use `TestCase` and `RefreshDatabase`
    39→- **Configuration:** Register the `Browser` test directory in `tests/Pest.php` with appropriate base classes
    40→- **Base URL:** Configured via `APP_URL` in `.env.testing`, set to `http://acme-fashion.test`
    41→
    42→### Pest v4 Browser Testing API Reference
    43→
    44→The coding agent should use these Pest v4 browser methods:
    45→
    46→| Method | Purpose |
    47→|--------|---------|
    48→| `visit(url)` | Open a real browser and navigate to the given URL |
    49→| `visit([url1, url2, ...])` | Visit multiple pages (for batch smoke testing) |
    50→| `fill(field, value)` | Type a value into a named form field |
    51→| `click(text)` | Click a visible element matching the given text |
    52→| `assertSee(text)` | Assert the page contains the given visible text |
    53→| `assertNoJavascriptErrors()` | Assert no uncaught JS exceptions occurred |
    54→| `assertNoConsoleLogs()` | Assert no console warnings or errors were logged |
    55→
    56→Additionally, standard Laravel test features are available within browser tests: factories, `Event::fake()`, `Notification::fake()`, `$this->actingAs()`, and model queries.
    57→
    58→### Environment Configuration
    59→
    60→The `.env.testing` file must contain the following keys:
    61→
    62→| Key | Value | Purpose |
    63→|-----|-------|---------|
    64→| `APP_ENV` | `testing` | Laravel environment |
    65→| `APP_URL` | `http://acme-fashion.test` | Browser base URL (served by Herd) |
    66→| `DB_CONNECTION` | `sqlite` | Database driver |
    67→| `DB_DATABASE` | Absolute path to test SQLite file | Isolated test database |
    68→| `STRIPE_SECRET_KEY` | `sk_test_fake` | Fake Stripe key for test mode |
    69→| `STRIPE_WEBHOOK_SECRET` | `whsec_test_fake` | Fake webhook secret |
    70→| `MAIL_MAILER` | `array` | Trap emails in memory |
    71→| `QUEUE_CONNECTION` | `sync` | Process jobs synchronously |
    72→
    73→### Prerequisites Before Running
    74→
    75→1. Database seeded with `php artisan migrate:fresh --seed`
    76→2. Laravel Herd serving `acme-fashion.test`
    77→3. Playwright browsers installed via `npx playwright install`
    78→4. Frontend assets built via `npm run build`
    79→
    80→---
    81→
    82→## Seeded Data Dependency Table
    83→
    84→All tests depend on the database seeder providing this baseline data. Each test gets a fresh database via `RefreshDatabase`, then the seeder repopulates it.
    85→
    86→Each browser test file must set `protected bool $seed = true;` on the test class (or call `$this->seed(DatabaseSeeder::class)` in `beforeEach`) to ensure the database is populated after RefreshDatabase clears it. Laravel's `RefreshDatabase` trait does NOT automatically run seeders unless explicitly configured.
    87→
    88→| Entity | Key Details | Used By Suites |
    89→|--------|-------------|----------------|
    90→| **Admin user** | Email: `admin@acme.test`, password: `password`, role: `owner` on Store 1 | 2, 3, 4, 5, 6, 12, 13, 15, 16, 17, 18 |
    91→| **Customer user** | Email: `customer@acme.test`, password: `password`, name: "John Doe", belongs to Store 1 | 10, 12 |
    92→| **Store 1** | Name: "Acme Fashion", handle: `acme-fashion`, domain: `acme-fashion.test`, currency: EUR | 1, 6, 7, 12, 14 |
    93→| **Product #1** | Title: "Classic Cotton T-Shirt", handle: `classic-cotton-t-shirt`, price: 24.99 EUR, status: active, options: Size (S/M/L/XL), Color (Black/White/Navy), inventory: in stock | 1, 3, 7, 8, 9, 11, 13, 14 |
    94→| **Product #2** | Title: "Premium Slim Fit Jeans", handle: `premium-slim-fit-jeans`, status: active, has compare-at pricing (sale), options: Size, Color | 7, 8 |
    95→| **Product #15** | Status: `draft` - must NOT appear on storefront | 3, 7 |
    96→| **Product #17** | Status: active, inventory: 0 on hand, policy: `deny` - displays "Sold out", add-to-cart disabled | 7, 11 |
    97→| **Product #18** | Status: active, inventory: 0 on hand, policy: `continue` - displays "Available on backorder", add-to-cart enabled | 7, 11 |
    98→| **Products (20 total)** | Seeded across collections, with variants, options, images, inventory | 1, 3, 7, 8, 9, 11, 13 |
    99→| **Collections** | "T-Shirts" (handle: `t-shirts`), "New Arrivals" (handle: `new-arrivals`), plus others | 1, 7, 13, 15 |
   100→| **Orders** | #1001 (status: paid, fulfillment: unfulfilled, customer: customer@acme.test), #1002, #1004 also belonging to the same customer | 4, 10, 16, 18 |
   101→| **Discounts** | WELCOME10 (10% off, min 20 EUR purchase, active), FLAT5 (5 EUR fixed, active), FREESHIP (free shipping, active), EXPIRED20 (expired end date), MAXED (usage_count >= usage_limit) | 5, 8, 9 |
   102→| **Shipping zones** | "Domestic" zone (countries: DE) with rate "Standard Shipping" at 4.99 EUR flat; "International" zone with its own rates | 6, 9 |
   103→| **Tax settings** | Store 1: manual mode, prices_include_tax varies by test | 6 |
   104→| **Pages** | "About" (handle: `about`, status: published) | 1, 7, 17 |
   105→| **Navigation** | Main menu with links to collections, about page | 7 |
   106→
   107→---
   108→
   109→## Test Execution Matrix
   110→
   111→Tests should be executed in the order shown below. Later suites depend on the infrastructure and pages verified by earlier suites.
   112→
   113→```mermaid
   114→graph TD
   115→    S1[Suite 1: Smoke Tests] --> S2[Suite 2: Admin Auth]
   116→    S1 --> S7[Suite 7: Storefront Browsing]
   117→    S2 --> S3[Suite 3: Admin Products]
   118→    S2 --> S4[Suite 4: Admin Orders]
   119→    S2 --> S5[Suite 5: Admin Discounts]
   120→    S2 --> S6[Suite 6: Admin Settings]
   121→    S2 --> S15[Suite 15: Admin Collections]
   122→    S2 --> S16[Suite 16: Admin Customers]
   123→    S2 --> S17[Suite 17: Admin Pages]
   124→    S2 --> S18[Suite 18: Admin Analytics]
   125→    S7 --> S8[Suite 8: Cart Flow]
   126→    S8 --> S9[Suite 9: Checkout Flow]
   127→    S7 --> S10[Suite 10: Customer Account]
   128→    S7 --> S11[Suite 11: Inventory Enforcement]
   129→    S7 --> S12[Suite 12: Tenant Isolation]
   130→    S7 --> S13[Suite 13: Responsive / Mobile]
   131→    S7 --> S14[Suite 14: Accessibility]
   132→```
   133→
   134→| Execution Phase | Suites | Rationale |
   135→|-----------------|--------|-----------|
   136→| Phase 1 | Suite 1 (Smoke) | Verifies all routes load before deeper testing |
   137→| Phase 2 | Suites 2, 7 | Core auth and storefront browsing - gate to all further tests |
   138→| Phase 3 | Suites 3, 4, 5, 6, 10, 11, 15, 16, 17, 18 | Admin CRUD and storefront feature suites (can run in parallel) |
   139→| Phase 4 | Suite 8 (Cart) | Depends on storefront browsing working |
   140→| Phase 5 | Suite 9 (Checkout) | Depends on cart flow working |
   141→| Phase 6 | Suites 12, 13, 14 | Cross-cutting concerns: isolation, responsive, accessibility |
   142→
   143→---
   144→
   145→## Test Directory Structure
   146→
   147→```
   148→tests/Browser/
   149→    SmokeTest.php
   150→    Admin/
   151→        AuthenticationTest.php
   152→        ProductManagementTest.php
   153→        OrderManagementTest.php
   154→        DiscountManagementTest.php
   155→        SettingsTest.php
   156→        CollectionManagementTest.php
   157→        CustomerManagementTest.php
   158→        PageManagementTest.php
   159→        AnalyticsTest.php
   160→    Storefront/
   161→        BrowsingTest.php
   162→        CartTest.php
   163→        CheckoutTest.php
   164→        CustomerAccountTest.php
   165→        InventoryTest.php
   166→        TenantIsolationTest.php
   167→        ResponsiveTest.php
   168→        AccessibilityTest.php
   169→```
   170→
   171→---
   172→
   173→## Suite 1: Smoke Tests
   174→
   175→**File:** `tests/Browser/SmokeTest.php`
   176→
   177→**Purpose:** Hit every major public and admin page, assert they load without JavaScript errors. This is the fastest way to catch broken routes, missing views, or uncaught JS exceptions.
   178→
   179→---
   180→
   181→### Test 1.1: Loads the storefront home page
   182→
   183→**Preconditions:** Seeded store
   184→
   185→**Steps:**
   186→1. Navigate to `/`
   187→
   188→**Expected Results:**
   189→- Page displays "Acme Fashion"
   190→- No JavaScript errors
   191→
   192→---
   193→
   194→### Test 1.2: Loads a collection page
   195→
   196→**Preconditions:** Seeded collections
   197→
   198→**Steps:**
   199→1. Navigate to `/collections/t-shirts`
   200→

# 08 - Playwright E2E Test Plan

> Complete end-to-end browser test specification for the Shopify-clone platform using Pest v4 browser testing (Playwright-backed). All tests verify the entire application works through a real browser. This document defines requirements and specifications for the coding agent to implement.

---

## Test Suite Summary

| Suite | File | Tests | Purpose | Dependencies |
|-------|------|:-----:|---------|--------------|
| 1 - Smoke Tests | `tests/Browser/SmokeTest.php` | 10 | Hit every major page, assert HTTP 200 and no JS errors | Store 1, Products, Collections, Pages |
| 2 - Admin Authentication | `tests/Browser/Admin/AuthenticationTest.php` | 10 | Admin login, logout, invalid credentials, session access control | Admin user |
| 3 - Admin Product Management | `tests/Browser/Admin/ProductManagementTest.php` | 7 | Product CRUD: listing, creating, editing, archiving, filtering | Admin user, Products (incl. #15 draft) |
| 4 - Admin Order Management | `tests/Browser/Admin/OrderManagementTest.php` | 7 | Order listing, filtering, detail view, fulfillment, refund | Admin user, Orders (#1001), Customer |
| 5 - Admin Discount Management | `tests/Browser/Admin/DiscountManagementTest.php` | 6 | Discount code listing, creation, editing | Admin user, Discounts |
| 6 - Admin Settings | `tests/Browser/Admin/SettingsTest.php` | 7 | Store settings, shipping zones, tax config, domains | Admin user, Shipping zones, Store 1 |
| 7 - Storefront Browsing | `tests/Browser/Storefront/BrowsingTest.php` | 15 | Home page, collections, product detail, variants, search, pages | Products, Collections, Pages, Products #15/#17/#18 |
| 8 - Cart Flow | `tests/Browser/Storefront/CartTest.php` | 12 | Cart lifecycle: add, update qty, remove, discounts, totals | Products, Discounts (all codes) |
| 9 - Checkout Flow | `tests/Browser/Storefront/CheckoutTest.php` | 8 | Full checkout: contact, address, shipping, payment, confirmation | Products, Shipping zones, Discounts |
| 10 - Customer Account | `tests/Browser/Storefront/CustomerAccountTest.php` | 12 | Registration, login, order history, addresses, logout | Customer user, Orders (#1001/#1002/#1004) |
| 11 - Inventory Enforcement | `tests/Browser/Storefront/InventoryTest.php` | 4 | Inventory policy enforcement: deny vs. continue | Products #17 (deny), #18 (continue), #1 |
| 12 - Tenant Isolation | `tests/Browser/Storefront/TenantIsolationTest.php` | 5 | Multi-store data isolation verification | Store 1, Admin user, Customer user |
| 13 - Responsive / Mobile | `tests/Browser/Storefront/ResponsiveTest.php` | 8 | Mobile and tablet viewport rendering and interaction | Products, Admin user |
| 14 - Accessibility | `tests/Browser/Storefront/AccessibilityTest.php` | 11 | Heading hierarchy, ARIA labels, form labels, keyboard nav | Products, Store 1 |
| 15 - Admin Collections | `tests/Browser/Admin/CollectionManagementTest.php` | 3 | Collection CRUD in admin | Admin user, Collections |
| 16 - Admin Customers | `tests/Browser/Admin/CustomerManagementTest.php` | 3 | Customer listing and detail views | Admin user, Customer user, Orders |
| 17 - Admin Pages | `tests/Browser/Admin/PageManagementTest.php` | 3 | CMS page management | Admin user, Pages |
| 18 - Admin Analytics | `tests/Browser/Admin/AnalyticsTest.php` | 3 | Analytics dashboard rendering | Admin user, Orders |
| **Total** | **18 files** | **134** | | |

---

## Test Infrastructure Requirements

### Framework

- **Test runner:** Pest v4 with browser testing capabilities (Playwright under the hood)
- **Test traits:** Each browser test file must use `TestCase` and `RefreshDatabase`
- **Configuration:** Register the `Browser` test directory in `tests/Pest.php` with appropriate base classes
- **Base URL:** Configured via `APP_URL` in `.env.testing`, set to `http://acme-fashion.test`

### Pest v4 Browser Testing API Reference

The coding agent should use these Pest v4 browser methods:

| Method | Purpose |
|--------|---------|
| `visit(url)` | Open a real browser and navigate to the given URL |
| `visit([url1, url2, ...])` | Visit multiple pages (for batch smoke testing) |
| `fill(field, value)` | Type a value into a named form field |
| `click(text)` | Click a visible element matching the given text |
| `assertSee(text)` | Assert the page contains the given visible text |
| `assertNoJavascriptErrors()` | Assert no uncaught JS exceptions occurred |
| `assertNoConsoleLogs()` | Assert no console warnings or errors were logged |

Additionally, standard Laravel test features are available within browser tests: factories, `Event::fake()`, `Notification::fake()`, `$this->actingAs()`, and model queries.

### Environment Configuration

The `.env.testing` file must contain the following keys:

| Key | Value | Purpose |
|-----|-------|---------|
| `APP_ENV` | `testing` | Laravel environment |
| `APP_URL` | `http://acme-fashion.test` | Browser base URL (served by Herd) |
| `DB_CONNECTION` | `sqlite` | Database driver |
| `DB_DATABASE` | Absolute path to test SQLite file | Isolated test database |
| `STRIPE_SECRET_KEY` | `sk_test_fake` | Fake Stripe key for test mode |
| `STRIPE_WEBHOOK_SECRET` | `whsec_test_fake` | Fake webhook secret |
| `MAIL_MAILER` | `array` | Trap emails in memory |
| `QUEUE_CONNECTION` | `sync` | Process jobs synchronously |

### Prerequisites Before Running

1. Database seeded with `php artisan migrate:fresh --seed`
2. Laravel Herd serving `acme-fashion.test`
3. Playwright browsers installed via `npx playwright install`
4. Frontend assets built via `npm run build`

---

## Seeded Data Dependency Table

All tests depend on the database seeder providing this baseline data. Each test gets a fresh database via `RefreshDatabase`, then the seeder repopulates it.

Each browser test file must set `protected bool $seed = true;` on the test class (or call `$this->seed(DatabaseSeeder::class)` in `beforeEach`) to ensure the database is populated after RefreshDatabase clears it. Laravel's `RefreshDatabase` trait does NOT automatically run seeders unless explicitly configured.

| Entity | Key Details | Used By Suites |
|--------|-------------|----------------|
| **Admin user** | Email: `admin@acme.test`, password: `password`, role: `owner` on Store 1 | 2, 3, 4, 5, 6, 12, 13, 15, 16, 17, 18 |
| **Customer user** | Email: `customer@acme.test`, password: `password`, name: "John Doe", belongs to Store 1 | 10, 12 |
| **Store 1** | Name: "Acme Fashion", handle: `acme-fashion`, domain: `acme-fashion.test`, currency: EUR | 1, 6, 7, 12, 14 |
| **Product #1** | Title: "Classic Cotton T-Shirt", handle: `classic-cotton-t-shirt`, price: 24.99 EUR, status: active, options: Size (S/M/L/XL), Color (Black/White/Navy), inventory: in stock | 1, 3, 7, 8, 9, 11, 13, 14 |
| **Product #2** | Title: "Premium Slim Fit Jeans", handle: `premium-slim-fit-jeans`, status: active, has compare-at pricing (sale), options: Size, Color | 7, 8 |
| **Product #15** | Status: `draft` - must NOT appear on storefront | 3, 7 |
| **Product #17** | Status: active, inventory: 0 on hand, policy: `deny` - displays "Sold out", add-to-cart disabled | 7, 11 |
| **Product #18** | Status: active, inventory: 0 on hand, policy: `continue` - displays "Available on backorder", add-to-cart enabled | 7, 11 |
| **Products (20 total)** | Seeded across collections, with variants, options, images, inventory | 1, 3, 7, 8, 9, 11, 13 |
| **Collections** | "T-Shirts" (handle: `t-shirts`), "New Arrivals" (handle: `new-arrivals`), plus others | 1, 7, 13, 15 |
| **Orders** | #1001 (status: paid, fulfillment: unfulfilled, customer: customer@acme.test), #1002, #1004 also belonging to the same customer | 4, 10, 16, 18 |
| **Discounts** | WELCOME10 (10% off, min 20 EUR purchase, active), FLAT5 (5 EUR fixed, active), FREESHIP (free shipping, active), EXPIRED20 (expired end date), MAXED (usage_count >= usage_limit) | 5, 8, 9 |
| **Shipping zones** | "Domestic" zone (countries: DE) with rate "Standard Shipping" at 4.99 EUR flat; "International" zone with its own rates | 6, 9 |
| **Tax settings** | Store 1: manual mode, prices_include_tax varies by test | 6 |
| **Pages** | "About" (handle: `about`, status: published) | 1, 7, 17 |
| **Navigation** | Main menu with links to collections, about page | 7 |

---

## Test Execution Matrix

Tests should be executed in the order shown below. Later suites depend on the infrastructure and pages verified by earlier suites.

```mermaid
graph TD
    S1[Suite 1: Smoke Tests] --> S2[Suite 2: Admin Auth]
    S1 --> S7[Suite 7: Storefront Browsing]
    S2 --> S3[Suite 3: Admin Products]
    S2 --> S4[Suite 4: Admin Orders]
    S2 --> S5[Suite 5: Admin Discounts]
    S2 --> S6[Suite 6: Admin Settings]
    S2 --> S15[Suite 15: Admin Collections]
    S2 --> S16[Suite 16: Admin Customers]
    S2 --> S17[Suite 17: Admin Pages]
    S2 --> S18[Suite 18: Admin Analytics]
    S7 --> S8[Suite 8: Cart Flow]
    S8 --> S9[Suite 9: Checkout Flow]
    S7 --> S10[Suite 10: Customer Account]
    S7 --> S11[Suite 11: Inventory Enforcement]
    S7 --> S12[Suite 12: Tenant Isolation]
    S7 --> S13[Suite 13: Responsive / Mobile]
    S7 --> S14[Suite 14: Accessibility]
```

| Execution Phase | Suites | Rationale |
|-----------------|--------|-----------|
| Phase 1 | Suite 1 (Smoke) | Verifies all routes load before deeper testing |
| Phase 2 | Suites 2, 7 | Core auth and storefront browsing - gate to all further tests |
| Phase 3 | Suites 3, 4, 5, 6, 10, 11, 15, 16, 17, 18 | Admin CRUD and storefront feature suites (can run in parallel) |
| Phase 4 | Suite 8 (Cart) | Depends on storefront browsing working |
| Phase 5 | Suite 9 (Checkout) | Depends on cart flow working |
| Phase 6 | Suites 12, 13, 14 | Cross-cutting concerns: isolation, responsive, accessibility |

---

## Test Directory Structure

```
tests/Browser/
    SmokeTest.php
    Admin/
        AuthenticationTest.php
        ProductManagementTest.php
        OrderManagementTest.php
        DiscountManagementTest.php
        SettingsTest.php
        CollectionManagementTest.php
        CustomerManagementTest.php
        PageManagementTest.php
        AnalyticsTest.php
    Storefront/
        BrowsingTest.php
        CartTest.php
        CheckoutTest.php
        CustomerAccountTest.php
        InventoryTest.php
        TenantIsolationTest.php
        ResponsiveTest.php
        AccessibilityTest.php
```

---

## Suite 1: Smoke Tests

**File:** `tests/Browser/SmokeTest.php`

**Purpose:** Hit every major public and admin page, assert they load without JavaScript errors. This is the fastest way to catch broken routes, missing views, or uncaught JS exceptions.

---

### Test 1.1: Loads the storefront home page

**Preconditions:** Seeded store

**Steps:**
1. Navigate to `/`

**Expected Results:**
- Page displays "Acme Fashion"
- No JavaScript errors

---

### Test 1.2: Loads a collection page

**Preconditions:** Seeded collections

**Steps:**
1. Navigate to `/collections/t-shirts`


     1→# 09 - Implementation Roadmap and Pest Test Specification
     2→
     3→> Shopify-clone e-commerce platform - Step-by-step build order and exhaustive test plan for Laravel 12 + Livewire v4 + Flux UI Free + SQLite + Tailwind CSS v4
     4→
     5→---
     6→
     7→## 1. Technology Decisions (Final)
     8→
     9→These decisions adapt the original PDF spec for a self-contained, benchmarkable Laravel application. All decisions are final and must not be revisited.
    10→
    11→| Concern | PDF Default | Our Decision | Rationale |
    12→|---------|-------------|--------------|-----------|
    13→| Database | PostgreSQL | SQLite | Self-contained, no external DB, zero config |
    14→| Cache | Redis | File (Array for tests) | No Redis dependency |
    15→| Session | Redis | File | No Redis dependency |
    16→| Queue | Redis + Horizon | Sync (dev) / Database (prod) | No Redis dependency, sync for immediate feedback |
    17→| Search | Elasticsearch + Scout | SQLite FTS5 | Self-contained, no external service |
    18→| Storage | S3 | Local filesystem (storage/app/public) | No AWS dependency |
    19→| Admin UI | React/Vue SPA | Livewire v4 + Flux UI Free | Server-rendered, matches project stack |
    20→| Storefront | Blade SSR + Vite | Blade + Livewire + Tailwind v4 | Server-rendered, matches project stack |
    21→| Auth (admin) | Sanctum SPA | Laravel session auth | Standard Livewire auth pattern |
    22→| Auth (API) | Sanctum tokens | Sanctum personal access tokens | For programmatic API access |
    23→| Auth (customer) | Separate guard | Custom `customer` guard | Storefront customer auth |
    24→| OAuth (apps) | Passport | Deferred/Stubbed | Simplifies initial build, can add later |
    25→| Payments | Stripe + PayPal | Stripe test mode + PayPal stub | Stripe with test keys, PayPal interface only |
    26→| Email | SMTP/SES | Log driver (dev) / SMTP | Log for benchmark, SMTP configurable |
    27→| Monitoring | OpenTelemetry | Laravel Log (structured JSON) | Simplified for benchmark |
    28→| CI/CD | GitHub Actions | Not implemented | Out of scope for one-shot benchmark |
    29→| Docker | Multi-stage | Not implemented | App served by Laravel Herd locally |
    30→
    31→---
    32→
    33→## 2. Implementation Phases (Build Order)
    34→
    35→Each phase builds on the previous. A coding agent should execute these in strict sequential order. Tests are written alongside each phase (see Section 3 for the full test specification).
    36→
    37→### Phase Dependency Graph
    38→
    39→```mermaid
    40→flowchart TD
    41→    P1[Phase 1: Foundation]
    42→    P2[Phase 2: Catalog]
    43→    P3[Phase 3: Themes and Storefront Layout]
    44→    P4[Phase 4: Cart, Checkout, Discounts, Shipping, Taxes]
    45→    P5[Phase 5: Payments, Orders, Fulfillment]
    46→    P6[Phase 6: Customer Accounts]
    47→    P7[Phase 7: Admin Panel]
    48→    P8[Phase 8: Search]
    49→    P9[Phase 9: Analytics]
    50→    P10[Phase 10: Apps and Webhooks]
    51→    P11[Phase 11: Polish]
    52→    P12[Phase 12: Full Test Suite]
    53→
    54→    P1 --> P2
    55→    P1 --> P3
    56→    P2 --> P4
    57→    P3 --> P4
    58→    P4 --> P5
    59→    P5 --> P6
    60→    P5 --> P7
    61→    P2 --> P8
    62→    P5 --> P9
    63→    P5 --> P10
    64→    P6 --> P11
    65→    P7 --> P11
    66→    P8 --> P11
    67→    P9 --> P11
    68→    P10 --> P11
    69→    P11 --> P12
    70→```
    71→
    72→---
    73→
    74→### Phase 1: Foundation (Migrations, Models, Middleware, Auth)
    75→
    76→**Priority:** CRITICAL - everything depends on this.
    77→
    78→#### Step 1.1: Environment and Config
    79→
    80→| Config File | Setting | Value | Purpose |
    81→|-------------|---------|-------|---------|
    82→| `.env` | `DB_CONNECTION` | `sqlite` | Use SQLite as database |
    83→| `.env` | `DB_DATABASE` | `database/database.sqlite` | SQLite database file path |
    84→| `.env` | `CACHE_STORE` | `file` | File-based caching |
    85→| `.env` | `SESSION_DRIVER` | `file` | File-based sessions |
    86→| `.env` | `QUEUE_CONNECTION` | `sync` | Synchronous queue for dev |
    87→| `.env` | `MAIL_MAILER` | `log` | Log-based mail for dev |
    88→| `.env` | `FILESYSTEM_DISK` | `local` | Local filesystem storage |
    89→| `config/database.php` | SQLite connection | WAL mode, foreign keys enabled, busy_timeout=5000, synchronous=normal | Optimized SQLite configuration |
    90→| `config/session.php` | `driver` | `file` | Session driver |
    91→| `config/session.php` | `lifetime` | `120` | Session lifetime in minutes |
    92→| `config/cache.php` | `default` | `file` | Cache driver |
    93→| `config/queue.php` | `default` | `sync` | Queue driver |
    94→| `config/auth.php` | Guards | Add `customer` guard with `customers` provider | Customer storefront authentication |
    95→| `config/auth.php` | Providers | Add `customers` provider pointing to Customer model | Customer user provider |
    96→| `config/auth.php` | Passwords | Add `customers` password broker | Customer password reset |
    97→| `config/filesystems.php` | `public` disk | Ensure configured for local storage | Media file storage |
    98→| `config/logging.php` | Channels | Add structured JSON channel | Structured logging |
    99→
   100→#### Step 1.2: Core Migrations (Batch 1-2)
   101→
   102→Create in this dependency order:
   103→
   104→1. `create_organizations_table` - no dependencies
   105→2. `create_stores_table` - FK: `organization_id`
   106→3. `create_store_domains_table` - FK: `store_id`
   107→4. Modify default `users` migration - add `status`, `last_login_at`, `two_factor_*` columns; rename `password` to `password_hash` internally
   108→5. `create_store_users_table` - composite PK: `store_id` + `user_id`, with `role` column
   109→6. `create_store_settings_table` - PK: `store_id`
   110→
   111→All monetary amounts stored as INTEGER in minor units (cents). All enums stored as TEXT with CHECK constraints. All timestamps as TEXT (ISO-8601) via Laravel.
   112→
   113→#### Step 1.3: Core Models
   114→
   115→**Files to create:**
   116→
   117→| Type | Name | Purpose |
   118→|------|------|---------|
   119→| Model (with migration, factory, seeder) | `Organization` | Top-level tenant entity |
   120→| Model (with migration, factory, seeder) | `Store` | Individual store within an organization |
   121→| Model (with migration, factory, seeder) | `StoreDomain` | Domain mapping for a store |
   122→| Model (pivot) | `StoreUser` | Pivot model with `role` attribute, custom Pivot class |
   123→| Model (with migration, factory, seeder) | `StoreSettings` | Per-store configuration |
   124→
   125→**Relationship definitions:**
   126→
   127→| Model | Key Relationships |
   128→|-------|-------------------|
   129→| `Organization` | `hasMany(Store::class)` |
   130→| `Store` | `belongsTo(Organization::class)`, `hasMany(StoreDomain::class)`, `belongsToMany(User::class)` through `store_users`, `hasOne(StoreSettings::class)` |
   131→| `StoreDomain` | `belongsTo(Store::class)` |
   132→| `User` | `belongsToMany(Store::class)` through `store_users` |
   133→| `StoreUser` | Pivot model with `role` attribute, custom `Pivot` class |
   134→| `StoreSettings` | `belongsTo(Store::class)` |
   135→
   136→Each model must have:
   137→- Explicit return type declarations on all methods
   138→- Proper `$fillable` or `$guarded` arrays
   139→- `casts()` method for JSON/enum columns
   140→- Factory with meaningful default values
   141→- Seeder that creates sample data
   142→
   143→#### Step 1.4: Enums
   144→
   145→**Files to create in `app/Enums/`:**
   146→
   147→| Enum | Values |
   148→|------|--------|
   149→| `StoreStatus` | `Active`, `Suspended` |
   150→| `StoreUserRole` | `Owner`, `Admin`, `Staff`, `Support` |
   151→| `StoreDomainType` | `Storefront`, `Admin`, `Api` |
   152→
   153→#### Step 1.5: Tenant Resolution Middleware
   154→
   155→**File to create:** `App\Http\Middleware\ResolveStore`
   156→
   157→Requirements:
   158→1. Storefront routes: extract hostname from the request, look up `store_domains`, cache hostname-to-store_id for 5 minutes, bind `Store` as singleton via container instance `current_store`
   159→2. Admin routes: read `current_store_id` from session, verify user has `store_users` record
   160→3. Return 404 if hostname not found
   161→4. Return 503 for suspended stores on storefront routes
   162→
   163→**Registration in `bootstrap/app.php`:**
   164→- Register middleware group `storefront` containing `ResolveStore`
   165→- Register middleware group `admin` containing `ResolveStore`
   166→
   167→#### Step 1.6: BelongsToStore Trait and Global Scope
   168→
   169→**Files to create:**
   170→
   171→| Type | Name | Purpose |
   172→|------|------|---------|
   173→| Trait | `App\Models\Concerns\BelongsToStore` | Applies StoreScope and auto-sets store_id on creating event |
   174→| Scope | `App\Models\Scopes\StoreScope` | Applies `where('store_id', app('current_store')->id)` to all queries |
   175→
   176→Applied on: products, collections, customers, orders, carts, checkouts, discounts, shipping_zones, themes, pages, navigation_menus, analytics_events, analytics_daily, webhook_subscriptions, inventory_items, search_queries.
   177→
   178→#### Step 1.7: Authentication
   179→
   180→**Admin auth requirements:**
   181→
   182→| Component | Description |
   183→|-----------|-------------|
   184→| Livewire component `Admin\Auth\Login` | Admin login form |
   185→| Livewire component `Admin\Auth\Logout` | Admin logout action |
   186→| Auth mechanism | Standard Laravel session auth via `Auth::guard('web')->attempt()` |
   187→| Rate limiting | 5 attempts per minute per IP via `RateLimiter::for('login')` |
   188→| Password reset | Standard Laravel flow with `Password::broker('users')` |
   189→
   190→**Customer auth requirements:**
   191→
   192→| Component | Description |
   193→|-----------|-------------|
   194→| Custom guard | `customer` guard with `customers` provider |
   195→| Custom user provider | `CustomerUserProvider` that injects `store_id` into queries |
   196→| Livewire component `Storefront\Account\Auth\Login` | Customer login form |
   197→| Livewire component `Storefront\Account\Auth\Register` | Customer registration form |
   198→| Uniqueness | Customer email unique per store, not globally |
   199→| Rate limiting | 5 attempts per minute per IP |
   200→

# 09 - Implementation Roadmap and Pest Test Specification

> Shopify-clone e-commerce platform - Step-by-step build order and exhaustive test plan for Laravel 12 + Livewire v4 + Flux UI Free + SQLite + Tailwind CSS v4

---

## 1. Technology Decisions (Final)

These decisions adapt the original PDF spec for a self-contained, benchmarkable Laravel application. All decisions are final and must not be revisited.

| Concern | PDF Default | Our Decision | Rationale |
|---------|-------------|--------------|-----------|
| Database | PostgreSQL | SQLite | Self-contained, no external DB, zero config |
| Cache | Redis | File (Array for tests) | No Redis dependency |
| Session | Redis | File | No Redis dependency |
| Queue | Redis + Horizon | Sync (dev) / Database (prod) | No Redis dependency, sync for immediate feedback |
| Search | Elasticsearch + Scout | SQLite FTS5 | Self-contained, no external service |
| Storage | S3 | Local filesystem (storage/app/public) | No AWS dependency |
| Admin UI | React/Vue SPA | Livewire v4 + Flux UI Free | Server-rendered, matches project stack |
| Storefront | Blade SSR + Vite | Blade + Livewire + Tailwind v4 | Server-rendered, matches project stack |
| Auth (admin) | Sanctum SPA | Laravel session auth | Standard Livewire auth pattern |
| Auth (API) | Sanctum tokens | Sanctum personal access tokens | For programmatic API access |
| Auth (customer) | Separate guard | Custom `customer` guard | Storefront customer auth |
| OAuth (apps) | Passport | Deferred/Stubbed | Simplifies initial build, can add later |
| Payments | Stripe + PayPal | Stripe test mode + PayPal stub | Stripe with test keys, PayPal interface only |
| Email | SMTP/SES | Log driver (dev) / SMTP | Log for benchmark, SMTP configurable |
| Monitoring | OpenTelemetry | Laravel Log (structured JSON) | Simplified for benchmark |
| CI/CD | GitHub Actions | Not implemented | Out of scope for one-shot benchmark |
| Docker | Multi-stage | Not implemented | App served by Laravel Herd locally |

---

## 2. Implementation Phases (Build Order)

Each phase builds on the previous. A coding agent should execute these in strict sequential order. Tests are written alongside each phase (see Section 3 for the full test specification).

### Phase Dependency Graph

```mermaid
flowchart TD
    P1[Phase 1: Foundation]
    P2[Phase 2: Catalog]
    P3[Phase 3: Themes and Storefront Layout]
    P4[Phase 4: Cart, Checkout, Discounts, Shipping, Taxes]
    P5[Phase 5: Payments, Orders, Fulfillment]
    P6[Phase 6: Customer Accounts]
    P7[Phase 7: Admin Panel]
    P8[Phase 8: Search]
    P9[Phase 9: Analytics]
    P10[Phase 10: Apps and Webhooks]
    P11[Phase 11: Polish]
    P12[Phase 12: Full Test Suite]

    P1 --> P2
    P1 --> P3
    P2 --> P4
    P3 --> P4
    P4 --> P5
    P5 --> P6
    P5 --> P7
    P2 --> P8
    P5 --> P9
    P5 --> P10
    P6 --> P11
    P7 --> P11
    P8 --> P11
    P9 --> P11
    P10 --> P11
    P11 --> P12
```

---

### Phase 1: Foundation (Migrations, Models, Middleware, Auth)

**Priority:** CRITICAL - everything depends on this.

#### Step 1.1: Environment and Config

| Config File | Setting | Value | Purpose |
|-------------|---------|-------|---------|
| `.env` | `DB_CONNECTION` | `sqlite` | Use SQLite as database |
| `.env` | `DB_DATABASE` | `database/database.sqlite` | SQLite database file path |
| `.env` | `CACHE_STORE` | `file` | File-based caching |
| `.env` | `SESSION_DRIVER` | `file` | File-based sessions |
| `.env` | `QUEUE_CONNECTION` | `sync` | Synchronous queue for dev |
| `.env` | `MAIL_MAILER` | `log` | Log-based mail for dev |
| `.env` | `FILESYSTEM_DISK` | `local` | Local filesystem storage |
| `config/database.php` | SQLite connection | WAL mode, foreign keys enabled, busy_timeout=5000, synchronous=normal | Optimized SQLite configuration |
| `config/session.php` | `driver` | `file` | Session driver |
| `config/session.php` | `lifetime` | `120` | Session lifetime in minutes |
| `config/cache.php` | `default` | `file` | Cache driver |
| `config/queue.php` | `default` | `sync` | Queue driver |
| `config/auth.php` | Guards | Add `customer` guard with `customers` provider | Customer storefront authentication |
| `config/auth.php` | Providers | Add `customers` provider pointing to Customer model | Customer user provider |
| `config/auth.php` | Passwords | Add `customers` password broker | Customer password reset |
| `config/filesystems.php` | `public` disk | Ensure configured for local storage | Media file storage |
| `config/logging.php` | Channels | Add structured JSON channel | Structured logging |

#### Step 1.2: Core Migrations (Batch 1-2)

Create in this dependency order:

1. `create_organizations_table` - no dependencies
2. `create_stores_table` - FK: `organization_id`
3. `create_store_domains_table` - FK: `store_id`
4. Modify default `users` migration - add `status`, `last_login_at`, `two_factor_*` columns; rename `password` to `password_hash` internally
5. `create_store_users_table` - composite PK: `store_id` + `user_id`, with `role` column
6. `create_store_settings_table` - PK: `store_id`

All monetary amounts stored as INTEGER in minor units (cents). All enums stored as TEXT with CHECK constraints. All timestamps as TEXT (ISO-8601) via Laravel.

#### Step 1.3: Core Models

**Files to create:**

| Type | Name | Purpose |
|------|------|---------|
| Model (with migration, factory, seeder) | `Organization` | Top-level tenant entity |
| Model (with migration, factory, seeder) | `Store` | Individual store within an organization |
| Model (with migration, factory, seeder) | `StoreDomain` | Domain mapping for a store |
| Model (pivot) | `StoreUser` | Pivot model with `role` attribute, custom Pivot class |
| Model (with migration, factory, seeder) | `StoreSettings` | Per-store configuration |

**Relationship definitions:**

| Model | Key Relationships |
|-------|-------------------|
| `Organization` | `hasMany(Store::class)` |
| `Store` | `belongsTo(Organization::class)`, `hasMany(StoreDomain::class)`, `belongsToMany(User::class)` through `store_users`, `hasOne(StoreSettings::class)` |
| `StoreDomain` | `belongsTo(Store::class)` |
| `User` | `belongsToMany(Store::class)` through `store_users` |
| `StoreUser` | Pivot model with `role` attribute, custom `Pivot` class |
| `StoreSettings` | `belongsTo(Store::class)` |

Each model must have:
- Explicit return type declarations on all methods
- Proper `$fillable` or `$guarded` arrays
- `casts()` method for JSON/enum columns
- Factory with meaningful default values
- Seeder that creates sample data

#### Step 1.4: Enums

**Files to create in `app/Enums/`:**

| Enum | Values |
|------|--------|
| `StoreStatus` | `Active`, `Suspended` |
| `StoreUserRole` | `Owner`, `Admin`, `Staff`, `Support` |
| `StoreDomainType` | `Storefront`, `Admin`, `Api` |

#### Step 1.5: Tenant Resolution Middleware

**File to create:** `App\Http\Middleware\ResolveStore`

Requirements:
1. Storefront routes: extract hostname from the request, look up `store_domains`, cache hostname-to-store_id for 5 minutes, bind `Store` as singleton via container instance `current_store`
2. Admin routes: read `current_store_id` from session, verify user has `store_users` record
3. Return 404 if hostname not found
4. Return 503 for suspended stores on storefront routes

**Registration in `bootstrap/app.php`:**
- Register middleware group `storefront` containing `ResolveStore`
- Register middleware group `admin` containing `ResolveStore`

#### Step 1.6: BelongsToStore Trait and Global Scope

**Files to create:**

| Type | Name | Purpose |
|------|------|---------|
| Trait | `App\Models\Concerns\BelongsToStore` | Applies StoreScope and auto-sets store_id on creating event |
| Scope | `App\Models\Scopes\StoreScope` | Applies `where('store_id', app('current_store')->id)` to all queries |

Applied on: products, collections, customers, orders, carts, checkouts, discounts, shipping_zones, themes, pages, navigation_menus, analytics_events, analytics_daily, webhook_subscriptions, inventory_items, search_queries.

#### Step 1.7: Authentication

**Admin auth requirements:**

| Component | Description |
|-----------|-------------|
| Livewire component `Admin\Auth\Login` | Admin login form |
| Livewire component `Admin\Auth\Logout` | Admin logout action |
| Auth mechanism | Standard Laravel session auth via `Auth::guard('web')->attempt()` |
| Rate limiting | 5 attempts per minute per IP via `RateLimiter::for('login')` |
| Password reset | Standard Laravel flow with `Password::broker('users')` |

**Customer auth requirements:**

| Component | Description |
|-----------|-------------|
| Custom guard | `customer` guard with `customers` provider |
| Custom user provider | `CustomerUserProvider` that injects `store_id` into queries |
| Livewire component `Storefront\Account\Auth\Login` | Customer login form |
| Livewire component `Storefront\Account\Auth\Register` | Customer registration form |
| Uniqueness | Customer email unique per store, not globally |
| Rate limiting | 5 attempts per minute per IP |


Now let me also read the remaining part of the OrderSeeder to see Order #1015 and the Payment factory section.


Let me get the last order and the payment factory.


  1799→**Order #1015 - Order with discount**
  1800→
  1801→| Field | Value |
  1802→|-------|-------|
  1803→| customer | customer@acme.test (John Doe) |
  1804→| status / financial / fulfillment | paid / paid / unfulfilled |
  1805→| placed_at | now (today) |
  1806→
  1807→Lines:
  1808→
  1809→| Product | Variant | Qty | Unit Price | Line Total | Discount Allocation |
  1810→|---------|---------|-----|-----------|-----------|-------------------|
  1811→| Classic Cotton T-Shirt | M / White | 1 | 2499 | 2499 | WELCOME10 discount, amount: 250 |
  1812→| Graphic Print Tee | M | 1 | 2999 | 2999 | WELCOME10 discount, amount: 300 |
  1813→
  1814→Totals: subtotal 5498, discount **550**, shipping 499, tax 790, total **5447**
  1815→
  1816→Payment: stripe, captured, 5447
  1817→
  1818→The discount_allocations_json on each order line references the WELCOME10 discount's database ID and the allocated discount amount for that specific line.
  1819→
  1820→---
  1821→
  1822→#### Order Summary Table (Acme Fashion)
  1823→
  1824→| Order | Customer | Status | Financial | Fulfillment | Total | Key Test Scenario |
  1825→|-------|----------|--------|-----------|-------------|-------|-------------------|
  1826→| #1001 | John Doe | paid | paid | unfulfilled | 5497 | Awaiting fulfillment |
  1827→| #1002 | John Doe | fulfilled | paid | fulfilled | 8997 | Delivered order |
  1828→| #1003 | Jane Smith | paid | paid | partial | 11997 | Partial fulfillment |
  1829→| #1004 | John Doe | cancelled | refunded | unfulfilled | 2998 | Full cancellation + refund |
  1830→| #1005 | Jane Smith | pending | pending | unfulfilled | 3998 | Payment pending |
  1831→| #1006 | Michael Brown | paid | paid | unfulfilled | 12498 | Standard paid order |
  1832→| #1007 | Sarah Wilson | fulfilled | paid | fulfilled | 10496 | Multi-item delivered |
  1833→| #1008 | David Lee | paid | partially_refunded | fulfilled | 8997 | Partial refund |
  1834→| #1009 | Emma Garcia | paid | paid | unfulfilled | 4997 | Accessories order |
  1835→| #1010 | John Doe | paid | paid | unfulfilled | 50498 | High-value order |
  1836→| #1011 | James Taylor | paid | paid | fulfilled | 3298 | Single item delivered |
  1837→| #1012 | Lisa Anderson | paid | paid | unfulfilled | 8497 | Multi-quantity order |
  1838→| #1013 | Robert Martinez | paid | paid | unfulfilled | 8497 | Multi-item order |
  1839→| #1014 | Anna Thomas | paid | paid | fulfilled | 5000 | Digital product order |
  1840→| #1015 | John Doe | paid | paid | unfulfilled | 5447 | Order with discount |
  1841→
  1842→---
  1843→
  1844→**Acme Electronics - 3 orders (minimal, for tenant isolation):**
  1845→
  1846→| Order | Customer | Status | Financial | Fulfillment | Total | Details |
  1847→|-------|----------|--------|-----------|-------------|-------|---------|
  1848→| #5001 | techfan@example.com | paid | paid | fulfilled | 121298 | Pro Laptop 512GB (119999) + USB-C Cable (1299), shipping 0 |
  1849→| #5002 | gadgetlover@example.com | paid | paid | unfulfilled | 14999 | Wireless Headphones Black (14999), shipping 0 |
  1850→| #5003 | techfan@example.com | pending | pending | unfulfilled | 4999 | Monitor Stand (4999), shipping 0, payment requires_action |
  1851→
  1852→---
  1853→
  1854→### 3.14 ThemeSeeder
  1855→
  1856→**Acme Fashion:**
  1857→
  1858→| Field | Value |
  1859→|-------|-------|
  1860→| name | Default Theme |
  1861→| version | 1.0.0 |
  1862→| status | published |
  1863→| published_at | now |
  1864→
  1865→**Theme settings (theme_settings row):**
  1866→
  1867→| Setting Key | Value |
  1868→|-------------|-------|
  1869→| primary_color | #1a1a2e |
  1870→| secondary_color | #e94560 |
  1871→| font_family | Inter, sans-serif |
  1872→| hero_heading | Welcome to Acme Fashion |
  1873→| hero_subheading | Discover our curated collection of modern essentials |
  1874→| hero_cta_text | Shop New Arrivals |
  1875→| hero_cta_link | /collections/new-arrivals |
  1876→| featured_collection_handles | ["new-arrivals", "t-shirts", "sale"] |
  1877→| footer_text | 2025 Acme Fashion. All rights reserved. |
  1878→| show_announcement_bar | true |
  1879→| announcement_text | Free shipping on orders over 50 EUR - Use code FREESHIP |
  1880→| products_per_page | 12 |
  1881→| show_vendor | true |
  1882→| show_quantity_selector | true |
  1883→
  1884→**Acme Electronics:**
  1885→
  1886→| Field | Value |
  1887→|-------|-------|
  1888→| name | Default Theme |
  1889→| version | 1.0.0 |
  1890→| status | published |
  1891→
  1892→Theme settings:
  1893→
  1894→| Setting Key | Value |
  1895→|-------------|-------|
  1896→| primary_color | #0f172a |
  1897→| secondary_color | #3b82f6 |
  1898→| font_family | Inter, sans-serif |

**Order #1015 - Order with discount**

| Field | Value |
|-------|-------|
| customer | customer@acme.test (John Doe) |
| status / financial / fulfillment | paid / paid / unfulfilled |
| placed_at | now (today) |

Lines:

| Product | Variant | Qty | Unit Price | Line Total | Discount Allocation |
|---------|---------|-----|-----------|-----------|-------------------|
| Classic Cotton T-Shirt | M / White | 1 | 2499 | 2499 | WELCOME10 discount, amount: 250 |
| Graphic Print Tee | M | 1 | 2999 | 2999 | WELCOME10 discount, amount: 300 |

Totals: subtotal 5498, discount **550**, shipping 499, tax 790, total **5447**

Payment: stripe, captured, 5447

The discount_allocations_json on each order line references the WELCOME10 discount's database ID and the allocated discount amount for that specific line.

---

#### Order Summary Table (Acme Fashion)

| Order | Customer | Status | Financial | Fulfillment | Total | Key Test Scenario |
|-------|----------|--------|-----------|-------------|-------|-------------------|
| #1001 | John Doe | paid | paid | unfulfilled | 5497 | Awaiting fulfillment |
| #1002 | John Doe | fulfilled | paid | fulfilled | 8997 | Delivered order |
| #1003 | Jane Smith | paid | paid | partial | 11997 | Partial fulfillment |
| #1004 | John Doe | cancelled | refunded | unfulfilled | 2998 | Full cancellation + refund |
| #1005 | Jane Smith | pending | pending | unfulfilled | 3998 | Payment pending |
| #1006 | Michael Brown | paid | paid | unfulfilled | 12498 | Standard paid order |
| #1007 | Sarah Wilson | fulfilled | paid | fulfilled | 10496 | Multi-item delivered |
| #1008 | David Lee | paid | partially_refunded | fulfilled | 8997 | Partial refund |
| #1009 | Emma Garcia | paid | paid | unfulfilled | 4997 | Accessories order |
| #1010 | John Doe | paid | paid | unfulfilled | 50498 | High-value order |
| #1011 | James Taylor | paid | paid | fulfilled | 3298 | Single item delivered |
| #1012 | Lisa Anderson | paid | paid | unfulfilled | 8497 | Multi-quantity order |
| #1013 | Robert Martinez | paid | paid | unfulfilled | 8497 | Multi-item order |
| #1014 | Anna Thomas | paid | paid | fulfilled | 5000 | Digital product order |
| #1015 | John Doe | paid | paid | unfulfilled | 5447 | Order with discount |

---

**Acme Electronics - 3 orders (minimal, for tenant isolation):**

| Order | Customer | Status | Financial | Fulfillment | Total | Details |
|-------|----------|--------|-----------|-------------|-------|---------|
| #5001 | techfan@example.com | paid | paid | fulfilled | 121298 | Pro Laptop 512GB (119999) + USB-C Cable (1299), shipping 0 |
| #5002 | gadgetlover@example.com | paid | paid | unfulfilled | 14999 | Wireless Headphones Black (14999), shipping 0 |
| #5003 | techfan@example.com | pending | pending | unfulfilled | 4999 | Monitor Stand (4999), shipping 0, payment requires_action |

---

### 3.14 ThemeSeeder

**Acme Fashion:**

| Field | Value |
|-------|-------|
| name | Default Theme |
| version | 1.0.0 |
| status | published |
| published_at | now |

**Theme settings (theme_settings row):**

| Setting Key | Value |
|-------------|-------|
| primary_color | #1a1a2e |
| secondary_color | #e94560 |
| font_family | Inter, sans-serif |
| hero_heading | Welcome to Acme Fashion |
| hero_subheading | Discover our curated collection of modern essentials |
| hero_cta_text | Shop New Arrivals |
| hero_cta_link | /collections/new-arrivals |
| featured_collection_handles | ["new-arrivals", "t-shirts", "sale"] |
| footer_text | 2025 Acme Fashion. All rights reserved. |
| show_announcement_bar | true |
| announcement_text | Free shipping on orders over 50 EUR - Use code FREESHIP |
| products_per_page | 12 |
| show_vendor | true |
| show_quantity_selector | true |

**Acme Electronics:**

| Field | Value |
|-------|-------|
| name | Default Theme |
| version | 1.0.0 |
| status | published |

Theme settings:

| Setting Key | Value |
|-------------|-------|
| primary_color | #0f172a |
| secondary_color | #3b82f6 |
| font_family | Inter, sans-serif |