> ## Documentation Index
> Fetch the complete documentation index at: https://docs.switchyard.run/llms.txt
> Use this file to discover all available pages before exploring further.

# Sourcing Architecture

> How Switchyard sources products from RFC inventory, retailers, and partner brands

# Sourcing Architecture

Switchyard uses a **multi-channel sourcing model** that combines warehouse inventory, retail sweeps, and partner brand consignment to fulfill customer orders.

## Sourcing Channels

| Channel                 | Description                                 | Lead Time   | Typical Use                         |
| ----------------------- | ------------------------------------------- | ----------- | ----------------------------------- |
| **RFC Inventory**       | Pre-stocked items in our fulfillment center | Minutes     | High-velocity items, perishables    |
| **Retail Sweeps**       | Shopping trips to partner retailers         | 1-4 hours   | Long-tail catalog, price arbitrage  |
| **Partner Consignment** | Inventory owned by partner brands           | Pre-stocked | Emerging brands, specialty products |

```mermaid theme={null}
flowchart TD
    subgraph Sources [Sourcing Channels]
        RFC[RFC Inventory<br/>Pre-stocked warehouse items]
        SWEEP[Retail Sweeps<br/>HEB, Target, Walmart, etc.]
        PARTNER[Partner Consignment<br/>Brand-owned inventory]
    end

    subgraph Allocation [Order Allocation]
        ORDER[Customer Order] --> ENGINE{Allocation<br/>Engine}
        ENGINE --> |Check stock| RFC
        ENGINE --> |Find retailer| SWEEP
        ENGINE --> |Consignment flag| PARTNER
    end

    subgraph Fulfillment [Fulfillment]
        RFC --> PICK[Pick List]
        SWEEP --> MANIFEST[Sweep Manifest]
        PARTNER --> PICK
        PICK --> BAGS[Bags & Totes]
        MANIFEST --> INTAKE[Intake] --> BAGS
        BAGS --> ROBOT[Robot Delivery]
    end
```

## RFC Inventory

### Overview

The RFC (Rapid Fulfillment Center) is our warehouse where we pre-stock high-velocity and perishable items for immediate fulfillment.

### Inventory Structure

```
inventory_groups (hierarchical)
├── Zone (A=Ambient, C=Chilled, F=Frozen)
│   ├── Aisle
│   │   ├── Bay
│   │   │   ├── Shelf
│   │   │   │   └── Slot → inventory_locations → inventory_items
```

### Picking Strategy

**FEFO/FIFO** (First Expiring First Out / First In First Out):

1. Items with earliest expiration date picked first
2. For non-perishables, oldest received date picked first
3. Reserved quantities tracked to prevent over-allocation

### Key Tables

| Table                 | Purpose                                                 |
| --------------------- | ------------------------------------------------------- |
| `inventory_items`     | Physical stock with quantities, expiration, lot numbers |
| `inventory_locations` | Where items are stored                                  |
| `inventory_groups`    | Hierarchical warehouse organization                     |
| `pick_lists`          | Picking assignments for staff                           |
| `pick_list_items`     | Individual items to pick                                |

## Retail Sweeps

### Overview

Sweeps are shopping trips to partner retailers (HEB, Target, Walmart, Central Market, Whole Foods, Costco, Trader Joe's) to source items not stocked in the RFC.

### Sweep Economics

The system optimizes sweeps based on profitability:

| Setting                    | Description                                 |
| -------------------------- | ------------------------------------------- |
| `in_store_breakeven_items` | Minimum items for profitable in-store sweep |
| `curbside_breakeven_items` | Minimum items for profitable curbside sweep |
| `labor_rate_hourly`        | Cost of driver time                         |
| `new_sweep_marginal_cost`  | Cost to add another sweep                   |

### Sweep Optimization

The **Sweep Optimizer** consolidates under-threshold sweeps:

1. Identifies sweeps below breakeven threshold
2. Finds alternative sweeps at same location with capacity
3. Moves items to maximize efficiency
4. Cancels empty sweeps

### Sweep Flow

```mermaid theme={null}
flowchart LR
    subgraph Planning
        ALLOC[Order Allocation] --> ROUTE[Route Planning]
        ROUTE --> SWEEP[Sweep Created]
    end

    subgraph Shopping
        SWEEP --> MANIFEST[Manifest Generated]
        MANIFEST --> |Aisle locations from| RM[(retailer_mappings)]
        MANIFEST --> DRIVER[Driver Shops]
    end

    subgraph Return
        DRIVER --> INTAKE[RFC Intake]
        INTAKE --> |Scan items| BAGS[Bags]
        INTAKE --> |Optional| INV[Add to Inventory]
    end
```

### Key Tables

| Table                      | Purpose                                  |
| -------------------------- | ---------------------------------------- |
| `routes`                   | Groups multiple sweeps for a driver trip |
| `sweeps`                   | Individual shopping trip to a retailer   |
| `sweep_items`              | Items on the sweep manifest              |
| `sweep_order_allocations`  | Links sweep items to customer orders     |
| `sweep_economics_settings` | Profitability thresholds                 |
| `retailer_mappings`        | Aisle locations at each retailer         |
| `retailer_pricing`         | Our acquisition cost at each retailer    |

## Partner Brand Consignment

### Overview

Partner brands ship their own products to our RFC for consignment sale. We handle fulfillment and take a commission on sales.

### Partner Lifecycle

```mermaid theme={null}
flowchart TD
    subgraph Onboarding
        REG[Partner Registration] --> REVIEW[Admin Review]
        REVIEW --> |Approve| SETUP[Create Retailer & Location]
        REVIEW --> |Reject| END1[Registration Denied]
    end

    subgraph Products
        SETUP --> IMPORT[Product Import<br/>Manual or Shopify]
        IMPORT --> PENDING[Products Pending Approval]
        PENDING --> |Admin approves| ACTIVE[Products Active]
    end

    subgraph Inventory
        ACTIVE --> REORDER[Admin Creates<br/>Reorder Request]
        REORDER --> |Partner accepts| MANIFEST[Manifest Created]
        MANIFEST --> |Partner ships| RECEIVE[Staff Receives<br/>at RFC]
        RECEIVE --> STOCK[Consignment<br/>Inventory]
    end

    subgraph Sales
        STOCK --> SALE[Customer Purchase]
        SALE --> RECORD[Consignment Sale<br/>Recorded]
        RECORD --> PAYOUT[Monthly Payout]
    end
```

### Commission Model

| Temperature Zone | Default Commission |
| ---------------- | ------------------ |
| Ambient          | 15%                |
| Chilled/Frozen   | 20%                |

Commission can be overridden at the product level via `sellable_products.commission_rate`.

### Manifest Flow

Partner manifests use **TYPE 27 taxonomy IDs** for QR code scanning:

1. Admin creates reorder request with items and quantities
2. Partner has 7 days to accept/reject
3. On acceptance, manifest auto-created with QR code
4. Partner ships to RFC with tracking info
5. Staff scans manifest QR at receiving
6. Items scanned into consignment inventory

### Consignment Sales Tracking

When consignment inventory is sold:

1. `record_consignment_sale()` function triggered
2. Captures commission rate at time of sale
3. Records `commission_amount` and `net_to_partner`
4. Assigns to `payout_period` (YYYY-MM format)
5. Partner views via portal, admin settles monthly

### Shopify Integration

Partners can sync products from their Shopify store:

| Feature               | Description                                             |
| --------------------- | ------------------------------------------------------- |
| **Import**            | Initial product import from Shopify catalog             |
| **Field Sync**        | Configurable per-field sync (name, price, images, etc.) |
| **Daily Cron**        | Automatic sync at 6am CST                               |
| **Conflict Handling** | Local edits pause sync for that field                   |
| **Audit Logs**        | All sync changes logged                                 |

### Key Tables

| Table                           | Purpose                                       |
| ------------------------------- | --------------------------------------------- |
| `partner_brands`                | Partner company profiles and commission rates |
| `partner_manifests`             | Shipments from partners                       |
| `partner_manifest_items`        | Line items with expected/received quantities  |
| `partner_reorder_requests`      | Admin-initiated inventory requests            |
| `partner_reorder_request_items` | Request line items                            |
| `consignment_sales`             | Sales records for payout calculation          |
| `shopify_sync`                  | Per-product sync configuration                |
| `shopify_sync_logs`             | Sync execution audit trail                    |

### Partner-Specific Fields

**On `scraped_products`:**

* `partner_brand_id` - Links product to partner

**On `sellable_products`:**

* `is_partner_brand` - Flag for partner products
* `commission_rate` - Product-level commission override

**On `inventory_items`:**

* `partner_brand_id` - Tracks consignment ownership
* `is_consignment` - Flags consignment inventory
* `manifest_item_id` - Links to receiving manifest

## Allocation Engine

### Decision Flow

When an order arrives, the allocation engine analyzes each line item:

```mermaid theme={null}
flowchart TD
    ITEM[Order Item] --> CHECK{Check RFC<br/>Inventory}
    CHECK --> |Sufficient stock| PICK[Create Pick List Item]
    CHECK --> |Insufficient| SWEEP_CHECK{Find Best<br/>Retailer}
    SWEEP_CHECK --> |Found| SWEEP[Create Sweep Item]
    SWEEP_CHECK --> |None available| OOS[Mark Out of Stock]

    PICK --> |Consignment?| CONSIGN{Record<br/>Consignment Sale}
    CONSIGN --> DONE[Allocated]
    PICK --> DONE
    SWEEP --> DONE
```

### Sourcing Priority

1. **RFC Inventory** - Check available stock (FEFO/FIFO)
2. **Best Retailer** - Compare prices across retailers
3. **Bulk vs Individual** - Consider bulk SKU breakdowns
4. **Route Optimization** - Prefer existing routes when possible

### Key Functions

| Function                    | Location              | Purpose                               |
| --------------------------- | --------------------- | ------------------------------------- |
| `allocateOrder()`           | `order-allocation.ts` | Main allocation logic                 |
| `runSweepOptimization()`    | `sweep-optimizer.ts`  | Post-allocation optimization          |
| `findBestSweepForProduct()` | Meilisearch           | Sweep selection by price/availability |

## Database Views

Prebuilt views for common queries:

| View                               | Purpose                                         |
| ---------------------------------- | ----------------------------------------------- |
| `partner_inventory_summary`        | Active consignment inventory by partner/product |
| `partner_sales_by_period`          | Sales aggregation with payout tracking          |
| `partner_pending_payouts`          | Current amounts due to partners                 |
| `partner_manifests_summary`        | Manifest status with receive percentages        |
| `partner_reorder_requests_summary` | Request status with expiry countdown            |
| `partner_products_view`            | Products with partner brand context             |

## Background Jobs

### Scraper Scheduling

Retailer scrapers run on `pg_cron` schedules:

* Updates `retailer_pricing` with current costs
* Updates `retailer_mappings` with availability
* Wakes scraper workers via `pg_net` HTTP calls

### Shopify Sync

Daily sync at 6am CST via `pg_cron`:

* Triggers `trigger_shopify_sync()` function
* Calls internal API endpoint
* Updates products based on sync configuration

### Reorder Request Expiry

Requests expire after 7 days:

* `expire_old_reorder_requests()` function
* Marks status as `expired`
* Partner notified of expiry

## API Endpoints

### Admin APIs

| Endpoint                                   | Purpose                |
| ------------------------------------------ | ---------------------- |
| `POST /admin/orders`                       | Create orders          |
| `POST /admin/sweeps/optimize`              | Run sweep optimization |
| `GET /admin/partners/brands`               | List partner brands    |
| `POST /admin/partners/brands/{id}/approve` | Approve partner        |
| `GET /admin/partners/manifests`            | List manifests         |
| `POST /admin/partners/reorders`            | Create reorder request |

### Partner Portal APIs

| Endpoint                                     | Purpose                 |
| -------------------------------------------- | ----------------------- |
| `GET /partner/products`                      | View submitted products |
| `GET /partner/manifests`                     | View shipments          |
| `POST /partner/reorder-requests/{id}/accept` | Accept reorder          |
| `GET /partner/sales`                         | View sales and payouts  |
| `POST /partner/shopify-import`               | Import from Shopify     |

### Scanner APIs

| Endpoint                                                       | Purpose                 |
| -------------------------------------------------------------- | ----------------------- |
| `GET /scanner/receiving/manifests`                             | List pending manifests  |
| `POST /scanner/receiving/manifest/{id}/items/{itemId}/receive` | Record received item    |
| `GET /scanner/sweeps/{id}/items`                               | Get sweep shopping list |

## Integration Points

| System               | Integration                          |
| -------------------- | ------------------------------------ |
| **Goods Mobile App** | Order placement via API              |
| **Admin Dashboard**  | Order management, partner admin      |
| **Partner Portal**   | Self-service partner management      |
| **Zebra Scanners**   | Picking, receiving, sweep operations |
| **Shopify**          | Product import and sync              |
| **Meilisearch**      | Product search, sweep selection      |
| **pg\_cron**         | Scheduled scraping and syncing       |
